Remove building with NOCRYPTO option
[minix.git] / crypto / external / bsd / heimdal / dist / lib / hdb / hdb.c
blob1df2d6a1fa3f26c3ac11e03d3105a8732effe372
1 /* $NetBSD: hdb.c,v 1.3 2014/04/24 13:45:34 pettai Exp $ */
3 /*
4 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
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
12 * are met:
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
35 * SUCH DAMAGE.
38 #include "krb5_locl.h"
39 #include "hdb_locl.h"
41 #ifdef HAVE_DLFCN_H
42 #include <dlfcn.h>
43 #endif
45 /*! @mainpage Heimdal database backend library
47 * @section intro Introduction
49 * Heimdal libhdb library provides the backend support for Heimdal kdc
50 * and kadmind. Its here where plugins for diffrent database engines
51 * can be pluged in and extend support for here Heimdal get the
52 * principal and policy data from.
54 * Example of Heimdal backend are:
55 * - Berkeley DB 1.85
56 * - Berkeley DB 3.0
57 * - Berkeley DB 4.0
58 * - New Berkeley DB
59 * - LDAP
62 * The project web page: http://www.h5l.org/
66 const int hdb_interface_version = HDB_INTERFACE_VERSION;
68 static struct hdb_method methods[] = {
69 #if HAVE_DB1 || HAVE_DB3
70 { HDB_INTERFACE_VERSION, "db:", hdb_db_create},
71 #endif
72 #if HAVE_DB1
73 { HDB_INTERFACE_VERSION, "mit-db:", hdb_mdb_create},
74 #endif
75 #if HAVE_NDBM
76 { HDB_INTERFACE_VERSION, "ndbm:", hdb_ndbm_create},
77 #endif
78 { HDB_INTERFACE_VERSION, "keytab:", hdb_keytab_create},
79 #if defined(OPENLDAP) && !defined(OPENLDAP_MODULE)
80 { HDB_INTERFACE_VERSION, "ldap:", hdb_ldap_create},
81 { HDB_INTERFACE_VERSION, "ldapi:", hdb_ldapi_create},
82 #endif
83 #ifdef HAVE_SQLITE3
84 { HDB_INTERFACE_VERSION, "sqlite:", hdb_sqlite_create},
85 #endif
86 {0, NULL, NULL}
89 #if HAVE_DB1 || HAVE_DB3
90 static struct hdb_method dbmetod =
91 { HDB_INTERFACE_VERSION, "", hdb_db_create };
92 #elif defined(HAVE_NDBM)
93 static struct hdb_method dbmetod =
94 { HDB_INTERFACE_VERSION, "", hdb_ndbm_create };
95 #endif
98 krb5_error_code
99 hdb_next_enctype2key(krb5_context context,
100 const hdb_entry *e,
101 krb5_enctype enctype,
102 Key **key)
104 Key *k;
106 for (k = *key ? (*key) + 1 : e->keys.val;
107 k < e->keys.val + e->keys.len;
108 k++)
110 if(k->key.keytype == enctype){
111 *key = k;
112 return 0;
115 krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
116 "No next enctype %d for hdb-entry",
117 (int)enctype);
118 return KRB5_PROG_ETYPE_NOSUPP; /* XXX */
121 krb5_error_code
122 hdb_enctype2key(krb5_context context,
123 hdb_entry *e,
124 krb5_enctype enctype,
125 Key **key)
127 *key = NULL;
128 return hdb_next_enctype2key(context, e, enctype, key);
131 void
132 hdb_free_key(Key *key)
134 memset(key->key.keyvalue.data,
136 key->key.keyvalue.length);
137 free_Key(key);
138 free(key);
142 krb5_error_code
143 hdb_lock(int fd, int operation)
145 int i, code = 0;
147 for(i = 0; i < 3; i++){
148 code = flock(fd, (operation == HDB_RLOCK ? LOCK_SH : LOCK_EX) | LOCK_NB);
149 if(code == 0 || errno != EWOULDBLOCK)
150 break;
151 sleep(1);
153 if(code == 0)
154 return 0;
155 if(errno == EWOULDBLOCK)
156 return HDB_ERR_DB_INUSE;
157 return HDB_ERR_CANT_LOCK_DB;
160 krb5_error_code
161 hdb_unlock(int fd)
163 int code;
164 code = flock(fd, LOCK_UN);
165 if(code)
166 return 4711 /* XXX */;
167 return 0;
170 void
171 hdb_free_entry(krb5_context context, hdb_entry_ex *ent)
173 size_t i;
175 if (ent->free_entry)
176 (*ent->free_entry)(context, ent);
178 for(i = 0; i < ent->entry.keys.len; ++i) {
179 Key *k = &ent->entry.keys.val[i];
181 memset (k->key.keyvalue.data, 0, k->key.keyvalue.length);
183 free_hdb_entry(&ent->entry);
186 krb5_error_code
187 hdb_foreach(krb5_context context,
188 HDB *db,
189 unsigned flags,
190 hdb_foreach_func_t func,
191 void *data)
193 krb5_error_code ret;
194 hdb_entry_ex entry;
195 ret = db->hdb_firstkey(context, db, flags, &entry);
196 if (ret == 0)
197 krb5_clear_error_message(context);
198 while(ret == 0){
199 ret = (*func)(context, db, &entry, data);
200 hdb_free_entry(context, &entry);
201 if(ret == 0)
202 ret = db->hdb_nextkey(context, db, flags, &entry);
204 if(ret == HDB_ERR_NOENTRY)
205 ret = 0;
206 return ret;
209 krb5_error_code
210 hdb_check_db_format(krb5_context context, HDB *db)
212 krb5_data tag;
213 krb5_data version;
214 krb5_error_code ret, ret2;
215 unsigned ver;
216 int foo;
218 ret = db->hdb_lock(context, db, HDB_RLOCK);
219 if (ret)
220 return ret;
222 tag.data = (void *)(intptr_t)HDB_DB_FORMAT_ENTRY;
223 tag.length = strlen(tag.data);
224 ret = (*db->hdb__get)(context, db, tag, &version);
225 ret2 = db->hdb_unlock(context, db);
226 if(ret)
227 return ret;
228 if (ret2)
229 return ret2;
230 foo = sscanf(version.data, "%u", &ver);
231 krb5_data_free (&version);
232 if (foo != 1)
233 return HDB_ERR_BADVERSION;
234 if(ver != HDB_DB_FORMAT)
235 return HDB_ERR_BADVERSION;
236 return 0;
239 krb5_error_code
240 hdb_init_db(krb5_context context, HDB *db)
242 krb5_error_code ret, ret2;
243 krb5_data tag;
244 krb5_data version;
245 char ver[32];
247 ret = hdb_check_db_format(context, db);
248 if(ret != HDB_ERR_NOENTRY)
249 return ret;
251 ret = db->hdb_lock(context, db, HDB_WLOCK);
252 if (ret)
253 return ret;
255 tag.data = (void *)(intptr_t)HDB_DB_FORMAT_ENTRY;
256 tag.length = strlen(tag.data);
257 snprintf(ver, sizeof(ver), "%u", HDB_DB_FORMAT);
258 version.data = ver;
259 version.length = strlen(version.data) + 1; /* zero terminated */
260 ret = (*db->hdb__put)(context, db, 0, tag, version);
261 ret2 = db->hdb_unlock(context, db);
262 if (ret) {
263 if (ret2)
264 krb5_clear_error_message(context);
265 return ret;
267 return ret2;
270 #ifdef HAVE_DLOPEN
273 * Load a dynamic backend from /usr/heimdal/lib/hdb_NAME.so,
274 * looking for the hdb_NAME_create symbol.
277 static const struct hdb_method *
278 find_dynamic_method (krb5_context context,
279 const char *filename,
280 const char **rest)
282 static struct hdb_method method;
283 struct hdb_so_method *mso;
284 char *prefix, *path, *symbol;
285 const char *p;
286 void *dl;
287 size_t len;
289 p = strchr(filename, ':');
291 /* if no prefix, don't know what module to load, just ignore it */
292 if (p == NULL)
293 return NULL;
295 len = p - filename;
296 *rest = filename + len + 1;
298 prefix = malloc(len + 1);
299 if (prefix == NULL)
300 krb5_errx(context, 1, "out of memory");
301 strlcpy(prefix, filename, len + 1);
303 if (asprintf(&path, LIBDIR "/hdb_%s.so", prefix) == -1)
304 krb5_errx(context, 1, "out of memory");
306 #ifndef RTLD_NOW
307 #define RTLD_NOW 0
308 #endif
309 #ifndef RTLD_GLOBAL
310 #define RTLD_GLOBAL 0
311 #endif
313 dl = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
314 if (dl == NULL) {
315 krb5_warnx(context, "error trying to load dynamic module %s: %s\n",
316 path, dlerror());
317 free(prefix);
318 free(path);
319 return NULL;
322 if (asprintf(&symbol, "hdb_%s_interface", prefix) == -1)
323 krb5_errx(context, 1, "out of memory");
325 mso = (struct hdb_so_method *) dlsym(dl, symbol);
326 if (mso == NULL) {
327 krb5_warnx(context, "error finding symbol %s in %s: %s\n",
328 symbol, path, dlerror());
329 dlclose(dl);
330 free(symbol);
331 free(prefix);
332 free(path);
333 return NULL;
335 free(path);
336 free(symbol);
338 if (mso->version != HDB_INTERFACE_VERSION) {
339 krb5_warnx(context,
340 "error wrong version in shared module %s "
341 "version: %d should have been %d\n",
342 prefix, mso->version, HDB_INTERFACE_VERSION);
343 dlclose(dl);
344 free(prefix);
345 return NULL;
348 if (mso->create == NULL) {
349 krb5_errx(context, 1,
350 "no entry point function in shared mod %s ",
351 prefix);
352 dlclose(dl);
353 free(prefix);
354 return NULL;
357 method.create = mso->create;
358 method.prefix = prefix;
360 return &method;
362 #endif /* HAVE_DLOPEN */
365 * find the relevant method for `filename', returning a pointer to the
366 * rest in `rest'.
367 * return NULL if there's no such method.
370 static const struct hdb_method *
371 find_method (const char *filename, const char **rest)
373 const struct hdb_method *h;
375 for (h = methods; h->prefix != NULL; ++h) {
376 if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0) {
377 *rest = filename + strlen(h->prefix);
378 return h;
381 #if defined(HAVE_DB1) || defined(HAVE_DB3) || defined(HAVE_NDBM)
382 if (strncmp(filename, "/", 1) == 0
383 || strncmp(filename, "./", 2) == 0
384 || strncmp(filename, "../", 3) == 0)
386 *rest = filename;
387 return &dbmetod;
389 #endif
391 return NULL;
394 krb5_error_code
395 hdb_list_builtin(krb5_context context, char **list)
397 const struct hdb_method *h;
398 size_t len = 0;
399 char *buf = NULL;
401 for (h = methods; h->prefix != NULL; ++h) {
402 if (h->prefix[0] == '\0')
403 continue;
404 len += strlen(h->prefix) + 2;
407 len += 1;
408 buf = malloc(len);
409 if (buf == NULL) {
410 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
411 return ENOMEM;
413 buf[0] = '\0';
415 for (h = methods; h->prefix != NULL; ++h) {
416 if (h != methods)
417 strlcat(buf, ", ", len);
418 strlcat(buf, h->prefix, len);
420 *list = buf;
421 return 0;
424 krb5_error_code
425 _hdb_keytab2hdb_entry(krb5_context context,
426 const krb5_keytab_entry *ktentry,
427 hdb_entry_ex *entry)
429 entry->entry.kvno = ktentry->vno;
430 entry->entry.created_by.time = ktentry->timestamp;
432 entry->entry.keys.val = calloc(1, sizeof(entry->entry.keys.val[0]));
433 if (entry->entry.keys.val == NULL)
434 return ENOMEM;
435 entry->entry.keys.len = 1;
437 entry->entry.keys.val[0].mkvno = NULL;
438 entry->entry.keys.val[0].salt = NULL;
440 return krb5_copy_keyblock_contents(context,
441 &ktentry->keyblock,
442 &entry->entry.keys.val[0].key);
446 * Create a handle for a Kerberos database
448 * Create a handle for a Kerberos database backend specified by a
449 * filename. Doesn't create a file if its doesn't exists, you have to
450 * use O_CREAT to tell the backend to create the file.
453 krb5_error_code
454 hdb_create(krb5_context context, HDB **db, const char *filename)
456 const struct hdb_method *h;
457 const char *residual;
458 krb5_error_code ret;
459 struct krb5_plugin *list = NULL, *e;
461 if(filename == NULL)
462 filename = HDB_DEFAULT_DB;
463 krb5_add_et_list(context, initialize_hdb_error_table_r);
464 h = find_method (filename, &residual);
466 if (h == NULL) {
467 ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, "hdb", &list);
468 if(ret == 0 && list != NULL) {
469 for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
470 h = _krb5_plugin_get_symbol(e);
471 if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0
472 && h->interface_version == HDB_INTERFACE_VERSION) {
473 residual = filename + strlen(h->prefix);
474 break;
477 if (e == NULL) {
478 h = NULL;
479 _krb5_plugin_free(list);
484 #ifdef HAVE_DLOPEN
485 if (h == NULL)
486 h = find_dynamic_method (context, filename, &residual);
487 #endif
488 if (h == NULL)
489 krb5_errx(context, 1, "No database support for %s", filename);
490 return (*h->create)(context, db, residual);