x
[heimdal.git] / lib / hdb / mkey.c
blobd7a097b9b0e2745beac5839822003d98357f70b7
1 /*
2 * Copyright (c) 2000 - 2004 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include "hdb_locl.h"
35 #ifndef O_BINARY
36 #define O_BINARY 0
37 #endif
39 RCSID("$Id$");
41 struct hdb_master_key_data {
42 krb5_keytab_entry keytab;
43 krb5_crypto crypto;
44 struct hdb_master_key_data *next;
47 void
48 hdb_free_master_key(krb5_context context, hdb_master_key mkey)
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);
61 krb5_error_code
62 hdb_process_master_key(krb5_context context,
63 int kvno, krb5_keyblock *key, krb5_enctype etype,
64 hdb_master_key *mkey)
66 krb5_error_code ret;
68 *mkey = calloc(1, sizeof(**mkey));
69 if(*mkey == NULL) {
70 krb5_set_error_string(context, "malloc: out of memory");
71 return ENOMEM;
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;
93 krb5_error_code
94 hdb_add_master_key(krb5_context context, krb5_keyblock *key,
95 hdb_master_key *inout)
97 int vno = 0;
98 hdb_master_key p;
99 krb5_error_code ret;
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;
112 static krb5_error_code
113 read_master_keytab(krb5_context context, const char *filename,
114 hdb_master_key *mkey)
116 krb5_error_code ret;
117 krb5_keytab id;
118 krb5_kt_cursor cursor;
119 krb5_keytab_entry entry;
120 hdb_master_key p;
122 ret = krb5_kt_resolve(context, filename, &id);
123 if(ret)
124 return ret;
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;
137 krb5_kt_end_seq_get(context, id, &cursor);
138 out:
139 krb5_kt_close(context, id);
140 return ret;
143 /* read a MIT master keyfile */
144 static krb5_error_code
145 read_master_mit(krb5_context context, const char *filename,
146 hdb_master_key *mkey)
148 int fd;
149 krb5_error_code ret;
150 krb5_storage *sp;
151 int16_t enctype;
152 krb5_keyblock key;
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;
161 sp = krb5_storage_from_fd(fd);
162 if(sp == NULL) {
163 close(fd);
164 return errno;
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;
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;
192 /* read an old master key file */
193 static krb5_error_code
194 read_master_encryptionkey(krb5_context context, const char *filename,
195 hdb_master_key *mkey)
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;
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;
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;
221 ret = decode_EncryptionKey(buf, len, &key, &ret_len);
222 memset(buf, 0, sizeof(buf));
223 if(ret)
224 return ret;
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;
234 ret = hdb_process_master_key(context, 0, &key, 0, mkey);
235 krb5_free_keyblock_contents(context, &key);
236 return ret;
239 /* read a krb4 /.k style file */
240 static krb5_error_code
241 read_master_krb4(krb5_context context, const char *filename,
242 hdb_master_key *mkey)
244 int fd;
245 krb5_keyblock key;
246 krb5_error_code ret;
247 unsigned char buf[256];
248 ssize_t len;
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;
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;
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 */
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;
278 ret = hdb_process_master_key(context, 0, &key, 0, mkey);
279 krb5_free_keyblock_contents(context, &key);
280 return ret;
283 krb5_error_code
284 hdb_read_master_key(krb5_context context, const char *filename,
285 hdb_master_key *mkey)
287 FILE *f;
288 unsigned char buf[16];
289 krb5_error_code ret;
291 off_t len;
293 *mkey = NULL;
295 if(filename == NULL)
296 filename = HDB_DB_DIR "/m-key";
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;
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;
312 fseek(f, 0, SEEK_END);
313 len = ftell(f);
315 if(fclose(f) != 0)
316 return errno;
318 if(len < 0)
319 return errno;
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);
330 return ret;
333 krb5_error_code
334 hdb_write_master_key(krb5_context context, const char *filename,
335 hdb_master_key mkey)
337 krb5_error_code ret;
338 hdb_master_key p;
339 krb5_keytab kt;
341 if(filename == NULL)
342 filename = HDB_DB_DIR "/m-key";
344 ret = krb5_kt_resolve(context, filename, &kt);
345 if(ret)
346 return ret;
348 for(p = mkey; p; p = p->next) {
349 ret = krb5_kt_add_entry(context, kt, &p->keytab);
352 krb5_kt_close(context, kt);
354 return ret;
357 static hdb_master_key
358 find_master_key(Key *key, hdb_master_key mkey)
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;
371 return ret;
374 krb5_error_code
375 hdb_unseal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
378 krb5_error_code ret;
379 krb5_data res;
380 size_t keysize;
382 hdb_master_key key;
384 if(k->mkvno == NULL)
385 return 0;
387 key = find_master_key(k, mkey);
389 if (key == NULL)
390 return HDB_ERR_NO_MKEY;
392 ret = krb5_decrypt(context, key->crypto, HDB_KU_MKEY,
393 k->key.keyvalue.data,
394 k->key.keyvalue.length,
395 &res);
396 if (ret)
397 return ret;
399 /* fixup keylength if the key got padded when encrypting it */
400 ret = krb5_enctype_keysize(context, k->key.keytype, &keysize);
401 if (ret) {
402 krb5_data_free(&res);
403 return ret;
405 if (keysize > res.length) {
406 krb5_data_free(&res);
407 return KRB5_BAD_KEYSIZE;
410 memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
411 free(k->key.keyvalue.data);
412 k->key.keyvalue = res;
413 k->key.keyvalue.length = keysize;
414 free(k->mkvno);
415 k->mkvno = NULL;
417 return 0;
420 krb5_error_code
421 hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
423 int i;
425 for(i = 0; i < ent->keys.len; i++){
426 krb5_error_code ret;
428 ret = hdb_unseal_key_mkey(context, &ent->keys.val[i], mkey);
429 if (ret)
430 return ret;
432 return 0;
435 krb5_error_code
436 hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent)
438 if (db->hdb_master_key_set == 0)
439 return 0;
440 return hdb_unseal_keys_mkey(context, ent, db->hdb_master_key);
443 krb5_error_code
444 hdb_unseal_key(krb5_context context, HDB *db, Key *k)
446 if (db->hdb_master_key_set == 0)
447 return 0;
448 return hdb_unseal_key_mkey(context, k, db->hdb_master_key);
451 krb5_error_code
452 hdb_seal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
454 krb5_error_code ret;
455 krb5_data res;
456 hdb_master_key key;
458 if(k->mkvno != NULL)
459 return 0;
461 key = find_master_key(k, mkey);
463 if (key == NULL)
464 return HDB_ERR_NO_MKEY;
466 ret = krb5_encrypt(context, key->crypto, HDB_KU_MKEY,
467 k->key.keyvalue.data,
468 k->key.keyvalue.length,
469 &res);
470 if (ret)
471 return ret;
473 memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
474 free(k->key.keyvalue.data);
475 k->key.keyvalue = res;
477 k->mkvno = malloc(sizeof(*k->mkvno));
478 if (k->mkvno == NULL)
479 return ENOMEM;
480 *k->mkvno = key->keytab.vno;
482 return 0;
485 krb5_error_code
486 hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
488 int i;
489 for(i = 0; i < ent->keys.len; i++){
490 krb5_error_code ret;
492 ret = hdb_seal_key_mkey(context, &ent->keys.val[i], mkey);
493 if (ret)
494 return ret;
496 return 0;
499 krb5_error_code
500 hdb_seal_keys(krb5_context context, HDB *db, hdb_entry *ent)
502 if (db->hdb_master_key_set == 0)
503 return 0;
505 return hdb_seal_keys_mkey(context, ent, db->hdb_master_key);
508 krb5_error_code
509 hdb_seal_key(krb5_context context, HDB *db, Key *k)
511 if (db->hdb_master_key_set == 0)
512 return 0;
514 return hdb_seal_key_mkey(context, k, db->hdb_master_key);
517 krb5_error_code
518 hdb_set_master_key (krb5_context context,
519 HDB *db,
520 krb5_keyblock *key)
522 krb5_error_code ret;
523 hdb_master_key mkey;
525 ret = hdb_process_master_key(context, 0, key, 0, &mkey);
526 if (ret)
527 return ret;
528 db->hdb_master_key = mkey;
529 #if 0 /* XXX - why? */
530 des_set_random_generator_seed(key.keyvalue.data);
531 #endif
532 db->hdb_master_key_set = 1;
533 return 0;
536 krb5_error_code
537 hdb_set_master_keyfile (krb5_context context,
538 HDB *db,
539 const char *keyfile)
541 hdb_master_key key;
542 krb5_error_code ret;
544 ret = hdb_read_master_key(context, keyfile, &key);
545 if (ret) {
546 if (ret != ENOENT)
547 return ret;
548 krb5_clear_error_string(context);
549 return 0;
551 db->hdb_master_key = key;
552 db->hdb_master_key_set = 1;
553 return ret;
556 krb5_error_code
557 hdb_clear_master_key (krb5_context context,
558 HDB *db)
560 if (db->hdb_master_key_set) {
561 hdb_free_master_key(context, db->hdb_master_key);
562 db->hdb_master_key_set = 0;
564 return 0;