mkey.c revision 72445
1/*
2 * Copyright (c) 2000 - 2001 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.8 2001/01/30 01:20:57 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	krb5_crypto_destroy(context, mkey->crypto);
54	ptr = mkey;
55	mkey = mkey->next;
56	free(ptr);
57    }
58}
59
60krb5_error_code
61hdb_process_master_key(krb5_context context,
62		       int kvno, krb5_keyblock *key, krb5_enctype etype,
63		       hdb_master_key *mkey)
64{
65    krb5_error_code ret;
66    *mkey = calloc(1, sizeof(**mkey));
67    if(*mkey == NULL)
68	return ENOMEM;
69    (*mkey)->keytab.vno = kvno;
70    ret = krb5_parse_name(context, "K/M", &(*mkey)->keytab.principal);
71    ret = krb5_copy_keyblock_contents(context, key, &(*mkey)->keytab.keyblock);
72    if(ret) {
73	free(*mkey);
74	*mkey = NULL;
75	return ret;
76    }
77    if(etype != 0)
78	(*mkey)->keytab.keyblock.keytype = etype;
79    (*mkey)->keytab.timestamp = time(NULL);
80    ret = krb5_crypto_init(context, key, etype, &(*mkey)->crypto);
81    if(ret) {
82	krb5_free_keyblock_contents(context, &(*mkey)->keytab.keyblock);
83	free(*mkey);
84	*mkey = NULL;
85    }
86    return ret;
87}
88
89krb5_error_code
90hdb_add_master_key(krb5_context context, krb5_keyblock *key,
91		   hdb_master_key *inout)
92{
93    int vno = 0;
94    hdb_master_key p;
95    krb5_error_code ret;
96
97    for(p = *inout; p; p = p->next)
98	vno = max(vno, p->keytab.vno);
99    vno++;
100    ret = hdb_process_master_key(context, vno, key, 0, &p);
101    if(ret)
102	return ret;
103    p->next = *inout;
104    *inout = p;
105    return 0;
106}
107
108static krb5_error_code
109read_master_keytab(krb5_context context, const char *filename,
110		   hdb_master_key *mkey)
111{
112    krb5_error_code ret;
113    krb5_keytab id;
114    krb5_kt_cursor cursor;
115    krb5_keytab_entry entry;
116    hdb_master_key p;
117
118    ret = krb5_kt_resolve(context, filename, &id);
119    if(ret)
120	return ret;
121
122    ret = krb5_kt_start_seq_get(context, id, &cursor);
123    if(ret)
124	goto out;
125    *mkey = NULL;
126    while(krb5_kt_next_entry(context, id, &entry, &cursor) == 0) {
127	p = calloc(1, sizeof(*p));
128	p->keytab = entry;
129	ret = krb5_crypto_init(context, &p->keytab.keyblock, 0, &p->crypto);
130	p->next = *mkey;
131	*mkey = p;
132    }
133    krb5_kt_end_seq_get(context, id, &cursor);
134  out:
135    krb5_kt_close(context, id);
136    return ret;
137}
138
139/* read a MIT master keyfile */
140static krb5_error_code
141read_master_mit(krb5_context context, const char *filename,
142		hdb_master_key *mkey)
143{
144    int fd;
145    krb5_error_code ret;
146    krb5_storage *sp;
147    u_int16_t enctype;
148    krb5_keyblock key;
149
150    fd = open(filename, O_RDONLY | O_BINARY);
151    if(fd < 0)
152	return errno;
153    sp = krb5_storage_from_fd(fd);
154    if(sp == NULL) {
155	close(fd);
156	return errno;
157    }
158    krb5_storage_set_flags(sp, KRB5_STORAGE_HOST_BYTEORDER);
159#if 0
160    /* could possibly use ret_keyblock here, but do it with more
161       checks for now */
162    ret = krb5_ret_keyblock(sp, &key);
163#else
164    ret = krb5_ret_int16(sp, &enctype);
165    if((htons(enctype) & 0xff00) == 0x3000) {
166	ret = HEIM_ERR_BAD_MKEY;
167	goto out;
168    }
169    key.keytype = enctype;
170    ret = krb5_ret_data(sp, &key.keyvalue);
171    if(ret)
172	goto out;
173#endif
174    ret = hdb_process_master_key(context, 0, &key, 0, mkey);
175    krb5_free_keyblock_contents(context, &key);
176  out:
177    krb5_storage_free(sp);
178    close(fd);
179    return ret;
180}
181
182/* read an old master key file */
183static krb5_error_code
184read_master_encryptionkey(krb5_context context, const char *filename,
185			  hdb_master_key *mkey)
186{
187    int fd;
188    krb5_keyblock key;
189    krb5_error_code ret;
190    unsigned char buf[256];
191    ssize_t len;
192
193    fd = open(filename, O_RDONLY | O_BINARY);
194    if(fd < 0)
195	return errno;
196
197    len = read(fd, buf, sizeof(buf));
198    close(fd);
199    if(len < 0)
200	return errno;
201
202    ret = decode_EncryptionKey(buf, len, &key, &len);
203    memset(buf, 0, sizeof(buf));
204    if(ret)
205	return ret;
206
207    /* Originally, the keytype was just that, and later it got changed
208       to des-cbc-md5, but we always used des in cfb64 mode. This
209       should cover all cases, but will break if someone has hacked
210       this code to really use des-cbc-md5 -- but then that's not my
211       problem. */
212    if(key.keytype == KEYTYPE_DES || key.keytype == ETYPE_DES_CBC_MD5)
213	key.keytype = ETYPE_DES_CFB64_NONE;
214
215    ret = hdb_process_master_key(context, 0, &key, 0, mkey);
216    krb5_free_keyblock_contents(context, &key);
217    return ret;
218}
219
220/* read a krb4 /.k style file */
221static krb5_error_code
222read_master_krb4(krb5_context context, const char *filename,
223		 hdb_master_key *mkey)
224{
225    int fd;
226    krb5_keyblock key;
227    krb5_error_code ret;
228    unsigned char buf[256];
229    ssize_t len;
230
231    fd = open(filename, O_RDONLY | O_BINARY);
232    if(fd < 0)
233	return errno;
234
235    len = read(fd, buf, sizeof(buf));
236    close(fd);
237    if(len < 0)
238	return errno;
239
240    memset(&key, 0, sizeof(key));
241    key.keytype = ETYPE_DES_PCBC_NONE;
242    ret = krb5_data_copy(&key.keyvalue, buf, len);
243    memset(buf, 0, sizeof(buf));
244    if(ret)
245	return ret;
246
247    ret = hdb_process_master_key(context, 0, &key, 0, mkey);
248    krb5_free_keyblock_contents(context, &key);
249    return ret;
250}
251
252krb5_error_code
253hdb_read_master_key(krb5_context context, const char *filename,
254		    hdb_master_key *mkey)
255{
256    FILE *f;
257    unsigned char buf[16];
258    krb5_error_code ret;
259
260    off_t len;
261
262    *mkey = NULL;
263
264    if(filename == NULL)
265	filename = HDB_DB_DIR "/m-key";
266
267    f = fopen(filename, "r");
268    if(f == NULL)
269	return errno;
270
271    if(fread(buf, 1, 2, f) != 2) {
272	fclose(f);
273	return HEIM_ERR_EOF;
274    }
275
276    fseek(f, 0, SEEK_END);
277    len = ftell(f);
278
279    if(fclose(f) != 0)
280	return errno;
281
282    if(len < 0)
283	return errno;
284
285    if(len == 8) {
286	ret = read_master_krb4(context, filename, mkey);
287    } else if(buf[0] == 0x30 && len <= 127 && buf[1] == len - 2) {
288	ret = read_master_encryptionkey(context, filename, mkey);
289    } else if(buf[0] == 5 && buf[1] >= 1 && buf[1] <= 2) {
290	ret = read_master_keytab(context, filename, mkey);
291    } else {
292	ret = read_master_mit(context, filename, mkey);
293    }
294    return ret;
295}
296
297krb5_error_code
298hdb_write_master_key(krb5_context context, const char *filename,
299		     hdb_master_key mkey)
300{
301    krb5_error_code ret;
302    hdb_master_key p;
303    krb5_keytab kt;
304
305    if(filename == NULL)
306	filename = HDB_DB_DIR "/m-key";
307
308    ret = krb5_kt_resolve(context, filename, &kt);
309    if(ret)
310	return ret;
311
312    for(p = mkey; p; p = p->next) {
313	ret = krb5_kt_add_entry(context, kt, &p->keytab);
314    }
315
316    krb5_kt_close(context, kt);
317
318    return ret;
319}
320
321static hdb_master_key
322find_master_key(Key *key, hdb_master_key mkey)
323{
324    hdb_master_key ret = NULL;
325    while(mkey) {
326	if(ret == NULL && mkey->keytab.vno == 0)
327	    ret = mkey;
328	if(key->mkvno == NULL) {
329	    if(ret == NULL || mkey->keytab.vno > ret->keytab.vno)
330		ret = mkey;
331	} else if(mkey->keytab.vno == *key->mkvno)
332	    return mkey;
333	mkey = mkey->next;
334    }
335    return ret;
336}
337
338krb5_error_code
339hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
340{
341    int i;
342    krb5_error_code ret;
343    krb5_data res;
344    Key *k;
345
346    for(i = 0; i < ent->keys.len; i++){
347	hdb_master_key key;
348
349	k = &ent->keys.val[i];
350	if(k->mkvno == NULL)
351	    continue;
352
353	key = find_master_key(&ent->keys.val[i], mkey);
354
355	if (key == NULL)
356	    return HDB_ERR_NO_MKEY;
357
358	ret = krb5_decrypt(context, key->crypto, HDB_KU_MKEY,
359			   k->key.keyvalue.data,
360			   k->key.keyvalue.length,
361			   &res);
362	if (ret)
363	    return ret;
364
365	memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
366	free(k->key.keyvalue.data);
367	k->key.keyvalue = res;
368	free(k->mkvno);
369	k->mkvno = NULL;
370    }
371    return 0;
372}
373
374krb5_error_code
375hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent)
376{
377    if (db->master_key_set == 0)
378	return 0;
379    return hdb_unseal_keys_mkey(context, ent, db->master_key);
380}
381
382krb5_error_code
383hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
384{
385    int i;
386    krb5_error_code ret;
387    krb5_data res;
388    for(i = 0; i < ent->keys.len; i++){
389	Key *k = &ent->keys.val[i];
390	hdb_master_key key;
391
392	if(k->mkvno != NULL)
393	    continue;
394
395	key = find_master_key(k, mkey);
396
397	if (key == NULL)
398	    return HDB_ERR_NO_MKEY;
399
400	ret = krb5_encrypt(context, key->crypto, HDB_KU_MKEY,
401			   k->key.keyvalue.data,
402			   k->key.keyvalue.length,
403			   &res);
404	if (ret)
405	    return ret;
406
407	memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
408	free(k->key.keyvalue.data);
409	k->key.keyvalue = res;
410
411	k->mkvno = malloc(sizeof(*k->mkvno));
412	if (k->mkvno == NULL)
413	    return ENOMEM;
414	*k->mkvno = key->keytab.vno;
415    }
416    return 0;
417}
418
419krb5_error_code
420hdb_seal_keys(krb5_context context, HDB *db, hdb_entry *ent)
421{
422    if (db->master_key_set == 0)
423	return 0;
424
425    return hdb_seal_keys_mkey(context, ent, db->master_key);
426}
427
428krb5_error_code
429hdb_set_master_key (krb5_context context,
430		    HDB *db,
431		    krb5_keyblock *key)
432{
433    krb5_error_code ret;
434    hdb_master_key mkey;
435
436    ret = hdb_process_master_key(context, 0, key, 0, &mkey);
437    if (ret)
438	return ret;
439    db->master_key = mkey;
440#if 0 /* XXX - why? */
441    des_set_random_generator_seed(key.keyvalue.data);
442#endif
443    db->master_key_set = 1;
444    return 0;
445}
446
447krb5_error_code
448hdb_set_master_keyfile (krb5_context context,
449			HDB *db,
450			const char *keyfile)
451{
452    hdb_master_key key;
453    krb5_error_code ret;
454
455    ret = hdb_read_master_key(context, keyfile, &key);
456    if (ret) {
457	if (ret != ENOENT)
458	    return ret;
459	return 0;
460    }
461    db->master_key = key;
462    db->master_key_set = 1;
463    return ret;
464}
465
466krb5_error_code
467hdb_clear_master_key (krb5_context context,
468		      HDB *db)
469{
470    if (db->master_key_set) {
471	hdb_free_master_key(context, db->master_key);
472	db->master_key_set = 0;
473    }
474    return 0;
475}
476