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