xref: /freebsd/crypto/heimdal/lib/hdb/mkey.c (revision 5000d023a446b81f6d45ed59aa379607ec814f01)
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 #include <assert.h>
36 #ifndef O_BINARY
37 #define O_BINARY 0
38 #endif
39 
40 struct hdb_master_key_data {
41     krb5_keytab_entry keytab;
42     krb5_crypto crypto;
43     struct hdb_master_key_data *next;
44     unsigned int key_usage;
45 };
46 
47 void
hdb_free_master_key(krb5_context context,hdb_master_key mkey)48 hdb_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 
61 krb5_error_code
hdb_process_master_key(krb5_context context,int kvno,krb5_keyblock * key,krb5_enctype etype,hdb_master_key * mkey)62 hdb_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_message(context, ENOMEM, "malloc: out of memory");
71 	return ENOMEM;
72     }
73     (*mkey)->key_usage = HDB_KU_MKEY;
74     (*mkey)->keytab.vno = kvno;
75     ret = krb5_parse_name(context, "K/M", &(*mkey)->keytab.principal);
76     if(ret)
77 	goto fail;
78     ret = krb5_copy_keyblock_contents(context, key, &(*mkey)->keytab.keyblock);
79     if(ret)
80 	goto fail;
81     if(etype != 0)
82 	(*mkey)->keytab.keyblock.keytype = etype;
83     (*mkey)->keytab.timestamp = time(NULL);
84     ret = krb5_crypto_init(context, key, etype, &(*mkey)->crypto);
85     if(ret)
86 	goto fail;
87     return 0;
88  fail:
89     hdb_free_master_key(context, *mkey);
90     *mkey = NULL;
91     return ret;
92 }
93 
94 krb5_error_code
hdb_add_master_key(krb5_context context,krb5_keyblock * key,hdb_master_key * inout)95 hdb_add_master_key(krb5_context context, krb5_keyblock *key,
96 		   hdb_master_key *inout)
97 {
98     int vno = 0;
99     hdb_master_key p;
100     krb5_error_code ret;
101 
102     for(p = *inout; p; p = p->next)
103 	vno = max(vno, p->keytab.vno);
104     vno++;
105     ret = hdb_process_master_key(context, vno, key, 0, &p);
106     if(ret)
107 	return ret;
108     p->next = *inout;
109     *inout = p;
110     return 0;
111 }
112 
113 static krb5_error_code
read_master_keytab(krb5_context context,const char * filename,hdb_master_key * mkey)114 read_master_keytab(krb5_context context, const char *filename,
115 		   hdb_master_key *mkey)
116 {
117     krb5_error_code ret;
118     krb5_keytab id;
119     krb5_kt_cursor cursor;
120     krb5_keytab_entry entry;
121     hdb_master_key p;
122 
123     ret = krb5_kt_resolve(context, filename, &id);
124     if(ret)
125 	return ret;
126 
127     ret = krb5_kt_start_seq_get(context, id, &cursor);
128     if(ret)
129 	goto out;
130     *mkey = NULL;
131     while(krb5_kt_next_entry(context, id, &entry, &cursor) == 0) {
132 	p = calloc(1, sizeof(*p));
133 	if(p == NULL) {
134 	    krb5_kt_end_seq_get(context, id, &cursor);
135 	    ret = ENOMEM;
136 	    goto out;
137 	}
138 	p->keytab = entry;
139 	ret = krb5_crypto_init(context, &p->keytab.keyblock, 0, &p->crypto);
140 	p->next = *mkey;
141 	*mkey = p;
142     }
143     krb5_kt_end_seq_get(context, id, &cursor);
144   out:
145     krb5_kt_close(context, id);
146     return ret;
147 }
148 
149 /* read a MIT master keyfile */
150 static krb5_error_code
read_master_mit(krb5_context context,const char * filename,int byteorder,hdb_master_key * mkey)151 read_master_mit(krb5_context context, const char *filename,
152 		int byteorder, hdb_master_key *mkey)
153 {
154     int fd;
155     krb5_error_code ret;
156     krb5_storage *sp;
157     int16_t enctype;
158     krb5_keyblock key;
159 
160     fd = open(filename, O_RDONLY | O_BINARY);
161     if(fd < 0) {
162 	int save_errno = errno;
163 	krb5_set_error_message(context, save_errno, "failed to open %s: %s",
164 			       filename, strerror(save_errno));
165 	return save_errno;
166     }
167     sp = krb5_storage_from_fd(fd);
168     if(sp == NULL) {
169 	close(fd);
170 	return errno;
171     }
172     krb5_storage_set_flags(sp, byteorder);
173     /* could possibly use ret_keyblock here, but do it with more
174        checks for now */
175     {
176 	ret = krb5_ret_int16(sp, &enctype);
177 	if (ret)
178 	    goto out;
179 	ret = krb5_enctype_valid(context, enctype);
180 	if (ret)
181 	   goto out;
182 	key.keytype = enctype;
183 	ret = krb5_ret_data(sp, &key.keyvalue);
184 	if(ret)
185 	    goto out;
186     }
187     ret = hdb_process_master_key(context, 1, &key, 0, mkey);
188     krb5_free_keyblock_contents(context, &key);
189   out:
190     krb5_storage_free(sp);
191     close(fd);
192     return ret;
193 }
194 
195 /* read an old master key file */
196 static krb5_error_code
read_master_encryptionkey(krb5_context context,const char * filename,hdb_master_key * mkey)197 read_master_encryptionkey(krb5_context context, const char *filename,
198 			  hdb_master_key *mkey)
199 {
200     int fd;
201     krb5_keyblock key;
202     krb5_error_code ret;
203     unsigned char buf[256];
204     ssize_t len;
205     size_t ret_len;
206 
207     fd = open(filename, O_RDONLY | O_BINARY);
208     if(fd < 0) {
209 	int save_errno = errno;
210 	krb5_set_error_message(context, save_errno, "failed to open %s: %s",
211 			      filename, strerror(save_errno));
212 	return save_errno;
213     }
214 
215     len = read(fd, buf, sizeof(buf));
216     close(fd);
217     if(len < 0) {
218 	int save_errno = errno;
219 	krb5_set_error_message(context, save_errno, "error reading %s: %s",
220 			      filename, strerror(save_errno));
221 	return save_errno;
222     }
223 
224     ret = decode_EncryptionKey(buf, len, &key, &ret_len);
225     memset(buf, 0, sizeof(buf));
226     if(ret)
227 	return ret;
228 
229     /* Originally, the keytype was just that, and later it got changed
230        to des-cbc-md5, but we always used des in cfb64 mode. This
231        should cover all cases, but will break if someone has hacked
232        this code to really use des-cbc-md5 -- but then that's not my
233        problem. */
234     if(key.keytype == ETYPE_DES_CBC_CRC || key.keytype == ETYPE_DES_CBC_MD5)
235 	key.keytype = ETYPE_DES_CFB64_NONE;
236 
237     ret = hdb_process_master_key(context, 0, &key, 0, mkey);
238     krb5_free_keyblock_contents(context, &key);
239     return ret;
240 }
241 
242 /* read a krb4 /.k style file */
243 static krb5_error_code
read_master_krb4(krb5_context context,const char * filename,hdb_master_key * mkey)244 read_master_krb4(krb5_context context, const char *filename,
245 		 hdb_master_key *mkey)
246 {
247     int fd;
248     krb5_keyblock key;
249     krb5_error_code ret;
250     unsigned char buf[256];
251     ssize_t len;
252 
253     fd = open(filename, O_RDONLY | O_BINARY);
254     if(fd < 0) {
255 	int save_errno = errno;
256 	krb5_set_error_message(context, save_errno, "failed to open %s: %s",
257 			       filename, strerror(save_errno));
258 	return save_errno;
259     }
260 
261     len = read(fd, buf, sizeof(buf));
262     close(fd);
263     if(len < 0) {
264 	int save_errno = errno;
265 	krb5_set_error_message(context, save_errno, "error reading %s: %s",
266 			       filename, strerror(save_errno));
267 	return save_errno;
268     }
269     if(len != 8) {
270 	krb5_set_error_message(context, HEIM_ERR_EOF,
271 			       "bad contents of %s", filename);
272 	return HEIM_ERR_EOF; /* XXX file might be too large */
273     }
274 
275     memset(&key, 0, sizeof(key));
276     key.keytype = ETYPE_DES_PCBC_NONE;
277     ret = krb5_data_copy(&key.keyvalue, buf, len);
278     memset(buf, 0, sizeof(buf));
279     if(ret)
280 	return ret;
281 
282     ret = hdb_process_master_key(context, 0, &key, 0, mkey);
283     krb5_free_keyblock_contents(context, &key);
284     return ret;
285 }
286 
287 krb5_error_code
hdb_read_master_key(krb5_context context,const char * filename,hdb_master_key * mkey)288 hdb_read_master_key(krb5_context context, const char *filename,
289 		    hdb_master_key *mkey)
290 {
291     FILE *f;
292     unsigned char buf[16];
293     krb5_error_code ret;
294 
295     off_t len;
296 
297     *mkey = NULL;
298 
299     if(filename == NULL)
300 	filename = HDB_DB_DIR "/m-key";
301 
302     f = fopen(filename, "r");
303     if(f == NULL) {
304 	int save_errno = errno;
305 	krb5_set_error_message(context, save_errno, "failed to open %s: %s",
306 			       filename, strerror(save_errno));
307 	return save_errno;
308     }
309 
310     if(fread(buf, 1, 2, f) != 2) {
311 	fclose(f);
312 	krb5_set_error_message(context, HEIM_ERR_EOF, "end of file reading %s", filename);
313 	return HEIM_ERR_EOF;
314     }
315 
316     fseek(f, 0, SEEK_END);
317     len = ftell(f);
318 
319     if(fclose(f) != 0)
320 	return errno;
321 
322     if(len < 0)
323 	return errno;
324 
325     if(len == 8) {
326 	ret = read_master_krb4(context, filename, mkey);
327     } else if(buf[0] == 0x30 && len <= 127 && buf[1] == len - 2) {
328 	ret = read_master_encryptionkey(context, filename, mkey);
329     } else if(buf[0] == 5 && buf[1] >= 1 && buf[1] <= 2) {
330 	ret = read_master_keytab(context, filename, mkey);
331     } else {
332       /*
333        * Check both LittleEndian and BigEndian since they key file
334        * might be moved from a machine with diffrent byte order, or
335        * its running on MacOS X that always uses BE master keys.
336        */
337       ret = read_master_mit(context, filename, KRB5_STORAGE_BYTEORDER_LE, mkey);
338       if (ret)
339           ret = read_master_mit(context, filename, KRB5_STORAGE_BYTEORDER_BE, mkey);
340     }
341     return ret;
342 }
343 
344 krb5_error_code
hdb_write_master_key(krb5_context context,const char * filename,hdb_master_key mkey)345 hdb_write_master_key(krb5_context context, const char *filename,
346 		     hdb_master_key mkey)
347 {
348     krb5_error_code ret;
349     hdb_master_key p;
350     krb5_keytab kt;
351 
352     if(filename == NULL)
353 	filename = HDB_DB_DIR "/m-key";
354 
355     ret = krb5_kt_resolve(context, filename, &kt);
356     if(ret)
357 	return ret;
358 
359     for(p = mkey; p; p = p->next) {
360 	ret = krb5_kt_add_entry(context, kt, &p->keytab);
361     }
362 
363     krb5_kt_close(context, kt);
364 
365     return ret;
366 }
367 
368 krb5_error_code
_hdb_set_master_key_usage(krb5_context context,HDB * db,unsigned int key_usage)369 _hdb_set_master_key_usage(krb5_context context, HDB *db, unsigned int key_usage)
370 {
371     if (db->hdb_master_key_set == 0)
372 	return HDB_ERR_NO_MKEY;
373     db->hdb_master_key->key_usage = key_usage;
374     return 0;
375 }
376 
377 hdb_master_key
_hdb_find_master_key(uint32_t * mkvno,hdb_master_key mkey)378 _hdb_find_master_key(uint32_t *mkvno, hdb_master_key mkey)
379 {
380     hdb_master_key ret = NULL;
381     while(mkey) {
382 	if(ret == NULL && mkey->keytab.vno == 0)
383 	    ret = mkey;
384 	if(mkvno == NULL) {
385 	    if(ret == NULL || mkey->keytab.vno > ret->keytab.vno)
386 		ret = mkey;
387 	} else if((uint32_t)mkey->keytab.vno == *mkvno)
388 	    return mkey;
389 	mkey = mkey->next;
390     }
391     return ret;
392 }
393 
394 int
_hdb_mkey_version(hdb_master_key mkey)395 _hdb_mkey_version(hdb_master_key mkey)
396 {
397     return mkey->keytab.vno;
398 }
399 
400 int
_hdb_mkey_decrypt(krb5_context context,hdb_master_key key,krb5_key_usage usage,void * ptr,size_t size,krb5_data * res)401 _hdb_mkey_decrypt(krb5_context context, hdb_master_key key,
402 		  krb5_key_usage usage,
403 		  void *ptr, size_t size, krb5_data *res)
404 {
405     return krb5_decrypt(context, key->crypto, usage,
406 			ptr, size, res);
407 }
408 
409 int
_hdb_mkey_encrypt(krb5_context context,hdb_master_key key,krb5_key_usage usage,const void * ptr,size_t size,krb5_data * res)410 _hdb_mkey_encrypt(krb5_context context, hdb_master_key key,
411 		  krb5_key_usage usage,
412 		  const void *ptr, size_t size, krb5_data *res)
413 {
414     return krb5_encrypt(context, key->crypto, usage,
415 			ptr, size, res);
416 }
417 
418 /*
419  * Unseal and optionally reseal the key in the MIT KDC master key.
420  * If mit_key != NULL, the key is sealed using this key.
421  */
422 static krb5_error_code
_hdb_reseal_key_mkey(krb5_context context,Key * k,hdb_master_key mkey,hdb_master_key mit_key)423 _hdb_reseal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey,
424     hdb_master_key mit_key)
425 {
426 
427     krb5_error_code ret;
428     krb5_data mitres, res;
429     size_t keysize;
430 
431     hdb_master_key key, mitkey;
432 
433     if(k->mkvno == NULL)
434 	return 0;
435 
436     key = _hdb_find_master_key(k->mkvno, mkey);
437 
438     if (key == NULL)
439 	return HDB_ERR_NO_MKEY;
440 
441     ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY,
442 			    k->key.keyvalue.data,
443 			    k->key.keyvalue.length,
444 			    &res);
445     if(ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
446 	/* try to decrypt with MIT key usage */
447 	ret = _hdb_mkey_decrypt(context, key, 0,
448 			    k->key.keyvalue.data,
449 			    k->key.keyvalue.length,
450 			    &res);
451     }
452     if (ret)
453 	return ret;
454 
455     /* fixup keylength if the key got padded when encrypting it */
456     ret = krb5_enctype_keysize(context, k->key.keytype, &keysize);
457     if (ret) {
458 	krb5_data_free(&res);
459 	return ret;
460     }
461     if (keysize > res.length) {
462 	krb5_data_free(&res);
463 	return KRB5_BAD_KEYSIZE;
464     }
465 
466     /* For mit_key != NULL, re-encrypt the key using the mitkey. */
467     if (mit_key != NULL) {
468 	mitkey = _hdb_find_master_key(NULL, mit_key);
469 	if (mitkey == NULL) {
470 	    krb5_data_free(&res);
471 	    return HDB_ERR_NO_MKEY;
472 	}
473 
474 	ret = _hdb_mkey_encrypt(context, mitkey, 0,
475 			    res.data,
476 			    keysize,
477 			    &mitres);
478 	krb5_data_free(&res);
479 	if (ret)
480 	    return ret;
481     }
482 
483     krb5_data_free(&k->key.keyvalue);
484     if (mit_key == NULL) {
485 	k->key.keyvalue = res;
486 	k->key.keyvalue.length = keysize;
487 	free(k->mkvno);
488 	k->mkvno = NULL;
489     } else {
490 	k->key.keyvalue = mitres;
491 	*k->mkvno = mitkey->keytab.vno;
492     }
493 
494     return 0;
495 }
496 
497 krb5_error_code
hdb_unseal_key_mkey(krb5_context context,Key * k,hdb_master_key mkey)498 hdb_unseal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
499 {
500 
501     krb5_error_code ret;
502 
503     ret = _hdb_reseal_key_mkey(context, k, mkey, NULL);
504     return ret;
505 }
506 
507 static krb5_error_code
_hdb_unseal_keys_mkey(krb5_context context,hdb_entry * ent,hdb_master_key mkey,hdb_master_key mitkey)508 _hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey,
509     hdb_master_key mitkey)
510 {
511     krb5_error_code ret;
512     size_t i;
513     int got_one = 0;
514 
515     for(i = 0; i < ent->keys.len; i++){
516 	if (mitkey == NULL || mit_strong_etype(ent->keys.val[i].key.keytype)) {
517 	    ret = _hdb_reseal_key_mkey(context, &ent->keys.val[i], mkey,
518 		mitkey);
519 	    if (ret)
520 		return ret;
521 	    got_one = 1;
522 	}
523     }
524 
525     /*
526      * If none of the keys were string enough, create a strong key,
527      * but one that is not encrypted in the MIT master key.  As such,
528      * it will require a "change_password" once in the MIT KDC to
529      * make it work.
530      */
531     if (got_one == 0 && mitkey != NULL && ent->keys.len > 0) {
532 	krb5_keyblock key;
533 	krb5_salt salt;
534 
535 	krb5_free_keyblock_contents(context, &ent->keys.val[0].key);
536 	salt.salttype = KRB5_PW_SALT;
537 	salt.saltvalue.data = NULL;
538 	salt.saltvalue.length = 0;
539 	ret = krb5_string_to_key_salt(context, ETYPE_AES256_CTS_HMAC_SHA1_96,
540 	    "XXXX", salt, &ent->keys.val[0].key);
541 	if (ret)
542 	    return ret;
543     }
544     return 0;
545 }
546 
547 krb5_error_code
hdb_unseal_keys_mkey(krb5_context context,hdb_entry * ent,hdb_master_key mkey)548 hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
549 {
550     krb5_error_code ret;
551 
552     ret = _hdb_unseal_keys_mkey(context, ent, mkey, NULL);
553     return ret;
554 }
555 
556 krb5_error_code
hdb_unseal_keys(krb5_context context,HDB * db,hdb_entry * ent)557 hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent)
558 {
559     if (db->hdb_master_key_set == 0)
560 	return 0;
561     if (db->hdb_mit_key_set != 0)
562 	return _hdb_unseal_keys_mkey(context, ent, db->hdb_master_key,
563 	    db->hdb_mit_key);
564     else
565 	return _hdb_unseal_keys_mkey(context, ent, db->hdb_master_key,
566 	    NULL);
567 }
568 
569 #ifdef notnow
570 krb5_error_code
hdb_unseal_keys_kvno(krb5_context context,HDB * db,krb5_kvno kvno,hdb_entry * ent)571 hdb_unseal_keys_kvno(krb5_context context, HDB *db, krb5_kvno kvno,
572 		     hdb_entry *ent)
573 {
574     krb5_error_code ret = KRB5KRB_AP_ERR_NOKEY;	/* XXX need a better code? */
575     HDB_extension *tmp;
576     HDB_Ext_KeySet *hist_keys;
577     hdb_keyset *tmp_keys;
578     Key *tmp_val;
579     unsigned int tmp_len;
580     krb5_kvno tmp_kvno;
581     int i, k;
582 
583     assert(kvno == 0 || kvno < ent->kvno);
584 
585     tmp = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys);
586     if (tmp == NULL)
587 	return ret;
588 
589     tmp_len = ent->keys.len;
590     tmp_val = ent->keys.val;
591     tmp_kvno = ent->kvno;
592 
593     hist_keys = &tmp->data.u.hist_keys;
594 
595     for (i = hist_keys->len - 1; i >= 0; i++) {
596 	if (kvno != 0 && hist_keys->val[i].kvno != kvno)
597 	    continue;
598 	for (k = 0; k < hist_keys->val[i].keys.len; k++) {
599 	    ret = _hdb_reseal_key_mkey(context,
600 				      &hist_keys->val[i].keys.val[k],
601 				      db->hdb_master_key, NULL);
602 	    if (ret)
603 		return (ret);
604 	}
605 
606 	if (kvno == 0)
607 	    continue;
608 
609 	/*
610 	 * NOTE: What follows is a bit of an ugly hack.
611 	 *
612 	 * This is the keyset we're being asked for, so we add the
613 	 * current keyset to the history, leave the one we were asked
614 	 * for in the history, and pretend the one we were asked for is
615 	 * also the current keyset.
616 	 *
617 	 * This is a bit of a defensive hack in case an entry fetched
618 	 * this way ever gets modified then stored: if the keyset is not
619 	 * changed we can detect this and put things back, else we won't
620 	 * drop any keysets from history by accident.
621 	 *
622 	 * Note too that we only ever get called with a non-zero kvno
623 	 * either in the KDC or in cases where we aren't changing the
624 	 * HDB entry anyways, which is why this is just a defensive
625 	 * hack.  We also don't fetch specific kvnos in the dump case,
626 	 * so there's no danger that we'll dump this entry and load it
627 	 * again, repeatedly causing the history to grow boundelessly.
628 	 */
629 	tmp_keys = realloc(hist_keys->val,
630 		      sizeof (*hist_keys->val) * (hist_keys->len + 1));
631 	if (tmp_keys == NULL)
632 	    return ENOMEM;
633 
634 	memmove(&tmp_keys[1], tmp_keys,
635 		sizeof (*hist_keys->val) * hist_keys->len++);
636 	tmp_keys[0].keys.len = ent->keys.len;
637 	tmp_keys[0].keys.val = ent->keys.val;
638 	tmp_keys[0].kvno = ent->kvno;
639 	tmp_keys[0].replace_time = time(NULL);
640 	i++;
641 	ent->keys.len = hist_keys->val[i].keys.len;
642 	ent->keys.val = hist_keys->val[i].keys.val;
643 	ent->kvno = kvno;
644     }
645 
646     return (ret);
647 }
648 #endif
649 
650 krb5_error_code
hdb_unseal_key(krb5_context context,HDB * db,Key * k)651 hdb_unseal_key(krb5_context context, HDB *db, Key *k)
652 {
653     if (db->hdb_master_key_set == 0)
654 	return 0;
655     return _hdb_reseal_key_mkey(context, k, db->hdb_master_key, NULL);
656 }
657 
658 krb5_error_code
hdb_seal_key_mkey(krb5_context context,Key * k,hdb_master_key mkey)659 hdb_seal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
660 {
661     krb5_error_code ret;
662     krb5_data res;
663     hdb_master_key key;
664 
665     if(k->mkvno != NULL)
666 	return 0;
667 
668     key = _hdb_find_master_key(k->mkvno, mkey);
669 
670     if (key == NULL)
671 	return HDB_ERR_NO_MKEY;
672 
673     ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY,
674 			    k->key.keyvalue.data,
675 			    k->key.keyvalue.length,
676 			    &res);
677     if (ret)
678 	return ret;
679 
680     memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
681     free(k->key.keyvalue.data);
682     k->key.keyvalue = res;
683 
684     if (k->mkvno == NULL) {
685 	k->mkvno = malloc(sizeof(*k->mkvno));
686 	if (k->mkvno == NULL)
687 	    return ENOMEM;
688     }
689     *k->mkvno = key->keytab.vno;
690 
691     return 0;
692 }
693 
694 krb5_error_code
hdb_seal_keys_mkey(krb5_context context,hdb_entry * ent,hdb_master_key mkey)695 hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
696 {
697     size_t i;
698     for(i = 0; i < ent->keys.len; i++){
699 	krb5_error_code ret;
700 
701 	ret = hdb_seal_key_mkey(context, &ent->keys.val[i], mkey);
702 	if (ret)
703 	    return ret;
704     }
705     return 0;
706 }
707 
708 krb5_error_code
hdb_seal_keys(krb5_context context,HDB * db,hdb_entry * ent)709 hdb_seal_keys(krb5_context context, HDB *db, hdb_entry *ent)
710 {
711     if (db->hdb_master_key_set == 0)
712 	return 0;
713 
714     return hdb_seal_keys_mkey(context, ent, db->hdb_master_key);
715 }
716 
717 krb5_error_code
hdb_seal_key(krb5_context context,HDB * db,Key * k)718 hdb_seal_key(krb5_context context, HDB *db, Key *k)
719 {
720     if (db->hdb_master_key_set == 0)
721 	return 0;
722 
723     return hdb_seal_key_mkey(context, k, db->hdb_master_key);
724 }
725 
726 krb5_error_code
hdb_set_master_key(krb5_context context,HDB * db,krb5_keyblock * key)727 hdb_set_master_key(krb5_context context,
728 		   HDB *db,
729 		   krb5_keyblock *key)
730 {
731     krb5_error_code ret;
732     hdb_master_key mkey;
733 
734     ret = hdb_process_master_key(context, 0, key, 0, &mkey);
735     if (ret)
736 	return ret;
737     db->hdb_master_key = mkey;
738 #if 0 /* XXX - why? */
739     des_set_random_generator_seed(key.keyvalue.data);
740 #endif
741     db->hdb_master_key_set = 1;
742     db->hdb_master_key->key_usage = HDB_KU_MKEY;
743     return 0;
744 }
745 
746 krb5_error_code
hdb_set_master_keyfile(krb5_context context,HDB * db,const char * keyfile)747 hdb_set_master_keyfile (krb5_context context,
748 			HDB *db,
749 			const char *keyfile)
750 {
751     hdb_master_key key;
752     krb5_error_code ret;
753 
754     ret = hdb_read_master_key(context, keyfile, &key);
755     if (ret) {
756 	if (ret != ENOENT)
757 	    return ret;
758 	krb5_clear_error_message(context);
759 	return 0;
760     }
761     db->hdb_master_key = key;
762     db->hdb_master_key_set = 1;
763     return ret;
764 }
765 
766 krb5_error_code
hdb_clear_master_key(krb5_context context,HDB * db)767 hdb_clear_master_key (krb5_context context,
768 		      HDB *db)
769 {
770     if (db->hdb_master_key_set) {
771 	hdb_free_master_key(context, db->hdb_master_key);
772 	db->hdb_master_key_set = 0;
773     }
774     return 0;
775 }
776