summaryrefslogtreecommitdiff
path: root/source4/heimdal/lib/hdb
diff options
context:
space:
mode:
Diffstat (limited to 'source4/heimdal/lib/hdb')
-rw-r--r--source4/heimdal/lib/hdb/db.c337
-rw-r--r--source4/heimdal/lib/hdb/dbinfo.c266
-rw-r--r--source4/heimdal/lib/hdb/ext.c421
-rw-r--r--source4/heimdal/lib/hdb/hdb.asn1127
-rw-r--r--source4/heimdal/lib/hdb/hdb.c413
-rw-r--r--source4/heimdal/lib/hdb/hdb.h144
-rw-r--r--source4/heimdal/lib/hdb/hdb_err.et28
-rw-r--r--source4/heimdal/lib/hdb/hdb_locl.h70
-rw-r--r--source4/heimdal/lib/hdb/keys.c401
-rw-r--r--source4/heimdal/lib/hdb/keytab.c272
-rw-r--r--source4/heimdal/lib/hdb/mkey.c604
-rw-r--r--source4/heimdal/lib/hdb/ndbm.c370
12 files changed, 3453 insertions, 0 deletions
diff --git a/source4/heimdal/lib/hdb/db.c b/source4/heimdal/lib/hdb/db.c
new file mode 100644
index 0000000000..a598e9e1a4
--- /dev/null
+++ b/source4/heimdal/lib/hdb/db.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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 "hdb_locl.h"
+
+RCSID("$Id$");
+
+#if HAVE_DB1
+
+#if defined(HAVE_DB_185_H)
+#include <db_185.h>
+#elif defined(HAVE_DB_H)
+#include <db.h>
+#endif
+
+static krb5_error_code
+DB_close(krb5_context context, HDB *db)
+{
+ DB *d = (DB*)db->hdb_db;
+ (*d->close)(d);
+ return 0;
+}
+
+static krb5_error_code
+DB_destroy(krb5_context context, HDB *db)
+{
+ krb5_error_code ret;
+
+ ret = hdb_clear_master_key (context, db);
+ free(db->hdb_name);
+ free(db);
+ return ret;
+}
+
+static krb5_error_code
+DB_lock(krb5_context context, HDB *db, int operation)
+{
+ DB *d = (DB*)db->hdb_db;
+ int fd = (*d->fd)(d);
+ if(fd < 0) {
+ krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
+ "Can't lock database: %s", db->hdb_name);
+ return HDB_ERR_CANT_LOCK_DB;
+ }
+ return hdb_lock(fd, operation);
+}
+
+static krb5_error_code
+DB_unlock(krb5_context context, HDB *db)
+{
+ DB *d = (DB*)db->hdb_db;
+ int fd = (*d->fd)(d);
+ if(fd < 0) {
+ krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
+ "Can't unlock database: %s", db->hdb_name);
+ return HDB_ERR_CANT_LOCK_DB;
+ }
+ return hdb_unlock(fd);
+}
+
+
+static krb5_error_code
+DB_seq(krb5_context context, HDB *db,
+ unsigned flags, hdb_entry_ex *entry, int flag)
+{
+ DB *d = (DB*)db->hdb_db;
+ DBT key, value;
+ krb5_data key_data, data;
+ int code;
+
+ code = db->hdb_lock(context, db, HDB_RLOCK);
+ if(code == -1) {
+ krb5_set_error_message(context, HDB_ERR_DB_INUSE, "Database %s in use", db->hdb_name);
+ return HDB_ERR_DB_INUSE;
+ }
+ code = (*d->seq)(d, &key, &value, flag);
+ db->hdb_unlock(context, db); /* XXX check value */
+ if(code == -1) {
+ code = errno;
+ krb5_set_error_message(context, code, "Database %s seq error: %s",
+ db->hdb_name, strerror(code));
+ return code;
+ }
+ if(code == 1) {
+ krb5_clear_error_string(context);
+ return HDB_ERR_NOENTRY;
+ }
+
+ key_data.data = key.data;
+ key_data.length = key.size;
+ data.data = value.data;
+ data.length = value.size;
+ memset(entry, 0, sizeof(*entry));
+ if (hdb_value2entry(context, &data, &entry->entry))
+ return DB_seq(context, db, flags, entry, R_NEXT);
+ if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
+ code = hdb_unseal_keys (context, db, &entry->entry);
+ if (code)
+ hdb_free_entry (context, entry);
+ }
+ if (code == 0 && entry->entry.principal == NULL) {
+ entry->entry.principal = malloc(sizeof(*entry->entry.principal));
+ if (entry->entry.principal == NULL) {
+ code = ENOMEM;
+ krb5_set_error_message(context, code, "malloc: out of memory");
+ hdb_free_entry (context, entry);
+ } else {
+ hdb_key2principal(context, &key_data, entry->entry.principal);
+ }
+ }
+ return code;
+}
+
+
+static krb5_error_code
+DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
+{
+ return DB_seq(context, db, flags, entry, R_FIRST);
+}
+
+
+static krb5_error_code
+DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
+{
+ return DB_seq(context, db, flags, entry, R_NEXT);
+}
+
+static krb5_error_code
+DB_rename(krb5_context context, HDB *db, const char *new_name)
+{
+ int ret;
+ char *old, *new;
+
+ asprintf(&old, "%s.db", db->hdb_name);
+ asprintf(&new, "%s.db", new_name);
+ ret = rename(old, new);
+ free(old);
+ free(new);
+ if(ret)
+ return errno;
+
+ free(db->hdb_name);
+ db->hdb_name = strdup(new_name);
+ return 0;
+}
+
+static krb5_error_code
+DB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
+{
+ DB *d = (DB*)db->hdb_db;
+ DBT k, v;
+ int code;
+
+ k.data = key.data;
+ k.size = key.length;
+ code = db->hdb_lock(context, db, HDB_RLOCK);
+ if(code)
+ return code;
+ code = (*d->get)(d, &k, &v, 0);
+ db->hdb_unlock(context, db);
+ if(code < 0) {
+ code = errno;
+ krb5_set_error_message(context, code, "Database %s get error: %s",
+ db->hdb_name, strerror(code));
+ return code;
+ }
+ if(code == 1) {
+ krb5_clear_error_string(context);
+ return HDB_ERR_NOENTRY;
+ }
+
+ krb5_data_copy(reply, v.data, v.size);
+ return 0;
+}
+
+static krb5_error_code
+DB__put(krb5_context context, HDB *db, int replace,
+ krb5_data key, krb5_data value)
+{
+ DB *d = (DB*)db->hdb_db;
+ DBT k, v;
+ int code;
+
+ k.data = key.data;
+ k.size = key.length;
+ v.data = value.data;
+ v.size = value.length;
+ code = db->hdb_lock(context, db, HDB_WLOCK);
+ if(code)
+ return code;
+ code = (*d->put)(d, &k, &v, replace ? 0 : R_NOOVERWRITE);
+ db->hdb_unlock(context, db);
+ if(code < 0) {
+ code = errno;
+ krb5_set_error_message(context, code, "Database %s put error: %s",
+ db->hdb_name, strerror(code));
+ return code;
+ }
+ if(code == 1) {
+ krb5_clear_error_string(context);
+ return HDB_ERR_EXISTS;
+ }
+ return 0;
+}
+
+static krb5_error_code
+DB__del(krb5_context context, HDB *db, krb5_data key)
+{
+ DB *d = (DB*)db->hdb_db;
+ DBT k;
+ krb5_error_code code;
+ k.data = key.data;
+ k.size = key.length;
+ code = db->hdb_lock(context, db, HDB_WLOCK);
+ if(code)
+ return code;
+ code = (*d->del)(d, &k, 0);
+ db->hdb_unlock(context, db);
+ if(code == 1) {
+ code = errno;
+ krb5_set_error_message(context, code, "Database %s put error: %s",
+ db->hdb_name, strerror(code));
+ return code;
+ }
+ if(code < 0)
+ return errno;
+ return 0;
+}
+
+static krb5_error_code
+DB_open(krb5_context context, HDB *db, int flags, mode_t mode)
+{
+ char *fn;
+ krb5_error_code ret;
+
+ asprintf(&fn, "%s.db", db->hdb_name);
+ if (fn == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL);
+ free(fn);
+ /* try to open without .db extension */
+ if(db->hdb_db == NULL && errno == ENOENT)
+ db->hdb_db = dbopen(db->hdb_name, flags, mode, DB_BTREE, NULL);
+ if(db->hdb_db == NULL) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "dbopen (%s): %s",
+ db->hdb_name, strerror(ret));
+ return ret;
+ }
+ if((flags & O_ACCMODE) == O_RDONLY)
+ ret = hdb_check_db_format(context, db);
+ else
+ ret = hdb_init_db(context, db);
+ if(ret == HDB_ERR_NOENTRY) {
+ krb5_clear_error_string(context);
+ return 0;
+ }
+ if (ret) {
+ DB_close(context, db);
+ krb5_set_error_message(context, ret, "hdb_open: failed %s database %s",
+ (flags & O_ACCMODE) == O_RDONLY ?
+ "checking format of" : "initialize",
+ db->hdb_name);
+ }
+ return ret;
+}
+
+krb5_error_code
+hdb_db_create(krb5_context context, HDB **db,
+ const char *filename)
+{
+ *db = calloc(1, sizeof(**db));
+ if (*db == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ (*db)->hdb_db = NULL;
+ (*db)->hdb_name = strdup(filename);
+ if ((*db)->hdb_name == NULL) {
+ free(*db);
+ *db = NULL;
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ (*db)->hdb_master_key_set = 0;
+ (*db)->hdb_openp = 0;
+ (*db)->hdb_open = DB_open;
+ (*db)->hdb_close = DB_close;
+ (*db)->hdb_fetch = _hdb_fetch;
+ (*db)->hdb_store = _hdb_store;
+ (*db)->hdb_remove = _hdb_remove;
+ (*db)->hdb_firstkey = DB_firstkey;
+ (*db)->hdb_nextkey= DB_nextkey;
+ (*db)->hdb_lock = DB_lock;
+ (*db)->hdb_unlock = DB_unlock;
+ (*db)->hdb_rename = DB_rename;
+ (*db)->hdb__get = DB__get;
+ (*db)->hdb__put = DB__put;
+ (*db)->hdb__del = DB__del;
+ (*db)->hdb_destroy = DB_destroy;
+ return 0;
+}
+
+#endif /* HAVE_DB1 */
diff --git a/source4/heimdal/lib/hdb/dbinfo.c b/source4/heimdal/lib/hdb/dbinfo.c
new file mode 100644
index 0000000000..67b9fc6ecf
--- /dev/null
+++ b/source4/heimdal/lib/hdb/dbinfo.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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 "hdb_locl.h"
+
+RCSID("$Id$");
+
+struct hdb_dbinfo {
+ char *label;
+ char *realm;
+ char *dbname;
+ char *mkey_file;
+ char *acl_file;
+ char *log_file;
+ const krb5_config_binding *binding;
+ struct hdb_dbinfo *next;
+};
+
+static int
+get_dbinfo(krb5_context context,
+ const krb5_config_binding *db_binding,
+ const char *label,
+ struct hdb_dbinfo **db)
+{
+ struct hdb_dbinfo *di;
+ const char *p;
+
+ *db = NULL;
+
+ p = krb5_config_get_string(context, db_binding, "dbname", NULL);
+ if(p == NULL)
+ return 0;
+
+ di = calloc(1, sizeof(*di));
+ if (di == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ di->label = strdup(label);
+ di->dbname = strdup(p);
+
+ p = krb5_config_get_string(context, db_binding, "realm", NULL);
+ if(p)
+ di->realm = strdup(p);
+ p = krb5_config_get_string(context, db_binding, "mkey_file", NULL);
+ if(p)
+ di->mkey_file = strdup(p);
+ p = krb5_config_get_string(context, db_binding, "acl_file", NULL);
+ if(p)
+ di->acl_file = strdup(p);
+ p = krb5_config_get_string(context, db_binding, "log_file", NULL);
+ if(p)
+ di->log_file = strdup(p);
+
+ di->binding = db_binding;
+
+ *db = di;
+ return 0;
+}
+
+
+int
+hdb_get_dbinfo(krb5_context context, struct hdb_dbinfo **dbp)
+{
+ const krb5_config_binding *db_binding;
+ struct hdb_dbinfo *di, **dt, *databases;
+ const char *default_dbname = HDB_DEFAULT_DB;
+ const char *default_mkey = HDB_DB_DIR "/m-key";
+ const char *default_acl = HDB_DB_DIR "/kadmind.acl";
+ const char *p;
+ int ret;
+
+ *dbp = NULL;
+ dt = NULL;
+ databases = NULL;
+
+ db_binding = krb5_config_get(context, NULL, krb5_config_list,
+ "kdc",
+ "database",
+ NULL);
+ if (db_binding) {
+
+ ret = get_dbinfo(context, db_binding, "default", &di);
+ if (ret == 0 && di) {
+ databases = di;
+ dt = &di->next;
+ }
+
+ for ( ; db_binding != NULL; db_binding = db_binding->next) {
+
+ if (db_binding->type != krb5_config_list)
+ continue;
+
+ ret = get_dbinfo(context, db_binding->u.list,
+ db_binding->name, &di);
+ if (ret)
+ krb5_err(context, 1, ret, "failed getting realm");
+
+ if (di == NULL)
+ continue;
+
+ if (dt)
+ *dt = di;
+ else
+ databases = di;
+ dt = &di->next;
+
+ }
+ }
+
+ if(databases == NULL) {
+ /* if there are none specified, create one and use defaults */
+ di = calloc(1, sizeof(*di));
+ databases = di;
+ di->label = strdup("default");
+ }
+
+ for(di = databases; di; di = di->next) {
+ if(di->dbname == NULL) {
+ di->dbname = strdup(default_dbname);
+ if (di->mkey_file == NULL)
+ di->mkey_file = strdup(default_mkey);
+ }
+ if(di->mkey_file == NULL) {
+ p = strrchr(di->dbname, '.');
+ if(p == NULL || strchr(p, '/') != NULL)
+ /* final pathname component does not contain a . */
+ asprintf(&di->mkey_file, "%s.mkey", di->dbname);
+ else
+ /* the filename is something.else, replace .else with
+ .mkey */
+ asprintf(&di->mkey_file, "%.*s.mkey",
+ (int)(p - di->dbname), di->dbname);
+ }
+ if(di->acl_file == NULL)
+ di->acl_file = strdup(default_acl);
+ }
+ *dbp = databases;
+ return 0;
+}
+
+
+struct hdb_dbinfo *
+hdb_dbinfo_get_next(struct hdb_dbinfo *dbp, struct hdb_dbinfo *dbprevp)
+{
+ if (dbprevp == NULL)
+ return dbp;
+ else
+ return dbprevp->next;
+}
+
+const char *
+hdb_dbinfo_get_label(krb5_context context, struct hdb_dbinfo *dbp)
+{
+ return dbp->label;
+}
+
+const char *
+hdb_dbinfo_get_realm(krb5_context context, struct hdb_dbinfo *dbp)
+{
+ return dbp->realm;
+}
+
+const char *
+hdb_dbinfo_get_dbname(krb5_context context, struct hdb_dbinfo *dbp)
+{
+ return dbp->dbname;
+}
+
+const char *
+hdb_dbinfo_get_mkey_file(krb5_context context, struct hdb_dbinfo *dbp)
+{
+ return dbp->mkey_file;
+}
+
+const char *
+hdb_dbinfo_get_acl_file(krb5_context context, struct hdb_dbinfo *dbp)
+{
+ return dbp->acl_file;
+}
+
+const char *
+hdb_dbinfo_get_log_file(krb5_context context, struct hdb_dbinfo *dbp)
+{
+ return dbp->log_file;
+}
+
+const krb5_config_binding *
+hdb_dbinfo_get_binding(krb5_context context, struct hdb_dbinfo *dbp)
+{
+ return dbp->binding;
+}
+
+void
+hdb_free_dbinfo(krb5_context context, struct hdb_dbinfo **dbp)
+{
+ struct hdb_dbinfo *di, *ndi;
+
+ for(di = *dbp; di != NULL; di = ndi) {
+ ndi = di->next;
+ free (di->realm);
+ free (di->dbname);
+ if (di->mkey_file)
+ free (di->mkey_file);
+ free(di);
+ }
+ *dbp = NULL;
+}
+
+/**
+ * Return the directory where the hdb database resides.
+ *
+ * @param context Kerberos 5 context.
+ *
+ * @return string pointing to directory.
+ */
+
+const char *
+hdb_db_dir(krb5_context context)
+{
+ return HDB_DB_DIR;
+}
+
+/**
+ * Return the default hdb database resides.
+ *
+ * @param context Kerberos 5 context.
+ *
+ * @return string pointing to directory.
+ */
+
+const char *
+hdb_default_db(krb5_context context)
+{
+ return HDB_DEFAULT_DB;
+}
diff --git a/source4/heimdal/lib/hdb/ext.c b/source4/heimdal/lib/hdb/ext.c
new file mode 100644
index 0000000000..92147254ee
--- /dev/null
+++ b/source4/heimdal/lib/hdb/ext.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2004 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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 "hdb_locl.h"
+#include <der.h>
+
+RCSID("$Id$");
+
+krb5_error_code
+hdb_entry_check_mandatory(krb5_context context, const hdb_entry *ent)
+{
+ int i;
+
+ if (ent->extensions == NULL)
+ return 0;
+
+ /*
+ * check for unknown extensions and if they where tagged mandatory
+ */
+
+ for (i = 0; i < ent->extensions->len; i++) {
+ if (ent->extensions->val[i].data.element !=
+ choice_HDB_extension_data_asn1_ellipsis)
+ continue;
+ if (ent->extensions->val[i].mandatory) {
+ krb5_set_error_message(context, HDB_ERR_MANDATORY_OPTION,
+ "Principal have unknown "
+ "mandatory extension");
+ return HDB_ERR_MANDATORY_OPTION;
+ }
+ }
+ return 0;
+}
+
+HDB_extension *
+hdb_find_extension(const hdb_entry *entry, int type)
+{
+ int i;
+
+ if (entry->extensions == NULL)
+ return NULL;
+
+ for (i = 0; i < entry->extensions->len; i++)
+ if (entry->extensions->val[i].data.element == type)
+ return &entry->extensions->val[i];
+ return NULL;
+}
+
+/*
+ * Replace the extension `ext' in `entry'. Make a copy of the
+ * extension, so the caller must still free `ext' on both success and
+ * failure. Returns 0 or error code.
+ */
+
+krb5_error_code
+hdb_replace_extension(krb5_context context,
+ hdb_entry *entry,
+ const HDB_extension *ext)
+{
+ HDB_extension *ext2;
+ HDB_extension *es;
+ int ret;
+
+ ext2 = NULL;
+
+ if (entry->extensions == NULL) {
+ entry->extensions = calloc(1, sizeof(*entry->extensions));
+ if (entry->extensions == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ } else if (ext->data.element != choice_HDB_extension_data_asn1_ellipsis) {
+ ext2 = hdb_find_extension(entry, ext->data.element);
+ } else {
+ /*
+ * This is an unknown extention, and we are asked to replace a
+ * possible entry in `entry' that is of the same type. This
+ * might seem impossible, but ASN.1 CHOICE comes to our
+ * rescue. The first tag in each branch in the CHOICE is
+ * unique, so just find the element in the list that have the
+ * same tag was we are putting into the list.
+ */
+ Der_class replace_class, list_class;
+ Der_type replace_type, list_type;
+ unsigned int replace_tag, list_tag;
+ size_t size;
+ int i;
+
+ ret = der_get_tag(ext->data.u.asn1_ellipsis.data,
+ ext->data.u.asn1_ellipsis.length,
+ &replace_class, &replace_type, &replace_tag,
+ &size);
+ if (ret) {
+ krb5_set_error_message(context, ret, "hdb: failed to decode "
+ "replacement hdb extention");
+ return ret;
+ }
+
+ for (i = 0; i < entry->extensions->len; i++) {
+ HDB_extension *ext3 = &entry->extensions->val[i];
+
+ if (ext3->data.element != choice_HDB_extension_data_asn1_ellipsis)
+ continue;
+
+ ret = der_get_tag(ext3->data.u.asn1_ellipsis.data,
+ ext3->data.u.asn1_ellipsis.length,
+ &list_class, &list_type, &list_tag,
+ &size);
+ if (ret) {
+ krb5_set_error_message(context, ret, "hdb: failed to decode "
+ "present hdb extention");
+ return ret;
+ }
+
+ if (MAKE_TAG(replace_class,replace_type,replace_type) ==
+ MAKE_TAG(list_class,list_type,list_type)) {
+ ext2 = ext3;
+ break;
+ }
+ }
+ }
+
+ if (ext2) {
+ free_HDB_extension(ext2);
+ ret = copy_HDB_extension(ext, ext2);
+ if (ret)
+ krb5_set_error_message(context, ret, "hdb: failed to copy replacement "
+ "hdb extention");
+ return ret;
+ }
+
+ es = realloc(entry->extensions->val,
+ (entry->extensions->len+1)*sizeof(entry->extensions->val[0]));
+ if (es == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ entry->extensions->val = es;
+
+ ret = copy_HDB_extension(ext,
+ &entry->extensions->val[entry->extensions->len]);
+ if (ret == 0)
+ entry->extensions->len++;
+ else
+ krb5_set_error_message(context, ret, "hdb: failed to copy new extension");
+
+ return ret;
+}
+
+krb5_error_code
+hdb_clear_extension(krb5_context context,
+ hdb_entry *entry,
+ int type)
+{
+ int i;
+
+ if (entry->extensions == NULL)
+ return 0;
+
+ for (i = 0; i < entry->extensions->len; i++) {
+ if (entry->extensions->val[i].data.element == type) {
+ free_HDB_extension(&entry->extensions->val[i]);
+ memmove(&entry->extensions->val[i],
+ &entry->extensions->val[i + 1],
+ sizeof(entry->extensions->val[i]) * (entry->extensions->len - i - 1));
+ entry->extensions->len--;
+ }
+ }
+ if (entry->extensions->len == 0) {
+ free(entry->extensions->val);
+ free(entry->extensions);
+ entry->extensions = NULL;
+ }
+
+ return 0;
+}
+
+
+krb5_error_code
+hdb_entry_get_pkinit_acl(const hdb_entry *entry, const HDB_Ext_PKINIT_acl **a)
+{
+ const HDB_extension *ext;
+
+ ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_acl);
+ if (ext)
+ *a = &ext->data.u.pkinit_acl;
+ else
+ *a = NULL;
+
+ return 0;
+}
+
+krb5_error_code
+hdb_entry_get_pkinit_hash(const hdb_entry *entry, const HDB_Ext_PKINIT_hash **a)
+{
+ const HDB_extension *ext;
+
+ ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_cert_hash);
+ if (ext)
+ *a = &ext->data.u.pkinit_cert_hash;
+ else
+ *a = NULL;
+
+ return 0;
+}
+
+krb5_error_code
+hdb_entry_get_pw_change_time(const hdb_entry *entry, time_t *t)
+{
+ const HDB_extension *ext;
+
+ ext = hdb_find_extension(entry, choice_HDB_extension_data_last_pw_change);
+ if (ext)
+ *t = ext->data.u.last_pw_change;
+ else
+ *t = 0;
+
+ return 0;
+}
+
+krb5_error_code
+hdb_entry_set_pw_change_time(krb5_context context,
+ hdb_entry *entry,
+ time_t t)
+{
+ HDB_extension ext;
+
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_last_pw_change;
+ if (t == 0)
+ t = time(NULL);
+ ext.data.u.last_pw_change = t;
+
+ return hdb_replace_extension(context, entry, &ext);
+}
+
+int
+hdb_entry_get_password(krb5_context context, HDB *db,
+ const hdb_entry *entry, char **p)
+{
+ HDB_extension *ext;
+ char *str;
+ int ret;
+
+ ext = hdb_find_extension(entry, choice_HDB_extension_data_password);
+ if (ext) {
+ heim_utf8_string str;
+ heim_octet_string pw;
+
+ if (db->hdb_master_key_set && ext->data.u.password.mkvno) {
+ hdb_master_key key;
+
+ key = _hdb_find_master_key(ext->data.u.password.mkvno,
+ db->hdb_master_key);
+
+ if (key == NULL) {
+ krb5_set_error_message(context, HDB_ERR_NO_MKEY,
+ "master key %d missing",
+ *ext->data.u.password.mkvno);
+ return HDB_ERR_NO_MKEY;
+ }
+
+ ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY,
+ ext->data.u.password.password.data,
+ ext->data.u.password.password.length,
+ &pw);
+ } else {
+ ret = der_copy_octet_string(&ext->data.u.password.password, &pw);
+ }
+ if (ret) {
+ krb5_clear_error_string(context);
+ return ret;
+ }
+
+ str = pw.data;
+ if (str[pw.length - 1] != '\0') {
+ krb5_set_error_message(context, EINVAL, "password malformated");
+ return EINVAL;
+ }
+
+ *p = strdup(str);
+
+ der_free_octet_string(&pw);
+ if (*p == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ return 0;
+ }
+
+ ret = krb5_unparse_name(context, entry->principal, &str);
+ if (ret == 0) {
+ krb5_set_error_message(context, ENOENT, "no password attributefor %s", str);
+ free(str);
+ } else
+ krb5_clear_error_string(context);
+
+ return ENOENT;
+}
+
+int
+hdb_entry_set_password(krb5_context context, HDB *db,
+ hdb_entry *entry, const char *p)
+{
+ HDB_extension ext;
+ hdb_master_key key;
+ int ret;
+
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_password;
+
+ if (db->hdb_master_key_set) {
+
+ key = _hdb_find_master_key(NULL, db->hdb_master_key);
+ if (key == NULL) {
+ krb5_set_error_message(context, HDB_ERR_NO_MKEY,
+ "hdb_entry_set_password: "
+ "failed to find masterkey");
+ return HDB_ERR_NO_MKEY;
+ }
+
+ ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY,
+ p, strlen(p) + 1,
+ &ext.data.u.password.password);
+ if (ret)
+ return ret;
+
+ ext.data.u.password.mkvno =
+ malloc(sizeof(*ext.data.u.password.mkvno));
+ if (ext.data.u.password.mkvno == NULL) {
+ free_HDB_extension(&ext);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ *ext.data.u.password.mkvno = _hdb_mkey_version(key);
+
+ } else {
+ ext.data.u.password.mkvno = NULL;
+
+ ret = krb5_data_copy(&ext.data.u.password.password,
+ p, strlen(p) + 1);
+ if (ret) {
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ free_HDB_extension(&ext);
+ return ret;
+ }
+ }
+
+ ret = hdb_replace_extension(context, entry, &ext);
+
+ free_HDB_extension(&ext);
+
+ return ret;
+}
+
+int
+hdb_entry_clear_password(krb5_context context, hdb_entry *entry)
+{
+ return hdb_clear_extension(context, entry,
+ choice_HDB_extension_data_password);
+}
+
+krb5_error_code
+hdb_entry_get_ConstrainedDelegACL(const hdb_entry *entry,
+ const HDB_Ext_Constrained_delegation_acl **a)
+{
+ const HDB_extension *ext;
+
+ ext = hdb_find_extension(entry,
+ choice_HDB_extension_data_allowed_to_delegate_to);
+ if (ext)
+ *a = &ext->data.u.allowed_to_delegate_to;
+ else
+ *a = NULL;
+
+ return 0;
+}
+
+krb5_error_code
+hdb_entry_get_aliases(const hdb_entry *entry, const HDB_Ext_Aliases **a)
+{
+ const HDB_extension *ext;
+
+ ext = hdb_find_extension(entry, choice_HDB_extension_data_aliases);
+ if (ext)
+ *a = &ext->data.u.aliases;
+ else
+ *a = NULL;
+
+ return 0;
+}
diff --git a/source4/heimdal/lib/hdb/hdb.asn1 b/source4/heimdal/lib/hdb/hdb.asn1
new file mode 100644
index 0000000000..5cddf8f1d0
--- /dev/null
+++ b/source4/heimdal/lib/hdb/hdb.asn1
@@ -0,0 +1,127 @@
+-- $Id$
+HDB DEFINITIONS ::=
+BEGIN
+
+IMPORTS EncryptionKey, KerberosTime, Principal FROM krb5;
+
+HDB_DB_FORMAT INTEGER ::= 2 -- format of database,
+ -- update when making changes
+
+-- these must have the same value as the pa-* counterparts
+hdb-pw-salt INTEGER ::= 3
+hdb-afs3-salt INTEGER ::= 10
+
+Salt ::= SEQUENCE {
+ type[0] INTEGER (0..4294967295),
+ salt[1] OCTET STRING
+}
+
+Key ::= SEQUENCE {
+ mkvno[0] INTEGER (0..4294967295) OPTIONAL, -- master key version number
+ key[1] EncryptionKey,
+ salt[2] Salt OPTIONAL
+}
+
+Event ::= SEQUENCE {
+ time[0] KerberosTime,
+ principal[1] Principal OPTIONAL
+}
+
+HDBFlags ::= BIT STRING {
+ initial(0), -- require as-req
+ forwardable(1), -- may issue forwardable
+ proxiable(2), -- may issue proxiable
+ renewable(3), -- may issue renewable
+ postdate(4), -- may issue postdatable
+ server(5), -- may be server
+ client(6), -- may be client
+ invalid(7), -- entry is invalid
+ require-preauth(8), -- must use preauth
+ change-pw(9), -- change password service
+ require-hwauth(10), -- must use hwauth
+ ok-as-delegate(11), -- as in TicketFlags
+ user-to-user(12), -- may use user-to-user auth
+ immutable(13), -- may not be deleted
+ trusted-for-delegation(14), -- Trusted to print forwardabled tickets
+ allow-kerberos4(15), -- Allow Kerberos 4 requests
+ allow-digest(16) -- Allow digest requests
+}
+
+GENERATION ::= SEQUENCE {
+ time[0] KerberosTime, -- timestamp
+ usec[1] INTEGER (0..4294967295), -- microseconds
+ gen[2] INTEGER (0..4294967295) -- generation number
+}
+
+HDB-Ext-PKINIT-acl ::= SEQUENCE OF SEQUENCE {
+ subject[0] UTF8String,
+ issuer[1] UTF8String OPTIONAL,
+ anchor[2] UTF8String OPTIONAL
+}
+
+HDB-Ext-PKINIT-hash ::= SEQUENCE OF SEQUENCE {
+ digest-type[0] OBJECT IDENTIFIER,
+ digest[1] OCTET STRING
+}
+
+HDB-Ext-Constrained-delegation-acl ::= SEQUENCE OF Principal
+
+-- hdb-ext-referrals ::= PA-SERVER-REFERRAL-DATA
+
+HDB-Ext-Lan-Manager-OWF ::= OCTET STRING
+
+HDB-Ext-Password ::= SEQUENCE {
+ mkvno[0] INTEGER (0..4294967295) OPTIONAL, -- master key version number
+ password OCTET STRING
+}
+
+HDB-Ext-Aliases ::= SEQUENCE {
+ case-insensitive[0] BOOLEAN, -- case insensitive name allowed
+ aliases[1] SEQUENCE OF Principal -- all names, inc primary
+}
+
+
+HDB-extension ::= SEQUENCE {
+ mandatory[0] BOOLEAN, -- kdc MUST understand this extension,
+ -- if not the whole entry must
+ -- be rejected
+ data[1] CHOICE {
+ pkinit-acl[0] HDB-Ext-PKINIT-acl,
+ pkinit-cert-hash[1] HDB-Ext-PKINIT-hash,
+ allowed-to-delegate-to[2] HDB-Ext-Constrained-delegation-acl,
+-- referral-info[3] HDB-Ext-Referrals,
+ lm-owf[4] HDB-Ext-Lan-Manager-OWF,
+ password[5] HDB-Ext-Password,
+ aliases[6] HDB-Ext-Aliases,
+ last-pw-change[7] KerberosTime,
+ ...
+ },
+ ...
+}
+
+HDB-extensions ::= SEQUENCE OF HDB-extension
+
+
+hdb_entry ::= SEQUENCE {
+ principal[0] Principal OPTIONAL, -- this is optional only
+ -- for compatibility with libkrb5
+ kvno[1] INTEGER (0..4294967295),
+ keys[2] SEQUENCE OF Key,
+ created-by[3] Event,
+ modified-by[4] Event OPTIONAL,
+ valid-start[5] KerberosTime OPTIONAL,
+ valid-end[6] KerberosTime OPTIONAL,
+ pw-end[7] KerberosTime OPTIONAL,
+ max-life[8] INTEGER (0..4294967295) OPTIONAL,
+ max-renew[9] INTEGER (0..4294967295) OPTIONAL,
+ flags[10] HDBFlags,
+ etypes[11] SEQUENCE OF INTEGER (0..4294967295) OPTIONAL,
+ generation[12] GENERATION OPTIONAL,
+ extensions[13] HDB-extensions OPTIONAL
+}
+
+hdb_entry_alias ::= [APPLICATION 0] SEQUENCE {
+ principal[0] Principal OPTIONAL
+}
+
+END
diff --git a/source4/heimdal/lib/hdb/hdb.c b/source4/heimdal/lib/hdb/hdb.c
new file mode 100644
index 0000000000..3fddabb2d0
--- /dev/null
+++ b/source4/heimdal/lib/hdb/hdb.c
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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 "hdb_locl.h"
+
+RCSID("$Id$");
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+struct hdb_method {
+ const char *prefix;
+ krb5_error_code (*create)(krb5_context, HDB **, const char *filename);
+};
+
+static struct hdb_method methods[] = {
+#if HAVE_DB1 || HAVE_DB3
+ {"db:", hdb_db_create},
+#endif
+#if HAVE_NDBM
+ {"ndbm:", hdb_ndbm_create},
+#endif
+#if defined(OPENLDAP) && !defined(OPENLDAP_MODULE)
+ {"ldap:", hdb_ldap_create},
+ {"ldapi:", hdb_ldapi_create},
+#endif
+#ifdef HAVE_LDB /* Used for integrated samba build */
+ {"ldb:", hdb_ldb_create},
+#endif
+ {NULL, NULL}
+};
+
+#if HAVE_DB1 || HAVE_DB3
+static struct hdb_method dbmetod = {"", hdb_db_create };
+#elif defined(HAVE_NDBM)
+static struct hdb_method dbmetod = {"", hdb_ndbm_create };
+#endif
+
+
+krb5_error_code
+hdb_next_enctype2key(krb5_context context,
+ const hdb_entry *e,
+ krb5_enctype enctype,
+ Key **key)
+{
+ Key *k;
+
+ for (k = *key ? (*key) + 1 : e->keys.val;
+ k < e->keys.val + e->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,
+ hdb_entry *e,
+ krb5_enctype enctype,
+ Key **key)
+{
+ *key = NULL;
+ return hdb_next_enctype2key(context, e, enctype, key);
+}
+
+void
+hdb_free_key(Key *key)
+{
+ memset(key->key.keyvalue.data,
+ 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_entry_ex *ent)
+{
+ int i;
+
+ if (ent->free_entry)
+ (*ent->free_entry)(context, ent);
+
+ for(i = 0; i < ent->entry.keys.len; ++i) {
+ Key *k = &ent->entry.keys.val[i];
+
+ memset (k->key.keyvalue.data, 0, k->key.keyvalue.length);
+ }
+ free_hdb_entry(&ent->entry);
+}
+
+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_ex entry;
+ ret = db->hdb_firstkey(context, db, flags, &entry);
+ if (ret == 0)
+ krb5_clear_error_string(context);
+ while(ret == 0){
+ ret = (*func)(context, db, &entry, data);
+ hdb_free_entry(context, &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 = 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 = 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_string(context);
+ return ret;
+ }
+ return ret2;
+}
+
+#ifdef HAVE_DLOPEN
+
+ /*
+ * Load a dynamic backend from /usr/heimdal/lib/hdb_NAME.so,
+ * looking for the hdb_NAME_create symbol.
+ */
+
+static const struct hdb_method *
+find_dynamic_method (krb5_context context,
+ const char *filename,
+ const char **rest)
+{
+ static struct hdb_method method;
+ struct hdb_so_method *mso;
+ char *prefix, *path, *symbol;
+ const char *p;
+ void *dl;
+ size_t len;
+
+ p = strchr(filename, ':');
+
+ /* if no prefix, don't know what module to load, just ignore it */
+ if (p == NULL)
+ return NULL;
+
+ len = p - filename;
+ *rest = filename + len + 1;
+
+ prefix = strndup(filename, len);
+ if (prefix == NULL)
+ krb5_errx(context, 1, "out of memory");
+
+ if (asprintf(&path, LIBDIR "/hdb_%s.so", prefix) == -1)
+ krb5_errx(context, 1, "out of memory");
+
+#ifndef RTLD_NOW
+#define RTLD_NOW 0
+#endif
+#ifndef RTLD_GLOBAL
+#define RTLD_GLOBAL 0
+#endif
+
+ dl = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
+ if (dl == NULL) {
+ krb5_warnx(context, "error trying to load dynamic module %s: %s\n",
+ path, dlerror());
+ free(prefix);
+ free(path);
+ return NULL;
+ }
+
+ if (asprintf(&symbol, "hdb_%s_interface", prefix) == -1)
+ krb5_errx(context, 1, "out of memory");
+
+ mso = dlsym(dl, symbol);
+ if (mso == NULL) {
+ krb5_warnx(context, "error finding symbol %s in %s: %s\n",
+ symbol, path, dlerror());
+ dlclose(dl);
+ free(symbol);
+ free(prefix);
+ free(path);
+ return NULL;
+ }
+ free(path);
+ free(symbol);
+
+ if (mso->version != HDB_INTERFACE_VERSION) {
+ krb5_warnx(context,
+ "error wrong version in shared module %s "
+ "version: %d should have been %d\n",
+ prefix, mso->version, HDB_INTERFACE_VERSION);
+ dlclose(dl);
+ free(prefix);
+ return NULL;
+ }
+
+ if (mso->create == NULL) {
+ krb5_errx(context, 1,
+ "no entry point function in shared mod %s ",
+ prefix);
+ dlclose(dl);
+ free(prefix);
+ return NULL;
+ }
+
+ method.create = mso->create;
+ method.prefix = prefix;
+
+ return &method;
+}
+#endif /* HAVE_DLOPEN */
+
+/*
+ * 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;
+
+ for (h = methods; h->prefix != NULL; ++h) {
+ if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0) {
+ *rest = filename + strlen(h->prefix);
+ return h;
+ }
+ }
+#if defined(HAVE_DB1) || defined(HAVE_DB3) || defined(HAVE_NDBM)
+ if (strncmp(filename, "/", 1) == 0
+ || strncmp(filename, "./", 2) == 0
+ || strncmp(filename, "../", 3) == 0)
+ {
+ *rest = filename;
+ return &dbmetod;
+ }
+#endif
+
+ return 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) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ buf[0] = '\0';
+
+ for (h = methods; h->prefix != NULL; ++h) {
+ if (h != methods)
+ strlcat(buf, ", ", len);
+ strlcat(buf, h->prefix, len);
+ }
+ *list = buf;
+ return 0;
+}
+
+krb5_error_code
+hdb_create(krb5_context context, HDB **db, const char *filename)
+{
+ const struct hdb_method *h;
+ const char *residual;
+
+ if(filename == NULL)
+ filename = HDB_DEFAULT_DB;
+ krb5_add_et_list(context, initialize_hdb_error_table_r);
+ h = find_method (filename, &residual);
+#ifdef HAVE_DLOPEN
+ if (h == NULL)
+ h = find_dynamic_method (context, filename, &residual);
+#endif
+ if (h == NULL)
+ krb5_errx(context, 1, "No database support for %s", filename);
+ return (*h->create)(context, db, residual);
+}
diff --git a/source4/heimdal/lib/hdb/hdb.h b/source4/heimdal/lib/hdb/hdb.h
new file mode 100644
index 0000000000..bc1b744015
--- /dev/null
+++ b/source4/heimdal/lib/hdb/hdb.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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.
+ */
+
+/* $Id$ */
+
+#ifndef __HDB_H__
+#define __HDB_H__
+
+#include <hdb_err.h>
+
+#include <heim_asn1.h>
+#include <hdb_asn1.h>
+
+struct hdb_dbinfo;
+
+enum hdb_lockop{ HDB_RLOCK, HDB_WLOCK };
+
+/* flags for various functions */
+#define HDB_F_DECRYPT 1 /* decrypt keys */
+#define HDB_F_REPLACE 2 /* replace entry */
+#define HDB_F_GET_CLIENT 4 /* fetch client */
+#define HDB_F_GET_SERVER 8 /* fetch server */
+#define HDB_F_GET_KRBTGT 16 /* fetch krbtgt */
+#define HDB_F_GET_ANY 28 /* fetch any of client,server,krbtgt */
+#define HDB_F_CANON 32 /* want canonicalition */
+
+/* key usage for master key */
+#define HDB_KU_MKEY 0x484442
+
+typedef struct hdb_master_key_data *hdb_master_key;
+
+typedef struct hdb_entry_ex {
+ void *ctx;
+ hdb_entry entry;
+ void (*free_entry)(krb5_context, struct hdb_entry_ex *);
+} hdb_entry_ex;
+
+
+typedef struct HDB{
+ void *hdb_db;
+ void *hdb_dbc;
+ char *hdb_name;
+ int hdb_master_key_set;
+ hdb_master_key hdb_master_key;
+ int hdb_openp;
+
+ krb5_error_code (*hdb_open)(krb5_context,
+ struct HDB*,
+ int,
+ mode_t);
+ krb5_error_code (*hdb_close)(krb5_context,
+ struct HDB*);
+ void (*hdb_free)(krb5_context,
+ struct HDB*,
+ hdb_entry_ex*);
+ krb5_error_code (*hdb_fetch)(krb5_context,
+ struct HDB*,
+ krb5_const_principal,
+ unsigned,
+ hdb_entry_ex*);
+ krb5_error_code (*hdb_store)(krb5_context,
+ struct HDB*,
+ unsigned,
+ hdb_entry_ex*);
+ krb5_error_code (*hdb_remove)(krb5_context,
+ struct HDB*,
+ krb5_const_principal);
+ krb5_error_code (*hdb_firstkey)(krb5_context,
+ struct HDB*,
+ unsigned,
+ hdb_entry_ex*);
+ krb5_error_code (*hdb_nextkey)(krb5_context,
+ struct HDB*,
+ unsigned,
+ hdb_entry_ex*);
+ krb5_error_code (*hdb_lock)(krb5_context,
+ struct HDB*,
+ int operation);
+ krb5_error_code (*hdb_unlock)(krb5_context,
+ struct HDB*);
+ krb5_error_code (*hdb_rename)(krb5_context,
+ struct HDB*,
+ const char*);
+ krb5_error_code (*hdb__get)(krb5_context,
+ struct HDB*,
+ krb5_data,
+ krb5_data*);
+ krb5_error_code (*hdb__put)(krb5_context,
+ struct HDB*,
+ int,
+ krb5_data,
+ krb5_data);
+ krb5_error_code (*hdb__del)(krb5_context,
+ struct HDB*,
+ krb5_data);
+ krb5_error_code (*hdb_destroy)(krb5_context,
+ struct HDB*);
+}HDB;
+
+#define HDB_INTERFACE_VERSION 4
+
+struct hdb_so_method {
+ int version;
+ const char *prefix;
+ krb5_error_code (*create)(krb5_context, HDB **, const char *filename);
+};
+
+typedef krb5_error_code (*hdb_foreach_func_t)(krb5_context, HDB*,
+ hdb_entry_ex*, void*);
+extern krb5_kt_ops hdb_kt_ops;
+
+#include <hdb-protos.h>
+
+#endif /* __HDB_H__ */
diff --git a/source4/heimdal/lib/hdb/hdb_err.et b/source4/heimdal/lib/hdb/hdb_err.et
new file mode 100644
index 0000000000..64f79fc84e
--- /dev/null
+++ b/source4/heimdal/lib/hdb/hdb_err.et
@@ -0,0 +1,28 @@
+#
+# Error messages for the hdb library
+#
+# This might look like a com_err file, but is not
+#
+id "$Id$"
+
+error_table hdb
+
+prefix HDB_ERR
+
+index 1
+#error_code INUSE, "Entry already exists in database"
+error_code UK_SERROR, "Database store error"
+error_code UK_RERROR, "Database read error"
+error_code NOENTRY, "No such entry in the database"
+error_code DB_INUSE, "Database is locked or in use--try again later"
+error_code DB_CHANGED, "Database was modified during read"
+error_code RECURSIVELOCK, "Attempt to lock database twice"
+error_code NOTLOCKED, "Attempt to unlock database when not locked"
+error_code BADLOCKMODE, "Invalid kdb lock mode"
+error_code CANT_LOCK_DB, "Insufficient access to lock database"
+error_code EXISTS, "Entry already exists in database"
+error_code BADVERSION, "Wrong database version"
+error_code NO_MKEY, "No correct master key"
+error_code MANDATORY_OPTION, "Entry contains unknown mandatory extension"
+
+end
diff --git a/source4/heimdal/lib/hdb/hdb_locl.h b/source4/heimdal/lib/hdb/hdb_locl.h
new file mode 100644
index 0000000000..9229146d04
--- /dev/null
+++ b/source4/heimdal/lib/hdb/hdb_locl.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1997-2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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.
+ */
+
+/* $Id$ */
+
+#ifndef __HDB_LOCL_H__
+#define __HDB_LOCL_H__
+
+#include <config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#include <roken.h>
+
+#include "crypto-headers.h"
+#include <krb5.h>
+#include <hdb.h>
+#include <hdb-private.h>
+
+#define HDB_DEFAULT_DB HDB_DB_DIR "/heimdal"
+#define HDB_DB_FORMAT_ENTRY "hdb/db-format"
+
+#endif /* __HDB_LOCL_H__ */
diff --git a/source4/heimdal/lib/hdb/keys.c b/source4/heimdal/lib/hdb/keys.c
new file mode 100644
index 0000000000..e649f445e0
--- /dev/null
+++ b/source4/heimdal/lib/hdb/keys.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 1997 - 2001, 2003 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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 "hdb_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * free all the memory used by (len, keys)
+ */
+
+void
+hdb_free_keys (krb5_context context, int len, Key *keys)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ free(keys[i].mkvno);
+ keys[i].mkvno = NULL;
+ if (keys[i].salt != NULL) {
+ free_Salt(keys[i].salt);
+ free(keys[i].salt);
+ keys[i].salt = NULL;
+ }
+ krb5_free_keyblock_contents(context, &keys[i].key);
+ }
+ free (keys);
+}
+
+/*
+ * for each entry in `default_keys' try to parse it as a sequence
+ * of etype:salttype:salt, syntax of this if something like:
+ * [(des|des3|etype):](pw-salt|afs3)[:string], if etype is omitted it
+ * means all etypes, and if string is omitted is means the default
+ * string (for that principal). Additional special values:
+ * v5 == pw-salt, and
+ * v4 == des:pw-salt:
+ * afs or afs3 == des:afs3-salt
+ */
+
+static const krb5_enctype des_etypes[] = {
+ ETYPE_DES_CBC_MD5,
+ ETYPE_DES_CBC_MD4,
+ ETYPE_DES_CBC_CRC
+};
+
+static const krb5_enctype all_etypes[] = {
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ ETYPE_ARCFOUR_HMAC_MD5,
+ ETYPE_DES3_CBC_SHA1
+};
+
+static krb5_error_code
+parse_key_set(krb5_context context, const char *key,
+ krb5_enctype **ret_enctypes, size_t *ret_num_enctypes,
+ krb5_salt *salt, krb5_principal principal)
+{
+ const char *p;
+ char buf[3][256];
+ int num_buf = 0;
+ int i, num_enctypes = 0;
+ krb5_enctype e;
+ const krb5_enctype *enctypes = NULL;
+ krb5_error_code ret;
+
+ p = key;
+
+ *ret_enctypes = NULL;
+ *ret_num_enctypes = 0;
+
+ /* split p in a list of :-separated strings */
+ for(num_buf = 0; num_buf < 3; num_buf++)
+ if(strsep_copy(&p, ":", buf[num_buf], sizeof(buf[num_buf])) == -1)
+ break;
+
+ salt->saltvalue.data = NULL;
+ salt->saltvalue.length = 0;
+
+ for(i = 0; i < num_buf; i++) {
+ if(enctypes == NULL && num_buf > 1) {
+ /* this might be a etype specifier */
+ /* XXX there should be a string_to_etypes handling
+ special cases like `des' and `all' */
+ if(strcmp(buf[i], "des") == 0) {
+ enctypes = des_etypes;
+ num_enctypes = sizeof(des_etypes)/sizeof(des_etypes[0]);
+ } else if(strcmp(buf[i], "des3") == 0) {
+ e = ETYPE_DES3_CBC_SHA1;
+ enctypes = &e;
+ num_enctypes = 1;
+ } else {
+ ret = krb5_string_to_enctype(context, buf[i], &e);
+ if (ret == 0) {
+ enctypes = &e;
+ num_enctypes = 1;
+ } else
+ return ret;
+ }
+ continue;
+ }
+ if(salt->salttype == 0) {
+ /* interpret string as a salt specifier, if no etype
+ is set, this sets default values */
+ /* XXX should perhaps use string_to_salttype, but that
+ interface sucks */
+ if(strcmp(buf[i], "pw-salt") == 0) {
+ if(enctypes == NULL) {
+ enctypes = all_etypes;
+ num_enctypes = sizeof(all_etypes)/sizeof(all_etypes[0]);
+ }
+ salt->salttype = KRB5_PW_SALT;
+ } else if(strcmp(buf[i], "afs3-salt") == 0) {
+ if(enctypes == NULL) {
+ enctypes = des_etypes;
+ num_enctypes = sizeof(des_etypes)/sizeof(des_etypes[0]);
+ }
+ salt->salttype = KRB5_AFS3_SALT;
+ }
+ continue;
+ }
+
+ {
+ /* if there is a final string, use it as the string to
+ salt with, this is mostly useful with null salt for
+ v4 compat, and a cell name for afs compat */
+ salt->saltvalue.data = strdup(buf[i]);
+ if (salt->saltvalue.data == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ salt->saltvalue.length = strlen(buf[i]);
+ }
+ }
+
+ if(enctypes == NULL || salt->salttype == 0) {
+ krb5_set_error_message(context, EINVAL, "bad value for default_keys `%s'", key);
+ return EINVAL;
+ }
+
+ /* if no salt was specified make up default salt */
+ if(salt->saltvalue.data == NULL) {
+ if(salt->salttype == KRB5_PW_SALT)
+ ret = krb5_get_pw_salt(context, principal, salt);
+ else if(salt->salttype == KRB5_AFS3_SALT) {
+ krb5_realm *realm = krb5_princ_realm(context, principal);
+ salt->saltvalue.data = strdup(*realm);
+ if(salt->saltvalue.data == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ "out of memory while "
+ "parsing salt specifiers");
+ return ENOMEM;
+ }
+ strlwr(salt->saltvalue.data);
+ salt->saltvalue.length = strlen(*realm);
+ }
+ }
+
+ *ret_enctypes = malloc(sizeof(enctypes[0]) * num_enctypes);
+ if (*ret_enctypes == NULL) {
+ krb5_free_salt(context, *salt);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memcpy(*ret_enctypes, enctypes, sizeof(enctypes[0]) * num_enctypes);
+ *ret_num_enctypes = num_enctypes;
+
+ return 0;
+}
+
+static krb5_error_code
+add_enctype_to_key_set(Key **key_set, size_t *nkeyset,
+ krb5_enctype enctype, krb5_salt *salt)
+{
+ krb5_error_code ret;
+ Key key, *tmp;
+
+ memset(&key, 0, sizeof(key));
+
+ tmp = realloc(*key_set, (*nkeyset + 1) * sizeof((*key_set)[0]));
+ if (tmp == NULL)
+ return ENOMEM;
+
+ *key_set = tmp;
+
+ key.key.keytype = enctype;
+ key.key.keyvalue.length = 0;
+ key.key.keyvalue.data = NULL;
+
+ if (salt) {
+ key.salt = malloc(sizeof(*key.salt));
+ if (key.salt == NULL) {
+ free_Key(&key);
+ return ENOMEM;
+ }
+
+ key.salt->type = salt->salttype;
+ krb5_data_zero (&key.salt->salt);
+
+ ret = krb5_data_copy(&key.salt->salt,
+ salt->saltvalue.data,
+ salt->saltvalue.length);
+ if (ret) {
+ free_Key(&key);
+ return ret;
+ }
+ } else
+ key.salt = NULL;
+
+ (*key_set)[*nkeyset] = key;
+
+ *nkeyset += 1;
+
+ return 0;
+}
+
+
+/*
+ * Generate the `key_set' from the [kadmin]default_keys statement. If
+ * `no_salt' is set, salt is not important (and will not be set) since
+ * it's random keys that is going to be created.
+ */
+
+krb5_error_code
+hdb_generate_key_set(krb5_context context, krb5_principal principal,
+ Key **ret_key_set, size_t *nkeyset, int no_salt)
+{
+ char **ktypes, **kp;
+ krb5_error_code ret;
+ Key *k, *key_set;
+ int i, j;
+ char *default_keytypes[] = {
+ "des:pw-salt",
+ "aes256-cts-hmac-sha1-96:pw-salt",
+ "des3-cbc-sha1:pw-salt",
+ "arcfour-hmac-md5:pw-salt",
+ NULL
+ };
+
+ ktypes = krb5_config_get_strings(context, NULL, "kadmin",
+ "default_keys", NULL);
+ if (ktypes == NULL)
+ ktypes = default_keytypes;
+
+ if (ktypes == NULL)
+ abort();
+
+ *ret_key_set = key_set = NULL;
+ *nkeyset = 0;
+
+ ret = 0;
+
+ for(kp = ktypes; kp && *kp; kp++) {
+ const char *p;
+ krb5_salt salt;
+ krb5_enctype *enctypes;
+ size_t num_enctypes;
+
+ p = *kp;
+ /* check alias */
+ if(strcmp(p, "v5") == 0)
+ p = "pw-salt";
+ else if(strcmp(p, "v4") == 0)
+ p = "des:pw-salt:";
+ else if(strcmp(p, "afs") == 0 || strcmp(p, "afs3") == 0)
+ p = "des:afs3-salt";
+ else if (strcmp(p, "arcfour-hmac-md5") == 0)
+ p = "arcfour-hmac-md5:pw-salt";
+
+ memset(&salt, 0, sizeof(salt));
+
+ ret = parse_key_set(context, p,
+ &enctypes, &num_enctypes, &salt, principal);
+ if (ret) {
+ krb5_warn(context, ret, "bad value for default_keys `%s'", *kp);
+ ret = 0;
+ continue;
+ }
+
+ for (i = 0; i < num_enctypes; i++) {
+ /* find duplicates */
+ for (j = 0; j < *nkeyset; j++) {
+
+ k = &key_set[j];
+
+ if (k->key.keytype == enctypes[i]) {
+ if (no_salt)
+ break;
+ if (k->salt == NULL && salt.salttype == KRB5_PW_SALT)
+ break;
+ if (k->salt->type == salt.salttype &&
+ k->salt->salt.length == salt.saltvalue.length &&
+ memcmp(k->salt->salt.data, salt.saltvalue.data,
+ salt.saltvalue.length) == 0)
+ break;
+ }
+ }
+ /* not a duplicate, lets add it */
+ if (j == *nkeyset) {
+ ret = add_enctype_to_key_set(&key_set, nkeyset, enctypes[i],
+ no_salt ? NULL : &salt);
+ if (ret) {
+ free(enctypes);
+ krb5_free_salt(context, salt);
+ goto out;
+ }
+ }
+ }
+ free(enctypes);
+ krb5_free_salt(context, salt);
+ }
+
+ *ret_key_set = key_set;
+
+ out:
+ if (ktypes != default_keytypes)
+ krb5_config_free_strings(ktypes);
+
+ if (ret) {
+ krb5_warn(context, ret,
+ "failed to parse the [kadmin]default_keys values");
+
+ for (i = 0; i < *nkeyset; i++)
+ free_Key(&key_set[i]);
+ free(key_set);
+ } else if (*nkeyset == 0) {
+ krb5_warnx(context,
+ "failed to parse any of the [kadmin]default_keys values");
+ ret = EINVAL; /* XXX */
+ }
+
+ return ret;
+}
+
+
+krb5_error_code
+hdb_generate_key_set_password(krb5_context context,
+ krb5_principal principal,
+ const char *password,
+ Key **keys, size_t *num_keys)
+{
+ krb5_error_code ret;
+ int i;
+
+ ret = hdb_generate_key_set(context, principal,
+ keys, num_keys, 0);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < (*num_keys); i++) {
+ krb5_salt salt;
+
+ salt.salttype = (*keys)[i].salt->type;
+ salt.saltvalue.length = (*keys)[i].salt->salt.length;
+ salt.saltvalue.data = (*keys)[i].salt->salt.data;
+
+ ret = krb5_string_to_key_salt (context,
+ (*keys)[i].key.keytype,
+ password,
+ salt,
+ &(*keys)[i].key);
+
+ if(ret)
+ break;
+ }
+
+ if(ret) {
+ hdb_free_keys (context, *num_keys, *keys);
+ return ret;
+ }
+ return ret;
+}
diff --git a/source4/heimdal/lib/hdb/keytab.c b/source4/heimdal/lib/hdb/keytab.c
new file mode 100644
index 0000000000..b2d1fec3d2
--- /dev/null
+++ b/source4/heimdal/lib/hdb/keytab.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 1999 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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 "hdb_locl.h"
+
+/* keytab backend for HDB databases */
+
+RCSID("$Id$");
+
+struct hdb_data {
+ char *dbname;
+ char *mkey;
+};
+
+/*
+ * the format for HDB keytabs is:
+ * HDB:[database:file:mkey]
+ */
+
+static krb5_error_code
+hdb_resolve(krb5_context context, const char *name, krb5_keytab id)
+{
+ struct hdb_data *d;
+ const char *db, *mkey;
+
+ d = malloc(sizeof(*d));
+ if(d == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ db = name;
+ mkey = strrchr(name, ':');
+ if(mkey == NULL || mkey[1] == '\0') {
+ if(*name == '\0')
+ d->dbname = NULL;
+ else {
+ d->dbname = strdup(name);
+ if(d->dbname == NULL) {
+ free(d);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ }
+ d->mkey = NULL;
+ } else {
+ if((mkey - db) == 0) {
+ d->dbname = NULL;
+ } else {
+ d->dbname = malloc(mkey - db + 1);
+ if(d->dbname == NULL) {
+ free(d);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memmove(d->dbname, db, mkey - db);
+ d->dbname[mkey - db] = '\0';
+ }
+ d->mkey = strdup(mkey + 1);
+ if(d->mkey == NULL) {
+ free(d->dbname);
+ free(d);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ }
+ id->data = d;
+ return 0;
+}
+
+static krb5_error_code
+hdb_close(krb5_context context, krb5_keytab id)
+{
+ struct hdb_data *d = id->data;
+
+ free(d->dbname);
+ free(d->mkey);
+ free(d);
+ return 0;
+}
+
+static krb5_error_code
+hdb_get_name(krb5_context context,
+ krb5_keytab id,
+ char *name,
+ size_t namesize)
+{
+ struct hdb_data *d = id->data;
+
+ snprintf(name, namesize, "%s%s%s",
+ d->dbname ? d->dbname : "",
+ (d->dbname || d->mkey) ? ":" : "",
+ d->mkey ? d->mkey : "");
+ return 0;
+}
+
+static void
+set_config (krb5_context context,
+ const krb5_config_binding *binding,
+ const char **dbname,
+ const char **mkey)
+{
+ *dbname = krb5_config_get_string(context, binding, "dbname", NULL);
+ *mkey = krb5_config_get_string(context, binding, "mkey_file", NULL);
+}
+
+/*
+ * try to figure out the database (`dbname') and master-key (`mkey')
+ * that should be used for `principal'.
+ */
+
+static void
+find_db (krb5_context context,
+ const char **dbname,
+ const char **mkey,
+ krb5_const_principal principal)
+{
+ const krb5_config_binding *top_bind = NULL;
+ const krb5_config_binding *default_binding = NULL;
+ const krb5_config_binding *db;
+ krb5_realm *prealm = krb5_princ_realm(context, rk_UNCONST(principal));
+
+ *dbname = *mkey = NULL;
+
+ while ((db =
+ krb5_config_get_next(context,
+ NULL,
+ &top_bind,
+ krb5_config_list,
+ "kdc",
+ "database",
+ NULL)) != NULL) {
+ const char *p;
+
+ p = krb5_config_get_string (context, db, "realm", NULL);
+ if (p == NULL) {
+ if(default_binding) {
+ krb5_warnx(context, "WARNING: more than one realm-less "
+ "database specification");
+ krb5_warnx(context, "WARNING: using the first encountered");
+ } else
+ default_binding = db;
+ } else if (strcmp (*prealm, p) == 0) {
+ set_config (context, db, dbname, mkey);
+ break;
+ }
+ }
+ if (*dbname == NULL && default_binding != NULL)
+ set_config (context, default_binding, dbname, mkey);
+ if (*dbname == NULL)
+ *dbname = HDB_DEFAULT_DB;
+}
+
+/*
+ * find the keytab entry in `id' for `principal, kvno, enctype' and return
+ * it in `entry'. return 0 or an error code
+ */
+
+static krb5_error_code
+hdb_get_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_const_principal principal,
+ krb5_kvno kvno,
+ krb5_enctype enctype,
+ krb5_keytab_entry *entry)
+{
+ hdb_entry_ex ent;
+ krb5_error_code ret;
+ struct hdb_data *d = id->data;
+ int i;
+ HDB *db;
+ const char *dbname = d->dbname;
+ const char *mkey = d->mkey;
+
+ memset(&ent, 0, sizeof(ent));
+
+ if (dbname == NULL)
+ find_db (context, &dbname, &mkey, principal);
+
+ ret = hdb_create (context, &db, dbname);
+ if (ret)
+ return ret;
+ ret = hdb_set_master_keyfile (context, db, mkey);
+ if (ret) {
+ (*db->hdb_destroy)(context, db);
+ return ret;
+ }
+
+ ret = (*db->hdb_open)(context, db, O_RDONLY, 0);
+ if (ret) {
+ (*db->hdb_destroy)(context, db);
+ return ret;
+ }
+ ret = (*db->hdb_fetch)(context, db, principal,
+ HDB_F_DECRYPT|
+ HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
+ &ent);
+
+ if(ret == HDB_ERR_NOENTRY) {
+ ret = KRB5_KT_NOTFOUND;
+ goto out;
+ }else if(ret)
+ goto out;
+
+ if(kvno && ent.entry.kvno != kvno) {
+ hdb_free_entry(context, &ent);
+ ret = KRB5_KT_NOTFOUND;
+ goto out;
+ }
+ if(enctype == 0)
+ if(ent.entry.keys.len > 0)
+ enctype = ent.entry.keys.val[0].key.keytype;
+ ret = KRB5_KT_NOTFOUND;
+ for(i = 0; i < ent.entry.keys.len; i++) {
+ if(ent.entry.keys.val[i].key.keytype == enctype) {
+ krb5_copy_principal(context, principal, &entry->principal);
+ entry->vno = ent.entry.kvno;
+ krb5_copy_keyblock_contents(context,
+ &ent.entry.keys.val[i].key,
+ &entry->keyblock);
+ ret = 0;
+ break;
+ }
+ }
+ hdb_free_entry(context, &ent);
+out:
+ (*db->hdb_close)(context, db);
+ (*db->hdb_destroy)(context, db);
+ return ret;
+}
+
+krb5_kt_ops hdb_kt_ops = {
+ "HDB",
+ hdb_resolve,
+ hdb_get_name,
+ hdb_close,
+ hdb_get_entry,
+ NULL, /* start_seq_get */
+ NULL, /* next_entry */
+ NULL, /* end_seq_get */
+ NULL, /* add */
+ NULL /* remove */
+};
diff --git a/source4/heimdal/lib/hdb/mkey.c b/source4/heimdal/lib/hdb/mkey.c
new file mode 100644
index 0000000000..7d2958b4ac
--- /dev/null
+++ b/source4/heimdal/lib/hdb/mkey.c
@@ -0,0 +1,604 @@
+/*
+ * Copyright (c) 2000 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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 "hdb_locl.h"
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+RCSID("$Id$");
+
+struct hdb_master_key_data {
+ krb5_keytab_entry keytab;
+ krb5_crypto crypto;
+ struct hdb_master_key_data *next;
+};
+
+void
+hdb_free_master_key(krb5_context context, hdb_master_key mkey)
+{
+ struct hdb_master_key_data *ptr;
+ while(mkey) {
+ krb5_kt_free_entry(context, &mkey->keytab);
+ if (mkey->crypto)
+ krb5_crypto_destroy(context, mkey->crypto);
+ ptr = mkey;
+ mkey = mkey->next;
+ free(ptr);
+ }
+}
+
+krb5_error_code
+hdb_process_master_key(krb5_context context,
+ int kvno, krb5_keyblock *key, krb5_enctype etype,
+ hdb_master_key *mkey)
+{
+ krb5_error_code ret;
+
+ *mkey = calloc(1, sizeof(**mkey));
+ if(*mkey == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ (*mkey)->keytab.vno = kvno;
+ ret = krb5_parse_name(context, "K/M", &(*mkey)->keytab.principal);
+ if(ret)
+ goto fail;
+ ret = krb5_copy_keyblock_contents(context, key, &(*mkey)->keytab.keyblock);
+ if(ret)
+ goto fail;
+ if(etype != 0)
+ (*mkey)->keytab.keyblock.keytype = etype;
+ (*mkey)->keytab.timestamp = time(NULL);
+ ret = krb5_crypto_init(context, key, etype, &(*mkey)->crypto);
+ if(ret)
+ goto fail;
+ return 0;
+ fail:
+ hdb_free_master_key(context, *mkey);
+ *mkey = NULL;
+ return ret;
+}
+
+krb5_error_code
+hdb_add_master_key(krb5_context context, krb5_keyblock *key,
+ hdb_master_key *inout)
+{
+ int vno = 0;
+ hdb_master_key p;
+ krb5_error_code ret;
+
+ for(p = *inout; p; p = p->next)
+ vno = max(vno, p->keytab.vno);
+ vno++;
+ ret = hdb_process_master_key(context, vno, key, 0, &p);
+ if(ret)
+ return ret;
+ p->next = *inout;
+ *inout = p;
+ return 0;
+}
+
+static krb5_error_code
+read_master_keytab(krb5_context context, const char *filename,
+ hdb_master_key *mkey)
+{
+ krb5_error_code ret;
+ krb5_keytab id;
+ krb5_kt_cursor cursor;
+ krb5_keytab_entry entry;
+ hdb_master_key p;
+
+ ret = krb5_kt_resolve(context, filename, &id);
+ if(ret)
+ return ret;
+
+ ret = krb5_kt_start_seq_get(context, id, &cursor);
+ if(ret)
+ goto out;
+ *mkey = NULL;
+ while(krb5_kt_next_entry(context, id, &entry, &cursor) == 0) {
+ p = calloc(1, sizeof(*p));
+ if(p == NULL) {
+ krb5_kt_end_seq_get(context, id, &cursor);
+ ret = ENOMEM;
+ goto out;
+ }
+ p->keytab = entry;
+ ret = krb5_crypto_init(context, &p->keytab.keyblock, 0, &p->crypto);
+ p->next = *mkey;
+ *mkey = p;
+ }
+ krb5_kt_end_seq_get(context, id, &cursor);
+ out:
+ krb5_kt_close(context, id);
+ return ret;
+}
+
+/* read a MIT master keyfile */
+static krb5_error_code
+read_master_mit(krb5_context context, const char *filename,
+ hdb_master_key *mkey)
+{
+ int fd;
+ krb5_error_code ret;
+ krb5_storage *sp;
+ int16_t enctype;
+ krb5_keyblock key;
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if(fd < 0) {
+ int save_errno = errno;
+ krb5_set_error_message(context, save_errno, "failed to open %s: %s",
+ filename, strerror(save_errno));
+ return save_errno;
+ }
+ sp = krb5_storage_from_fd(fd);
+ if(sp == NULL) {
+ close(fd);
+ return errno;
+ }
+ krb5_storage_set_flags(sp, KRB5_STORAGE_HOST_BYTEORDER);
+#if 0
+ /* could possibly use ret_keyblock here, but do it with more
+ checks for now */
+ ret = krb5_ret_keyblock(sp, &key);
+#else
+ ret = krb5_ret_int16(sp, &enctype);
+ if((htons(enctype) & 0xff00) == 0x3000) {
+ ret = HEIM_ERR_BAD_MKEY;
+ krb5_set_error_message(context, ret, "unknown keytype in %s: %#x, expected %#x",
+ filename, htons(enctype), 0x3000);
+ goto out;
+ }
+ key.keytype = enctype;
+ ret = krb5_ret_data(sp, &key.keyvalue);
+ if(ret)
+ goto out;
+#endif
+ ret = hdb_process_master_key(context, 0, &key, 0, mkey);
+ krb5_free_keyblock_contents(context, &key);
+ out:
+ krb5_storage_free(sp);
+ close(fd);
+ return ret;
+}
+
+/* read an old master key file */
+static krb5_error_code
+read_master_encryptionkey(krb5_context context, const char *filename,
+ hdb_master_key *mkey)
+{
+ int fd;
+ krb5_keyblock key;
+ krb5_error_code ret;
+ unsigned char buf[256];
+ ssize_t len;
+ size_t ret_len;
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if(fd < 0) {
+ int save_errno = errno;
+ krb5_set_error_message(context, save_errno, "failed to open %s: %s",
+ filename, strerror(save_errno));
+ return save_errno;
+ }
+
+ len = read(fd, buf, sizeof(buf));
+ close(fd);
+ if(len < 0) {
+ int save_errno = errno;
+ krb5_set_error_message(context, save_errno, "error reading %s: %s",
+ filename, strerror(save_errno));
+ return save_errno;
+ }
+
+ ret = decode_EncryptionKey(buf, len, &key, &ret_len);
+ memset(buf, 0, sizeof(buf));
+ if(ret)
+ return ret;
+
+ /* Originally, the keytype was just that, and later it got changed
+ to des-cbc-md5, but we always used des in cfb64 mode. This
+ should cover all cases, but will break if someone has hacked
+ this code to really use des-cbc-md5 -- but then that's not my
+ problem. */
+ if(key.keytype == KEYTYPE_DES || key.keytype == ETYPE_DES_CBC_MD5)
+ key.keytype = ETYPE_DES_CFB64_NONE;
+
+ ret = hdb_process_master_key(context, 0, &key, 0, mkey);
+ krb5_free_keyblock_contents(context, &key);
+ return ret;
+}
+
+/* read a krb4 /.k style file */
+static krb5_error_code
+read_master_krb4(krb5_context context, const char *filename,
+ hdb_master_key *mkey)
+{
+ int fd;
+ krb5_keyblock key;
+ krb5_error_code ret;
+ unsigned char buf[256];
+ ssize_t len;
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if(fd < 0) {
+ int save_errno = errno;
+ krb5_set_error_message(context, save_errno, "failed to open %s: %s",
+ filename, strerror(save_errno));
+ return save_errno;
+ }
+
+ len = read(fd, buf, sizeof(buf));
+ close(fd);
+ if(len < 0) {
+ int save_errno = errno;
+ krb5_set_error_message(context, save_errno, "error reading %s: %s",
+ filename, strerror(save_errno));
+ return save_errno;
+ }
+ if(len != 8) {
+ krb5_set_error_message(context, HEIM_ERR_EOF,
+ "bad contents of %s", filename);
+ return HEIM_ERR_EOF; /* XXX file might be too large */
+ }
+
+ memset(&key, 0, sizeof(key));
+ key.keytype = ETYPE_DES_PCBC_NONE;
+ ret = krb5_data_copy(&key.keyvalue, buf, len);
+ memset(buf, 0, sizeof(buf));
+ if(ret)
+ return ret;
+
+ ret = hdb_process_master_key(context, 0, &key, 0, mkey);
+ krb5_free_keyblock_contents(context, &key);
+ return ret;
+}
+
+krb5_error_code
+hdb_read_master_key(krb5_context context, const char *filename,
+ hdb_master_key *mkey)
+{
+ FILE *f;
+ unsigned char buf[16];
+ krb5_error_code ret;
+
+ off_t len;
+
+ *mkey = NULL;
+
+ if(filename == NULL)
+ filename = HDB_DB_DIR "/m-key";
+
+ f = fopen(filename, "r");
+ if(f == NULL) {
+ int save_errno = errno;
+ krb5_set_error_message(context, save_errno, "failed to open %s: %s",
+ filename, strerror(save_errno));
+ return save_errno;
+ }
+
+ if(fread(buf, 1, 2, f) != 2) {
+ fclose(f);
+ krb5_set_error_message(context, HEIM_ERR_EOF, "end of file reading %s", filename);
+ return HEIM_ERR_EOF;
+ }
+
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+
+ if(fclose(f) != 0)
+ return errno;
+
+ if(len < 0)
+ return errno;
+
+ if(len == 8) {
+ ret = read_master_krb4(context, filename, mkey);
+ } else if(buf[0] == 0x30 && len <= 127 && buf[1] == len - 2) {
+ ret = read_master_encryptionkey(context, filename, mkey);
+ } else if(buf[0] == 5 && buf[1] >= 1 && buf[1] <= 2) {
+ ret = read_master_keytab(context, filename, mkey);
+ } else {
+ ret = read_master_mit(context, filename, mkey);
+ }
+ return ret;
+}
+
+krb5_error_code
+hdb_write_master_key(krb5_context context, const char *filename,
+ hdb_master_key mkey)
+{
+ krb5_error_code ret;
+ hdb_master_key p;
+ krb5_keytab kt;
+
+ if(filename == NULL)
+ filename = HDB_DB_DIR "/m-key";
+
+ ret = krb5_kt_resolve(context, filename, &kt);
+ if(ret)
+ return ret;
+
+ for(p = mkey; p; p = p->next) {
+ ret = krb5_kt_add_entry(context, kt, &p->keytab);
+ }
+
+ krb5_kt_close(context, kt);
+
+ return ret;
+}
+
+hdb_master_key
+_hdb_find_master_key(uint32_t *mkvno, hdb_master_key mkey)
+{
+ hdb_master_key ret = NULL;
+ while(mkey) {
+ if(ret == NULL && mkey->keytab.vno == 0)
+ ret = mkey;
+ if(mkvno == NULL) {
+ if(ret == NULL || mkey->keytab.vno > ret->keytab.vno)
+ ret = mkey;
+ } else if(mkey->keytab.vno == *mkvno)
+ return mkey;
+ mkey = mkey->next;
+ }
+ return ret;
+}
+
+int
+_hdb_mkey_version(hdb_master_key mkey)
+{
+ return mkey->keytab.vno;
+}
+
+int
+_hdb_mkey_decrypt(krb5_context context, hdb_master_key key,
+ krb5_key_usage usage,
+ void *ptr, size_t size, krb5_data *res)
+{
+ return krb5_decrypt(context, key->crypto, usage,
+ ptr, size, res);
+}
+
+int
+_hdb_mkey_encrypt(krb5_context context, hdb_master_key key,
+ krb5_key_usage usage,
+ const void *ptr, size_t size, krb5_data *res)
+{
+ return krb5_encrypt(context, key->crypto, usage,
+ ptr, size, res);
+}
+
+krb5_error_code
+hdb_unseal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
+{
+
+ krb5_error_code ret;
+ krb5_data res;
+ size_t keysize;
+
+ hdb_master_key key;
+
+ if(k->mkvno == NULL)
+ return 0;
+
+ key = _hdb_find_master_key(k->mkvno, mkey);
+
+ if (key == NULL)
+ return HDB_ERR_NO_MKEY;
+
+ ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY,
+ k->key.keyvalue.data,
+ k->key.keyvalue.length,
+ &res);
+ if(ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
+ /* try to decrypt with MIT key usage */
+ ret = _hdb_mkey_decrypt(context, key, 0,
+ k->key.keyvalue.data,
+ k->key.keyvalue.length,
+ &res);
+ }
+ if (ret)
+ return ret;
+
+ /* fixup keylength if the key got padded when encrypting it */
+ ret = krb5_enctype_keysize(context, k->key.keytype, &keysize);
+ if (ret) {
+ krb5_data_free(&res);
+ return ret;
+ }
+ if (keysize > res.length) {
+ krb5_data_free(&res);
+ return KRB5_BAD_KEYSIZE;
+ }
+
+ memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
+ free(k->key.keyvalue.data);
+ k->key.keyvalue = res;
+ k->key.keyvalue.length = keysize;
+ free(k->mkvno);
+ k->mkvno = NULL;
+
+ return 0;
+}
+
+krb5_error_code
+hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
+{
+ int i;
+
+ for(i = 0; i < ent->keys.len; i++){
+ krb5_error_code ret;
+
+ ret = hdb_unseal_key_mkey(context, &ent->keys.val[i], mkey);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+krb5_error_code
+hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent)
+{
+ if (db->hdb_master_key_set == 0)
+ return 0;
+ return hdb_unseal_keys_mkey(context, ent, db->hdb_master_key);
+}
+
+krb5_error_code
+hdb_unseal_key(krb5_context context, HDB *db, Key *k)
+{
+ if (db->hdb_master_key_set == 0)
+ return 0;
+ return hdb_unseal_key_mkey(context, k, db->hdb_master_key);
+}
+
+krb5_error_code
+hdb_seal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
+{
+ krb5_error_code ret;
+ krb5_data res;
+ hdb_master_key key;
+
+ if(k->mkvno != NULL)
+ return 0;
+
+ key = _hdb_find_master_key(k->mkvno, mkey);
+
+ if (key == NULL)
+ return HDB_ERR_NO_MKEY;
+
+ ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY,
+ k->key.keyvalue.data,
+ k->key.keyvalue.length,
+ &res);
+ if (ret)
+ return ret;
+
+ memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
+ free(k->key.keyvalue.data);
+ k->key.keyvalue = res;
+
+ if (k->mkvno == NULL) {
+ k->mkvno = malloc(sizeof(*k->mkvno));
+ if (k->mkvno == NULL)
+ return ENOMEM;
+ }
+ *k->mkvno = key->keytab.vno;
+
+ return 0;
+}
+
+krb5_error_code
+hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
+{
+ int i;
+ for(i = 0; i < ent->keys.len; i++){
+ krb5_error_code ret;
+
+ ret = hdb_seal_key_mkey(context, &ent->keys.val[i], mkey);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+krb5_error_code
+hdb_seal_keys(krb5_context context, HDB *db, hdb_entry *ent)
+{
+ if (db->hdb_master_key_set == 0)
+ return 0;
+
+ return hdb_seal_keys_mkey(context, ent, db->hdb_master_key);
+}
+
+krb5_error_code
+hdb_seal_key(krb5_context context, HDB *db, Key *k)
+{
+ if (db->hdb_master_key_set == 0)
+ return 0;
+
+ return hdb_seal_key_mkey(context, k, db->hdb_master_key);
+}
+
+krb5_error_code
+hdb_set_master_key (krb5_context context,
+ HDB *db,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ hdb_master_key mkey;
+
+ ret = hdb_process_master_key(context, 0, key, 0, &mkey);
+ if (ret)
+ return ret;
+ db->hdb_master_key = mkey;
+#if 0 /* XXX - why? */
+ des_set_random_generator_seed(key.keyvalue.data);
+#endif
+ db->hdb_master_key_set = 1;
+ return 0;
+}
+
+krb5_error_code
+hdb_set_master_keyfile (krb5_context context,
+ HDB *db,
+ const char *keyfile)
+{
+ hdb_master_key key;
+ krb5_error_code ret;
+
+ ret = hdb_read_master_key(context, keyfile, &key);
+ if (ret) {
+ if (ret != ENOENT)
+ return ret;
+ krb5_clear_error_string(context);
+ return 0;
+ }
+ db->hdb_master_key = key;
+ db->hdb_master_key_set = 1;
+ return ret;
+}
+
+krb5_error_code
+hdb_clear_master_key (krb5_context context,
+ HDB *db)
+{
+ if (db->hdb_master_key_set) {
+ hdb_free_master_key(context, db->hdb_master_key);
+ db->hdb_master_key_set = 0;
+ }
+ return 0;
+}
diff --git a/source4/heimdal/lib/hdb/ndbm.c b/source4/heimdal/lib/hdb/ndbm.c
new file mode 100644
index 0000000000..c4fc52e17f
--- /dev/null
+++ b/source4/heimdal/lib/hdb/ndbm.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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 "hdb_locl.h"
+
+RCSID("$Id$");
+
+#if HAVE_NDBM
+
+#if defined(HAVE_GDBM_NDBM_H)
+#include <gdbm/ndbm.h>
+#elif defined(HAVE_NDBM_H)
+#include <ndbm.h>
+#elif defined(HAVE_DBM_H)
+#include <dbm.h>
+#endif
+
+struct ndbm_db {
+ DBM *db;
+ int lock_fd;
+};
+
+static krb5_error_code
+NDBM_destroy(krb5_context context, HDB *db)
+{
+ krb5_error_code ret;
+
+ ret = hdb_clear_master_key (context, db);
+ free(db->hdb_name);
+ free(db);
+ return 0;
+}
+
+static krb5_error_code
+NDBM_lock(krb5_context context, HDB *db, int operation)
+{
+ struct ndbm_db *d = db->hdb_db;
+ return hdb_lock(d->lock_fd, operation);
+}
+
+static krb5_error_code
+NDBM_unlock(krb5_context context, HDB *db)
+{
+ struct ndbm_db *d = db->hdb_db;
+ return hdb_unlock(d->lock_fd);
+}
+
+static krb5_error_code
+NDBM_seq(krb5_context context, HDB *db,
+ unsigned flags, hdb_entry_ex *entry, int first)
+
+{
+ struct ndbm_db *d = (struct ndbm_db *)db->hdb_db;
+ datum key, value;
+ krb5_data key_data, data;
+ krb5_error_code ret = 0;
+
+ if(first)
+ key = dbm_firstkey(d->db);
+ else
+ key = dbm_nextkey(d->db);
+ if(key.dptr == NULL)
+ return HDB_ERR_NOENTRY;
+ key_data.data = key.dptr;
+ key_data.length = key.dsize;
+ ret = db->hdb_lock(context, db, HDB_RLOCK);
+ if(ret) return ret;
+ value = dbm_fetch(d->db, key);
+ db->hdb_unlock(context, db);
+ data.data = value.dptr;
+ data.length = value.dsize;
+ memset(entry, 0, sizeof(*entry));
+ if(hdb_value2entry(context, &data, &entry->entry))
+ return NDBM_seq(context, db, flags, entry, 0);
+ if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
+ ret = hdb_unseal_keys (context, db, &entry->entry);
+ if (ret)
+ hdb_free_entry (context, entry);
+ }
+ if (ret == 0 && entry->entry.principal == NULL) {
+ entry->entry.principal = malloc (sizeof(*entry->entry.principal));
+ if (entry->entry.principal == NULL) {
+ hdb_free_entry (context, entry);
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ } else {
+ hdb_key2principal (context, &key_data, entry->entry.principal);
+ }
+ }
+ return ret;
+}
+
+
+static krb5_error_code
+NDBM_firstkey(krb5_context context, HDB *db,unsigned flags,hdb_entry_ex *entry)
+{
+ return NDBM_seq(context, db, flags, entry, 1);
+}
+
+
+static krb5_error_code
+NDBM_nextkey(krb5_context context, HDB *db, unsigned flags,hdb_entry_ex *entry)
+{
+ return NDBM_seq(context, db, flags, entry, 0);
+}
+
+static krb5_error_code
+NDBM_rename(krb5_context context, HDB *db, const char *new_name)
+{
+ /* XXX this function will break */
+ struct ndbm_db *d = db->hdb_db;
+
+ int ret;
+ char *old_dir, *old_pag, *new_dir, *new_pag;
+ char *new_lock;
+ int lock_fd;
+
+ /* lock old and new databases */
+ ret = db->hdb_lock(context, db, HDB_WLOCK);
+ if(ret)
+ return ret;
+ asprintf(&new_lock, "%s.lock", new_name);
+ if(new_lock == NULL) {
+ db->hdb_unlock(context, db);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ lock_fd = open(new_lock, O_RDWR | O_CREAT, 0600);
+ if(lock_fd < 0) {
+ ret = errno;
+ db->hdb_unlock(context, db);
+ krb5_set_error_message(context, ret, "open(%s): %s", new_lock,
+ strerror(ret));
+ free(new_lock);
+ return ret;
+ }
+ free(new_lock);
+ ret = hdb_lock(lock_fd, HDB_WLOCK);
+ if(ret) {
+ db->hdb_unlock(context, db);
+ close(lock_fd);
+ return ret;
+ }
+
+ asprintf(&old_dir, "%s.dir", db->hdb_name);
+ asprintf(&old_pag, "%s.pag", db->hdb_name);
+ asprintf(&new_dir, "%s.dir", new_name);
+ asprintf(&new_pag, "%s.pag", new_name);
+
+ ret = rename(old_dir, new_dir) || rename(old_pag, new_pag);
+ free(old_dir);
+ free(old_pag);
+ free(new_dir);
+ free(new_pag);
+ hdb_unlock(lock_fd);
+ db->hdb_unlock(context, db);
+
+ if(ret) {
+ ret = errno;
+ close(lock_fd);
+ krb5_set_error_message(context, ret, "rename: %s", strerror(ret));
+ return ret;
+ }
+
+ close(d->lock_fd);
+ d->lock_fd = lock_fd;
+
+ free(db->hdb_name);
+ db->hdb_name = strdup(new_name);
+ return 0;
+}
+
+static krb5_error_code
+NDBM__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
+{
+ struct ndbm_db *d = (struct ndbm_db *)db->hdb_db;
+ datum k, v;
+ int code;
+
+ k.dptr = key.data;
+ k.dsize = key.length;
+ code = db->hdb_lock(context, db, HDB_RLOCK);
+ if(code)
+ return code;
+ v = dbm_fetch(d->db, k);
+ db->hdb_unlock(context, db);
+ if(v.dptr == NULL)
+ return HDB_ERR_NOENTRY;
+
+ krb5_data_copy(reply, v.dptr, v.dsize);
+ return 0;
+}
+
+static krb5_error_code
+NDBM__put(krb5_context context, HDB *db, int replace,
+ krb5_data key, krb5_data value)
+{
+ struct ndbm_db *d = (struct ndbm_db *)db->hdb_db;
+ datum k, v;
+ int code;
+
+ k.dptr = key.data;
+ k.dsize = key.length;
+ v.dptr = value.data;
+ v.dsize = value.length;
+
+ code = db->hdb_lock(context, db, HDB_WLOCK);
+ if(code)
+ return code;
+ code = dbm_store(d->db, k, v, replace ? DBM_REPLACE : DBM_INSERT);
+ db->hdb_unlock(context, db);
+ if(code == 1)
+ return HDB_ERR_EXISTS;
+ if (code < 0)
+ return code;
+ return 0;
+}
+
+static krb5_error_code
+NDBM__del(krb5_context context, HDB *db, krb5_data key)
+{
+ struct ndbm_db *d = (struct ndbm_db *)db->hdb_db;
+ datum k;
+ int code;
+ krb5_error_code ret;
+
+ k.dptr = key.data;
+ k.dsize = key.length;
+ ret = db->hdb_lock(context, db, HDB_WLOCK);
+ if(ret) return ret;
+ code = dbm_delete(d->db, k);
+ db->hdb_unlock(context, db);
+ if(code < 0)
+ return errno;
+ return 0;
+}
+
+
+static krb5_error_code
+NDBM_close(krb5_context context, HDB *db)
+{
+ struct ndbm_db *d = db->hdb_db;
+ dbm_close(d->db);
+ close(d->lock_fd);
+ free(d);
+ return 0;
+}
+
+static krb5_error_code
+NDBM_open(krb5_context context, HDB *db, int flags, mode_t mode)
+{
+ krb5_error_code ret;
+ struct ndbm_db *d = malloc(sizeof(*d));
+ char *lock_file;
+
+ if(d == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ asprintf(&lock_file, "%s.lock", (char*)db->hdb_name);
+ if(lock_file == NULL) {
+ free(d);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ d->db = dbm_open((char*)db->hdb_name, flags, mode);
+ if(d->db == NULL){
+ ret = errno;
+ free(d);
+ free(lock_file);
+ krb5_set_error_message(context, ret, "dbm_open(%s): %s", db->hdb_name,
+ strerror(ret));
+ return ret;
+ }
+ d->lock_fd = open(lock_file, O_RDWR | O_CREAT, 0600);
+ if(d->lock_fd < 0){
+ ret = errno;
+ dbm_close(d->db);
+ free(d);
+ krb5_set_error_message(context, ret, "open(%s): %s", lock_file,
+ strerror(ret));
+ free(lock_file);
+ return ret;
+ }
+ free(lock_file);
+ db->hdb_db = d;
+ if((flags & O_ACCMODE) == O_RDONLY)
+ ret = hdb_check_db_format(context, db);
+ else
+ ret = hdb_init_db(context, db);
+ if(ret == HDB_ERR_NOENTRY)
+ return 0;
+ if (ret) {
+ NDBM_close(context, db);
+ krb5_set_error_message(context, ret, "hdb_open: failed %s database %s",
+ (flags & O_ACCMODE) == O_RDONLY ?
+ "checking format of" : "initialize",
+ db->hdb_name);
+ }
+ return ret;
+}
+
+krb5_error_code
+hdb_ndbm_create(krb5_context context, HDB **db,
+ const char *filename)
+{
+ *db = calloc(1, sizeof(**db));
+ if (*db == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ (*db)->hdb_db = NULL;
+ (*db)->hdb_name = strdup(filename);
+ if ((*db)->hdb_name == NULL) {
+ free(*db);
+ *db = NULL;
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ (*db)->hdb_master_key_set = 0;
+ (*db)->hdb_openp = 0;
+ (*db)->hdb_open = NDBM_open;
+ (*db)->hdb_close = NDBM_close;
+ (*db)->hdb_fetch = _hdb_fetch;
+ (*db)->hdb_store = _hdb_store;
+ (*db)->hdb_remove = _hdb_remove;
+ (*db)->hdb_firstkey = NDBM_firstkey;
+ (*db)->hdb_nextkey= NDBM_nextkey;
+ (*db)->hdb_lock = NDBM_lock;
+ (*db)->hdb_unlock = NDBM_unlock;
+ (*db)->hdb_rename = NDBM_rename;
+ (*db)->hdb__get = NDBM__get;
+ (*db)->hdb__put = NDBM__put;
+ (*db)->hdb__del = NDBM__del;
+ (*db)->hdb_destroy = NDBM_destroy;
+ return 0;
+}
+
+#endif /* HAVE_NDBM */