mkey.c revision 120945
150477Speter/*
212029Sache * Copyright (c) 2000 - 2002 Kungliga Tekniska H�gskolan
312029Sache * (Royal Institute of Technology, Stockholm, Sweden).
412029Sache * All rights reserved.
512029Sache *
6118459Smtm * Redistribution and use in source and binary forms, with or without
7118459Smtm * modification, are permitted provided that the following conditions
886072Sache * are met:
988473Sphantom *
10117259Sache * 1. Redistributions of source code must retain the above copyright
1188473Sphantom *    notice, this list of conditions and the following disclaimer.
1277977Sache *
13118652Sache * 2. Redistributions in binary form must reproduce the above copyright
1477977Sache *    notice, this list of conditions and the following disclaimer in the
1577977Sache *    documentation and/or other materials provided with the distribution.
1688473Sphantom *
1777977Sache * 3. Neither the name of the Institute nor the names of its contributors
1877977Sache *    may be used to endorse or promote products derived from this software
1988473Sphantom *    without specific prior written permission.
20105965Sache *
2170484Sphantom * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2277977Sache * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2370484Sphantom * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2452388Sache * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2577977Sache * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26118174Sache * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27122151Sdavidxu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28115628Sache * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2947831Sfoxfair * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3023222Swosch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3112029Sache * SUCH DAMAGE.
3212029Sache */
3388473Sphantom
34108428Sache#include "hdb_locl.h"
35108428Sache#ifndef O_BINARY
36108428Sache#define O_BINARY 0
3793885Sphantom#endif
3893885Sphantom
39105445SacheRCSID("$Id: mkey.c,v 1.15 2003/03/28 02:01:33 lha Exp $");
4088473Sphantom
41105445Sachestruct hdb_master_key_data {
42115912Sache    krb5_keytab_entry keytab;
4378002Sache    krb5_crypto crypto;
4412029Sache    struct hdb_master_key_data *next;
4512029Sache};
4612029Sache
4737481Sbdevoid
4812029Sachehdb_free_master_key(krb5_context context, hdb_master_key mkey)
4912029Sache{
5012029Sache    struct hdb_master_key_data *ptr;
5112029Sache    while(mkey) {
5223224Sadam	krb5_kt_free_entry(context, &mkey->keytab);
53100872Sru	if (mkey->crypto)
5423224Sadam	    krb5_crypto_destroy(context, mkey->crypto);
5523222Swosch	ptr = mkey;
5623222Swosch	mkey = mkey->next;
5777977Sache	free(ptr);
5877988Sache    }
5923222Swosch}
6074398Sache
6177977Sachekrb5_error_code
6277988Sachehdb_process_master_key(krb5_context context,
6341760Sdillon		       int kvno, krb5_keyblock *key, krb5_enctype etype,
6423222Swosch		       hdb_master_key *mkey)
6577977Sache{
6677988Sache    krb5_error_code ret;
6723222Swosch
6855075Sache    *mkey = calloc(1, sizeof(**mkey));
6977977Sache    if(*mkey == NULL) {
7077988Sache	krb5_set_error_string(context, "malloc: out of memory");
7155075Sache	return ENOMEM;
7288314Sache    }
7388314Sache    (*mkey)->keytab.vno = kvno;
7488314Sache    ret = krb5_parse_name(context, "K/M", &(*mkey)->keytab.principal);
7588314Sache    if(ret)
76115912Sache	goto fail;
77115912Sache    ret = krb5_copy_keyblock_contents(context, key, &(*mkey)->keytab.keyblock);
78115912Sache    if(ret)
79115912Sache	goto fail;
8078002Sache    if(etype != 0)
8177977Sache	(*mkey)->keytab.keyblock.keytype = etype;
8278002Sache    (*mkey)->keytab.timestamp = time(NULL);
8378002Sache    ret = krb5_crypto_init(context, key, etype, &(*mkey)->crypto);
8493889Sphantom    if(ret)
8593889Sphantom	goto fail;
8612029Sache    return 0;
8712029Sache 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    size_t keysize;
381    Key *k;
382
383    for(i = 0; i < ent->keys.len; i++){
384	hdb_master_key key;
385
386	k = &ent->keys.val[i];
387	if(k->mkvno == NULL)
388	    continue;
389
390	key = find_master_key(&ent->keys.val[i], mkey);
391
392	if (key == NULL)
393	    return HDB_ERR_NO_MKEY;
394
395	ret = krb5_decrypt(context, key->crypto, HDB_KU_MKEY,
396			   k->key.keyvalue.data,
397			   k->key.keyvalue.length,
398			   &res);
399	if (ret)
400	    return ret;
401
402	/* fixup keylength if the key got padded when encrypting it */
403	ret = krb5_enctype_keysize(context, k->key.keytype, &keysize);
404	if (ret) {
405	    krb5_data_free(&res);
406	    return ret;
407	}
408	if (keysize > res.length) {
409	    krb5_data_free(&res);
410	    return KRB5_BAD_KEYSIZE;
411	}
412
413	memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
414	free(k->key.keyvalue.data);
415	k->key.keyvalue = res;
416	k->key.keyvalue.length = keysize;
417	free(k->mkvno);
418	k->mkvno = NULL;
419    }
420    return 0;
421}
422
423krb5_error_code
424hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent)
425{
426    if (db->master_key_set == 0)
427	return 0;
428    return hdb_unseal_keys_mkey(context, ent, db->master_key);
429}
430
431krb5_error_code
432hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
433{
434    int i;
435    krb5_error_code ret;
436    krb5_data res;
437    for(i = 0; i < ent->keys.len; i++){
438	Key *k = &ent->keys.val[i];
439	hdb_master_key key;
440
441	if(k->mkvno != NULL)
442	    continue;
443
444	key = find_master_key(k, mkey);
445
446	if (key == NULL)
447	    return HDB_ERR_NO_MKEY;
448
449	ret = krb5_encrypt(context, key->crypto, HDB_KU_MKEY,
450			   k->key.keyvalue.data,
451			   k->key.keyvalue.length,
452			   &res);
453	if (ret)
454	    return ret;
455
456	memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
457	free(k->key.keyvalue.data);
458	k->key.keyvalue = res;
459
460	k->mkvno = malloc(sizeof(*k->mkvno));
461	if (k->mkvno == NULL)
462	    return ENOMEM;
463	*k->mkvno = key->keytab.vno;
464    }
465    return 0;
466}
467
468krb5_error_code
469hdb_seal_keys(krb5_context context, HDB *db, hdb_entry *ent)
470{
471    if (db->master_key_set == 0)
472	return 0;
473
474    return hdb_seal_keys_mkey(context, ent, db->master_key);
475}
476
477krb5_error_code
478hdb_set_master_key (krb5_context context,
479		    HDB *db,
480		    krb5_keyblock *key)
481{
482    krb5_error_code ret;
483    hdb_master_key mkey;
484
485    ret = hdb_process_master_key(context, 0, key, 0, &mkey);
486    if (ret)
487	return ret;
488    db->master_key = mkey;
489#if 0 /* XXX - why? */
490    des_set_random_generator_seed(key.keyvalue.data);
491#endif
492    db->master_key_set = 1;
493    return 0;
494}
495
496krb5_error_code
497hdb_set_master_keyfile (krb5_context context,
498			HDB *db,
499			const char *keyfile)
500{
501    hdb_master_key key;
502    krb5_error_code ret;
503
504    ret = hdb_read_master_key(context, keyfile, &key);
505    if (ret) {
506	if (ret != ENOENT)
507	    return ret;
508	krb5_clear_error_string(context);
509	return 0;
510    }
511    db->master_key = key;
512    db->master_key_set = 1;
513    return ret;
514}
515
516krb5_error_code
517hdb_clear_master_key (krb5_context context,
518		      HDB *db)
519{
520    if (db->master_key_set) {
521	hdb_free_master_key(context, db->master_key);
522	db->master_key_set = 0;
523    }
524    return 0;
525}
526