source: libabac/abac_id.c @ 888df49

mei_rt2mei_rt2_fix_1
Last change on this file since 888df49 was 2efdff5, checked in by Mei <mei@…>, 12 years ago

1) fix the missing check for 'This' rt2.y when called from creddy/prover

combo

2) patch up the stringify of abac_term that is of time type.
3) update the testing to reflect the changes to baseline output

  • 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 * Copy an abac ID. Actually just increases its reference count.
401 */
402abac_id_t *abac_id_dup(abac_id_t *id) {
403    if(id)
404        ++id->refcount;
405    return id;
406}
407
408void abac_id_free(abac_id_t *id) {
409    if (id == NULL)
410        return;
411
412    --id->refcount;
413    if (id->refcount > 0)
414        return;
415
416    // free once the reference count reaches 0
417    DESTROY_IF(id->cert);
418    DESTROY_IF(id->key);
419
420    free(id->keyid);
421    free(id->cn);
422    free(id);
423}
424
425/****************************************************************/
426/**
427 * Load private key for a cert.
428 */
429int abac_id_load_privkey_file(abac_id_t *id, char *filename) {
430    struct cb_opts c_opts = { 1, 0, 3, "Key password:" };
431
432    assert(id != NULL);
433
434    // load signer key
435    private_key_t *key = lib->creds->create(lib->creds,
436        CRED_PRIVATE_KEY, KEY_RSA,
437        BUILD_FROM_FILE, filename,
438/*NOT in 4.6.4 Ask for password if the key's encrypted
439        BUILD_PASSPHRASE_CALLBACK, _passphrase_callback, &c_opts,
440*/
441        BUILD_END
442    );
443    if (key == NULL)
444        return 0;
445
446    id->key = key;
447    return 1;
448}
449
450int abac_id_load_privkey_chunk(abac_id_t *id, chunk_t chunk) {
451    struct cb_opts c_opts = { 1, 0, 3, "Key password:" };
452
453    assert(id != NULL);
454
455    // load signer key
456    private_key_t *key = lib->creds->create(lib->creds,
457        CRED_PRIVATE_KEY, KEY_RSA,
458        BUILD_BLOB_PEM, chunk,
459/* NOT in 4.6.4 Ask for password if the key's encrypted
460        BUILD_PASSPHRASE_CALLBACK, _passphrase_callback, &c_opts,
461*/
462        BUILD_END
463    );
464    if (key == NULL)
465        return 0;
466
467    id->key = key;
468    return 1;
469}
470
471/**
472 * Default private key filename. Value must be freed by caller.
473 */
474char *abac_id_privkey_filename(abac_id_t *id) {
475    assert(id != NULL);
476    assert(id->cn != NULL);
477
478    /* this cn has pCN attached to it */
479    char *filename = NULL;
480    char* ptr=id->cn;
481    asprintf(&filename, "%s%s",(ptr+1),KEY_SUFFIX);
482    return filename;
483}
484
485/**
486 * Get the default filename for the cert. Value must be freed by caller.
487 */
488char *abac_id_cert_filename(abac_id_t *id) {
489    assert(id != NULL);
490    assert(id->cn != NULL);
491
492    /* this cn has pCN attached to it */
493    char *filename = NULL;
494    char* ptr=id->cn;
495    asprintf(&filename, "%s%s",(ptr+1),CERT_SUFFIX);
496    return filename;
497}
498
499
500
501/****************************************************************/
502abac_id_t *abac_id_new(int idtype,char *keyid, char *cn, 
503certificate_t *cert)
504{
505    abac_id_t *id = abac_xmalloc(sizeof(abac_id_t));
506    id->idtype = idtype;
507    id->keyid = abac_xstrdup(keyid);
508    id->cn = abac_xstrdup(cn);
509    id->cert = cert;
510    id->key = NULL;
511    id->refcount = 1;
512    if(debug) printf("abac_id_new: made a new id, %ld\n",(long) id);
513    return id;
514}
515
516abac_id_t *abac_id_keyid_new(char *keyid, char *cn, certificate_t *cert)
517{
518    return abac_id_new(e_KEYID,keyid,cn,cert); 
519}
520
521static abac_id_t *_abac_id_from_cert(certificate_t *cert) {
522    abac_id_t *id = abac_xmalloc(sizeof(abac_id_t));
523    id->idtype = e_KEYID;
524    id->keyid = NULL;
525    id->cn = NULL;
526    id->cert = cert;
527    id->key = NULL;
528
529    id->keyid = _get_keyid(id->cert);
530
531    // get the CN from the cert
532    id_part_t type;
533    chunk_t data;
534
535    identification_t *cert_id = id->cert->get_subject(id->cert);
536    enumerator_t *id_enum = cert_id->create_part_enumerator(cert_id);
537    while (id_enum->enumerate(id_enum, &type, &data))
538        if (type == ID_PART_RDN_CN) {
539            id->cn = abac_xmalloc(data.len + 1);
540            memcpy(id->cn, data.ptr, data.len);
541            id->cn[data.len] = 0;
542        }
543    id_enum->destroy(id_enum);
544
545    id->refcount = 1;
546
547    return id;
548}
549
550/**
551 * Load an ID cert from a file.
552 * this one does not add id into id hash list
553 */
554abac_id_t *abac_id_from_file(char *filename) {
555
556    /* make sure file exists */
557    if(!file_exist(filename)) return NULL;
558
559    libabac_init();
560    certificate_t *cert = lib->creds->create(lib->creds,
561        CRED_CERTIFICATE, CERT_X509,
562        BUILD_FROM_FILE, filename,
563        BUILD_X509_FLAG, X509_AA,
564        BUILD_END
565    );
566
567    if (cert == NULL)
568        return NULL;
569
570    return _abac_id_from_cert(cert);
571}
572
573/**
574 * Load an ID cert from a chunk.
575 */
576abac_id_t *abac_id_from_chunk(abac_chunk_t achunk) {
577    chunk_t chunk = { .ptr = achunk.ptr, .len = achunk.len };
578
579    certificate_t *cert = lib->creds->create(lib->creds,
580        CRED_CERTIFICATE, CERT_X509,
581        BUILD_BLOB_ASN1_DER, chunk,
582        BUILD_X509_FLAG, X509_AA,
583        BUILD_END
584    );
585
586    if (cert == NULL)
587        return NULL;
588
589    return _abac_id_from_cert(cert);
590}
591
592/**
593 * Generate an ID with the specified CN and validity.
594 *
595 * validity is measured in seconds (as of 0.2.0)
596 */
597int abac_id_generate(abac_id_t **ret, char *cn, int validity) {
598    if (cn == NULL || !abac_validate_clean_name(cn))
599        return ABAC_ID_GENERATE_INVALID_CN;
600
601    if(debug) printf("abac_id_generate: generating id with cn(%s)\n",cn);
602
603    if (validity < 0)
604        return ABAC_ID_GENERATE_INVALID_VALIDITY;
605
606    if (validity == 0) validity = 1080 * 86400;
607
608    abac_id_t *id = abac_xmalloc(sizeof(abac_id_t));
609
610    char *tmp=NULL;
611    asprintf(&tmp,"p%s",cn);
612    id->idtype = e_KEYID;
613    id->cn = tmp;
614    id->key = _generate_key();
615    id->cert = _generate_cert(id->key, cn, validity);
616    id->keyid = _get_keyid(id->cert);
617
618    id->refcount = 1;
619
620    *ret = id;
621    return ABAC_ID_SUCCESS;
622}
623
624/*****************************************************************/
Note: See TracBrowser for help on using the repository browser.