1/*
2 * Copyright (c) 2000 - 2004 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 "hdb_locl.h"
35#ifndef O_BINARY
36#define O_BINARY 0
37#endif
38
39RCSID("$Id: mkey.c 21745 2007-07-31 16:11:25Z lha $");
40
41struct hdb_master_key_data {
42    krb5_keytab_entry keytab;
43    krb5_crypto crypto;
44    struct hdb_master_key_data *next;
45};
46
47void
48hdb_free_master_key(krb5_context context, hdb_master_key mkey)
49{
50    struct hdb_master_key_data *ptr;
51    while(mkey) {
52	krb5_kt_free_entry(context, &mkey->keytab);
53	if (mkey->crypto)
54	    krb5_crypto_destroy(context, mkey->crypto);
55	ptr = mkey;
56	mkey = mkey->next;
57	free(ptr);
58    }
59}
60
61krb5_error_code
62hdb_process_master_key(krb5_context context,
63		       int kvno, krb5_keyblock *key, krb5_enctype etype,
64		       hdb_master_key *mkey)
65{
66    krb5_error_code ret;
67
68    *mkey = calloc(1, sizeof(**mkey));
69    if(*mkey == NULL) {
70	krb5_set_error_string(context, "malloc: out of memory");
71	return ENOMEM;
72    }
73    (*mkey)->keytab.vno = kvno;
74    ret = krb5_parse_name(context, "K/M", &(*mkey)->keytab.principal);
75    if(ret)
76	goto fail;
77    ret = krb5_copy_keyblock_contents(context, key, &(*mkey)->keytab.keyblock);
78    if(ret)
79	goto fail;
80    if(etype != 0)
81	(*mkey)->keytab.keyblock.keytype = etype;
82    (*mkey)->keytab.timestamp = time(NULL);
83    ret = krb5_crypto_init(context, key, etype, &(*mkey)->crypto);
84    if(ret)
85	goto fail;
86    return 0;
87 fail:
88    hdb_free_master_key(context, *mkey);
89    *mkey = NULL;
90    return ret;
91}
92
93krb5_error_code
94hdb_add_master_key(krb5_context context, krb5_keyblock *key,
95		   hdb_master_key *inout)
96{
97    int vno = 0;
98    hdb_master_key p;
99    krb5_error_code ret;
100
101    for(p = *inout; p; p = p->next)
102	vno = max(vno, p->keytab.vno);
103    vno++;
104    ret = hdb_process_master_key(context, vno, key, 0, &p);
105    if(ret)
106	return ret;
107    p->next = *inout;
108    *inout = p;
109    return 0;
110}
111
112static krb5_error_code
113read_master_keytab(krb5_context context, const char *filename,
114		   hdb_master_key *mkey)
115{
116    krb5_error_code ret;
117    krb5_keytab id;
118    krb5_kt_cursor cursor;
119    krb5_keytab_entry entry;
120    hdb_master_key p;
121
122    ret = krb5_kt_resolve(context, filename, &id);
123    if(ret)
124	return ret;
125
126    ret = krb5_kt_start_seq_get(context, id, &cursor);
127    if(ret)
128	goto out;
129    *mkey = NULL;
130    while(krb5_kt_next_entry(context, id, &entry, &cursor) == 0) {
131	p = calloc(1, sizeof(*p));
132	if(p == NULL) {
133	    krb5_kt_end_seq_get(context, id, &cursor);
134	    ret = ENOMEM;
135	    goto out;
136	}
137	p->keytab = entry;
138	ret = krb5_crypto_init(context, &p->keytab.keyblock, 0, &p->crypto);
139	p->next = *mkey;
140	*mkey = p;
141    }
142    krb5_kt_end_seq_get(context, id, &cursor);
143  out:
144    krb5_kt_close(context, id);
145    return ret;
146}
147
148/* read a MIT master keyfile */
149static krb5_error_code
150read_master_mit(krb5_context context, const char *filename,
151		hdb_master_key *mkey)
152{
153    int fd;
154    krb5_error_code ret;
155    krb5_storage *sp;
156    int16_t enctype;
157    krb5_keyblock key;
158
159    fd = open(filename, O_RDONLY | O_BINARY);
160    if(fd < 0) {
161	int save_errno = errno;
162	krb5_set_error_string(context, "failed to open %s: %s", filename,
163			      strerror(save_errno));
164	return save_errno;
165    }
166    sp = krb5_storage_from_fd(fd);
167    if(sp == NULL) {
168	close(fd);
169	return errno;
170    }
171    krb5_storage_set_flags(sp, KRB5_STORAGE_HOST_BYTEORDER);
172#if 0
173    /* could possibly use ret_keyblock here, but do it with more
174       checks for now */
175    ret = krb5_ret_keyblock(sp, &key);
176#else
177    ret = krb5_ret_int16(sp, &enctype);
178    if((htons(enctype) & 0xff00) == 0x3000) {
179	krb5_set_error_string(context, "unknown keytype in %s: %#x, expected %#x",
180			      filename, htons(enctype), 0x3000);
181	ret = HEIM_ERR_BAD_MKEY;
182	goto out;
183    }
184    key.keytype = enctype;
185    ret = krb5_ret_data(sp, &key.keyvalue);
186    if(ret)
187	goto out;
188#endif
189    ret = hdb_process_master_key(context, 0, &key, 0, mkey);
190    krb5_free_keyblock_contents(context, &key);
191  out:
192    krb5_storage_free(sp);
193    close(fd);
194    return ret;
195}
196
197/* read an old master key file */
198static krb5_error_code
199read_master_encryptionkey(krb5_context context, const char *filename,
200			  hdb_master_key *mkey)
201{
202    int fd;
203    krb5_keyblock key;
204    krb5_error_code ret;
205    unsigned char buf[256];
206    ssize_t len;
207    size_t ret_len;
208
209    fd = open(filename, O_RDONLY | O_BINARY);
210    if(fd < 0) {
211	int save_errno = errno;
212	krb5_set_error_string(context, "failed to open %s: %s",
213			      filename, strerror(save_errno));
214	return save_errno;
215    }
216
217    len = read(fd, buf, sizeof(buf));
218    close(fd);
219    if(len < 0) {
220	int save_errno = errno;
221	krb5_set_error_string(context, "error reading %s: %s",
222			      filename, strerror(save_errno));
223	return save_errno;
224    }
225
226    ret = decode_EncryptionKey(buf, len, &key, &ret_len);
227    memset(buf, 0, sizeof(buf));
228    if(ret)
229	return ret;
230
231    /* Originally, the keytype was just that, and later it got changed
232       to des-cbc-md5, but we always used des in cfb64 mode. This
233       should cover all cases, but will break if someone has hacked
234       this code to really use des-cbc-md5 -- but then that's not my
235       problem. */
236    if(key.keytype == KEYTYPE_DES || key.keytype == ETYPE_DES_CBC_MD5)
237	key.keytype = ETYPE_DES_CFB64_NONE;
238
239    ret = hdb_process_master_key(context, 0, &key, 0, mkey);
240    krb5_free_keyblock_contents(context, &key);
241    return ret;
242}
243
244/* read a krb4 /.k style file */
245static krb5_error_code
246read_master_krb4(krb5_context context, const char *filename,
247		 hdb_master_key *mkey)
248{
249    int fd;
250    krb5_keyblock key;
251    krb5_error_code ret;
252    unsigned char buf[256];
253    ssize_t len;
254
255    fd = open(filename, O_RDONLY | O_BINARY);
256    if(fd < 0) {
257	int save_errno = errno;
258	krb5_set_error_string(context, "failed to open %s: %s",
259			      filename, strerror(save_errno));
260	return save_errno;
261    }
262
263    len = read(fd, buf, sizeof(buf));
264    close(fd);
265    if(len < 0) {
266	int save_errno = errno;
267	krb5_set_error_string(context, "error reading %s: %s",
268			      filename, strerror(save_errno));
269	return save_errno;
270    }
271    if(len != 8) {
272	krb5_set_error_string(context, "bad contents of %s", filename);
273	return HEIM_ERR_EOF; /* XXX file might be too large */
274    }
275
276    memset(&key, 0, sizeof(key));
277    key.keytype = ETYPE_DES_PCBC_NONE;
278    ret = krb5_data_copy(&key.keyvalue, buf, len);
279    memset(buf, 0, sizeof(buf));
280    if(ret)
281	return ret;
282
283    ret = hdb_process_master_key(context, 0, &key, 0, mkey);
284    krb5_free_keyblock_contents(context, &key);
285    return ret;
286}
287
288krb5_error_code
289hdb_read_master_key(krb5_context context, const char *filename,
290		    hdb_master_key *mkey)
291{
292    FILE *f;
293    unsigned char buf[16];
294    krb5_error_code ret;
295
296    off_t len;
297
298    *mkey = NULL;
299
300    if(filename == NULL)
301	filename = HDB_DB_DIR "/m-key";
302
303    f = fopen(filename, "r");
304    if(f == NULL) {
305	int save_errno = errno;
306	krb5_set_error_string(context, "failed to open %s: %s",
307			      filename, strerror(save_errno));
308	return save_errno;
309    }
310
311    if(fread(buf, 1, 2, f) != 2) {
312	krb5_set_error_string(context, "end of file reading %s", filename);
313	fclose(f);
314	return HEIM_ERR_EOF;
315    }
316
317    fseek(f, 0, SEEK_END);
318    len = ftell(f);
319
320    if(fclose(f) != 0)
321	return errno;
322
323    if(len < 0)
324	return errno;
325
326    if(len == 8) {
327	ret = read_master_krb4(context, filename, mkey);
328    } else if(buf[0] == 0x30 && len <= 127 && buf[1] == len - 2) {
329	ret = read_master_encryptionkey(context, filename, mkey);
330    } else if(buf[0] == 5 && buf[1] >= 1 && buf[1] <= 2) {
331	ret = read_master_keytab(context, filename, mkey);
332    } else {
333	ret = read_master_mit(context, filename, mkey);
334    }
335    return ret;
336}
337
338krb5_error_code
339hdb_write_master_key(krb5_context context, const char *filename,
340		     hdb_master_key mkey)
341{
342    krb5_error_code ret;
343    hdb_master_key p;
344    krb5_keytab kt;
345
346    if(filename == NULL)
347	filename = HDB_DB_DIR "/m-key";
348
349    ret = krb5_kt_resolve(context, filename, &kt);
350    if(ret)
351	return ret;
352
353    for(p = mkey; p; p = p->next) {
354	ret = krb5_kt_add_entry(context, kt, &p->keytab);
355    }
356
357    krb5_kt_close(context, kt);
358
359    return ret;
360}
361
362hdb_master_key
363_hdb_find_master_key(uint32_t *mkvno, hdb_master_key mkey)
364{
365    hdb_master_key ret = NULL;
366    while(mkey) {
367	if(ret == NULL && mkey->keytab.vno == 0)
368	    ret = mkey;
369	if(mkvno == NULL) {
370	    if(ret == NULL || mkey->keytab.vno > ret->keytab.vno)
371		ret = mkey;
372	} else if(mkey->keytab.vno == *mkvno)
373	    return mkey;
374	mkey = mkey->next;
375    }
376    return ret;
377}
378
379int
380_hdb_mkey_version(hdb_master_key mkey)
381{
382    return mkey->keytab.vno;
383}
384
385int
386_hdb_mkey_decrypt(krb5_context context, hdb_master_key key,
387		  krb5_key_usage usage,
388		  void *ptr, size_t size, krb5_data *res)
389{
390    return krb5_decrypt(context, key->crypto, usage,
391			ptr, size, res);
392}
393
394int
395_hdb_mkey_encrypt(krb5_context context, hdb_master_key key,
396		  krb5_key_usage usage,
397		  const void *ptr, size_t size, krb5_data *res)
398{
399    return krb5_encrypt(context, key->crypto, usage,
400			ptr, size, res);
401}
402
403krb5_error_code
404hdb_unseal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
405{
406
407    krb5_error_code ret;
408    krb5_data res;
409    size_t keysize;
410
411    hdb_master_key key;
412
413    if(k->mkvno == NULL)
414	return 0;
415
416    key = _hdb_find_master_key(k->mkvno, mkey);
417
418    if (key == NULL)
419	return HDB_ERR_NO_MKEY;
420
421    ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY,
422			    k->key.keyvalue.data,
423			    k->key.keyvalue.length,
424			    &res);
425    if(ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
426	/* try to decrypt with MIT key usage */
427	ret = _hdb_mkey_decrypt(context, key, 0,
428				k->key.keyvalue.data,
429				k->key.keyvalue.length,
430				&res);
431    }
432    if (ret)
433	return ret;
434
435    /* fixup keylength if the key got padded when encrypting it */
436    ret = krb5_enctype_keysize(context, k->key.keytype, &keysize);
437    if (ret) {
438	krb5_data_free(&res);
439	return ret;
440    }
441    if (keysize > res.length) {
442	krb5_data_free(&res);
443	return KRB5_BAD_KEYSIZE;
444    }
445
446    memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
447    free(k->key.keyvalue.data);
448    k->key.keyvalue = res;
449    k->key.keyvalue.length = keysize;
450    free(k->mkvno);
451    k->mkvno = NULL;
452
453    return 0;
454}
455
456krb5_error_code
457hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
458{
459    int i;
460
461    for(i = 0; i < ent->keys.len; i++){
462	krb5_error_code ret;
463
464	ret = hdb_unseal_key_mkey(context, &ent->keys.val[i], mkey);
465	if (ret)
466	    return ret;
467    }
468    return 0;
469}
470
471krb5_error_code
472hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent)
473{
474    if (db->hdb_master_key_set == 0)
475	return 0;
476    return hdb_unseal_keys_mkey(context, ent, db->hdb_master_key);
477}
478
479krb5_error_code
480hdb_unseal_key(krb5_context context, HDB *db, Key *k)
481{
482    if (db->hdb_master_key_set == 0)
483	return 0;
484    return hdb_unseal_key_mkey(context, k, db->hdb_master_key);
485}
486
487krb5_error_code
488hdb_seal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
489{
490    krb5_error_code ret;
491    krb5_data res;
492    hdb_master_key key;
493
494    if(k->mkvno != NULL)
495	return 0;
496
497    key = _hdb_find_master_key(k->mkvno, mkey);
498
499    if (key == NULL)
500	return HDB_ERR_NO_MKEY;
501
502    ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY,
503			    k->key.keyvalue.data,
504			    k->key.keyvalue.length,
505			    &res);
506    if (ret)
507	return ret;
508
509    memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
510    free(k->key.keyvalue.data);
511    k->key.keyvalue = res;
512
513    if (k->mkvno == NULL) {
514	k->mkvno = malloc(sizeof(*k->mkvno));
515	if (k->mkvno == NULL)
516	    return ENOMEM;
517    }
518    *k->mkvno = key->keytab.vno;
519
520    return 0;
521}
522
523krb5_error_code
524hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
525{
526    int i;
527    for(i = 0; i < ent->keys.len; i++){
528	krb5_error_code ret;
529
530	ret = hdb_seal_key_mkey(context, &ent->keys.val[i], mkey);
531	if (ret)
532	    return ret;
533    }
534    return 0;
535}
536
537krb5_error_code
538hdb_seal_keys(krb5_context context, HDB *db, hdb_entry *ent)
539{
540    if (db->hdb_master_key_set == 0)
541	return 0;
542
543    return hdb_seal_keys_mkey(context, ent, db->hdb_master_key);
544}
545
546krb5_error_code
547hdb_seal_key(krb5_context context, HDB *db, Key *k)
548{
549    if (db->hdb_master_key_set == 0)
550	return 0;
551
552    return hdb_seal_key_mkey(context, k, db->hdb_master_key);
553}
554
555krb5_error_code
556hdb_set_master_key (krb5_context context,
557		    HDB *db,
558		    krb5_keyblock *key)
559{
560    krb5_error_code ret;
561    hdb_master_key mkey;
562
563    ret = hdb_process_master_key(context, 0, key, 0, &mkey);
564    if (ret)
565	return ret;
566    db->hdb_master_key = mkey;
567#if 0 /* XXX - why? */
568    des_set_random_generator_seed(key.keyvalue.data);
569#endif
570    db->hdb_master_key_set = 1;
571    return 0;
572}
573
574krb5_error_code
575hdb_set_master_keyfile (krb5_context context,
576			HDB *db,
577			const char *keyfile)
578{
579    hdb_master_key key;
580    krb5_error_code ret;
581
582    ret = hdb_read_master_key(context, keyfile, &key);
583    if (ret) {
584	if (ret != ENOENT)
585	    return ret;
586	krb5_clear_error_string(context);
587	return 0;
588    }
589    db->hdb_master_key = key;
590    db->hdb_master_key_set = 1;
591    return ret;
592}
593
594krb5_error_code
595hdb_clear_master_key (krb5_context context,
596		      HDB *db)
597{
598    if (db->hdb_master_key_set) {
599	hdb_free_master_key(context, db->hdb_master_key);
600	db->hdb_master_key_set = 0;
601    }
602    return 0;
603}
604