source: libabac/abac_id.c @ 9b43fc3

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

1) add code to load id into context with both cert chunk and privkey
chunk
2) test out the sample scripts in swig/perl and swig/python

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