diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
commit | 8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch) | |
tree | 4099e8021376c7d8c05bdf8503093d80e9c7bad0 /third_party/heimdal/lib/hdb/hdb.c | |
parent | Initial commit. (diff) | |
download | samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip |
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/heimdal/lib/hdb/hdb.c')
-rw-r--r-- | third_party/heimdal/lib/hdb/hdb.c | 848 |
1 files changed, 848 insertions, 0 deletions
diff --git a/third_party/heimdal/lib/hdb/hdb.c b/third_party/heimdal/lib/hdb/hdb.c new file mode 100644 index 0000000..864b4f6 --- /dev/null +++ b/third_party/heimdal/lib/hdb/hdb.c @@ -0,0 +1,848 @@ +/* + * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Portions Copyright (c) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" +#include "hdb_locl.h" + +#ifdef HAVE_DLFCN_H +#include <dlfcn.h> +#endif + +/*! @mainpage Heimdal database backend library + * + * @section intro Introduction + * + * Heimdal libhdb library provides the backend support for Heimdal kdc + * and kadmind. Its here where plugins for diffrent database engines + * can be pluged in and extend support for here Heimdal get the + * principal and policy data from. + * + * Example of Heimdal backend are: + * - Berkeley DB 1.85 + * - Berkeley DB 3.0 + * - Berkeley DB 4.0 + * - New Berkeley DB + * - LDAP + * + * + * The project web page: http://www.h5l.org/ + * + */ + +const int hdb_interface_version = HDB_INTERFACE_VERSION; + +static struct hdb_method methods[] = { + /* "db:" should be db3 if we have db3, or db1 if we have db1 */ +#if HAVE_DB3 + { HDB_INTERFACE_VERSION, NULL, NULL, 1 /*is_file_based*/, 1 /*can_taste*/, + "db:", hdb_db3_create}, +#elif HAVE_DB1 + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "db:", hdb_db1_create}, +#endif +#if HAVE_DB1 + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "db1:", hdb_db1_create}, +#endif +#if HAVE_DB3 + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "db3:", hdb_db3_create}, +#endif +#if HAVE_DB1 + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "mit-db:", hdb_mitdb_create}, +#endif +#if HAVE_LMDB + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "mdb:", hdb_mdb_create}, + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "lmdb:", hdb_mdb_create}, +#endif +#if HAVE_NDBM + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 0, "ndbm:", hdb_ndbm_create}, +#endif +#ifdef HAVE_SQLITE3 + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "sqlite:", hdb_sqlite_create}, +#endif + /* The keytab interface can't use its hdb_open() method to "taste" a DB */ + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 0, "keytab:", hdb_keytab_create}, + /* The rest are not file-based */ +#if defined(OPENLDAP) && !defined(OPENLDAP_MODULE) + { HDB_INTERFACE_VERSION, NULL, NULL, 0, 0, "ldap:", hdb_ldap_create}, + { HDB_INTERFACE_VERSION, NULL, NULL, 0, 0, "ldapi:", hdb_ldapi_create}, +#elif defined(OPENLDAP) + { HDB_INTERFACE_VERSION, NULL, NULL, 0, 0, "ldap:", NULL}, + { HDB_INTERFACE_VERSION, NULL, NULL, 0, 0, "ldapi:", NULL}, +#endif + { 0, NULL, NULL, 0, 0, NULL, NULL} +}; + +/** + * Returns the Keys of `e' for `kvno', or NULL if not found. The Keys will + * remain valid provided that the entry is not mutated. + * + * @param context Context + * @param e The HDB entry + * @param kvno The kvno + * + * @return A pointer to the Keys for the requested kvno. + */ +const Keys * +hdb_kvno2keys(krb5_context context, + const hdb_entry *e, + krb5_kvno kvno) +{ + HDB_Ext_KeySet *hist_keys; + HDB_extension *extp; + size_t i; + + if (kvno == 0 || e->kvno == kvno) + return &e->keys; + + extp = hdb_find_extension(e, choice_HDB_extension_data_hist_keys); + if (extp == NULL) + return 0; + + hist_keys = &extp->data.u.hist_keys; + for (i = 0; i < hist_keys->len; i++) { + if (hist_keys->val[i].kvno == kvno) + return &hist_keys->val[i].keys; + } + + return NULL; +} + +/* Based on remove_HDB_Ext_KeySet(), generated by the ASN.1 compiler */ +static int +dequeue_HDB_Ext_KeySet(HDB_Ext_KeySet *data, unsigned int element, hdb_keyset *ks) +{ + if (element >= data->len) { + ks->kvno = 0; + ks->keys.len = 0; + ks->keys.val = 0; + ks->set_time = 0; + return ASN1_OVERRUN; + } + *ks = data->val[element]; + data->len--; + /* Swap instead of memmove()... changes the order of elements */ + if (element < data->len) + data->val[element] = data->val[data->len]; + if (data->len == 0) { + free(data->val); + data->val = 0; + } + return 0; +} + + +/** + * Removes from `e' and optionally outputs the keyset for the requested `kvno'. + * + * @param context Context + * @param e The HDB entry + * @param kvno The key version number + * @param ks A pointer to a variable of type hdb_keyset (may be NULL) + * + * @return Zero on success, an error code otherwise. + */ +krb5_error_code +hdb_remove_keys(krb5_context context, + hdb_entry *e, + krb5_kvno kvno, + hdb_keyset *ks) +{ + HDB_Ext_KeySet *hist_keys; + HDB_extension *extp; + size_t i; + + if (kvno == 0 || e->kvno == kvno) { + if (ks) { + KerberosTime t; + + (void) hdb_entry_get_pw_change_time(e, &t); + if (t) { + if ((ks->set_time = malloc(sizeof(*ks->set_time))) == NULL) + return krb5_enomem(context); + *ks->set_time = t; + } + ks->kvno = e->kvno; + ks->keys = e->keys; + e->keys.len = 0; + e->keys.val = NULL; + e->kvno = 0; + } else { + free_Keys(&e->keys); + } + return 0; + } + + if (ks) { + ks->kvno = 0; + ks->keys.len = 0; + ks->keys.val = 0; + ks->set_time = 0; + } + + extp = hdb_find_extension(e, choice_HDB_extension_data_hist_keys); + if (extp == NULL) + return 0; + + hist_keys = &extp->data.u.hist_keys; + for (i = 0; i < hist_keys->len; i++) { + if (hist_keys->val[i].kvno != kvno) + continue; + if (ks) + return dequeue_HDB_Ext_KeySet(hist_keys, i, ks); + return remove_HDB_Ext_KeySet(hist_keys, i); + } + return HDB_ERR_NOENTRY; +} + +/** + * Removes from `e' and outputs all the base keys for virtual principal and/or + * key derivation. + * + * @param context Context + * @param e The HDB entry + * @param ks A pointer to a variable of type HDB_Ext_KeySet + * @param ckr A pointer to stable (copied) HDB_Ext_KeyRotation + * + * @return Zero on success, an error code otherwise. + */ +krb5_error_code +_hdb_remove_base_keys(krb5_context context, + hdb_entry *e, + HDB_Ext_KeySet *base_keys, + const HDB_Ext_KeyRotation *ckr) +{ + krb5_error_code ret = 0; + size_t i, k; + + base_keys->len = 0; + if ((base_keys->val = calloc(ckr->len, sizeof(base_keys->val[0]))) == NULL) + ret = krb5_enomem(context); + + for (k = i = 0; ret == 0 && i < ckr->len; i++) { + const KeyRotation *krp = &ckr->val[i]; + + /* + * WARNING: O(N * M) where M is number of keysets and N is the number + * of base keysets. + * + * In practice N will never be > 3 because the ASN.1 module imposes + * that as a constraint, and M will generally be the same as N, so this + * will be O(1) after all. + */ + ret = hdb_remove_keys(context, e, krp->base_key_kvno, + &base_keys->val[k]); + if (ret == 0) + k++; + else if (ret == HDB_ERR_NOENTRY) + ret = 0; + } + if (ret == 0) + base_keys->len = k; + else + free_HDB_Ext_KeySet(base_keys); + return 0; +} + +/** + * Removes from `e' and outputs all the base keys for virtual principal and/or + * key derivation. + * + * @param context Context + * @param e The HDB entry + * @param is_current_keyset Whether to make the keys the current keys for `e' + * @param ks A pointer to an hdb_keyset containing the keys to set + * + * @return Zero on success, an error code otherwise. + */ +krb5_error_code +hdb_install_keyset(krb5_context context, + hdb_entry *e, + int is_current_keyset, + const hdb_keyset *ks) +{ + krb5_error_code ret = 0; + + if (is_current_keyset) { + if (e->keys.len && + (ret = hdb_add_current_keys_to_history(context, e))) + return ret; + free_Keys(&e->keys); + e->kvno = ks->kvno; + if (ret == 0) + ret = copy_Keys(&ks->keys, &e->keys); + if (ret == 0 && ks->set_time) + ret = hdb_entry_set_pw_change_time(context, e, *ks->set_time); + return ret; + } + return hdb_add_history_keyset(context, e, ks); +} + + +krb5_error_code +hdb_next_enctype2key(krb5_context context, + const hdb_entry *e, + const Keys *keyset, + krb5_enctype enctype, + Key **key) +{ + const Keys *keys = keyset ? keyset : &e->keys; + Key *k; + + for (k = *key ? (*key) + 1 : keys->val; k < keys->val + keys->len; k++) { + if(k->key.keytype == enctype){ + *key = k; + return 0; + } + } + krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP, + "No next enctype %d for hdb-entry", + (int)enctype); + return KRB5_PROG_ETYPE_NOSUPP; /* XXX */ +} + +krb5_error_code +hdb_enctype2key(krb5_context context, + const hdb_entry *e, + const Keys *keyset, + krb5_enctype enctype, + Key **key) +{ + *key = NULL; + return hdb_next_enctype2key(context, e, keyset, enctype, key); +} + +void +hdb_free_key(Key *key) +{ + memset_s(key->key.keyvalue.data, + key->key.keyvalue.length, + 0, + key->key.keyvalue.length); + free_Key(key); + free(key); +} + + +krb5_error_code +hdb_lock(int fd, int operation) +{ + int i, code = 0; + + for(i = 0; i < 3; i++){ + code = flock(fd, (operation == HDB_RLOCK ? LOCK_SH : LOCK_EX) | LOCK_NB); + if(code == 0 || errno != EWOULDBLOCK) + break; + sleep(1); + } + if(code == 0) + return 0; + if(errno == EWOULDBLOCK) + return HDB_ERR_DB_INUSE; + return HDB_ERR_CANT_LOCK_DB; +} + +krb5_error_code +hdb_unlock(int fd) +{ + int code; + code = flock(fd, LOCK_UN); + if(code) + return 4711 /* XXX */; + return 0; +} + +void +hdb_free_entry(krb5_context context, HDB *db, hdb_entry *ent) +{ + Key *k; + size_t i; + + if (db && db->hdb_free_entry_context) + db->hdb_free_entry_context(context, db, ent); + + for(i = 0; i < ent->keys.len; i++) { + k = &ent->keys.val[i]; + + memset_s(k->key.keyvalue.data, + k->key.keyvalue.length, + 0, + k->key.keyvalue.length); + } + free_HDB_entry(ent); +} + +krb5_error_code +hdb_foreach(krb5_context context, + HDB *db, + unsigned flags, + hdb_foreach_func_t func, + void *data) +{ + krb5_error_code ret; + hdb_entry entry; + ret = db->hdb_firstkey(context, db, flags, &entry); + if (ret == 0) + krb5_clear_error_message(context); + while(ret == 0){ + ret = (*func)(context, db, &entry, data); + hdb_free_entry(context, db, &entry); + if(ret == 0) + ret = db->hdb_nextkey(context, db, flags, &entry); + } + if(ret == HDB_ERR_NOENTRY) + ret = 0; + return ret; +} + +krb5_error_code +hdb_check_db_format(krb5_context context, HDB *db) +{ + krb5_data tag; + krb5_data version; + krb5_error_code ret, ret2; + unsigned ver; + int foo; + + ret = db->hdb_lock(context, db, HDB_RLOCK); + if (ret) + return ret; + + tag.data = (void *)(intptr_t)HDB_DB_FORMAT_ENTRY; + tag.length = strlen(tag.data); + ret = (*db->hdb__get)(context, db, tag, &version); + ret2 = db->hdb_unlock(context, db); + if(ret) + return ret; + if (ret2) + return ret2; + foo = sscanf(version.data, "%u", &ver); + krb5_data_free (&version); + if (foo != 1) + return HDB_ERR_BADVERSION; + if(ver != HDB_DB_FORMAT) + return HDB_ERR_BADVERSION; + return 0; +} + +krb5_error_code +hdb_init_db(krb5_context context, HDB *db) +{ + krb5_error_code ret, ret2; + krb5_data tag; + krb5_data version; + char ver[32]; + + ret = hdb_check_db_format(context, db); + if(ret != HDB_ERR_NOENTRY) + return ret; + + ret = db->hdb_lock(context, db, HDB_WLOCK); + if (ret) + return ret; + + tag.data = (void *)(intptr_t)HDB_DB_FORMAT_ENTRY; + tag.length = strlen(tag.data); + snprintf(ver, sizeof(ver), "%u", HDB_DB_FORMAT); + version.data = ver; + version.length = strlen(version.data) + 1; /* zero terminated */ + ret = (*db->hdb__put)(context, db, 0, tag, version); + ret2 = db->hdb_unlock(context, db); + if (ret) { + if (ret2) + krb5_clear_error_message(context); + return ret; + } + return ret2; +} + +/* + * `default_dbmethod' is the last resort default. + * + * In hdb_create() we may try all the `methods[]' until one succeeds or all + * fail. + */ +#if defined(HAVE_LMDB) +static struct hdb_method default_dbmethod = + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "", hdb_mdb_create }; +#elif defined(HAVE_DB3) +static struct hdb_method default_dbmethod = + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "", hdb_db3_create }; +#elif defined(HAVE_DB1) +static struct hdb_method default_dbmethod = + { HDB_INTERFACE_VERSION, NULL, NULL, 1, 1, "", hdb_db1_create }; +#elif defined(HAVE_NDBM) +static struct hdb_method default_dbmethod = + { HDB_INTERFACE_VERSION, NULL, NULL, 0, 1, "", hdb_ndbm_create }; +#else +static struct hdb_method default_dbmethod = + { 0, NULL, NULL, 0, 0, NULL, NULL}; +#endif + +static int +is_pathish(const char *s) +{ + if (s[0] == '/' || + strncmp(s, "./", sizeof("./") - 1) == 0 || + strncmp(s, "../", sizeof("../") - 1) == 0) + return 1; +#ifdef WIN32 + if (s[0] == '\\' || (isalpha((unsigned char)s[0]) && s[0] == ':') || + strncmp(s, ".\\", sizeof(".\\") - 1) == 0 || + strncmp(s, "\\\\", sizeof("\\\\") - 1) == 0) + return 1; +#endif + return 0; +} + +static const struct hdb_method * +has_method_prefix(const char *filename) +{ + const struct hdb_method *h; + + for (h = methods; h->prefix != NULL; ++h) + if (strncmp(filename, h->prefix, strlen(h->prefix)) == 0) + return h; + return NULL; +} + +/* + * find the relevant method for `filename', returning a pointer to the + * rest in `rest'. + * return NULL if there's no such method. + */ + +static const struct hdb_method * +find_method(const char *filename, const char **rest) +{ + const struct hdb_method *h = has_method_prefix(filename); + + *rest = h ? filename + strlen(h->prefix) : filename; + return h; +} + +struct cb_s { + const char *residual; + const char *filename; + const struct hdb_method *h; +}; + +static krb5_error_code KRB5_LIB_CALL +callback(krb5_context context, const void *plug, void *plugctx, void *userctx) +{ + const struct hdb_method *h = (const struct hdb_method *)plug; + struct cb_s *cb_ctx = (struct cb_s *)userctx; + + if (strncmp(cb_ctx->filename, h->prefix, strlen(h->prefix)) == 0) { + cb_ctx->residual = cb_ctx->filename + strlen(h->prefix) + 1; + cb_ctx->h = h; + return 0; + } + return KRB5_PLUGIN_NO_HANDLE; +} + +static char * +make_sym(const char *prefix) +{ + char *s, *sym; + + errno = 0; + if (prefix == NULL || prefix[0] == '\0') + return NULL; + if ((s = strdup(prefix)) == NULL) + return NULL; + if (strchr(s, ':') != NULL) + *strchr(s, ':') = '\0'; + if (asprintf(&sym, "hdb_%s_interface", s) == -1) + sym = NULL; + free(s); + return sym; +} + +static const char *hdb_plugin_deps[] = { "hdb", "krb5", NULL }; + +krb5_error_code +hdb_list_builtin(krb5_context context, char **list) +{ + const struct hdb_method *h; + size_t len = 0; + char *buf = NULL; + + for (h = methods; h->prefix != NULL; ++h) { + if (h->prefix[0] == '\0') + continue; + len += strlen(h->prefix) + 2; + } + + len += 1; + buf = malloc(len); + if (buf == NULL) { + return krb5_enomem(context); + } + buf[0] = '\0'; + + for (h = methods; h->prefix != NULL; ++h) { + if (h->create == NULL) { + struct cb_s cb_ctx; + char *f; + struct heim_plugin_data hdb_plugin_data; + + hdb_plugin_data.module = "krb5"; + hdb_plugin_data.min_version = HDB_INTERFACE_VERSION; + hdb_plugin_data.deps = hdb_plugin_deps; + hdb_plugin_data.get_instance = hdb_get_instance; + + /* Try loading the plugin */ + if (asprintf(&f, "%sfoo", h->prefix) == -1) + f = NULL; + if ((hdb_plugin_data.name = make_sym(h->prefix)) == NULL) { + free(buf); + free(f); + return krb5_enomem(context); + } + cb_ctx.filename = f; + cb_ctx.residual = NULL; + cb_ctx.h = NULL; + (void)_krb5_plugin_run_f(context, &hdb_plugin_data, 0, + &cb_ctx, callback); + free(f); + free(rk_UNCONST(hdb_plugin_data.name)); + if (cb_ctx.h == NULL || cb_ctx.h->create == NULL) + continue; + } + if (h != methods) + strlcat(buf, ", ", len); + strlcat(buf, h->prefix, len); + } + *list = buf; + return 0; +} + +krb5_error_code +_hdb_keytab2hdb_entry(krb5_context context, + const krb5_keytab_entry *ktentry, + hdb_entry *entry) +{ + entry->kvno = ktentry->vno; + entry->created_by.time = ktentry->timestamp; + + entry->keys.val = calloc(1, sizeof(entry->keys.val[0])); + if (entry->keys.val == NULL) + return ENOMEM; + entry->keys.len = 1; + + entry->keys.val[0].mkvno = NULL; + entry->keys.val[0].salt = NULL; + + return krb5_copy_keyblock_contents(context, + &ktentry->keyblock, + &entry->keys.val[0].key); +} + +static krb5_error_code +load_config(krb5_context context, HDB *db) +{ + db->enable_virtual_hostbased_princs = + krb5_config_get_bool_default(context, NULL, FALSE, "hdb", + "enable_virtual_hostbased_princs", + NULL); + db->virtual_hostbased_princ_ndots = + krb5_config_get_int_default(context, NULL, 1, "hdb", + "virtual_hostbased_princ_mindots", + NULL); + db->virtual_hostbased_princ_maxdots = + krb5_config_get_int_default(context, NULL, 0, "hdb", + "virtual_hostbased_princ_maxdots", + NULL); + db->new_service_key_delay = + krb5_config_get_time_default(context, NULL, 0, "hdb", + "new_service_key_delay", NULL); + /* + * XXX Needs freeing in the HDB backends because we don't have a + * first-class hdb_close() :( + */ + db->virtual_hostbased_princ_svcs = + krb5_config_get_strings(context, NULL, "hdb", + "virtual_hostbased_princ_svcs", NULL); + /* Check for ENOMEM */ + if (db->virtual_hostbased_princ_svcs == NULL + && krb5_config_get_string(context, NULL, "hdb", + "virtual_hostbased_princ_svcs", NULL)) { + return krb5_enomem(context); + } + return 0; +} + +/** + * Create a handle for a Kerberos database + * + * Create a handle for a Kerberos database backend specified by a + * filename. Doesn't actually create or even open an HDB file(s); + * you have to call the hdb_open() open method of the resulting HDB + * to open the database, and you have to use O_CREAT to create it. + * + * If `filename' does not have a backend type prefix, all file-based + * backends will be tried until one succeeds or all fail, and if the + * HDB exists for some backend, that will be used. A build-time + * default backend type will be used if the `filename' does not exist. + * + * Note that the actual filename may have a suffix added, such as + * ".db". Also, for backends such as "ldap:" and "ldapi:" the + * `filename' is more like a URI. + * + * @param [in] context Context + * @param [out] db HDB handle output + * @param [in] filename The name of the HDB + * + * @return Zero on success else a krb5 error code. + */ + +krb5_error_code +hdb_create(krb5_context context, HDB **db, const char *filename) +{ + krb5_error_code ret = ENOTSUP; + struct cb_s cb_ctx; + + *db = NULL; + if (filename == NULL) + filename = hdb_default_db(context); + + cb_ctx.h = find_method(filename, &cb_ctx.residual); + cb_ctx.filename = filename; + + if (cb_ctx.h == NULL || cb_ctx.h->create == NULL) { + struct heim_plugin_data hdb_plugin_data; + + /* + * `filename' does not start with a known HDB backend prefix. + * + * Try plugins. + */ + hdb_plugin_data.module = "krb5"; + hdb_plugin_data.min_version = HDB_INTERFACE_VERSION; + hdb_plugin_data.deps = hdb_plugin_deps; + hdb_plugin_data.get_instance = hdb_get_instance; + + if ((hdb_plugin_data.name = make_sym(filename)) == NULL) + return krb5_enomem(context); + + (void)_krb5_plugin_run_f(context, &hdb_plugin_data, 0 /* flags */, + &cb_ctx, callback); + + free(rk_UNCONST(hdb_plugin_data.name)); + } + + if (cb_ctx.h == NULL || cb_ctx.h->create == NULL) { + int pathish = is_pathish(filename); + /* + * `filename' does not start with a known HDB backend prefix and it + * wasn't handled by any plugin. + * + * If it's "filename-ish", try all builtin HDB backends that are + * local-file-ish, but use hdb_open() to see if the HDB exists and stop + * when a backend is found for which the HDB exists. + */ + if (!pathish) { + krb5_set_error_message(context, ret = ENOTSUP, + "No database support for %s", + cb_ctx.filename); + return ret; + } + for (cb_ctx.h = methods; cb_ctx.h->prefix != NULL; cb_ctx.h++) { + if (cb_ctx.h->is_file_based) + continue; + if (!cb_ctx.h->can_taste) + continue; + /* Taste the file */ + ret = (*cb_ctx.h->create)(context, db, filename); + if (ret == 0) + ret = (*db)->hdb_open(context, *db, O_RDONLY, 0); + if (ret == 0) { + (void) (*db)->hdb_close(context, *db); + break; + } + if (*db) + (*db)->hdb_destroy(context, *db); + *db = NULL; + } + if (cb_ctx.h->prefix == NULL) + cb_ctx.h = NULL; + } +#ifdef HDB_DEFAULT_DB_TYPE + if (cb_ctx.h == NULL) { + /* + * If still we've not picked a backend, use a build configuration time + * default. + */ + for (cb_ctx.h = methods; cb_ctx.h->prefix != NULL; cb_ctx.h++) + if (strcmp(cb_ctx.h->prefix, HDB_DEFAULT_DB_TYPE) == 0) + break; + if (cb_ctx.h->prefix == NULL) + cb_ctx.h = NULL; + } +#endif + if (cb_ctx.h == NULL) + /* Last resort default */ + cb_ctx.h = &default_dbmethod; + if (cb_ctx.h->prefix == NULL) { + krb5_set_error_message(context, ENOTSUP, + "Could not determine default DB backend for %s", + filename); + return ENOTSUP; + } + if (!*db) { + ret = (*cb_ctx.h->create)(context, db, cb_ctx.residual); + if (ret == 0) + (*db)->hdb_method_name = cb_ctx.h->prefix; + } + if (ret == 0 && *db) + ret = load_config(context, *db); + if (ret && *db) { + (*db)->hdb_destroy(context, *db); + *db = NULL; + } + return ret; +} + +uintptr_t KRB5_CALLCONV +hdb_get_instance(const char *libname) +{ + static const char *instance = "libhdb"; + + if (strcmp(libname, "hdb") == 0) + return (uintptr_t)instance; + else if (strcmp(libname, "krb5") == 0) + return krb5_get_instance(libname); + + return 0; +} |