xref: /freebsd/crypto/heimdal/lib/hdb/hdb.c (revision 23f282aa31e9b6fceacd449020e936e98d6f2298)
1 /*
2  * Copyright (c) 1997, 1998, 1999 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include "hdb_locl.h"
35 
36 RCSID("$Id: hdb.c,v 1.35 1999/12/02 17:05:05 joda Exp $");
37 
38 krb5_error_code
39 hdb_next_enctype2key(krb5_context context,
40 		     hdb_entry *e,
41 		     krb5_enctype enctype,
42 		     Key **key)
43 {
44     Key *k;
45 
46     for (k = *key ? *key : e->keys.val;
47 	 k < e->keys.val + e->keys.len;
48 	 k++)
49 	if(k->key.keytype == enctype){
50 	    *key = k;
51 	    return 0;
52 	}
53     return KRB5_PROG_ETYPE_NOSUPP; /* XXX */
54 }
55 
56 krb5_error_code
57 hdb_enctype2key(krb5_context context,
58 		hdb_entry *e,
59 		krb5_enctype enctype,
60 		Key **key)
61 {
62     *key = NULL;
63     return hdb_next_enctype2key(context, e, enctype, key);
64 }
65 
66 /* this is a bit ugly, but will get better when the crypto framework
67    gets fixed */
68 
69 krb5_error_code
70 hdb_process_master_key(krb5_context context, EncryptionKey key,
71 		       krb5_data *schedule)
72 {
73     krb5_error_code ret;
74 
75     if(key.keytype != ETYPE_DES_CBC_MD5)
76 	return KRB5_PROG_KEYTYPE_NOSUPP;
77 
78     ret = krb5_data_alloc (schedule, sizeof(des_key_schedule));
79     if (ret)
80 	return ret;
81 
82     des_set_key((des_cblock*)key.keyvalue.data, schedule->data);
83     return 0;
84 }
85 
86 krb5_error_code
87 hdb_read_master_key(krb5_context context, const char *filename,
88 		    EncryptionKey *key)
89 {
90     FILE *f;
91     unsigned char buf[256];
92     size_t len;
93     krb5_error_code ret;
94     if(filename == NULL)
95 	filename = HDB_DB_DIR "/m-key";
96     f = fopen(filename, "r");
97     if(f == NULL)
98 	return errno;
99     len = fread(buf, 1, sizeof(buf), f);
100     if(ferror(f))
101 	ret = errno;
102     else
103 	ret = decode_EncryptionKey(buf, len, key, &len);
104     fclose(f);
105     memset(buf, 0, sizeof(buf));
106     return ret;
107 }
108 
109 void
110 _hdb_unseal_keys_int(hdb_entry *ent, int key_version, krb5_data schedule)
111 {
112     int i;
113     for(i = 0; i < ent->keys.len; i++){
114 	des_cblock iv;
115 	int num = 0;
116 	if(ent->keys.val[i].mkvno == NULL)
117 	    continue;
118 	if(*ent->keys.val[i].mkvno != key_version)
119 	    ;
120 	memset(&iv, 0, sizeof(iv));
121 
122 	des_cfb64_encrypt(ent->keys.val[i].key.keyvalue.data,
123 			  ent->keys.val[i].key.keyvalue.data,
124 			  ent->keys.val[i].key.keyvalue.length,
125 			  schedule.data, &iv, &num, 0);
126 	free(ent->keys.val[i].mkvno);
127 	ent->keys.val[i].mkvno = NULL;
128     }
129 }
130 
131 void
132 hdb_unseal_keys(HDB *db, hdb_entry *ent)
133 {
134     if (db->master_key_set == 0)
135 	return;
136     _hdb_unseal_keys_int(ent, db->master_key_version, db->master_key);
137 }
138 
139 void
140 _hdb_seal_keys_int(hdb_entry *ent, int key_version, krb5_data schedule)
141 {
142     int i;
143     for(i = 0; i < ent->keys.len; i++){
144 	des_cblock iv;
145 	int num = 0;
146 
147 	if(ent->keys.val[i].mkvno != NULL)
148 	    continue;
149 	memset(&iv, 0, sizeof(iv));
150 	des_cfb64_encrypt(ent->keys.val[i].key.keyvalue.data,
151 			  ent->keys.val[i].key.keyvalue.data,
152 			  ent->keys.val[i].key.keyvalue.length,
153 			  schedule.data, &iv, &num, 1);
154 	ent->keys.val[i].mkvno = malloc(sizeof(*ent->keys.val[i].mkvno));
155 	*ent->keys.val[i].mkvno = key_version;
156     }
157 }
158 
159 void
160 hdb_seal_keys(HDB *db, hdb_entry *ent)
161 {
162     if (db->master_key_set == 0)
163 	return;
164 
165     _hdb_seal_keys_int(ent, db->master_key_version, db->master_key);
166 }
167 
168 void
169 hdb_free_key(Key *key)
170 {
171     memset(key->key.keyvalue.data,
172 	   0,
173 	   key->key.keyvalue.length);
174     free_Key(key);
175     free(key);
176 }
177 
178 
179 krb5_error_code
180 hdb_lock(int fd, int operation)
181 {
182     int i, code;
183     for(i = 0; i < 3; i++){
184 	code = flock(fd, (operation == HDB_RLOCK ? LOCK_SH : LOCK_EX) | LOCK_NB);
185 	if(code == 0 || errno != EWOULDBLOCK)
186 	    break;
187 	sleep(1);
188     }
189     if(code == 0)
190 	return 0;
191     if(errno == EWOULDBLOCK)
192 	return HDB_ERR_DB_INUSE;
193     return HDB_ERR_CANT_LOCK_DB;
194 }
195 
196 krb5_error_code
197 hdb_unlock(int fd)
198 {
199     int code;
200     code = flock(fd, LOCK_UN);
201     if(code)
202 	return 4711 /* XXX */;
203     return 0;
204 }
205 
206 void
207 hdb_free_entry(krb5_context context, hdb_entry *ent)
208 {
209     int i;
210 
211     for(i = 0; i < ent->keys.len; ++i) {
212 	Key *k = &ent->keys.val[i];
213 
214 	memset (k->key.keyvalue.data, 0, k->key.keyvalue.length);
215     }
216     free_hdb_entry(ent);
217 }
218 
219 krb5_error_code
220 hdb_foreach(krb5_context context,
221 	    HDB *db,
222 	    unsigned flags,
223 	    hdb_foreach_func_t func,
224 	    void *data)
225 {
226     krb5_error_code ret;
227     hdb_entry entry;
228     ret = db->firstkey(context, db, flags, &entry);
229     while(ret == 0){
230 	ret = (*func)(context, db, &entry, data);
231 	hdb_free_entry(context, &entry);
232 	if(ret == 0)
233 	    ret = db->nextkey(context, db, flags, &entry);
234     }
235     if(ret == HDB_ERR_NOENTRY)
236 	ret = 0;
237     return ret;
238 }
239 
240 krb5_error_code
241 hdb_check_db_format(krb5_context context, HDB *db)
242 {
243     krb5_data tag;
244     krb5_data version;
245     krb5_error_code ret;
246     unsigned ver;
247     int foo;
248 
249     tag.data = HDB_DB_FORMAT_ENTRY;
250     tag.length = strlen(tag.data);
251     ret = (*db->_get)(context, db, tag, &version);
252     if(ret)
253 	return ret;
254     foo = sscanf(version.data, "%u", &ver);
255     krb5_data_free (&version);
256     if (foo != 1)
257 	return HDB_ERR_BADVERSION;
258     if(ver != HDB_DB_FORMAT)
259 	return HDB_ERR_BADVERSION;
260     return 0;
261 }
262 
263 krb5_error_code
264 hdb_init_db(krb5_context context, HDB *db)
265 {
266     krb5_error_code ret;
267     krb5_data tag;
268     krb5_data version;
269     char ver[32];
270 
271     ret = hdb_check_db_format(context, db);
272     if(ret != HDB_ERR_NOENTRY)
273 	return ret;
274 
275     tag.data = HDB_DB_FORMAT_ENTRY;
276     tag.length = strlen(tag.data);
277     snprintf(ver, sizeof(ver), "%u", HDB_DB_FORMAT);
278     version.data = ver;
279     version.length = strlen(version.data) + 1; /* zero terminated */
280     ret = (*db->_put)(context, db, 0, tag, version);
281     return ret;
282 }
283 
284 krb5_error_code
285 hdb_create(krb5_context context, HDB **db, const char *filename)
286 {
287     krb5_error_code ret = 0;
288     if(filename == NULL)
289 	filename = HDB_DEFAULT_DB;
290     initialize_hdb_error_table_r(&context->et_list);
291 #ifdef HAVE_DB_H
292     ret = hdb_db_create(context, db, filename);
293 #elif HAVE_NDBM_H
294     ret = hdb_ndbm_create(context, db, filename);
295 #else
296     krb5_errx(context, 1, "No database support! (hdb_create)");
297 #endif
298     return ret;
299 }
300 
301 krb5_error_code
302 hdb_set_master_key (krb5_context context,
303 		    HDB *db,
304 		    EncryptionKey key)
305 {
306     krb5_error_code ret;
307 
308     ret = hdb_process_master_key(context, key, &db->master_key);
309     if (ret)
310 	return ret;
311 #if 0 /* XXX - why? */
312     des_set_random_generator_seed(key.keyvalue.data);
313 #endif
314     db->master_key_set = 1;
315     db->master_key_version = 0; /* XXX */
316     return 0;
317 }
318 
319 krb5_error_code
320 hdb_set_master_keyfile (krb5_context context,
321 			HDB *db,
322 			const char *keyfile)
323 {
324     EncryptionKey key;
325     krb5_error_code ret;
326 
327     ret = hdb_read_master_key(context, keyfile, &key);
328     if (ret) {
329 	if (ret != ENOENT)
330 	    return ret;
331 	return 0;
332     }
333     ret = hdb_set_master_key(context, db, key);
334     memset(key.keyvalue.data, 0, key.keyvalue.length);
335     free_EncryptionKey(&key);
336     return ret;
337 }
338 
339 krb5_error_code
340 hdb_clear_master_key (krb5_context context,
341 		      HDB *db)
342 {
343     if (db->master_key_set) {
344 	memset(db->master_key.data, 0, db->master_key.length);
345 	krb5_data_free(&db->master_key);
346 	db->master_key_set = 0;
347     }
348     return 0;
349 }
350