source: creddy/id.c @ 4f114cc

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

load CN from ID certificate

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