source: creddy/id.c @ 956e1c6

abac0-leakabac0-meicompt_changesgec13mei-idmei-rt0-nmei_rt0mei_rt2mei_rt2_fix_1meiyap-rt1meiyap1rt2tvf-new-xml
Last change on this file since 956e1c6 was 956e1c6, checked in by Mike Ryan <mikeryan@…>, 13 years ago

load an ID from a chunk

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