source: creddy/id.c @ d56e51b

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

raise an error (either return value or exception) when trying to write a
private key when it hasn't been loaded

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