summaryrefslogtreecommitdiffstats
path: root/third_party/heimdal/lib/hdb/keytab.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/heimdal/lib/hdb/keytab.c')
-rw-r--r--third_party/heimdal/lib/hdb/keytab.c448
1 files changed, 448 insertions, 0 deletions
diff --git a/third_party/heimdal/lib/hdb/keytab.c b/third_party/heimdal/lib/hdb/keytab.c
new file mode 100644
index 0000000..df16cb7
--- /dev/null
+++ b/third_party/heimdal/lib/hdb/keytab.c
@@ -0,0 +1,448 @@
+/*
+ * 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 */
+
+struct hdb_data {
+ char *dbname;
+ char *mkey;
+};
+
+struct hdb_cursor {
+ HDB *db;
+ hdb_entry hdb_entry;
+ int first, next;
+ int key_idx;
+};
+
+/*
+ * the format for HDB keytabs is:
+ * HDB:[HDBFORMAT:database-specific-data[:mkey=mkey-file]]
+ */
+
+static krb5_error_code KRB5_CALLCONV
+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 = strstr(name, ":mkey=");
+ if(mkey == NULL || mkey[6] == '\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 {
+ 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 + 6);
+ 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 KRB5_CALLCONV
+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 KRB5_CALLCONV
+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;
+}
+
+/*
+ * try to figure out the database (`dbname') and master-key (`mkey')
+ * that should be used for `principal'.
+ */
+
+static krb5_error_code
+find_db (krb5_context context,
+ char **dbname,
+ char **mkey,
+ krb5_const_principal principal)
+{
+ krb5_const_realm realm = krb5_principal_get_realm(context, principal);
+ krb5_error_code ret;
+ struct hdb_dbinfo *head, *dbinfo = NULL;
+
+ *dbname = *mkey = NULL;
+
+ ret = hdb_get_dbinfo(context, &head);
+ if (ret)
+ return ret;
+
+ while ((dbinfo = hdb_dbinfo_get_next(head, dbinfo)) != NULL) {
+ const char *p = hdb_dbinfo_get_realm(context, dbinfo);
+ if (p && strcmp (realm, p) == 0) {
+ p = hdb_dbinfo_get_dbname(context, dbinfo);
+ if (p)
+ *dbname = strdup(p);
+ p = hdb_dbinfo_get_mkey_file(context, dbinfo);
+ if (p)
+ *mkey = strdup(p);
+ break;
+ }
+ }
+ hdb_free_dbinfo(context, &head);
+ if (*dbname == NULL &&
+ (*dbname = strdup(hdb_default_db(context))) == NULL) {
+ free(*mkey);
+ *mkey = NULL;
+ return krb5_enomem(context);
+ }
+ return 0;
+}
+
+/*
+ * 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 KRB5_CALLCONV
+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 ent;
+ krb5_error_code ret;
+ struct hdb_data *d = id->data;
+ const char *dbname = d->dbname;
+ const char *mkey = d->mkey;
+ char *fdbname = NULL, *fmkey = NULL;
+ HDB *db;
+ size_t i;
+
+ if (!principal)
+ return KRB5_KT_NOTFOUND;
+
+ memset(&ent, 0, sizeof(ent));
+
+ if (dbname == NULL) {
+ ret = find_db(context, &fdbname, &fmkey, principal);
+ if (ret)
+ return ret;
+ dbname = fdbname;
+ mkey = fmkey;
+ }
+
+ ret = hdb_create (context, &db, dbname);
+ if (ret)
+ goto out2;
+ ret = hdb_set_master_keyfile (context, db, mkey);
+ if (ret) {
+ (*db->hdb_destroy)(context, db);
+ goto out2;
+ }
+
+ ret = (*db->hdb_open)(context, db, O_RDONLY, 0);
+ if (ret) {
+ (*db->hdb_destroy)(context, db);
+ goto out2;
+ }
+
+ ret = hdb_fetch_kvno(context, db, principal,
+ HDB_F_DECRYPT|HDB_F_KVNO_SPECIFIED|
+ HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
+ 0, 0, kvno, &ent);
+
+ if (ret == HDB_ERR_WRONG_REALM || ret == HDB_ERR_NOENTRY)
+ ret = KRB5_KT_NOTFOUND;
+ if (ret)
+ goto out;
+
+ if(kvno && (krb5_kvno)ent.kvno != kvno) {
+ hdb_free_entry(context, db, &ent);
+ ret = KRB5_KT_NOTFOUND;
+ goto out;
+ }
+ if(enctype == 0)
+ if(ent.keys.len > 0)
+ enctype = ent.keys.val[0].key.keytype;
+ ret = KRB5_KT_NOTFOUND;
+ for(i = 0; i < ent.keys.len; i++) {
+ if(ent.keys.val[i].key.keytype == enctype) {
+ krb5_copy_principal(context, principal, &entry->principal);
+ entry->vno = ent.kvno;
+ krb5_copy_keyblock_contents(context,
+ &ent.keys.val[i].key,
+ &entry->keyblock);
+ ret = 0;
+ break;
+ }
+ }
+ hdb_free_entry(context, db, &ent);
+ out:
+ (*db->hdb_close)(context, db);
+ (*db->hdb_destroy)(context, db);
+ out2:
+ free(fdbname);
+ free(fmkey);
+ return ret;
+}
+
+/*
+ * 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 KRB5_CALLCONV
+hdb_start_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *cursor)
+{
+ krb5_error_code ret;
+ struct hdb_cursor *c;
+ struct hdb_data *d = id->data;
+ const char *dbname = d->dbname;
+ const char *mkey = d->mkey;
+ HDB *db;
+
+ if (dbname == NULL) {
+ /*
+ * We don't support enumerating without being told what
+ * backend to enumerate on
+ */
+ ret = KRB5_KT_NOTFOUND;
+ return ret;
+ }
+
+ 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;
+ }
+
+ cursor->data = c = malloc (sizeof(*c));
+ if(c == NULL){
+ (*db->hdb_close)(context, db);
+ (*db->hdb_destroy)(context, db);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ c->db = db;
+ c->first = TRUE;
+ c->next = TRUE;
+ c->key_idx = 0;
+
+ cursor->data = c;
+ return ret;
+}
+
+static int KRB5_CALLCONV
+hdb_next_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry,
+ krb5_kt_cursor *cursor)
+{
+ struct hdb_cursor *c = cursor->data;
+ krb5_error_code ret;
+
+ memset(entry, 0, sizeof(*entry));
+
+ if (c->first) {
+ c->first = FALSE;
+ ret = (c->db->hdb_firstkey)(context, c->db,
+ HDB_F_DECRYPT|
+ HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
+ &c->hdb_entry);
+ if (ret == HDB_ERR_NOENTRY)
+ return KRB5_KT_END;
+ else if (ret)
+ return ret;
+
+ if (c->hdb_entry.keys.len == 0)
+ hdb_free_entry(context, c->db, &c->hdb_entry);
+ else
+ c->next = FALSE;
+ }
+
+ while (c->next) {
+ ret = (c->db->hdb_nextkey)(context, c->db,
+ HDB_F_DECRYPT|
+ HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
+ &c->hdb_entry);
+ if (ret == HDB_ERR_NOENTRY)
+ return KRB5_KT_END;
+ else if (ret)
+ return ret;
+
+ /* If no keys on this entry, try again */
+ if (c->hdb_entry.keys.len == 0)
+ hdb_free_entry(context, c->db, &c->hdb_entry);
+ else
+ c->next = FALSE;
+ }
+
+ /*
+ * Return next enc type (keytabs are one slot per key, while
+ * hdb is one record per principal.
+ */
+
+ ret = krb5_copy_principal(context,
+ c->hdb_entry.principal,
+ &entry->principal);
+ if (ret)
+ return ret;
+
+ entry->vno = c->hdb_entry.kvno;
+ ret = krb5_copy_keyblock_contents(context,
+ &c->hdb_entry.keys.val[c->key_idx].key,
+ &entry->keyblock);
+ if (ret) {
+ krb5_free_principal(context, entry->principal);
+ memset(entry, 0, sizeof(*entry));
+ return ret;
+ }
+ c->key_idx++;
+
+ /*
+ * Once we get to the end of the list, signal that we want the
+ * next entry
+ */
+
+ if ((size_t)c->key_idx == c->hdb_entry.keys.len) {
+ hdb_free_entry(context, c->db, &c->hdb_entry);
+ c->next = TRUE;
+ c->key_idx = 0;
+ }
+
+ return 0;
+}
+
+
+static int KRB5_CALLCONV
+hdb_end_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *cursor)
+{
+ struct hdb_cursor *c = cursor->data;
+
+ if (!c->next)
+ hdb_free_entry(context, c->db, &c->hdb_entry);
+
+ (c->db->hdb_close)(context, c->db);
+ (c->db->hdb_destroy)(context, c->db);
+
+ free(c);
+ return 0;
+}
+
+krb5_kt_ops hdb_kt_ops = {
+ "HDB",
+ hdb_resolve,
+ hdb_get_name,
+ hdb_close,
+ NULL, /* destroy */
+ hdb_get_entry,
+ hdb_start_seq_get,
+ hdb_next_entry,
+ hdb_end_seq_get,
+ NULL, /* add */
+ NULL, /* remove */
+ NULL,
+ 0
+};
+
+krb5_kt_ops hdb_get_kt_ops = {
+ "HDBGET",
+ hdb_resolve,
+ hdb_get_name,
+ hdb_close,
+ NULL,
+ hdb_get_entry,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0
+};