mkey.c revision 102644
1/*
2 * Copyright (c) 2000 - 2002 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,v 1.14 2002/08/16 18:59:49 assar Exp $");
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	p->keytab = entry;
133	ret = krb5_crypto_init(context, &p->keytab.keyblock, 0, &p->crypto);
134	p->next = *mkey;
135	*mkey = p;
136    }
137    krb5_kt_end_seq_get(context, id, &cursor);
138  out:
139    krb5_kt_close(context, id);
140    return ret;
141}
142
143/* read a MIT master keyfile */
144static krb5_error_code
145read_master_mit(krb5_context context, const char *filename,
146		hdb_master_key *mkey)
147{
148    int fd;
149    krb5_error_code ret;
150    krb5_storage *sp;
151    u_int16_t enctype;
152    krb5_keyblock key;
153
154    fd = open(filename, O_RDONLY | O_BINARY);
155    if(fd < 0) {
156	int save_errno = errno;
157	krb5_set_error_string(context, "failed to open %s: %s", filename,
158			      strerror(save_errno));
159	return save_errno;
160    }
161    sp = krb5_storage_from_fd(fd);
162    if(sp == NULL) {
163	close(fd);
164	return errno;
165    }
166    krb5_storage_set_flags(sp, KRB5_STORAGE_HOST_BYTEORDER);
167#if 0
168    /* could possibly use ret_keyblock here, but do it with more
169       checks for now */
170    ret = krb5_ret_keyblock(sp, &key);
171#else
172    ret = krb5_ret_int16(sp, &enctype);
173    if((htons(enctype) & 0xff00) == 0x3000) {
174	krb5_set_error_string(context, "unknown keytype in %s: %#x, expected %#x",
175			      filename, htons(enctype), 0x3000);
176	ret = HEIM_ERR_BAD_MKEY;
177	goto out;
178    }
179    key.keytype = enctype;
180    ret = krb5_ret_data(sp, &key.keyvalue);
181    if(ret)
182	goto out;
183#endif
184    ret = hdb_process_master_key(context, 0, &key, 0, mkey);
185    krb5_free_keyblock_contents(context, &key);
186  out:
187    krb5_storage_free(sp);
188    close(fd);
189    return ret;
190}
191
192/* read an old master key file */
193static krb5_error_code
194read_master_encryptionkey(krb5_context context, const char *filename,
195			  hdb_master_key *mkey)
196{
197    int fd;
198    krb5_keyblock key;
199    krb5_error_code ret;
200    unsigned char buf[256];
201    ssize_t len;
202    size_t ret_len;
203
204    fd = open(filename, O_RDONLY | O_BINARY);
205    if(fd < 0) {
206	int save_errno = errno;
207	krb5_set_error_string(context, "failed to open %s: %s",
208			      filename, strerror(save_errno));
209	return save_errno;
210    }
211
212    len = read(fd, buf, sizeof(buf));
213    close(fd);
214    if(len < 0) {
215	int save_errno = errno;
216	krb5_set_error_string(context, "error reading %s: %s",
217			      filename, strerror(save_errno));
218	return save_errno;
219    }
220
221    ret = decode_EncryptionKey(buf, len, &key, &ret_len);
222    memset(buf, 0, sizeof(buf));
223    if(ret)
224	return ret;
225
226    /* Originally, the keytype was just that, and later it got changed
227       to des-cbc-md5, but we always used des in cfb64 mode. This
228       should cover all cases, but will break if someone has hacked
229       this code to really use des-cbc-md5 -- but then that's not my
230       problem. */
231    if(key.keytype == KEYTYPE_DES || key.keytype == ETYPE_DES_CBC_MD5)
232	key.keytype = ETYPE_DES_CFB64_NONE;
233
234    ret = hdb_process_master_key(context, 0, &key, 0, mkey);
235    krb5_free_keyblock_contents(context, &key);
236    return ret;
237}
238
239/* read a krb4 /.k style file */
240static krb5_error_code
241read_master_krb4(krb5_context context, const char *filename,
242		 hdb_master_key *mkey)
243{
244    int fd;
245    krb5_keyblock key;
246    krb5_error_code ret;
247    unsigned char buf[256];
248    ssize_t len;
249
250    fd = open(filename, O_RDONLY | O_BINARY);
251    if(fd < 0) {
252	int save_errno = errno;
253	krb5_set_error_string(context, "failed to open %s: %s",
254			      filename, strerror(save_errno));
255	return save_errno;
256    }
257
258    len = read(fd, buf, sizeof(buf));
259    close(fd);
260    if(len < 0) {
261	int save_errno = errno;
262	krb5_set_error_string(context, "error reading %s: %s",
263			      filename, strerror(save_errno));
264	return save_errno;
265    }
266    if(len != 8) {
267	krb5_set_error_string(context, "bad contents of %s", filename);
268	return HEIM_ERR_EOF; /* XXX file might be too large */
269    }
270
271    memset(&key, 0, sizeof(key));
272    key.keytype = ETYPE_DES_PCBC_NONE;
273    ret = krb5_data_copy(&key.keyvalue, buf, len);
274    memset(buf, 0, sizeof(buf));
275    if(ret)
276	return ret;
277
278    ret = hdb_process_master_key(context, 0, &key, 0, mkey);
279    krb5_free_keyblock_contents(context, &key);
280    return ret;
281}
282
283krb5_error_code
284hdb_read_master_key(krb5_context context, const char *filename,
285		    hdb_master_key *mkey)
286{
287    FILE *f;
288    unsigned char buf[16];
289    krb5_error_code ret;
290
291    off_t len;
292
293    *mkey = NULL;
294
295    if(filename == NULL)
296	filename = HDB_DB_DIR "/m-key";
297
298    f = fopen(filename, "r");
299    if(f == NULL) {
300	int save_errno = errno;
301	krb5_set_error_string(context, "failed to open %s: %s",
302			      filename, strerror(save_errno));
303	return save_errno;
304    }
305
306    if(fread(buf, 1, 2, f) != 2) {
307	krb5_set_error_string(context, "end of file reading %s", filename);
308	fclose(f);
309	return HEIM_ERR_EOF;
310    }
311
312    fseek(f, 0, SEEK_END);
313    len = ftell(f);
314
315    if(fclose(f) != 0)
316	return errno;
317
318    if(len < 0)
319	return errno;
320
321    if(len == 8) {
322	ret = read_master_krb4(context, filename, mkey);
323    } else if(buf[0] == 0x30 && len <= 127 && buf[1] == len - 2) {
324	ret = read_master_encryptionkey(context, filename, mkey);
325    } else if(buf[0] == 5 && buf[1] >= 1 && buf[1] <= 2) {
326	ret = read_master_keytab(context, filename, mkey);
327    } else {
328	ret = read_master_mit(context, filename, mkey);
329    }
330    return ret;
331}
332
333krb5_error_code
334hdb_write_master_key(krb5_context context, const char *filename,
335		     hdb_master_key mkey)
336{
337    krb5_error_code ret;
338    hdb_master_key p;
339    krb5_keytab kt;
340
341    if(filename == NULL)
342	filename = HDB_DB_DIR "/m-key";
343
344    ret = krb5_kt_resolve(context, filename, &kt);
345    if(ret)
346	return ret;
347
348    for(p = mkey; p; p = p->next) {
349	ret = krb5_kt_add_entry(context, kt, &p->keytab);
350    }
351
352    krb5_kt_close(context, kt);
353
354    return ret;
355}
356
357static hdb_master_key
358find_master_key(Key *key, hdb_master_key mkey)
359{
360    hdb_master_key ret = NULL;
361    while(mkey) {
362	if(ret == NULL && mkey->keytab.vno == 0)
363	    ret = mkey;
364	if(key->mkvno == NULL) {
365	    if(ret == NULL || mkey->keytab.vno > ret->keytab.vno)
366		ret = mkey;
367	} else if(mkey->keytab.vno == *key->mkvno)
368	    return mkey;
369	mkey = mkey->next;
370    }
371    return ret;
372}
373
374krb5_error_code
375hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
376{
377    int i;
378    krb5_error_code ret;
379    krb5_data res;
380    Key *k;
381
382    for(i = 0; i < ent->keys.len; i++){
383	hdb_master_key key;
384
385	k = &ent->keys.val[i];
386	if(k->mkvno == NULL)
387	    continue;
388
389	key = find_master_key(&ent->keys.val[i], mkey);
390
391	if (key == NULL)
392	    return HDB_ERR_NO_MKEY;
393
394	ret = krb5_decrypt(context, key->crypto, HDB_KU_MKEY,
395			   k->key.keyvalue.data,
396			   k->key.keyvalue.length,
397			   &res);
398	if (ret)
399	    return ret;
400
401	memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
402	free(k->key.keyvalue.data);
403	k->key.keyvalue = res;
404	free(k->mkvno);
405	k->mkvno = NULL;
406    }
407    return 0;
408}
409
410krb5_error_code
411hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent)
412{
413    if (db->master_key_set == 0)
414	return 0;
415    return hdb_unseal_keys_mkey(context, ent, db->master_key);
416}
417
418krb5_error_code
419hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
420{
421    int i;
422    krb5_error_code ret;
423    krb5_data res;
424    for(i = 0; i < ent->keys.len; i++){
425	Key *k = &ent->keys.val[i];
426	hdb_master_key key;
427
428	if(k->mkvno != NULL)
429	    continue;
430
431	key = find_master_key(k, mkey);
432
433	if (key == NULL)
434	    return HDB_ERR_NO_MKEY;
435
436	ret = krb5_encrypt(context, key->crypto, HDB_KU_MKEY,
437			   k->key.keyvalue.data,
438			   k->key.keyvalue.length,
439			   &res);
440	if (ret)
441	    return ret;
442
443	memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
444	free(k->key.keyvalue.data);
445	k->key.keyvalue = res;
446
447	k->mkvno = malloc(sizeof(*k->mkvno));
448	if (k->mkvno == NULL)
449	    return ENOMEM;
450	*k->mkvno = key->keytab.vno;
451    }
452    return 0;
453}
454
455krb5_error_code
456hdb_seal_keys(krb5_context context, HDB *db, hdb_entry *ent)
457{
458    if (db->master_key_set == 0)
459	return 0;
460
461    return hdb_seal_keys_mkey(context, ent, db->master_key);
462}
463
464krb5_error_code
465hdb_set_master_key (krb5_context context,
466		    HDB *db,
467		    krb5_keyblock *key)
468{
469    krb5_error_code ret;
470    hdb_master_key mkey;
471
472    ret = hdb_process_master_key(context, 0, key, 0, &mkey);
473    if (ret)
474	return ret;
475    db->master_key = mkey;
476#if 0 /* XXX - why? */
477    des_set_random_generator_seed(key.keyvalue.data);
478#endif
479    db->master_key_set = 1;
480    return 0;
481}
482
483krb5_error_code
484hdb_set_master_keyfile (krb5_context context,
485			HDB *db,
486			const char *keyfile)
487{
488    hdb_master_key key;
489    krb5_error_code ret;
490
491    ret = hdb_read_master_key(context, keyfile, &key);
492    if (ret) {
493	if (ret != ENOENT)
494	    return ret;
495	krb5_clear_error_string(context);
496	return 0;
497    }
498    db->master_key = key;
499    db->master_key_set = 1;
500    return ret;
501}
502
503krb5_error_code
504hdb_clear_master_key (krb5_context context,
505		      HDB *db)
506{
507    if (db->master_key_set) {
508	hdb_free_master_key(context, db->master_key);
509	db->master_key_set = 0;
510    }
511    return 0;
512}
513