source: creddy/id.c @ bf7498b

abac0-leakabac0-meicompt_changesgec13mei-idmei-rt0-nmei_rt0mei_rt2mei_rt2_fix_1meiyap-rt1meiyap1rt2tvf-new-xml
Last change on this file since bf7498b was ff81abb, checked in by Ted Faber <faber@…>, 13 years ago

Fixes #16. Missing init_libabac calls.

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