1 /* $NetBSD: hdb-mitdb.c,v 1.1.1.2 2014/04/24 12:45:28 pettai Exp $ */
4 * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
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.
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.
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
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
55 key: krb5_unparse_name + NUL
60 32: max renewable time
63 32: last successful passwd
64 32: last failed attempt
65 32: num of failed attempts
74 for num key data times
75 16: version (num keyblocks)
86 read-of-data: key-encrypted, key-usage 0, master-key
89 version2 = salt in key_data->key_data_contents[1]
96 #define KDB_V1_BASE_LENGTH 38
100 #if defined(HAVE_DB_185_H)
102 #elif defined(HAVE_DB_H)
106 #define CHECK(x) do { if ((x)) goto out; } while(0)
108 static krb5_error_code
109 mdb_principal2key(krb5_context context
,
110 krb5_const_principal principal
,
116 ret
= krb5_unparse_name(context
, principal
, &str
);
120 key
->length
= strlen(str
) + 1;
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
132 static krb5_error_code
133 fix_salt(krb5_context context
, hdb_entry
*ent
, int key_num
)
136 Salt
*salt
= ent
->keys
.val
[key_num
].salt
;
138 switch((int)salt
->type
) {
139 case KRB5_KDB_SALTTYPE_NORMAL
:
140 salt
->type
= KRB5_PADATA_PW_SALT
;
142 case KRB5_KDB_SALTTYPE_V4
:
143 krb5_data_free(&salt
->salt
);
144 salt
->type
= KRB5_PADATA_PW_SALT
;
146 case KRB5_KDB_SALTTYPE_NOREALM
:
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
);
159 for (i
= 0; i
< ent
->principal
->name
.name_string
.len
; ++i
) {
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
]);
166 salt
->type
= KRB5_PADATA_PW_SALT
;
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
));
176 salt
->type
= KRB5_PADATA_PW_SALT
;
178 case KRB5_KDB_SALTTYPE_SPECIAL
:
179 salt
->type
= KRB5_PADATA_PW_SALT
;
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
));
188 salt
->type
= KRB5_PADATA_AFS3_SALT
;
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
;
202 static krb5_error_code
203 mdb_value2entry(krb5_context context
, krb5_data
*data
, krb5_kvno kvno
, hdb_entry
*entry
)
208 uint16_t u16
, num_keys
, num_tl
;
212 sp
= krb5_storage_from_data(data
);
214 krb5_set_error_message(context
, ENOMEM
, "out of memory");
218 krb5_storage_set_byteorder(sp
, KRB5_STORAGE_BYTEORDER_LE
);
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.
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.
235 CHECK(ret
= krb5_ret_uint16(sp
, &u16
));
236 if (u16
!= KDB_V1_BASE_LENGTH
) { ret
= EINVAL
; goto out
; }
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
);
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 */
253 CHECK(ret
= krb5_ret_uint32(sp
, &u32
));
255 entry
->max_life
= malloc(sizeof(*entry
->max_life
));
256 *entry
->max_life
= u32
;
258 /* 32: max renewable time */
259 CHECK(ret
= krb5_ret_uint32(sp
, &u32
));
261 entry
->max_renew
= malloc(sizeof(*entry
->max_renew
));
262 *entry
->max_renew
= u32
;
264 /* 32: client expire */
265 CHECK(ret
= krb5_ret_uint32(sp
, &u32
));
267 entry
->valid_end
= malloc(sizeof(*entry
->valid_end
));
268 *entry
->valid_end
= u32
;
270 /* 32: passwd expire */
271 CHECK(ret
= krb5_ret_uint32(sp
, &u32
));
273 entry
->pw_end
= malloc(sizeof(*entry
->pw_end
));
274 *entry
->pw_end
= u32
;
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
));
285 /* 16: num key data */
286 CHECK(ret
= krb5_ret_uint16(sp
, &u16
));
288 /* 16: principal length */
289 CHECK(ret
= krb5_ret_uint16(sp
, &u16
));
290 /* length: principal */
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.
301 krb5_storage_read(sp
, p
, u16
);
303 CHECK(ret
= krb5_parse_name(context
, p
, &entry
->principal
));
306 /* for num tl data times
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
);
318 * for num key data times
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.
330 for (i
= 0; i
< num_keys
; i
++) {
335 CHECK(ret
= krb5_ret_uint16(sp
, &u16
));
337 CHECK(ret
= krb5_ret_uint16(sp
, &u16
));
340 * First time through, and until we find one matching key,
343 if ((entry
->kvno
< u16
) && (kvno
== 0 || kvno
== u16
)) {
347 * Found a higher kvno than earlier, so free the old highest
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.
354 for (j
= 0; j
< entry
->keys
.len
; j
++)
355 free_Key(&entry
->keys
.val
[j
]);
356 free(entry
->keys
.val
);
358 entry
->keys
.val
= NULL
;
359 } else if (entry
->kvno
== u16
)
360 /* Accumulate keys */
366 ptr
= realloc(entry
->keys
.val
, sizeof(entry
->keys
.val
[0]) * (entry
->keys
.len
+ 1));
371 entry
->keys
.val
= ptr
;
373 /* k points to current Key */
374 k
= &entry
->keys
.val
[entry
->keys
.len
];
376 memset(k
, 0, sizeof(*k
));
377 entry
->keys
.len
+= 1;
379 k
->mkvno
= malloc(sizeof(*k
->mkvno
));
380 if (k
->mkvno
== NULL
) {
386 for (j
= 0; j
< version
; j
++) {
388 CHECK(ret
= krb5_ret_uint16(sp
, &type
));
389 CHECK(ret
= krb5_ret_uint16(sp
, &u16
));
391 /* This "version" means we have a key */
392 k
->key
.keytype
= type
;
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.
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
);
412 /* This "version" means we have a salt */
413 k
->salt
= calloc(1, sizeof(*k
->salt
));
414 if (k
->salt
== NULL
) {
418 k
->salt
->type
= type
;
420 k
->salt
->salt
.data
= malloc(u16
);
421 if (k
->salt
->salt
.data
== NULL
) {
425 k
->salt
->salt
.length
= u16
;
426 krb5_storage_read(sp
, k
->salt
->salt
.data
, k
->salt
->salt
.length
);
428 fix_salt(context
, entry
, entry
->keys
.len
- 1);
431 * Whatever this "version" might be, we skip it
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.
437 krb5_storage_seek(sp
, u16
, SEEK_CUR
);
442 * XXX For now we skip older kvnos, but we should extract
445 for (j
= 0; j
< version
; j
++) {
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
);
455 if (entry
->kvno
== 0 && kvno
!= 0) {
456 ret
= HDB_ERR_NOT_FOUND_HERE
;
462 if (ret
== HEIM_ERR_EOF
)
463 /* Better error code than "end of file" */
464 ret
= HEIM_ERR_BAD_HDBENT_ENCODING
;
469 static krb5_error_code
470 mdb_entry2value(krb5_context context
, hdb_entry
*entry
, krb5_data
*data
)
477 static krb5_error_code
478 mdb_close(krb5_context context
, HDB
*db
)
480 DB
*d
= (DB
*)db
->hdb_db
;
485 static krb5_error_code
486 mdb_destroy(krb5_context context
, HDB
*db
)
490 ret
= hdb_clear_master_key (context
, db
);
496 static krb5_error_code
497 mdb_lock(krb5_context context
, HDB
*db
, int operation
)
499 DB
*d
= (DB
*)db
->hdb_db
;
500 int fd
= (*d
->fd
)(d
);
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
;
506 return hdb_lock(fd
, operation
);
509 static krb5_error_code
510 mdb_unlock(krb5_context context
, HDB
*db
)
512 DB
*d
= (DB
*)db
->hdb_db
;
513 int fd
= (*d
->fd
)(d
);
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
;
519 return hdb_unlock(fd
);
523 static krb5_error_code
524 mdb_seq(krb5_context context
, HDB
*db
,
525 unsigned flags
, hdb_entry_ex
*entry
, int flag
)
527 DB
*d
= (DB
*)db
->hdb_db
;
529 krb5_data key_data
, data
;
532 code
= db
->hdb_lock(context
, db
, HDB_RLOCK
);
534 krb5_set_error_message(context
, HDB_ERR_DB_INUSE
, "Database %s in use", db
->hdb_name
);
535 return HDB_ERR_DB_INUSE
;
537 code
= (*d
->seq
)(d
, &key
, &value
, flag
);
538 db
->hdb_unlock(context
, db
); /* XXX check value */
541 krb5_set_error_message(context
, code
, "Database %s seq error: %s",
542 db
->hdb_name
, strerror(code
));
546 krb5_clear_error_message(context
);
547 return HDB_ERR_NOENTRY
;
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
));
556 if (mdb_value2entry(context
, &data
, 0, &entry
->entry
))
557 return mdb_seq(context
, db
, flags
, entry
, R_NEXT
);
559 if (db
->hdb_master_key_set
&& (flags
& HDB_F_DECRYPT
)) {
560 code
= hdb_unseal_keys (context
, db
, &entry
->entry
);
562 hdb_free_entry (context
, entry
);
569 static krb5_error_code
570 mdb_firstkey(krb5_context context
, HDB
*db
, unsigned flags
, hdb_entry_ex
*entry
)
572 return mdb_seq(context
, db
, flags
, entry
, R_FIRST
);
576 static krb5_error_code
577 mdb_nextkey(krb5_context context
, HDB
*db
, unsigned flags
, hdb_entry_ex
*entry
)
579 return mdb_seq(context
, db
, flags
, entry
, R_NEXT
);
582 static krb5_error_code
583 mdb_rename(krb5_context context
, HDB
*db
, const char *new_name
)
588 asprintf(&old
, "%s.db", db
->hdb_name
);
589 asprintf(&new, "%s.db", new_name
);
590 ret
= rename(old
, new);
597 db
->hdb_name
= strdup(new_name
);
601 static krb5_error_code
602 mdb__get(krb5_context context
, HDB
*db
, krb5_data key
, krb5_data
*reply
)
604 DB
*d
= (DB
*)db
->hdb_db
;
610 code
= db
->hdb_lock(context
, db
, HDB_RLOCK
);
613 code
= (*d
->get
)(d
, &k
, &v
, 0);
614 db
->hdb_unlock(context
, db
);
617 krb5_set_error_message(context
, code
, "Database %s get error: %s",
618 db
->hdb_name
, strerror(code
));
622 krb5_clear_error_message(context
);
623 return HDB_ERR_NOENTRY
;
626 krb5_data_copy(reply
, v
.data
, v
.size
);
630 static krb5_error_code
631 mdb__put(krb5_context context
, HDB
*db
, int replace
,
632 krb5_data key
, krb5_data value
)
634 DB
*d
= (DB
*)db
->hdb_db
;
641 v
.size
= value
.length
;
642 code
= db
->hdb_lock(context
, db
, HDB_WLOCK
);
645 code
= (*d
->put
)(d
, &k
, &v
, replace
? 0 : R_NOOVERWRITE
);
646 db
->hdb_unlock(context
, db
);
649 krb5_set_error_message(context
, code
, "Database %s put error: %s",
650 db
->hdb_name
, strerror(code
));
654 krb5_clear_error_message(context
);
655 return HDB_ERR_EXISTS
;
660 static krb5_error_code
661 mdb__del(krb5_context context
, HDB
*db
, krb5_data key
)
663 DB
*d
= (DB
*)db
->hdb_db
;
665 krb5_error_code code
;
668 code
= db
->hdb_lock(context
, db
, HDB_WLOCK
);
671 code
= (*d
->del
)(d
, &k
, 0);
672 db
->hdb_unlock(context
, db
);
675 krb5_set_error_message(context
, code
, "Database %s put error: %s",
676 db
->hdb_name
, strerror(code
));
684 static krb5_error_code
685 mdb_fetch_kvno(krb5_context context
, HDB
*db
, krb5_const_principal principal
,
686 unsigned flags
, krb5_kvno kvno
, hdb_entry_ex
*entry
)
688 krb5_data key
, value
;
689 krb5_error_code code
;
691 code
= mdb_principal2key(context
, principal
, &key
);
694 code
= db
->hdb__get(context
, db
, key
, &value
);
695 krb5_data_free(&key
);
698 code
= mdb_value2entry(context
, &value
, kvno
, &entry
->entry
);
699 krb5_data_free(&value
);
703 if (db
->hdb_master_key_set
&& (flags
& HDB_F_DECRYPT
)) {
704 code
= hdb_unseal_keys (context
, db
, &entry
->entry
);
706 hdb_free_entry(context
, entry
);
712 static krb5_error_code
713 mdb_store(krb5_context context
, HDB
*db
, unsigned flags
, hdb_entry_ex
*entry
)
715 krb5_set_error_message(context
, EINVAL
, "can't set principal in mdb");
719 static krb5_error_code
720 mdb_remove(krb5_context context
, HDB
*db
, krb5_const_principal principal
)
722 krb5_error_code code
;
725 mdb_principal2key(context
, principal
, &key
);
726 code
= db
->hdb__del(context
, db
, key
);
727 krb5_data_free(&key
);
731 static krb5_error_code
732 mdb_open(krb5_context context
, HDB
*db
, int flags
, mode_t mode
)
737 asprintf(&fn
, "%s.db", db
->hdb_name
);
739 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
742 db
->hdb_db
= dbopen(fn
, flags
, mode
, DB_BTREE
, NULL
);
745 if (db
->hdb_db
== NULL
) {
751 db
->hdb_db
= dbopen(fn
, flags
, mode
, DB_BTREE
, NULL
);
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
) {
760 krb5_set_error_message(context
, ret
, "dbopen (%s): %s",
761 db
->hdb_name
, strerror(ret
));
764 if((flags
& O_ACCMODE
) == O_RDONLY
)
765 ret
= hdb_check_db_format(context
, db
);
767 ret
= hdb_init_db(context
, db
);
768 if(ret
== HDB_ERR_NOENTRY
) {
769 krb5_clear_error_message(context
);
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",
783 hdb_mdb_create(krb5_context context
, HDB
**db
,
784 const char *filename
)
786 *db
= calloc(1, sizeof(**db
));
788 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
792 (*db
)->hdb_db
= NULL
;
793 (*db
)->hdb_name
= strdup(filename
);
794 if ((*db
)->hdb_name
== NULL
) {
797 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
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
;
820 #endif /* HAVE_DB1 */