• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src/router/samba-3.5.8/source4/heimdal/lib/hdb/
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#ifndef O_BINARY
36#define O_BINARY 0
37#endif
38
39struct hdb_master_key_data {
40    krb5_keytab_entry keytab;
41    krb5_crypto crypto;
42    struct hdb_master_key_data *next;
43};
44
45void
46hdb_free_master_key(krb5_context context, hdb_master_key mkey)
47{
48    struct hdb_master_key_data *ptr;
49    while(mkey) {
50	krb5_kt_free_entry(context, &mkey->keytab);
51	if (mkey->crypto)
52	    krb5_crypto_destroy(context, mkey->crypto);
53	ptr = mkey;
54	mkey = mkey->next;
55	free(ptr);
56    }
57}
58
59krb5_error_code
60hdb_process_master_key(krb5_context context,
61		       int kvno, krb5_keyblock *key, krb5_enctype etype,
62		       hdb_master_key *mkey)
63{
64    krb5_error_code ret;
65
66    *mkey = calloc(1, sizeof(**mkey));
67    if(*mkey == NULL) {
68	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
69	return ENOMEM;
70    }
71    (*mkey)->keytab.vno = kvno;
72    ret = krb5_parse_name(context, "K/M", &(*mkey)->keytab.principal);
73    if(ret)
74	goto fail;
75    ret = krb5_copy_keyblock_contents(context, key, &(*mkey)->keytab.keyblock);
76    if(ret)
77	goto fail;
78    if(etype != 0)
79	(*mkey)->keytab.keyblock.keytype = etype;
80    (*mkey)->keytab.timestamp = time(NULL);
81    ret = krb5_crypto_init(context, key, etype, &(*mkey)->crypto);
82    if(ret)
83	goto fail;
84    return 0;
85 fail:
86    hdb_free_master_key(context, *mkey);
87    *mkey = NULL;
88    return ret;
89}
90
91krb5_error_code
92hdb_add_master_key(krb5_context context, krb5_keyblock *key,
93		   hdb_master_key *inout)
94{
95    int vno = 0;
96    hdb_master_key p;
97    krb5_error_code ret;
98
99    for(p = *inout; p; p = p->next)
100	vno = max(vno, p->keytab.vno);
101    vno++;
102    ret = hdb_process_master_key(context, vno, key, 0, &p);
103    if(ret)
104	return ret;
105    p->next = *inout;
106    *inout = p;
107    return 0;
108}
109
110static krb5_error_code
111read_master_keytab(krb5_context context, const char *filename,
112		   hdb_master_key *mkey)
113{
114    krb5_error_code ret;
115    krb5_keytab id;
116    krb5_kt_cursor cursor;
117    krb5_keytab_entry entry;
118    hdb_master_key p;
119
120    ret = krb5_kt_resolve(context, filename, &id);
121    if(ret)
122	return ret;
123
124    ret = krb5_kt_start_seq_get(context, id, &cursor);
125    if(ret)
126	goto out;
127    *mkey = NULL;
128    while(krb5_kt_next_entry(context, id, &entry, &cursor) == 0) {
129	p = calloc(1, sizeof(*p));
130	if(p == NULL) {
131	    krb5_kt_end_seq_get(context, id, &cursor);
132	    ret = ENOMEM;
133	    goto out;
134	}
135	p->keytab = entry;
136	ret = krb5_crypto_init(context, &p->keytab.keyblock, 0, &p->crypto);
137	p->next = *mkey;
138	*mkey = p;
139    }
140    krb5_kt_end_seq_get(context, id, &cursor);
141  out:
142    krb5_kt_close(context, id);
143    return ret;
144}
145
146/* read a MIT master keyfile */
147static krb5_error_code
148read_master_mit(krb5_context context, const char *filename,
149		hdb_master_key *mkey)
150{
151    int fd;
152    krb5_error_code ret;
153    krb5_storage *sp;
154    int16_t enctype;
155    krb5_keyblock key;
156
157    fd = open(filename, O_RDONLY | O_BINARY);
158    if(fd < 0) {
159	int save_errno = errno;
160	krb5_set_error_message(context, save_errno, "failed to open %s: %s",
161			       filename, strerror(save_errno));
162	return save_errno;
163    }
164    sp = krb5_storage_from_fd(fd);
165    if(sp == NULL) {
166	close(fd);
167	return errno;
168    }
169    krb5_storage_set_flags(sp, KRB5_STORAGE_HOST_BYTEORDER);
170    /* could possibly use ret_keyblock here, but do it with more
171       checks for now */
172    {
173	ret = krb5_ret_int16(sp, &enctype);
174	if (ret)
175	    goto out;
176	if((htons(enctype) & 0xff00) == 0x3000) {
177	    ret = HEIM_ERR_BAD_MKEY;
178	    krb5_set_error_message(context, ret, "unknown keytype in %s: "
179				   "%#x, expected %#x",
180				   filename, htons(enctype), 0x3000);
181	    goto out;
182	}
183	key.keytype = enctype;
184	ret = krb5_ret_data(sp, &key.keyvalue);
185	if(ret)
186	    goto out;
187    }
188    ret = hdb_process_master_key(context, 0, &key, 0, mkey);
189    krb5_free_keyblock_contents(context, &key);
190  out:
191    krb5_storage_free(sp);
192    close(fd);
193    return ret;
194}
195
196/* read an old master key file */
197static krb5_error_code
198read_master_encryptionkey(krb5_context context, const char *filename,
199			  hdb_master_key *mkey)
200{
201    int fd;
202    krb5_keyblock key;
203    krb5_error_code ret;
204    unsigned char buf[256];
205    ssize_t len;
206    size_t ret_len;
207
208    fd = open(filename, O_RDONLY | O_BINARY);
209    if(fd < 0) {
210	int save_errno = errno;
211	krb5_set_error_message(context, save_errno, "failed to open %s: %s",
212			      filename, strerror(save_errno));
213	return save_errno;
214    }
215
216    len = read(fd, buf, sizeof(buf));
217    close(fd);
218    if(len < 0) {
219	int save_errno = errno;
220	krb5_set_error_message(context, save_errno, "error reading %s: %s",
221			      filename, strerror(save_errno));
222	return save_errno;
223    }
224
225    ret = decode_EncryptionKey(buf, len, &key, &ret_len);
226    memset(buf, 0, sizeof(buf));
227    if(ret)
228	return ret;
229
230    /* Originally, the keytype was just that, and later it got changed
231       to des-cbc-md5, but we always used des in cfb64 mode. This
232       should cover all cases, but will break if someone has hacked
233       this code to really use des-cbc-md5 -- but then that's not my
234       problem. */
235    if(key.keytype == KEYTYPE_DES || key.keytype == ETYPE_DES_CBC_MD5)
236	key.keytype = ETYPE_DES_CFB64_NONE;
237
238    ret = hdb_process_master_key(context, 0, &key, 0, mkey);
239    krb5_free_keyblock_contents(context, &key);
240    return ret;
241}
242
243/* read a krb4 /.k style file */
244static krb5_error_code
245read_master_krb4(krb5_context context, const char *filename,
246		 hdb_master_key *mkey)
247{
248    int fd;
249    krb5_keyblock key;
250    krb5_error_code ret;
251    unsigned char buf[256];
252    ssize_t len;
253
254    fd = open(filename, O_RDONLY | O_BINARY);
255    if(fd < 0) {
256	int save_errno = errno;
257	krb5_set_error_message(context, save_errno, "failed to open %s: %s",
258			       filename, strerror(save_errno));
259	return save_errno;
260    }
261
262    len = read(fd, buf, sizeof(buf));
263    close(fd);
264    if(len < 0) {
265	int save_errno = errno;
266	krb5_set_error_message(context, save_errno, "error reading %s: %s",
267			       filename, strerror(save_errno));
268	return save_errno;
269    }
270    if(len != 8) {
271	krb5_set_error_message(context, HEIM_ERR_EOF,
272			       "bad contents of %s", filename);
273	return HEIM_ERR_EOF; /* XXX file might be too large */
274    }
275
276    memset(&key, 0, sizeof(key));
277    key.keytype = ETYPE_DES_PCBC_NONE;
278    ret = krb5_data_copy(&key.keyvalue, buf, len);
279    memset(buf, 0, sizeof(buf));
280    if(ret)
281	return ret;
282
283    ret = hdb_process_master_key(context, 0, &key, 0, mkey);
284    krb5_free_keyblock_contents(context, &key);
285    return ret;
286}
287
288krb5_error_code
289hdb_read_master_key(krb5_context context, const char *filename,
290		    hdb_master_key *mkey)
291{
292    FILE *f;
293    unsigned char buf[16];
294    krb5_error_code ret;
295
296    off_t len;
297
298    *mkey = NULL;
299
300    if(filename == NULL)
301	filename = HDB_DB_DIR "/m-key";
302
303    f = fopen(filename, "r");
304    if(f == NULL) {
305	int save_errno = errno;
306	krb5_set_error_message(context, save_errno, "failed to open %s: %s",
307			       filename, strerror(save_errno));
308	return save_errno;
309    }
310
311    if(fread(buf, 1, 2, f) != 2) {
312	fclose(f);
313	krb5_set_error_message(context, HEIM_ERR_EOF, "end of file reading %s", filename);
314	return HEIM_ERR_EOF;
315    }
316
317    fseek(f, 0, SEEK_END);
318    len = ftell(f);
319
320    if(fclose(f) != 0)
321	return errno;
322
323    if(len < 0)
324	return errno;
325
326    if(len == 8) {
327	ret = read_master_krb4(context, filename, mkey);
328    } else if(buf[0] == 0x30 && len <= 127 && buf[1] == len - 2) {
329	ret = read_master_encryptionkey(context, filename, mkey);
330    } else if(buf[0] == 5 && buf[1] >= 1 && buf[1] <= 2) {
331	ret = read_master_keytab(context, filename, mkey);
332    } else {
333	ret = read_master_mit(context, filename, mkey);
334    }
335    return ret;
336}
337
338krb5_error_code
339hdb_write_master_key(krb5_context context, const char *filename,
340		     hdb_master_key mkey)
341{
342    krb5_error_code ret;
343    hdb_master_key p;
344    krb5_keytab kt;
345
346    if(filename == NULL)
347	filename = HDB_DB_DIR "/m-key";
348
349    ret = krb5_kt_resolve(context, filename, &kt);
350    if(ret)
351	return ret;
352
353    for(p = mkey; p; p = p->next) {
354	ret = krb5_kt_add_entry(context, kt, &p->keytab);
355    }
356
357    krb5_kt_close(context, kt);
358
359    return ret;
360}
361
362hdb_master_key
363_hdb_find_master_key(uint32_t *mkvno, hdb_master_key mkey)
364{
365    hdb_master_key ret = NULL;
366    while(mkey) {
367	if(ret == NULL && mkey->keytab.vno == 0)
368	    ret = mkey;
369	if(mkvno == NULL) {
370	    if(ret == NULL || mkey->keytab.vno > ret->keytab.vno)
371		ret = mkey;
372	} else if(mkey->keytab.vno == *mkvno)
373	    return mkey;
374	mkey = mkey->next;
375    }
376    return ret;
377}
378
379int
380_hdb_mkey_version(hdb_master_key mkey)
381{
382    return mkey->keytab.vno;
383}
384
385int
386_hdb_mkey_decrypt(krb5_context context, hdb_master_key key,
387		  krb5_key_usage usage,
388		  void *ptr, size_t size, krb5_data *res)
389{
390    return krb5_decrypt(context, key->crypto, usage,
391			ptr, size, res);
392}
393
394int
395_hdb_mkey_encrypt(krb5_context context, hdb_master_key key,
396		  krb5_key_usage usage,
397		  const void *ptr, size_t size, krb5_data *res)
398{
399    return krb5_encrypt(context, key->crypto, usage,
400			ptr, size, res);
401}
402
403krb5_error_code
404hdb_unseal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
405{
406
407    krb5_error_code ret;
408    krb5_data res;
409    size_t keysize;
410
411    hdb_master_key key;
412
413    if(k->mkvno == NULL)
414	return 0;
415
416    key = _hdb_find_master_key(k->mkvno, mkey);
417
418    if (key == NULL)
419	return HDB_ERR_NO_MKEY;
420
421    ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY,
422			    k->key.keyvalue.data,
423			    k->key.keyvalue.length,
424			    &res);
425    if(ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
426	/* try to decrypt with MIT key usage */
427	ret = _hdb_mkey_decrypt(context, key, 0,
428				k->key.keyvalue.data,
429				k->key.keyvalue.length,
430				&res);
431    }
432    if (ret)
433	return ret;
434
435    /* fixup keylength if the key got padded when encrypting it */
436    ret = krb5_enctype_keysize(context, k->key.keytype, &keysize);
437    if (ret) {
438	krb5_data_free(&res);
439	return ret;
440    }
441    if (keysize > res.length) {
442	krb5_data_free(&res);
443	return KRB5_BAD_KEYSIZE;
444    }
445
446    memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
447    free(k->key.keyvalue.data);
448    k->key.keyvalue = res;
449    k->key.keyvalue.length = keysize;
450    free(k->mkvno);
451    k->mkvno = NULL;
452
453    return 0;
454}
455
456krb5_error_code
457hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
458{
459    int i;
460
461    for(i = 0; i < ent->keys.len; i++){
462	krb5_error_code ret;
463
464	ret = hdb_unseal_key_mkey(context, &ent->keys.val[i], mkey);
465	if (ret)
466	    return ret;
467    }
468    return 0;
469}
470
471krb5_error_code
472hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent)
473{
474    if (db->hdb_master_key_set == 0)
475	return 0;
476    return hdb_unseal_keys_mkey(context, ent, db->hdb_master_key);
477}
478
479krb5_error_code
480hdb_unseal_key(krb5_context context, HDB *db, Key *k)
481{
482    if (db->hdb_master_key_set == 0)
483	return 0;
484    return hdb_unseal_key_mkey(context, k, db->hdb_master_key);
485}
486
487krb5_error_code
488hdb_seal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
489{
490    krb5_error_code ret;
491    krb5_data res;
492    hdb_master_key key;
493
494    if(k->mkvno != NULL)
495	return 0;
496
497    key = _hdb_find_master_key(k->mkvno, mkey);
498
499    if (key == NULL)
500	return HDB_ERR_NO_MKEY;
501
502    ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY,
503			    k->key.keyvalue.data,
504			    k->key.keyvalue.length,
505			    &res);
506    if (ret)
507	return ret;
508
509    memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
510    free(k->key.keyvalue.data);
511    k->key.keyvalue = res;
512
513    if (k->mkvno == NULL) {
514	k->mkvno = malloc(sizeof(*k->mkvno));
515	if (k->mkvno == NULL)
516	    return ENOMEM;
517    }
518    *k->mkvno = key->keytab.vno;
519
520    return 0;
521}
522
523krb5_error_code
524hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
525{
526    int i;
527    for(i = 0; i < ent->keys.len; i++){
528	krb5_error_code ret;
529
530	ret = hdb_seal_key_mkey(context, &ent->keys.val[i], mkey);
531	if (ret)
532	    return ret;
533    }
534    return 0;
535}
536
537krb5_error_code
538hdb_seal_keys(krb5_context context, HDB *db, hdb_entry *ent)
539{
540    if (db->hdb_master_key_set == 0)
541	return 0;
542
543    return hdb_seal_keys_mkey(context, ent, db->hdb_master_key);
544}
545
546krb5_error_code
547hdb_seal_key(krb5_context context, HDB *db, Key *k)
548{
549    if (db->hdb_master_key_set == 0)
550	return 0;
551
552    return hdb_seal_key_mkey(context, k, db->hdb_master_key);
553}
554
555krb5_error_code
556hdb_set_master_key (krb5_context context,
557		    HDB *db,
558		    krb5_keyblock *key)
559{
560    krb5_error_code ret;
561    hdb_master_key mkey;
562
563    ret = hdb_process_master_key(context, 0, key, 0, &mkey);
564    if (ret)
565	return ret;
566    db->hdb_master_key = mkey;
567#if 0 /* XXX - why? */
568    des_set_random_generator_seed(key.keyvalue.data);
569#endif
570    db->hdb_master_key_set = 1;
571    return 0;
572}
573
574krb5_error_code
575hdb_set_master_keyfile (krb5_context context,
576			HDB *db,
577			const char *keyfile)
578{
579    hdb_master_key key;
580    krb5_error_code ret;
581
582    ret = hdb_read_master_key(context, keyfile, &key);
583    if (ret) {
584	if (ret != ENOENT)
585	    return ret;
586	krb5_clear_error_message(context);
587	return 0;
588    }
589    db->hdb_master_key = key;
590    db->hdb_master_key_set = 1;
591    return ret;
592}
593
594krb5_error_code
595hdb_clear_master_key (krb5_context context,
596		      HDB *db)
597{
598    if (db->hdb_master_key_set) {
599	hdb_free_master_key(context, db->hdb_master_key);
600	db->hdb_master_key_set = 0;
601    }
602    return 0;
603}
604