/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- hdb_next_enctype2key
- hdb_enctype2key
- hdb_free_key
- hdb_lock
- hdb_unlock
- hdb_free_entry
- hdb_foreach
- hdb_check_db_format
- hdb_init_db
- find_dynamic_method
- find_method
- hdb_list_builtin
- hdb_create
1 /*
2 * Copyright (c) 1997 - 2007 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 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37
38 #include "krb5.h"
39 #include "krb5_locl.h"
40 #include "hdb_locl.h"
41 RCSID("$Id$");
42
43 #ifdef HAVE_DLFCN_H
44 #include <dlfcn.h>
45 #endif
46
47 static struct hdb_method methods[] = {
48 #if HAVE_DB1 || HAVE_DB3
49 {HDB_INTERFACE_VERSION, "db:", hdb_db_create},
50 #endif
51 #if HAVE_NDBM
52 {HDB_INTERFACE_VERSION, "ndbm:", hdb_ndbm_create},
53 #endif
54 #if defined(OPENLDAP) && !defined(OPENLDAP_MODULE)
55 {HDB_INTERFACE_VERSION, "ldap:", hdb_ldap_create},
56 {HDB_INTERFACE_VERSION, "ldapi:", hdb_ldapi_create},
57 #endif
58 {0, NULL, NULL}
59 };
60
61 #if HAVE_DB1 || HAVE_DB3
62 static struct hdb_method dbmetod = {"", hdb_db_create };
63 #elif defined(HAVE_NDBM)
64 static struct hdb_method dbmetod = {"", hdb_ndbm_create };
65 #endif
66
67
68 krb5_error_code
69 hdb_next_enctype2key(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
70 const hdb_entry *e,
71 krb5_enctype enctype,
72 Key **key)
73 {
74 Key *k;
75
76 for (k = *key ? (*key) + 1 : e->keys.val;
77 k < e->keys.val + e->keys.len;
78 k++)
79 {
80 if(k->key.keytype == enctype){
81 *key = k;
82 return 0;
83 }
84 }
85 krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
86 "No next enctype %d for hdb-entry",
87 (int)enctype);
88 return KRB5_PROG_ETYPE_NOSUPP; /* XXX */
89 }
90
91 krb5_error_code
92 hdb_enctype2key(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
93 hdb_entry *e,
94 krb5_enctype enctype,
95 Key **key)
96 {
97 *key = NULL;
98 return hdb_next_enctype2key(context, e, enctype, key);
99 }
100
101 void
102 hdb_free_key(Key *key)
/* [<][>][^][v][top][bottom][index][help] */
103 {
104 memset(key->key.keyvalue.data,
105 0,
106 key->key.keyvalue.length);
107 free_Key(key);
108 free(key);
109 }
110
111
112 krb5_error_code
113 hdb_lock(int fd, int operation)
/* [<][>][^][v][top][bottom][index][help] */
114 {
115 int i, code = 0;
116
117 for(i = 0; i < 3; i++){
118 code = flock(fd, (operation == HDB_RLOCK ? LOCK_SH : LOCK_EX) | LOCK_NB);
119 if(code == 0 || errno != EWOULDBLOCK)
120 break;
121 sleep(1);
122 }
123 if(code == 0)
124 return 0;
125 if(errno == EWOULDBLOCK)
126 return HDB_ERR_DB_INUSE;
127 return HDB_ERR_CANT_LOCK_DB;
128 }
129
130 krb5_error_code
131 hdb_unlock(int fd)
/* [<][>][^][v][top][bottom][index][help] */
132 {
133 int code;
134 code = flock(fd, LOCK_UN);
135 if(code)
136 return 4711 /* XXX */;
137 return 0;
138 }
139
140 void
141 hdb_free_entry(krb5_context context, hdb_entry_ex *ent)
/* [<][>][^][v][top][bottom][index][help] */
142 {
143 int i;
144
145 if (ent->free_entry)
146 (*ent->free_entry)(context, ent);
147
148 for(i = 0; i < ent->entry.keys.len; ++i) {
149 Key *k = &ent->entry.keys.val[i];
150
151 memset (k->key.keyvalue.data, 0, k->key.keyvalue.length);
152 }
153 free_hdb_entry(&ent->entry);
154 }
155
156 krb5_error_code
157 hdb_foreach(krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
158 HDB *db,
159 unsigned flags,
160 hdb_foreach_func_t func,
161 void *data)
162 {
163 krb5_error_code ret;
164 hdb_entry_ex entry;
165 ret = db->hdb_firstkey(context, db, flags, &entry);
166 if (ret == 0)
167 krb5_clear_error_message(context);
168 while(ret == 0){
169 ret = (*func)(context, db, &entry, data);
170 hdb_free_entry(context, &entry);
171 if(ret == 0)
172 ret = db->hdb_nextkey(context, db, flags, &entry);
173 }
174 if(ret == HDB_ERR_NOENTRY)
175 ret = 0;
176 return ret;
177 }
178
179 krb5_error_code
180 hdb_check_db_format(krb5_context context, HDB *db)
/* [<][>][^][v][top][bottom][index][help] */
181 {
182 krb5_data tag;
183 krb5_data version;
184 krb5_error_code ret, ret2;
185 unsigned ver;
186 int foo;
187
188 ret = db->hdb_lock(context, db, HDB_RLOCK);
189 if (ret)
190 return ret;
191
192 tag.data = HDB_DB_FORMAT_ENTRY;
193 tag.length = strlen(tag.data);
194 ret = (*db->hdb__get)(context, db, tag, &version);
195 ret2 = db->hdb_unlock(context, db);
196 if(ret)
197 return ret;
198 if (ret2)
199 return ret2;
200 foo = sscanf(version.data, "%u", &ver);
201 krb5_data_free (&version);
202 if (foo != 1)
203 return HDB_ERR_BADVERSION;
204 if(ver != HDB_DB_FORMAT)
205 return HDB_ERR_BADVERSION;
206 return 0;
207 }
208
209 krb5_error_code
210 hdb_init_db(krb5_context context, HDB *db)
/* [<][>][^][v][top][bottom][index][help] */
211 {
212 krb5_error_code ret, ret2;
213 krb5_data tag;
214 krb5_data version;
215 char ver[32];
216
217 ret = hdb_check_db_format(context, db);
218 if(ret != HDB_ERR_NOENTRY)
219 return ret;
220
221 ret = db->hdb_lock(context, db, HDB_WLOCK);
222 if (ret)
223 return ret;
224
225 tag.data = HDB_DB_FORMAT_ENTRY;
226 tag.length = strlen(tag.data);
227 snprintf(ver, sizeof(ver), "%u", HDB_DB_FORMAT);
228 version.data = ver;
229 version.length = strlen(version.data) + 1; /* zero terminated */
230 ret = (*db->hdb__put)(context, db, 0, tag, version);
231 ret2 = db->hdb_unlock(context, db);
232 if (ret) {
233 if (ret2)
234 krb5_clear_error_message(context);
235 return ret;
236 }
237 return ret2;
238 }
239
240 #ifdef HAVE_DLOPEN
241
242 /*
243 * Load a dynamic backend from /usr/heimdal/lib/hdb_NAME.so,
244 * looking for the hdb_NAME_create symbol.
245 */
246
247 static const struct hdb_method *
248 find_dynamic_method (krb5_context context,
/* [<][>][^][v][top][bottom][index][help] */
249 const char *filename,
250 const char **rest)
251 {
252 static struct hdb_method method;
253 struct hdb_so_method *mso;
254 char *prefix, *path, *symbol;
255 const char *p;
256 void *dl;
257 size_t len;
258
259 p = strchr(filename, ':');
260
261 /* if no prefix, don't know what module to load, just ignore it */
262 if (p == NULL)
263 return NULL;
264
265 len = p - filename;
266 *rest = filename + len + 1;
267
268 prefix = strndup(filename, len);
269 if (prefix == NULL)
270 krb5_errx(context, 1, "out of memory");
271
272 if (asprintf(&path, LIBDIR "/hdb_%s.so", prefix) == -1)
273 krb5_errx(context, 1, "out of memory");
274
275 #ifndef RTLD_NOW
276 #define RTLD_NOW 0
277 #endif
278 #ifndef RTLD_GLOBAL
279 #define RTLD_GLOBAL 0
280 #endif
281
282 dl = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
283 if (dl == NULL) {
284 krb5_warnx(context, "error trying to load dynamic module %s: %s\n",
285 path, dlerror());
286 free(prefix);
287 free(path);
288 return NULL;
289 }
290
291 if (asprintf(&symbol, "hdb_%s_interface", prefix) == -1)
292 krb5_errx(context, 1, "out of memory");
293
294 mso = dlsym(dl, symbol);
295 if (mso == NULL) {
296 krb5_warnx(context, "error finding symbol %s in %s: %s\n",
297 symbol, path, dlerror());
298 dlclose(dl);
299 free(symbol);
300 free(prefix);
301 free(path);
302 return NULL;
303 }
304 free(path);
305 free(symbol);
306
307 if (mso->version != HDB_INTERFACE_VERSION) {
308 krb5_warnx(context,
309 "error wrong version in shared module %s "
310 "version: %d should have been %d\n",
311 prefix, mso->version, HDB_INTERFACE_VERSION);
312 dlclose(dl);
313 free(prefix);
314 return NULL;
315 }
316
317 if (mso->create == NULL) {
318 krb5_errx(context, 1,
319 "no entry point function in shared mod %s ",
320 prefix);
321 dlclose(dl);
322 free(prefix);
323 return NULL;
324 }
325
326 method.create = mso->create;
327 method.prefix = prefix;
328
329 return &method;
330 }
331 #endif /* HAVE_DLOPEN */
332
333 /*
334 * find the relevant method for `filename', returning a pointer to the
335 * rest in `rest'.
336 * return NULL if there's no such method.
337 */
338
339 static const struct hdb_method *
340 find_method (const char *filename, const char **rest)
/* [<][>][^][v][top][bottom][index][help] */
341 {
342 const struct hdb_method *h;
343
344 for (h = methods; h->prefix != NULL; ++h) {
345 if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0) {
346 *rest = filename + strlen(h->prefix);
347 return h;
348 }
349 }
350 #if defined(HAVE_DB1) || defined(HAVE_DB3) || defined(HAVE_NDBM)
351 if (strncmp(filename, "/", 1) == 0
352 || strncmp(filename, "./", 2) == 0
353 || strncmp(filename, "../", 3) == 0)
354 {
355 *rest = filename;
356 return &dbmetod;
357 }
358 #endif
359
360 return NULL;
361 }
362
363 krb5_error_code
364 hdb_list_builtin(krb5_context context, char **list)
/* [<][>][^][v][top][bottom][index][help] */
365 {
366 const struct hdb_method *h;
367 size_t len = 0;
368 char *buf = NULL;
369
370 for (h = methods; h->prefix != NULL; ++h) {
371 if (h->prefix[0] == '\0')
372 continue;
373 len += strlen(h->prefix) + 2;
374 }
375
376 len += 1;
377 buf = malloc(len);
378 if (buf == NULL) {
379 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
380 return ENOMEM;
381 }
382 buf[0] = '\0';
383
384 for (h = methods; h->prefix != NULL; ++h) {
385 if (h != methods)
386 strlcat(buf, ", ", len);
387 strlcat(buf, h->prefix, len);
388 }
389 *list = buf;
390 return 0;
391 }
392
393 krb5_error_code
394 hdb_create(krb5_context context, HDB **db, const char *filename)
/* [<][>][^][v][top][bottom][index][help] */
395 {
396 const struct hdb_method *h;
397 const char *residual;
398 krb5_error_code ret;
399 struct krb5_plugin *list = NULL, *e;
400
401 if(filename == NULL)
402 filename = HDB_DEFAULT_DB;
403 krb5_add_et_list(context, initialize_hdb_error_table_r);
404 h = find_method (filename, &residual);
405
406 if (h == NULL) {
407 ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, "hdb", &list);
408 if(ret == 0 && list != NULL) {
409 for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
410 h = _krb5_plugin_get_symbol(e);
411 if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0
412 && h->interface_version == HDB_INTERFACE_VERSION) {
413 residual = filename + strlen(h->prefix);
414 break;
415 }
416 }
417 if (e == NULL) {
418 h = NULL;
419 _krb5_plugin_free(list);
420 }
421 }
422 }
423
424 #ifdef HAVE_DLOPEN
425 if (h == NULL)
426 h = find_dynamic_method (context, filename, &residual);
427 #endif
428 if (h == NULL)
429 krb5_errx(context, 1, "No database support for %s", filename);
430 return (*h->create)(context, db, residual);
431 }