source: libabac/abac_id.c @ 13c9479

mei_rt2mei_rt2_fix_1
Last change on this file since 13c9479 was 8bd77b5, checked in by Mei <mei@…>, 12 years ago

1) convert parser and libabac to use id cred and attr cred like

creddy (move those 2 files to libabac).

2) fix up abac.hh to work with expanded libabac. can now build

structure from python script

3) redid the credential dump using the internal credential table

instead of depending on a search in db.

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