Rename context handle lifetime to endtime
[heimdal.git] / lib / hdb / hdb.c
blob3a6dd44c4fc0f357a46404b69797834e516e81da
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 #if HAVE_DB1 || HAVE_DB3
68 { HDB_INTERFACE_VERSION, NULL, NULL, "db:", hdb_db_create},
69 #endif
70 #if HAVE_DB1
71 { HDB_INTERFACE_VERSION, NULL, NULL, "mit-db:", hdb_mitdb_create},
72 #endif
73 #if HAVE_MDB
74 { HDB_INTERFACE_VERSION, NULL, NULL, "mdb:", hdb_mdb_create},
75 #endif
76 #if HAVE_NDBM
77 { HDB_INTERFACE_VERSION, NULL, NULL, "ndbm:", hdb_ndbm_create},
78 #endif
79 { HDB_INTERFACE_VERSION, NULL, NULL, "keytab:", hdb_keytab_create},
80 #if defined(OPENLDAP) && !defined(OPENLDAP_MODULE)
81 { HDB_INTERFACE_VERSION, NULL, NULL, "ldap:", hdb_ldap_create},
82 { HDB_INTERFACE_VERSION, NULL, NULL, "ldapi:", hdb_ldapi_create},
83 #endif
84 #ifdef HAVE_SQLITE3
85 { HDB_INTERFACE_VERSION, NULL, NULL, "sqlite:", hdb_sqlite_create},
86 #endif
87 { 0, NULL, NULL, NULL, NULL}
90 #if HAVE_DB1 || HAVE_DB3
91 static struct hdb_method dbmetod =
92 { HDB_INTERFACE_VERSION, NULL, NULL, "", hdb_db_create };
93 #elif defined(HAVE_NDBM)
94 static struct hdb_method dbmetod =
95 { HDB_INTERFACE_VERSION, NULL, NULL, "", hdb_ndbm_create };
96 #endif
98 const Keys *
99 hdb_kvno2keys(krb5_context context,
100 const hdb_entry *e,
101 krb5_kvno kvno)
103 HDB_Ext_KeySet *hist_keys;
104 HDB_extension *extp;
105 size_t i;
107 if (kvno == 0)
108 return &e->keys;
110 extp = hdb_find_extension(e, choice_HDB_extension_data_hist_keys);
111 if (extp == NULL)
112 return 0;
114 hist_keys = &extp->data.u.hist_keys;
115 for (i = 0; i < hist_keys->len; i++) {
116 if (hist_keys->val[i].kvno == kvno)
117 return &hist_keys->val[i].keys;
120 return NULL;
123 krb5_error_code
124 hdb_next_enctype2key(krb5_context context,
125 const hdb_entry *e,
126 const Keys *keyset,
127 krb5_enctype enctype,
128 Key **key)
130 const Keys *keys = keyset ? keyset : &e->keys;
131 Key *k;
133 for (k = *key ? (*key) + 1 : keys->val; k < keys->val + keys->len; k++) {
134 if(k->key.keytype == enctype){
135 *key = k;
136 return 0;
139 krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
140 "No next enctype %d for hdb-entry",
141 (int)enctype);
142 return KRB5_PROG_ETYPE_NOSUPP; /* XXX */
145 krb5_error_code
146 hdb_enctype2key(krb5_context context,
147 hdb_entry *e,
148 const Keys *keyset,
149 krb5_enctype enctype,
150 Key **key)
152 *key = NULL;
153 return hdb_next_enctype2key(context, e, keyset, enctype, key);
156 void
157 hdb_free_key(Key *key)
159 memset(key->key.keyvalue.data,
161 key->key.keyvalue.length);
162 free_Key(key);
163 free(key);
167 krb5_error_code
168 hdb_lock(int fd, int operation)
170 int i, code = 0;
172 for(i = 0; i < 3; i++){
173 code = flock(fd, (operation == HDB_RLOCK ? LOCK_SH : LOCK_EX) | LOCK_NB);
174 if(code == 0 || errno != EWOULDBLOCK)
175 break;
176 sleep(1);
178 if(code == 0)
179 return 0;
180 if(errno == EWOULDBLOCK)
181 return HDB_ERR_DB_INUSE;
182 return HDB_ERR_CANT_LOCK_DB;
185 krb5_error_code
186 hdb_unlock(int fd)
188 int code;
189 code = flock(fd, LOCK_UN);
190 if(code)
191 return 4711 /* XXX */;
192 return 0;
195 void
196 hdb_free_entry(krb5_context context, hdb_entry_ex *ent)
198 Key *k;
199 size_t i;
201 if (ent->free_entry)
202 (*ent->free_entry)(context, ent);
204 for(i = 0; i < ent->entry.keys.len; i++) {
205 k = &ent->entry.keys.val[i];
207 memset (k->key.keyvalue.data, 0, k->key.keyvalue.length);
209 free_hdb_entry(&ent->entry);
212 krb5_error_code
213 hdb_foreach(krb5_context context,
214 HDB *db,
215 unsigned flags,
216 hdb_foreach_func_t func,
217 void *data)
219 krb5_error_code ret;
220 hdb_entry_ex entry;
221 ret = db->hdb_firstkey(context, db, flags, &entry);
222 if (ret == 0)
223 krb5_clear_error_message(context);
224 while(ret == 0){
225 ret = (*func)(context, db, &entry, data);
226 hdb_free_entry(context, &entry);
227 if(ret == 0)
228 ret = db->hdb_nextkey(context, db, flags, &entry);
230 if(ret == HDB_ERR_NOENTRY)
231 ret = 0;
232 return ret;
235 krb5_error_code
236 hdb_check_db_format(krb5_context context, HDB *db)
238 krb5_data tag;
239 krb5_data version;
240 krb5_error_code ret, ret2;
241 unsigned ver;
242 int foo;
244 ret = db->hdb_lock(context, db, HDB_RLOCK);
245 if (ret)
246 return ret;
248 tag.data = (void *)(intptr_t)HDB_DB_FORMAT_ENTRY;
249 tag.length = strlen(tag.data);
250 ret = (*db->hdb__get)(context, db, tag, &version);
251 ret2 = db->hdb_unlock(context, db);
252 if(ret)
253 return ret;
254 if (ret2)
255 return ret2;
256 foo = sscanf(version.data, "%u", &ver);
257 krb5_data_free (&version);
258 if (foo != 1)
259 return HDB_ERR_BADVERSION;
260 if(ver != HDB_DB_FORMAT)
261 return HDB_ERR_BADVERSION;
262 return 0;
265 krb5_error_code
266 hdb_init_db(krb5_context context, HDB *db)
268 krb5_error_code ret, ret2;
269 krb5_data tag;
270 krb5_data version;
271 char ver[32];
273 ret = hdb_check_db_format(context, db);
274 if(ret != HDB_ERR_NOENTRY)
275 return ret;
277 ret = db->hdb_lock(context, db, HDB_WLOCK);
278 if (ret)
279 return ret;
281 tag.data = (void *)(intptr_t)HDB_DB_FORMAT_ENTRY;
282 tag.length = strlen(tag.data);
283 snprintf(ver, sizeof(ver), "%u", HDB_DB_FORMAT);
284 version.data = ver;
285 version.length = strlen(version.data) + 1; /* zero terminated */
286 ret = (*db->hdb__put)(context, db, 0, tag, version);
287 ret2 = db->hdb_unlock(context, db);
288 if (ret) {
289 if (ret2)
290 krb5_clear_error_message(context);
291 return ret;
293 return ret2;
297 * find the relevant method for `filename', returning a pointer to the
298 * rest in `rest'.
299 * return NULL if there's no such method.
302 static const struct hdb_method *
303 find_method (const char *filename, const char **rest)
305 const struct hdb_method *h;
307 for (h = methods; h->prefix != NULL; ++h) {
308 if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0) {
309 *rest = filename + strlen(h->prefix);
310 return h;
313 #if defined(HAVE_DB1) || defined(HAVE_DB3) || defined(HAVE_NDBM)
314 if (strncmp(filename, "/", 1) == 0
315 || strncmp(filename, "./", 2) == 0
316 || strncmp(filename, "../", 3) == 0)
318 *rest = filename;
319 return &dbmetod;
321 #endif
323 return NULL;
326 krb5_error_code
327 hdb_list_builtin(krb5_context context, char **list)
329 const struct hdb_method *h;
330 size_t len = 0;
331 char *buf = NULL;
333 for (h = methods; h->prefix != NULL; ++h) {
334 if (h->prefix[0] == '\0')
335 continue;
336 len += strlen(h->prefix) + 2;
339 len += 1;
340 buf = malloc(len);
341 if (buf == NULL) {
342 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
343 return ENOMEM;
345 buf[0] = '\0';
347 for (h = methods; h->prefix != NULL; ++h) {
348 if (h != methods)
349 strlcat(buf, ", ", len);
350 strlcat(buf, h->prefix, len);
352 *list = buf;
353 return 0;
356 krb5_error_code
357 _hdb_keytab2hdb_entry(krb5_context context,
358 const krb5_keytab_entry *ktentry,
359 hdb_entry_ex *entry)
361 entry->entry.kvno = ktentry->vno;
362 entry->entry.created_by.time = ktentry->timestamp;
364 entry->entry.keys.val = calloc(1, sizeof(entry->entry.keys.val[0]));
365 if (entry->entry.keys.val == NULL)
366 return ENOMEM;
367 entry->entry.keys.len = 1;
369 entry->entry.keys.val[0].mkvno = NULL;
370 entry->entry.keys.val[0].salt = NULL;
372 return krb5_copy_keyblock_contents(context,
373 &ktentry->keyblock,
374 &entry->entry.keys.val[0].key);
378 * Create a handle for a Kerberos database
380 * Create a handle for a Kerberos database backend specified by a
381 * filename. Doesn't create a file if its doesn't exists, you have to
382 * use O_CREAT to tell the backend to create the file.
385 struct cb_s {
386 const char *residual;
387 const char *filename;
388 const struct hdb_method *h;
391 static krb5_error_code KRB5_LIB_CALL
392 callback(krb5_context context, const void *plug, void *plugctx, void *userctx)
394 const struct hdb_method *h = (const struct hdb_method *)plug;
395 struct cb_s *cb_ctx = (struct cb_s *)userctx;
397 if (strncmp(cb_ctx->filename, h->prefix, strlen(h->prefix)) == 0) {
398 cb_ctx->residual = cb_ctx->filename + strlen(h->prefix);
399 cb_ctx->h = h;
400 return 0;
402 return KRB5_PLUGIN_NO_HANDLE;
405 krb5_error_code
406 hdb_create(krb5_context context, HDB **db, const char *filename)
408 struct cb_s cb_ctx;
410 if (filename == NULL)
411 filename = HDB_DEFAULT_DB;
412 cb_ctx.h = find_method (filename, &cb_ctx.residual);
413 cb_ctx.filename = filename;
415 if (cb_ctx.h == NULL) {
416 (void)_krb5_plugin_run_f(context, "krb5", "hdb",
417 HDB_INTERFACE_VERSION, 0, &cb_ctx, callback);
419 if (cb_ctx.h == NULL)
420 krb5_errx(context, 1, "No database support for %s", cb_ctx.filename);
421 return (*cb_ctx.h->create)(context, db, cb_ctx.residual);