1 /* $NetBSD: mkey.c,v 1.1.1.2 2014/04/24 12:45:28 pettai Exp $ */
2
3 /*
4 * Copyright (c) 2000 - 2004 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include "hdb_locl.h"
37 #ifndef O_BINARY
38 #define O_BINARY 0
39 #endif
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
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)->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
93 krb5_error_code
hdb_add_master_key(krb5_context context,krb5_keyblock * key,hdb_master_key * inout)94 hdb_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
112 static krb5_error_code
read_master_keytab(krb5_context context,const char * filename,hdb_master_key * mkey)113 read_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 if(p == NULL) {
133 krb5_kt_end_seq_get(context, id, &cursor);
134 ret = ENOMEM;
135 goto out;
136 }
137 p->keytab = entry;
138 ret = krb5_crypto_init(context, &p->keytab.keyblock, 0, &p->crypto);
139 p->next = *mkey;
140 *mkey = p;
141 }
142 krb5_kt_end_seq_get(context, id, &cursor);
143 out:
144 krb5_kt_close(context, id);
145 return ret;
146 }
147
148 /* read a MIT master keyfile */
149 static krb5_error_code
read_master_mit(krb5_context context,const char * filename,int byteorder,hdb_master_key * mkey)150 read_master_mit(krb5_context context, const char *filename,
151 int byteorder, hdb_master_key *mkey)
152 {
153 int fd;
154 krb5_error_code ret;
155 krb5_storage *sp;
156 int16_t enctype;
157 krb5_keyblock key;
158
159 fd = open(filename, O_RDONLY | O_BINARY);
160 if(fd < 0) {
161 int save_errno = errno;
162 krb5_set_error_message(context, save_errno, "failed to open %s: %s",
163 filename, strerror(save_errno));
164 return save_errno;
165 }
166 sp = krb5_storage_from_fd(fd);
167 if(sp == NULL) {
168 close(fd);
169 return errno;
170 }
171 krb5_storage_set_flags(sp, byteorder);
172 /* could possibly use ret_keyblock here, but do it with more
173 checks for now */
174 {
175 ret = krb5_ret_int16(sp, &enctype);
176 if (ret)
177 goto out;
178 ret = krb5_enctype_valid(context, enctype);
179 if (ret)
180 goto out;
181 key.keytype = enctype;
182 ret = krb5_ret_data(sp, &key.keyvalue);
183 if(ret)
184 goto out;
185 }
186 ret = hdb_process_master_key(context, 1, &key, 0, mkey);
187 krb5_free_keyblock_contents(context, &key);
188 out:
189 krb5_storage_free(sp);
190 close(fd);
191 return ret;
192 }
193
194 /* read an old master key file */
195 static krb5_error_code
read_master_encryptionkey(krb5_context context,const char * filename,hdb_master_key * mkey)196 read_master_encryptionkey(krb5_context context, const char *filename,
197 hdb_master_key *mkey)
198 {
199 int fd;
200 krb5_keyblock key;
201 krb5_error_code ret;
202 unsigned char buf[256];
203 ssize_t len;
204 size_t ret_len;
205
206 fd = open(filename, O_RDONLY | O_BINARY);
207 if(fd < 0) {
208 int save_errno = errno;
209 krb5_set_error_message(context, save_errno, "failed to open %s: %s",
210 filename, strerror(save_errno));
211 return save_errno;
212 }
213
214 len = read(fd, buf, sizeof(buf));
215 close(fd);
216 if(len < 0) {
217 int save_errno = errno;
218 krb5_set_error_message(context, save_errno, "error reading %s: %s",
219 filename, strerror(save_errno));
220 return save_errno;
221 }
222
223 ret = decode_EncryptionKey(buf, len, &key, &ret_len);
224 memset(buf, 0, sizeof(buf));
225 if(ret)
226 return ret;
227
228 /* Originally, the keytype was just that, and later it got changed
229 to des-cbc-md5, but we always used des in cfb64 mode. This
230 should cover all cases, but will break if someone has hacked
231 this code to really use des-cbc-md5 -- but then that's not my
232 problem. */
233 if(key.keytype == ETYPE_DES_CBC_CRC || key.keytype == ETYPE_DES_CBC_MD5)
234 key.keytype = ETYPE_DES_CFB64_NONE;
235
236 ret = hdb_process_master_key(context, 0, &key, 0, mkey);
237 krb5_free_keyblock_contents(context, &key);
238 return ret;
239 }
240
241 /* read a krb4 /.k style file */
242 static krb5_error_code
read_master_krb4(krb5_context context,const char * filename,hdb_master_key * mkey)243 read_master_krb4(krb5_context context, const char *filename,
244 hdb_master_key *mkey)
245 {
246 int fd;
247 krb5_keyblock key;
248 krb5_error_code ret;
249 unsigned char buf[256];
250 ssize_t len;
251
252 fd = open(filename, O_RDONLY | O_BINARY);
253 if(fd < 0) {
254 int save_errno = errno;
255 krb5_set_error_message(context, save_errno, "failed to open %s: %s",
256 filename, strerror(save_errno));
257 return save_errno;
258 }
259
260 len = read(fd, buf, sizeof(buf));
261 close(fd);
262 if(len < 0) {
263 int save_errno = errno;
264 krb5_set_error_message(context, save_errno, "error reading %s: %s",
265 filename, strerror(save_errno));
266 return save_errno;
267 }
268 if(len != 8) {
269 krb5_set_error_message(context, HEIM_ERR_EOF,
270 "bad contents of %s", filename);
271 return HEIM_ERR_EOF; /* XXX file might be too large */
272 }
273
274 memset(&key, 0, sizeof(key));
275 key.keytype = ETYPE_DES_PCBC_NONE;
276 ret = krb5_data_copy(&key.keyvalue, buf, len);
277 memset(buf, 0, sizeof(buf));
278 if(ret)
279 return ret;
280
281 ret = hdb_process_master_key(context, 0, &key, 0, mkey);
282 krb5_free_keyblock_contents(context, &key);
283 return ret;
284 }
285
286 krb5_error_code
hdb_read_master_key(krb5_context context,const char * filename,hdb_master_key * mkey)287 hdb_read_master_key(krb5_context context, const char *filename,
288 hdb_master_key *mkey)
289 {
290 FILE *f;
291 unsigned char buf[16];
292 krb5_error_code ret;
293
294 off_t len;
295
296 *mkey = NULL;
297
298 if(filename == NULL)
299 filename = HDB_DB_DIR "/m-key";
300
301 f = fopen(filename, "r");
302 if(f == NULL) {
303 int save_errno = errno;
304 krb5_set_error_message(context, save_errno, "failed to open %s: %s",
305 filename, strerror(save_errno));
306 return save_errno;
307 }
308
309 if(fread(buf, 1, 2, f) != 2) {
310 fclose(f);
311 krb5_set_error_message(context, HEIM_ERR_EOF, "end of file reading %s", filename);
312 return HEIM_ERR_EOF;
313 }
314
315 fseek(f, 0, SEEK_END);
316 len = ftell(f);
317
318 if(fclose(f) != 0)
319 return errno;
320
321 if(len < 0)
322 return errno;
323
324 if(len == 8) {
325 ret = read_master_krb4(context, filename, mkey);
326 } else if(buf[0] == 0x30 && len <= 127 && buf[1] == len - 2) {
327 ret = read_master_encryptionkey(context, filename, mkey);
328 } else if(buf[0] == 5 && buf[1] >= 1 && buf[1] <= 2) {
329 ret = read_master_keytab(context, filename, mkey);
330 } else {
331 /*
332 * Check both LittleEndian and BigEndian since they key file
333 * might be moved from a machine with diffrent byte order, or
334 * its running on MacOS X that always uses BE master keys.
335 */
336 ret = read_master_mit(context, filename, KRB5_STORAGE_BYTEORDER_LE, mkey);
337 if (ret)
338 ret = read_master_mit(context, filename, KRB5_STORAGE_BYTEORDER_BE, mkey);
339 }
340 return ret;
341 }
342
343 krb5_error_code
hdb_write_master_key(krb5_context context,const char * filename,hdb_master_key mkey)344 hdb_write_master_key(krb5_context context, const char *filename,
345 hdb_master_key mkey)
346 {
347 krb5_error_code ret;
348 hdb_master_key p;
349 krb5_keytab kt;
350
351 if(filename == NULL)
352 filename = HDB_DB_DIR "/m-key";
353
354 ret = krb5_kt_resolve(context, filename, &kt);
355 if(ret)
356 return ret;
357
358 for(p = mkey; p; p = p->next) {
359 ret = krb5_kt_add_entry(context, kt, &p->keytab);
360 }
361
362 krb5_kt_close(context, kt);
363
364 return ret;
365 }
366
367 hdb_master_key
_hdb_find_master_key(uint32_t * mkvno,hdb_master_key mkey)368 _hdb_find_master_key(uint32_t *mkvno, hdb_master_key mkey)
369 {
370 hdb_master_key ret = NULL;
371 while(mkey) {
372 if(ret == NULL && mkey->keytab.vno == 0)
373 ret = mkey;
374 if(mkvno == NULL) {
375 if(ret == NULL || mkey->keytab.vno > ret->keytab.vno)
376 ret = mkey;
377 } else if((uint32_t)mkey->keytab.vno == *mkvno)
378 return mkey;
379 mkey = mkey->next;
380 }
381 return ret;
382 }
383
384 int
_hdb_mkey_version(hdb_master_key mkey)385 _hdb_mkey_version(hdb_master_key mkey)
386 {
387 return mkey->keytab.vno;
388 }
389
390 int
_hdb_mkey_decrypt(krb5_context context,hdb_master_key key,krb5_key_usage usage,void * ptr,size_t size,krb5_data * res)391 _hdb_mkey_decrypt(krb5_context context, hdb_master_key key,
392 krb5_key_usage usage,
393 void *ptr, size_t size, krb5_data *res)
394 {
395 return krb5_decrypt(context, key->crypto, usage,
396 ptr, size, res);
397 }
398
399 int
_hdb_mkey_encrypt(krb5_context context,hdb_master_key key,krb5_key_usage usage,const void * ptr,size_t size,krb5_data * res)400 _hdb_mkey_encrypt(krb5_context context, hdb_master_key key,
401 krb5_key_usage usage,
402 const void *ptr, size_t size, krb5_data *res)
403 {
404 return krb5_encrypt(context, key->crypto, usage,
405 ptr, size, res);
406 }
407
408 krb5_error_code
hdb_unseal_key_mkey(krb5_context context,Key * k,hdb_master_key mkey)409 hdb_unseal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
410 {
411
412 krb5_error_code ret;
413 krb5_data res;
414 size_t keysize;
415
416 hdb_master_key key;
417
418 if(k->mkvno == NULL)
419 return 0;
420
421 key = _hdb_find_master_key(k->mkvno, mkey);
422
423 if (key == NULL)
424 return HDB_ERR_NO_MKEY;
425
426 ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY,
427 k->key.keyvalue.data,
428 k->key.keyvalue.length,
429 &res);
430 if(ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
431 /* try to decrypt with MIT key usage */
432 ret = _hdb_mkey_decrypt(context, key, 0,
433 k->key.keyvalue.data,
434 k->key.keyvalue.length,
435 &res);
436 }
437 if (ret)
438 return ret;
439
440 /* fixup keylength if the key got padded when encrypting it */
441 ret = krb5_enctype_keysize(context, k->key.keytype, &keysize);
442 if (ret) {
443 krb5_data_free(&res);
444 return ret;
445 }
446 if (keysize > res.length) {
447 krb5_data_free(&res);
448 return KRB5_BAD_KEYSIZE;
449 }
450
451 memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
452 free(k->key.keyvalue.data);
453 k->key.keyvalue = res;
454 k->key.keyvalue.length = keysize;
455 free(k->mkvno);
456 k->mkvno = NULL;
457
458 return 0;
459 }
460
461 krb5_error_code
hdb_unseal_keys_mkey(krb5_context context,hdb_entry * ent,hdb_master_key mkey)462 hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
463 {
464 size_t i;
465
466 for(i = 0; i < ent->keys.len; i++){
467 krb5_error_code ret;
468
469 ret = hdb_unseal_key_mkey(context, &ent->keys.val[i], mkey);
470 if (ret)
471 return ret;
472 }
473 return 0;
474 }
475
476 krb5_error_code
hdb_unseal_keys(krb5_context context,HDB * db,hdb_entry * ent)477 hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent)
478 {
479 if (db->hdb_master_key_set == 0)
480 return 0;
481 return hdb_unseal_keys_mkey(context, ent, db->hdb_master_key);
482 }
483
484 krb5_error_code
hdb_unseal_key(krb5_context context,HDB * db,Key * k)485 hdb_unseal_key(krb5_context context, HDB *db, Key *k)
486 {
487 if (db->hdb_master_key_set == 0)
488 return 0;
489 return hdb_unseal_key_mkey(context, k, db->hdb_master_key);
490 }
491
492 krb5_error_code
hdb_seal_key_mkey(krb5_context context,Key * k,hdb_master_key mkey)493 hdb_seal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
494 {
495 krb5_error_code ret;
496 krb5_data res;
497 hdb_master_key key;
498
499 if(k->mkvno != NULL)
500 return 0;
501
502 key = _hdb_find_master_key(k->mkvno, mkey);
503
504 if (key == NULL)
505 return HDB_ERR_NO_MKEY;
506
507 ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY,
508 k->key.keyvalue.data,
509 k->key.keyvalue.length,
510 &res);
511 if (ret)
512 return ret;
513
514 memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
515 free(k->key.keyvalue.data);
516 k->key.keyvalue = res;
517
518 if (k->mkvno == NULL) {
519 k->mkvno = malloc(sizeof(*k->mkvno));
520 if (k->mkvno == NULL)
521 return ENOMEM;
522 }
523 *k->mkvno = key->keytab.vno;
524
525 return 0;
526 }
527
528 krb5_error_code
hdb_seal_keys_mkey(krb5_context context,hdb_entry * ent,hdb_master_key mkey)529 hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
530 {
531 size_t i;
532 for(i = 0; i < ent->keys.len; i++){
533 krb5_error_code ret;
534
535 ret = hdb_seal_key_mkey(context, &ent->keys.val[i], mkey);
536 if (ret)
537 return ret;
538 }
539 return 0;
540 }
541
542 krb5_error_code
hdb_seal_keys(krb5_context context,HDB * db,hdb_entry * ent)543 hdb_seal_keys(krb5_context context, HDB *db, hdb_entry *ent)
544 {
545 if (db->hdb_master_key_set == 0)
546 return 0;
547
548 return hdb_seal_keys_mkey(context, ent, db->hdb_master_key);
549 }
550
551 krb5_error_code
hdb_seal_key(krb5_context context,HDB * db,Key * k)552 hdb_seal_key(krb5_context context, HDB *db, Key *k)
553 {
554 if (db->hdb_master_key_set == 0)
555 return 0;
556
557 return hdb_seal_key_mkey(context, k, db->hdb_master_key);
558 }
559
560 krb5_error_code
hdb_set_master_key(krb5_context context,HDB * db,krb5_keyblock * key)561 hdb_set_master_key (krb5_context context,
562 HDB *db,
563 krb5_keyblock *key)
564 {
565 krb5_error_code ret;
566 hdb_master_key mkey;
567
568 ret = hdb_process_master_key(context, 0, key, 0, &mkey);
569 if (ret)
570 return ret;
571 db->hdb_master_key = mkey;
572 #if 0 /* XXX - why? */
573 des_set_random_generator_seed(key.keyvalue.data);
574 #endif
575 db->hdb_master_key_set = 1;
576 return 0;
577 }
578
579 krb5_error_code
hdb_set_master_keyfile(krb5_context context,HDB * db,const char * keyfile)580 hdb_set_master_keyfile (krb5_context context,
581 HDB *db,
582 const char *keyfile)
583 {
584 hdb_master_key key;
585 krb5_error_code ret;
586
587 ret = hdb_read_master_key(context, keyfile, &key);
588 if (ret) {
589 if (ret != ENOENT)
590 return ret;
591 krb5_clear_error_message(context);
592 return 0;
593 }
594 db->hdb_master_key = key;
595 db->hdb_master_key_set = 1;
596 return ret;
597 }
598
599 krb5_error_code
hdb_clear_master_key(krb5_context context,HDB * db)600 hdb_clear_master_key (krb5_context context,
601 HDB *db)
602 {
603 if (db->hdb_master_key_set) {
604 hdb_free_master_key(context, db->hdb_master_key);
605 db->hdb_master_key_set = 0;
606 }
607 return 0;
608 }
609