2 * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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.
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.
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
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
53 key: krb5_unparse_name + NUL
58 32: max renewable time
61 32: last successful passwd
62 32: last failed attempt
63 32: num of failed attempts
72 for num key data times
73 16: version (num keyblocks)
84 read-of-data: key-encrypted, key-usage 0, master-key
87 version2 = salt in key_data->key_data_contents[1]
94 #define KDB_V1_BASE_LENGTH 38
98 #if defined(HAVE_DB_185_H)
100 #elif defined(HAVE_DB_H)
104 #define CHECK(x) do { if ((x)) goto out; } while(0)
106 static krb5_error_code
107 mdb_principal2key(krb5_context context
,
108 krb5_const_principal principal
,
114 ret
= krb5_unparse_name(context
, principal
, &str
);
118 key
->length
= strlen(str
) + 1;
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
130 static krb5_error_code
131 fix_salt(krb5_context context
, hdb_entry
*ent
, int key_num
)
134 Salt
*salt
= ent
->keys
.val
[key_num
].salt
;
136 switch((int)salt
->type
) {
137 case KRB5_KDB_SALTTYPE_NORMAL
:
138 salt
->type
= KRB5_PADATA_PW_SALT
;
140 case KRB5_KDB_SALTTYPE_V4
:
141 krb5_data_free(&salt
->salt
);
142 salt
->type
= KRB5_PADATA_PW_SALT
;
144 case KRB5_KDB_SALTTYPE_NOREALM
:
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
);
157 for (i
= 0; i
< ent
->principal
->name
.name_string
.len
; ++i
) {
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
]);
164 salt
->type
= KRB5_PADATA_PW_SALT
;
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
));
174 salt
->type
= KRB5_PADATA_PW_SALT
;
176 case KRB5_KDB_SALTTYPE_SPECIAL
:
177 salt
->type
= KRB5_PADATA_PW_SALT
;
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
));
186 salt
->type
= KRB5_PADATA_AFS3_SALT
;
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
;
200 static krb5_error_code
201 mdb_value2entry(krb5_context context
, krb5_data
*data
, krb5_kvno kvno
, hdb_entry
*entry
)
206 uint16_t u16
, num_keys
, num_tl
;
210 sp
= krb5_storage_from_data(data
);
212 krb5_set_error_message(context
, ENOMEM
, "out of memory");
216 krb5_storage_set_byteorder(sp
, KRB5_STORAGE_BYTEORDER_LE
);
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.
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.
233 CHECK(ret
= krb5_ret_uint16(sp
, &u16
));
234 if (u16
!= KDB_V1_BASE_LENGTH
) { ret
= EINVAL
; goto out
; }
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
);
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 */
251 CHECK(ret
= krb5_ret_uint32(sp
, &u32
));
253 entry
->max_life
= malloc(sizeof(*entry
->max_life
));
254 *entry
->max_life
= u32
;
256 /* 32: max renewable time */
257 CHECK(ret
= krb5_ret_uint32(sp
, &u32
));
259 entry
->max_renew
= malloc(sizeof(*entry
->max_renew
));
260 *entry
->max_renew
= u32
;
262 /* 32: client expire */
263 CHECK(ret
= krb5_ret_uint32(sp
, &u32
));
265 entry
->valid_end
= malloc(sizeof(*entry
->valid_end
));
266 *entry
->valid_end
= u32
;
268 /* 32: passwd expire */
269 CHECK(ret
= krb5_ret_uint32(sp
, &u32
));
271 entry
->pw_end
= malloc(sizeof(*entry
->pw_end
));
272 *entry
->pw_end
= u32
;
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
));
283 /* 16: num key data */
284 CHECK(ret
= krb5_ret_uint16(sp
, &u16
));
286 /* 16: principal length */
287 CHECK(ret
= krb5_ret_uint16(sp
, &u16
));
288 /* length: principal */
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.
299 krb5_storage_read(sp
, p
, u16
);
301 CHECK(ret
= krb5_parse_name(context
, p
, &entry
->principal
));
304 /* for num tl data times
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
);
316 * for num key data times
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.
328 for (i
= 0; i
< num_keys
; i
++) {
333 CHECK(ret
= krb5_ret_uint16(sp
, &u16
));
335 CHECK(ret
= krb5_ret_uint16(sp
, &u16
));
338 * First time through, and until we find one matching key,
341 if ((entry
->kvno
< u16
) && (kvno
== 0 || kvno
== u16
)) {
345 * Found a higher kvno than earlier, so free the old highest
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.
352 for (j
= 0; j
< entry
->keys
.len
; j
++)
353 free_Key(&entry
->keys
.val
[j
]);
354 free(entry
->keys
.val
);
356 entry
->keys
.val
= NULL
;
357 } else if (entry
->kvno
== u16
)
358 /* Accumulate keys */
364 ptr
= realloc(entry
->keys
.val
, sizeof(entry
->keys
.val
[0]) * (entry
->keys
.len
+ 1));
369 entry
->keys
.val
= ptr
;
371 /* k points to current Key */
372 k
= &entry
->keys
.val
[entry
->keys
.len
];
374 memset(k
, 0, sizeof(*k
));
375 entry
->keys
.len
+= 1;
377 k
->mkvno
= malloc(sizeof(*k
->mkvno
));
378 if (k
->mkvno
== NULL
) {
384 for (j
= 0; j
< version
; j
++) {
386 CHECK(ret
= krb5_ret_uint16(sp
, &type
));
387 CHECK(ret
= krb5_ret_uint16(sp
, &u16
));
389 /* This "version" means we have a key */
390 k
->key
.keytype
= type
;
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.
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
);
410 /* This "version" means we have a salt */
411 k
->salt
= calloc(1, sizeof(*k
->salt
));
412 if (k
->salt
== NULL
) {
416 k
->salt
->type
= type
;
418 k
->salt
->salt
.data
= malloc(u16
);
419 if (k
->salt
->salt
.data
== NULL
) {
423 k
->salt
->salt
.length
= u16
;
424 krb5_storage_read(sp
, k
->salt
->salt
.data
, k
->salt
->salt
.length
);
426 fix_salt(context
, entry
, entry
->keys
.len
- 1);
429 * Whatever this "version" might be, we skip it
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.
435 krb5_storage_seek(sp
, u16
, SEEK_CUR
);
440 * XXX For now we skip older kvnos, but we should extract
443 for (j
= 0; j
< version
; j
++) {
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
);
453 if (entry
->kvno
== 0 && kvno
!= 0) {
454 ret
= HDB_ERR_NOT_FOUND_HERE
;
460 if (ret
== HEIM_ERR_EOF
)
461 /* Better error code than "end of file" */
462 ret
= HEIM_ERR_BAD_HDBENT_ENCODING
;
467 static krb5_error_code
468 mdb_entry2value(krb5_context context
, hdb_entry
*entry
, krb5_data
*data
)
475 static krb5_error_code
476 mdb_close(krb5_context context
, HDB
*db
)
478 DB
*d
= (DB
*)db
->hdb_db
;
483 static krb5_error_code
484 mdb_destroy(krb5_context context
, HDB
*db
)
488 ret
= hdb_clear_master_key (context
, db
);
494 static krb5_error_code
495 mdb_lock(krb5_context context
, HDB
*db
, int operation
)
497 DB
*d
= (DB
*)db
->hdb_db
;
498 int fd
= (*d
->fd
)(d
);
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
;
504 return hdb_lock(fd
, operation
);
507 static krb5_error_code
508 mdb_unlock(krb5_context context
, HDB
*db
)
510 DB
*d
= (DB
*)db
->hdb_db
;
511 int fd
= (*d
->fd
)(d
);
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
;
517 return hdb_unlock(fd
);
521 static krb5_error_code
522 mdb_seq(krb5_context context
, HDB
*db
,
523 unsigned flags
, hdb_entry_ex
*entry
, int flag
)
525 DB
*d
= (DB
*)db
->hdb_db
;
527 krb5_data key_data
, data
;
530 code
= db
->hdb_lock(context
, db
, HDB_RLOCK
);
532 krb5_set_error_message(context
, HDB_ERR_DB_INUSE
, "Database %s in use", db
->hdb_name
);
533 return HDB_ERR_DB_INUSE
;
535 code
= (*d
->seq
)(d
, &key
, &value
, flag
);
536 db
->hdb_unlock(context
, db
); /* XXX check value */
539 krb5_set_error_message(context
, code
, "Database %s seq error: %s",
540 db
->hdb_name
, strerror(code
));
544 krb5_clear_error_message(context
);
545 return HDB_ERR_NOENTRY
;
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
));
554 if (mdb_value2entry(context
, &data
, 0, &entry
->entry
))
555 return mdb_seq(context
, db
, flags
, entry
, R_NEXT
);
557 if (db
->hdb_master_key_set
&& (flags
& HDB_F_DECRYPT
)) {
558 code
= hdb_unseal_keys (context
, db
, &entry
->entry
);
560 hdb_free_entry (context
, entry
);
567 static krb5_error_code
568 mdb_firstkey(krb5_context context
, HDB
*db
, unsigned flags
, hdb_entry_ex
*entry
)
570 return mdb_seq(context
, db
, flags
, entry
, R_FIRST
);
574 static krb5_error_code
575 mdb_nextkey(krb5_context context
, HDB
*db
, unsigned flags
, hdb_entry_ex
*entry
)
577 return mdb_seq(context
, db
, flags
, entry
, R_NEXT
);
580 static krb5_error_code
581 mdb_rename(krb5_context context
, HDB
*db
, const char *new_name
)
586 asprintf(&old
, "%s.db", db
->hdb_name
);
587 asprintf(&new, "%s.db", new_name
);
588 ret
= rename(old
, new);
595 db
->hdb_name
= strdup(new_name
);
599 static krb5_error_code
600 mdb__get(krb5_context context
, HDB
*db
, krb5_data key
, krb5_data
*reply
)
602 DB
*d
= (DB
*)db
->hdb_db
;
608 code
= db
->hdb_lock(context
, db
, HDB_RLOCK
);
611 code
= (*d
->get
)(d
, &k
, &v
, 0);
612 db
->hdb_unlock(context
, db
);
615 krb5_set_error_message(context
, code
, "Database %s get error: %s",
616 db
->hdb_name
, strerror(code
));
620 krb5_clear_error_message(context
);
621 return HDB_ERR_NOENTRY
;
624 krb5_data_copy(reply
, v
.data
, v
.size
);
628 static krb5_error_code
629 mdb__put(krb5_context context
, HDB
*db
, int replace
,
630 krb5_data key
, krb5_data value
)
632 DB
*d
= (DB
*)db
->hdb_db
;
639 v
.size
= value
.length
;
640 code
= db
->hdb_lock(context
, db
, HDB_WLOCK
);
643 code
= (*d
->put
)(d
, &k
, &v
, replace
? 0 : R_NOOVERWRITE
);
644 db
->hdb_unlock(context
, db
);
647 krb5_set_error_message(context
, code
, "Database %s put error: %s",
648 db
->hdb_name
, strerror(code
));
652 krb5_clear_error_message(context
);
653 return HDB_ERR_EXISTS
;
658 static krb5_error_code
659 mdb__del(krb5_context context
, HDB
*db
, krb5_data key
)
661 DB
*d
= (DB
*)db
->hdb_db
;
663 krb5_error_code code
;
666 code
= db
->hdb_lock(context
, db
, HDB_WLOCK
);
669 code
= (*d
->del
)(d
, &k
, 0);
670 db
->hdb_unlock(context
, db
);
673 krb5_set_error_message(context
, code
, "Database %s put error: %s",
674 db
->hdb_name
, strerror(code
));
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
)
686 krb5_data key
, value
;
687 krb5_error_code code
;
689 code
= mdb_principal2key(context
, principal
, &key
);
692 code
= db
->hdb__get(context
, db
, key
, &value
);
693 krb5_data_free(&key
);
696 code
= mdb_value2entry(context
, &value
, kvno
, &entry
->entry
);
697 krb5_data_free(&value
);
701 if (db
->hdb_master_key_set
&& (flags
& HDB_F_DECRYPT
)) {
702 code
= hdb_unseal_keys (context
, db
, &entry
->entry
);
704 hdb_free_entry(context
, entry
);
710 static krb5_error_code
711 mdb_store(krb5_context context
, HDB
*db
, unsigned flags
, hdb_entry_ex
*entry
)
713 krb5_set_error_message(context
, EINVAL
, "can't set principal in mdb");
717 static krb5_error_code
718 mdb_remove(krb5_context context
, HDB
*db
, krb5_const_principal principal
)
720 krb5_error_code code
;
723 mdb_principal2key(context
, principal
, &key
);
724 code
= db
->hdb__del(context
, db
, key
);
725 krb5_data_free(&key
);
729 static krb5_error_code
730 mdb_open(krb5_context context
, HDB
*db
, int flags
, mode_t mode
)
735 asprintf(&fn
, "%s.db", db
->hdb_name
);
737 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
740 db
->hdb_db
= dbopen(fn
, flags
, mode
, DB_BTREE
, NULL
);
743 if (db
->hdb_db
== NULL
) {
749 db
->hdb_db
= dbopen(fn
, flags
, mode
, DB_BTREE
, NULL
);
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
) {
758 krb5_set_error_message(context
, ret
, "dbopen (%s): %s",
759 db
->hdb_name
, strerror(ret
));
762 if((flags
& O_ACCMODE
) == O_RDONLY
)
763 ret
= hdb_check_db_format(context
, db
);
765 ret
= hdb_init_db(context
, db
);
766 if(ret
== HDB_ERR_NOENTRY
) {
767 krb5_clear_error_message(context
);
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",
781 hdb_mdb_create(krb5_context context
, HDB
**db
,
782 const char *filename
)
784 *db
= calloc(1, sizeof(**db
));
786 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
790 (*db
)->hdb_db
= NULL
;
791 (*db
)->hdb_name
= strdup(filename
);
792 if ((*db
)->hdb_name
== NULL
) {
795 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
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
;
818 #endif /* HAVE_DB1 */