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
53key: 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
81key_data_contents[0]
82
83	int16: length
84	read-of-data: key-encrypted, key-usage 0, master-key
85
86salt:
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
106static krb5_error_code
107mdb_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
130static krb5_error_code
131fix_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
200static krb5_error_code
201mdb_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
467static krb5_error_code
468mdb_entry2value(krb5_context context, hdb_entry *entry, krb5_data *data)
469{
470    return EINVAL;
471}
472#endif
473
474
475static krb5_error_code
476mdb_close(krb5_context context, HDB *db)
477{
478    DB *d = (DB*)db->hdb_db;
479    (*d->close)(d);
480    return 0;
481}
482
483static krb5_error_code
484mdb_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
494static krb5_error_code
495mdb_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
507static krb5_error_code
508mdb_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
521static krb5_error_code
522mdb_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
567static krb5_error_code
568mdb_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
574static krb5_error_code
575mdb_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
580static krb5_error_code
581mdb_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
599static krb5_error_code
600mdb__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
628static krb5_error_code
629mdb__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
658static krb5_error_code
659mdb__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
682static krb5_error_code
683mdb_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
710static krb5_error_code
711mdb_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
717static krb5_error_code
718mdb_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
729static krb5_error_code
730mdb_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
780krb5_error_code
781hdb_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