Major key-management revision.
[u/mdw/catacomb] / key-misc.c
diff --git a/key-misc.c b/key-misc.c
new file mode 100644 (file)
index 0000000..2d9c855
--- /dev/null
@@ -0,0 +1,382 @@
+/* -*-c-*-
+ *
+ * $Id: key-misc.c,v 1.1 1999/12/22 15:47:48 mdw Exp $
+ *
+ * Simple key management
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: key-misc.c,v $
+ * Revision 1.1  1999/12/22 15:47:48  mdw
+ * Major key-management revision.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <mLib/alloc.h>
+#include <mLib/bits.h>
+#include <mLib/hash.h>
+#include <mLib/sub.h>
+#include <mLib/sym.h>
+
+#include "key.h"
+
+/*----- Useful macros -----------------------------------------------------*/
+
+#define KEY_WRITE(f) do {                                              \
+     if (!(f)->f & KF_WRITE)                                           \
+       return (KERR_READONLY);                                         \
+   } while (0)
+
+#define KEY_MODIFY(f) do { (f)->f |= KF_MODIFIED; } while (0)
+
+#define KEY_LOAD(n) ((n) * 2)
+
+/*----- Error reporting ---------------------------------------------------*/
+
+/* --- @key_strerror@ --- *
+ *
+ * Arguments:  @int err@ = error code from @key_new@
+ *
+ * Returns:    Pointer to error string.
+ *
+ * Use:                Translates a @KERR@ error code into a human-readable
+ *             string.
+ */
+
+const char *key_strerror(int err)
+{
+  char *tab[] = {
+    "No error",
+    "Bad tag string",
+    "Bad type string",
+    "Bad comment string",
+    "Keyid already exists",
+    "Key tag already exists",
+    "Key file is read-only",
+    "Key will eventually expire",
+    "Bad key flags string",
+    "Unknown error code"
+  };
+  
+  unsigned e = -err;
+  if (e >= KERR_MAX)
+    e = KERR_MAX;
+ return (tab[e]);
+}
+
+/*----- Iteration and iterators -------------------------------------------*/
+
+/* --- @key_mkiter@ --- *
+ *
+ * Arguments:  @key_iter *i@ = pointer to iterator object
+ *             @key_file *f@ = pointer to file structure
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes a key iterator.  The keys are returned by
+ *             @key_next@.
+ */
+
+void key_mkiter(key_iter *i, key_file *f)
+{
+  HASH_MKITER(&i->i, &f->byid);
+  i->t = time(0);
+}
+
+/* --- @key_next@ --- *
+ *
+ * Arguments:  @key_iter *i@ = pointer to iterator object
+ *
+ * Returns:    Pointer to next key, or null.
+ *
+ * Use:                Returns the next key in some arbitrary sequence.
+ */
+
+key *key_next(key_iter *i)
+{
+  hash_base *b;
+  key *k;
+  do {
+    HASH_NEXT(&i->i, b);
+    k = (key *)b;
+  } while (k && KEY_EXPIRED(i->t, k->exp) && KEY_EXPIRED(i->t, k->del));
+  return (k);
+}
+
+/*----- Lookup ------------------------------------------------------------*/
+
+/* --- @key_bytype@ --- *
+ *
+ * Arguments:  @key_file *f@ = key file we want a key from
+ *             @const char *type@ = type string for desired key
+ *
+ * Returns:    Pointer to the best key to use, or null.
+ *
+ * Use:                Looks up a key by its type.  Returns the key with the latest
+ *             expiry time.  This function will not return an expired key.
+ */
+
+key *key_bytype(key_file *f, const char *type)
+{
+  time_t now = time(0);
+  key *k;
+  key_ref *kr;
+
+  if ((kr = sym_find(&f->bytype, type, -1, 0, 0)) == 0)
+    return (0);
+  for (k = kr->k; k && KEY_EXPIRED(now, k->exp); k = k->next)
+    ;
+  return (k);
+}
+
+/* --- @key_byid@ --- *
+ *
+ * Arguments:  @key_file *f@ = key file to find a key from
+ *             @uint32 id@ = id to look for
+ *
+ * Returns:    Key with matching id.
+ *
+ * Use:                Returns a key given its id.  This function will return an
+ *             expired key, but not a deleted one.
+ */
+
+key *key_byid(key_file *f, uint32 id)
+{
+  time_t t = time(0);
+  hash_base **bin, *b;
+
+  bin = HASH_BIN(&f->byid, id);
+  for (b = *bin; b; b = b->next) {
+    if (b->hash == id) {
+      key *k = (key *)b;
+      if (KEY_EXPIRED(t, k->exp) && KEY_EXPIRED(t, k->del))
+       return (0);
+      return (k);
+    }
+  }
+  return (0);
+}
+
+/* --- @key_bytag@ --- *
+ *
+ * Arguments:  @key_file *f@ = key file to find a key from
+ *             @const char *tag@ = pointer to tag string
+ *
+ * Returns:    Key with matching id or tag.
+ *
+ * Use:                Returns a key given its tag or id.  This function will return
+ *             an expired key, but not a deleted one.
+ */
+
+key *key_bytag(key_file *f, const char *tag)
+{
+  time_t t = time(0);
+  char *p;
+  uint32 id;
+  key_ref *kr = sym_find(&f->bytag, tag, -1, 0, 0);
+
+  if (kr && !(KEY_EXPIRED(t, kr->k->exp) && KEY_EXPIRED(t, kr->k->exp)))
+    return (kr->k);
+  id = strtoul(tag, &p, 16);
+  if (!*p)
+    return (key_byid(f, id));
+  return (key_bytype(f, tag));
+}
+
+/* --- @key_qtag@ --- *
+ *
+ * Arguments:  @key_file *f@ = key file to find a key from
+ *             @const char *tag@ = pointer to tag string
+ *             @dstr *d@ = pointer to string for full tag name
+ *             @key **k@ = where to store the key pointer
+ *             @key_data **kd@ = where to store the key data pointer
+ *
+ * Returns:    Zero if OK, nonzero if it failed.
+ *
+ * Use:                Performs a full lookup on a qualified tag name.  The tag is
+ *             qualified by the names of subkeys, separated by dots.  Hence,
+ *             a qualified tag is ID|TAG[.TAG...].  The various result
+ *             pointers can be null to indicate that the result isn't
+ *             interesting. 
+ */
+
+int key_qtag(key_file *f, const char *tag, dstr *d, key **k, key_data **kd)
+{
+  dstr dd = DSTR_INIT;
+  const char *q;
+  key *kk;
+  key_data *kkd;
+
+  /* --- Find the end of the base tag --- */
+
+  if ((q = strchr(tag, '.')) == 0)
+    DPUTS(&dd, tag);
+  else {
+    DPUTM(&dd, tag, q - tag);
+    DPUTZ(&dd);
+    q++;
+  }
+
+  /* --- Look up the key tag --- */
+
+  if ((kk = key_bytag(f, dd.buf)) == 0) {
+    dstr_destroy(&dd);
+    return (-1);
+  }
+
+  /* --- Set the various initial bits of result up --- */
+
+  if (d)
+    key_fulltag(kk, d);
+  if (k)
+    *k = kk;
+  kkd = &kk->k;
+
+  /* --- Now dig through the rest of the tag --- */
+
+  while (q && *q) {
+
+    /* --- Stick on the next bit of the fullqtag --- */
+
+    DRESET(&dd);
+    while (*q && *q != '.') {
+      DPUTC(&dd, *q);
+      q++;
+    }
+    DPUTZ(&dd);
+    if (d) {
+      DPUTC(d, '.');
+      DPUTD(d, &dd);
+    }
+
+    /* --- Look up the subkey --- */
+
+    if (kkd->e != KENC_STRUCT) {
+      kkd = 0;
+      break;
+    }
+    if ((kkd = key_structfind(kkd, dd.buf)) == 0)
+      break;
+  }
+
+  /* --- Return the results --- */
+
+  if (!kkd)
+    return (-1);
+  dstr_destroy(&dd);
+  if (kd)
+    *kd = kkd;
+  return (0);
+}
+
+/*----- Miscellaneous functions -------------------------------------------*/
+
+/* --- @key_delete@ --- *
+ *
+ * Arguments:  @key_file *f@ = pointer to file block
+ *             @key *k@ = key to delete
+ *
+ * Returns:    Error code (one of the @KERR@ constants).
+ *
+ * Use:                Removes the given key from the list.  The key file must be
+ *             writable.  (Due to the horridness of the data structures,
+ *             deleted keys aren't actually removed, just marked so that
+ *             they can't be looked up or iterated over.  One upshot of
+ *             this is that they don't get written back to the file when
+ *             it's closed.)
+ */
+
+int key_delete(key_file *f, key *k)
+{
+  KEY_WRITE(f);
+  k->exp = KEXP_EXPIRE;
+  k->del = KEXP_EXPIRE;
+  KEY_MODIFY(f);
+  return (0);
+}
+
+/* --- @key_expire@ --- *
+ *
+ * Arguments:  @key_file *f@ = pointer to file block
+ *             @key *k@ = pointer to key block
+ *
+ * Returns:    Error code (one of the @KERR@ constants).
+ *
+ * Use:                Immediately marks the key as expired.  It may be removed
+ *             immediately, if it is no longer required, and will be removed
+ *             by a tidy operation when it is no longer required.  The key
+ *             file must be writable.
+ */
+
+int key_expire(key_file *f, key *k)
+{
+  KEY_WRITE(f);
+  k->exp = KEXP_EXPIRE;
+  if (k->del == KEXP_FOREVER)
+    k->del = KEXP_EXPIRE;
+  KEY_MODIFY(f);
+  return (0);
+}
+
+/* --- @key_used@ --- *
+ *
+ * Arguments:  @key_file *f@ = pointer to key file
+ *             @key *k@ = pointer to key block
+ *             @time_t t@ = when key can be removed
+ *
+ * Returns:    Zero if OK, nonzero on failure.
+ *
+ * Use:                Marks a key as being required until a given time.  Even
+ *             though the key may expire before then (and won't be returned
+ *             by type after that time), it will still be available when
+ *             requested explicitly by id.  The key file must be writable.
+ *
+ *             The only (current) reason for failure is attempting to use
+ *             a key which can expire for something which can't.
+ */
+
+int key_used(key_file *f, key *k, time_t t)
+{
+  KEY_WRITE(f);
+  if (t == KEXP_FOREVER) {
+    if (k->exp != KEXP_FOREVER)
+      return (KERR_WILLEXPIRE);
+  } else if (k->del >= t)
+    return (0);
+
+  k->del = t;
+  KEY_MODIFY(f);
+  return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/