xref: /freebsd/crypto/heimdal/lib/hdb/mkey.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
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 
39 RCSID("$Id: mkey.c,v 1.8 2001/01/30 01:20:57 assar Exp $");
40 
41 struct hdb_master_key_data {
42     krb5_keytab_entry keytab;
43     krb5_crypto crypto;
44     struct hdb_master_key_data *next;
45 };
46 
47 void
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 	krb5_crypto_destroy(context, mkey->crypto);
54 	ptr = mkey;
55 	mkey = mkey->next;
56 	free(ptr);
57     }
58 }
59 
60 krb5_error_code
61 hdb_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 
89 krb5_error_code
90 hdb_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 
108 static krb5_error_code
109 read_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 */
140 static krb5_error_code
141 read_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 */
183 static krb5_error_code
184 read_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 */
221 static krb5_error_code
222 read_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 
252 krb5_error_code
253 hdb_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 
297 krb5_error_code
298 hdb_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 
321 static hdb_master_key
322 find_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 
338 krb5_error_code
339 hdb_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 
374 krb5_error_code
375 hdb_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 
382 krb5_error_code
383 hdb_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 
419 krb5_error_code
420 hdb_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 
428 krb5_error_code
429 hdb_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 
447 krb5_error_code
448 hdb_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 
466 krb5_error_code
467 hdb_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