source: creddy/id.c @ df3fe37

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

dup IDs added to an attribute

  • Property mode set to 100644
File size: 10.5 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
338    // generate the key
339    key = lib->creds->create(
340        lib->creds,
341        CRED_PRIVATE_KEY, KEY_RSA,
342        BUILD_KEY_SIZE, 2048,
343        BUILD_END
344    );
345    if (key == NULL)
346        errx(1, "Key generation failed");
347
348    return key;
349}
350
351static char *_create_dn(char *cn) {
352
353#define DN "cn="
354
355    char *dn = creddy_xmalloc(sizeof(DN) + strlen(cn));
356    memcpy(dn, DN, sizeof(DN));
357    strcat(dn, cn);
358
359    return dn;
360}
361
362/**
363 * Generate certificate.
364 */
365static certificate_t *_generate_cert(private_key_t *private, char *cn, int validity) {
366    // build the DN
367    char *dn_string = _create_dn(cn);
368    identification_t *id = identification_create_from_string(dn_string);
369    if (id == NULL)
370        errx(1, "couldn't create ID from DN %s", dn_string);
371    free(dn_string);
372
373    // get the public key
374    public_key_t *public = private->get_public_key(private);
375    if (public == NULL)
376        errx(1, "couldn't get public key from private key");
377
378    // create a serial (stolen from strongswan pki)
379    rng_t *rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
380    if (!rng)
381        errx(1, "no random number generator");
382
383    // random serial
384    chunk_t serial = creddy_generate_serial();
385
386    // validity period
387    time_t not_before = time(NULL);
388    time_t not_after = not_before + validity * 24 * 60 * 60;
389
390    // create!
391    certificate_t *cert = lib->creds->create(lib->creds,
392        CRED_CERTIFICATE, CERT_X509,
393        BUILD_SIGNING_KEY, private,
394        BUILD_PUBLIC_KEY, public,
395        BUILD_SUBJECT, id,
396        BUILD_NOT_BEFORE_TIME, not_before,
397        BUILD_NOT_AFTER_TIME, not_after,
398        BUILD_SERIAL, serial,
399        BUILD_DIGEST_ALG, HASH_SHA1,
400        BUILD_X509_FLAG, X509_CA,
401        BUILD_PATHLEN, X509_NO_PATH_LEN_CONSTRAINT,
402        BUILD_END
403    );
404    if (cert == NULL)
405        errx(1, "couldn't build cert :(");
406
407    DESTROY_IF(id);
408    DESTROY_IF(public);
409    free(serial.ptr);
410
411    return cert;
412}
413
414#define BYTES_PER_LINE 64
415
416// thx libstrongswan
417static void _encode_base64(FILE *out, chunk_t encoding) {
418    int start;
419
420    chunk_t b64 = chunk_to_base64(encoding, NULL);
421
422    fprintf(out, "-----BEGIN CERTIFICATE-----\n");
423
424    for (start = 0; start < b64.len; start += BYTES_PER_LINE) {
425        int left = b64.len - start;
426        int len = left < BYTES_PER_LINE ? left : BYTES_PER_LINE;
427        fwrite(b64.ptr + start, len, 1, out);
428        fprintf(out, "\n");
429    }
430
431    fprintf(out, "-----END CERTIFICATE-----\n");
432
433    free(b64.ptr);
434}
Note: See TracBrowser for help on using the repository browser.