x
[heimdal.git] / lib / hdb / hdb.c
blob80b69e33efe35eb4d626d0e6d3f59b1f552fd516
1 /*
2 * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include "hdb_locl.h"
36 RCSID("$Id$");
38 #ifdef HAVE_DLFCN_H
39 #include <dlfcn.h>
40 #endif
42 struct hdb_method {
43 const char *prefix;
44 krb5_error_code (*create)(krb5_context, HDB **, const char *filename);
47 static struct hdb_method methods[] = {
48 #if HAVE_DB1 || HAVE_DB3
49 {"db:", hdb_db_create},
50 #endif
51 #if HAVE_NDBM
52 {"ndbm:", hdb_ndbm_create},
53 #endif
54 #if defined(OPENLDAP) && !defined(OPENLDAP_MODULE)
55 {"ldap:", hdb_ldap_create},
56 #endif
57 #if HAVE_DB1 || HAVE_DB3
58 {"", hdb_db_create},
59 #elif defined(HAVE_NDBM)
60 {"", hdb_ndbm_create},
61 #elif defined(OPENLDAP) && !defined(OPENLDAP_MODULE)
62 {"", hdb_ldap_create},
63 #endif
64 {NULL, NULL}
67 krb5_error_code
68 hdb_next_enctype2key(krb5_context context,
69 const hdb_entry *e,
70 krb5_enctype enctype,
71 Key **key)
73 Key *k;
75 for (k = *key ? (*key) + 1 : e->keys.val;
76 k < e->keys.val + e->keys.len;
77 k++)
78 if(k->key.keytype == enctype){
79 *key = k;
80 return 0;
82 return KRB5_PROG_ETYPE_NOSUPP; /* XXX */
85 krb5_error_code
86 hdb_enctype2key(krb5_context context,
87 hdb_entry *e,
88 krb5_enctype enctype,
89 Key **key)
91 *key = NULL;
92 return hdb_next_enctype2key(context, e, enctype, key);
95 void
96 hdb_free_key(Key *key)
98 memset(key->key.keyvalue.data,
100 key->key.keyvalue.length);
101 free_Key(key);
102 free(key);
106 krb5_error_code
107 hdb_lock(int fd, int operation)
109 int i, code = 0;
111 for(i = 0; i < 3; i++){
112 code = flock(fd, (operation == HDB_RLOCK ? LOCK_SH : LOCK_EX) | LOCK_NB);
113 if(code == 0 || errno != EWOULDBLOCK)
114 break;
115 sleep(1);
117 if(code == 0)
118 return 0;
119 if(errno == EWOULDBLOCK)
120 return HDB_ERR_DB_INUSE;
121 return HDB_ERR_CANT_LOCK_DB;
124 krb5_error_code
125 hdb_unlock(int fd)
127 int code;
128 code = flock(fd, LOCK_UN);
129 if(code)
130 return 4711 /* XXX */;
131 return 0;
134 void
135 hdb_free_entry(krb5_context context, hdb_entry *ent)
137 int i;
139 for(i = 0; i < ent->keys.len; ++i) {
140 Key *k = &ent->keys.val[i];
142 memset (k->key.keyvalue.data, 0, k->key.keyvalue.length);
144 free_hdb_entry(ent);
147 krb5_error_code
148 hdb_foreach(krb5_context context,
149 HDB *db,
150 unsigned flags,
151 hdb_foreach_func_t func,
152 void *data)
154 krb5_error_code ret;
155 hdb_entry entry;
156 ret = db->hdb_firstkey(context, db, flags, &entry);
157 while(ret == 0){
158 ret = (*func)(context, db, &entry, data);
159 hdb_free_entry(context, &entry);
160 if(ret == 0)
161 ret = db->hdb_nextkey(context, db, flags, &entry);
163 if(ret == HDB_ERR_NOENTRY)
164 ret = 0;
165 return ret;
168 krb5_error_code
169 hdb_check_db_format(krb5_context context, HDB *db)
171 krb5_data tag;
172 krb5_data version;
173 krb5_error_code ret;
174 unsigned ver;
175 int foo;
177 tag.data = HDB_DB_FORMAT_ENTRY;
178 tag.length = strlen(tag.data);
179 ret = (*db->hdb__get)(context, db, tag, &version);
180 if(ret)
181 return ret;
182 foo = sscanf(version.data, "%u", &ver);
183 krb5_data_free (&version);
184 if (foo != 1)
185 return HDB_ERR_BADVERSION;
186 if(ver != HDB_DB_FORMAT)
187 return HDB_ERR_BADVERSION;
188 return 0;
191 krb5_error_code
192 hdb_init_db(krb5_context context, HDB *db)
194 krb5_error_code ret;
195 krb5_data tag;
196 krb5_data version;
197 char ver[32];
199 ret = hdb_check_db_format(context, db);
200 if(ret != HDB_ERR_NOENTRY)
201 return ret;
203 tag.data = HDB_DB_FORMAT_ENTRY;
204 tag.length = strlen(tag.data);
205 snprintf(ver, sizeof(ver), "%u", HDB_DB_FORMAT);
206 version.data = ver;
207 version.length = strlen(version.data) + 1; /* zero terminated */
208 ret = (*db->hdb__put)(context, db, 0, tag, version);
209 return ret;
212 #ifdef HAVE_DLOPEN
215 * Load a dynamic backend from /usr/heimdal/lib/hdb_NAME.so,
216 * looking for the hdb_NAME_create symbol.
219 static const struct hdb_method *
220 find_dynamic_method (krb5_context context,
221 const char *filename,
222 const char **rest)
224 static struct hdb_method method;
225 struct hdb_so_method *mso;
226 char *prefix, *path, *symbol;
227 const char *p;
228 void *dl;
229 size_t len;
231 p = strchr(filename, ':');
233 /* if no prefix, don't know what module to load, just ignore it */
234 if (p == NULL)
235 return NULL;
237 len = p - filename;
238 *rest = filename + len + 1;
240 prefix = strndup(filename, len);
241 if (prefix == NULL)
242 krb5_errx(context, 1, "out of memory");
244 if (asprintf(&path, LIBDIR "/hdb_%s.so", prefix) == -1)
245 krb5_errx(context, 1, "out of memory");
247 #ifndef RTLD_NOW
248 #define RTLD_NOW 0
249 #endif
250 #ifndef RTLD_GLOBAL
251 #define RTLD_GLOBAL 0
252 #endif
254 dl = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
255 if (dl == NULL) {
256 krb5_warnx(context, "error trying to load dynamic module %s: %s\n",
257 path, dlerror());
258 free(prefix);
259 free(path);
260 return NULL;
263 if (asprintf(&symbol, "hdb_%s_interface", prefix) == -1)
264 krb5_errx(context, 1, "out of memory");
266 mso = dlsym(dl, symbol);
267 if (mso == NULL) {
268 krb5_warnx(context, "error finding symbol %s in %s: %s\n",
269 symbol, path, dlerror());
270 dlclose(dl);
271 free(symbol);
272 free(prefix);
273 free(path);
274 return NULL;
276 free(path);
277 free(symbol);
279 if (mso->version != HDB_INTERFACE_VERSION) {
280 krb5_warnx(context,
281 "error wrong version in shared module %s "
282 "version: %d should have been %d\n",
283 prefix, mso->version, HDB_INTERFACE_VERSION);
284 dlclose(dl);
285 free(prefix);
286 return NULL;
289 if (mso->create == NULL) {
290 krb5_errx(context, 1,
291 "no entry point function in shared mod %s ",
292 prefix);
293 dlclose(dl);
294 free(prefix);
295 return NULL;
298 method.create = mso->create;
299 method.prefix = prefix;
301 return &method;
303 #endif /* HAVE_DLOPEN */
306 * find the relevant method for `filename', returning a pointer to the
307 * rest in `rest'.
308 * return NULL if there's no such method.
311 static const struct hdb_method *
312 find_method (const char *filename, const char **rest)
314 const struct hdb_method *h;
316 for (h = methods; h->prefix != NULL; ++h)
317 if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0) {
318 *rest = filename + strlen(h->prefix);
319 return h;
321 return NULL;
324 krb5_error_code
325 hdb_list_builtin(krb5_context context, char **list)
327 const struct hdb_method *h;
328 size_t len = 0;
329 char *buf = NULL;
331 for (h = methods; h->prefix != NULL; ++h) {
332 if (h->prefix[0] == '\0')
333 continue;
334 len += strlen(h->prefix) + 2;
337 len += 1;
338 buf = malloc(len);
339 if (buf == NULL) {
340 krb5_set_error_string(context, "malloc: out of memory");
341 return ENOMEM;
343 buf[0] = '\0';
345 for (h = methods; h->prefix != NULL; ++h) {
346 if (h->prefix[0] == '\0')
347 continue;
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_create(krb5_context context, HDB **db, const char *filename)
359 const struct hdb_method *h;
360 const char *residual;
362 if(filename == NULL)
363 filename = HDB_DEFAULT_DB;
364 krb5_add_et_list(context, initialize_hdb_error_table_r);
365 h = find_method (filename, &residual);
366 #ifdef HAVE_DLOPEN
367 if (h == NULL)
368 h = find_dynamic_method (context, filename, &residual);
369 #endif
370 if (h == NULL)
371 krb5_errx(context, 1, "No database support! (hdb_create)");
372 return (*h->create)(context, db, residual);