]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - crypto/heimdal/lib/hdb/hdb-mitdb.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / crypto / heimdal / lib / hdb / hdb-mitdb.c
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 */