1 /*	$NetBSD: hdb-mitdb.c,v 1.1.1.2 2014/04/24 12:45:28 pettai Exp $	*/
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 
55 key: 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 
83 key_data_contents[0]
84 
85 	int16: length
86 	read-of-data: key-encrypted, key-usage 0, master-key
87 
88 salt:
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 
108 static krb5_error_code
mdb_principal2key(krb5_context context,krb5_const_principal principal,krb5_data * key)109 mdb_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 
132 static krb5_error_code
fix_salt(krb5_context context,hdb_entry * ent,int key_num)133 fix_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 	size_t 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 
202 static krb5_error_code
mdb_value2entry(krb5_context context,krb5_data * data,krb5_kvno kvno,hdb_entry * entry)203 mdb_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
469 static krb5_error_code
470 mdb_entry2value(krb5_context context, hdb_entry *entry, krb5_data *data)
471 {
472     return EINVAL;
473 }
474 #endif
475 
476 
477 static krb5_error_code
mdb_close(krb5_context context,HDB * db)478 mdb_close(krb5_context context, HDB *db)
479 {
480     DB *d = (DB*)db->hdb_db;
481     (*d->close)(d);
482     return 0;
483 }
484 
485 static krb5_error_code
mdb_destroy(krb5_context context,HDB * db)486 mdb_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 
496 static krb5_error_code
mdb_lock(krb5_context context,HDB * db,int operation)497 mdb_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 
509 static krb5_error_code
mdb_unlock(krb5_context context,HDB * db)510 mdb_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 
523 static krb5_error_code
mdb_seq(krb5_context context,HDB * db,unsigned flags,hdb_entry_ex * entry,int flag)524 mdb_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 
569 static krb5_error_code
mdb_firstkey(krb5_context context,HDB * db,unsigned flags,hdb_entry_ex * entry)570 mdb_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 
576 static krb5_error_code
mdb_nextkey(krb5_context context,HDB * db,unsigned flags,hdb_entry_ex * entry)577 mdb_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 
582 static krb5_error_code
mdb_rename(krb5_context context,HDB * db,const char * new_name)583 mdb_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 
601 static krb5_error_code
mdb__get(krb5_context context,HDB * db,krb5_data key,krb5_data * reply)602 mdb__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 
630 static krb5_error_code
mdb__put(krb5_context context,HDB * db,int replace,krb5_data key,krb5_data value)631 mdb__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 
660 static krb5_error_code
mdb__del(krb5_context context,HDB * db,krb5_data key)661 mdb__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 
684 static krb5_error_code
mdb_fetch_kvno(krb5_context context,HDB * db,krb5_const_principal principal,unsigned flags,krb5_kvno kvno,hdb_entry_ex * entry)685 mdb_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 
712 static krb5_error_code
mdb_store(krb5_context context,HDB * db,unsigned flags,hdb_entry_ex * entry)713 mdb_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 
719 static krb5_error_code
mdb_remove(krb5_context context,HDB * db,krb5_const_principal principal)720 mdb_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 
731 static krb5_error_code
mdb_open(krb5_context context,HDB * db,int flags,mode_t mode)732 mdb_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 
782 krb5_error_code
hdb_mdb_create(krb5_context context,HDB ** db,const char * filename)783 hdb_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