source: libabac/abac_id.c @ ca72963

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

1) wrap up refactoring to move all the code gen to abac structure
2) all original testsuite passed
3) add couple more ui calls in abac.hh ie. manage constraint's

creation, hook to dump yap db.

  • Property mode set to 100644
File size: 12.9 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_keyid(abac_id_t *id) {
253    assert(id != NULL);
254    return id->keyid;
255}
256
257char *abac_id_cn(abac_id_t *id) {
258    assert(id != NULL);
259    return id->cn;
260}
261
262/**
263 * Get the issuer of an ID cert.
264 * Returns a malloc'd string that must be free'd.
265 */
266char *abac_id_issuer(abac_id_t *id) {
267    char *ret;
268    int rv = asprintf(&ret, "%Y", id->cert->get_issuer(id->cert));
269
270    if (rv < 0)
271        err(1, "couldn't malloc string for issuer\n");
272
273    return ret;
274}
275
276int abac_id_lastone(abac_id_t *ptr)
277{
278    assert(ptr);
279    if(ptr->refcount == 1)
280        return 1;
281    return 0;
282}
283
284
285/**
286 * Gets the subject DN of an ID cert.
287 * Returns a malloc'd string that must be free'd.
288 */
289char *abac_id_subject(abac_id_t *id) {
290    char *ret;
291    int rv = asprintf(&ret, "%Y", id->cert->get_subject(id->cert));
292
293    if (rv < 0)
294        err(1, "couldn't malloc string for subject\n");
295
296    return ret;
297}
298
299/**
300 * Get the validity period.
301 */
302void abac_id_validity(abac_id_t *id, time_t *not_before, time_t *not_after) {
303    id->cert->get_validity(id->cert, NULL, not_before, not_after);
304}
305
306certificate_t *abac_id_cert(abac_id_t *id) {
307    assert(id != NULL);
308
309    return id->cert;
310}
311
312private_key_t *abac_id_privkey(abac_id_t *id) {
313    assert(id != NULL);
314
315    return id->key;
316}
317
318int abac_id_has_privkey(abac_id_t *id) {
319    if(id->key !=0)
320       return 1;
321    return 0;
322}
323
324
325/**
326 * Write the ID cert to an open file pointer.
327 */
328void abac_id_write_cert(abac_id_t *id, FILE *out) {
329    assert(id != NULL);
330
331    chunk_t encoding = id->cert->get_encoding(id->cert);
332    _encode_base64(out, encoding);
333    free(encoding.ptr);
334}
335
336/**
337 * Write the private key to a file.
338 * Returns false if there's no private key loaded
339 */
340int abac_id_write_privkey(abac_id_t *id, FILE *out) {
341    int ret;
342    chunk_t encoding;
343
344    assert(id != NULL);
345
346    if (id->key == NULL)
347        return 0;
348
349    ret = id->key->get_encoding(id->key, KEY_PRIV_PEM, &encoding);
350    if (!ret)
351        errx(1, "Couldn't encode private key");
352
353    fwrite(encoding.ptr, encoding.len, 1, out);
354
355    free(encoding.ptr);
356    return 1;
357}
358
359/**
360 * Get a DER-encoded chunk representing the cert.
361 */
362abac_chunk_t abac_id_cert_chunk(abac_id_t *id) {
363    assert(id->cert);
364    chunk_t encoding = id->cert->get_encoding(id->cert);
365    abac_chunk_t ret = { encoding.ptr, encoding.len };
366    return ret;
367}
368
369/**
370 * Copy an abac ID. Actually just increases its reference count.
371 */
372abac_id_t *abac_id_dup(abac_id_t *id) {
373    ++id->refcount;
374    return id;
375}
376
377void abac_id_free(abac_id_t *id) {
378    if (id == NULL)
379        return;
380
381    --id->refcount;
382    if (id->refcount > 0)
383        return;
384
385    // free once the reference count reaches 0
386    DESTROY_IF(id->cert);
387    DESTROY_IF(id->key);
388
389    free(id->keyid);
390    free(id->cn);
391    free(id);
392}
393
394/****************************************************************/
395/**
396 * Load private key for a cert.
397 */
398int abac_id_load_privkey(abac_id_t *id, char *filename) {
399    struct cb_opts c_opts = { 1, 0, 3, "Key password:" };
400
401    assert(id != NULL);
402
403    // load signer key
404    private_key_t *key = lib->creds->create(lib->creds,
405        CRED_PRIVATE_KEY, KEY_RSA,
406        BUILD_FROM_FILE, filename,
407        /* Ask for password if the key's encrypted */
408        BUILD_PASSPHRASE_CALLBACK, _passphrase_callback, &c_opts,
409        BUILD_END
410    );
411    if (key == NULL)
412        return 0;
413
414    id->key = key;
415    return 1;
416}
417
418/****************************************************************/
419abac_id_t *abac_id_new(int idtype,char *keyid, char *cn, 
420certificate_t *cert)
421{
422    abac_id_t *id = abac_xmalloc(sizeof(abac_id_t));
423    id->idtype = idtype;
424    id->keyid = abac_xstrdup(keyid);
425    id->cn = abac_xstrdup(cn);
426    id->cert = cert;
427    id->key = NULL;
428    id->refcount = 1;
429    if(debug) printf("abac_id_new: made a new id, %ld\n",(long) id);
430    return id;
431}
432
433abac_id_t *abac_id_keyid_new(char *keyid, char *cn, certificate_t *cert)
434{
435    return abac_id_new(e_KEYID,keyid,cn,cert); 
436}
437
438static abac_id_t *_abac_id_from_cert(certificate_t *cert) {
439    abac_id_t *id = abac_xmalloc(sizeof(abac_id_t));
440    id->idtype = e_KEYID;
441    id->keyid = NULL;
442    id->cn = NULL;
443    id->cert = cert;
444    id->key = NULL;
445
446    id->keyid = _get_keyid(id->cert);
447
448    // get the CN from the cert
449    id_part_t type;
450    chunk_t data;
451
452    identification_t *cert_id = id->cert->get_subject(id->cert);
453    enumerator_t *id_enum = cert_id->create_part_enumerator(cert_id);
454    while (id_enum->enumerate(id_enum, &type, &data))
455        if (type == ID_PART_RDN_CN) {
456            id->cn = abac_xmalloc(data.len + 1);
457            memcpy(id->cn, data.ptr, data.len);
458            id->cn[data.len] = 0;
459        }
460    id_enum->destroy(id_enum);
461
462    id->refcount = 1;
463
464    return id;
465}
466
467/**
468 * Load an ID cert from a file.
469 */
470abac_id_t *abac_id_from_file(char *filename) {
471
472    certificate_t *cert = lib->creds->create(lib->creds,
473        CRED_CERTIFICATE, CERT_X509,
474        BUILD_FROM_FILE, filename,
475        BUILD_X509_FLAG, X509_AA,
476        BUILD_END
477    );
478
479    if (cert == NULL)
480        return NULL;
481
482    return _abac_id_from_cert(cert);
483}
484
485/**
486 * Load an ID cert from a chunk.
487 */
488abac_id_t *abac_id_from_chunk(abac_chunk_t achunk) {
489    chunk_t chunk = { .ptr = achunk.ptr, .len = achunk.len };
490
491    certificate_t *cert = lib->creds->create(lib->creds,
492        CRED_CERTIFICATE, CERT_X509,
493        BUILD_BLOB_ASN1_DER, chunk,
494        BUILD_X509_FLAG, X509_AA,
495        BUILD_END
496    );
497
498    if (cert == NULL)
499        return NULL;
500
501    return _abac_id_from_cert(cert);
502}
503
504/**
505 * Generate an ID with the specified CN and validity.
506 *
507 * validity is measured in seconds (as of 0.2.0)
508 */
509int abac_id_generate(abac_id_t **ret, char *cn, int validity) {
510    if (cn == NULL || !abac_validate_clean_name(cn))
511        return ABAC_ID_GENERATE_INVALID_CN;
512
513    if (validity < 0)
514        return ABAC_ID_GENERATE_INVALID_VALIDITY;
515
516    if (validity == 0) validity = 1080 * 86400;
517
518    abac_id_t *id = abac_xmalloc(sizeof(abac_id_t));
519
520    char *tmp=NULL;
521    asprintf(&tmp,"p%s",cn);
522    id->idtype = e_KEYID;
523    id->cn = tmp;
524    id->key = _generate_key();
525    id->cert = _generate_cert(id->key, cn, validity);
526    id->keyid = _get_keyid(id->cert);
527
528    id->refcount = 1;
529
530    *ret = id;
531    return ABAC_ID_SUCCESS;
532}
533
534/*****************************************************************/
Note: See TracBrowser for help on using the repository browser.