xref: /freebsd/crypto/heimdal/lib/hdb/hdb-mitdb.c (revision ed549cb0c53f8438c52593ce811f6fcc812248e9)
1*ae771770SStanislav Sedov /*
2*ae771770SStanislav Sedov  * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
3*ae771770SStanislav Sedov  * (Royal Institute of Technology, Stockholm, Sweden).
4*ae771770SStanislav Sedov  * All rights reserved.
5*ae771770SStanislav Sedov  *
6*ae771770SStanislav Sedov  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7*ae771770SStanislav Sedov  *
8*ae771770SStanislav Sedov  * Redistribution and use in source and binary forms, with or without
9*ae771770SStanislav Sedov  * modification, are permitted provided that the following conditions
10*ae771770SStanislav Sedov  * are met:
11*ae771770SStanislav Sedov  *
12*ae771770SStanislav Sedov  * 1. Redistributions of source code must retain the above copyright
13*ae771770SStanislav Sedov  *    notice, this list of conditions and the following disclaimer.
14*ae771770SStanislav Sedov  *
15*ae771770SStanislav Sedov  * 2. Redistributions in binary form must reproduce the above copyright
16*ae771770SStanislav Sedov  *    notice, this list of conditions and the following disclaimer in the
17*ae771770SStanislav Sedov  *    documentation and/or other materials provided with the distribution.
18*ae771770SStanislav Sedov  *
19*ae771770SStanislav Sedov  * 3. Neither the name of the Institute nor the names of its contributors
20*ae771770SStanislav Sedov  *    may be used to endorse or promote products derived from this software
21*ae771770SStanislav Sedov  *    without specific prior written permission.
22*ae771770SStanislav Sedov  *
23*ae771770SStanislav Sedov  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24*ae771770SStanislav Sedov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25*ae771770SStanislav Sedov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26*ae771770SStanislav Sedov  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27*ae771770SStanislav Sedov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28*ae771770SStanislav Sedov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29*ae771770SStanislav Sedov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30*ae771770SStanislav Sedov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31*ae771770SStanislav Sedov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32*ae771770SStanislav Sedov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33*ae771770SStanislav Sedov  * SUCH DAMAGE.
34*ae771770SStanislav Sedov  */
35*ae771770SStanislav Sedov 
36*ae771770SStanislav Sedov #define KRB5_KDB_DISALLOW_POSTDATED	0x00000001
37*ae771770SStanislav Sedov #define KRB5_KDB_DISALLOW_FORWARDABLE	0x00000002
38*ae771770SStanislav Sedov #define KRB5_KDB_DISALLOW_TGT_BASED	0x00000004
39*ae771770SStanislav Sedov #define KRB5_KDB_DISALLOW_RENEWABLE	0x00000008
40*ae771770SStanislav Sedov #define KRB5_KDB_DISALLOW_PROXIABLE	0x00000010
41*ae771770SStanislav Sedov #define KRB5_KDB_DISALLOW_DUP_SKEY	0x00000020
42*ae771770SStanislav Sedov #define KRB5_KDB_DISALLOW_ALL_TIX	0x00000040
43*ae771770SStanislav Sedov #define KRB5_KDB_REQUIRES_PRE_AUTH	0x00000080
44*ae771770SStanislav Sedov #define KRB5_KDB_REQUIRES_HW_AUTH	0x00000100
45*ae771770SStanislav Sedov #define KRB5_KDB_REQUIRES_PWCHANGE	0x00000200
46*ae771770SStanislav Sedov #define KRB5_KDB_DISALLOW_SVR		0x00001000
47*ae771770SStanislav Sedov #define KRB5_KDB_PWCHANGE_SERVICE	0x00002000
48*ae771770SStanislav Sedov #define KRB5_KDB_SUPPORT_DESMD5		0x00004000
49*ae771770SStanislav Sedov #define KRB5_KDB_NEW_PRINC		0x00008000
50*ae771770SStanislav Sedov 
51*ae771770SStanislav Sedov /*
52*ae771770SStanislav Sedov 
53*ae771770SStanislav Sedov key: krb5_unparse_name  + NUL
54*ae771770SStanislav Sedov 
55*ae771770SStanislav Sedov  16: baselength
56*ae771770SStanislav Sedov  32: attributes
57*ae771770SStanislav Sedov  32: max time
58*ae771770SStanislav Sedov  32: max renewable time
59*ae771770SStanislav Sedov  32: client expire
60*ae771770SStanislav Sedov  32: passwd expire
61*ae771770SStanislav Sedov  32: last successful passwd
62*ae771770SStanislav Sedov  32: last failed attempt
63*ae771770SStanislav Sedov  32: num of failed attempts
64*ae771770SStanislav Sedov  16: num tl data
65*ae771770SStanislav Sedov  16: num data data
66*ae771770SStanislav Sedov  16: principal length
67*ae771770SStanislav Sedov  length: principal
68*ae771770SStanislav Sedov  for num tl data times
69*ae771770SStanislav Sedov     16: tl data type
70*ae771770SStanislav Sedov     16: tl data length
71*ae771770SStanislav Sedov     length: length
72*ae771770SStanislav Sedov  for num key data times
73*ae771770SStanislav Sedov     16: version (num keyblocks)
74*ae771770SStanislav Sedov     16: kvno
75*ae771770SStanislav Sedov     for version times:
76*ae771770SStanislav Sedov         16: type
77*ae771770SStanislav Sedov         16: length
78*ae771770SStanislav Sedov         length: keydata
79*ae771770SStanislav Sedov 
80*ae771770SStanislav Sedov 
81*ae771770SStanislav Sedov key_data_contents[0]
82*ae771770SStanislav Sedov 
83*ae771770SStanislav Sedov 	int16: length
84*ae771770SStanislav Sedov 	read-of-data: key-encrypted, key-usage 0, master-key
85*ae771770SStanislav Sedov 
86*ae771770SStanislav Sedov salt:
87*ae771770SStanislav Sedov     version2 = salt in key_data->key_data_contents[1]
88*ae771770SStanislav Sedov     else default salt.
89*ae771770SStanislav Sedov 
90*ae771770SStanislav Sedov */
91*ae771770SStanislav Sedov 
92*ae771770SStanislav Sedov #include "hdb_locl.h"
93*ae771770SStanislav Sedov 
94*ae771770SStanislav Sedov #define KDB_V1_BASE_LENGTH 38
95*ae771770SStanislav Sedov 
96*ae771770SStanislav Sedov #if HAVE_DB1
97*ae771770SStanislav Sedov 
98*ae771770SStanislav Sedov #if defined(HAVE_DB_185_H)
99*ae771770SStanislav Sedov #include <db_185.h>
100*ae771770SStanislav Sedov #elif defined(HAVE_DB_H)
101*ae771770SStanislav Sedov #include <db.h>
102*ae771770SStanislav Sedov #endif
103*ae771770SStanislav Sedov 
104*ae771770SStanislav Sedov #define CHECK(x) do { if ((x)) goto out; } while(0)
105*ae771770SStanislav Sedov 
106*ae771770SStanislav Sedov static krb5_error_code
mdb_principal2key(krb5_context context,krb5_const_principal principal,krb5_data * key)107*ae771770SStanislav Sedov mdb_principal2key(krb5_context context,
108*ae771770SStanislav Sedov 		  krb5_const_principal principal,
109*ae771770SStanislav Sedov 		  krb5_data *key)
110*ae771770SStanislav Sedov {
111*ae771770SStanislav Sedov     krb5_error_code ret;
112*ae771770SStanislav Sedov     char *str;
113*ae771770SStanislav Sedov 
114*ae771770SStanislav Sedov     ret = krb5_unparse_name(context, principal, &str);
115*ae771770SStanislav Sedov     if (ret)
116*ae771770SStanislav Sedov 	return ret;
117*ae771770SStanislav Sedov     key->data = str;
118*ae771770SStanislav Sedov     key->length = strlen(str) + 1;
119*ae771770SStanislav Sedov     return 0;
120*ae771770SStanislav Sedov }
121*ae771770SStanislav Sedov 
122*ae771770SStanislav Sedov #define KRB5_KDB_SALTTYPE_NORMAL	0
123*ae771770SStanislav Sedov #define KRB5_KDB_SALTTYPE_V4		1
124*ae771770SStanislav Sedov #define KRB5_KDB_SALTTYPE_NOREALM	2
125*ae771770SStanislav Sedov #define KRB5_KDB_SALTTYPE_ONLYREALM	3
126*ae771770SStanislav Sedov #define KRB5_KDB_SALTTYPE_SPECIAL	4
127*ae771770SStanislav Sedov #define KRB5_KDB_SALTTYPE_AFS3		5
128*ae771770SStanislav Sedov #define KRB5_KDB_SALTTYPE_CERTHASH	6
129*ae771770SStanislav Sedov 
130*ae771770SStanislav Sedov static krb5_error_code
fix_salt(krb5_context context,hdb_entry * ent,int key_num)131*ae771770SStanislav Sedov fix_salt(krb5_context context, hdb_entry *ent, int key_num)
132*ae771770SStanislav Sedov {
133*ae771770SStanislav Sedov     krb5_error_code ret;
134*ae771770SStanislav Sedov     Salt *salt = ent->keys.val[key_num].salt;
135*ae771770SStanislav Sedov     /* fix salt type */
136*ae771770SStanislav Sedov     switch((int)salt->type) {
137*ae771770SStanislav Sedov     case KRB5_KDB_SALTTYPE_NORMAL:
138*ae771770SStanislav Sedov 	salt->type = KRB5_PADATA_PW_SALT;
139*ae771770SStanislav Sedov 	break;
140*ae771770SStanislav Sedov     case KRB5_KDB_SALTTYPE_V4:
141*ae771770SStanislav Sedov 	krb5_data_free(&salt->salt);
142*ae771770SStanislav Sedov 	salt->type = KRB5_PADATA_PW_SALT;
143*ae771770SStanislav Sedov 	break;
144*ae771770SStanislav Sedov     case KRB5_KDB_SALTTYPE_NOREALM:
145*ae771770SStanislav Sedov     {
146*ae771770SStanislav Sedov 	size_t len;
147*ae771770SStanislav Sedov 	size_t i;
148*ae771770SStanislav Sedov 	char *p;
149*ae771770SStanislav Sedov 
150*ae771770SStanislav Sedov 	len = 0;
151*ae771770SStanislav Sedov 	for (i = 0; i < ent->principal->name.name_string.len; ++i)
152*ae771770SStanislav Sedov 	    len += strlen(ent->principal->name.name_string.val[i]);
153*ae771770SStanislav Sedov 	ret = krb5_data_alloc (&salt->salt, len);
154*ae771770SStanislav Sedov 	if (ret)
155*ae771770SStanislav Sedov 	    return ret;
156*ae771770SStanislav Sedov 	p = salt->salt.data;
157*ae771770SStanislav Sedov 	for (i = 0; i < ent->principal->name.name_string.len; ++i) {
158*ae771770SStanislav Sedov 	    memcpy (p,
159*ae771770SStanislav Sedov 		    ent->principal->name.name_string.val[i],
160*ae771770SStanislav Sedov 		    strlen(ent->principal->name.name_string.val[i]));
161*ae771770SStanislav Sedov 	    p += strlen(ent->principal->name.name_string.val[i]);
162*ae771770SStanislav Sedov 	}
163*ae771770SStanislav Sedov 
164*ae771770SStanislav Sedov 	salt->type = KRB5_PADATA_PW_SALT;
165*ae771770SStanislav Sedov 	break;
166*ae771770SStanislav Sedov     }
167*ae771770SStanislav Sedov     case KRB5_KDB_SALTTYPE_ONLYREALM:
168*ae771770SStanislav Sedov 	krb5_data_free(&salt->salt);
169*ae771770SStanislav Sedov 	ret = krb5_data_copy(&salt->salt,
170*ae771770SStanislav Sedov 			     ent->principal->realm,
171*ae771770SStanislav Sedov 			     strlen(ent->principal->realm));
172*ae771770SStanislav Sedov 	if(ret)
173*ae771770SStanislav Sedov 	    return ret;
174*ae771770SStanislav Sedov 	salt->type = KRB5_PADATA_PW_SALT;
175*ae771770SStanislav Sedov 	break;
176*ae771770SStanislav Sedov     case KRB5_KDB_SALTTYPE_SPECIAL:
177*ae771770SStanislav Sedov 	salt->type = KRB5_PADATA_PW_SALT;
178*ae771770SStanislav Sedov 	break;
179*ae771770SStanislav Sedov     case KRB5_KDB_SALTTYPE_AFS3:
180*ae771770SStanislav Sedov 	krb5_data_free(&salt->salt);
181*ae771770SStanislav Sedov 	ret = krb5_data_copy(&salt->salt,
182*ae771770SStanislav Sedov 		       ent->principal->realm,
183*ae771770SStanislav Sedov 		       strlen(ent->principal->realm));
184*ae771770SStanislav Sedov 	if(ret)
185*ae771770SStanislav Sedov 	    return ret;
186*ae771770SStanislav Sedov 	salt->type = KRB5_PADATA_AFS3_SALT;
187*ae771770SStanislav Sedov 	break;
188*ae771770SStanislav Sedov     case KRB5_KDB_SALTTYPE_CERTHASH:
189*ae771770SStanislav Sedov 	krb5_data_free(&salt->salt);
190*ae771770SStanislav Sedov 	free(ent->keys.val[key_num].salt);
191*ae771770SStanislav Sedov 	ent->keys.val[key_num].salt = NULL;
192*ae771770SStanislav Sedov 	break;
193*ae771770SStanislav Sedov     default:
194*ae771770SStanislav Sedov 	abort();
195*ae771770SStanislav Sedov     }
196*ae771770SStanislav Sedov     return 0;
197*ae771770SStanislav Sedov }
198*ae771770SStanislav Sedov 
199*ae771770SStanislav Sedov 
200*ae771770SStanislav Sedov static krb5_error_code
mdb_value2entry(krb5_context context,krb5_data * data,krb5_kvno kvno,hdb_entry * entry)201*ae771770SStanislav Sedov mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry *entry)
202*ae771770SStanislav Sedov {
203*ae771770SStanislav Sedov     krb5_error_code ret;
204*ae771770SStanislav Sedov     krb5_storage *sp;
205*ae771770SStanislav Sedov     uint32_t u32;
206*ae771770SStanislav Sedov     uint16_t u16, num_keys, num_tl;
207*ae771770SStanislav Sedov     size_t i, j;
208*ae771770SStanislav Sedov     char *p;
209*ae771770SStanislav Sedov 
210*ae771770SStanislav Sedov     sp = krb5_storage_from_data(data);
211*ae771770SStanislav Sedov     if (sp == NULL) {
212*ae771770SStanislav Sedov 	krb5_set_error_message(context, ENOMEM, "out of memory");
213*ae771770SStanislav Sedov 	return ENOMEM;
214*ae771770SStanislav Sedov     }
215*ae771770SStanislav Sedov 
216*ae771770SStanislav Sedov     krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
217*ae771770SStanislav Sedov 
218*ae771770SStanislav Sedov     /*
219*ae771770SStanislav Sedov      * 16: baselength
220*ae771770SStanislav Sedov      *
221*ae771770SStanislav Sedov      * The story here is that these 16 bits have to be a constant:
222*ae771770SStanislav Sedov      * KDB_V1_BASE_LENGTH.  Once upon a time a different value here
223*ae771770SStanislav Sedov      * would have been used to indicate the presence of "extra data"
224*ae771770SStanislav Sedov      * between the "base" contents and the {principal name, TL data,
225*ae771770SStanislav Sedov      * keys} that follow it.  Nothing supports such "extra data"
226*ae771770SStanislav Sedov      * nowadays, so neither do we here.
227*ae771770SStanislav Sedov      *
228*ae771770SStanislav Sedov      * XXX But... surely we ought to log about this extra data, or skip
229*ae771770SStanislav Sedov      * it, or something, in case anyone has MIT KDBs with ancient
230*ae771770SStanislav Sedov      * entries in them...  Logging would allow the admin to know which
231*ae771770SStanislav Sedov      * entries to dump with MIT krb5's kdb5_util.
232*ae771770SStanislav Sedov      */
233*ae771770SStanislav Sedov     CHECK(ret = krb5_ret_uint16(sp, &u16));
234*ae771770SStanislav Sedov     if (u16 != KDB_V1_BASE_LENGTH) { ret = EINVAL; goto out; }
235*ae771770SStanislav Sedov     /* 32: attributes */
236*ae771770SStanislav Sedov     CHECK(ret = krb5_ret_uint32(sp, &u32));
237*ae771770SStanislav Sedov     entry->flags.postdate =	 !(u32 & KRB5_KDB_DISALLOW_POSTDATED);
238*ae771770SStanislav Sedov     entry->flags.forwardable =	 !(u32 & KRB5_KDB_DISALLOW_FORWARDABLE);
239*ae771770SStanislav Sedov     entry->flags.initial =	!!(u32 & KRB5_KDB_DISALLOW_TGT_BASED);
240*ae771770SStanislav Sedov     entry->flags.renewable =	 !(u32 & KRB5_KDB_DISALLOW_RENEWABLE);
241*ae771770SStanislav Sedov     entry->flags.proxiable =	 !(u32 & KRB5_KDB_DISALLOW_PROXIABLE);
242*ae771770SStanislav Sedov     /* DUP_SKEY */
243*ae771770SStanislav Sedov     entry->flags.invalid =	!!(u32 & KRB5_KDB_DISALLOW_ALL_TIX);
244*ae771770SStanislav Sedov     entry->flags.require_preauth =!!(u32 & KRB5_KDB_REQUIRES_PRE_AUTH);
245*ae771770SStanislav Sedov     entry->flags.require_hwauth =!!(u32 & KRB5_KDB_REQUIRES_HW_AUTH);
246*ae771770SStanislav Sedov     entry->flags.server =	 !(u32 & KRB5_KDB_DISALLOW_SVR);
247*ae771770SStanislav Sedov     entry->flags.change_pw = 	!!(u32 & KRB5_KDB_PWCHANGE_SERVICE);
248*ae771770SStanislav Sedov     entry->flags.client =	   1; /* XXX */
249*ae771770SStanislav Sedov 
250*ae771770SStanislav Sedov     /* 32: max time */
251*ae771770SStanislav Sedov     CHECK(ret = krb5_ret_uint32(sp, &u32));
252*ae771770SStanislav Sedov     if (u32) {
253*ae771770SStanislav Sedov 	entry->max_life = malloc(sizeof(*entry->max_life));
254*ae771770SStanislav Sedov 	*entry->max_life = u32;
255*ae771770SStanislav Sedov     }
256*ae771770SStanislav Sedov     /* 32: max renewable time */
257*ae771770SStanislav Sedov     CHECK(ret = krb5_ret_uint32(sp, &u32));
258*ae771770SStanislav Sedov     if (u32) {
259*ae771770SStanislav Sedov 	entry->max_renew = malloc(sizeof(*entry->max_renew));
260*ae771770SStanislav Sedov 	*entry->max_renew = u32;
261*ae771770SStanislav Sedov     }
262*ae771770SStanislav Sedov     /* 32: client expire */
263*ae771770SStanislav Sedov     CHECK(ret = krb5_ret_uint32(sp, &u32));
264*ae771770SStanislav Sedov     if (u32) {
265*ae771770SStanislav Sedov 	entry->valid_end = malloc(sizeof(*entry->valid_end));
266*ae771770SStanislav Sedov 	*entry->valid_end = u32;
267*ae771770SStanislav Sedov     }
268*ae771770SStanislav Sedov     /* 32: passwd expire */
269*ae771770SStanislav Sedov     CHECK(ret = krb5_ret_uint32(sp, &u32));
270*ae771770SStanislav Sedov     if (u32) {
271*ae771770SStanislav Sedov 	entry->pw_end = malloc(sizeof(*entry->pw_end));
272*ae771770SStanislav Sedov 	*entry->pw_end = u32;
273*ae771770SStanislav Sedov     }
274*ae771770SStanislav Sedov     /* 32: last successful passwd */
275*ae771770SStanislav Sedov     CHECK(ret = krb5_ret_uint32(sp, &u32));
276*ae771770SStanislav Sedov     /* 32: last failed attempt */
277*ae771770SStanislav Sedov     CHECK(ret = krb5_ret_uint32(sp, &u32));
278*ae771770SStanislav Sedov     /* 32: num of failed attempts */
279*ae771770SStanislav Sedov     CHECK(ret = krb5_ret_uint32(sp, &u32));
280*ae771770SStanislav Sedov     /* 16: num tl data */
281*ae771770SStanislav Sedov     CHECK(ret = krb5_ret_uint16(sp, &u16));
282*ae771770SStanislav Sedov     num_tl = u16;
283*ae771770SStanislav Sedov     /* 16: num key data */
284*ae771770SStanislav Sedov     CHECK(ret = krb5_ret_uint16(sp, &u16));
285*ae771770SStanislav Sedov     num_keys = u16;
286*ae771770SStanislav Sedov     /* 16: principal length */
287*ae771770SStanislav Sedov     CHECK(ret = krb5_ret_uint16(sp, &u16));
288*ae771770SStanislav Sedov     /* length: principal */
289*ae771770SStanislav Sedov     {
290*ae771770SStanislav Sedov 	/*
291*ae771770SStanislav Sedov 	 * Note that the principal name includes the NUL in the entry,
292*ae771770SStanislav Sedov 	 * but we don't want to take chances, so we add an extra NUL.
293*ae771770SStanislav Sedov 	 */
294*ae771770SStanislav Sedov 	p = malloc(u16 + 1);
295*ae771770SStanislav Sedov 	if (p == NULL) {
296*ae771770SStanislav Sedov 	    ret = ENOMEM;
297*ae771770SStanislav Sedov 	    goto out;
298*ae771770SStanislav Sedov 	}
299*ae771770SStanislav Sedov 	krb5_storage_read(sp, p, u16);
300*ae771770SStanislav Sedov 	p[u16] = '\0';
301*ae771770SStanislav Sedov 	CHECK(ret = krb5_parse_name(context, p, &entry->principal));
302*ae771770SStanislav Sedov 	free(p);
303*ae771770SStanislav Sedov     }
304*ae771770SStanislav Sedov     /* for num tl data times
305*ae771770SStanislav Sedov            16: tl data type
306*ae771770SStanislav Sedov            16: tl data length
307*ae771770SStanislav Sedov            length: length */
308*ae771770SStanislav Sedov     for (i = 0; i < num_tl; i++) {
309*ae771770SStanislav Sedov 	/* 16: TL data type */
310*ae771770SStanislav Sedov 	CHECK(ret = krb5_ret_uint16(sp, &u16));
311*ae771770SStanislav Sedov 	/* 16: TL data length */
312*ae771770SStanislav Sedov 	CHECK(ret = krb5_ret_uint16(sp, &u16));
313*ae771770SStanislav Sedov 	krb5_storage_seek(sp, u16, SEEK_CUR);
314*ae771770SStanislav Sedov     }
315*ae771770SStanislav Sedov     /*
316*ae771770SStanislav Sedov      * for num key data times
317*ae771770SStanislav Sedov      * 16: "version"
318*ae771770SStanislav Sedov      * 16: kvno
319*ae771770SStanislav Sedov      * for version times:
320*ae771770SStanislav Sedov      *     16: type
321*ae771770SStanislav Sedov      *     16: length
322*ae771770SStanislav Sedov      *     length: keydata
323*ae771770SStanislav Sedov      *
324*ae771770SStanislav Sedov      * "version" here is really 1 or 2, the first meaning there's only
325*ae771770SStanislav Sedov      * keys for this kvno, the second meaning there's keys and salt[s?].
326*ae771770SStanislav Sedov      * That's right... hold that gag reflex, you can do it.
327*ae771770SStanislav Sedov      */
328*ae771770SStanislav Sedov     for (i = 0; i < num_keys; i++) {
329*ae771770SStanislav Sedov 	int keep = 0;
330*ae771770SStanislav Sedov 	uint16_t version;
331*ae771770SStanislav Sedov 	void *ptr;
332*ae771770SStanislav Sedov 
333*ae771770SStanislav Sedov 	CHECK(ret = krb5_ret_uint16(sp, &u16));
334*ae771770SStanislav Sedov 	version = u16;
335*ae771770SStanislav Sedov 	CHECK(ret = krb5_ret_uint16(sp, &u16));
336*ae771770SStanislav Sedov 
337*ae771770SStanislav Sedov 	/*
338*ae771770SStanislav Sedov 	 * First time through, and until we find one matching key,
339*ae771770SStanislav Sedov 	 * entry->kvno == 0.
340*ae771770SStanislav Sedov 	 */
341*ae771770SStanislav Sedov 	if ((entry->kvno < u16) && (kvno == 0 || kvno == u16)) {
342*ae771770SStanislav Sedov 	    keep = 1;
343*ae771770SStanislav Sedov 	    entry->kvno = u16;
344*ae771770SStanislav Sedov 	    /*
345*ae771770SStanislav Sedov 	     * Found a higher kvno than earlier, so free the old highest
346*ae771770SStanislav Sedov 	     * kvno keys.
347*ae771770SStanislav Sedov 	     *
348*ae771770SStanislav Sedov 	     * XXX Of course, we actually want to extract the old kvnos
349*ae771770SStanislav Sedov 	     * as well, for some of the kadm5 APIs.  We shouldn't free
350*ae771770SStanislav Sedov 	     * these keys, but keep them elsewhere.
351*ae771770SStanislav Sedov 	     */
352*ae771770SStanislav Sedov 	    for (j = 0; j < entry->keys.len; j++)
353*ae771770SStanislav Sedov 		free_Key(&entry->keys.val[j]);
354*ae771770SStanislav Sedov 	    free(entry->keys.val);
355*ae771770SStanislav Sedov 	    entry->keys.len = 0;
356*ae771770SStanislav Sedov 	    entry->keys.val = NULL;
357*ae771770SStanislav Sedov 	} else if (entry->kvno == u16)
358*ae771770SStanislav Sedov 	    /* Accumulate keys */
359*ae771770SStanislav Sedov 	    keep = 1;
360*ae771770SStanislav Sedov 
361*ae771770SStanislav Sedov 	if (keep) {
362*ae771770SStanislav Sedov 	    Key *k;
363*ae771770SStanislav Sedov 
364*ae771770SStanislav Sedov 	    ptr = realloc(entry->keys.val, sizeof(entry->keys.val[0]) * (entry->keys.len + 1));
365*ae771770SStanislav Sedov 	    if (ptr == NULL) {
366*ae771770SStanislav Sedov 		ret = ENOMEM;
367*ae771770SStanislav Sedov 		goto out;
368*ae771770SStanislav Sedov 	    }
369*ae771770SStanislav Sedov 	    entry->keys.val = ptr;
370*ae771770SStanislav Sedov 
371*ae771770SStanislav Sedov 	    /* k points to current Key */
372*ae771770SStanislav Sedov 	    k = &entry->keys.val[entry->keys.len];
373*ae771770SStanislav Sedov 
374*ae771770SStanislav Sedov 	    memset(k, 0, sizeof(*k));
375*ae771770SStanislav Sedov 	    entry->keys.len += 1;
376*ae771770SStanislav Sedov 
377*ae771770SStanislav Sedov 	    k->mkvno = malloc(sizeof(*k->mkvno));
378*ae771770SStanislav Sedov 	    if (k->mkvno == NULL) {
379*ae771770SStanislav Sedov 		ret = ENOMEM;
380*ae771770SStanislav Sedov 		goto out;
381*ae771770SStanislav Sedov 	    }
382*ae771770SStanislav Sedov 	    *k->mkvno = 1;
383*ae771770SStanislav Sedov 
384*ae771770SStanislav Sedov 	    for (j = 0; j < version; j++) {
385*ae771770SStanislav Sedov 		uint16_t type;
386*ae771770SStanislav Sedov 		CHECK(ret = krb5_ret_uint16(sp, &type));
387*ae771770SStanislav Sedov 		CHECK(ret = krb5_ret_uint16(sp, &u16));
388*ae771770SStanislav Sedov 		if (j == 0) {
389*ae771770SStanislav Sedov 		    /* This "version" means we have a key */
390*ae771770SStanislav Sedov 		    k->key.keytype = type;
391*ae771770SStanislav Sedov 		    if (u16 < 2) {
392*ae771770SStanislav Sedov 			ret = EINVAL;
393*ae771770SStanislav Sedov 			goto out;
394*ae771770SStanislav Sedov 		    }
395*ae771770SStanislav Sedov 		    /*
396*ae771770SStanislav Sedov 		     * MIT stores keys encrypted keys as {16-bit length
397*ae771770SStanislav Sedov 		     * of plaintext key, {encrypted key}}.  The reason
398*ae771770SStanislav Sedov 		     * for this is that the Kerberos cryptosystem is not
399*ae771770SStanislav Sedov 		     * length-preserving.  Heimdal's approach is to
400*ae771770SStanislav Sedov 		     * truncate the plaintext to the expected length of
401*ae771770SStanislav Sedov 		     * the key given its enctype, so we ignore this
402*ae771770SStanislav Sedov 		     * 16-bit length-of-plaintext-key field.
403*ae771770SStanislav Sedov 		     */
404*ae771770SStanislav Sedov 		    krb5_storage_seek(sp, 2, SEEK_CUR); /* skip real length */
405*ae771770SStanislav Sedov 		    k->key.keyvalue.length = u16 - 2;   /* adjust cipher len */
406*ae771770SStanislav Sedov 		    k->key.keyvalue.data = malloc(k->key.keyvalue.length);
407*ae771770SStanislav Sedov 		    krb5_storage_read(sp, k->key.keyvalue.data,
408*ae771770SStanislav Sedov 				      k->key.keyvalue.length);
409*ae771770SStanislav Sedov 		} else if (j == 1) {
410*ae771770SStanislav Sedov 		    /* This "version" means we have a salt */
411*ae771770SStanislav Sedov 		    k->salt = calloc(1, sizeof(*k->salt));
412*ae771770SStanislav Sedov 		    if (k->salt == NULL) {
413*ae771770SStanislav Sedov 			ret = ENOMEM;
414*ae771770SStanislav Sedov 			goto out;
415*ae771770SStanislav Sedov 		    }
416*ae771770SStanislav Sedov 		    k->salt->type = type;
417*ae771770SStanislav Sedov 		    if (u16 != 0) {
418*ae771770SStanislav Sedov 			k->salt->salt.data = malloc(u16);
419*ae771770SStanislav Sedov 			if (k->salt->salt.data == NULL) {
420*ae771770SStanislav Sedov 			    ret = ENOMEM;
421*ae771770SStanislav Sedov 			    goto out;
422*ae771770SStanislav Sedov 			}
423*ae771770SStanislav Sedov 			k->salt->salt.length = u16;
424*ae771770SStanislav Sedov 			krb5_storage_read(sp, k->salt->salt.data, k->salt->salt.length);
425*ae771770SStanislav Sedov 		    }
426*ae771770SStanislav Sedov 		    fix_salt(context, entry, entry->keys.len - 1);
427*ae771770SStanislav Sedov 		} else {
428*ae771770SStanislav Sedov 		    /*
429*ae771770SStanislav Sedov 		     * Whatever this "version" might be, we skip it
430*ae771770SStanislav Sedov 		     *
431*ae771770SStanislav Sedov 		     * XXX A krb5.conf parameter requesting that we log
432*ae771770SStanislav Sedov 		     * about strangeness like this, or return an error
433*ae771770SStanislav Sedov 		     * from here, might be nice.
434*ae771770SStanislav Sedov 		     */
435*ae771770SStanislav Sedov 		    krb5_storage_seek(sp, u16, SEEK_CUR);
436*ae771770SStanislav Sedov 		}
437*ae771770SStanislav Sedov 	    }
438*ae771770SStanislav Sedov 	} else {
439*ae771770SStanislav Sedov 	    /*
440*ae771770SStanislav Sedov 	     * XXX For now we skip older kvnos, but we should extract
441*ae771770SStanislav Sedov 	     * them...
442*ae771770SStanislav Sedov 	     */
443*ae771770SStanislav Sedov 	    for (j = 0; j < version; j++) {
444*ae771770SStanislav Sedov 		/* enctype */
445*ae771770SStanislav Sedov 		CHECK(ret = krb5_ret_uint16(sp, &u16));
446*ae771770SStanislav Sedov 		/* encrypted key (or plaintext salt) */
447*ae771770SStanislav Sedov 		CHECK(ret = krb5_ret_uint16(sp, &u16));
448*ae771770SStanislav Sedov 		krb5_storage_seek(sp, u16, SEEK_CUR);
449*ae771770SStanislav Sedov 	    }
450*ae771770SStanislav Sedov 	}
451*ae771770SStanislav Sedov     }
452*ae771770SStanislav Sedov 
453*ae771770SStanislav Sedov     if (entry->kvno == 0 && kvno != 0) {
454*ae771770SStanislav Sedov 	ret = HDB_ERR_NOT_FOUND_HERE;
455*ae771770SStanislav Sedov 	goto out;
456*ae771770SStanislav Sedov     }
457*ae771770SStanislav Sedov 
458*ae771770SStanislav Sedov     return 0;
459*ae771770SStanislav Sedov  out:
460*ae771770SStanislav Sedov     if (ret == HEIM_ERR_EOF)
461*ae771770SStanislav Sedov 	/* Better error code than "end of file" */
462*ae771770SStanislav Sedov 	ret = HEIM_ERR_BAD_HDBENT_ENCODING;
463*ae771770SStanislav Sedov     return ret;
464*ae771770SStanislav Sedov }
465*ae771770SStanislav Sedov 
466*ae771770SStanislav Sedov #if 0
467*ae771770SStanislav Sedov static krb5_error_code
468*ae771770SStanislav Sedov mdb_entry2value(krb5_context context, hdb_entry *entry, krb5_data *data)
469*ae771770SStanislav Sedov {
470*ae771770SStanislav Sedov     return EINVAL;
471*ae771770SStanislav Sedov }
472*ae771770SStanislav Sedov #endif
473*ae771770SStanislav Sedov 
474*ae771770SStanislav Sedov 
475*ae771770SStanislav Sedov static krb5_error_code
mdb_close(krb5_context context,HDB * db)476*ae771770SStanislav Sedov mdb_close(krb5_context context, HDB *db)
477*ae771770SStanislav Sedov {
478*ae771770SStanislav Sedov     DB *d = (DB*)db->hdb_db;
479*ae771770SStanislav Sedov     (*d->close)(d);
480*ae771770SStanislav Sedov     return 0;
481*ae771770SStanislav Sedov }
482*ae771770SStanislav Sedov 
483*ae771770SStanislav Sedov static krb5_error_code
mdb_destroy(krb5_context context,HDB * db)484*ae771770SStanislav Sedov mdb_destroy(krb5_context context, HDB *db)
485*ae771770SStanislav Sedov {
486*ae771770SStanislav Sedov     krb5_error_code ret;
487*ae771770SStanislav Sedov 
488*ae771770SStanislav Sedov     ret = hdb_clear_master_key (context, db);
489*ae771770SStanislav Sedov     free(db->hdb_name);
490*ae771770SStanislav Sedov     free(db);
491*ae771770SStanislav Sedov     return ret;
492*ae771770SStanislav Sedov }
493*ae771770SStanislav Sedov 
494*ae771770SStanislav Sedov static krb5_error_code
mdb_lock(krb5_context context,HDB * db,int operation)495*ae771770SStanislav Sedov mdb_lock(krb5_context context, HDB *db, int operation)
496*ae771770SStanislav Sedov {
497*ae771770SStanislav Sedov     DB *d = (DB*)db->hdb_db;
498*ae771770SStanislav Sedov     int fd = (*d->fd)(d);
499*ae771770SStanislav Sedov     if(fd < 0) {
500*ae771770SStanislav Sedov 	krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
501*ae771770SStanislav Sedov 			       "Can't lock database: %s", db->hdb_name);
502*ae771770SStanislav Sedov 	return HDB_ERR_CANT_LOCK_DB;
503*ae771770SStanislav Sedov     }
504*ae771770SStanislav Sedov     return hdb_lock(fd, operation);
505*ae771770SStanislav Sedov }
506*ae771770SStanislav Sedov 
507*ae771770SStanislav Sedov static krb5_error_code
mdb_unlock(krb5_context context,HDB * db)508*ae771770SStanislav Sedov mdb_unlock(krb5_context context, HDB *db)
509*ae771770SStanislav Sedov {
510*ae771770SStanislav Sedov     DB *d = (DB*)db->hdb_db;
511*ae771770SStanislav Sedov     int fd = (*d->fd)(d);
512*ae771770SStanislav Sedov     if(fd < 0) {
513*ae771770SStanislav Sedov 	krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
514*ae771770SStanislav Sedov 			       "Can't unlock database: %s", db->hdb_name);
515*ae771770SStanislav Sedov 	return HDB_ERR_CANT_LOCK_DB;
516*ae771770SStanislav Sedov     }
517*ae771770SStanislav Sedov     return hdb_unlock(fd);
518*ae771770SStanislav Sedov }
519*ae771770SStanislav Sedov 
520*ae771770SStanislav Sedov 
521*ae771770SStanislav Sedov static krb5_error_code
mdb_seq(krb5_context context,HDB * db,unsigned flags,hdb_entry_ex * entry,int flag)522*ae771770SStanislav Sedov mdb_seq(krb5_context context, HDB *db,
523*ae771770SStanislav Sedov        unsigned flags, hdb_entry_ex *entry, int flag)
524*ae771770SStanislav Sedov {
525*ae771770SStanislav Sedov     DB *d = (DB*)db->hdb_db;
526*ae771770SStanislav Sedov     DBT key, value;
527*ae771770SStanislav Sedov     krb5_data key_data, data;
528*ae771770SStanislav Sedov     int code;
529*ae771770SStanislav Sedov 
530*ae771770SStanislav Sedov     code = db->hdb_lock(context, db, HDB_RLOCK);
531*ae771770SStanislav Sedov     if(code == -1) {
532*ae771770SStanislav Sedov 	krb5_set_error_message(context, HDB_ERR_DB_INUSE, "Database %s in use", db->hdb_name);
533*ae771770SStanislav Sedov 	return HDB_ERR_DB_INUSE;
534*ae771770SStanislav Sedov     }
535*ae771770SStanislav Sedov     code = (*d->seq)(d, &key, &value, flag);
536*ae771770SStanislav Sedov     db->hdb_unlock(context, db); /* XXX check value */
537*ae771770SStanislav Sedov     if(code == -1) {
538*ae771770SStanislav Sedov 	code = errno;
539*ae771770SStanislav Sedov 	krb5_set_error_message(context, code, "Database %s seq error: %s",
540*ae771770SStanislav Sedov 			       db->hdb_name, strerror(code));
541*ae771770SStanislav Sedov 	return code;
542*ae771770SStanislav Sedov     }
543*ae771770SStanislav Sedov     if(code == 1) {
544*ae771770SStanislav Sedov 	krb5_clear_error_message(context);
545*ae771770SStanislav Sedov 	return HDB_ERR_NOENTRY;
546*ae771770SStanislav Sedov     }
547*ae771770SStanislav Sedov 
548*ae771770SStanislav Sedov     key_data.data = key.data;
549*ae771770SStanislav Sedov     key_data.length = key.size;
550*ae771770SStanislav Sedov     data.data = value.data;
551*ae771770SStanislav Sedov     data.length = value.size;
552*ae771770SStanislav Sedov     memset(entry, 0, sizeof(*entry));
553*ae771770SStanislav Sedov 
554*ae771770SStanislav Sedov     if (mdb_value2entry(context, &data, 0, &entry->entry))
555*ae771770SStanislav Sedov 	return mdb_seq(context, db, flags, entry, R_NEXT);
556*ae771770SStanislav Sedov 
557*ae771770SStanislav Sedov     if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
558*ae771770SStanislav Sedov 	code = hdb_unseal_keys (context, db, &entry->entry);
559*ae771770SStanislav Sedov 	if (code)
560*ae771770SStanislav Sedov 	    hdb_free_entry (context, entry);
561*ae771770SStanislav Sedov     }
562*ae771770SStanislav Sedov 
563*ae771770SStanislav Sedov     return code;
564*ae771770SStanislav Sedov }
565*ae771770SStanislav Sedov 
566*ae771770SStanislav Sedov 
567*ae771770SStanislav Sedov static krb5_error_code
mdb_firstkey(krb5_context context,HDB * db,unsigned flags,hdb_entry_ex * entry)568*ae771770SStanislav Sedov mdb_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
569*ae771770SStanislav Sedov {
570*ae771770SStanislav Sedov     return mdb_seq(context, db, flags, entry, R_FIRST);
571*ae771770SStanislav Sedov }
572*ae771770SStanislav Sedov 
573*ae771770SStanislav Sedov 
574*ae771770SStanislav Sedov static krb5_error_code
mdb_nextkey(krb5_context context,HDB * db,unsigned flags,hdb_entry_ex * entry)575*ae771770SStanislav Sedov mdb_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
576*ae771770SStanislav Sedov {
577*ae771770SStanislav Sedov     return mdb_seq(context, db, flags, entry, R_NEXT);
578*ae771770SStanislav Sedov }
579*ae771770SStanislav Sedov 
580*ae771770SStanislav Sedov static krb5_error_code
mdb_rename(krb5_context context,HDB * db,const char * new_name)581*ae771770SStanislav Sedov mdb_rename(krb5_context context, HDB *db, const char *new_name)
582*ae771770SStanislav Sedov {
583*ae771770SStanislav Sedov     int ret;
584*ae771770SStanislav Sedov     char *old, *new;
585*ae771770SStanislav Sedov 
586*ae771770SStanislav Sedov     asprintf(&old, "%s.db", db->hdb_name);
587*ae771770SStanislav Sedov     asprintf(&new, "%s.db", new_name);
588*ae771770SStanislav Sedov     ret = rename(old, new);
589*ae771770SStanislav Sedov     free(old);
590*ae771770SStanislav Sedov     free(new);
591*ae771770SStanislav Sedov     if(ret)
592*ae771770SStanislav Sedov 	return errno;
593*ae771770SStanislav Sedov 
594*ae771770SStanislav Sedov     free(db->hdb_name);
595*ae771770SStanislav Sedov     db->hdb_name = strdup(new_name);
596*ae771770SStanislav Sedov     return 0;
597*ae771770SStanislav Sedov }
598*ae771770SStanislav Sedov 
599*ae771770SStanislav Sedov static krb5_error_code
mdb__get(krb5_context context,HDB * db,krb5_data key,krb5_data * reply)600*ae771770SStanislav Sedov mdb__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
601*ae771770SStanislav Sedov {
602*ae771770SStanislav Sedov     DB *d = (DB*)db->hdb_db;
603*ae771770SStanislav Sedov     DBT k, v;
604*ae771770SStanislav Sedov     int code;
605*ae771770SStanislav Sedov 
606*ae771770SStanislav Sedov     k.data = key.data;
607*ae771770SStanislav Sedov     k.size = key.length;
608*ae771770SStanislav Sedov     code = db->hdb_lock(context, db, HDB_RLOCK);
609*ae771770SStanislav Sedov     if(code)
610*ae771770SStanislav Sedov 	return code;
611*ae771770SStanislav Sedov     code = (*d->get)(d, &k, &v, 0);
612*ae771770SStanislav Sedov     db->hdb_unlock(context, db);
613*ae771770SStanislav Sedov     if(code < 0) {
614*ae771770SStanislav Sedov 	code = errno;
615*ae771770SStanislav Sedov 	krb5_set_error_message(context, code, "Database %s get error: %s",
616*ae771770SStanislav Sedov 			       db->hdb_name, strerror(code));
617*ae771770SStanislav Sedov 	return code;
618*ae771770SStanislav Sedov     }
619*ae771770SStanislav Sedov     if(code == 1) {
620*ae771770SStanislav Sedov 	krb5_clear_error_message(context);
621*ae771770SStanislav Sedov 	return HDB_ERR_NOENTRY;
622*ae771770SStanislav Sedov     }
623*ae771770SStanislav Sedov 
624*ae771770SStanislav Sedov     krb5_data_copy(reply, v.data, v.size);
625*ae771770SStanislav Sedov     return 0;
626*ae771770SStanislav Sedov }
627*ae771770SStanislav Sedov 
628*ae771770SStanislav Sedov static krb5_error_code
mdb__put(krb5_context context,HDB * db,int replace,krb5_data key,krb5_data value)629*ae771770SStanislav Sedov mdb__put(krb5_context context, HDB *db, int replace,
630*ae771770SStanislav Sedov 	krb5_data key, krb5_data value)
631*ae771770SStanislav Sedov {
632*ae771770SStanislav Sedov     DB *d = (DB*)db->hdb_db;
633*ae771770SStanislav Sedov     DBT k, v;
634*ae771770SStanislav Sedov     int code;
635*ae771770SStanislav Sedov 
636*ae771770SStanislav Sedov     k.data = key.data;
637*ae771770SStanislav Sedov     k.size = key.length;
638*ae771770SStanislav Sedov     v.data = value.data;
639*ae771770SStanislav Sedov     v.size = value.length;
640*ae771770SStanislav Sedov     code = db->hdb_lock(context, db, HDB_WLOCK);
641*ae771770SStanislav Sedov     if(code)
642*ae771770SStanislav Sedov 	return code;
643*ae771770SStanislav Sedov     code = (*d->put)(d, &k, &v, replace ? 0 : R_NOOVERWRITE);
644*ae771770SStanislav Sedov     db->hdb_unlock(context, db);
645*ae771770SStanislav Sedov     if(code < 0) {
646*ae771770SStanislav Sedov 	code = errno;
647*ae771770SStanislav Sedov 	krb5_set_error_message(context, code, "Database %s put error: %s",
648*ae771770SStanislav Sedov 			       db->hdb_name, strerror(code));
649*ae771770SStanislav Sedov 	return code;
650*ae771770SStanislav Sedov     }
651*ae771770SStanislav Sedov     if(code == 1) {
652*ae771770SStanislav Sedov 	krb5_clear_error_message(context);
653*ae771770SStanislav Sedov 	return HDB_ERR_EXISTS;
654*ae771770SStanislav Sedov     }
655*ae771770SStanislav Sedov     return 0;
656*ae771770SStanislav Sedov }
657*ae771770SStanislav Sedov 
658*ae771770SStanislav Sedov static krb5_error_code
mdb__del(krb5_context context,HDB * db,krb5_data key)659*ae771770SStanislav Sedov mdb__del(krb5_context context, HDB *db, krb5_data key)
660*ae771770SStanislav Sedov {
661*ae771770SStanislav Sedov     DB *d = (DB*)db->hdb_db;
662*ae771770SStanislav Sedov     DBT k;
663*ae771770SStanislav Sedov     krb5_error_code code;
664*ae771770SStanislav Sedov     k.data = key.data;
665*ae771770SStanislav Sedov     k.size = key.length;
666*ae771770SStanislav Sedov     code = db->hdb_lock(context, db, HDB_WLOCK);
667*ae771770SStanislav Sedov     if(code)
668*ae771770SStanislav Sedov 	return code;
669*ae771770SStanislav Sedov     code = (*d->del)(d, &k, 0);
670*ae771770SStanislav Sedov     db->hdb_unlock(context, db);
671*ae771770SStanislav Sedov     if(code == 1) {
672*ae771770SStanislav Sedov 	code = errno;
673*ae771770SStanislav Sedov 	krb5_set_error_message(context, code, "Database %s put error: %s",
674*ae771770SStanislav Sedov 			       db->hdb_name, strerror(code));
675*ae771770SStanislav Sedov 	return code;
676*ae771770SStanislav Sedov     }
677*ae771770SStanislav Sedov     if(code < 0)
678*ae771770SStanislav Sedov 	return errno;
679*ae771770SStanislav Sedov     return 0;
680*ae771770SStanislav Sedov }
681*ae771770SStanislav Sedov 
682*ae771770SStanislav Sedov 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)683*ae771770SStanislav Sedov mdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
684*ae771770SStanislav Sedov 	       unsigned flags, krb5_kvno kvno, hdb_entry_ex *entry)
685*ae771770SStanislav Sedov {
686*ae771770SStanislav Sedov     krb5_data key, value;
687*ae771770SStanislav Sedov     krb5_error_code code;
688*ae771770SStanislav Sedov 
689*ae771770SStanislav Sedov     code = mdb_principal2key(context, principal, &key);
690*ae771770SStanislav Sedov     if (code)
691*ae771770SStanislav Sedov 	return code;
692*ae771770SStanislav Sedov     code = db->hdb__get(context, db, key, &value);
693*ae771770SStanislav Sedov     krb5_data_free(&key);
694*ae771770SStanislav Sedov     if(code)
695*ae771770SStanislav Sedov 	return code;
696*ae771770SStanislav Sedov     code = mdb_value2entry(context, &value, kvno, &entry->entry);
697*ae771770SStanislav Sedov     krb5_data_free(&value);
698*ae771770SStanislav Sedov     if (code)
699*ae771770SStanislav Sedov 	return code;
700*ae771770SStanislav Sedov 
701*ae771770SStanislav Sedov     if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
702*ae771770SStanislav Sedov 	code = hdb_unseal_keys (context, db, &entry->entry);
703*ae771770SStanislav Sedov 	if (code)
704*ae771770SStanislav Sedov 	    hdb_free_entry(context, entry);
705*ae771770SStanislav Sedov     }
706*ae771770SStanislav Sedov 
707*ae771770SStanislav Sedov     return 0;
708*ae771770SStanislav Sedov }
709*ae771770SStanislav Sedov 
710*ae771770SStanislav Sedov static krb5_error_code
mdb_store(krb5_context context,HDB * db,unsigned flags,hdb_entry_ex * entry)711*ae771770SStanislav Sedov mdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
712*ae771770SStanislav Sedov {
713*ae771770SStanislav Sedov     krb5_set_error_message(context, EINVAL, "can't set principal in mdb");
714*ae771770SStanislav Sedov     return EINVAL;
715*ae771770SStanislav Sedov }
716*ae771770SStanislav Sedov 
717*ae771770SStanislav Sedov static krb5_error_code
mdb_remove(krb5_context context,HDB * db,krb5_const_principal principal)718*ae771770SStanislav Sedov mdb_remove(krb5_context context, HDB *db, krb5_const_principal principal)
719*ae771770SStanislav Sedov {
720*ae771770SStanislav Sedov     krb5_error_code code;
721*ae771770SStanislav Sedov     krb5_data key;
722*ae771770SStanislav Sedov 
723*ae771770SStanislav Sedov     code = db->hdb__del(context, db, key);
724*ae771770SStanislav Sedov     krb5_data_free(&key);
725*ae771770SStanislav Sedov     return code;
726*ae771770SStanislav Sedov }
727*ae771770SStanislav Sedov 
728*ae771770SStanislav Sedov static krb5_error_code
mdb_open(krb5_context context,HDB * db,int flags,mode_t mode)729*ae771770SStanislav Sedov mdb_open(krb5_context context, HDB *db, int flags, mode_t mode)
730*ae771770SStanislav Sedov {
731*ae771770SStanislav Sedov     char *fn;
732*ae771770SStanislav Sedov     krb5_error_code ret;
733*ae771770SStanislav Sedov 
734*ae771770SStanislav Sedov     asprintf(&fn, "%s.db", db->hdb_name);
735*ae771770SStanislav Sedov     if (fn == NULL) {
736*ae771770SStanislav Sedov 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
737*ae771770SStanislav Sedov 	return ENOMEM;
738*ae771770SStanislav Sedov     }
739*ae771770SStanislav Sedov     db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL);
740*ae771770SStanislav Sedov     free(fn);
741*ae771770SStanislav Sedov 
742*ae771770SStanislav Sedov     if (db->hdb_db == NULL) {
743*ae771770SStanislav Sedov 	switch (errno) {
744*ae771770SStanislav Sedov #ifdef EFTYPE
745*ae771770SStanislav Sedov 	case EFTYPE:
746*ae771770SStanislav Sedov #endif
747*ae771770SStanislav Sedov 	case EINVAL:
748*ae771770SStanislav Sedov 	    db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL);
749*ae771770SStanislav Sedov 	}
750*ae771770SStanislav Sedov     }
751*ae771770SStanislav Sedov 
752*ae771770SStanislav Sedov     /* try to open without .db extension */
753*ae771770SStanislav Sedov     if(db->hdb_db == NULL && errno == ENOENT)
754*ae771770SStanislav Sedov 	db->hdb_db = dbopen(db->hdb_name, flags, mode, DB_BTREE, NULL);
755*ae771770SStanislav Sedov     if(db->hdb_db == NULL) {
756*ae771770SStanislav Sedov 	ret = errno;
757*ae771770SStanislav Sedov 	krb5_set_error_message(context, ret, "dbopen (%s): %s",
758*ae771770SStanislav Sedov 			      db->hdb_name, strerror(ret));
759*ae771770SStanislav Sedov 	return ret;
760*ae771770SStanislav Sedov     }
761*ae771770SStanislav Sedov     if((flags & O_ACCMODE) == O_RDONLY)
762*ae771770SStanislav Sedov 	ret = hdb_check_db_format(context, db);
763*ae771770SStanislav Sedov     else
764*ae771770SStanislav Sedov 	ret = hdb_init_db(context, db);
765*ae771770SStanislav Sedov     if(ret == HDB_ERR_NOENTRY) {
766*ae771770SStanislav Sedov 	krb5_clear_error_message(context);
767*ae771770SStanislav Sedov 	return 0;
768*ae771770SStanislav Sedov     }
769*ae771770SStanislav Sedov     if (ret) {
770*ae771770SStanislav Sedov 	mdb_close(context, db);
771*ae771770SStanislav Sedov 	krb5_set_error_message(context, ret, "hdb_open: failed %s database %s",
772*ae771770SStanislav Sedov 			      (flags & O_ACCMODE) == O_RDONLY ?
773*ae771770SStanislav Sedov 			      "checking format of" : "initialize",
774*ae771770SStanislav Sedov 			      db->hdb_name);
775*ae771770SStanislav Sedov     }
776*ae771770SStanislav Sedov     return ret;
777*ae771770SStanislav Sedov }
778*ae771770SStanislav Sedov 
779*ae771770SStanislav Sedov krb5_error_code
hdb_mdb_create(krb5_context context,HDB ** db,const char * filename)780*ae771770SStanislav Sedov hdb_mdb_create(krb5_context context, HDB **db,
781*ae771770SStanislav Sedov 	       const char *filename)
782*ae771770SStanislav Sedov {
783*ae771770SStanislav Sedov     *db = calloc(1, sizeof(**db));
784*ae771770SStanislav Sedov     if (*db == NULL) {
785*ae771770SStanislav Sedov 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
786*ae771770SStanislav Sedov 	return ENOMEM;
787*ae771770SStanislav Sedov     }
788*ae771770SStanislav Sedov 
789*ae771770SStanislav Sedov     (*db)->hdb_db = NULL;
790*ae771770SStanislav Sedov     (*db)->hdb_name = strdup(filename);
791*ae771770SStanislav Sedov     if ((*db)->hdb_name == NULL) {
792*ae771770SStanislav Sedov 	free(*db);
793*ae771770SStanislav Sedov 	*db = NULL;
794*ae771770SStanislav Sedov 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
795*ae771770SStanislav Sedov 	return ENOMEM;
796*ae771770SStanislav Sedov     }
797*ae771770SStanislav Sedov     (*db)->hdb_master_key_set = 0;
798*ae771770SStanislav Sedov     (*db)->hdb_openp = 0;
799*ae771770SStanislav Sedov     (*db)->hdb_capability_flags = 0;
800*ae771770SStanislav Sedov     (*db)->hdb_open = mdb_open;
801*ae771770SStanislav Sedov     (*db)->hdb_close = mdb_close;
802*ae771770SStanislav Sedov     (*db)->hdb_fetch_kvno = mdb_fetch_kvno;
803*ae771770SStanislav Sedov     (*db)->hdb_store = mdb_store;
804*ae771770SStanislav Sedov     (*db)->hdb_remove = mdb_remove;
805*ae771770SStanislav Sedov     (*db)->hdb_firstkey = mdb_firstkey;
806*ae771770SStanislav Sedov     (*db)->hdb_nextkey= mdb_nextkey;
807*ae771770SStanislav Sedov     (*db)->hdb_lock = mdb_lock;
808*ae771770SStanislav Sedov     (*db)->hdb_unlock = mdb_unlock;
809*ae771770SStanislav Sedov     (*db)->hdb_rename = mdb_rename;
810*ae771770SStanislav Sedov     (*db)->hdb__get = mdb__get;
811*ae771770SStanislav Sedov     (*db)->hdb__put = mdb__put;
812*ae771770SStanislav Sedov     (*db)->hdb__del = mdb__del;
813*ae771770SStanislav Sedov     (*db)->hdb_destroy = mdb_destroy;
814*ae771770SStanislav Sedov     return 0;
815*ae771770SStanislav Sedov }
816*ae771770SStanislav Sedov 
817*ae771770SStanislav Sedov #endif /* HAVE_DB1 */
818