~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/heimdal/lib/hx509/ks_file.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2005 - 2007 Kungliga Tekniska Högskolan
 
3
 * (Royal Institute of Technology, Stockholm, Sweden).
 
4
 * All rights reserved.
 
5
 *
 
6
 * Redistribution and use in source and binary forms, with or without
 
7
 * modification, are permitted provided that the following conditions
 
8
 * are met:
 
9
 *
 
10
 * 1. Redistributions of source code must retain the above copyright
 
11
 *    notice, this list of conditions and the following disclaimer.
 
12
 *
 
13
 * 2. Redistributions in binary form must reproduce the above copyright
 
14
 *    notice, this list of conditions and the following disclaimer in the
 
15
 *    documentation and/or other materials provided with the distribution.
 
16
 *
 
17
 * 3. Neither the name of the Institute nor the names of its contributors
 
18
 *    may be used to endorse or promote products derived from this software
 
19
 *    without specific prior written permission.
 
20
 *
 
21
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 
22
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
23
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
24
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 
25
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 
26
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 
27
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 
28
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 
29
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 
30
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 
31
 * SUCH DAMAGE.
 
32
 */
 
33
 
 
34
#include "hx_locl.h"
 
35
RCSID("$Id$");
 
36
 
 
37
typedef enum { USE_PEM, USE_DER } outformat;
 
38
 
 
39
struct ks_file {
 
40
    hx509_certs certs;
 
41
    char *fn;
 
42
    outformat format;
 
43
};
 
44
 
 
45
/*
 
46
 *
 
47
 */
 
48
 
 
49
static int
 
50
parse_certificate(hx509_context context, const char *fn,
 
51
                  struct hx509_collector *c,
 
52
                  const hx509_pem_header *headers,
 
53
                  const void *data, size_t len)
 
54
{
 
55
    hx509_cert cert;
 
56
    int ret;
 
57
 
 
58
    ret = hx509_cert_init_data(context, data, len, &cert);
 
59
    if (ret)
 
60
        return ret;
 
61
 
 
62
    ret = _hx509_collector_certs_add(context, c, cert);
 
63
    hx509_cert_free(cert);
 
64
    return ret;
 
65
}
 
66
 
 
67
static int
 
68
try_decrypt(hx509_context context,
 
69
            struct hx509_collector *collector,
 
70
            const AlgorithmIdentifier *alg,
 
71
            const EVP_CIPHER *c,
 
72
            const void *ivdata,
 
73
            const void *password,
 
74
            size_t passwordlen,
 
75
            const void *cipher,
 
76
            size_t len)
 
77
{
 
78
    heim_octet_string clear;
 
79
    size_t keylen;
 
80
    void *key;
 
81
    int ret;
 
82
 
 
83
    keylen = EVP_CIPHER_key_length(c);
 
84
 
 
85
    key = malloc(keylen);
 
86
    if (key == NULL) {
 
87
        hx509_clear_error_string(context);
 
88
        return ENOMEM;
 
89
    }
 
90
 
 
91
    ret = EVP_BytesToKey(c, EVP_md5(), ivdata,
 
92
                         password, passwordlen,
 
93
                         1, key, NULL);
 
94
    if (ret <= 0) {
 
95
        hx509_set_error_string(context, 0, HX509_CRYPTO_INTERNAL_ERROR,
 
96
                               "Failed to do string2key for private key");
 
97
        return HX509_CRYPTO_INTERNAL_ERROR;
 
98
    }
 
99
 
 
100
    clear.data = malloc(len);
 
101
    if (clear.data == NULL) {
 
102
        hx509_set_error_string(context, 0, ENOMEM,
 
103
                               "Out of memory to decrypt for private key");
 
104
        ret = ENOMEM;
 
105
        goto out;
 
106
    }
 
107
    clear.length = len;
 
108
 
 
109
    {
 
110
        EVP_CIPHER_CTX ctx;
 
111
        EVP_CIPHER_CTX_init(&ctx);
 
112
        EVP_CipherInit_ex(&ctx, c, NULL, key, ivdata, 0);
 
113
        EVP_Cipher(&ctx, clear.data, cipher, len);
 
114
        EVP_CIPHER_CTX_cleanup(&ctx);
 
115
    }   
 
116
 
 
117
    ret = _hx509_collector_private_key_add(context,
 
118
                                           collector,
 
119
                                           alg,
 
120
                                           NULL,
 
121
                                           &clear,
 
122
                                           NULL);
 
123
 
 
124
    memset(clear.data, 0, clear.length);
 
125
    free(clear.data);
 
126
out:
 
127
    memset(key, 0, keylen);
 
128
    free(key);
 
129
    return ret;
 
130
}
 
131
 
 
132
static int
 
133
parse_rsa_private_key(hx509_context context, const char *fn,
 
134
                      struct hx509_collector *c,
 
135
                      const hx509_pem_header *headers,
 
136
                      const void *data, size_t len)
 
137
{
 
138
    int ret = 0;
 
139
    const char *enc;
 
140
 
 
141
    enc = hx509_pem_find_header(headers, "Proc-Type");
 
142
    if (enc) {
 
143
        const char *dek;
 
144
        char *type, *iv;
 
145
        ssize_t ssize, size;
 
146
        void *ivdata;
 
147
        const EVP_CIPHER *cipher;
 
148
        const struct _hx509_password *pw;
 
149
        hx509_lock lock;
 
150
        int i, decrypted = 0;
 
151
 
 
152
        lock = _hx509_collector_get_lock(c);
 
153
        if (lock == NULL) {
 
154
            hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
 
155
                                   "Failed to get password for "
 
156
                                   "password protected file %s", fn);
 
157
            return HX509_ALG_NOT_SUPP;
 
158
        }
 
159
 
 
160
        if (strcmp(enc, "4,ENCRYPTED") != 0) {
 
161
            hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
 
162
                                   "RSA key encrypted in unknown method %s "
 
163
                                   "in file",
 
164
                                   enc, fn);
 
165
            hx509_clear_error_string(context);
 
166
            return HX509_PARSING_KEY_FAILED;
 
167
        }
 
168
 
 
169
        dek = hx509_pem_find_header(headers, "DEK-Info");
 
170
        if (dek == NULL) {
 
171
            hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
 
172
                                   "Encrypted RSA missing DEK-Info");
 
173
            return HX509_PARSING_KEY_FAILED;
 
174
        }
 
175
 
 
176
        type = strdup(dek);
 
177
        if (type == NULL) {
 
178
            hx509_clear_error_string(context);
 
179
            return ENOMEM;
 
180
        }
 
181
 
 
182
        iv = strchr(type, ',');
 
183
        if (iv == NULL) {
 
184
            free(type);
 
185
            hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
 
186
                                   "IV missing");
 
187
            return HX509_PARSING_KEY_FAILED;
 
188
        }
 
189
 
 
190
        *iv++ = '\0';
 
191
 
 
192
        size = strlen(iv);
 
193
        ivdata = malloc(size);
 
194
        if (ivdata == NULL) {
 
195
            hx509_clear_error_string(context);
 
196
            free(type);
 
197
            return ENOMEM;
 
198
        }
 
199
 
 
200
        cipher = EVP_get_cipherbyname(type);
 
201
        if (cipher == NULL) {
 
202
            free(ivdata);
 
203
            hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
 
204
                                   "RSA key encrypted with "
 
205
                                   "unsupported cipher: %s",
 
206
                                   type);
 
207
            free(type);
 
208
            return HX509_ALG_NOT_SUPP;
 
209
        }
 
210
 
 
211
#define PKCS5_SALT_LEN 8
 
212
 
 
213
        ssize = hex_decode(iv, ivdata, size);
 
214
        free(type);
 
215
        type = NULL;
 
216
        iv = NULL;
 
217
 
 
218
        if (ssize < 0 || ssize < PKCS5_SALT_LEN || ssize < EVP_CIPHER_iv_length(cipher)) {
 
219
            free(ivdata);
 
220
            hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
 
221
                                   "Salt have wrong length in RSA key file");
 
222
            return HX509_PARSING_KEY_FAILED;
 
223
        }
 
224
        
 
225
        pw = _hx509_lock_get_passwords(lock);
 
226
        if (pw != NULL) {
 
227
            const void *password;
 
228
            size_t passwordlen;
 
229
 
 
230
            for (i = 0; i < pw->len; i++) {
 
231
                password = pw->val[i];
 
232
                passwordlen = strlen(password);
 
233
                
 
234
                ret = try_decrypt(context, c, hx509_signature_rsa(),
 
235
                                  cipher, ivdata, password, passwordlen,
 
236
                                  data, len);
 
237
                if (ret == 0) {
 
238
                    decrypted = 1;
 
239
                    break;
 
240
                }
 
241
            }
 
242
        }
 
243
        if (!decrypted) {
 
244
            hx509_prompt prompt;
 
245
            char password[128];
 
246
 
 
247
            memset(&prompt, 0, sizeof(prompt));
 
248
 
 
249
            prompt.prompt = "Password for keyfile: ";
 
250
            prompt.type = HX509_PROMPT_TYPE_PASSWORD;
 
251
            prompt.reply.data = password;
 
252
            prompt.reply.length = sizeof(password);
 
253
 
 
254
            ret = hx509_lock_prompt(lock, &prompt);
 
255
            if (ret == 0)
 
256
                ret = try_decrypt(context, c, hx509_signature_rsa(),
 
257
                                  cipher, ivdata, password, strlen(password),
 
258
                                  data, len);
 
259
            /* XXX add password to lock password collection ? */
 
260
            memset(password, 0, sizeof(password));
 
261
        }
 
262
        free(ivdata);
 
263
 
 
264
    } else {
 
265
        heim_octet_string keydata;
 
266
 
 
267
        keydata.data = rk_UNCONST(data);
 
268
        keydata.length = len;
 
269
 
 
270
        ret = _hx509_collector_private_key_add(context,
 
271
                                               c,
 
272
                                               hx509_signature_rsa(),
 
273
                                               NULL,
 
274
                                               &keydata,
 
275
                                               NULL);
 
276
    }
 
277
 
 
278
    return ret;
 
279
}
 
280
 
 
281
 
 
282
struct pem_formats {
 
283
    const char *name;
 
284
    int (*func)(hx509_context, const char *, struct hx509_collector *,
 
285
                const hx509_pem_header *, const void *, size_t);
 
286
} formats[] = {
 
287
    { "CERTIFICATE", parse_certificate },
 
288
    { "RSA PRIVATE KEY", parse_rsa_private_key }
 
289
};
 
290
 
 
291
 
 
292
struct pem_ctx {
 
293
    int flags;
 
294
    struct hx509_collector *c;
 
295
};
 
296
 
 
297
static int
 
298
pem_func(hx509_context context, const char *type,
 
299
         const hx509_pem_header *header,
 
300
         const void *data, size_t len, void *ctx)
 
301
{
 
302
    struct pem_ctx *pem_ctx = (struct pem_ctx*)ctx;
 
303
    int ret = 0, j;
 
304
 
 
305
    for (j = 0; j < sizeof(formats)/sizeof(formats[0]); j++) {
 
306
        const char *q = formats[j].name;
 
307
        if (strcasecmp(type, q) == 0) {
 
308
            ret = (*formats[j].func)(context, NULL, pem_ctx->c,  header, data, len);
 
309
            if (ret == 0)
 
310
                break;
 
311
        }
 
312
    }
 
313
    if (j == sizeof(formats)/sizeof(formats[0])) {
 
314
        ret = HX509_UNSUPPORTED_OPERATION;
 
315
        hx509_set_error_string(context, 0, ret,
 
316
                               "Found no matching PEM format for %s", type);
 
317
        return ret;
 
318
    }
 
319
    if (ret && (pem_ctx->flags & HX509_CERTS_UNPROTECT_ALL))
 
320
        return ret;
 
321
    return 0;
 
322
}
 
323
 
 
324
/*
 
325
 *
 
326
 */
 
327
 
 
328
static int
 
329
file_init_common(hx509_context context,
 
330
                 hx509_certs certs, void **data, int flags,
 
331
                 const char *residue, hx509_lock lock, outformat format)
 
332
{
 
333
    char *p, *pnext;
 
334
    struct ks_file *f = NULL;
 
335
    hx509_private_key *keys = NULL;
 
336
    int ret;
 
337
    struct pem_ctx pem_ctx;
 
338
 
 
339
    pem_ctx.flags = flags;
 
340
    pem_ctx.c = NULL;
 
341
 
 
342
    *data = NULL;
 
343
 
 
344
    if (lock == NULL)
 
345
        lock = _hx509_empty_lock;
 
346
 
 
347
    f = calloc(1, sizeof(*f));
 
348
    if (f == NULL) {
 
349
        hx509_clear_error_string(context);
 
350
        return ENOMEM;
 
351
    }
 
352
    f->format = format;
 
353
 
 
354
    f->fn = strdup(residue);
 
355
    if (f->fn == NULL) {
 
356
        hx509_clear_error_string(context);
 
357
        ret = ENOMEM;
 
358
        goto out;
 
359
    }
 
360
 
 
361
    /*
 
362
     * XXX this is broken, the function should parse the file before
 
363
     * overwriting it
 
364
     */
 
365
 
 
366
    if (flags & HX509_CERTS_CREATE) {
 
367
        ret = hx509_certs_init(context, "MEMORY:ks-file-create",
 
368
                               0, lock, &f->certs);
 
369
        if (ret)
 
370
            goto out;
 
371
        *data = f;
 
372
        return 0;
 
373
    }
 
374
 
 
375
    ret = _hx509_collector_alloc(context, lock, &pem_ctx.c);
 
376
    if (ret)
 
377
        goto out;
 
378
 
 
379
    for (p = f->fn; p != NULL; p = pnext) {
 
380
        FILE *f;
 
381
 
 
382
        pnext = strchr(p, ',');
 
383
        if (pnext)
 
384
            *pnext++ = '\0';
 
385
        
 
386
 
 
387
        if ((f = fopen(p, "r")) == NULL) {
 
388
            ret = ENOENT;
 
389
            hx509_set_error_string(context, 0, ret,
 
390
                                   "Failed to open PEM file \"%s\": %s",
 
391
                                   p, strerror(errno));
 
392
            goto out;
 
393
        }
 
394
        rk_cloexec_file(f);
 
395
 
 
396
        ret = hx509_pem_read(context, f, pem_func, &pem_ctx);
 
397
        fclose(f);              
 
398
        if (ret != 0 && ret != HX509_PARSING_KEY_FAILED)
 
399
            goto out;
 
400
        else if (ret == HX509_PARSING_KEY_FAILED) {
 
401
            size_t length;
 
402
            void *ptr;
 
403
            int i;
 
404
 
 
405
            ret = rk_undumpdata(p, &ptr, &length);
 
406
            if (ret) {
 
407
                hx509_clear_error_string(context);
 
408
                goto out;
 
409
            }
 
410
 
 
411
            for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) {
 
412
                ret = (*formats[i].func)(context, p, pem_ctx.c, NULL, ptr, length);
 
413
                if (ret == 0)
 
414
                    break;
 
415
            }
 
416
            rk_xfree(ptr);
 
417
            if (ret)
 
418
                goto out;
 
419
        }
 
420
    }
 
421
 
 
422
    ret = _hx509_collector_collect_certs(context, pem_ctx.c, &f->certs);
 
423
    if (ret)
 
424
        goto out;
 
425
 
 
426
    ret = _hx509_collector_collect_private_keys(context, pem_ctx.c, &keys);
 
427
    if (ret == 0) {
 
428
        int i;
 
429
 
 
430
        for (i = 0; keys[i]; i++)
 
431
            _hx509_certs_keys_add(context, f->certs, keys[i]);
 
432
        _hx509_certs_keys_free(context, keys);
 
433
    }
 
434
 
 
435
out:
 
436
    if (ret == 0)
 
437
        *data = f;
 
438
    else {
 
439
        if (f->fn)
 
440
            free(f->fn);
 
441
        free(f);
 
442
    }
 
443
    if (pem_ctx.c)
 
444
        _hx509_collector_free(pem_ctx.c);
 
445
 
 
446
    return ret;
 
447
}
 
448
 
 
449
static int
 
450
file_init_pem(hx509_context context,
 
451
              hx509_certs certs, void **data, int flags,
 
452
              const char *residue, hx509_lock lock)
 
453
{
 
454
    return file_init_common(context, certs, data, flags, residue, lock, USE_PEM);
 
455
}
 
456
 
 
457
static int
 
458
file_init_der(hx509_context context,
 
459
              hx509_certs certs, void **data, int flags,
 
460
              const char *residue, hx509_lock lock)
 
461
{
 
462
    return file_init_common(context, certs, data, flags, residue, lock, USE_DER);
 
463
}
 
464
 
 
465
static int
 
466
file_free(hx509_certs certs, void *data)
 
467
{
 
468
    struct ks_file *f = data;
 
469
    hx509_certs_free(&f->certs);
 
470
    free(f->fn);
 
471
    free(f);
 
472
    return 0;
 
473
}
 
474
 
 
475
struct store_ctx {
 
476
    FILE *f;
 
477
    outformat format;
 
478
};
 
479
 
 
480
static int
 
481
store_func(hx509_context context, void *ctx, hx509_cert c)
 
482
{
 
483
    struct store_ctx *sc = ctx;
 
484
    heim_octet_string data;
 
485
    int ret;
 
486
 
 
487
    ret = hx509_cert_binary(context, c, &data);
 
488
    if (ret)
 
489
        return ret;
 
490
 
 
491
    switch (sc->format) {
 
492
    case USE_DER:
 
493
        fwrite(data.data, data.length, 1, sc->f);
 
494
        free(data.data);
 
495
        break;
 
496
    case USE_PEM:
 
497
        hx509_pem_write(context, "CERTIFICATE", NULL, sc->f,
 
498
                        data.data, data.length);
 
499
        free(data.data);
 
500
        if (_hx509_cert_private_key_exportable(c)) {
 
501
            hx509_private_key key = _hx509_cert_private_key(c);
 
502
            ret = _hx509_private_key_export(context, key, &data);
 
503
            if (ret)
 
504
                break;
 
505
            hx509_pem_write(context, _hx509_private_pem_name(key), NULL, sc->f,
 
506
                            data.data, data.length);
 
507
            free(data.data);
 
508
        }
 
509
        break;
 
510
    }
 
511
 
 
512
    return 0;
 
513
}
 
514
 
 
515
static int
 
516
file_store(hx509_context context,
 
517
           hx509_certs certs, void *data, int flags, hx509_lock lock)
 
518
{
 
519
    struct ks_file *f = data;
 
520
    struct store_ctx sc;
 
521
    int ret;
 
522
 
 
523
    sc.f = fopen(f->fn, "w");
 
524
    if (sc.f == NULL) {
 
525
        hx509_set_error_string(context, 0, ENOENT,
 
526
                               "Failed to open file %s for writing");
 
527
        return ENOENT;
 
528
    }
 
529
    rk_cloexec_file(sc.f);
 
530
    sc.format = f->format;
 
531
 
 
532
    ret = hx509_certs_iter(context, f->certs, store_func, &sc);
 
533
    fclose(sc.f);
 
534
    return ret;
 
535
}
 
536
 
 
537
static int
 
538
file_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c)
 
539
{
 
540
    struct ks_file *f = data;
 
541
    return hx509_certs_add(context, f->certs, c);
 
542
}
 
543
 
 
544
static int
 
545
file_iter_start(hx509_context context,
 
546
                hx509_certs certs, void *data, void **cursor)
 
547
{
 
548
    struct ks_file *f = data;
 
549
    return hx509_certs_start_seq(context, f->certs, cursor);
 
550
}
 
551
 
 
552
static int
 
553
file_iter(hx509_context context,
 
554
          hx509_certs certs, void *data, void *iter, hx509_cert *cert)
 
555
{
 
556
    struct ks_file *f = data;
 
557
    return hx509_certs_next_cert(context, f->certs, iter, cert);
 
558
}
 
559
 
 
560
static int
 
561
file_iter_end(hx509_context context,
 
562
              hx509_certs certs,
 
563
              void *data,
 
564
              void *cursor)
 
565
{
 
566
    struct ks_file *f = data;
 
567
    return hx509_certs_end_seq(context, f->certs, cursor);
 
568
}
 
569
 
 
570
static int
 
571
file_getkeys(hx509_context context,
 
572
             hx509_certs certs,
 
573
             void *data,
 
574
             hx509_private_key **keys)
 
575
{
 
576
    struct ks_file *f = data;
 
577
    return _hx509_certs_keys_get(context, f->certs, keys);
 
578
}
 
579
 
 
580
static int
 
581
file_addkey(hx509_context context,
 
582
             hx509_certs certs,
 
583
             void *data,
 
584
             hx509_private_key key)
 
585
{
 
586
    struct ks_file *f = data;
 
587
    return _hx509_certs_keys_add(context, f->certs, key);
 
588
}
 
589
 
 
590
static struct hx509_keyset_ops keyset_file = {
 
591
    "FILE",
 
592
    0,
 
593
    file_init_pem,
 
594
    file_store,
 
595
    file_free,
 
596
    file_add,
 
597
    NULL,
 
598
    file_iter_start,
 
599
    file_iter,
 
600
    file_iter_end,
 
601
    NULL,
 
602
    file_getkeys,
 
603
    file_addkey
 
604
};
 
605
 
 
606
static struct hx509_keyset_ops keyset_pemfile = {
 
607
    "PEM-FILE",
 
608
    0,
 
609
    file_init_pem,
 
610
    file_store,
 
611
    file_free,
 
612
    file_add,
 
613
    NULL,
 
614
    file_iter_start,
 
615
    file_iter,
 
616
    file_iter_end,
 
617
    NULL,
 
618
    file_getkeys,
 
619
    file_addkey
 
620
};
 
621
 
 
622
static struct hx509_keyset_ops keyset_derfile = {
 
623
    "DER-FILE",
 
624
    0,
 
625
    file_init_der,
 
626
    file_store,
 
627
    file_free,
 
628
    file_add,
 
629
    NULL,
 
630
    file_iter_start,
 
631
    file_iter,
 
632
    file_iter_end,
 
633
    NULL,
 
634
    file_getkeys,
 
635
    file_addkey
 
636
};
 
637
 
 
638
 
 
639
void
 
640
_hx509_ks_file_register(hx509_context context)
 
641
{
 
642
    _hx509_ks_register(context, &keyset_file);
 
643
    _hx509_ks_register(context, &keyset_pemfile);
 
644
    _hx509_ks_register(context, &keyset_derfile);
 
645
}