source: libabac/abac_id.c @ 5d06689

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

1) modify abac.hh and added abac_c.c to unify the c and c++ api

interface (almost)

2) add new API
3) tweak the tests
4) filling missing code for abac_verifier_load_attribute_cert_attribute

  • Property mode set to 100644
File size: 14.1 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 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        BUILD_PATHLEN, X509_NO_PATH_LEN_CONSTRAINT,
212        BUILD_END
213    );
214    if (cert == NULL)
215        errx(1, "couldn't build cert :(");
216
217    DESTROY_IF(id);
218    DESTROY_IF(public);
219    free(serial.ptr);
220
221    return cert;
222}
223
224#define BYTES_PER_LINE 64
225
226// thx libstrongswan
227static void _encode_base64(FILE *out, chunk_t encoding) {
228    int start;
229
230    chunk_t b64 = chunk_to_base64(encoding, NULL);
231
232    fprintf(out, "-----BEGIN CERTIFICATE-----\n");
233
234    for (start = 0; start < b64.len; start += BYTES_PER_LINE) {
235        int left = b64.len - start;
236        int len = left < BYTES_PER_LINE ? left : BYTES_PER_LINE;
237        fwrite(b64.ptr + start, len, 1, out);
238        fprintf(out, "\n");
239    }
240
241    fprintf(out, "-----END CERTIFICATE-----\n");
242
243    free(b64.ptr);
244}
245/****************************************************************/
246int abac_id_idtype(abac_id_t *id)
247{
248    assert(id != NULL);
249    return id->idtype;
250}
251
252char* abac_id_idtype_string(abac_id_t *id)
253{
254    assert(id != NULL);
255    return abac_idtype_string(id->idtype);
256}
257
258char *abac_id_keyid(abac_id_t *id) {
259    assert(id != NULL);
260    return id->keyid;
261}
262
263char *abac_id_cn(abac_id_t *id) {
264    assert(id != NULL);
265    return id->cn;
266}
267
268
269char *abac_id_name(abac_id_t *id)
270{
271    assert(id != NULL);
272    if(USE("ABAC_CN"))
273        return abac_id_cn(id);
274    else return abac_id_keyid(id);
275}
276
277char *abac_id_string(abac_id_t *id) 
278{
279    char *tmp=NULL;
280    if(abac_id_has_privkey(id)) 
281        asprintf(&tmp,"(%s,%s,y)",abac_id_name(id),abac_id_idtype_string(id));
282        else asprintf(&tmp,"(%s,%s,n)",abac_id_name(id),abac_id_idtype_string(id));
283    return tmp;
284}
285
286/**
287 * Get the issuer of an ID cert.
288 * Returns a malloc'd string that must be free'd.
289 */
290char *abac_id_issuer(abac_id_t *id) {
291    char *ret;
292    int rv = asprintf(&ret, "%Y", id->cert->get_issuer(id->cert));
293
294    if (rv < 0)
295        err(1, "couldn't malloc string for issuer\n");
296
297    return ret;
298}
299
300int abac_id_lastone(abac_id_t *ptr)
301{
302    assert(ptr);
303    if(ptr->refcount == 1)
304        return 1;
305    return 0;
306}
307
308
309/**
310 * Gets the subject DN of an ID cert.
311 * Returns a malloc'd string that must be free'd.
312 */
313char *abac_id_subject(abac_id_t *id) {
314    char *ret;
315    int rv = asprintf(&ret, "%Y", id->cert->get_subject(id->cert));
316
317    if (rv < 0)
318        err(1, "couldn't malloc string for subject\n");
319
320    return ret;
321}
322
323/**
324 * Get the validity period.
325 */
326void abac_id_validity(abac_id_t *id, time_t *not_before, time_t *not_after) {
327    id->cert->get_validity(id->cert, NULL, not_before, not_after);
328}
329
330certificate_t *abac_id_cert(abac_id_t *id) {
331    assert(id != NULL);
332
333    return id->cert;
334}
335
336private_key_t *abac_id_privkey(abac_id_t *id) {
337    assert(id != NULL);
338
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 = id->cert->get_encoding(id->cert);
356    _encode_base64(out, encoding);
357    free(encoding.ptr);
358}
359
360/**
361 * Write the private key to a file.
362 * Returns false if there's no private key loaded
363 */
364int abac_id_write_privkey(abac_id_t *id, FILE *out) {
365    int ret;
366    chunk_t encoding;
367
368    assert(id != NULL);
369
370    if (id->key == NULL)
371        return 0;
372
373    ret = id->key->get_encoding(id->key, KEY_PRIV_PEM, &encoding);
374    if (!ret)
375        errx(1, "Couldn't encode private key");
376
377    fwrite(encoding.ptr, encoding.len, 1, out);
378
379    free(encoding.ptr);
380    return 1;
381}
382
383/**
384 * Get a DER-encoded chunk representing the cert.
385 */
386abac_chunk_t abac_id_cert_chunk(abac_id_t *id) {
387    assert(id->cert);
388    chunk_t encoding = id->cert->get_encoding(id->cert);
389    abac_chunk_t ret = { encoding.ptr, encoding.len };
390    return ret;
391}
392
393/**
394 * Copy an abac ID. Actually just increases its reference count.
395 */
396abac_id_t *abac_id_dup(abac_id_t *id) {
397    if(id)
398        ++id->refcount;
399    return id;
400}
401
402void abac_id_free(abac_id_t *id) {
403    if (id == NULL)
404        return;
405
406    --id->refcount;
407    if (id->refcount > 0)
408        return;
409
410    // free once the reference count reaches 0
411    DESTROY_IF(id->cert);
412    DESTROY_IF(id->key);
413
414    free(id->keyid);
415    free(id->cn);
416    free(id);
417}
418
419/****************************************************************/
420/**
421 * Load private key for a cert.
422 */
423int abac_id_load_privkey_file(abac_id_t *id, char *filename) {
424    struct cb_opts c_opts = { 1, 0, 3, "Key password:" };
425
426    assert(id != NULL);
427
428    // load signer key
429    private_key_t *key = lib->creds->create(lib->creds,
430        CRED_PRIVATE_KEY, KEY_RSA,
431        BUILD_FROM_FILE, filename,
432        /* Ask for password if the key's encrypted */
433        BUILD_PASSPHRASE_CALLBACK, _passphrase_callback, &c_opts,
434        BUILD_END
435    );
436    if (key == NULL)
437        return 0;
438
439    id->key = key;
440    return 1;
441}
442
443int abac_id_load_privkey_chunk(abac_id_t *id, chunk_t chunk) {
444    struct cb_opts c_opts = { 1, 0, 3, "Key password:" };
445
446    assert(id != NULL);
447
448    // load signer key
449    private_key_t *key = lib->creds->create(lib->creds,
450        CRED_PRIVATE_KEY, KEY_RSA,
451        BUILD_BLOB_PEM, chunk,
452        /* Ask for password if the key's encrypted */
453        BUILD_PASSPHRASE_CALLBACK, _passphrase_callback, &c_opts,
454        BUILD_END
455    );
456    if (key == NULL)
457        return 0;
458
459    id->key = key;
460    return 1;
461}
462
463
464/****************************************************************/
465abac_id_t *abac_id_new(int idtype,char *keyid, char *cn, 
466certificate_t *cert)
467{
468    abac_id_t *id = abac_xmalloc(sizeof(abac_id_t));
469    id->idtype = idtype;
470    id->keyid = abac_xstrdup(keyid);
471    id->cn = abac_xstrdup(cn);
472    id->cert = cert;
473    id->key = NULL;
474    id->refcount = 1;
475    if(debug) printf("abac_id_new: made a new id, %ld\n",(long) id);
476    return id;
477}
478
479abac_id_t *abac_id_keyid_new(char *keyid, char *cn, certificate_t *cert)
480{
481    return abac_id_new(e_KEYID,keyid,cn,cert); 
482}
483
484static abac_id_t *_abac_id_from_cert(certificate_t *cert) {
485    abac_id_t *id = abac_xmalloc(sizeof(abac_id_t));
486    id->idtype = e_KEYID;
487    id->keyid = NULL;
488    id->cn = NULL;
489    id->cert = cert;
490    id->key = NULL;
491
492    id->keyid = _get_keyid(id->cert);
493
494    // get the CN from the cert
495    id_part_t type;
496    chunk_t data;
497
498    identification_t *cert_id = id->cert->get_subject(id->cert);
499    enumerator_t *id_enum = cert_id->create_part_enumerator(cert_id);
500    while (id_enum->enumerate(id_enum, &type, &data))
501        if (type == ID_PART_RDN_CN) {
502            id->cn = abac_xmalloc(data.len + 1);
503            memcpy(id->cn, data.ptr, data.len);
504            id->cn[data.len] = 0;
505        }
506    id_enum->destroy(id_enum);
507
508    id->refcount = 1;
509
510    return id;
511}
512
513/**
514 * Load an ID cert from a file.
515 */
516abac_id_t *abac_id_from_file(char *filename) {
517
518    certificate_t *cert = lib->creds->create(lib->creds,
519        CRED_CERTIFICATE, CERT_X509,
520        BUILD_FROM_FILE, filename,
521        BUILD_X509_FLAG, X509_AA,
522        BUILD_END
523    );
524
525    if (cert == NULL)
526        return NULL;
527
528    return _abac_id_from_cert(cert);
529}
530
531/**
532 * Load an ID cert from a chunk.
533 */
534abac_id_t *abac_id_from_chunk(abac_chunk_t achunk) {
535    chunk_t chunk = { .ptr = achunk.ptr, .len = achunk.len };
536
537    certificate_t *cert = lib->creds->create(lib->creds,
538        CRED_CERTIFICATE, CERT_X509,
539        BUILD_BLOB_ASN1_DER, chunk,
540        BUILD_X509_FLAG, X509_AA,
541        BUILD_END
542    );
543
544    if (cert == NULL)
545        return NULL;
546
547    return _abac_id_from_cert(cert);
548}
549
550/**
551 * Generate an ID with the specified CN and validity.
552 *
553 * validity is measured in seconds (as of 0.2.0)
554 */
555int abac_id_generate(abac_id_t **ret, char *cn, int validity) {
556    if (cn == NULL || !abac_validate_clean_name(cn))
557        return ABAC_ID_GENERATE_INVALID_CN;
558
559    if(debug) printf("abac_id_generate: generating id with cn(%s)\n",cn);
560
561    if (validity < 0)
562        return ABAC_ID_GENERATE_INVALID_VALIDITY;
563
564    if (validity == 0) validity = 1080 * 86400;
565
566    abac_id_t *id = abac_xmalloc(sizeof(abac_id_t));
567
568    char *tmp=NULL;
569    asprintf(&tmp,"p%s",cn);
570    id->idtype = e_KEYID;
571    id->cn = tmp;
572    id->key = _generate_key();
573    id->cert = _generate_cert(id->key, cn, validity);
574    id->keyid = _get_keyid(id->cert);
575
576    id->refcount = 1;
577
578    *ret = id;
579    return ABAC_ID_SUCCESS;
580}
581
582/*****************************************************************/
Note: See TracBrowser for help on using the repository browser.