Line data Source code
1 : /*
2 : * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3 : * (Royal Institute of Technology, Stockholm, Sweden).
4 : * All rights reserved.
5 : *
6 : * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7 : *
8 : * Redistribution and use in source and binary forms, with or without
9 : * modification, are permitted provided that the following conditions
10 : * are met:
11 : *
12 : * 1. Redistributions of source code must retain the above copyright
13 : * notice, this list of conditions and the following disclaimer.
14 : *
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.
18 : *
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.
22 : *
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.
34 : */
35 :
36 : #include "krb5_locl.h"
37 : #include "hdb_locl.h"
38 :
39 : #ifdef HAVE_DLFCN_H
40 : #include <dlfcn.h>
41 : #endif
42 :
43 : /*! @mainpage Heimdal database backend library
44 : *
45 : * @section intro Introduction
46 : *
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.
51 : *
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
58 : *
59 : *
60 : * The project web page: http://www.h5l.org/
61 : *
62 : */
63 :
64 : const int hdb_interface_version = HDB_INTERFACE_VERSION;
65 :
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, 1 /*is_file_based*/, 1 /*can_taste*/,
70 : "db:", hdb_db3_create},
71 : #elif HAVE_DB1
72 : { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "db:", hdb_db1_create},
73 : #endif
74 : #if HAVE_DB1
75 : { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "db1:", hdb_db1_create},
76 : #endif
77 : #if HAVE_DB3
78 : { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "db3:", hdb_db3_create},
79 : #endif
80 : #if HAVE_MITDB
81 : { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "mit-db:", hdb_mitdb_create},
82 : #endif
83 : #if HAVE_LMDB
84 : { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "mdb:", hdb_mdb_create},
85 : { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "lmdb:", hdb_mdb_create},
86 : #endif
87 : #if HAVE_NDBM
88 : { HDB_INTERFACE_VERSION, NULL, NULL, 1, 0, "ndbm:", hdb_ndbm_create},
89 : #endif
90 : #ifdef HAVE_SQLITE3
91 : { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "sqlite:", hdb_sqlite_create},
92 : #endif
93 : /* The keytab interface can't use its hdb_open() method to "taste" a DB */
94 : { HDB_INTERFACE_VERSION, NULL, NULL, 1, 0, "keytab:", hdb_keytab_create},
95 : /* The rest are not file-based */
96 : #if defined(OPENLDAP) && !defined(OPENLDAP_MODULE)
97 : { HDB_INTERFACE_VERSION, NULL, NULL, 0, 0, "ldap:", hdb_ldap_create},
98 : { HDB_INTERFACE_VERSION, NULL, NULL, 0, 0, "ldapi:", hdb_ldapi_create},
99 : #elif defined(OPENLDAP)
100 : { HDB_INTERFACE_VERSION, NULL, NULL, 0, 0, "ldap:", NULL},
101 : { HDB_INTERFACE_VERSION, NULL, NULL, 0, 0, "ldapi:", NULL},
102 : #endif
103 : { 0, NULL, NULL, 0, 0, NULL, NULL}
104 : };
105 :
106 : /**
107 : * Returns the Keys of `e' for `kvno', or NULL if not found. The Keys will
108 : * remain valid provided that the entry is not mutated.
109 : *
110 : * @param context Context
111 : * @param e The HDB entry
112 : * @param kvno The kvno
113 : *
114 : * @return A pointer to the Keys for the requested kvno.
115 : */
116 : const Keys *
117 82431 : hdb_kvno2keys(krb5_context context,
118 : const hdb_entry *e,
119 : krb5_kvno kvno)
120 : {
121 2828 : HDB_Ext_KeySet *hist_keys;
122 2828 : HDB_extension *extp;
123 2828 : size_t i;
124 :
125 82431 : if (kvno == 0 || e->kvno == kvno)
126 82121 : return &e->keys;
127 :
128 310 : extp = hdb_find_extension(e, choice_HDB_extension_data_hist_keys);
129 310 : if (extp == NULL)
130 159 : return 0;
131 :
132 151 : hist_keys = &extp->data.u.hist_keys;
133 164 : for (i = 0; i < hist_keys->len; i++) {
134 163 : if (hist_keys->val[i].kvno == kvno)
135 150 : return &hist_keys->val[i].keys;
136 : }
137 :
138 1 : return NULL;
139 : }
140 :
141 : /* Based on remove_HDB_Ext_KeySet(), generated by the ASN.1 compiler */
142 : static int
143 0 : dequeue_HDB_Ext_KeySet(HDB_Ext_KeySet *data, unsigned int element, hdb_keyset *ks)
144 : {
145 0 : if (element >= data->len) {
146 0 : ks->kvno = 0;
147 0 : ks->keys.len = 0;
148 0 : ks->keys.val = 0;
149 0 : ks->set_time = 0;
150 0 : return ASN1_OVERRUN;
151 : }
152 0 : *ks = data->val[element];
153 0 : data->len--;
154 : /* Swap instead of memmove()... changes the order of elements */
155 0 : if (element < data->len)
156 0 : data->val[element] = data->val[data->len];
157 0 : if (data->len == 0) {
158 0 : free(data->val);
159 0 : data->val = 0;
160 : }
161 0 : return 0;
162 : }
163 :
164 :
165 : /**
166 : * Removes from `e' and optionally outputs the keyset for the requested `kvno'.
167 : *
168 : * @param context Context
169 : * @param e The HDB entry
170 : * @param kvno The key version number
171 : * @param ks A pointer to a variable of type hdb_keyset (may be NULL)
172 : *
173 : * @return Zero on success, an error code otherwise.
174 : */
175 : krb5_error_code
176 0 : hdb_remove_keys(krb5_context context,
177 : hdb_entry *e,
178 : krb5_kvno kvno,
179 : hdb_keyset *ks)
180 : {
181 0 : HDB_Ext_KeySet *hist_keys;
182 0 : HDB_extension *extp;
183 0 : size_t i;
184 :
185 0 : if (kvno == 0 || e->kvno == kvno) {
186 0 : if (ks) {
187 0 : KerberosTime t;
188 :
189 0 : (void) hdb_entry_get_pw_change_time(e, &t);
190 0 : if (t) {
191 0 : if ((ks->set_time = malloc(sizeof(*ks->set_time))) == NULL)
192 0 : return krb5_enomem(context);
193 0 : *ks->set_time = t;
194 : }
195 0 : ks->kvno = e->kvno;
196 0 : ks->keys = e->keys;
197 0 : e->keys.len = 0;
198 0 : e->keys.val = NULL;
199 0 : e->kvno = 0;
200 : } else {
201 0 : free_Keys(&e->keys);
202 : }
203 0 : return 0;
204 : }
205 :
206 0 : if (ks) {
207 0 : ks->kvno = 0;
208 0 : ks->keys.len = 0;
209 0 : ks->keys.val = 0;
210 0 : ks->set_time = 0;
211 : }
212 :
213 0 : extp = hdb_find_extension(e, choice_HDB_extension_data_hist_keys);
214 0 : if (extp == NULL)
215 0 : return 0;
216 :
217 0 : hist_keys = &extp->data.u.hist_keys;
218 0 : for (i = 0; i < hist_keys->len; i++) {
219 0 : if (hist_keys->val[i].kvno != kvno)
220 0 : continue;
221 0 : if (ks)
222 0 : return dequeue_HDB_Ext_KeySet(hist_keys, i, ks);
223 0 : return remove_HDB_Ext_KeySet(hist_keys, i);
224 : }
225 0 : return HDB_ERR_NOENTRY;
226 : }
227 :
228 : /**
229 : * Removes from `e' and outputs all the base keys for virtual principal and/or
230 : * key derivation.
231 : *
232 : * @param context Context
233 : * @param e The HDB entry
234 : * @param ks A pointer to a variable of type HDB_Ext_KeySet
235 : * @param ckr A pointer to stable (copied) HDB_Ext_KeyRotation
236 : *
237 : * @return Zero on success, an error code otherwise.
238 : */
239 : krb5_error_code
240 0 : _hdb_remove_base_keys(krb5_context context,
241 : hdb_entry *e,
242 : HDB_Ext_KeySet *base_keys,
243 : const HDB_Ext_KeyRotation *ckr)
244 : {
245 0 : krb5_error_code ret = 0;
246 0 : size_t i, k;
247 :
248 0 : base_keys->len = 0;
249 0 : if ((base_keys->val = calloc(ckr->len, sizeof(base_keys->val[0]))) == NULL)
250 0 : ret = krb5_enomem(context);
251 :
252 0 : for (k = i = 0; ret == 0 && i < ckr->len; i++) {
253 0 : const KeyRotation *krp = &ckr->val[i];
254 :
255 : /*
256 : * WARNING: O(N * M) where M is number of keysets and N is the number
257 : * of base keysets.
258 : *
259 : * In practice N will never be > 3 because the ASN.1 module imposes
260 : * that as a constraint, and M will generally be the same as N, so this
261 : * will be O(1) after all.
262 : */
263 0 : ret = hdb_remove_keys(context, e, krp->base_key_kvno,
264 0 : &base_keys->val[k]);
265 0 : if (ret == 0)
266 0 : k++;
267 0 : else if (ret == HDB_ERR_NOENTRY)
268 0 : ret = 0;
269 : }
270 0 : if (ret == 0)
271 0 : base_keys->len = k;
272 : else
273 0 : free_HDB_Ext_KeySet(base_keys);
274 0 : return 0;
275 : }
276 :
277 : /**
278 : * Removes from `e' and outputs all the base keys for virtual principal and/or
279 : * key derivation.
280 : *
281 : * @param context Context
282 : * @param e The HDB entry
283 : * @param is_current_keyset Whether to make the keys the current keys for `e'
284 : * @param ks A pointer to an hdb_keyset containing the keys to set
285 : *
286 : * @return Zero on success, an error code otherwise.
287 : */
288 : krb5_error_code
289 0 : hdb_install_keyset(krb5_context context,
290 : hdb_entry *e,
291 : int is_current_keyset,
292 : const hdb_keyset *ks)
293 : {
294 0 : krb5_error_code ret = 0;
295 :
296 0 : if (is_current_keyset) {
297 0 : if (e->keys.len &&
298 0 : (ret = hdb_add_current_keys_to_history(context, e)))
299 0 : return ret;
300 0 : free_Keys(&e->keys);
301 0 : e->kvno = ks->kvno;
302 0 : if (ret == 0)
303 0 : ret = copy_Keys(&ks->keys, &e->keys);
304 0 : if (ret == 0 && ks->set_time)
305 0 : ret = hdb_entry_set_pw_change_time(context, e, *ks->set_time);
306 0 : return ret;
307 : }
308 0 : return hdb_add_history_keyset(context, e, ks);
309 : }
310 :
311 :
312 : krb5_error_code
313 406322 : hdb_next_enctype2key(krb5_context context,
314 : const hdb_entry *e,
315 : const Keys *keyset,
316 : krb5_enctype enctype,
317 : Key **key)
318 : {
319 406322 : const Keys *keys = keyset ? keyset : &e->keys;
320 10742 : Key *k;
321 :
322 535598 : for (k = *key ? (*key) + 1 : keys->val; k < keys->val + keys->len; k++) {
323 423105 : if(k->key.keytype == enctype){
324 304571 : *key = k;
325 304571 : return 0;
326 : }
327 : }
328 101751 : krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
329 : "No next enctype %d for hdb-entry",
330 : (int)enctype);
331 101751 : return KRB5_PROG_ETYPE_NOSUPP; /* XXX */
332 : }
333 :
334 : krb5_error_code
335 367462 : hdb_enctype2key(krb5_context context,
336 : const hdb_entry *e,
337 : const Keys *keyset,
338 : krb5_enctype enctype,
339 : Key **key)
340 : {
341 367462 : *key = NULL;
342 367462 : return hdb_next_enctype2key(context, e, keyset, enctype, key);
343 : }
344 :
345 : void
346 0 : hdb_free_key(Key *key)
347 : {
348 0 : memset_s(key->key.keyvalue.data,
349 : key->key.keyvalue.length,
350 : 0,
351 : key->key.keyvalue.length);
352 0 : free_Key(key);
353 0 : free(key);
354 0 : }
355 :
356 :
357 : krb5_error_code
358 0 : hdb_lock(int fd, int operation)
359 : {
360 0 : int i, code = 0;
361 :
362 0 : for(i = 0; i < 3; i++){
363 0 : code = flock(fd, (operation == HDB_RLOCK ? LOCK_SH : LOCK_EX) | LOCK_NB);
364 0 : if(code == 0 || errno != EWOULDBLOCK)
365 : break;
366 0 : sleep(1);
367 : }
368 0 : if(code == 0)
369 0 : return 0;
370 0 : if(errno == EWOULDBLOCK)
371 0 : return HDB_ERR_DB_INUSE;
372 0 : return HDB_ERR_CANT_LOCK_DB;
373 : }
374 :
375 : krb5_error_code
376 0 : hdb_unlock(int fd)
377 : {
378 0 : int code;
379 0 : code = flock(fd, LOCK_UN);
380 0 : if(code)
381 0 : return 4711 /* XXX */;
382 0 : return 0;
383 : }
384 :
385 : void
386 313571 : hdb_free_entry(krb5_context context, HDB *db, hdb_entry *ent)
387 : {
388 10142 : Key *k;
389 10142 : size_t i;
390 :
391 313571 : if (db && db->hdb_free_entry_context)
392 313571 : db->hdb_free_entry_context(context, db, ent);
393 :
394 842491 : for(i = 0; i < ent->keys.len; i++) {
395 528920 : k = &ent->keys.val[i];
396 :
397 528920 : memset_s(k->key.keyvalue.data,
398 : k->key.keyvalue.length,
399 : 0,
400 : k->key.keyvalue.length);
401 : }
402 313571 : free_HDB_entry(ent);
403 313571 : }
404 :
405 : krb5_error_code
406 0 : hdb_foreach(krb5_context context,
407 : HDB *db,
408 : unsigned flags,
409 : hdb_foreach_func_t func,
410 : void *data)
411 : {
412 0 : krb5_error_code ret;
413 0 : hdb_entry entry;
414 0 : ret = db->hdb_firstkey(context, db, flags, &entry);
415 0 : if (ret == 0)
416 0 : krb5_clear_error_message(context);
417 0 : while(ret == 0){
418 0 : ret = (*func)(context, db, &entry, data);
419 0 : hdb_free_entry(context, db, &entry);
420 0 : if(ret == 0)
421 0 : ret = db->hdb_nextkey(context, db, flags, &entry);
422 : }
423 0 : if(ret == HDB_ERR_NOENTRY)
424 0 : ret = 0;
425 0 : return ret;
426 : }
427 :
428 : krb5_error_code
429 0 : hdb_check_db_format(krb5_context context, HDB *db)
430 : {
431 0 : krb5_data tag;
432 0 : krb5_data version;
433 0 : krb5_error_code ret, ret2;
434 0 : unsigned ver;
435 0 : int foo;
436 :
437 0 : ret = db->hdb_lock(context, db, HDB_RLOCK);
438 0 : if (ret)
439 0 : return ret;
440 :
441 0 : tag.data = (void *)(intptr_t)HDB_DB_FORMAT_ENTRY;
442 0 : tag.length = strlen(tag.data);
443 0 : ret = (*db->hdb__get)(context, db, tag, &version);
444 0 : ret2 = db->hdb_unlock(context, db);
445 0 : if(ret)
446 0 : return ret;
447 0 : if (ret2)
448 0 : return ret2;
449 0 : foo = sscanf(version.data, "%u", &ver);
450 0 : krb5_data_free (&version);
451 0 : if (foo != 1)
452 0 : return HDB_ERR_BADVERSION;
453 0 : if(ver != HDB_DB_FORMAT)
454 0 : return HDB_ERR_BADVERSION;
455 0 : return 0;
456 : }
457 :
458 : krb5_error_code
459 0 : hdb_init_db(krb5_context context, HDB *db)
460 : {
461 0 : krb5_error_code ret, ret2;
462 0 : krb5_data tag;
463 0 : krb5_data version;
464 0 : char ver[32];
465 :
466 0 : ret = hdb_check_db_format(context, db);
467 0 : if(ret != HDB_ERR_NOENTRY)
468 0 : return ret;
469 :
470 0 : ret = db->hdb_lock(context, db, HDB_WLOCK);
471 0 : if (ret)
472 0 : return ret;
473 :
474 0 : tag.data = (void *)(intptr_t)HDB_DB_FORMAT_ENTRY;
475 0 : tag.length = strlen(tag.data);
476 0 : snprintf(ver, sizeof(ver), "%u", HDB_DB_FORMAT);
477 0 : version.data = ver;
478 0 : version.length = strlen(version.data) + 1; /* zero terminated */
479 0 : ret = (*db->hdb__put)(context, db, 0, tag, version);
480 0 : ret2 = db->hdb_unlock(context, db);
481 0 : if (ret) {
482 0 : if (ret2)
483 0 : krb5_clear_error_message(context);
484 0 : return ret;
485 : }
486 0 : return ret2;
487 : }
488 :
489 : /*
490 : * `default_dbmethod' is the last resort default.
491 : *
492 : * In hdb_create() we may try all the `methods[]' until one succeeds or all
493 : * fail.
494 : */
495 : #if defined(HAVE_LMDB)
496 : static struct hdb_method default_dbmethod =
497 : { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "", hdb_mdb_create };
498 : #elif defined(HAVE_DB3)
499 : static struct hdb_method default_dbmethod =
500 : { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "", hdb_db3_create };
501 : #elif defined(HAVE_DB1)
502 : static struct hdb_method default_dbmethod =
503 : { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "", hdb_db1_create };
504 : #elif defined(HAVE_NDBM)
505 : static struct hdb_method default_dbmethod =
506 : { HDB_INTERFACE_VERSION, NULL, NULL, 0, 1, "", hdb_ndbm_create };
507 : #else
508 : static struct hdb_method default_dbmethod =
509 : { 0, NULL, NULL, 0, 0, NULL, NULL};
510 : #endif
511 :
512 : static int
513 0 : is_pathish(const char *s)
514 : {
515 0 : if (s[0] == '/' ||
516 0 : strncmp(s, "./", sizeof("./") - 1) == 0 ||
517 0 : strncmp(s, "../", sizeof("../") - 1) == 0)
518 0 : return 1;
519 : #ifdef WIN32
520 : if (s[0] == '\\' || (isalpha((unsigned char)s[0]) && s[0] == ':') ||
521 : strncmp(s, ".\\", sizeof(".\\") - 1) == 0 ||
522 : strncmp(s, "\\\\", sizeof("\\\\") - 1) == 0)
523 : return 1;
524 : #endif
525 0 : return 0;
526 : }
527 :
528 : static const struct hdb_method *
529 68 : has_method_prefix(const char *filename)
530 : {
531 0 : const struct hdb_method *h;
532 :
533 136 : for (h = methods; h->prefix != NULL; ++h)
534 68 : if (strncmp(filename, h->prefix, strlen(h->prefix)) == 0)
535 0 : return h;
536 68 : return NULL;
537 : }
538 :
539 : /*
540 : * find the relevant method for `filename', returning a pointer to the
541 : * rest in `rest'.
542 : * return NULL if there's no such method.
543 : */
544 :
545 : static const struct hdb_method *
546 68 : find_method(const char *filename, const char **rest)
547 : {
548 68 : const struct hdb_method *h = has_method_prefix(filename);
549 :
550 68 : *rest = h ? filename + strlen(h->prefix) : filename;
551 68 : return h;
552 : }
553 :
554 : struct cb_s {
555 : const char *residual;
556 : const char *filename;
557 : const struct hdb_method *h;
558 : };
559 :
560 : static krb5_error_code KRB5_LIB_CALL
561 68 : callback(krb5_context context, const void *plug, void *plugctx, void *userctx)
562 : {
563 68 : const struct hdb_method *h = (const struct hdb_method *)plug;
564 68 : struct cb_s *cb_ctx = (struct cb_s *)userctx;
565 :
566 68 : if (strncmp(cb_ctx->filename, h->prefix, strlen(h->prefix)) == 0) {
567 68 : cb_ctx->residual = cb_ctx->filename + strlen(h->prefix) + 1;
568 68 : cb_ctx->h = h;
569 68 : return 0;
570 : }
571 0 : return KRB5_PLUGIN_NO_HANDLE;
572 : }
573 :
574 : static char *
575 68 : make_sym(const char *prefix)
576 : {
577 0 : char *s, *sym;
578 :
579 68 : errno = 0;
580 68 : if (prefix == NULL || prefix[0] == '\0')
581 0 : return NULL;
582 68 : if ((s = strdup(prefix)) == NULL)
583 0 : return NULL;
584 68 : if (strchr(s, ':') != NULL)
585 68 : *strchr(s, ':') = '\0';
586 68 : if (asprintf(&sym, "hdb_%s_interface", s) == -1)
587 0 : sym = NULL;
588 68 : free(s);
589 68 : return sym;
590 : }
591 :
592 : static const char *hdb_plugin_deps[] = { "hdb", "krb5", NULL };
593 :
594 : krb5_error_code
595 0 : hdb_list_builtin(krb5_context context, char **list)
596 : {
597 0 : const struct hdb_method *h;
598 0 : size_t len = 0;
599 0 : char *buf = NULL;
600 :
601 0 : for (h = methods; h->prefix != NULL; ++h) {
602 0 : if (h->prefix[0] == '\0')
603 0 : continue;
604 0 : len += strlen(h->prefix) + 2;
605 : }
606 :
607 0 : len += 1;
608 0 : buf = malloc(len);
609 0 : if (buf == NULL) {
610 0 : return krb5_enomem(context);
611 : }
612 0 : buf[0] = '\0';
613 :
614 0 : for (h = methods; h->prefix != NULL; ++h) {
615 0 : if (h->create == NULL) {
616 0 : struct cb_s cb_ctx;
617 0 : char *f;
618 0 : struct heim_plugin_data hdb_plugin_data;
619 :
620 0 : hdb_plugin_data.module = "krb5";
621 0 : hdb_plugin_data.min_version = HDB_INTERFACE_VERSION;
622 0 : hdb_plugin_data.deps = hdb_plugin_deps;
623 0 : hdb_plugin_data.get_instance = hdb_get_instance;
624 :
625 : /* Try loading the plugin */
626 0 : if (asprintf(&f, "%sfoo", h->prefix) == -1)
627 0 : f = NULL;
628 0 : if ((hdb_plugin_data.name = make_sym(h->prefix)) == NULL) {
629 0 : free(buf);
630 0 : free(f);
631 0 : return krb5_enomem(context);
632 : }
633 0 : cb_ctx.filename = f;
634 0 : cb_ctx.residual = NULL;
635 0 : cb_ctx.h = NULL;
636 0 : (void)_krb5_plugin_run_f(context, &hdb_plugin_data, 0,
637 : &cb_ctx, callback);
638 0 : free(f);
639 0 : free(rk_UNCONST(hdb_plugin_data.name));
640 0 : if (cb_ctx.h == NULL || cb_ctx.h->create == NULL)
641 0 : continue;
642 : }
643 0 : if (h != methods)
644 0 : strlcat(buf, ", ", len);
645 0 : strlcat(buf, h->prefix, len);
646 : }
647 0 : *list = buf;
648 0 : return 0;
649 : }
650 :
651 : krb5_error_code
652 0 : _hdb_keytab2hdb_entry(krb5_context context,
653 : const krb5_keytab_entry *ktentry,
654 : hdb_entry *entry)
655 : {
656 0 : entry->kvno = ktentry->vno;
657 0 : entry->created_by.time = ktentry->timestamp;
658 :
659 0 : entry->keys.val = calloc(1, sizeof(entry->keys.val[0]));
660 0 : if (entry->keys.val == NULL)
661 0 : return ENOMEM;
662 0 : entry->keys.len = 1;
663 :
664 0 : entry->keys.val[0].mkvno = NULL;
665 0 : entry->keys.val[0].salt = NULL;
666 :
667 0 : return krb5_copy_keyblock_contents(context,
668 : &ktentry->keyblock,
669 0 : &entry->keys.val[0].key);
670 : }
671 :
672 : static krb5_error_code
673 68 : load_config(krb5_context context, HDB *db)
674 : {
675 68 : db->enable_virtual_hostbased_princs =
676 68 : krb5_config_get_bool_default(context, NULL, FALSE, "hdb",
677 : "enable_virtual_hostbased_princs",
678 : NULL);
679 68 : db->virtual_hostbased_princ_ndots =
680 68 : krb5_config_get_int_default(context, NULL, 1, "hdb",
681 : "virtual_hostbased_princ_mindots",
682 : NULL);
683 68 : db->virtual_hostbased_princ_maxdots =
684 68 : krb5_config_get_int_default(context, NULL, 0, "hdb",
685 : "virtual_hostbased_princ_maxdots",
686 : NULL);
687 68 : db->new_service_key_delay =
688 68 : krb5_config_get_time_default(context, NULL, 0, "hdb",
689 : "new_service_key_delay", NULL);
690 : /*
691 : * XXX Needs freeing in the HDB backends because we don't have a
692 : * first-class hdb_close() :(
693 : */
694 68 : db->virtual_hostbased_princ_svcs =
695 68 : krb5_config_get_strings(context, NULL, "hdb",
696 : "virtual_hostbased_princ_svcs", NULL);
697 : /* Check for ENOMEM */
698 68 : if (db->virtual_hostbased_princ_svcs == NULL
699 68 : && krb5_config_get_string(context, NULL, "hdb",
700 : "virtual_hostbased_princ_svcs", NULL)) {
701 0 : return krb5_enomem(context);
702 : }
703 68 : return 0;
704 : }
705 :
706 : /**
707 : * Create a handle for a Kerberos database
708 : *
709 : * Create a handle for a Kerberos database backend specified by a
710 : * filename. Doesn't actually create or even open an HDB file(s);
711 : * you have to call the hdb_open() open method of the resulting HDB
712 : * to open the database, and you have to use O_CREAT to create it.
713 : *
714 : * If `filename' does not have a backend type prefix, all file-based
715 : * backends will be tried until one succeeds or all fail, and if the
716 : * HDB exists for some backend, that will be used. A build-time
717 : * default backend type will be used if the `filename' does not exist.
718 : *
719 : * Note that the actual filename may have a suffix added, such as
720 : * ".db". Also, for backends such as "ldap:" and "ldapi:" the
721 : * `filename' is more like a URI.
722 : *
723 : * @param [in] context Context
724 : * @param [out] db HDB handle output
725 : * @param [in] filename The name of the HDB
726 : *
727 : * @return Zero on success else a krb5 error code.
728 : */
729 :
730 : krb5_error_code
731 68 : hdb_create(krb5_context context, HDB **db, const char *filename)
732 : {
733 68 : krb5_error_code ret = ENOTSUP;
734 0 : struct cb_s cb_ctx;
735 :
736 68 : *db = NULL;
737 68 : if (filename == NULL)
738 0 : filename = hdb_default_db(context);
739 :
740 68 : cb_ctx.h = find_method(filename, &cb_ctx.residual);
741 68 : cb_ctx.filename = filename;
742 :
743 68 : if (cb_ctx.h == NULL || cb_ctx.h->create == NULL) {
744 0 : struct heim_plugin_data hdb_plugin_data;
745 :
746 : /*
747 : * `filename' does not start with a known HDB backend prefix.
748 : *
749 : * Try plugins.
750 : */
751 68 : hdb_plugin_data.module = "krb5";
752 68 : hdb_plugin_data.min_version = HDB_INTERFACE_VERSION;
753 68 : hdb_plugin_data.deps = hdb_plugin_deps;
754 68 : hdb_plugin_data.get_instance = hdb_get_instance;
755 :
756 68 : if ((hdb_plugin_data.name = make_sym(filename)) == NULL)
757 0 : return krb5_enomem(context);
758 :
759 68 : (void)_krb5_plugin_run_f(context, &hdb_plugin_data, 0 /* flags */,
760 : &cb_ctx, callback);
761 :
762 68 : free(rk_UNCONST(hdb_plugin_data.name));
763 : }
764 :
765 68 : if (cb_ctx.h == NULL || cb_ctx.h->create == NULL) {
766 0 : int pathish = is_pathish(filename);
767 : /*
768 : * `filename' does not start with a known HDB backend prefix and it
769 : * wasn't handled by any plugin.
770 : *
771 : * If it's "filename-ish", try all builtin HDB backends that are
772 : * local-file-ish, but use hdb_open() to see if the HDB exists and stop
773 : * when a backend is found for which the HDB exists.
774 : */
775 0 : if (!pathish) {
776 0 : krb5_set_error_message(context, ret = ENOTSUP,
777 : "No database support for %s",
778 : cb_ctx.filename);
779 0 : return ret;
780 : }
781 0 : for (cb_ctx.h = methods; cb_ctx.h->prefix != NULL; cb_ctx.h++) {
782 0 : if (cb_ctx.h->is_file_based)
783 0 : continue;
784 0 : if (!cb_ctx.h->can_taste)
785 0 : continue;
786 : /* Taste the file */
787 0 : ret = (*cb_ctx.h->create)(context, db, filename);
788 0 : if (ret == 0)
789 0 : ret = (*db)->hdb_open(context, *db, O_RDONLY, 0);
790 0 : if (ret == 0) {
791 0 : (void) (*db)->hdb_close(context, *db);
792 0 : break;
793 : }
794 0 : if (*db)
795 0 : (*db)->hdb_destroy(context, *db);
796 0 : *db = NULL;
797 : }
798 0 : if (cb_ctx.h->prefix == NULL)
799 0 : cb_ctx.h = NULL;
800 : }
801 : #ifdef HDB_DEFAULT_DB_TYPE
802 : if (cb_ctx.h == NULL) {
803 : /*
804 : * If still we've not picked a backend, use a build configuration time
805 : * default.
806 : */
807 : for (cb_ctx.h = methods; cb_ctx.h->prefix != NULL; cb_ctx.h++)
808 : if (strcmp(cb_ctx.h->prefix, HDB_DEFAULT_DB_TYPE) == 0)
809 : break;
810 : if (cb_ctx.h->prefix == NULL)
811 : cb_ctx.h = NULL;
812 : }
813 : #endif
814 68 : if (cb_ctx.h == NULL)
815 : /* Last resort default */
816 0 : cb_ctx.h = &default_dbmethod;
817 68 : if (cb_ctx.h->prefix == NULL) {
818 0 : krb5_set_error_message(context, ENOTSUP,
819 : "Could not determine default DB backend for %s",
820 : filename);
821 0 : return ENOTSUP;
822 : }
823 68 : if (!*db) {
824 68 : ret = (*cb_ctx.h->create)(context, db, cb_ctx.residual);
825 68 : if (ret == 0)
826 68 : (*db)->hdb_method_name = cb_ctx.h->prefix;
827 : }
828 68 : if (ret == 0 && *db)
829 68 : ret = load_config(context, *db);
830 68 : if (ret && *db) {
831 0 : (*db)->hdb_destroy(context, *db);
832 0 : *db = NULL;
833 : }
834 68 : return ret;
835 : }
836 :
837 : uintptr_t KRB5_CALLCONV
838 0 : hdb_get_instance(const char *libname)
839 : {
840 0 : static const char *instance = "libhdb";
841 :
842 0 : if (strcmp(libname, "hdb") == 0)
843 0 : return (uintptr_t)instance;
844 0 : else if (strcmp(libname, "krb5") == 0)
845 0 : return krb5_get_instance(libname);
846 :
847 0 : return 0;
848 : }
|