source: creddy/id.c @ aa33ad9

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

generate ID moved into creddy_id

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