xref: /freebsd/crypto/heimdal/lib/hdb/hdb-mitdb.c (revision d6b92ffa)
1 /*
2  * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Portions Copyright (c) 2009 Apple Inc. 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 #define KRB5_KDB_DISALLOW_POSTDATED	0x00000001
37 #define KRB5_KDB_DISALLOW_FORWARDABLE	0x00000002
38 #define KRB5_KDB_DISALLOW_TGT_BASED	0x00000004
39 #define KRB5_KDB_DISALLOW_RENEWABLE	0x00000008
40 #define KRB5_KDB_DISALLOW_PROXIABLE	0x00000010
41 #define KRB5_KDB_DISALLOW_DUP_SKEY	0x00000020
42 #define KRB5_KDB_DISALLOW_ALL_TIX	0x00000040
43 #define KRB5_KDB_REQUIRES_PRE_AUTH	0x00000080
44 #define KRB5_KDB_REQUIRES_HW_AUTH	0x00000100
45 #define KRB5_KDB_REQUIRES_PWCHANGE	0x00000200
46 #define KRB5_KDB_DISALLOW_SVR		0x00001000
47 #define KRB5_KDB_PWCHANGE_SERVICE	0x00002000
48 #define KRB5_KDB_SUPPORT_DESMD5		0x00004000
49 #define KRB5_KDB_NEW_PRINC		0x00008000
50 
51 /*
52 
53 key: krb5_unparse_name  + NUL
54 
55  16: baselength
56  32: attributes
57  32: max time
58  32: max renewable time
59  32: client expire
60  32: passwd expire
61  32: last successful passwd
62  32: last failed attempt
63  32: num of failed attempts
64  16: num tl data
65  16: num data data
66  16: principal length
67  length: principal
68  for num tl data times
69     16: tl data type
70     16: tl data length
71     length: length
72  for num key data times
73     16: version (num keyblocks)
74     16: kvno
75     for version times:
76         16: type
77         16: length
78         length: keydata
79 
80 
81 key_data_contents[0]
82 
83 	int16: length
84 	read-of-data: key-encrypted, key-usage 0, master-key
85 
86 salt:
87     version2 = salt in key_data->key_data_contents[1]
88     else default salt.
89 
90 */
91 
92 #include "hdb_locl.h"
93 
94 #define KDB_V1_BASE_LENGTH 38
95 
96 #if HAVE_DB1
97 
98 #if defined(HAVE_DB_185_H)
99 #include <db_185.h>
100 #elif defined(HAVE_DB_H)
101 #include <db.h>
102 #endif
103 
104 #define CHECK(x) do { if ((x)) goto out; } while(0)
105 
106 static krb5_error_code
107 mdb_principal2key(krb5_context context,
108 		  krb5_const_principal principal,
109 		  krb5_data *key)
110 {
111     krb5_error_code ret;
112     char *str;
113 
114     ret = krb5_unparse_name(context, principal, &str);
115     if (ret)
116 	return ret;
117     key->data = str;
118     key->length = strlen(str) + 1;
119     return 0;
120 }
121 
122 #define KRB5_KDB_SALTTYPE_NORMAL	0
123 #define KRB5_KDB_SALTTYPE_V4		1
124 #define KRB5_KDB_SALTTYPE_NOREALM	2
125 #define KRB5_KDB_SALTTYPE_ONLYREALM	3
126 #define KRB5_KDB_SALTTYPE_SPECIAL	4
127 #define KRB5_KDB_SALTTYPE_AFS3		5
128 #define KRB5_KDB_SALTTYPE_CERTHASH	6
129 
130 static krb5_error_code
131 fix_salt(krb5_context context, hdb_entry *ent, int key_num)
132 {
133     krb5_error_code ret;
134     Salt *salt = ent->keys.val[key_num].salt;
135     /* fix salt type */
136     switch((int)salt->type) {
137     case KRB5_KDB_SALTTYPE_NORMAL:
138 	salt->type = KRB5_PADATA_PW_SALT;
139 	break;
140     case KRB5_KDB_SALTTYPE_V4:
141 	krb5_data_free(&salt->salt);
142 	salt->type = KRB5_PADATA_PW_SALT;
143 	break;
144     case KRB5_KDB_SALTTYPE_NOREALM:
145     {
146 	size_t len;
147 	size_t i;
148 	char *p;
149 
150 	len = 0;
151 	for (i = 0; i < ent->principal->name.name_string.len; ++i)
152 	    len += strlen(ent->principal->name.name_string.val[i]);
153 	ret = krb5_data_alloc (&salt->salt, len);
154 	if (ret)
155 	    return ret;
156 	p = salt->salt.data;
157 	for (i = 0; i < ent->principal->name.name_string.len; ++i) {
158 	    memcpy (p,
159 		    ent->principal->name.name_string.val[i],
160 		    strlen(ent->principal->name.name_string.val[i]));
161 	    p += strlen(ent->principal->name.name_string.val[i]);
162 	}
163 
164 	salt->type = KRB5_PADATA_PW_SALT;
165 	break;
166     }
167     case KRB5_KDB_SALTTYPE_ONLYREALM:
168 	krb5_data_free(&salt->salt);
169 	ret = krb5_data_copy(&salt->salt,
170 			     ent->principal->realm,
171 			     strlen(ent->principal->realm));
172 	if(ret)
173 	    return ret;
174 	salt->type = KRB5_PADATA_PW_SALT;
175 	break;
176     case KRB5_KDB_SALTTYPE_SPECIAL:
177 	salt->type = KRB5_PADATA_PW_SALT;
178 	break;
179     case KRB5_KDB_SALTTYPE_AFS3:
180 	krb5_data_free(&salt->salt);
181 	ret = krb5_data_copy(&salt->salt,
182 		       ent->principal->realm,
183 		       strlen(ent->principal->realm));
184 	if(ret)
185 	    return ret;
186 	salt->type = KRB5_PADATA_AFS3_SALT;
187 	break;
188     case KRB5_KDB_SALTTYPE_CERTHASH:
189 	krb5_data_free(&salt->salt);
190 	free(ent->keys.val[key_num].salt);
191 	ent->keys.val[key_num].salt = NULL;
192 	break;
193     default:
194 	abort();
195     }
196     return 0;
197 }
198 
199 
200 static krb5_error_code
201 mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry *entry)
202 {
203     krb5_error_code ret;
204     krb5_storage *sp;
205     uint32_t u32;
206     uint16_t u16, num_keys, num_tl;
207     size_t i, j;
208     char *p;
209 
210     sp = krb5_storage_from_data(data);
211     if (sp == NULL) {
212 	krb5_set_error_message(context, ENOMEM, "out of memory");
213 	return ENOMEM;
214     }
215 
216     krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
217 
218     /*
219      * 16: baselength
220      *
221      * The story here is that these 16 bits have to be a constant:
222      * KDB_V1_BASE_LENGTH.  Once upon a time a different value here
223      * would have been used to indicate the presence of "extra data"
224      * between the "base" contents and the {principal name, TL data,
225      * keys} that follow it.  Nothing supports such "extra data"
226      * nowadays, so neither do we here.
227      *
228      * XXX But... surely we ought to log about this extra data, or skip
229      * it, or something, in case anyone has MIT KDBs with ancient
230      * entries in them...  Logging would allow the admin to know which
231      * entries to dump with MIT krb5's kdb5_util.
232      */
233     CHECK(ret = krb5_ret_uint16(sp, &u16));
234     if (u16 != KDB_V1_BASE_LENGTH) { ret = EINVAL; goto out; }
235     /* 32: attributes */
236     CHECK(ret = krb5_ret_uint32(sp, &u32));
237     entry->flags.postdate =	 !(u32 & KRB5_KDB_DISALLOW_POSTDATED);
238     entry->flags.forwardable =	 !(u32 & KRB5_KDB_DISALLOW_FORWARDABLE);
239     entry->flags.initial =	!!(u32 & KRB5_KDB_DISALLOW_TGT_BASED);
240     entry->flags.renewable =	 !(u32 & KRB5_KDB_DISALLOW_RENEWABLE);
241     entry->flags.proxiable =	 !(u32 & KRB5_KDB_DISALLOW_PROXIABLE);
242     /* DUP_SKEY */
243     entry->flags.invalid =	!!(u32 & KRB5_KDB_DISALLOW_ALL_TIX);
244     entry->flags.require_preauth =!!(u32 & KRB5_KDB_REQUIRES_PRE_AUTH);
245     entry->flags.require_hwauth =!!(u32 & KRB5_KDB_REQUIRES_HW_AUTH);
246     entry->flags.server =	 !(u32 & KRB5_KDB_DISALLOW_SVR);
247     entry->flags.change_pw = 	!!(u32 & KRB5_KDB_PWCHANGE_SERVICE);
248     entry->flags.client =	   1; /* XXX */
249 
250     /* 32: max time */
251     CHECK(ret = krb5_ret_uint32(sp, &u32));
252     if (u32) {
253 	entry->max_life = malloc(sizeof(*entry->max_life));
254 	*entry->max_life = u32;
255     }
256     /* 32: max renewable time */
257     CHECK(ret = krb5_ret_uint32(sp, &u32));
258     if (u32) {
259 	entry->max_renew = malloc(sizeof(*entry->max_renew));
260 	*entry->max_renew = u32;
261     }
262     /* 32: client expire */
263     CHECK(ret = krb5_ret_uint32(sp, &u32));
264     if (u32) {
265 	entry->valid_end = malloc(sizeof(*entry->valid_end));
266 	*entry->valid_end = u32;
267     }
268     /* 32: passwd expire */
269     CHECK(ret = krb5_ret_uint32(sp, &u32));
270     if (u32) {
271 	entry->pw_end = malloc(sizeof(*entry->pw_end));
272 	*entry->pw_end = u32;
273     }
274     /* 32: last successful passwd */
275     CHECK(ret = krb5_ret_uint32(sp, &u32));
276     /* 32: last failed attempt */
277     CHECK(ret = krb5_ret_uint32(sp, &u32));
278     /* 32: num of failed attempts */
279     CHECK(ret = krb5_ret_uint32(sp, &u32));
280     /* 16: num tl data */
281     CHECK(ret = krb5_ret_uint16(sp, &u16));
282     num_tl = u16;
283     /* 16: num key data */
284     CHECK(ret = krb5_ret_uint16(sp, &u16));
285     num_keys = u16;
286     /* 16: principal length */
287     CHECK(ret = krb5_ret_uint16(sp, &u16));
288     /* length: principal */
289     {
290 	/*
291 	 * Note that the principal name includes the NUL in the entry,
292 	 * but we don't want to take chances, so we add an extra NUL.
293 	 */
294 	p = malloc(u16 + 1);
295 	if (p == NULL) {
296 	    ret = ENOMEM;
297 	    goto out;
298 	}
299 	krb5_storage_read(sp, p, u16);
300 	p[u16] = '\0';
301 	CHECK(ret = krb5_parse_name(context, p, &entry->principal));
302 	free(p);
303     }
304     /* for num tl data times
305            16: tl data type
306            16: tl data length
307            length: length */
308     for (i = 0; i < num_tl; i++) {
309 	/* 16: TL data type */
310 	CHECK(ret = krb5_ret_uint16(sp, &u16));
311 	/* 16: TL data length */
312 	CHECK(ret = krb5_ret_uint16(sp, &u16));
313 	krb5_storage_seek(sp, u16, SEEK_CUR);
314     }
315     /*
316      * for num key data times
317      * 16: "version"
318      * 16: kvno
319      * for version times:
320      *     16: type
321      *     16: length
322      *     length: keydata
323      *
324      * "version" here is really 1 or 2, the first meaning there's only
325      * keys for this kvno, the second meaning there's keys and salt[s?].
326      * That's right... hold that gag reflex, you can do it.
327      */
328     for (i = 0; i < num_keys; i++) {
329 	int keep = 0;
330 	uint16_t version;
331 	void *ptr;
332 
333 	CHECK(ret = krb5_ret_uint16(sp, &u16));
334 	version = u16;
335 	CHECK(ret = krb5_ret_uint16(sp, &u16));
336 
337 	/*
338 	 * First time through, and until we find one matching key,
339 	 * entry->kvno == 0.
340 	 */
341 	if ((entry->kvno < u16) && (kvno == 0 || kvno == u16)) {
342 	    keep = 1;
343 	    entry->kvno = u16;
344 	    /*
345 	     * Found a higher kvno than earlier, so free the old highest
346 	     * kvno keys.
347 	     *
348 	     * XXX Of course, we actually want to extract the old kvnos
349 	     * as well, for some of the kadm5 APIs.  We shouldn't free
350 	     * these keys, but keep them elsewhere.
351 	     */
352 	    for (j = 0; j < entry->keys.len; j++)
353 		free_Key(&entry->keys.val[j]);
354 	    free(entry->keys.val);
355 	    entry->keys.len = 0;
356 	    entry->keys.val = NULL;
357 	} else if (entry->kvno == u16)
358 	    /* Accumulate keys */
359 	    keep = 1;
360 
361 	if (keep) {
362 	    Key *k;
363 
364 	    ptr = realloc(entry->keys.val, sizeof(entry->keys.val[0]) * (entry->keys.len + 1));
365 	    if (ptr == NULL) {
366 		ret = ENOMEM;
367 		goto out;
368 	    }
369 	    entry->keys.val = ptr;
370 
371 	    /* k points to current Key */
372 	    k = &entry->keys.val[entry->keys.len];
373 
374 	    memset(k, 0, sizeof(*k));
375 	    entry->keys.len += 1;
376 
377 	    k->mkvno = malloc(sizeof(*k->mkvno));
378 	    if (k->mkvno == NULL) {
379 		ret = ENOMEM;
380 		goto out;
381 	    }
382 	    *k->mkvno = 1;
383 
384 	    for (j = 0; j < version; j++) {
385 		uint16_t type;
386 		CHECK(ret = krb5_ret_uint16(sp, &type));
387 		CHECK(ret = krb5_ret_uint16(sp, &u16));
388 		if (j == 0) {
389 		    /* This "version" means we have a key */
390 		    k->key.keytype = type;
391 		    if (u16 < 2) {
392 			ret = EINVAL;
393 			goto out;
394 		    }
395 		    /*
396 		     * MIT stores keys encrypted keys as {16-bit length
397 		     * of plaintext key, {encrypted key}}.  The reason
398 		     * for this is that the Kerberos cryptosystem is not
399 		     * length-preserving.  Heimdal's approach is to
400 		     * truncate the plaintext to the expected length of
401 		     * the key given its enctype, so we ignore this
402 		     * 16-bit length-of-plaintext-key field.
403 		     */
404 		    krb5_storage_seek(sp, 2, SEEK_CUR); /* skip real length */
405 		    k->key.keyvalue.length = u16 - 2;   /* adjust cipher len */
406 		    k->key.keyvalue.data = malloc(k->key.keyvalue.length);
407 		    krb5_storage_read(sp, k->key.keyvalue.data,
408 				      k->key.keyvalue.length);
409 		} else if (j == 1) {
410 		    /* This "version" means we have a salt */
411 		    k->salt = calloc(1, sizeof(*k->salt));
412 		    if (k->salt == NULL) {
413 			ret = ENOMEM;
414 			goto out;
415 		    }
416 		    k->salt->type = type;
417 		    if (u16 != 0) {
418 			k->salt->salt.data = malloc(u16);
419 			if (k->salt->salt.data == NULL) {
420 			    ret = ENOMEM;
421 			    goto out;
422 			}
423 			k->salt->salt.length = u16;
424 			krb5_storage_read(sp, k->salt->salt.data, k->salt->salt.length);
425 		    }
426 		    fix_salt(context, entry, entry->keys.len - 1);
427 		} else {
428 		    /*
429 		     * Whatever this "version" might be, we skip it
430 		     *
431 		     * XXX A krb5.conf parameter requesting that we log
432 		     * about strangeness like this, or return an error
433 		     * from here, might be nice.
434 		     */
435 		    krb5_storage_seek(sp, u16, SEEK_CUR);
436 		}
437 	    }
438 	} else {
439 	    /*
440 	     * XXX For now we skip older kvnos, but we should extract
441 	     * them...
442 	     */
443 	    for (j = 0; j < version; j++) {
444 		/* enctype */
445 		CHECK(ret = krb5_ret_uint16(sp, &u16));
446 		/* encrypted key (or plaintext salt) */
447 		CHECK(ret = krb5_ret_uint16(sp, &u16));
448 		krb5_storage_seek(sp, u16, SEEK_CUR);
449 	    }
450 	}
451     }
452 
453     if (entry->kvno == 0 && kvno != 0) {
454 	ret = HDB_ERR_NOT_FOUND_HERE;
455 	goto out;
456     }
457 
458     return 0;
459  out:
460     if (ret == HEIM_ERR_EOF)
461 	/* Better error code than "end of file" */
462 	ret = HEIM_ERR_BAD_HDBENT_ENCODING;
463     return ret;
464 }
465 
466 #if 0
467 static krb5_error_code
468 mdb_entry2value(krb5_context context, hdb_entry *entry, krb5_data *data)
469 {
470     return EINVAL;
471 }
472 #endif
473 
474 
475 static krb5_error_code
476 mdb_close(krb5_context context, HDB *db)
477 {
478     DB *d = (DB*)db->hdb_db;
479     (*d->close)(d);
480     return 0;
481 }
482 
483 static krb5_error_code
484 mdb_destroy(krb5_context context, HDB *db)
485 {
486     krb5_error_code ret;
487 
488     ret = hdb_clear_master_key (context, db);
489     free(db->hdb_name);
490     free(db);
491     return ret;
492 }
493 
494 static krb5_error_code
495 mdb_lock(krb5_context context, HDB *db, int operation)
496 {
497     DB *d = (DB*)db->hdb_db;
498     int fd = (*d->fd)(d);
499     if(fd < 0) {
500 	krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
501 			       "Can't lock database: %s", db->hdb_name);
502 	return HDB_ERR_CANT_LOCK_DB;
503     }
504     return hdb_lock(fd, operation);
505 }
506 
507 static krb5_error_code
508 mdb_unlock(krb5_context context, HDB *db)
509 {
510     DB *d = (DB*)db->hdb_db;
511     int fd = (*d->fd)(d);
512     if(fd < 0) {
513 	krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
514 			       "Can't unlock database: %s", db->hdb_name);
515 	return HDB_ERR_CANT_LOCK_DB;
516     }
517     return hdb_unlock(fd);
518 }
519 
520 
521 static krb5_error_code
522 mdb_seq(krb5_context context, HDB *db,
523        unsigned flags, hdb_entry_ex *entry, int flag)
524 {
525     DB *d = (DB*)db->hdb_db;
526     DBT key, value;
527     krb5_data key_data, data;
528     int code;
529 
530     code = db->hdb_lock(context, db, HDB_RLOCK);
531     if(code == -1) {
532 	krb5_set_error_message(context, HDB_ERR_DB_INUSE, "Database %s in use", db->hdb_name);
533 	return HDB_ERR_DB_INUSE;
534     }
535     code = (*d->seq)(d, &key, &value, flag);
536     db->hdb_unlock(context, db); /* XXX check value */
537     if(code == -1) {
538 	code = errno;
539 	krb5_set_error_message(context, code, "Database %s seq error: %s",
540 			       db->hdb_name, strerror(code));
541 	return code;
542     }
543     if(code == 1) {
544 	krb5_clear_error_message(context);
545 	return HDB_ERR_NOENTRY;
546     }
547 
548     key_data.data = key.data;
549     key_data.length = key.size;
550     data.data = value.data;
551     data.length = value.size;
552     memset(entry, 0, sizeof(*entry));
553 
554     if (mdb_value2entry(context, &data, 0, &entry->entry))
555 	return mdb_seq(context, db, flags, entry, R_NEXT);
556 
557     if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
558 	code = hdb_unseal_keys (context, db, &entry->entry);
559 	if (code)
560 	    hdb_free_entry (context, entry);
561     }
562 
563     return code;
564 }
565 
566 
567 static krb5_error_code
568 mdb_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
569 {
570     return mdb_seq(context, db, flags, entry, R_FIRST);
571 }
572 
573 
574 static krb5_error_code
575 mdb_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
576 {
577     return mdb_seq(context, db, flags, entry, R_NEXT);
578 }
579 
580 static krb5_error_code
581 mdb_rename(krb5_context context, HDB *db, const char *new_name)
582 {
583     int ret;
584     char *old, *new;
585 
586     asprintf(&old, "%s.db", db->hdb_name);
587     asprintf(&new, "%s.db", new_name);
588     ret = rename(old, new);
589     free(old);
590     free(new);
591     if(ret)
592 	return errno;
593 
594     free(db->hdb_name);
595     db->hdb_name = strdup(new_name);
596     return 0;
597 }
598 
599 static krb5_error_code
600 mdb__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
601 {
602     DB *d = (DB*)db->hdb_db;
603     DBT k, v;
604     int code;
605 
606     k.data = key.data;
607     k.size = key.length;
608     code = db->hdb_lock(context, db, HDB_RLOCK);
609     if(code)
610 	return code;
611     code = (*d->get)(d, &k, &v, 0);
612     db->hdb_unlock(context, db);
613     if(code < 0) {
614 	code = errno;
615 	krb5_set_error_message(context, code, "Database %s get error: %s",
616 			       db->hdb_name, strerror(code));
617 	return code;
618     }
619     if(code == 1) {
620 	krb5_clear_error_message(context);
621 	return HDB_ERR_NOENTRY;
622     }
623 
624     krb5_data_copy(reply, v.data, v.size);
625     return 0;
626 }
627 
628 static krb5_error_code
629 mdb__put(krb5_context context, HDB *db, int replace,
630 	krb5_data key, krb5_data value)
631 {
632     DB *d = (DB*)db->hdb_db;
633     DBT k, v;
634     int code;
635 
636     k.data = key.data;
637     k.size = key.length;
638     v.data = value.data;
639     v.size = value.length;
640     code = db->hdb_lock(context, db, HDB_WLOCK);
641     if(code)
642 	return code;
643     code = (*d->put)(d, &k, &v, replace ? 0 : R_NOOVERWRITE);
644     db->hdb_unlock(context, db);
645     if(code < 0) {
646 	code = errno;
647 	krb5_set_error_message(context, code, "Database %s put error: %s",
648 			       db->hdb_name, strerror(code));
649 	return code;
650     }
651     if(code == 1) {
652 	krb5_clear_error_message(context);
653 	return HDB_ERR_EXISTS;
654     }
655     return 0;
656 }
657 
658 static krb5_error_code
659 mdb__del(krb5_context context, HDB *db, krb5_data key)
660 {
661     DB *d = (DB*)db->hdb_db;
662     DBT k;
663     krb5_error_code code;
664     k.data = key.data;
665     k.size = key.length;
666     code = db->hdb_lock(context, db, HDB_WLOCK);
667     if(code)
668 	return code;
669     code = (*d->del)(d, &k, 0);
670     db->hdb_unlock(context, db);
671     if(code == 1) {
672 	code = errno;
673 	krb5_set_error_message(context, code, "Database %s put error: %s",
674 			       db->hdb_name, strerror(code));
675 	return code;
676     }
677     if(code < 0)
678 	return errno;
679     return 0;
680 }
681 
682 static krb5_error_code
683 mdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
684 	       unsigned flags, krb5_kvno kvno, hdb_entry_ex *entry)
685 {
686     krb5_data key, value;
687     krb5_error_code code;
688 
689     code = mdb_principal2key(context, principal, &key);
690     if (code)
691 	return code;
692     code = db->hdb__get(context, db, key, &value);
693     krb5_data_free(&key);
694     if(code)
695 	return code;
696     code = mdb_value2entry(context, &value, kvno, &entry->entry);
697     krb5_data_free(&value);
698     if (code)
699 	return code;
700 
701     if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
702 	code = hdb_unseal_keys (context, db, &entry->entry);
703 	if (code)
704 	    hdb_free_entry(context, entry);
705     }
706 
707     return 0;
708 }
709 
710 static krb5_error_code
711 mdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
712 {
713     krb5_set_error_message(context, EINVAL, "can't set principal in mdb");
714     return EINVAL;
715 }
716 
717 static krb5_error_code
718 mdb_remove(krb5_context context, HDB *db, krb5_const_principal principal)
719 {
720     krb5_error_code code;
721     krb5_data key;
722 
723     mdb_principal2key(context, principal, &key);
724     code = db->hdb__del(context, db, key);
725     krb5_data_free(&key);
726     return code;
727 }
728 
729 static krb5_error_code
730 mdb_open(krb5_context context, HDB *db, int flags, mode_t mode)
731 {
732     char *fn;
733     krb5_error_code ret;
734 
735     asprintf(&fn, "%s.db", db->hdb_name);
736     if (fn == NULL) {
737 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
738 	return ENOMEM;
739     }
740     db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL);
741     free(fn);
742 
743     if (db->hdb_db == NULL) {
744 	switch (errno) {
745 #ifdef EFTYPE
746 	case EFTYPE:
747 #endif
748 	case EINVAL:
749 	    db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL);
750 	}
751     }
752 
753     /* try to open without .db extension */
754     if(db->hdb_db == NULL && errno == ENOENT)
755 	db->hdb_db = dbopen(db->hdb_name, flags, mode, DB_BTREE, NULL);
756     if(db->hdb_db == NULL) {
757 	ret = errno;
758 	krb5_set_error_message(context, ret, "dbopen (%s): %s",
759 			      db->hdb_name, strerror(ret));
760 	return ret;
761     }
762     if((flags & O_ACCMODE) == O_RDONLY)
763 	ret = hdb_check_db_format(context, db);
764     else
765 	ret = hdb_init_db(context, db);
766     if(ret == HDB_ERR_NOENTRY) {
767 	krb5_clear_error_message(context);
768 	return 0;
769     }
770     if (ret) {
771 	mdb_close(context, db);
772 	krb5_set_error_message(context, ret, "hdb_open: failed %s database %s",
773 			      (flags & O_ACCMODE) == O_RDONLY ?
774 			      "checking format of" : "initialize",
775 			      db->hdb_name);
776     }
777     return ret;
778 }
779 
780 krb5_error_code
781 hdb_mdb_create(krb5_context context, HDB **db,
782 	       const char *filename)
783 {
784     *db = calloc(1, sizeof(**db));
785     if (*db == NULL) {
786 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
787 	return ENOMEM;
788     }
789 
790     (*db)->hdb_db = NULL;
791     (*db)->hdb_name = strdup(filename);
792     if ((*db)->hdb_name == NULL) {
793 	free(*db);
794 	*db = NULL;
795 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
796 	return ENOMEM;
797     }
798     (*db)->hdb_master_key_set = 0;
799     (*db)->hdb_openp = 0;
800     (*db)->hdb_capability_flags = 0;
801     (*db)->hdb_open = mdb_open;
802     (*db)->hdb_close = mdb_close;
803     (*db)->hdb_fetch_kvno = mdb_fetch_kvno;
804     (*db)->hdb_store = mdb_store;
805     (*db)->hdb_remove = mdb_remove;
806     (*db)->hdb_firstkey = mdb_firstkey;
807     (*db)->hdb_nextkey= mdb_nextkey;
808     (*db)->hdb_lock = mdb_lock;
809     (*db)->hdb_unlock = mdb_unlock;
810     (*db)->hdb_rename = mdb_rename;
811     (*db)->hdb__get = mdb__get;
812     (*db)->hdb__put = mdb__put;
813     (*db)->hdb__del = mdb__del;
814     (*db)->hdb_destroy = mdb_destroy;
815     return 0;
816 }
817 
818 #endif /* HAVE_DB1 */
819