source: creddy/id.c @ 04f5da1

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

extract -lcreddy

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