source: libabac/abac_id.c @ adc0815

mei_rt2mei_rt2_fix_1
Last change on this file since adc0815 was 5110d42, checked in by Mei <mei@…>, 12 years ago

1) reorganized the test directory to include python tests
2) attribute via api and principal via api from python scripts is

working (although there is a annoying seg fault at the very end
that must be related to something not been dup()ed.. need to wait
for c example to debug it)

3) able to query via api
4) replicated access_rt2 example in python and the query result matches
5) expanded api to make it easier to generate rt2 structure

  • Property mode set to 100644
File size: 13.8 KB
Line 
1
2/**
3**  abac_id.c
4**/
5
6#include <assert.h>
7#include <err.h>
8#include <termios.h>
9#include <time.h>
10
11// include the GNU extension of asprintf
12#define _GNU_SOURCE
13#include <stdio.h>
14#include <err.h>
15
16#include "abac_internal.h"
17#include "abac_util.h"
18
19static int debug=0;
20int ABAC_IN_QUERY=0;
21
22#define KEY_SUFFIX  "_private.pem"
23#define CERT_SUFFIX "_ID.pem"
24/* Size of password memory allocation */
25#define PWLEN 128
26
27//
28// abac_id object
29//
30struct _abac_id_t {
31    int idtype;    /* default to 'keyid' for now */
32
33    char *keyid; /* sha */
34    char *cn;
35
36    certificate_t *cert;
37    private_key_t *key;
38
39    int refcount;
40};
41
42/* Callback configuration, local */
43struct cb_opts {
44    bool use_prompt;    /* Print a prompt to stderr */
45    bool use_echo;      /* If true, turn off input echo on stdin */
46    unsigned int tries; /* Number of attempts allowed */
47    char prompt[20];    /* The prompt to display if use_echo is true */
48};
49
50/****************************************************************/
51//
52// Helper functions below
53//
54
55static char *_get_keyid(certificate_t *cert) {
56    // get the keyid
57    x509_t *x509 = (x509_t *)cert;
58    chunk_t keyid = x509->get_subjectKeyIdentifier(x509);
59    chunk_t string = chunk_to_hex(keyid, NULL, 0);
60    return (char *)string.ptr;
61}
62
63static chunk_t _passphrase_callback(void *user, int try) {
64    /* Get a password from stdin and return it as a chunk_t.  If too many tries
65     * have occurred or there is any other problem, return an empty chunk_t,
66     * which libstrongswan takes as giving up.  The chunk is alloated here
67     * (inside getline), and presumably freed by libstrongswan. User points to
68     * a cb_opts struct, which affects this routine in the obvious ways.
69     */
70    /* Configuration options */
71    struct cb_opts *opts = (struct cb_opts *) user;
72    chunk_t rv = chunk_empty;   /* Return value, starts empty */
73
74    if (try -1 < opts->tries ) {
75        struct termios t;   /* Terminal settings */
76        size_t len = 0;     /* Length of string from getline */
77        tcflag_t orig = 0;  /* Holds the original local flags (echo in here) */
78
79        if (!opts->use_echo) {
80            /* Use tc{get,set}attr to turn echo off and restore the intial
81             * echo settings */
82            if (!tcgetattr(0, &t)) {
83                orig = t.c_lflag;
84
85                t.c_lflag &= ~ECHO;
86                if ( tcsetattr(0, TCSANOW, &t) ) {
87                    perror("Cannot turn off echo");
88                    return rv;
89                }
90            }
91            else {
92                perror("Cannot turn get attributes to off echo");
93                return rv;
94            }
95        }
96        if (opts->use_prompt) printf("%s", opts->prompt);
97
98        /* Because rv.ptr starts as NULL, getline allocates memory.  The size
99         * of the allocation returns in rv.len and the size of the string
100         * (including newline and NUL) is in len.  */
101        if ((rv.ptr = (u_char *) malloc(rv.len = PWLEN))) {
102            if ( fgets(rv.ptr, rv.len, stdin) ) {
103                /* Readjust the chunk_t's len field to the size of the string
104                 * w/o the newline or NUL */
105                /* would prefer strnlen, but no such luck in FBSD7 or earlier*/
106                size_t len = strlen(rv.ptr);
107
108                if (rv.ptr[len-2] == '\n') rv.len = len-2;
109                else rv.len = len -1;
110            }
111            else {
112                /* Read failed.  Deallocate and clear rv */
113                free(rv.ptr);
114                rv = chunk_empty;
115            }
116        }
117        else {
118            /* Failed malloc.  Restore rv to empty and return it */
119            perror("malloc");
120            rv = chunk_empty;
121            return rv;
122        }
123
124        if (!opts->use_echo ) {
125            /* Pop echo beck to its original setting. */
126            t.c_lflag = orig;
127
128            if ( tcsetattr(0, TCSANOW, &t) )
129                perror("Cannot restore echo setting?");
130
131            if (opts->use_prompt) printf("\n");
132        }
133    }
134    else fprintf(stderr, "Too many tries (%d)", try-1);
135    return rv;
136}
137
138/**
139 * Generate a private key.
140 */
141static private_key_t *_generate_key(void) {
142    private_key_t *key;
143    libabac_init();
144
145    // generate the key
146    key = lib->creds->create(
147        lib->creds,
148        CRED_PRIVATE_KEY, KEY_RSA,
149        BUILD_KEY_SIZE, 2048,
150        BUILD_END
151    );
152    if (key == NULL)
153        errx(1, "Key generation failed");
154
155    return key;
156}
157
158static char *_create_dn(char *cn) {
159
160#define DN "cn="
161
162    char *dn = abac_xmalloc(sizeof(DN) + strlen(cn));
163    memcpy(dn, DN, sizeof(DN));
164    strcat(dn, cn);
165
166    return dn;
167}
168
169/**
170 * Generate certificate.
171 *
172 * validity is measured in seconds (as of 0.2.0)
173 */
174static certificate_t *_generate_cert(private_key_t *private, char *cn, int validity) {
175    // build the DN
176    char *dn_string = _create_dn(cn);
177    libabac_init();
178
179    identification_t *id = identification_create_from_string(dn_string);
180    if (id == NULL)
181        errx(1, "couldn't create ID from DN %s", dn_string);
182    free(dn_string);
183
184    // get the public key
185    public_key_t *public = private->get_public_key(private);
186    if (public == NULL)
187        errx(1, "couldn't get public key from private key");
188
189    // create a serial (stolen from strongswan pki)
190    rng_t *rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
191    if (!rng)
192        errx(1, "no random number generator");
193
194    // random serial
195    chunk_t serial = abac_generate_serial();
196
197    // validity period
198    time_t not_before = time(NULL);
199    time_t not_after = not_before + validity;
200
201    // create!
202    certificate_t *cert = lib->creds->create(lib->creds,
203        CRED_CERTIFICATE, CERT_X509,
204        BUILD_SIGNING_KEY, private,
205        BUILD_PUBLIC_KEY, public,
206        BUILD_SUBJECT, id,
207        BUILD_NOT_BEFORE_TIME, not_before,
208        BUILD_NOT_AFTER_TIME, not_after,
209        BUILD_SERIAL, serial,
210        BUILD_DIGEST_ALG, HASH_SHA1,
211        BUILD_X509_FLAG, X509_CA,
212        BUILD_PATHLEN, X509_NO_PATH_LEN_CONSTRAINT,
213        BUILD_END
214    );
215    if (cert == NULL)
216        errx(1, "couldn't build cert :(");
217
218    DESTROY_IF(id);
219    DESTROY_IF(public);
220    free(serial.ptr);
221
222    return cert;
223}
224
225#define BYTES_PER_LINE 64
226
227// thx libstrongswan
228static void _encode_base64(FILE *out, chunk_t encoding) {
229    int start;
230
231    chunk_t b64 = chunk_to_base64(encoding, NULL);
232
233    fprintf(out, "-----BEGIN CERTIFICATE-----\n");
234
235    for (start = 0; start < b64.len; start += BYTES_PER_LINE) {
236        int left = b64.len - start;
237        int len = left < BYTES_PER_LINE ? left : BYTES_PER_LINE;
238        fwrite(b64.ptr + start, len, 1, out);
239        fprintf(out, "\n");
240    }
241
242    fprintf(out, "-----END CERTIFICATE-----\n");
243
244    free(b64.ptr);
245}
246/****************************************************************/
247int abac_id_idtype(abac_id_t *id)
248{
249    assert(id != NULL);
250    return id->idtype;
251}
252
253char* abac_id_idtype_string(abac_id_t *id)
254{
255    assert(id != NULL);
256    return abac_idtype_string(id->idtype);
257}
258
259char *abac_id_keyid(abac_id_t *id) {
260    assert(id != NULL);
261    return id->keyid;
262}
263
264char *abac_id_cn(abac_id_t *id) {
265    assert(id != NULL);
266    return id->cn;
267}
268
269
270char *abac_id_name(abac_id_t *id)
271{
272    assert(id != NULL);
273    if(USE("ABAC_CN") && !ABAC_IN_QUERY)
274        return abac_id_cn(id);
275    else return abac_id_keyid(id);
276}
277
278/**
279 * Get the issuer of an ID cert.
280 * Returns a malloc'd string that must be free'd.
281 */
282char *abac_id_issuer(abac_id_t *id) {
283    char *ret;
284    int rv = asprintf(&ret, "%Y", id->cert->get_issuer(id->cert));
285
286    if (rv < 0)
287        err(1, "couldn't malloc string for issuer\n");
288
289    return ret;
290}
291
292int abac_id_lastone(abac_id_t *ptr)
293{
294    assert(ptr);
295    if(ptr->refcount == 1)
296        return 1;
297    return 0;
298}
299
300
301/**
302 * Gets the subject DN of an ID cert.
303 * Returns a malloc'd string that must be free'd.
304 */
305char *abac_id_subject(abac_id_t *id) {
306    char *ret;
307    int rv = asprintf(&ret, "%Y", id->cert->get_subject(id->cert));
308
309    if (rv < 0)
310        err(1, "couldn't malloc string for subject\n");
311
312    return ret;
313}
314
315/**
316 * Get the validity period.
317 */
318void abac_id_validity(abac_id_t *id, time_t *not_before, time_t *not_after) {
319    id->cert->get_validity(id->cert, NULL, not_before, not_after);
320}
321
322certificate_t *abac_id_cert(abac_id_t *id) {
323    assert(id != NULL);
324
325    return id->cert;
326}
327
328private_key_t *abac_id_privkey(abac_id_t *id) {
329    assert(id != NULL);
330
331    return id->key;
332}
333
334int abac_id_has_privkey(abac_id_t *id) {
335    if(id->key !=0)
336       return 1;
337    return 0;
338}
339
340
341/**
342 * Write the ID cert to an open file pointer.
343 */
344void abac_id_write_cert(abac_id_t *id, FILE *out) {
345    assert(id != NULL);
346
347    chunk_t encoding = id->cert->get_encoding(id->cert);
348    _encode_base64(out, encoding);
349    free(encoding.ptr);
350}
351
352/**
353 * Write the private key to a file.
354 * Returns false if there's no private key loaded
355 */
356int abac_id_write_privkey(abac_id_t *id, FILE *out) {
357    int ret;
358    chunk_t encoding;
359
360    assert(id != NULL);
361
362    if (id->key == NULL)
363        return 0;
364
365    ret = id->key->get_encoding(id->key, KEY_PRIV_PEM, &encoding);
366    if (!ret)
367        errx(1, "Couldn't encode private key");
368
369    fwrite(encoding.ptr, encoding.len, 1, out);
370
371    free(encoding.ptr);
372    return 1;
373}
374
375/**
376 * Get a DER-encoded chunk representing the cert.
377 */
378abac_chunk_t abac_id_cert_chunk(abac_id_t *id) {
379    assert(id->cert);
380    chunk_t encoding = id->cert->get_encoding(id->cert);
381    abac_chunk_t ret = { encoding.ptr, encoding.len };
382    return ret;
383}
384
385/**
386 * Copy an abac ID. Actually just increases its reference count.
387 */
388abac_id_t *abac_id_dup(abac_id_t *id) {
389    if(id)
390        ++id->refcount;
391    return id;
392}
393
394void abac_id_free(abac_id_t *id) {
395    if (id == NULL)
396        return;
397
398    --id->refcount;
399    if (id->refcount > 0)
400        return;
401
402    // free once the reference count reaches 0
403    DESTROY_IF(id->cert);
404    DESTROY_IF(id->key);
405
406    free(id->keyid);
407    free(id->cn);
408    free(id);
409}
410
411/****************************************************************/
412/**
413 * Load private key for a cert.
414 */
415int abac_id_load_privkey_file(abac_id_t *id, char *filename) {
416    struct cb_opts c_opts = { 1, 0, 3, "Key password:" };
417
418    assert(id != NULL);
419
420    // load signer key
421    private_key_t *key = lib->creds->create(lib->creds,
422        CRED_PRIVATE_KEY, KEY_RSA,
423        BUILD_FROM_FILE, filename,
424        /* Ask for password if the key's encrypted */
425        BUILD_PASSPHRASE_CALLBACK, _passphrase_callback, &c_opts,
426        BUILD_END
427    );
428    if (key == NULL)
429        return 0;
430
431    id->key = key;
432    return 1;
433}
434
435int abac_id_load_privkey_chunk(abac_id_t *id, chunk_t chunk) {
436    struct cb_opts c_opts = { 1, 0, 3, "Key password:" };
437
438    assert(id != NULL);
439
440    // load signer key
441    private_key_t *key = lib->creds->create(lib->creds,
442        CRED_PRIVATE_KEY, KEY_RSA,
443        BUILD_BLOB_PEM, chunk,
444        /* Ask for password if the key's encrypted */
445        BUILD_PASSPHRASE_CALLBACK, _passphrase_callback, &c_opts,
446        BUILD_END
447    );
448    if (key == NULL)
449        return 0;
450
451    id->key = key;
452    return 1;
453}
454
455
456/****************************************************************/
457abac_id_t *abac_id_new(int idtype,char *keyid, char *cn, 
458certificate_t *cert)
459{
460    abac_id_t *id = abac_xmalloc(sizeof(abac_id_t));
461    id->idtype = idtype;
462    id->keyid = abac_xstrdup(keyid);
463    id->cn = abac_xstrdup(cn);
464    id->cert = cert;
465    id->key = NULL;
466    id->refcount = 1;
467    if(debug) printf("abac_id_new: made a new id, %ld\n",(long) id);
468    return id;
469}
470
471abac_id_t *abac_id_keyid_new(char *keyid, char *cn, certificate_t *cert)
472{
473    return abac_id_new(e_KEYID,keyid,cn,cert); 
474}
475
476static abac_id_t *_abac_id_from_cert(certificate_t *cert) {
477    abac_id_t *id = abac_xmalloc(sizeof(abac_id_t));
478    id->idtype = e_KEYID;
479    id->keyid = NULL;
480    id->cn = NULL;
481    id->cert = cert;
482    id->key = NULL;
483
484    id->keyid = _get_keyid(id->cert);
485
486    // get the CN from the cert
487    id_part_t type;
488    chunk_t data;
489
490    identification_t *cert_id = id->cert->get_subject(id->cert);
491    enumerator_t *id_enum = cert_id->create_part_enumerator(cert_id);
492    while (id_enum->enumerate(id_enum, &type, &data))
493        if (type == ID_PART_RDN_CN) {
494            id->cn = abac_xmalloc(data.len + 1);
495            memcpy(id->cn, data.ptr, data.len);
496            id->cn[data.len] = 0;
497        }
498    id_enum->destroy(id_enum);
499
500    id->refcount = 1;
501
502    return id;
503}
504
505/**
506 * Load an ID cert from a file.
507 */
508abac_id_t *abac_id_from_file(char *filename) {
509
510    certificate_t *cert = lib->creds->create(lib->creds,
511        CRED_CERTIFICATE, CERT_X509,
512        BUILD_FROM_FILE, filename,
513        BUILD_X509_FLAG, X509_AA,
514        BUILD_END
515    );
516
517    if (cert == NULL)
518        return NULL;
519
520    return _abac_id_from_cert(cert);
521}
522
523/**
524 * Load an ID cert from a chunk.
525 */
526abac_id_t *abac_id_from_chunk(abac_chunk_t achunk) {
527    chunk_t chunk = { .ptr = achunk.ptr, .len = achunk.len };
528
529    certificate_t *cert = lib->creds->create(lib->creds,
530        CRED_CERTIFICATE, CERT_X509,
531        BUILD_BLOB_ASN1_DER, chunk,
532        BUILD_X509_FLAG, X509_AA,
533        BUILD_END
534    );
535
536    if (cert == NULL)
537        return NULL;
538
539    return _abac_id_from_cert(cert);
540}
541
542/**
543 * Generate an ID with the specified CN and validity.
544 *
545 * validity is measured in seconds (as of 0.2.0)
546 */
547int abac_id_generate(abac_id_t **ret, char *cn, int validity) {
548    if (cn == NULL || !abac_validate_clean_name(cn))
549        return ABAC_ID_GENERATE_INVALID_CN;
550
551    if(debug) printf("abac_id_generate: generating id with cn(%s)\n",cn);
552
553    if (validity < 0)
554        return ABAC_ID_GENERATE_INVALID_VALIDITY;
555
556    if (validity == 0) validity = 1080 * 86400;
557
558    abac_id_t *id = abac_xmalloc(sizeof(abac_id_t));
559
560    char *tmp=NULL;
561    asprintf(&tmp,"p%s",cn);
562    id->idtype = e_KEYID;
563    id->cn = tmp;
564    id->key = _generate_key();
565    id->cert = _generate_cert(id->key, cn, validity);
566    id->keyid = _get_keyid(id->cert);
567
568    id->refcount = 1;
569
570    *ret = id;
571    return ABAC_ID_SUCCESS;
572}
573
574/*****************************************************************/
Note: See TracBrowser for help on using the repository browser.