Round #2 of scan-build warnings cleanup
[heimdal.git] / lib / hdb / hdb.c
blob1cb33df0885c4f7eaf51ae7b908ab0de72dc2f75
1 /*
2 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
36 #include "krb5_locl.h"
37 #include "hdb_locl.h"
39 #ifdef HAVE_DLFCN_H
40 #include <dlfcn.h>
41 #endif
43 /*! @mainpage Heimdal database backend library
45 * @section intro Introduction
47 * Heimdal libhdb library provides the backend support for Heimdal kdc
48 * and kadmind. Its here where plugins for diffrent database engines
49 * can be pluged in and extend support for here Heimdal get the
50 * principal and policy data from.
52 * Example of Heimdal backend are:
53 * - Berkeley DB 1.85
54 * - Berkeley DB 3.0
55 * - Berkeley DB 4.0
56 * - New Berkeley DB
57 * - LDAP
60 * The project web page: http://www.h5l.org/
64 const int hdb_interface_version = HDB_INTERFACE_VERSION;
66 static struct hdb_method methods[] = {
67 /* "db:" should be db3 if we have db3, or db1 if we have db1 */
68 #if HAVE_DB3
69 { HDB_INTERFACE_VERSION, NULL, NULL, "db:", hdb_db3_create},
70 #elif HAVE_DB1
71 { HDB_INTERFACE_VERSION, NULL, NULL, "db:", hdb_db1_create},
72 #endif
73 #if HAVE_DB1
74 { HDB_INTERFACE_VERSION, NULL, NULL, "db1:", hdb_db1_create},
75 #endif
76 #if HAVE_DB3
77 { HDB_INTERFACE_VERSION, NULL, NULL, "db3:", hdb_db3_create},
78 #endif
79 #if HAVE_DB1
80 { HDB_INTERFACE_VERSION, NULL, NULL, "mit-db:", hdb_mitdb_create},
81 #endif
82 #if HAVE_LMDB
83 { HDB_INTERFACE_VERSION, NULL, NULL, "mdb:", hdb_mdb_create},
84 { HDB_INTERFACE_VERSION, NULL, NULL, "lmdb:", hdb_mdb_create},
85 #endif
86 #if HAVE_NDBM
87 { HDB_INTERFACE_VERSION, NULL, NULL, "ndbm:", hdb_ndbm_create},
88 #endif
89 { HDB_INTERFACE_VERSION, NULL, NULL, "keytab:", hdb_keytab_create},
90 #if defined(OPENLDAP) && !defined(OPENLDAP_MODULE)
91 { HDB_INTERFACE_VERSION, NULL, NULL, "ldap:", hdb_ldap_create},
92 { HDB_INTERFACE_VERSION, NULL, NULL, "ldapi:", hdb_ldapi_create},
93 #elif defined(OPENLDAP)
94 { HDB_INTERFACE_VERSION, NULL, NULL, "ldap:", NULL},
95 { HDB_INTERFACE_VERSION, NULL, NULL, "ldapi:", NULL},
96 #endif
97 #ifdef HAVE_SQLITE3
98 { HDB_INTERFACE_VERSION, NULL, NULL, "sqlite:", hdb_sqlite_create},
99 #endif
100 { 0, NULL, NULL, NULL, NULL}
104 * It'd be nice if we could try opening an HDB with each supported
105 * backend until one works or all fail. It may not be possible for all
106 * flavors, but where it's possible we should.
108 #if defined(HAVE_LMDB)
109 static struct hdb_method default_dbmethod =
110 { HDB_INTERFACE_VERSION, NULL, NULL, "", hdb_mdb_create };
111 #elif defined(HAVE_DB3)
112 static struct hdb_method default_dbmethod =
113 { HDB_INTERFACE_VERSION, NULL, NULL, "", hdb_db3_create };
114 #elif defined(HAVE_DB1)
115 static struct hdb_method default_dbmethod =
116 { HDB_INTERFACE_VERSION, NULL, NULL, "", hdb_db1_create };
117 #elif defined(HAVE_NDBM)
118 static struct hdb_method default_dbmethod =
119 { HDB_INTERFACE_VERSION, NULL, NULL, "", hdb_ndbm_create };
120 #endif
122 const Keys *
123 hdb_kvno2keys(krb5_context context,
124 const hdb_entry *e,
125 krb5_kvno kvno)
127 HDB_Ext_KeySet *hist_keys;
128 HDB_extension *extp;
129 size_t i;
131 if (kvno == 0)
132 return &e->keys;
134 extp = hdb_find_extension(e, choice_HDB_extension_data_hist_keys);
135 if (extp == NULL)
136 return 0;
138 hist_keys = &extp->data.u.hist_keys;
139 for (i = 0; i < hist_keys->len; i++) {
140 if (hist_keys->val[i].kvno == kvno)
141 return &hist_keys->val[i].keys;
144 return NULL;
147 krb5_error_code
148 hdb_next_enctype2key(krb5_context context,
149 const hdb_entry *e,
150 const Keys *keyset,
151 krb5_enctype enctype,
152 Key **key)
154 const Keys *keys = keyset ? keyset : &e->keys;
155 Key *k;
157 for (k = *key ? (*key) + 1 : keys->val; k < keys->val + keys->len; k++) {
158 if(k->key.keytype == enctype){
159 *key = k;
160 return 0;
163 krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
164 "No next enctype %d for hdb-entry",
165 (int)enctype);
166 return KRB5_PROG_ETYPE_NOSUPP; /* XXX */
169 krb5_error_code
170 hdb_enctype2key(krb5_context context,
171 hdb_entry *e,
172 const Keys *keyset,
173 krb5_enctype enctype,
174 Key **key)
176 *key = NULL;
177 return hdb_next_enctype2key(context, e, keyset, enctype, key);
180 void
181 hdb_free_key(Key *key)
183 memset(key->key.keyvalue.data,
185 key->key.keyvalue.length);
186 free_Key(key);
187 free(key);
191 krb5_error_code
192 hdb_lock(int fd, int operation)
194 int i, code = 0;
196 for(i = 0; i < 3; i++){
197 code = flock(fd, (operation == HDB_RLOCK ? LOCK_SH : LOCK_EX) | LOCK_NB);
198 if(code == 0 || errno != EWOULDBLOCK)
199 break;
200 sleep(1);
202 if(code == 0)
203 return 0;
204 if(errno == EWOULDBLOCK)
205 return HDB_ERR_DB_INUSE;
206 return HDB_ERR_CANT_LOCK_DB;
209 krb5_error_code
210 hdb_unlock(int fd)
212 int code;
213 code = flock(fd, LOCK_UN);
214 if(code)
215 return 4711 /* XXX */;
216 return 0;
219 void
220 hdb_free_entry(krb5_context context, hdb_entry_ex *ent)
222 Key *k;
223 size_t i;
225 if (ent->free_entry)
226 (*ent->free_entry)(context, ent);
228 for(i = 0; i < ent->entry.keys.len; i++) {
229 k = &ent->entry.keys.val[i];
231 memset (k->key.keyvalue.data, 0, k->key.keyvalue.length);
233 free_hdb_entry(&ent->entry);
236 krb5_error_code
237 hdb_foreach(krb5_context context,
238 HDB *db,
239 unsigned flags,
240 hdb_foreach_func_t func,
241 void *data)
243 krb5_error_code ret;
244 hdb_entry_ex entry;
245 ret = db->hdb_firstkey(context, db, flags, &entry);
246 if (ret == 0)
247 krb5_clear_error_message(context);
248 while(ret == 0){
249 ret = (*func)(context, db, &entry, data);
250 hdb_free_entry(context, &entry);
251 if(ret == 0)
252 ret = db->hdb_nextkey(context, db, flags, &entry);
254 if(ret == HDB_ERR_NOENTRY)
255 ret = 0;
256 return ret;
259 krb5_error_code
260 hdb_check_db_format(krb5_context context, HDB *db)
262 krb5_data tag;
263 krb5_data version;
264 krb5_error_code ret, ret2;
265 unsigned ver;
266 int foo;
268 ret = db->hdb_lock(context, db, HDB_RLOCK);
269 if (ret)
270 return ret;
272 tag.data = (void *)(intptr_t)HDB_DB_FORMAT_ENTRY;
273 tag.length = strlen(tag.data);
274 ret = (*db->hdb__get)(context, db, tag, &version);
275 ret2 = db->hdb_unlock(context, db);
276 if(ret)
277 return ret;
278 if (ret2)
279 return ret2;
280 foo = sscanf(version.data, "%u", &ver);
281 krb5_data_free (&version);
282 if (foo != 1)
283 return HDB_ERR_BADVERSION;
284 if(ver != HDB_DB_FORMAT)
285 return HDB_ERR_BADVERSION;
286 return 0;
289 krb5_error_code
290 hdb_init_db(krb5_context context, HDB *db)
292 krb5_error_code ret, ret2;
293 krb5_data tag;
294 krb5_data version;
295 char ver[32];
297 ret = hdb_check_db_format(context, db);
298 if(ret != HDB_ERR_NOENTRY)
299 return ret;
301 ret = db->hdb_lock(context, db, HDB_WLOCK);
302 if (ret)
303 return ret;
305 tag.data = (void *)(intptr_t)HDB_DB_FORMAT_ENTRY;
306 tag.length = strlen(tag.data);
307 snprintf(ver, sizeof(ver), "%u", HDB_DB_FORMAT);
308 version.data = ver;
309 version.length = strlen(version.data) + 1; /* zero terminated */
310 ret = (*db->hdb__put)(context, db, 0, tag, version);
311 ret2 = db->hdb_unlock(context, db);
312 if (ret) {
313 if (ret2)
314 krb5_clear_error_message(context);
315 return ret;
317 return ret2;
321 * find the relevant method for `filename', returning a pointer to the
322 * rest in `rest'.
323 * return NULL if there's no such method.
326 static const struct hdb_method *
327 find_method (const char *filename, const char **rest)
329 const struct hdb_method *h;
331 for (h = methods; h->prefix != NULL; ++h) {
332 if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0) {
333 *rest = filename + strlen(h->prefix);
334 return h;
337 #if defined(HAVE_DB1) || defined(HAVE_DB3) || defined(HAVE_LMDB) || defined(HAVE_NDBM)
338 if (strncmp(filename, "/", sizeof("/") - 1) == 0
339 || strncmp(filename, "./", sizeof("./") - 1) == 0
340 || strncmp(filename, "../", sizeof("../") - 1) == 0
341 #ifdef WIN32
342 || strncmp(filename, "\\\\", sizeof("\\\\") - 1)
343 || (isalpha(filename[0]) && filename[1] == ':')
344 #endif
347 *rest = filename;
348 return &default_dbmethod;
350 #endif
352 return NULL;
355 struct cb_s {
356 const char *residual;
357 const char *filename;
358 const struct hdb_method *h;
361 static krb5_error_code KRB5_LIB_CALL
362 callback(krb5_context context, const void *plug, void *plugctx, void *userctx)
364 const struct hdb_method *h = (const struct hdb_method *)plug;
365 struct cb_s *cb_ctx = (struct cb_s *)userctx;
367 if (strncmp(cb_ctx->filename, h->prefix, strlen(h->prefix)) == 0) {
368 cb_ctx->residual = cb_ctx->filename + strlen(h->prefix) + 1;
369 cb_ctx->h = h;
370 return 0;
372 return KRB5_PLUGIN_NO_HANDLE;
375 static char *
376 make_sym(const char *prefix)
378 char *s, *sym;
380 errno = 0;
381 if (prefix == NULL || prefix[0] == '\0')
382 return NULL;
383 if ((s = strdup(prefix)) == NULL)
384 return NULL;
385 if (strchr(s, ':') != NULL)
386 *strchr(s, ':') = '\0';
387 if (asprintf(&sym, "hdb_%s_interface", s) == -1)
388 sym = NULL;
389 free(s);
390 return sym;
393 krb5_error_code
394 hdb_list_builtin(krb5_context context, char **list)
396 const struct hdb_method *h;
397 size_t len = 0;
398 char *buf = NULL;
400 for (h = methods; h->prefix != NULL; ++h) {
401 if (h->prefix[0] == '\0')
402 continue;
403 len += strlen(h->prefix) + 2;
406 len += 1;
407 buf = malloc(len);
408 if (buf == NULL) {
409 return krb5_enomem(context);
411 buf[0] = '\0';
413 for (h = methods; h->prefix != NULL; ++h) {
414 if (h->create == NULL) {
415 struct cb_s cb_ctx;
416 char *f;
417 char *sym;
419 /* Try loading the plugin */
420 if (asprintf(&f, "%sfoo", h->prefix) == -1)
421 f = NULL;
422 if ((sym = make_sym(h->prefix)) == NULL) {
423 free(buf);
424 free(f);
425 return krb5_enomem(context);
427 cb_ctx.filename = f;
428 cb_ctx.residual = NULL;
429 cb_ctx.h = NULL;
430 (void)_krb5_plugin_run_f(context, "krb5", sym,
431 HDB_INTERFACE_VERSION, 0, &cb_ctx,
432 callback);
433 free(f);
434 free(sym);
435 if (cb_ctx.h == NULL || cb_ctx.h->create == NULL)
436 continue;
438 if (h != methods)
439 strlcat(buf, ", ", len);
440 strlcat(buf, h->prefix, len);
442 *list = buf;
443 return 0;
446 krb5_error_code
447 _hdb_keytab2hdb_entry(krb5_context context,
448 const krb5_keytab_entry *ktentry,
449 hdb_entry_ex *entry)
451 entry->entry.kvno = ktentry->vno;
452 entry->entry.created_by.time = ktentry->timestamp;
454 entry->entry.keys.val = calloc(1, sizeof(entry->entry.keys.val[0]));
455 if (entry->entry.keys.val == NULL)
456 return ENOMEM;
457 entry->entry.keys.len = 1;
459 entry->entry.keys.val[0].mkvno = NULL;
460 entry->entry.keys.val[0].salt = NULL;
462 return krb5_copy_keyblock_contents(context,
463 &ktentry->keyblock,
464 &entry->entry.keys.val[0].key);
468 * Create a handle for a Kerberos database
470 * Create a handle for a Kerberos database backend specified by a
471 * filename. Doesn't create a file if its doesn't exists, you have to
472 * use O_CREAT to tell the backend to create the file.
475 krb5_error_code
476 hdb_create(krb5_context context, HDB **db, const char *filename)
478 struct cb_s cb_ctx;
480 if (filename == NULL)
481 filename = HDB_DEFAULT_DB;
482 cb_ctx.h = find_method (filename, &cb_ctx.residual);
483 cb_ctx.filename = filename;
485 if (cb_ctx.h == NULL || cb_ctx.h->create == NULL) {
486 char *sym;
488 if ((sym = make_sym(filename)) == NULL)
489 return krb5_enomem(context);
491 (void)_krb5_plugin_run_f(context, "krb5", sym, HDB_INTERFACE_VERSION,
492 0, &cb_ctx, callback);
494 free(sym);
496 if (cb_ctx.h == NULL)
497 krb5_errx(context, 1, "No database support for %s", cb_ctx.filename);
498 return (*cb_ctx.h->create)(context, db, cb_ctx.residual);