--- /dev/null
+/* -*-c-*-
+ *
+ * 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.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.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)
+
+/*----- 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->del)))
+ 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 --- */
+
+ if (q) {
+ while (*q) {
+ key_struct *ks;
+
+ /* --- 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 ((ks = sym_find(&(*kkd)->u.s, dd.buf, -1, 0, 0)) == 0) {
+ kkd = 0;
+ break;
+ }
+ kkd = &ks->k;
+ }
+ }
+
+ /* --- Return the results --- */
+
+ dstr_destroy(&dd);
+ if (!kkd)
+ return (-1);
+ 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_expired@ --- *
+ *
+ * Arguments: @key *k@ = pointer to key block
+ *
+ * Returns: Zero if the key is OK, nonzero if it's expired.
+ */
+
+int key_expired(key *k)
+{
+ time_t now = time(0);
+ return (KEY_EXPIRED(now, k->exp) || KEY_EXPIRED(now, k->del));
+}
+
+/* --- @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);
+}
+
+/* --- @key_fingerprint@ --- *
+ *
+ * Arguments: @key *k@ = the key to fingerprint
+ * @ghash *h@ = the hash to use
+ * @const key_filter *kf@ = filter to apply
+ *
+ * Returns: Nonzero if the key slightly matched the filter.
+ *
+ * Use: Updates the hash context with the key contents.
+ */
+
+static int abyname(const void *a, const void *b) {
+ key_attr *const *x = a, *const *y = b;
+ return (strcmp(SYM_NAME(*x), SYM_NAME(*y)));
+}
+
+int key_fingerprint(key *k, ghash *h, const key_filter *kf)
+{
+ dstr d = DSTR_INIT;
+ int rc = 0;
+ key_attr *a, **v;
+ size_t n, i;
+ sym_iter ai;
+
+ if (!key_encode(k->k, &d, kf))
+ goto done;
+ rc = 1;
+ GH_HASHSTR(h, "catacomb-key-fingerprint:");
+ GH_HASHU32(h, k->id);
+ GH_HASHSTR8(h, k->type);
+ GH_HASH(h, d.buf, d.len);
+ for (n = 0, sym_mkiter(&ai, &k->a); (a = sym_next(&ai)) != 0; n++);
+ if (n) {
+ v = xmalloc(n * sizeof(*v));
+ for (i = 0, sym_mkiter(&ai, &k->a); (a = sym_next(&ai)) != 0; i++)
+ v[i] = a;
+ qsort(v, n, sizeof(*v), abyname);
+ for (i = 0; i < n; i++) {
+ GH_HASHSTR8(h, SYM_NAME(v[i]));
+ GH_HASHSTR16(h, v[i]->p);
+ }
+ xfree(v);
+ }
+done:
+ dstr_destroy(&d);
+ return (rc);
+}
+
+/*----- That's all, folks -------------------------------------------------*/