source: libabac/abac_id.c @ 137b55f

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

1) upgraded to use strongswan-4.6.4

  • Property mode set to 100644
File size: 14.2 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
335    return id->cert;
336}
337
338private_key_t *abac_id_privkey(abac_id_t *id) {
339    assert(id != NULL);
340
341    return id->key;
342}
343
344int abac_id_has_privkey(abac_id_t *id) {
345    if(id->key !=0)
346       return 1;
347    return 0;
348}
349
350
351/**
352 * Write the ID cert to an open file pointer.
353 */
354void abac_id_write_cert(abac_id_t *id, FILE *out) {
355    assert(id != NULL);
356
357    chunk_t encoding = chunk_empty;
358    int rc=id->cert->get_encoding(id->cert,CERT_ASN1_DER,&encoding);
359    if(rc) {
360        _encode_base64(out, encoding);
361        free(encoding.ptr);
362    }
363}
364
365/**
366 * Write the private key to a file.
367 * Returns false if there's no private key loaded
368 */
369int abac_id_write_privkey(abac_id_t *id, FILE *out) {
370    int ret;
371    chunk_t encoding;
372
373    assert(id != NULL);
374
375    if (id->key == NULL)
376        return 0;
377
378    ret = id->key->get_encoding(id->key, PRIVKEY_PEM, &encoding);
379    if (!ret)
380        errx(1, "Couldn't encode private key");
381
382    fwrite(encoding.ptr, encoding.len, 1, out);
383
384    free(encoding.ptr);
385    return 1;
386}
387
388/**
389 * Get a DER-encoded chunk representing the cert.
390 */
391abac_chunk_t abac_id_cert_chunk(abac_id_t *id) {
392    assert(id->cert);
393
394    chunk_t encoding = chunk_empty;
395    int rc= id->cert->get_encoding(id->cert, CERT_ASN1_DER, &encoding);
396    abac_chunk_t ret = { encoding.ptr, encoding.len };
397
398    return ret;
399}
400
401/**
402 * Copy an abac ID. Actually just increases its reference count.
403 */
404abac_id_t *abac_id_dup(abac_id_t *id) {
405    if(id)
406        ++id->refcount;
407    return id;
408}
409
410void abac_id_free(abac_id_t *id) {
411    if (id == NULL)
412        return;
413
414    --id->refcount;
415    if (id->refcount > 0)
416        return;
417
418    // free once the reference count reaches 0
419    DESTROY_IF(id->cert);
420    DESTROY_IF(id->key);
421
422    free(id->keyid);
423    free(id->cn);
424    free(id);
425}
426
427/****************************************************************/
428/**
429 * Load private key for a cert.
430 */
431int abac_id_load_privkey_file(abac_id_t *id, char *filename) {
432    struct cb_opts c_opts = { 1, 0, 3, "Key password:" };
433
434    assert(id != NULL);
435
436    // load signer key
437    private_key_t *key = lib->creds->create(lib->creds,
438        CRED_PRIVATE_KEY, KEY_RSA,
439        BUILD_FROM_FILE, filename,
440/*NOT in 4.6.4 Ask for password if the key's encrypted
441        BUILD_PASSPHRASE_CALLBACK, _passphrase_callback, &c_opts,
442*/
443        BUILD_END
444    );
445    if (key == NULL)
446        return 0;
447
448    id->key = key;
449    return 1;
450}
451
452int abac_id_load_privkey_chunk(abac_id_t *id, chunk_t chunk) {
453    struct cb_opts c_opts = { 1, 0, 3, "Key password:" };
454
455    assert(id != NULL);
456
457    // load signer key
458    private_key_t *key = lib->creds->create(lib->creds,
459        CRED_PRIVATE_KEY, KEY_RSA,
460        BUILD_BLOB_PEM, chunk,
461/* NOT in 4.6.4 Ask for password if the key's encrypted
462        BUILD_PASSPHRASE_CALLBACK, _passphrase_callback, &c_opts,
463*/
464        BUILD_END
465    );
466    if (key == NULL)
467        return 0;
468
469    id->key = key;
470    return 1;
471}
472
473
474/****************************************************************/
475abac_id_t *abac_id_new(int idtype,char *keyid, char *cn, 
476certificate_t *cert)
477{
478    abac_id_t *id = abac_xmalloc(sizeof(abac_id_t));
479    id->idtype = idtype;
480    id->keyid = abac_xstrdup(keyid);
481    id->cn = abac_xstrdup(cn);
482    id->cert = cert;
483    id->key = NULL;
484    id->refcount = 1;
485    if(debug) printf("abac_id_new: made a new id, %ld\n",(long) id);
486    return id;
487}
488
489abac_id_t *abac_id_keyid_new(char *keyid, char *cn, certificate_t *cert)
490{
491    return abac_id_new(e_KEYID,keyid,cn,cert); 
492}
493
494static abac_id_t *_abac_id_from_cert(certificate_t *cert) {
495    abac_id_t *id = abac_xmalloc(sizeof(abac_id_t));
496    id->idtype = e_KEYID;
497    id->keyid = NULL;
498    id->cn = NULL;
499    id->cert = cert;
500    id->key = NULL;
501
502    id->keyid = _get_keyid(id->cert);
503
504    // get the CN from the cert
505    id_part_t type;
506    chunk_t data;
507
508    identification_t *cert_id = id->cert->get_subject(id->cert);
509    enumerator_t *id_enum = cert_id->create_part_enumerator(cert_id);
510    while (id_enum->enumerate(id_enum, &type, &data))
511        if (type == ID_PART_RDN_CN) {
512            id->cn = abac_xmalloc(data.len + 1);
513            memcpy(id->cn, data.ptr, data.len);
514            id->cn[data.len] = 0;
515        }
516    id_enum->destroy(id_enum);
517
518    id->refcount = 1;
519
520    return id;
521}
522
523/**
524 * Load an ID cert from a file.
525 */
526abac_id_t *abac_id_from_file(char *filename) {
527
528    certificate_t *cert = lib->creds->create(lib->creds,
529        CRED_CERTIFICATE, CERT_X509,
530        BUILD_FROM_FILE, filename,
531        BUILD_X509_FLAG, X509_AA,
532        BUILD_END
533    );
534
535    if (cert == NULL)
536        return NULL;
537
538    return _abac_id_from_cert(cert);
539}
540
541/**
542 * Load an ID cert from a chunk.
543 */
544abac_id_t *abac_id_from_chunk(abac_chunk_t achunk) {
545    chunk_t chunk = { .ptr = achunk.ptr, .len = achunk.len };
546
547    certificate_t *cert = lib->creds->create(lib->creds,
548        CRED_CERTIFICATE, CERT_X509,
549        BUILD_BLOB_ASN1_DER, chunk,
550        BUILD_X509_FLAG, X509_AA,
551        BUILD_END
552    );
553
554    if (cert == NULL)
555        return NULL;
556
557    return _abac_id_from_cert(cert);
558}
559
560/**
561 * Generate an ID with the specified CN and validity.
562 *
563 * validity is measured in seconds (as of 0.2.0)
564 */
565int abac_id_generate(abac_id_t **ret, char *cn, int validity) {
566    if (cn == NULL || !abac_validate_clean_name(cn))
567        return ABAC_ID_GENERATE_INVALID_CN;
568
569    if(debug) printf("abac_id_generate: generating id with cn(%s)\n",cn);
570
571    if (validity < 0)
572        return ABAC_ID_GENERATE_INVALID_VALIDITY;
573
574    if (validity == 0) validity = 1080 * 86400;
575
576    abac_id_t *id = abac_xmalloc(sizeof(abac_id_t));
577
578    char *tmp=NULL;
579    asprintf(&tmp,"p%s",cn);
580    id->idtype = e_KEYID;
581    id->cn = tmp;
582    id->key = _generate_key();
583    id->cert = _generate_cert(id->key, cn, validity);
584    id->keyid = _get_keyid(id->cert);
585
586    id->refcount = 1;
587
588    *ret = id;
589    return ABAC_ID_SUCCESS;
590}
591
592/*****************************************************************/
Note: See TracBrowser for help on using the repository browser.