s4:heimdal Extend the 'hdb as a keytab' code
authorAndrew Bartlett <abartlet@samba.org>
Mon, 27 Jul 2009 03:50:50 +0000 (13:50 +1000)
committerAndrew Bartlett <abartlet@samba.org>
Mon, 27 Jul 2009 12:41:41 +0000 (22:41 +1000)
This extends the hdb_keytab code to allow enumeration of all the keys.

The plan is to allow ktutil's copy command to copy from Samba4's
hdb_samba4 into a file-based keytab used in wireshark.

One day, with a few more hacks, we might even make this a loadable
module that can be used directly...

Andrew Bartlett

source4/heimdal/lib/hdb/keytab.c

index 9b36a268cfd7b34feddf9134c1a334b0bb68382b..2ec7837ae33b50ceb65dc5e08231e39d14037377 100644 (file)
 
 struct hdb_data {
     char *dbname;
-    char *mkey;
+    char *mkey;   
+};
+
+struct hdb_cursor {
+    HDB *db;
+    hdb_entry_ex hdb_entry;
+    bool first, next;
+    int key_idx;
 };
 
 /*
@@ -247,6 +254,140 @@ hdb_get_entry(krb5_context context,
     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
+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 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;
+
+       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.entry.keys.len == 0) {
+                       hdb_free_entry(context, &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 (c->hdb_entry.entry.keys.len == 0) {
+                       /* If no keys on this entry, try again */
+                       hdb_free_entry(context, &c->hdb_entry);
+               } else {
+                       /* We have an entry, set the flag */
+                       c->next = false;
+               }
+       };
+
+       /* return next enc type (keytabs are one slot per key, while hdb is one record per principal */
+       krb5_copy_principal(context, 
+                           c->hdb_entry.entry.principal, 
+                           &entry->principal);
+       entry->vno = c->hdb_entry.entry.kvno;
+       krb5_copy_keyblock_contents(context,
+                                   &c->hdb_entry.entry.keys.val[c->key_idx].key,
+                                   &entry->keyblock);
+       c->key_idx++;
+
+       /* Once we get to the end of the list, signal that we want the next entry */
+       if (c->key_idx == c->hdb_entry.entry.keys.len) {
+               hdb_free_entry(context, &c->hdb_entry);
+               c->next = true;
+               c->key_idx = 0;
+       }
+       return 0;
+}
+
+
+static int hdb_end_seq_get(krb5_context context,
+                          krb5_keytab id,
+                          krb5_kt_cursor *cursor) {
+       struct hdb_cursor *c = cursor->data;
+       (c->db->hdb_close)(context, c->db);
+       (c->db->hdb_destroy)(context, c->db);
+       if (!c->next) {
+               hdb_free_entry(context, &c->hdb_entry);
+       }
+       free(c);
+       return 0;
+}
+
 krb5_kt_ops hdb_kt_ops = {
     "HDB",
     hdb_resolve,
@@ -254,9 +395,9 @@ krb5_kt_ops hdb_kt_ops = {
     hdb_close,
     NULL,              /* destroy */
     hdb_get_entry,
-    NULL,              /* start_seq_get */
-    NULL,              /* next_entry */
-    NULL,              /* end_seq_get */
+    hdb_start_seq_get,
+    hdb_next_entry,
+    hdb_end_seq_get,
     NULL,              /* add */
     NULL               /* remove */
 };