source: creddy/id.c @ 8c76b45

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

finer granularity over validity periods

API: ID and attr certs take seconds for validity period
command line: validity option takes a suffix for time period

fixes #20

this is an API-breaking change, so a bump to 0.2.0 is necessary

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