--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: key-attr.c,v 1.1 1999/12/22 15:47:48 mdw Exp $
+ *
+ * Key attribute manipulation
+ *
+ * (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-attr.c,v $
+ * Revision 1.1 1999/12/22 15:47:48 mdw
+ * Major key-management revision.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <mLib/alloc.h>
+#include <mLib/sym.h>
+
+#include "key.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @key_chkident@ --- *
+ *
+ * Arguments: @const char *p@ = pointer to a type string
+ *
+ * Returns: Zero if OK, -1 on error.
+ *
+ * Use: Checks whether an identification component string is OK.
+ */
+
+int key_chkident(const char *p)
+{
+ if (!p || !*p)
+ return (-1);
+ while (*p) {
+ if (*p == ':' || *p == '.' || isspace((unsigned char)*p))
+ return (-1);
+ p++;
+ }
+ return (0);
+}
+
+/* --- @key_chkcomment@ --- *
+ *
+ * Arguments: @const char *p@ = pointer to a comment string
+ *
+ * Returns: Zero if OK, -1 on error.
+ *
+ * Use: Checks whether a comment string is OK.
+ */
+
+int key_chkcomment(const char *p)
+{
+ if (!p)
+ return (0);
+ if (!*p)
+ return (-1);
+ while (*p) {
+ if (*p == '\n')
+ return (-1);
+ p++;
+ }
+ return (0);
+}
+
+/* --- @key_mkattriter@ --- *
+ *
+ * Arguments: @key_attriter *i@ = pointer to attribute iterator
+ * @key *k@ = pointer to key
+ *
+ * Returns: ---
+ *
+ * Use: Initializes an attribute iterator. The attributes are
+ * returned by @key_nextattr@.
+ */
+
+void key_mkattriter(key_attriter *i, key *k)
+{
+ sym_mkiter(&i->i, &k->a);
+}
+
+/* --- @key_nextattr@ --- *
+ *
+ * Arguments: @key_attriter *i@ = pointer to attribute iterator
+ * @const char **n, **v@ = pointers to name and value
+ *
+ * Returns: Zero if no attribute available, or nonzero if returned OK.
+ *
+ * Use: Returns the next attribute.
+ */
+
+int key_nextattr(key_attriter *i, const char **n, const char **v)
+{
+ key_attr *a = sym_next(&i->i);
+ if (!a)
+ return (0);
+ *n = SYM_NAME(a);
+ *v = a->p;
+ return (1);
+}
+
+/* --- @key_getattr@ --- *
+ *
+ * Arguments: @key_file *f@ = pointer to file
+ * @key *k@ = pointer to key
+ * @const char *n@ = pointer to attribute name
+ *
+ * Returns: Pointer to attribute value, or null if not found.
+ *
+ * Use: Returns the value of a key attribute.
+ */
+
+const char *key_getattr(key_file *f, key *k, const char *n)
+{
+ key_attr *a;
+ if ((a = sym_find(&k->a, n, -1, 0, 0)) == 0)
+ return (0);
+ return (a->p);
+}
+
+/* --- @key_putattr@ --- *
+ *
+ * Arguments: @key_file *f@ = pointer to file
+ * @key *k@ = pointer to key
+ * @const char *n@ = pointer to attribute name
+ * @const char *v@ = pointer to attribute value or null
+ *
+ * Returns: Error code (one of the @KERR@ constants).
+ *
+ * Use: Inserts an attribute on a key. If an attribute with the same
+ * name already exists, it is deleted. Setting a null value
+ * removes the attribute.
+ */
+
+int key_putattr(key_file *f, key *k, const char *n, const char *v)
+{
+ key_attr *a;
+ unsigned found;
+
+ if (!(f->f & KF_WRITE))
+ return (KERR_READONLY);
+
+ if (v) {
+ a = sym_find(&k->a, n, -1, sizeof(*a), &found);
+ if (found)
+ free(a->p);
+ a->p = xstrdup(v);
+ } else if ((a = sym_find(&k->a, n, -1, 0, 0)) != 0) {
+ free(a->p);
+ sym_remove(&k->a, a);
+ }
+
+ f->f |= KF_MODIFIED;
+ return (0);
+}
+
+/* --- @key_setcomment@ --- *
+ *
+ * Arguments: @key_file *f@ = pointer to key file block
+ * @key *k@ = pointer to key block
+ * @const char *c@ = pointer to comment to set, or zero
+ *
+ * Returns: Error code (one of the @KERR@ constants).
+ *
+ * Use: Replaces the key's current comment with a new one.
+ */
+
+int key_setcomment(key_file *f, key *k, const char *c)
+{
+ if (!(f->f & KF_WRITE))
+ return (KERR_READONLY);
+ if (key_chkcomment(c))
+ return (KERR_BADCOMMENT);
+ if (k->c)
+ free(k->c);
+ if (c)
+ k->c = xstrdup(c);
+ else
+ k->c = 0;
+ f->f |= KF_MODIFIED;
+ return (0);
+}
+
+/* --- @key_settag@ --- *
+ *
+ * Arguments: @key_file *f@ = pointer to key file block
+ * @key *k@ = pointer to key block
+ * @const char *tag@ = pointer to comment to set, or zero
+ *
+ * Returns: Error code (one of the @KERR@ constants).
+ *
+ * Use: Replaces the key's current tag with a new one.
+ */
+
+int key_settag(key_file *f, key *k, const char *tag)
+{
+ key_ref *kr;
+ unsigned found;
+
+ if (!(f->f & KF_WRITE))
+ return (KERR_READONLY);
+
+ /* --- Make sure the tag is OK --- */
+
+ if (tag && key_chkident(tag))
+ return (KERR_BADTAG);
+
+ /* --- See if the new tag is the same as the old one --- */
+
+ if ((!tag && !k->tag) ||
+ (tag && k->tag && strcmp(tag, k->tag) == 0))
+ return (0);
+
+ /* --- Allocate an entry for the new tag --- */
+
+ if (tag) {
+ kr = sym_find(&f->bytag, tag, -1, sizeof(*kr), &found);
+ if (found)
+ return (KERR_DUPTAG);
+ kr->k = k;
+ }
+
+ /* --- Remove any existing tag --- */
+
+ if (k->tag) {
+ kr = sym_find(&f->bytag, k->tag, -1, 0, 0);
+ assert(((void)"No bytag link", kr));
+ sym_remove(&f->bytag, kr);
+ free(k->tag);
+ }
+
+ /* --- Done --- */
+
+ f->f |= KF_MODIFIED;
+ if (tag)
+ k->tag = xstrdup(tag);
+ else
+ k->tag = 0;
+ return (0);
+}
+
+/* --- @key_fulltag@ --- *
+ *
+ * Arguments: @key *k@ = pointer to key
+ * @dstr *d@ = pointer to destination string
+ *
+ * Returns: ---
+ *
+ * Use: Emits the key's full tag, which has the form
+ * `ID:TYPE[:TAG]'. This is used in the textual file format,
+ * and to identify passphrases for locked keys.
+ */
+
+void key_fulltag(key *k, dstr *d)
+{
+ dstr_putf(d, "%08lx:%s", (unsigned long)k->id, k->type);
+ if (k->tag)
+ dstr_putf(d, ":%s", k->tag);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: key-data.c,v 1.1 1999/12/22 15:47:48 mdw Exp $
+ *
+ * Encoding and decoding of key data
+ *
+ * (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-data.c,v $
+ * Revision 1.1 1999/12/22 15:47:48 mdw
+ * Major key-management revision.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mLib/alloc.h>
+#include <mLib/base64.h>
+#include <mLib/bits.h>
+#include <mLib/dstr.h>
+#include <mLib/sub.h>
+#include <mLib/sym.h>
+
+#include "key.h"
+#include "mp.h"
+#include "mptext.h"
+
+/*----- Disposal ----------------------------------------------------------*/
+
+/* --- @key_destroy@ --- *
+ *
+ * Arguments: @key_data *k@ = pointer to key data to destroy
+ *
+ * Returns: ---
+ *
+ * Use: Destroys a lump of key data.
+ */
+
+void key_destroy(key_data *k)
+{
+ switch (k->e & KF_ENCMASK) {
+ case KENC_BINARY:
+ case KENC_ENCRYPT:
+ if (k->e & KF_BURN)
+ memset(k->u.k.k, 0, k->u.k.sz);
+ sub_free(k->u.k.k, k->u.k.sz);
+ break;
+ case KENC_MP:
+ mp_drop(k->u.m);
+ break;
+ case KENC_STRUCT: {
+ sym_iter i;
+ key_struct *ks;
+
+ for (sym_mkiter(&i, &k->u.s); (ks = sym_next(&i)) != 0; )
+ key_destroy(&ks->k);
+ sym_destroy(&k->u.s);
+ } break;
+ }
+}
+
+/*----- Setting new values ------------------------------------------------*/
+
+/* --- @key_binary@ --- *
+ *
+ * Arguments: @key_data *k@ = pointer to key data block
+ * @const void *p@ = pointer to key data
+ * @size_t sz@ = size of the key data
+ *
+ * Returns: ---
+ *
+ * Use: Sets a binary key in a key data block.
+ */
+
+void key_binary(key_data *k, const void *p, size_t sz)
+{
+ k->e = (k->e & ~KF_ENCMASK) | KENC_BINARY;
+ k->u.k.k = sub_alloc(sz);
+ memcpy(k->u.k.k, p, sz);
+ k->u.k.sz = sz;
+}
+
+/* --- @key_encrypted@ --- *
+ *
+ * Arguments: @key_data *k@ = pointer to key data block
+ * @const void *p@ = pointer to key data
+ * @size_t sz@ = size of the key data
+ *
+ * Returns: ---
+ *
+ * Use: Sets an encrypted key in a key data block.
+ */
+
+void key_encrypted(key_data *k, const void *p, size_t sz)
+{
+ k->e = (k->e & ~KF_ENCMASK) | KENC_ENCRYPT;
+ k->u.k.k = sub_alloc(sz);
+ memcpy(k->u.k.k, p, sz);
+ k->u.k.sz = sz;
+}
+
+/* --- @key_mp@ --- *
+ *
+ * Arguments: @key_data *k@ = pointer to key data block
+ * @mp *m@ = pointer to the value to set
+ *
+ * Returns: ---
+ *
+ * Use: Sets a multiprecision integer key in a key block.
+ */
+
+void key_mp(key_data *k, mp *m)
+{
+ k->e = (k->e & ~KF_ENCMASK) | KENC_MP;
+ k->u.m = MP_COPY(m);
+}
+
+/* --- @key_structure@ --- *
+ *
+ * Arguments: @key_data *k@ = pointer to key data block
+ *
+ * Returns: ---
+ *
+ * Use: Initializes a structured key type.
+ */
+
+void key_structure(key_data *k)
+{
+ k->e = KENC_STRUCT;
+ sym_create(&k->u.s);
+}
+
+/* --- @key_structfind@ --- *
+ *
+ * Arguments: @key_data *k@ = pointer to key data block
+ * @const char *tag@ = pointer to tag string
+ *
+ * Returns: Pointer to key data block, or null.
+ *
+ * Use: Looks up the tag in a structured key.
+ */
+
+key_data *key_structfind(key_data *k, const char *tag)
+{
+ key_struct *ks;
+ assert(((void)"Key is not structured", k->e == KENC_STRUCT));
+ ks = sym_find(&k->u.s, tag, -1, 0, 0);
+ return (&ks->k);
+}
+
+/* --- @key_structcreate@ --- *
+ *
+ * Arguments: @key_data *k@ = pointer to key data block
+ * @const char *tag@ = pointer to tag string
+ *
+ * Returns: Pointer to newly created key data.
+ *
+ * Use: Creates a new uninitialized subkey.
+ */
+
+key_data *key_structcreate(key_data *k, const char *tag)
+{
+ key_struct *ks;
+ unsigned f;
+
+ assert(((void)"Key is not structured", k->e == KENC_STRUCT));
+ ks = sym_find(&k->u.s, tag, -1, sizeof(*ks), &f);
+ if (f)
+ key_destroy(&ks->k);
+ ks->k.e = 0;
+ return (&ks->k);
+}
+
+/* --- @key_match@ --- *
+ *
+ * Arguments: @key_data *k@ = pointer to key data block
+ * @const key_filter *kf@ = pointer to filter block
+ *
+ * Returns: Nonzero if the key matches the filter.
+ *
+ * Use: Checks whether a key matches a filter.
+ */
+
+int key_match(key_data *k, const key_filter *kf)
+{
+ sym_iter i;
+ key_struct *ks;
+
+ if (!kf)
+ return (1);
+ if ((k->e & KF_ENCMASK) != KENC_STRUCT)
+ return ((k->e & kf->m) == kf->f);
+
+ for (sym_mkiter(&i, &k->u.s); (ks = sym_next(&i)) != 0; ) {
+ if (key_match(&ks->k, kf))
+ return (1);
+ }
+ return (0);
+}
+
+/* --- @key_do@ --- *
+ *
+ * Arguments: @key_data *k@ = pointer to key data block
+ * @const key_filter *kf@ = pointer to filter block
+ * @dstr *d@ = pointer to base string
+ * @int (*func)(key_data *kd, dstr *d, void *p@ = function
+ * @void *p@ = argument to function
+ *
+ * Returns: Nonzero return code from function, or zero.
+ *
+ * Use: Runs a function over all the leaves of a key.
+ */
+
+int key_do(key_data *k, const key_filter *kf, dstr *d,
+ int (*func)(key_data */*kd*/, dstr */*d*/, void */*p*/),
+ void *p)
+{
+ if (!KEY_MATCH(k, kf))
+ return (0);
+ if ((k->e & KF_ENCMASK) != KENC_STRUCT)
+ return (func(k, d, p));
+ else {
+ sym_iter i;
+ key_struct *ks;
+ size_t n;
+ int rc;
+
+ if (d)
+ n = d->len;
+ for (sym_mkiter(&i, &k->u.s); (ks = sym_next(&i)) != 0; ) {
+ if (d) {
+ d->len = n;
+ dstr_putf(d, ".%s", SYM_NAME(ks));
+ }
+ if ((rc = key_do(&ks->k, kf, d, func, p)) != 0)
+ return (rc);
+ }
+ return (0);
+ }
+}
+
+/*----- Copying -----------------------------------------------------------*/
+
+/* --- @key_copy@ --- *
+ *
+ * Arguments: @key_data *kd@ = pointer to destination data block
+ * @key_data *k@ = pointer to source data block
+ * @const key_filter *kf@ = pointer to filter block
+ *
+ * Returns: Nonzero if an item was actually copied.
+ *
+ * Use: Copies a chunk of key data from one place to another.
+ */
+
+int key_copy(key_data *kd, key_data *k, const key_filter *kf)
+{
+ kd->e = k->e;
+
+ if (!KEY_MATCH(kd, kf))
+ return (0);
+ switch (k->e & KF_ENCMASK) {
+
+ /* --- Plain binary data --- */
+
+ case KENC_BINARY:
+ case KENC_ENCRYPT:
+ kd->u.k.k = sub_alloc(k->u.k.sz);
+ memcpy(kd->u.k.k, k->u.k.k, k->u.k.sz);
+ kd->u.k.sz = k->u.k.sz;
+ break;
+
+ /* --- Multiprecision integers --- */
+
+ case KENC_MP:
+ kd->u.m = MP_COPY(k->u.m);
+ break;
+
+ /* --- Structured key data --- */
+
+ case KENC_STRUCT: {
+ sym_iter i;
+ key_struct *ks;
+ int rc = 0;
+
+ sym_create(&kd->u.s);
+ for (sym_mkiter(&i, &k->u.s); (ks = sym_next(&i)) != 0; ) {
+ unsigned f;
+ key_struct *kks = sym_find(&kd->u.s, SYM_NAME(ks), -1,
+ sizeof(*kks), &f);
+ assert(((void)"Duplicate subkey tags", !f));
+ if (key_copy(&kks->k, &ks->k, kf))
+ rc = 1;
+ else
+ sym_remove(&kd->u.s, kks);
+ }
+ if (!rc) {
+ sym_destroy(&kd->u.s);
+ return (0);
+ }
+ } break;
+ }
+ return (1);
+}
+
+/*----- Textual encoding --------------------------------------------------*/
+
+/* --- @key_read@ --- *
+ *
+ * Arguments: @const char *p@ = pointer to textual key representation
+ * @key_data *k@ = pointer to output block for key data
+ * @char **pp@ = where to store the end pointer
+ *
+ * Returns: Zero if all went well, nonzero if there was a problem.
+ *
+ * Use: Parses a textual key description.
+ */
+
+int key_read(const char *p, key_data *k, char **pp)
+{
+ unsigned e;
+
+ /* --- Read the encoding type --- *
+ *
+ * The key format is `[FLAGS:]DATA'. If there is no encoding type
+ * named, assume that it's `binary' for backwards compatibility.
+ */
+
+ if (strchr(p, ':') == 0)
+ e = 0;
+ else {
+ char *q;
+ if (key_readflags(p, &q, &e, 0))
+ return (-1);
+ p = q + 1;
+ }
+
+ /* --- Now scan the data based on the encoding type --- */
+
+ k->e = e;
+ switch (e & KF_ENCMASK) {
+
+ /* --- Binary encoding --- *
+ *
+ * Simply read out the Base64-encoded data. Since `,' and `]' are our
+ * delimeter characters, and they can't appear in Base64-encoded data, I
+ * can just do a simple search to find the end of the encoded data.
+ */
+
+ case KENC_BINARY:
+ case KENC_ENCRYPT: {
+ dstr d = DSTR_INIT;
+ base64_ctx b;
+ size_t sz = strcspn(p, ",]");
+
+ base64_init(&b);
+ base64_decode(&b, p, sz, &d);
+ base64_decode(&b, 0, 0, &d);
+ k->u.k.k = sub_alloc(d.len);
+ k->u.k.sz = d.len;
+ memcpy(k->u.k.k, d.buf, d.len);
+ dstr_destroy(&d);
+ p += sz;
+ } break;
+
+ /* --- Multiprecision integer encoding --- *
+ *
+ * Multiprecision integers have a convenient reading function.
+ */
+
+ case KENC_MP: {
+ char *q;
+ mp *m = mp_readstring(MP_NEW, p, &q, 0);
+ if (!m)
+ return (-1);
+ if (k->e & KF_BURN)
+ mp_burn(m);
+ k->u.m = m;
+ p = q;
+ } break;
+
+ /* --- Structured information encoding --- *
+ *
+ * The format for structured key data is `[NAME=KEY,...]', where the
+ * brackets are part of the syntax. Structured keys have no flags apart
+ * from the encoding.
+ *
+ * The binary encoding only allows names up to 255 bytes long. Check for
+ * this here.
+ */
+
+ case KENC_STRUCT: {
+ dstr d = DSTR_INIT;
+ char *q;
+
+ /* --- Read the opening bracket --- */
+
+ k->e &= KF_ENCMASK;
+ if (*p != '[')
+ return (-1);
+ p++;
+ sym_create(&k->u.s);
+
+ /* --- Read named key subparts --- */
+
+ for (;;) {
+ size_t sz;
+ key_struct *ks;
+
+ /* --- Stop if there's a close-bracket --- *
+ *
+ * This allows `[]' to be an empty structured key, which is good. It
+ * also makes `[foo=enc:bar,]' legal, and that's less good but I can
+ * live with it.
+ */
+
+ if (*p == ']')
+ break;
+
+ /* --- Read the name out and check the length --- */
+
+ if ((q = strchr(p, '=')) == 0)
+ goto fail;
+ sz = q - p;
+ if (sz >= 256)
+ goto fail;
+ DRESET(&d);
+ DPUTM(&d, p, sz);
+ DPUTZ(&d);
+
+ /* --- Add an appropriate block to the key table --- *
+ *
+ * Simply destroy old data if there's already a match.
+ */
+
+ {
+ unsigned f;
+ ks = sym_find(&k->u.s, d.buf, d.len + 1, sizeof(*ks), &f);
+ if (f)
+ key_destroy(&ks->k);
+ }
+
+ /* --- Read the key data for the subkey --- */
+
+ if (key_read(q + 1, &ks->k, &q)) {
+ sym_remove(&k->u.s, ks);
+ goto fail;
+ }
+ p = q;
+
+ /* --- Read the comma or close-bracket --- */
+
+ if (*p == ']')
+ break;
+ else if (*p == ',')
+ p++;
+ else
+ goto fail;
+ }
+
+ /* --- Step past the close bracket --- */
+
+ p++;
+ dstr_destroy(&d);
+ break;
+
+ /* --- Tidy up after a failure --- */
+
+ fail:
+ dstr_destroy(&d);
+ key_destroy(k);
+ return (-1);
+ } break;
+
+ /* --- Anything else is unknown --- */
+
+ default:
+ return (-1);
+ }
+
+ /* --- Return the end pointer --- */
+
+ if (pp)
+ *pp = (char *)p;
+ return (0);
+}
+
+/* --- @key_write@ --- *
+ *
+ * Arguments: @key_data *k@ = pointer to key data
+ * @dstr *d@ = destination string to write on
+ * @const key_filter *kf@ = pointer to key selection block
+ *
+ * Returns: Nonzero if an item was actually written.
+ *
+ * Use: Writes a key in a textual encoding.
+ */
+
+int key_write(key_data *k, dstr *d, const key_filter *kf)
+{
+ int rc = 0;
+ if (!KEY_MATCH(k, kf))
+ return (0);
+ switch (k->e & KF_ENCMASK) {
+ case KENC_BINARY:
+ case KENC_ENCRYPT: {
+ base64_ctx b;
+
+ if ((k->e & KF_ENCMASK) == KENC_BINARY)
+ key_writeflags(k->e, d);
+ else
+ DPUTS(d, "encrypt,secret");
+ DPUTC(d, ':');
+ base64_init(&b);
+ b.indent = "";
+ b.maxline = 0;
+ base64_encode(&b, k->u.k.k, k->u.k.sz, d);
+ base64_encode(&b, 0, 0, d);
+ rc = 1;
+ } break;
+ case KENC_MP:
+ key_writeflags(k->e, d);
+ DPUTC(d, ':');
+ mp_writedstr(k->u.m, d, 10);
+ rc = 1;
+ break;
+ case KENC_STRUCT: {
+ sym_iter i;
+ key_struct *ks;
+ char del = 0;
+ size_t n = d->len;
+
+ DPUTS(d, "struct:[");
+ for (sym_mkiter(&i, &k->u.s); (ks = sym_next(&i)) != 0; ) {
+ size_t o = d->len;
+ if (del)
+ DPUTC(d, del);
+ DPUTS(d, SYM_NAME(ks));
+ DPUTC(d, '=');
+ if (!key_write(&ks->k, d, kf))
+ d->len = o;
+ else {
+ del = ',';
+ rc = 1;
+ }
+ }
+ if (!rc)
+ d->len = n;
+ else
+ DPUTC(d, ']');
+ } break;
+ }
+ DPUTZ(d);
+
+ return (rc);
+}
+
+/*----- Binary encoding ---------------------------------------------------*/
+
+/* --- @key_decode@ --- *
+ *
+ * Arguments: @const void *p@ = pointer to buffer to read
+ * @size_t sz@ = size of the buffer
+ * @key_data *k@ = pointer to key data block to write to
+ *
+ * Returns: Zero if everything worked, nonzero otherwise.
+ *
+ * Use: Decodes a binary representation of a key.
+ */
+
+int key_decode(const void *p, size_t sz, key_data *k)
+{
+ const octet *q = p;
+ size_t psz;
+ unsigned e;
+
+ /* --- Parse the header information --- *
+ *
+ * Make sure the size matches external reality. Security holes have been
+ * known to creep in without this sort of check. (No, this isn't an after-
+ * the-fact patch-up.)
+ */
+
+ e = LOAD16(q);
+ psz = LOAD16(q + 2);
+ if (psz + 4 > sz)
+ return (-1);
+ k->e = e;
+
+ /* --- Now decide what to do --- */
+
+ switch (e & KF_ENCMASK) {
+
+ /* --- Plain binary data --- */
+
+ case KENC_BINARY:
+ case KENC_ENCRYPT:
+ k->u.k.k = sub_alloc(psz);
+ memcpy(k->u.k.k, q + 4, psz);
+ k->u.k.sz = psz;
+ break;
+
+ /* --- Multiprecision integer data --- */
+
+ case KENC_MP:
+ k->u.m = mp_loadb(MP_NEW, q + 4, psz);
+ if (k->e & KF_BURN)
+ mp_burn(k->u.m);
+ break;
+
+ /* --- Structured key data --- */
+
+ case KENC_STRUCT: {
+ dstr d = DSTR_INIT;
+ key_struct *ks;
+ unsigned f;
+
+ if ((k->e & ~KF_ENCMASK) || (psz & 3))
+ return (-1);
+ q += 4;
+ sym_create(&k->u.s);
+
+ while (psz) {
+
+ /* --- Read the tag string --- */
+
+ DRESET(&d);
+ sz = LOAD8(q);
+ if (sz >= psz)
+ goto fail;
+ DPUTM(&d, q + 1, sz);
+ DPUTZ(&d);
+ sz = (sz + 4) & ~3;
+ q += sz; psz -= sz;
+
+ /* --- Read the encoding and size --- */
+
+ e = LOAD16(q);
+ sz = (LOAD16(q + 2) + 7) & ~3;
+ if (sz > psz)
+ goto fail;
+
+ /* --- Create a table node and fill it in --- */
+
+ ks = sym_find(&k->u.s, d.buf, d.len + 1, sizeof(*ks), &f);
+ if (f)
+ goto fail;
+ if (key_decode(q, sz, &ks->k)) {
+ sym_remove(&k->u.s, ks);
+ goto fail;
+ }
+ psz -= sz;
+ q += sz;
+ }
+ dstr_destroy(&d);
+ break;
+
+ /* --- Tidy up after a failure --- */
+
+ fail:
+ dstr_destroy(&d);
+ key_destroy(k);
+ return (-1);
+ } break;
+
+ /* --- Everything else --- */
+
+ default:
+ return (-1);
+ }
+
+ /* --- OK, that was good --- */
+
+ return (0);
+}
+
+/* --- @key_encode@ --- *
+ *
+ * Arguments: @key_data *k@ = pointer to key data block
+ * @dstr *d@ = pointer to destination string
+ * @const key_filter *kf@ = pointer to key selection block
+ *
+ * Returns: Nonzero if an item was actually written.
+ *
+ * Use: Encodes a key block as binary data.
+ */
+
+int key_encode(key_data *k, dstr *d, const key_filter *kf)
+{
+ int rc = 0;
+ if (!KEY_MATCH(k, kf))
+ return (0);
+ switch (k->e & KF_ENCMASK) {
+ case KENC_BINARY:
+ case KENC_ENCRYPT: {
+ char *p;
+
+ DENSURE(d, (k->u.k.sz + 7) & ~3);
+ p = d->buf + d->len;
+ STORE16(p, k->e);
+ STORE16(p + 2, k->u.k.sz);
+ d->len += 4;
+ DPUTM(d, k->u.k.k, k->u.k.sz);
+ rc = 1;
+ } break;
+
+ case KENC_MP: {
+ char *p;
+ size_t sz = mp_octets(k->u.m);
+
+ DENSURE(d, (sz + 7) & ~3);
+ p = d->buf + d->len;
+ STORE16(p, k->e);
+ STORE16(p + 2, sz);
+ mp_storeb(k->u.m, p + 4, sz);
+ d->len += sz + 4;
+ rc = 1;
+ } break;
+
+ case KENC_STRUCT: {
+ size_t n;
+ char *p;
+ key_struct *ks;
+ sym_iter i;
+
+ n = d->len;
+ DENSURE(d, 4);
+ p = d->buf + n;
+ STORE16(p, k->e & KF_ENCMASK);
+ d->len += 4;
+ for (sym_mkiter(&i, &k->u.s); (ks = sym_next(&i)) != 0; ) {
+ size_t o = d->len;
+ DENSURE(d, 1);
+ *(octet *)(d->buf + d->len++) = strlen(SYM_NAME(ks));
+ DPUTS(d, SYM_NAME(ks));
+ while (d->len & 3)
+ DPUTC(d, 0);
+ if (key_encode(&ks->k, d, kf))
+ rc = 1;
+ else
+ d->len = o;
+ }
+ if (!rc)
+ d->len = n;
+ else {
+ p = d->buf + n + 2;
+ n = d->len - n - 4;
+ STORE16(p, n);
+ }
+ } break;
+ }
+ while (d->len & 3)
+ DPUTC(d, 0);
+ return (rc);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: key-file.c,v 1.1 1999/12/22 15:47:48 mdw Exp $
+ *
+ * System-dependent key filing operations
+ *
+ * (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-file.c,v $
+ * Revision 1.1 1999/12/22 15:47:48 mdw
+ * Major key-management revision.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <mLib/dstr.h>
+#include <mLib/lock.h>
+
+#include "key.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @fdcopy@ --- *
+ *
+ * Arguments: @int source@ = source file descriptor
+ * @int dest@ = destination file descriptor
+ *
+ * Returns: Zero if OK, nonzero otherwise.
+ *
+ * Use: Copies data from one file descriptor to another.
+ */
+
+static int fdcopy(int source, int dest)
+{
+ char buf[4096];
+
+ if (lseek(source, 0, SEEK_SET) < 0||
+ lseek(dest, 0, SEEK_SET) < 0 ||
+ ftruncate(dest, 0) < 0)
+ return (-1);
+ for (;;) {
+ int n = read(source, buf, sizeof(buf));
+ if (n < 0)
+ return (-1);
+ else if (n == 0)
+ break;
+ else if (write(dest, buf, n) < 0)
+ return (-1);
+ }
+ return (0);
+}
+
+/* --- @key_save@ --- *
+ *
+ * Arguments: @key_file *f@ = pointer to key file block
+ *
+ * Returns: A @KWRITE_@ code indicating how well it worked.
+ *
+ * Use: Writes a key file's data back to the actual file. This code
+ * is extremely careful about error handling. It should usually
+ * be able to back out somewhere sensible, but it can tell when
+ * it's got itself into a real pickle and starts leaving well
+ * alone.
+ *
+ * Callers, please make sure that you ring alarm bells when this
+ * function returns @KWRITE_BROKEN@.
+ */
+
+int key_save(key_file *f)
+{
+ dstr n_older = DSTR_INIT, n_old = DSTR_INIT, n_new = DSTR_INIT;
+ int rc = KWRITE_FAIL;
+
+ if (!(f->f & KF_MODIFIED))
+ return (KWRITE_OK);
+
+ /* --- Write a new key file out --- *
+ *
+ * Check for an error after each key line. This ought to be enough.
+ * Checking after each individual byte write and @fprintf@ isn't much fun.
+ */
+
+ dstr_putf(&n_new, "%s.new", f->name);
+
+ {
+ key *k;
+ key_iter i;
+ FILE *fp;
+
+ if ((fp = fopen(n_new.buf, "w")) == 0)
+ goto fail_open;
+
+ for (key_mkiter(&i, f); (k = key_next(&i)) != 0; ) {
+ if (key_extract(f, k, fp, 0)) {
+ fclose(fp);
+ goto fail_write;
+ }
+ }
+
+ if (fclose(fp))
+ goto fail_write;
+ }
+
+ /* --- Set up the other filenames --- */
+
+ dstr_putf(&n_older, "%s.older", f->name);
+ dstr_putf(&n_old, "%s.old", f->name);
+
+ /* --- Move the current backup on one --- *
+ *
+ * If the `older' file exists, then we're in need of attention.
+ */
+
+ {
+ struct stat st;
+ if (stat(n_older.buf, &st) == 0 || errno != ENOENT) {
+ errno = EEXIST;
+ rc = KWRITE_BROKEN;
+ goto fail_shift;
+ }
+ if (rename(n_old.buf, n_older.buf) && errno != ENOENT)
+ goto fail_shift;
+ }
+
+ /* --- Copy the current file to the backup --- */
+
+ {
+ int fd;
+ if ((fd = open(n_old.buf, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0)
+ goto fail_backup;
+ if (fdcopy(fileno(f->fp), fd)) {
+ close(fd);
+ goto fail_backup;
+ }
+ if (close(fd))
+ goto fail_backup;
+ }
+
+ /* --- Copy the newly created file to the current one --- *
+ *
+ * This is the dangerous bit.
+ */
+
+ {
+ int fd;
+ if ((fd = open(n_new.buf, O_RDONLY)) < 0)
+ goto fail_update;
+ if (fdcopy(fd, fileno(f->fp))) {
+ close(fd);
+ goto fail_update;
+ }
+ close(fd);
+ if (fsync(fileno(f->fp)))
+ goto fail_update;
+ }
+
+ /* --- Clean up --- *
+ *
+ * Remove the `new' file and the `older' backup. Then we're done.
+ */
+
+ unlink(n_new.buf);
+ unlink(n_older.buf);
+ return (KWRITE_OK);
+
+ /* --- Failure while writing the new key file --- *
+ *
+ * I need to copy the backup back. If that fails then I'm really stuffed.
+ * If not, then I might as well try to get the backups sorted back out
+ * again.
+ */
+
+fail_update:
+ {
+ int fd;
+ int e = errno;
+
+ if ((fd = open(n_old.buf, O_RDONLY)) < 0)
+ rc = KWRITE_BROKEN;
+ else if (fdcopy(fd, fileno(f->fp))) {
+ close(fd);
+ rc = KWRITE_BROKEN;
+ } else {
+ close(fd);
+ if (fsync(fileno(f->fp)))
+ rc = KWRITE_BROKEN;
+ }
+
+ errno = e;
+ if (rc == KWRITE_BROKEN)
+ goto fail_shift;
+ }
+ /* Now drop through */
+
+ /* --- Failure while writing the new backup --- *
+ *
+ * The new backup isn't any use. Try to recover the old one.
+ */
+
+fail_backup:
+ {
+ int e = errno;
+ unlink(n_old.buf);
+ if (rename(n_older.buf, n_old.buf) && errno != ENOENT)
+ rc = KWRITE_BROKEN;
+ errno = e;
+ }
+ /* Now drop through */
+
+ /* --- Failure while demoting the current backup --- *
+ *
+ * Leave the completed output file there for the operator in case he wants
+ * to clean up.
+ */
+
+fail_shift:
+ dstr_destroy(&n_new);
+ dstr_destroy(&n_old);
+ dstr_destroy(&n_older);
+ return (rc);
+
+/* --- Failure during write of new data --- *
+ *
+ * Clean up the new file and return. These errors can never cause
+ * breakage.
+ */
+
+fail_write:
+ unlink(n_new.buf);
+ fail_open:
+ dstr_destroy(&n_new);
+ return (rc);
+}
+
+/* --- @key_lockfile@ --- *
+ *
+ * Arguments: @key_file *f@ = pointer to file structure to initialize
+ * @const char *file@ = pointer to the file name
+ * @int how@ = opening options (@KOPEN_*@).
+ *
+ * Returns: Zero if it worked, nonzero otherwise.
+ *
+ * Use: Opens a keyfile and stores the information needed for
+ * continued access in the structure.
+ *
+ * If the file is opened with @KOPEN_WRITE@, it's created if
+ * necessary with read and write permissions for owner only, and
+ * locked for update while it's open.
+ *
+ * This is a system-dependent routine, and only really intended
+ * for the private use of @key_open@.
+ */
+
+int key_lockfile(key_file *f, const char *file, int how)
+{
+ int of, lf;
+ const char *ff;
+ int fd;
+
+ /* --- Lots of things depend on whether we're writing --- */
+
+ switch (how) {
+ case KOPEN_READ:
+ of = O_RDONLY;
+ lf = LOCK_NONEXCL;
+ ff = "r";
+ break;
+ case KOPEN_WRITE:
+ of = O_RDWR | O_CREAT;
+ lf = LOCK_EXCL;
+ ff = "r+";
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+
+ /* --- Open and lock the file --- */
+
+ if ((fd = open(file, of, 0600)) < 0)
+ return (-1);
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0 ||
+ lock_file(fd, lf) < 0 ||
+ (f->fp = fdopen(fd, ff)) == 0) {
+ close(fd);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: key-flags.c,v 1.1 1999/12/22 15:47:48 mdw Exp $
+ *
+ * Reading and writing key flag strings
+ *
+ * (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-flags.c,v $
+ * Revision 1.1 1999/12/22 15:47:48 mdw
+ * Major key-management revision.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <mLib/bits.h>
+#include <mLib/dstr.h>
+
+#include "key.h"
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct key_flags {
+ unsigned f;
+ unsigned m;
+} key_flags;
+
+/*----- Flags table -------------------------------------------------------*/
+
+typedef struct flagent {
+ const char *name;
+ unsigned f;
+ unsigned m;
+} flagent;
+
+static flagent flagtab[] = {
+
+ /* --- Encoding types --- */
+
+ { "binary", KENC_BINARY, KF_ENCMASK },
+ { "integer", KENC_MP, KF_ENCMASK },
+ { "struct", KENC_STRUCT, KF_ENCMASK },
+ { "encrypt", KENC_ENCRYPT, KF_ENCMASK },
+
+ /* --- Classes of keys --- */
+
+ { "shared", KCAT_SHARE, KF_CATMASK },
+ { "public", KCAT_PUB, KF_CATMASK },
+ { "private", KCAT_PRIV, KF_CATMASK },
+ { "symmetric", KCAT_SYMM, KF_CATMASK },
+ { "secret", 0, KF_NONSECRET },
+ { "-secret", KF_NONSECRET, KF_NONSECRET },
+
+ /* --- Other flags --- */
+
+ { "burn", KF_BURN, KF_BURN },
+ { "-burn", 0, KF_BURN },
+
+ /* --- End marker --- */
+
+ { 0, 0, 0 }
+};
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @key_readflags@ --- *
+ *
+ * Arguments: @const char *p@ = pointer to string to read
+ * @char **pp@ = where to store the end pointer
+ * @unsigned *ff@ = where to store the flags
+ * @unsigned *mm@ = where to store the mask
+ *
+ * Returns: Zero if all went well, nonzero if there was an error.
+ *
+ * Use: Reads a flag string.
+ */
+
+int key_readflags(const char *p, char **pp, unsigned *ff, unsigned *mm)
+{
+ unsigned f = 0, m = 0;
+
+ for (;;) {
+ size_t sz = strcspn(p, ",:");
+ flagent *e, *ee = 0;
+
+ /* --- Look up the string in the flags table --- */
+
+ for (e = flagtab; e->name; e++) {
+ if (strncmp(e->name, p, sz) == 0) {
+ if (e->name[sz] == 0) {
+ ee = e;
+ break;
+ } else if (ee)
+ return (KERR_BADFLAGS);
+ else
+ ee = e;
+ }
+ }
+ if (!ee)
+ return (KERR_BADFLAGS);
+
+ /* --- Adjust the flag words --- *
+ *
+ * Ensure that the flags set are disjoint.
+ */
+
+ if (m & ee->m)
+ return (KERR_BADFLAGS);
+ m |= ee->m;
+ f |= ee->f;
+ p += sz;
+ if (*p == 0 || *p == ':')
+ break;
+ p++;
+ }
+
+ /* --- Report the results --- */
+
+ if (ff) *ff = f;
+ if (mm) *mm = m;
+ if (pp) *pp = (char *)p;
+ return (0);
+}
+
+/* --- @key_writeflags@ --- *
+ *
+ * Arguments: @unsigned f@ = flags to write
+ * @dstr *d@ = pointer to destination string
+ *
+ * Returns: ---
+ *
+ * Use: Emits a flags word as a string representation.
+ */
+
+void key_writeflags(unsigned f, dstr *d)
+{
+ int del = 0;
+ flagent *e;
+ unsigned m = 0;
+
+ for (e = flagtab; e->name; e++) {
+ if (m & e->m || e->name[0] == '-' || (f & e->m) != e->f)
+ continue;
+ if (del)
+ DPUTC(d, ',');
+ DPUTS(d, e->name);
+ m |= e->m;
+ del = 1;
+ }
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: key-io.c,v 1.1 1999/12/22 15:47:48 mdw Exp $
+ *
+ * Adding new keys to a key file
+ *
+ * (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-io.c,v $
+ * Revision 1.1 1999/12/22 15:47:48 mdw
+ * Major key-management revision.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <mLib/alloc.h>
+#include <mLib/bits.h>
+#include <mLib/crc32.h>
+#include <mLib/dstr.h>
+#include <mLib/hash.h>
+#include <mLib/str.h>
+#include <mLib/sub.h>
+#include <mLib/sym.h>
+#include <mLib/url.h>
+
+#include "key.h"
+
+/*----- Tweakable macros --------------------------------------------------*/
+
+#define KEY_LOAD(n) ((n) * 2)
+
+/*----- Low-level functions -----------------------------------------------*/
+
+/* --- @insert@ --- *
+ *
+ * Arguments: @key_file *f@ = pointer to file structure
+ * @key *k@ = pointer to key block to insert
+ *
+ * Returns: Error code (one of the @KERR@ code).
+ *
+ * Use: Links a new key block into the complicated data structure
+ * which is a keyring file.
+ */
+
+static int insert(key_file *f, key *k)
+{
+ key_ref *kr = 0;
+ unsigned found;
+
+ /* --- Sanity preservatives --- */
+
+ if (key_chkident(k->type))
+ return (KERR_BADTYPE);
+ else if (k->tag && key_chkident(k->tag))
+ return (KERR_BADTAG);
+
+ /* --- Insert into the tag table --- */
+
+ if (k->tag) {
+ kr = sym_find(&f->bytag, k->tag, -1, sizeof(*kr), &found);
+ if (found)
+ return (KERR_DUPTAG);
+ kr->k = k;
+ }
+
+ /* --- Insert into the id table --- */
+
+ {
+ hash_base **bin, *b;
+
+ bin = HASH_BIN(&f->byid, k->id);
+ for (b = *bin; b; b = b->next) {
+ if (b->hash == k->id) {
+ if (kr)
+ sym_remove(&f->bytag, kr);
+ return (KERR_DUPID);
+ }
+ }
+
+ k->_b.next = *bin;
+ *bin = &k->_b;
+ k->_b.hash = k->id;
+ }
+
+ /* --- Extend the table --- */
+
+ if (f->idload > 0)
+ f->idload--;
+ else if (hash_extend(&f->byid))
+ f->idload = KEY_LOAD(f->byid.mask / 2);
+
+ /* --- Insert into the type table --- */
+
+ kr = sym_find(&f->bytype, k->type, -1, sizeof(*kr), &found);
+ if (!found) {
+ kr->k = k;
+ k->next = 0;
+ } else {
+ key **p = &kr->k;
+ if (k->exp != KEXP_FOREVER) {
+ while (*p && (*p)->exp != KEXP_EXPIRE && (*p)->exp > k->exp)
+ p = &(*p)->next;
+ }
+ k->next = *p;
+ *p = k;
+ }
+
+ return (KERR_OK);
+}
+
+/*----- Reading and writing keys ------------------------------------------*/
+
+/* --- @exptime@ --- *
+ *
+ * Arguments: @const char *p@ = pointer to string
+ *
+ * Returns: Time value.
+ *
+ * Use: Translates an expiry or deletion time.
+ */
+
+time_t exptime(const char *p)
+{
+ size_t sz = strlen(p);
+ if (strncmp(p, "expired", sz) == 0)
+ return (KEXP_EXPIRE);
+ else if (strncmp(p, "forever", sz) == 0)
+ return (KEXP_FOREVER);
+ else
+ return (atol(p));
+}
+
+/* --- @key_merge@ --- *
+ *
+ * Arguments: @key_file *f@ = pointer to file structure
+ * @const char *file@ = name of file (for error messages)
+ * @FILE *fp@ = file handle to read from
+ * @key_reporter *rep@ = error reporting function
+ * @void *arg@ = argument for function
+ *
+ * Returns: Error code (one of the @KERR@ constants).
+ *
+ * Use: Reads keys from a file, and inserts them into the file.
+ */
+
+int key_merge(key_file *f, const char *file, FILE *fp,
+ key_reporter *rep, void *arg)
+{
+ int line = 0;
+ dstr l = DSTR_INIT;
+ dstr n = DSTR_INIT, v = DSTR_INIT;
+
+ if (!(f->f & KF_WRITE))
+ return (KERR_READONLY);
+
+ for (; dstr_putline(&l, fp) != EOF; DRESET(&l)) {
+ char *vf[6];
+ char *p = l.buf;
+ key *k;
+
+ /* --- Skip blank lines and comments --- *
+ *
+ * Quite what they're doing in what ought to be an automatically-
+ * maintained file I don't know.
+ */
+
+ line++;
+ while (isspace((unsigned char)*p))
+ p++;
+ if (!*p || *p == '#')
+ continue;
+
+ /* --- Break the line into fields --- *
+ *
+ * There are currently six fields of interest:
+ *
+ * * The key's identification (id, tag and type).
+ * * The actual key data itself.
+ * * The key expiry time.
+ * * The key deletion time.
+ * * The attributes field.
+ * * Any further comments.
+ *
+ * All but the last field can contain no spaces.
+ */
+
+ {
+ int n = str_split(p, vf, 5, &vf[5]);
+ if (n < 4) {
+ if (rep)
+ rep(file, line, "too few fields", arg);
+ goto skip_0;
+ }
+ }
+
+ /* --- Allocate a new key block --- */
+
+ k = CREATE(key);
+
+ /* --- Extract the key data into the block --- */
+
+ if (key_read(vf[1], &k->k, 0)) {
+ if (rep)
+ rep(file, line, "bad key data", arg);
+ goto skip_1;
+ }
+
+ /* --- Decode the identification field --- *
+ *
+ * For compatibility, derive a keyid from the key data. This can only be
+ * done if the key encoding is binary (and presumably old-encoding binary
+ * at that).
+ */
+
+ {
+ char *q = strchr(vf[0], ':');
+ char *qq;
+
+ if (!q) {
+ if (k->k.e != KENC_BINARY) {
+ if (rep)
+ rep(file, line, "new-style key encoding but no keyid", arg);
+ goto skip_2;
+ }
+ k->id = crc32(0, k->k.u.k.k, k->k.u.k.sz);
+ k->type = xstrdup(vf[0]);
+ k->tag = 0;
+ } else {
+ *q++ = 0;
+ k->id = strtoul(p, 0, 16);
+ if ((qq = strchr(q, ':')) == 0 || !qq[1]) {
+ if (qq)
+ *qq = 0;
+ k->tag = 0;
+ } else {
+ *qq++ = 0;
+ k->tag = xstrdup(qq);
+ }
+ k->type = xstrdup(q);
+ }
+ }
+
+ /* --- Get a key block for the new key --- */
+
+ k->exp = exptime(vf[2]);
+ k->del = exptime(vf[3]);
+
+ /* --- Insert the key block into the table --- */
+
+ {
+ int err;
+
+ again:
+ if ((err = insert(f, k)) < 0) {
+ if (err == KERR_DUPTAG) {
+ if (rep)
+ rep(file, line, "duplicate key tag stripped", arg);
+ free(k->tag);
+ k->tag = 0;
+ goto again;
+ }
+ if (rep)
+ rep(file, line, key_strerror(err), arg);
+ goto skip_3;
+ }
+ }
+
+ /* --- Parse up the attributes, if specified --- */
+
+ sym_create(&k->a);
+ if (vf[4] && strcmp(vf[4], "-") != 0) {
+ url_dctx uc;
+ for (url_initdec(&uc, vf[4]); url_dec(&uc, &n, &v); ) {
+ key_putattr(f, k, n.buf, v.buf);
+ DRESET(&n); DRESET(&v);
+ }
+ }
+
+ /* --- Insert the comment --- */
+
+ if (vf[5])
+ k->c = xstrdup(vf[5]);
+ else
+ k->c = 0;
+ continue;
+
+ /* --- Tidy up after something going wrong --- */
+
+ skip_3:
+ if (k->tag)
+ free(k->tag);
+ free(k->type);
+ skip_2:
+ key_destroy(&k->k);
+ skip_1:
+ DESTROY(k);
+ skip_0:;
+ }
+
+ /* --- Extensive tidying up now required --- */
+
+ dstr_destroy(&l);
+ dstr_destroy(&n);
+ dstr_destroy(&v);
+ f->f |= KF_MODIFIED;
+ return (0);
+}
+
+/* --- @key_extract@ --- *
+ *
+ * Arguments: @key_file *f@ = pointer to file structure
+ * @key *k@ = key to extract
+ * @FILE *fp@ = file to write on
+ * @const key_filter *kf@ = pointer to key selection block
+ *
+ * Returns: Zero if OK, EOF on error.
+ *
+ * Use: Extracts a key to an ouptut file.
+ */
+
+int key_extract(key_file *f, key *k, FILE *fp, const key_filter *kf)
+{
+ dstr d = DSTR_INIT;
+ time_t t = time(0);
+
+ /* --- Skip the key if it's deleted or unselected--- */
+
+ if (KEY_EXPIRED(t, k->del) || !key_match(&k->k, kf))
+ return (0);
+
+ /* --- Encode the key and write the easy stuff --- */
+
+ key_fulltag(k, &d);
+ DPUTC(&d, ' ');
+ key_write(&k->k, &d, kf);
+ DPUTC(&d, ' ');
+ dstr_write(&d, fp);
+ DRESET(&d);
+
+ /* --- Write out the expiry and deletion times --- */
+
+ if (KEY_EXPIRED(t, k->exp))
+ fputs("expired ", fp);
+ else if (k->exp == KEXP_FOREVER)
+ fputs("forever ", fp);
+ else
+ fprintf(fp, "%li ", (long)k->exp);
+
+ if (k->del == KEXP_FOREVER)
+ fputs("forever ", fp);
+ else
+ fprintf(fp, "%li ", (long)k->del);
+
+ /* --- Output the attributes --- */
+
+ {
+ int none = 1;
+ sym_iter i;
+ key_attr *a;
+ url_ectx uc;
+
+ url_initenc(&uc);
+ for (sym_mkiter(&i, &k->a); (a = sym_next(&i)) != 0; ) {
+ none = 0;
+ url_enc(&uc, &d, SYM_NAME(a), a->p);
+ }
+ if (none)
+ DPUTS(&d, "-");
+ DWRITE(&d, fp);
+ }
+
+ dstr_destroy(&d);
+ if (k->c) {
+ putc(' ', fp);
+ fputs(k->c, fp);
+ }
+ putc('\n', fp);
+ return (ferror(fp) ? EOF : 0);
+}
+
+/*----- Opening and closing files -----------------------------------------*/
+
+/* --- @key_open@ --- *
+ *
+ * Arguments: @key_file *f@ = pointer to file structure to initialize
+ * @const char *file@ = pointer to the file name
+ * @int how@ = opening options (@KOPEN_*@).
+ * @key_reporter *rep@ = error reporting function
+ * @void *arg@ = argument for function
+ *
+ * Returns: Zero if it worked, nonzero otherwise.
+ *
+ * Use: Opens a key file, reads its contents, and stores them in a
+ * structure. The file is locked appropriately until closed
+ * using @key_close@. On an error, everything is cleared away
+ * tidily. If the file is opened with @KOPEN_WRITE@, it's
+ * created if necessary, with read and write permissions for its
+ * owner only.
+ */
+
+int key_open(key_file *f, const char *file, int how,
+ key_reporter *rep, void *arg)
+{
+ if (key_lockfile(f, file, how))
+ return (-1);
+
+ /* --- Trivial bits of initialization --- */
+
+ f->f = 0;
+ f->name = xstrdup(file);
+
+ /* --- Read the file of keys into the table --- */
+
+ hash_create(&f->byid, 16);
+ f->idload = KEY_LOAD(16);
+ sym_create(&f->bytype);
+ sym_create(&f->bytag);
+ f->f |= KF_WRITE;
+ key_merge(f, file, f->fp, rep, arg);
+ if (how == KOPEN_READ)
+ f->f &= ~(KF_WRITE | KF_MODIFIED);
+ else
+ f->f &= ~KF_MODIFIED;
+
+ /* --- Close the file if only needed for reading --- */
+
+ if (how == KOPEN_READ) {
+ fclose(f->fp);
+ f->fp = 0;
+ }
+
+ return (0);
+}
+
+/* --- @key_close@ --- *
+ *
+ * Arguments: @key_file *f@ = pointer to key file block
+ *
+ * Returns: A @KWRITE_@ code indicating how it went.
+ *
+ * Use: Frees all the key data, writes any changes. Make sure that
+ * all hell breaks loose if this returns @KWRITE_BROKEN@.
+ */
+
+int key_close(key_file *f)
+{
+ int e;
+ hash_base *b;
+ hash_iter i;
+
+ if ((e = key_save(f)) != KWRITE_OK)
+ return (e);
+
+ /* --- Free all the individual keys --- */
+
+ for (hash_mkiter(&i, &f->byid); (b = hash_next(&i)) != 0; ) {
+ sym_iter j;
+ key_attr *a;
+ key *k = (key *)b;
+
+ key_destroy(&k->k);
+ free(k->type);
+ free(k->tag);
+ if (k->c)
+ free(k->c);
+ for (sym_mkiter(&j, &k->a); (a = sym_next(&j)) != 0; )
+ free(a->p);
+ sym_destroy(&k->a);
+ DESTROY(k);
+ }
+ hash_destroy(&f->byid);
+ sym_destroy(&f->bytype);
+ sym_destroy(&f->bytag);
+
+ if (f->fp)
+ fclose(f->fp);
+ free(f->name);
+ return (KWRITE_OK);
+}
+
+/* --- @key_new@ ---
+ *
+ * Arguments: @key_file *f@ = pointer to key file
+ * @uint32 id@ = keyid to set
+ * @const char *type@ = the type of this key
+ * @time_t exp@ = when the key expires
+ * @int *err@ = where to store the error condition
+ *
+ * Returns: Key block containing new data, or null if it couldn't be
+ * done.
+ *
+ * Use: Attaches a new key to a key file. You must have a writable
+ * key file for this to work.
+ *
+ * The type is a key type string. This interface doesn't care
+ * about how type strings are formatted: it just treats them as
+ * opaque gobs of text. Clients are advised to choose some
+ * standard for representing key types, though.
+ *
+ * The expiry time should either be a time in the future, or the
+ * magic value @KEXP_FOREVER@ which means `never expire this
+ * key'. Be careful with `forever' keys. If I were you, I'd
+ * use a more sophisticated key management system than this for
+ * them.
+ *
+ * You have to set the actual key yourself.
+ */
+
+key *key_new(key_file *f, uint32 id, const char *type, time_t exp, int *err)
+{
+ key *k = 0;
+ time_t t = time(0);
+ int e = KERR_OK;
+
+ /* --- Make sure the file is writable --- */
+
+ if (!(f->f & KF_WRITE))
+ e = KERR_READONLY;
+ else if (KEY_EXPIRED(t, exp))
+ e = KERR_EXPIRED;
+ else if (key_chkident(type))
+ e = KERR_BADTYPE;
+ else {
+ k = CREATE(key);
+ k->id = id;
+ k->tag = 0;
+ k->type = xstrdup(type);
+ k->exp = k->del = exp;
+ k->c = 0;
+ k->k.e = 0;
+ sym_create(&k->a);
+ if ((e = insert(f, k)) == 0)
+ f->f |= KF_MODIFIED;
+ else {
+ free(k->type);
+ DESTROY(k);
+ k = 0;
+ }
+ }
+ return (k);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: key-moan.c,v 1.1 1999/12/22 15:47:48 mdw Exp $
+ *
+ * Standard error handling function for key loading
+ *
+ * (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-moan.c,v $
+ * Revision 1.1 1999/12/22 15:47:48 mdw
+ * Major key-management revision.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/report.h>
+
+#include "key.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @key_moan@ --- *
+ *
+ * Arguments: @const char *file@ = name of the file
+ * @int line@ = line number in file
+ * @const char *msg@ = error message
+ * @void *p@ = argument pointer
+ *
+ * Returns: ---
+ *
+ * Use: Reports an error message about loading a key file.
+ */
+
+void key_moan(const char *file, int line, const char *msg, void *p)
+{
+ moan("%s:%i: error: %s", file, line, msg);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: key-pass.c,v 1.1 1999/12/22 15:47:48 mdw Exp $
+ *
+ * Encrypting keys with passphrases
+ *
+ * (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-pass.c,v $
+ * Revision 1.1 1999/12/22 15:47:48 mdw
+ * Major key-management revision.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/dstr.h>
+
+#include "key.h"
+#include "paranoia.h"
+#include "passphrase.h"
+#include "rand.h"
+
+#include "blowfish-cbc.h"
+#include "rmd160.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @key_plock@ --- *
+ *
+ * Arguments: @const char *tag@ = tag to use for passphrase
+ * @key_data *k@ = source key data block
+ * @key_data *kt@ = target key data block
+ *
+ * Returns: Zero if successful, nonzero if there was a problem.
+ *
+ * Use: Locks a key by encrypting it with a passphrase.
+ */
+
+int key_plock(const char *tag, key_data *k, key_data *kt)
+{
+ dstr d = DSTR_INIT;
+ octet hash[RMD160_HASHSZ];
+ char buf[256];
+
+ /* --- Sanity check --- */
+
+ assert(((void)"Key data is already encrypted",
+ (k->e & KF_ENCMASK) != KENC_ENCRYPT));
+
+ /* --- Read the passphrase --- */
+
+ if (passphrase_read(tag, PMODE_VERIFY, buf, sizeof(buf)))
+ return (-1);
+
+ /* --- Extract the key data --- *
+ *
+ * On the front, put four random bytes to act as a passphrase salt (which
+ * remain in the clear), and four zero bytes to be able to spot duff
+ * passphrases when unlocking.
+ */
+
+ DENSURE(&d, 8);
+ rand_getgood(RAND_GLOBAL, d.buf, 4);
+ memset(d.buf + 4, 0, 4);
+ d.len += 8;
+ key_encode(k, &d, 0);
+ if (k == kt)
+ key_destroy(k);
+
+ /* --- Hash the passphrase into a key --- */
+
+ {
+ rmd160_ctx h;
+ rmd160_init(&h);
+ rmd160_hash(&h, d.buf, 4);
+ rmd160_hash(&h, buf, strlen(buf));
+ rmd160_done(&h, hash);
+ BURN(h);
+ BURN(buf);
+ }
+
+ /* --- Encrypt the key data --- */
+
+ {
+ blowfish_cbcctx c;
+ blowfish_cbcinit(&c, hash, sizeof(hash), 0);
+ blowfish_cbcencrypt(&c, d.buf + 4, d.buf + 4, d.len - 4);
+ BURN(hash);
+ BURN(c);
+ }
+
+ /* --- Put the key data back in the key --- */
+
+ key_encrypted(kt, d.buf, d.len);
+ dstr_destroy(&d);
+ return (0);
+}
+
+/* --- @key_punlock@ --- *
+ *
+ * Arguments: @const char *tag@ = tag to use for passphrase
+ * @key_data *k@ = source key data block
+ * @key_data *kt@ = target key data block
+ *
+ * Returns: Zero if it worked, nonzero if it didn't.
+ *
+ * Use: Unlocks a passphrase-locked key.
+ */
+
+int key_punlock(const char *tag, key_data *k, key_data *kt)
+{
+ octet hash[RMD160_HASHSZ];
+ char buf[256];
+ octet *p;
+ size_t sz;
+
+ /* --- Sanity check --- */
+
+ assert(((void)"Key data isn't encrypted",
+ (k->e & KF_ENCMASK) == KENC_ENCRYPT));
+
+ /* --- Fetch the passphrase --- */
+
+ if (passphrase_read(tag, PMODE_READ, buf, sizeof(buf)))
+ goto fail_0;
+
+ /* --- Allocate a destination buffer --- *
+ *
+ * Minimum size for a key is four bytes salt, four bytes zeroes, two bytes
+ * type, and two bytes length, making a total of twelve.
+ */
+
+ if (k->u.k.sz < 12)
+ goto fail_0;
+ sz = k->u.k.sz - 4;
+ p = xmalloc(k->u.k.sz);
+
+ /* --- Hash the passphrase --- */
+
+ {
+ rmd160_ctx h;
+ rmd160_init(&h);
+ rmd160_hash(&h, k->u.k.k, 4);
+ rmd160_hash(&h, buf, strlen(buf));
+ rmd160_done(&h, hash);
+ BURN(h);
+ BURN(buf);
+ }
+
+ /* --- Decrypt the key data --- */
+
+ {
+ blowfish_cbcctx c;
+ blowfish_cbcinit(&c, hash, sizeof(hash), 0);
+ blowfish_cbcdecrypt(&c, k->u.k.k + 4, p, sz);
+ BURN(hash);
+ BURN(c);
+ if (LOAD32(p) != 0) {
+ passphrase_cancel(tag);
+ goto fail_1;
+ }
+ }
+
+ /* --- Decode the key data into the destination buffer --- */
+
+ if (k == kt) {
+ key_destroy(k);
+ passphrase_cancel(tag);
+ }
+ if (key_decode(p + 4, sz - 4, kt))
+ goto fail_1;
+
+ /* --- Done --- */
+
+ free(p);
+ return (0);
+
+ /* --- Tidy up if things went wrong --- */
+
+fail_1:
+ free(p);
+fail_0:
+ return (-1);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
+++ /dev/null
-/* -*-c-*-
- *
- * $Id: key.c,v 1.1 1999/09/03 08:41:12 mdw Exp $
- *
- * Simple key management
- *
- * (c) 1999 Mark Wooding
- */
-
-/*----- 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.c,v $
- * Revision 1.1 1999/09/03 08:41:12 mdw
- * Initial import.
- *
- */
-
-/*----- Header files ------------------------------------------------------*/
-
-#include <ctype.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include <mLib/alloc.h>
-#include <mLib/base64.h>
-#include <mLib/bits.h>
-#include <mLib/crc32.h>
-#include <mLib/dstr.h>
-#include <mLib/hash.h>
-#include <mLib/lock.h>
-#include <mLib/report.h>
-#include <mLib/str.h>
-#include <mLib/sub.h>
-#include <mLib/sym.h>
-#include <mLib/url.h>
-
-#include "key.h"
-
-/*----- Useful macros -----------------------------------------------------*/
-
-#define KEY_PARANOID
-#define NOTHING
-
-#ifdef KEY_PARANOID
-# define KEY_WRITE(f, func, val) do { \
- if (!(f)->f & KF_WRITE) { \
- moan(#func " [caller error]: keyfile is readonly"); \
- errno = EROFS; \
- return val; \
- } \
- } while (0)
-#else
-# define KEY_WRITE(f, func) do { ; } while (0)
-#endif
-
-#define KEY_MODIFY(f) do { (f)->f |= KF_MODIFIED; } while (0)
-
-#define KEY_LOAD(n) ((n) * 2)
-
-/*----- Sanity checking of values -----------------------------------------*/
-
-/* --- @key_chktype@ --- *
- *
- * Arguments: @const char *type@ = pointer to a type string
- *
- * Returns: Zero if OK, -1 on error.
- *
- * Use: Checks whether a type string is OK.
- */
-
-int key_chktype(const char *type)
-{
- if (!type || !*type)
- goto fail;
- while (*type) {
- if (isspace((unsigned char)*type))
- goto fail;
- type++;
- }
- return (0);
-
-fail:
- errno = EINVAL;
- return (-1);
-}
-
-/* --- @key_chkcomment@ --- *
- *
- * Arguments: @const char *comment@ = pointer to a comment string
- *
- * Returns: Zero if OK, -1 on error.
- *
- * Use: Checks whether a comment string is OK.
- */
-
-int key_chkcomment(const char *c)
-{
- if (!c)
- return (0);
- if (!*c)
- goto fail;
- while (*c) {
- if (*c == '\n')
- goto fail;
- c++;
- }
- return (0);
-
-fail:
- errno = EINVAL;
- return (-1);
-}
-
-/*----- Low-level fiddling ------------------------------------------------*/
-
-/* --- @insert@ --- *
- *
- * Arguments: @key_file *f@ = pointer to file structure
- * @const char *type@ = type of key to insert
- * @const void *k@ = pointer to key data
- * @size_t ksz@ = size of key data
- * @time_t exp@ = expiry time for key
- * @time_t del@ = deletion time for key
- *
- * Returns: Pointer to key block to fill in the rest of, or zero.
- *
- * Use: Inserts a key into a key file.
- */
-
-static key *insert(key_file *f,
- const char *type,
- const void *k, size_t ksz,
- time_t exp, time_t del)
-{
- uint32 id;
- key *kk;
- key_type *t;
-
- /* --- Sanity preservatives --- */
-
- if (key_chktype(type))
- return (0);
-
- /* --- Insert into the id table --- */
-
- {
- hash_base **bin, **b;
-
- CRC32(id, 0, k, ksz);
- bin = HASH_BIN(&f->byid, id);
- for (b = bin; *b; b = &(*b)->next) {
- if ((*b)->hash == id) {
- errno = EEXIST;
- return (0);
- }
- }
-
- kk = CREATE(key);
- kk->_b.next = 0;
- *b = &kk->_b;
- kk->_b.hash = id;
- }
-
- /* --- Extend the table --- */
-
- if (f->idload > 0)
- f->idload--;
- else if (hash_extend(&f->byid))
- f->idload = KEY_LOAD(f->byid.mask / 2);
-
- /* --- Initialize the key block --- */
-
- kk->id = id;
- kk->k = sub_alloc(ksz);
- memcpy(kk->k, k, ksz);
- kk->ksz = ksz;
- kk->type = xstrdup(type);
- kk->exp = exp;
- kk->del = del;
- sym_create(&kk->a);
- kk->c = 0;
-
- /* --- Insert into the type table --- */
-
- {
- unsigned found;
- t = sym_find(&f->bytype, type, -1, sizeof(*t), &found);
- if (!found) {
- t->k = kk;
- kk->next = 0;
- } else {
- key **p = &t->k;
- if (exp != KEXP_FOREVER) {
- while (*p && (*p)->exp != KEXP_EXPIRE && (*p)->exp > exp)
- p = &(*p)->next;
- }
- kk->next = *p;
- *p = kk;
- }
- }
-
- return (kk);
-}
-
-/*----- 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_DELETED(i->t, k->del));
- return (k);
-}
-
-/* --- @key_mkattriter@ --- *
- *
- * Arguments: @key_attriter *i@ = pointer to attribute iterator
- * @key_file *f@ = pointer to key file
- * @key *k@ = pointer to key
- *
- * Returns: ---
- *
- * Use: Initializes an attribute iterator. The attributes are
- * returned by @key_nextattr@.
- */
-
-void key_mkattriter(key_attriter *i, key_file *f, key *k)
-{
- sym_mkiter(&i->i, &k->a);
-}
-
-/* --- @key_nextattr@ --- *
- *
- * Arguments: @key_attriter *i@ = pointer to attribute iterator
- * @const char **n, **v@ = pointers to name and value
- *
- * Returns: Zero if no attribute available, or nonzero if returned OK.
- *
- * Use: Returns the next attribute.
- */
-
-int key_nextattr(key_attriter *i, const char **n, const char **v)
-{
- key_attr *a = sym_next(&i->i);
- if (!a)
- return (0);
- *n = SYM_NAME(a);
- *v = a->p;
- return (1);
-}
-
-/*----- 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_type *t;
-
- if ((t = sym_find(&f->bytype, type, -1, 0, 0)) == 0)
- return (0);
- for (k = t->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_DELETED(t, k->del))
- return (0);
- return (k);
- }
- }
- return (0);
-}
-
-/*----- Attributes --------------------------------------------------------*/
-
-/* --- @key_getattr@ --- *
- *
- * Arguments: @key_file *f@ = pointer to file
- * @key *k@ = pointer to key
- * @const char *n@ = pointer to attribute name
- *
- * Returns: Pointer to attribute value, or null if not found.
- *
- * Use: Returns the value of a key attribute.
- */
-
-const char *key_getattr(key_file *f, key *k, const char *n)
-{
- key_attr *a;
- if ((a = sym_find(&k->a, n, -1, 0, 0)) == 0)
- return (0);
- return (a->p);
-}
-
-/* --- @key_putattr@ --- *
- *
- * Arguments: @key_file *f@ = pointer to file
- * @key *k@ = pointer to key
- * @const char *n@ = pointer to attribute name
- * @const char *v@ = pointer to attribute value or null
- *
- * Returns: ---
- *
- * Use: Inserts an attribute on a key. If an attribute with the same
- * name already exists, it is deleted. Setting a null value
- * removes the attribute.
- */
-
-void key_putattr(key_file *f, key *k, const char *n, const char *v)
-{
- key_attr *a;
- unsigned found;
-
- KEY_WRITE(f, key_putattr, NOTHING);
-
- if (v) {
- a = sym_find(&k->a, n, -1, sizeof(*a), &found);
- if (found)
- free(a->p);
- a->p = xstrdup(v);
- } else if ((a = sym_find(&k->a, n, -1, 0, 0)) != 0) {
- free(a->p);
- sym_remove(&k->a, a);
- }
-
- KEY_MODIFY(f);
-}
-
-/* --- @key_setcomment@ --- *
- *
- * Arguments: @key_file *f@ = pointer to key file block
- * @key *k@ = pointer to key block
- * @const char *c@ = pointer to comment to set, or zero
- *
- * Returns: ---
- *
- * Use: Replaces the key's current comment with a new one.
- */
-
-void key_setcomment(key_file *f, key *k, const char *c)
-{
- KEY_WRITE(f, key_setcomment, NOTHING);
- if (key_chkcomment(c))
- return;
- if (k->c)
- free(k->c);
- if (c)
- k->c = xstrdup(c);
- else
- k->c = 0;
- KEY_MODIFY(f);
-}
-
-/*----- Low-level file I/O ------------------------------------------------*/
-
-/* --- @key_merge@ --- *
- *
- * Arguments: @key_file *f@ = pointer to file structure
- * @const char *file@ = name of file (for error messages)
- * @FILE *fp@ = file handle to read from
- *
- * Returns: ---
- *
- * Use: Reads keys from a file, and inserts them into the file.
- */
-
-void key_merge(key_file *f, const char *file, FILE *fp)
-{
- int line = 0;
- dstr l = DSTR_INIT;
- dstr d = DSTR_INIT;
- dstr n = DSTR_INIT, v = DSTR_INIT;
-
- KEY_WRITE(f, key_merge, NOTHING);
-
- for (; dstr_putline(&l, fp) != EOF; DRESET(&l)) {
- char *vf[6];
- char *p = l.buf;
- key *k;
-
- /* --- Skip blank lines and comments --- *
- *
- * Quite what they're doing in what ought to be an automatically-
- * maintained file I don't know.
- */
-
- line++;
- while (isspace((unsigned char)*p))
- p++;
- if (!*p || *p == '#')
- continue;
-
- /* --- Break the line into fields --- *
- *
- * There are currently six fields of interest:
- *
- * * The key's type tag.
- * * The actual key data itself.
- * * The key expiry time.
- * * The key deletion time.
- * * The attributes field.
- * * Any further comments.
- *
- * All but the last field can contain no spaces.
- */
-
- {
- int n = str_split(p, vf, 5, &vf[5]);
- if (n < 4) {
- moan("key file `%s', line %i: too few fields", file, line);
- continue;
- }
- }
-
- /* --- Decode various bits and insert the key --- */
-
- {
- base64_ctx b;
- time_t exp, del;
-
- base64_init(&b);
- base64_decode(&b, vf[1], strlen(vf[1]), &d);
- base64_decode(&b, 0, 0, &d);
-
- exp = (time_t)atol(vf[2]);
- del = (time_t)atol(vf[3]);
-
- if ((k = insert(f, vf[0], d.buf, d.len, exp, del)) == 0)
- continue;
- DRESET(&d);
- }
-
- /* --- Parse up the attributes, if specified --- */
-
- if (vf[4]) {
- url_dctx uc;
- for (url_initdec(&uc, vf[4]); url_dec(&uc, &n, &v); ) {
- key_putattr(f, k, n.buf, v.buf);
- DRESET(&n); DRESET(&v);
- }
- }
-
- /* --- Insert the comment --- */
-
- if (vf[5])
- k->c = xstrdup(vf[5]);
- }
-
- /* --- Extensive tidying up now required --- */
-
- dstr_destroy(&l);
- dstr_destroy(&d);
- dstr_destroy(&n);
- dstr_destroy(&v);
- KEY_MODIFY(f);
-}
-
-/* --- @key_extract@ --- *
- *
- * Arguments: @key_file *f@ = pointer to file structure
- * @key *k@ = key to extract
- * @FILE *fp@ = file to write on
- *
- * Returns: Zero if OK, EOF on error.
- *
- * Use: Extracts a key to an ouptut file.
- */
-
-int key_extract(key_file *f, key *k, FILE *fp)
-{
- dstr d = DSTR_INIT;
-
- /* --- Encode the key and write the easy stuff --- */
-
- {
- base64_ctx b;
- base64_init(&b);
- b.indent = "";
- base64_encode(&b, k->k, k->ksz, &d);
- base64_encode(&b, 0, 0, &d);
- DPUTZ(&d);
- fprintf(fp, "%s %s %li %li ",
- k->type, d.buf, (long)k->exp, (long)k->del);
- DRESET(&d);
- }
-
- /* --- Output the attributes --- */
-
- {
- int none = 1;
- sym_iter i;
- key_attr *a;
- url_ectx uc;
-
- url_initenc(&uc);
- for (sym_mkiter(&i, &k->a); (a = sym_next(&i)) != 0; ) {
- none = 0;
- url_enc(&uc, &d, SYM_NAME(a), a->p);
- }
- if (none)
- DPUTS(&d, "-");
- DWRITE(&d, fp);
- }
-
- dstr_destroy(&d);
- if (k->c) {
- putc(' ', fp);
- fputs(k->c, fp);
- }
- putc('\n', fp);
- return (ferror(fp) ? EOF : 0);
-}
-
-/* --- @fdcopy@ --- *
- *
- * Arguments: @int source@ = source file descriptor
- * @int dest@ = destination file descriptor
- *
- * Returns: Zero if OK, nonzero otherwise.
- *
- * Use: Copies data from one file descriptor to another.
- */
-
-static int fdcopy(int source, int dest)
-{
- char buf[4096];
-
- if (lseek(source, 0, SEEK_SET) < 0||
- lseek(dest, 0, SEEK_SET) < 0 ||
- ftruncate(dest, 0) < 0)
- return (-1);
- for (;;) {
- int n = read(source, buf, sizeof(buf));
- if (n < 0)
- return (-1);
- else if (n == 0)
- break;
- else if (write(dest, buf, n) < 0)
- return (-1);
- }
- return (0);
-}
-
-/* --- @key_write@ --- *
- *
- * Arguments: @key_file *f@ = pointer to key file block
- *
- * Returns: A @KWRITE_@ code indicating how well it worked.
- *
- * Use: Writes a key file's data back to the actual file. This code
- * is extremely careful about error handling. It should usually
- * be able to back out somewhere sensible, but it can tell when
- * it's got itself into a real pickle and starts leaving well
- * alone.
- *
- * Callers, please make sure that you ring alarm bells when this
- * function returns @KWRITE_BROKEN@.
- */
-
-int key_write(key_file *f)
-{
- dstr n_older = DSTR_INIT, n_old = DSTR_INIT, n_new = DSTR_INIT;
- int rc = KWRITE_FAIL;
-
- if (!(f->f & KF_MODIFIED))
- return (KWRITE_OK);
-
- /* --- Write a new key file out --- *
- *
- * Check for an error after each key line. This ought to be enough.
- * Checking after each individual byte write and @fprintf@ isn't much fun.
- */
-
- dstr_putf(&n_new, "%s.new", f->name);
-
- {
- key *k;
- key_iter i;
- FILE *fp;
-
- if ((fp = fopen(n_new.buf, "w")) == 0)
- goto fail_open;
-
- for (key_mkiter(&i, f); (k = key_next(&i)) != 0; ) {
- if (key_extract(f, k, fp)) {
- fclose(fp);
- goto fail_write;
- }
- }
-
- if (fclose(fp))
- goto fail_write;
- }
-
- /* --- Set up the other filenames --- */
-
- dstr_putf(&n_older, "%s.older", f->name);
- dstr_putf(&n_old, "%s.old", f->name);
-
- /* --- Move the current backup on one --- *
- *
- * If the `older' file exists, then we're in need of attention.
- */
-
- {
- struct stat st;
- if (stat(n_older.buf, &st) == 0 || errno != ENOENT) {
- errno = EEXIST;
- rc = KWRITE_BROKEN;
- goto fail_shift;
- }
- if (rename(n_old.buf, n_older.buf) && errno != ENOENT)
- goto fail_shift;
- }
-
- /* --- Copy the current file to the backup --- */
-
- {
- int fd;
- if ((fd = open(n_old.buf, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0)
- goto fail_backup;
- if (fdcopy(f->fd, fd)) {
- close(fd);
- goto fail_backup;
- }
- if (close(fd))
- goto fail_backup;
- }
-
- /* --- Copy the newly created file to the current one --- *
- *
- * This is the dangerous bit.
- */
-
- {
- int fd;
- if ((fd = open(n_new.buf, O_RDONLY)) < 0)
- goto fail_update;
- if (fdcopy(fd, f->fd)) {
- close(fd);
- goto fail_update;
- }
- close(fd);
- if (fsync(f->fd))
- goto fail_update;
- }
-
- /* --- Clean up --- *
- *
- * Remove the `new' file and the `older' backup. Then we're done.
- */
-
- unlink(n_new.buf);
- unlink(n_older.buf);
- return (KWRITE_OK);
-
- /* --- Failure while writing the new key file --- *
- *
- * I need to copy the backup back. If that fails then I'm really stuffed.
- * If not, then I might as well try to get the backups sorted back out
- * again.
- */
-
-fail_update:
- {
- int fd;
- int e = errno;
-
- if ((fd = open(n_old.buf, O_RDONLY)) < 0)
- rc = KWRITE_BROKEN;
- else if (fdcopy(fd, f->fd)) {
- close(fd);
- rc = KWRITE_BROKEN;
- } else {
- close(fd);
- if (fsync(f->fd))
- rc = KWRITE_BROKEN;
- }
-
- errno = e;
- if (rc == KWRITE_BROKEN)
- goto fail_shift;
- }
- /* Now drop through */
-
- /* --- Failure while writing the new backup --- *
- *
- * The new backup isn't any use. Try to recover the old one.
- */
-
-fail_backup:
- {
- int e = errno;
- unlink(n_old.buf);
- if (rename(n_older.buf, n_old.buf) && errno != ENOENT)
- rc = KWRITE_BROKEN;
- errno = e;
- }
- /* Now drop through */
-
- /* --- Failure while demoting the current backup --- *
- *
- * Leave the completed output file there for the operator in case he wants
- * to clean up.
- */
-
-fail_shift:
- dstr_destroy(&n_new);
- dstr_destroy(&n_old);
- dstr_destroy(&n_older);
- return (rc);
-
-/* --- Failure during write of new data --- *
- *
- * Clean up the new file and return. These errors can never cause
- * breakage.
- */
-
-fail_write:
- unlink(n_new.buf);
- fail_open:
- dstr_destroy(&n_new);
- return (rc);
-}
-
-/*----- Opening and closing files -----------------------------------------*/
-
-/* --- @key_open@ --- *
- *
- * Arguments: @key_file *f@ = pointer to file structure to initialize
- * @const char *file@ = pointer to the file name
- * @int how@ = opening options (@KOPEN_*@).
- *
- * Returns: Zero if it worked, nonzero otherwise.
- *
- * Use: Opens a key file, reads its contents, and stores them in a
- * structure. The file is locked appropriately until closed
- * using @key_close@. On an error, everything is cleared away
- * tidily. If the file is opened with @KOPEN_WRITE@, it's
- * created if necessary, with read and write permissions for its
- * owner only.
- */
-
-int key_open(key_file *f, const char *file, int how)
-{
- FILE *fp;
-
- /* --- Trivial bits of initialization --- */
-
- f->f = 0;
- f->name = xstrdup(file);
-
- /* --- Open the file and get the lock --- */
-
- {
- int of, lf;
- const char *ff;
- int fd;
-
- /* --- Lots of things depend on whether we're writing --- */
-
- switch (how) {
- case KOPEN_READ:
- of = O_RDONLY;
- lf = LOCK_NONEXCL;
- ff = "r";
- break;
- case KOPEN_WRITE:
- of = O_RDWR | O_CREAT;
- lf = LOCK_EXCL;
- ff = "r+";
- break;
- default:
- errno = EINVAL;
- return (-1);
- }
-
- if ((fd = open(file, of, 0600)) < 0)
- return (-1);
- if (fcntl(fd, F_SETFD, 1) < 0 ||
- lock_file(fd, lf) < 0 || (fp = fdopen(fd, ff)) == 0) {
- close(fd);
- return (-1);
- }
- f->fd = fd;
- }
-
- /* --- Read the file of keys into the table --- */
-
- hash_create(&f->byid, 64);
- f->idload = KEY_LOAD(64);
- sym_create(&f->bytype);
- f->f |= KF_WRITE;
- key_merge(f, file, fp);
- if (how == KOPEN_READ)
- f->f &= ~(KF_WRITE | KF_MODIFIED);
- else
- f->f &= ~KF_MODIFIED;
-
- /* --- Close the file if only needed for reading --- */
-
- if (how == KOPEN_READ) {
- f->fp = 0;
- fclose(fp);
- } else
- f->fp = fp;
-
- return (0);
-}
-
-/* --- @key_close@ --- *
- *
- * Arguments: @key_file *f@ = pointer to key file block
- *
- * Returns: A @KWRITE_@ code indicating how it went.
- *
- * Use: Frees all the key data, writes any changes. Make sure that
- * all hell breaks loose if this returns @KWRITE_BROKEN@.
- */
-
-int key_close(key_file *f)
-{
- int e;
- hash_base *b;
- hash_iter i;
-
- if ((e = key_write(f)) != KWRITE_OK)
- return (e);
-
- /* --- Free all the individual keys --- */
-
- for (hash_mkiter(&i, &f->byid); (b = hash_next(&i)) != 0; ) {
- sym_iter j;
- key_attr *a;
- key *k = (key *)b;
-
- sub_free(k->k, k->ksz);
- free(k->type);
- if (k->c)
- free(k->c);
- for (sym_mkiter(&j, &k->a); (a = sym_next(&j)) != 0; )
- free(a->p);
- sym_destroy(&k->a);
- DESTROY(k);
- }
- hash_destroy(&f->byid);
- sym_destroy(&f->bytype);
-
- if (f->fp)
- fclose(f->fp);
- free(f->name);
- return (KWRITE_OK);
-}
-
-/*----- Miscellaneous functions -------------------------------------------*/
-
-/* --- @key_new@ ---
- *
- * Arguments: @key_file *f@ = pointer to key file
- * @const char *type@ = the type of this key
- * @const void *k@ = pointer to key data
- * @size_t ksz@ = size of key data
- * @time_t exp@ = when the key expires
- * @const char *c@ = textual comment to attach
- *
- * Returns: Key block containing new data, or null if it couldn't be
- * done.
- *
- * Use: Attaches a new key to a key file. You must have a writable
- * key file for this to work.
- *
- * The type is a key type string. This interface doesn't care
- * about how type strings are formatted: it just treats them as
- * opaque gobs of text. Clients are advised to choose some
- * standard for representing key types, though.
- *
- * The key can be any old binary mess.
- *
- * The expiry time should either be a time in the future, or the
- * magic value @KEXP_FOREVER@ which means `never expire this
- * key'. Be careful with `forever' keys. If I were you, I'd
- * use a more sophisticated key management system than this for
- * them.
- *
- * The comment can be any old text not containing newlines or
- * nulls. This interface doesn't impose any length restrictions
- * on comment lengths.
- */
-
-key *key_new(key_file *f, const char *type,
- const void *k, size_t ksz,
- time_t exp, const char *c)
-{
- key *kk;
- time_t t = time(0);
-
- KEY_WRITE(f, key_new, 0);
-
- if (KEY_EXPIRED(t, exp) ||
- key_chktype(type) || key_chkcomment(c) ||
- (kk = insert(f, type, k, ksz, exp, KEXP_UNUSED)) == 0)
- return (0);
- if (c)
- kk->c = xstrdup(c);
- KEY_MODIFY(f);
- return (kk);
-}
-
-/* --- @key_delete@ --- *
- *
- * Arguments: @key_file *f@ = pointer to file block
- * @key *k@ = key to delete
- *
- * Returns: ---
- *
- * 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.)
- */
-
-void key_delete(key_file *f, key *k)
-{
- KEY_WRITE(f, key_delete, NOTHING);
- k->exp = KEXP_EXPIRE;
- k->del = KEXP_UNUSED;
- KEY_MODIFY(f);
-}
-
-/* --- @key_expire@ --- *
- *
- * Arguments: @key_file *f@ = pointer to file block
- * @key *k@ = pointer to key block
- *
- * Returns: ---
- *
- * 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.
- */
-
-void key_expire(key_file *f, key *k)
-{
- KEY_WRITE(f, key_expire, NOTHING);
- k->exp = KEXP_EXPIRE;
- if (k->del == KEXP_FOREVER)
- k->del = KEXP_UNUSED;
- KEY_MODIFY(f);
-}
-
-/* --- @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, key_used, -1);
- if (t == KEXP_FOREVER) {
- if (k->exp != KEXP_FOREVER) {
- errno = EINVAL;
- return (-1);
- }
- } else if (k->del >= t)
- return (0);
-
- k->del = t;
- KEY_MODIFY(f);
- return (0);
-}
-
-/*----- That's all, folks -------------------------------------------------*/
/* -*-c-*-
*
- * $Id: key.h,v 1.2 1999/12/10 23:29:48 mdw Exp $
+ * $Id: key.h,v 1.3 1999/12/22 15:47:48 mdw Exp $
*
* Simple key management
*
- * (c) 1999 Mark Wooding
+ * (c) 1999 Straylight/Edgeware
*/
/*----- Licensing notice --------------------------------------------------*
/*----- Revision history --------------------------------------------------*
*
* $Log: key.h,v $
+ * Revision 1.3 1999/12/22 15:47:48 mdw
+ * Major key-management revision.
+ *
* Revision 1.2 1999/12/10 23:29:48 mdw
* Change header file guard names.
*
#include <time.h>
#include <mLib/bits.h>
+#include <mLib/dstr.h>
#include <mLib/hash.h>
#include <mLib/sym.h>
+#ifndef CATACOMB_MP_H
+# include "mp.h"
+#endif
+
/*----- Data structures ---------------------------------------------------*/
/* --- Key attributes --- *
char *p; /* Pointer to attribute value */
} key_attr;
+/* --- Key data structure --- */
+
+typedef struct key_data {
+ unsigned e; /* Encoding type for key data */
+ union {
+
+ /* --- Plain binary key data --- *
+ *
+ * Also used for encrypted key types.
+ */
+
+ struct {
+ octet *k; /* Actual key data */
+ size_t sz; /* Size of the key data */
+ } k; /* Plain binary key */
+
+ /* --- Multiprecision integer keys --- */
+
+ mp *m; /* Multiprecision integer */
+
+ /* --- Structured key data --- */
+
+ sym_table s; /* Structured key data */
+ } u;
+} key_data;
+
+typedef struct key_struct {
+ sym_base _b;
+ key_data k;
+} key_struct;
+
+/* --- Key binary encoding --- *
+ *
+ * The binary encoding consists of a header containing a 16-bit encoding type
+ * and a 16-bit length, followed immediately by the key data, followed by
+ * between zero and three zero bytes to make the total length a multiple of
+ * four. The format of the following data depends on the encoding type:
+ *
+ * @KENC_BINARY@ Binary data.
+ *
+ * @KENC_MP@ Octet array interpreted in big-endian byte order.
+ *
+ * @KENC_STRUCT@ An array of pairs, each containing a string (8-bit
+ * length followed by data and zero-padding to 4-byte
+ * boundary) and key binary encodings.
+ *
+ * @KENC_ENCRYPT@ Binary data, format
+ */
+
+/* --- Key encoding methods and other flags--- */
+
+enum {
+
+ /* --- Bottom two bits are the encoding type --- */
+
+ KF_ENCMASK = 0x03, /* Encoding mask */
+ KENC_BINARY = 0x00, /* Plain binary key (@k@) */
+ KENC_MP = 0x01, /* Multiprecision integer (@i@) */
+ KENC_STRUCT = 0x02, /* Structured key data (@s@) */
+ KENC_ENCRYPT = 0x03, /* Encrypted key type (@k@) */
+
+ /* --- Key category bits --- */
+
+ KF_CATMASK = 0x0c, /* Category mask */
+ KCAT_SYMM = 0x00, /* Symmetric encryption key */
+ KCAT_PRIV = 0x04, /* Private (asymmetric) key */
+ KCAT_PUB = 0x08, /* Public (asymmetric) key */
+ KCAT_SHARE = 0x0c, /* Shared (asymmetric) key */
+ KF_NONSECRET = 0x08, /* Bit flag for non-secret keys */
+
+ /* --- Other flags --- */
+
+ KF_BURN = 0x10, /* Burn key after use */
+
+ /* --- Tag end --- */
+
+ KENC_MAX /* Dummy limit constant */
+};
+
+/* --- Key flag filtering --- */
+
+typedef struct key_filter {
+ unsigned f;
+ unsigned m;
+} key_filter;
+
+/* --- Matching aginst key selection --- */
+
+#define KEY_MATCH(kd, kf) \
+ (!(kf) || \
+ ((kd)->e & KF_ENCMASK) == KENC_STRUCT || \
+ ((kd)->e & (kf)->m) == (kf)->f)
+
/* --- Main key structure --- *
*
* Each key is stored in two symbol tables, one indexed by keyid, and the
*/
typedef struct key {
+
+ /* --- Hashtable management --- */
+
hash_base _b; /* Symbol table data */
struct key *next; /* Next key of the same type */
+
+ /* --- Basic key attributes --- */
+
uint32 id; /* Key id used to name it */
+ char *tag; /* Textual tag name */
char *type; /* Textual key type */
- void *k; /* Actual key data */
- size_t ksz; /* Size of the key data */
time_t exp, del; /* Expiry times for keys */
+
+ /* --- The key data itself --- */
+
+ key_data k; /* The actual key data */
+
+ /* --- Other attributes and commentary --- */
+
sym_table a; /* Hashtable of key attributes */
char *c; /* Any additional comments */
} key;
/* --- The keys-by-type entries --- */
-typedef struct key_type {
+typedef struct key_ref {
sym_base _b; /* Symbol table data */
key *k; /* Pointer to first key in list */
-} key_type;
+} key_ref;
/* --- A key file --- */
typedef struct key_file {
FILE *fp; /* File pointer open on file */
- int fd; /* File descriptor open on file */
char *name; /* Filename used to create it */
unsigned f; /* Various useful flags */
hash_table byid; /* Table of keys by keyid */
sym_table bytype; /* Table of keys by type */
+ sym_table bytag; /* Table of keys by tag */
size_t idload; /* Loading on id table */
} key_file;
/* --- Various other magic numbers --- */
-#define KEXP_UNUSED ((time_t)0) /* Key has never been used */
#define KEXP_FOREVER ((time_t)-1) /* Never expire this key */
#define KEXP_EXPIRE ((time_t)-2) /* Expire this key when unused */
-/* --- Write attempt codes --- */
+/* --- Key error codes --- */
+
+enum {
+ KERR_OK, /* No error */
+ KERR_BADTAG = -1, /* Malformed tag string */
+ KERR_BADTYPE = -2, /* Malformed type string */
+ KERR_BADCOMMENT = -3, /* Malformed comment string */
+ KERR_DUPID = -4, /* Duplicate keyid */
+ KERR_DUPTAG = -5, /* Duplicate key tag string */
+ KERR_READONLY = -6, /* Key file is read-only */
+ KERR_WILLEXPIRE = -7, /* Key will eventually expire */
+ KERR_EXPIRED = -8, /* Key has already expired */
+ KERR_BADFLAGS = -9, /* Error in flags string */
+ KERR_MAX /* Largest possible error */
+};
+
+/* --- Write error codes --- */
enum {
KWRITE_OK, /* Everything went fine */
KWRITE_BROKEN = -2 /* Key ring needs manual fixing */
};
+/* --- Error reporting functions for @key_merge@ and @key_open@ --- */
+
+typedef void key_reporter(const char */*file*/, int /*line*/,
+ const char */*err*/, void */*p*/);
+
/* --- Macros for testing expiry --- */
#define KEY_EXPIRED(now, exp) \
((exp) == KEXP_EXPIRE || ((exp) != KEXP_FOREVER && (exp) < (now)))
-#define KEY_DELETED(now, del) ((del) == KEXP_FOREVER || (del) < (now))
+/*----- Key data manipulation ---------------------------------------------*/
+
+/* --- @key_destroy@ --- *
+ *
+ * Arguments: @key_data *k@ = pointer to key data to destroy
+ *
+ * Returns: ---
+ *
+ * Use: Destroys a lump of key data.
+ */
-/*----- Functions provided ------------------------------------------------*/
+extern void key_destroy(key_data */*k*/);
-/* --- @key_chktype@ --- *
+/* --- @key_readflags@ --- *
*
- * Arguments: @const char *type@ = pointer to a type string
+ * Arguments: @const char *p@ = pointer to string to read
+ * @char **pp@ = where to store the end pointer
+ * @unsigned *ff@ = where to store the flags
+ * @unsigned *mm@ = where to store the mask
*
- * Returns: Zero if OK, -1 on error.
+ * Returns: Zero if all went well, nonzero if there was an error.
*
- * Use: Checks whether a type string is OK.
+ * Use: Reads a flag string.
*/
-extern int key_chktype(const char */*type*/);
+extern int key_readflags(const char */*p*/, char **/*pp*/,
+ unsigned */*ff*/, unsigned */*mm*/);
-/* --- @key_chkcomment@ --- *
+/* --- @key_writeflags@ --- *
*
- * Arguments: @const char *comment@ = pointer to a comment string
+ * Arguments: @unsigned f@ = flags to write
+ * @dstr *d@ = pointer to destination string
*
- * Returns: Zero if OK, -1 on error.
+ * Returns: ---
*
- * Use: Checks whether a comment string is OK.
+ * Use: Emits a flags word as a string representation.
*/
-extern int key_chkcomment(const char */*c*/);
+extern void key_writeflags(unsigned /*f*/, dstr */*d*/);
-/* --- @key_mkiter@ --- *
+/* --- @key_binary@ --- *
*
- * Arguments: @key_iter *i@ = pointer to iterator object
- * @key_file *f@ = pointer to file structure
+ * Arguments: @key_data *k@ = pointer to key data block
+ * @const void *p@ = pointer to key data
+ * @size_t sz@ = size of the key data
*
* Returns: ---
*
- * Use: Initializes a key iterator. The keys are returned by
- * @key_next@.
+ * Use: Sets a binary key in a key data block.
*/
-extern void key_mkiter(key_iter */*i*/, key_file */*f*/);
+extern void key_binary(key_data */*k*/, const void */*p*/, size_t /*sz*/);
-/* --- @key_next@ --- *
+/* --- @key_encrypted@ --- *
*
- * Arguments: @key_iter *i@ = pointer to iterator object
+ * Arguments: @key_data *k@ = pointer to key data block
+ * @const void *p@ = pointer to key data
+ * @size_t sz@ = size of the key data
*
- * Returns: Pointer to next key, or null.
+ * Returns: ---
*
- * Use: Returns the next key in some arbitrary sequence.
+ * Use: Sets an encrypted key in a key data block.
*/
-extern key *key_next(key_iter */*i*/);
+extern void key_encrypted(key_data */*k*/, const void */*p*/, size_t /*sz*/);
-/* --- @key_mkattriter@ --- *
+/* --- @key_mp@ --- *
*
- * Arguments: @key_attriter *i@ = pointer to attribute iterator
- * @key_file *f@ = pointer to key file
- * @key *k@ = pointer to key
+ * Arguments: @key_data *k@ = pointer to key data block
+ * @mp *m@ = pointer to the value to set
*
* Returns: ---
*
- * Use: Initializes an attribute iterator. The attributes are
- * returned by @key_nextattr@.
+ * Use: Sets a multiprecision integer key in a key block.
*/
-extern void key_mkattriter(key_attriter */*i*/, key_file */*f*/, key */*k*/);
+extern void key_mp(key_data */*k*/, mp */*m*/);
-/* --- @key_nextattr@ --- *
+/* --- @key_structure@ --- *
*
- * Arguments: @key_attriter *i@ = pointer to attribute iterator
- * @const char **n, **v@ = pointers to name and value
+ * Arguments: @key_data *k@ = pointer to key data block
*
- * Returns: Zero if no attribute available, or nonzero if returned OK.
+ * Returns: ---
*
- * Use: Returns the next attribute.
+ * Use: Initializes a structured key type.
*/
-extern int key_nextattr(key_attriter */*i*/,
- const char **/*n*/, const char **/*v*/);
+extern void key_structure(key_data */*k*/);
-/* --- @key_bytype@ --- *
+/* --- @key_structfind@ --- *
*
- * Arguments: @key_file *f@ = key file we want a key from
- * @const char *type@ = type string for desired key
+ * Arguments: @key_data *k@ = pointer to key data block
+ * @const char *tag@ = pointer to tag string
*
- * Returns: Pointer to the best key to use, or null.
+ * Returns: Pointer to key data block, 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.
+ * Use: Looks up the tag in a structured key.
*/
-extern key *key_bytype(key_file */*f*/, const char */*type*/);
+extern key_data *key_structfind(key_data */*k*/, const char */*tag*/);
-/* --- @key_byid@ --- *
+/* --- @key_structcreate@ --- *
*
- * Arguments: @key_file *f@ = key file to find a key from
- * @uint32 id@ = id to look for
+ * Arguments: @key_data *k@ = pointer to key data block
+ * @const char *tag@ = pointer to tag string
*
- * Returns: Key with matching id.
+ * Returns: Pointer to newly created key data.
*
- * Use: Returns a key given its id. This function will return an
- * expired key, but not a deleted one.
+ * Use: Creates a new uninitialized subkey.
*/
-extern key *key_byid(key_file */*f*/, uint32 /*id*/);
+extern key_data *key_structcreate(key_data */*k*/, const char */*tag*/);
-/* --- @key_getattr@ --- *
+/* --- @key_match@ --- *
*
- * Arguments: @key_file *f@ = pointer to file
- * @key *k@ = pointer to key
- * @const char *n@ = pointer to attribute name
+ * Arguments: @key_data *k@ = pointer to key data block
+ * @const key_filter *kf@ = pointer to filter block
*
- * Returns: Pointer to attribute value, or null if not found.
+ * Returns: Nonzero if the key matches the filter.
*
- * Use: Returns the value of a key attribute.
+ * Use: Checks whether a key matches a filter.
*/
-extern const char *key_getattr(key_file */*f*/, key */*k*/,
- const char */*n*/);
+extern int key_match(key_data */*k*/, const key_filter */*kf*/);
-/* --- @key_putattr@ --- *
+/* --- @key_do@ --- *
*
- * Arguments: @key_file *f@ = pointer to file
- * @key *k@ = pointer to key
- * @const char *n@ = pointer to attribute name
- * @const char *v@ = pointer to attribute value
+ * Arguments: @key_data *k@ = pointer to key data block
+ * @const key_filter *kf@ = pointer to filter block
+ * @dstr *d@ = pointer to base string
+ * @int (*func)(key_data *kd, dstr *d, void *p@ = function
+ * @void *p@ = argument to function
*
- * Returns: ---
+ * Returns: Nonzero return code from function, or zero.
*
- * Use: Inserts an attribute on a key. If an attribute with the same
- * name already exists, it is deleted.
+ * Use: Runs a function over all the leaves of a key.
*/
-extern void key_putattr(key_file */*f*/, key */*k*/,
- const char */*n*/, const char */*v*/);
+extern int key_do(key_data */*k*/, const key_filter */*kf*/, dstr */*d*/,
+ int (*/*func*/)(key_data */*kd*/,
+ dstr */*d*/, void */*p*/),
+ void */*p*/);
-/* --- @key_setcomment@ --- *
+/* --- @key_copy@ --- *
*
- * Arguments: @key_file *f@ = pointer to key file block
- * @key *k@ = pointer to key block
- * @const char *c@ = pointer to comment to set, or zero
+ * Arguments: @key_data *kd@ = pointer to destination data block
+ * @key_data *k@ = pointer to source data block
+ * @const key_filter *kf@ = pointer to filter block
*
- * Returns: ---
+ * Returns: Nonzero if an item was actually copied.
*
- * Use: Replaces the key's current comment with a new one.
+ * Use: Copies a chunk of key data from one place to another.
+ */
+
+extern int key_copy(key_data */*kd*/, key_data */*k*/,
+ const key_filter */*kf*/);
+
+/* --- @key_read@ --- *
+ *
+ * Arguments: @const char *p@ = pointer to textual key representation
+ * @key_data *k@ = pointer to output block for key data
+ * @char **pp@ = where to store the end pointer
+ *
+ * Returns: Zero if all went well, nonzero if there was a problem.
+ *
+ * Use: Parses a textual key description.
+ */
+
+extern int key_read(const char */*p*/, key_data */*k*/, char **/*pp*/);
+
+/* --- @key_write@ --- *
+ *
+ * Arguments: @key_data *k@ = pointer to key data
+ * @dstr *d@ = destination string to write on
+ * @const key_filter *kf@ = pointer to key selection block
+ *
+ * Returns: Nonzero if any items were actually written.
+ *
+ * Use: Writes a key in a textual encoding.
+ */
+
+extern int key_write(key_data */*k*/, dstr */*d*/,
+ const key_filter */*kf*/);
+
+/* --- @key_decode@ --- *
+ *
+ * Arguments: @const void *p@ = pointer to buffer to read
+ * @size_t sz@ = size of the buffer
+ * @key_data *k@ = pointer to key data block to write to
+ *
+ * Returns: Zero if everything worked, nonzero otherwise.
+ *
+ * Use: Decodes a binary representation of a key.
+ */
+
+extern int key_decode(const void */*p*/, size_t /*sz*/, key_data */*k*/);
+
+/* --- @key_encode@ --- *
+ *
+ * Arguments: @key_data *k@ = pointer to key data block
+ * @dstr *d@ = pointer to destination string
+ * @const key_filter *kf@ = pointer to key selection block
+ *
+ * Returns: Nonzero if any items were actually written.
+ *
+ * Use: Encodes a key block as binary data.
+ */
+
+extern int key_encode(key_data */*k*/, dstr */*d*/,
+ const key_filter */*kf*/);
+
+/* --- @key_plock@ --- *
+ *
+ * Arguments: @const char *tag@ = tag to use for passphrase
+ * @key_data *k@ = source key data block
+ * @key_data *kt@ = target key data block
+ *
+ * Returns: Zero if successful, nonzero if there was a problem.
+ *
+ * Use: Locks a key by encrypting it with a passphrase.
+ */
+
+extern int key_plock(const char */*tag*/, key_data */*k*/, key_data */*kt*/);
+
+/* --- @key_punlock@ --- *
+ *
+ * Arguments: @const char *tag@ = tag to use for passphrase
+ * @key_data *k@ = source key data block
+ * @key_data *kt@ = target key data block
+ *
+ * Returns: Zero if it worked, nonzero if it didn't.
+ *
+ * Use: Unlocks a passphrase-locked key.
*/
-extern void key_setcomment(key_file */*f*/, key */*k*/, const char */*c*/);
+extern int key_punlock(const char */*tag*/,
+ key_data */*k*/, key_data */*kt*/);
+
+/*----- Reading and writing keys and files --------------------------------*/
/* --- @key_merge@ --- *
*
* Arguments: @key_file *f@ = pointer to file structure
* @const char *file@ = name of file (for error messages)
* @FILE *fp@ = file handle to read from
+ * @key_reporter *rep@ = error reporting function
+ * @void *arg@ = argument for function
*
- * Returns: ---
+ * Returns: Error code (one of the @KERR@ constants).
*
* Use: Reads keys from a file, and inserts them into the file.
*/
-extern void key_merge(key_file */*f*/, const char */*file*/, FILE */*fp*/);
+extern int key_merge(key_file */*f*/, const char */*file*/, FILE */*fp*/,
+ key_reporter */*rep*/, void */*arg*/);
/* --- @key_extract@ --- *
*
* Arguments: @key_file *f@ = pointer to file structure
* @key *k@ = key to extract
* @FILE *fp@ = file to write on
+ * @const key_filter *kf@ = pointer to key selection block
*
* Returns: Zero if OK, EOF on error.
*
* Use: Extracts a key to an ouptut file.
*/
-extern int key_extract(key_file */*f*/, key */*k*/, FILE */*fp*/);
-
-/* --- @key_write@ --- *
- *
- * Arguments: @key_file *f@ = pointer to key file block
- *
- * Returns: A @KWRITE_@ code indicating how well it worked.
- *
- * Use: Writes a key file's data back to the actual file. This code
- * is extremely careful about error handling. It should usually
- * be able to back out somewhere sensible, but it can tell when
- * it's got itself into a real pickle and starts leaving well
- * alone.
- *
- * Callers, please make sure that you ring alarm bells when this
- * function returns @KWRITE_BROKEN@.
- */
-
-extern int key_write(key_file */*f*/);
+extern int key_extract(key_file */*f*/, key */*k*/, FILE */*fp*/,
+ const key_filter */*kf*/);
/* --- @key_open@ --- *
*
* Arguments: @key_file *f@ = pointer to file structure to initialize
* @const char *file@ = pointer to the file name
* @int how@ = opening options (@KOPEN_*@).
+ * @key_reporter *rep@ = error reporting function
+ * @void *arg@ = argument for function
*
* Returns: Zero if it worked, nonzero otherwise.
*
* owner only.
*/
-extern int key_open(key_file */*f*/, const char */*file*/, int /*how*/);
+extern int key_open(key_file */*f*/, const char */*file*/, int /*how*/,
+ key_reporter */*rep*/, void */*arg*/);
/* --- @key_close@ --- *
*
extern int key_close(key_file */*f*/);
+/* --- @key_save@ --- *
+ *
+ * Arguments: @key_file *f@ = pointer to key file block
+ *
+ * Returns: A @KWRITE_@ code indicating how well it worked.
+ *
+ * Use: Writes a key file's data back to the actual file. This code
+ * is extremely careful about error handling. It should usually
+ * be able to back out somewhere sensible, but it can tell when
+ * it's got itself into a real pickle and starts leaving well
+ * alone.
+ *
+ * Callers, please make sure that you ring alarm bells when this
+ * function returns @KWRITE_BROKEN@.
+ */
+
+extern int key_save(key_file */*f*/);
+
+/* --- @key_lockfile@ --- *
+ *
+ * Arguments: @key_file *f@ = pointer to file structure to initialize
+ * @const char *file@ = pointer to the file name
+ * @int how@ = opening options (@KOPEN_*@).
+ *
+ * Returns: Zero if it worked, nonzero otherwise.
+ *
+ * Use: Opens a keyfile and stores the information needed for
+ * continued access in the structure.
+ *
+ * If the file is opened with @KOPEN_WRITE@, it's created if
+ * necessary with read and write permissions for owner only, and
+ * locked for update while it's open.
+ *
+ * This is a system-dependent routine, and only really intended
+ * for the private use of @key_open@.
+ */
+
+extern int key_lockfile(key_file */*f*/, const char */*file*/, int /*how*/);
+
+/*----- Creating and manipulating keys ------------------------------------*/
+
/* --- @key_new@ ---
*
* Arguments: @key_file *f@ = pointer to key file
+ * @uint32 id@ = keyid to set
* @const char *type@ = the type of this key
- * @const void *k@ = pointer to key data
- * @size_t ksz@ = size of key data
* @time_t exp@ = when the key expires
- * @const char *c@ = textual comment to attach
+ * @int *err@ = where to store the error condition
*
* Returns: Key block containing new data, or null if it couldn't be
* done.
* opaque gobs of text. Clients are advised to choose some
* standard for representing key types, though.
*
- * The key can be any old binary mess.
- *
* The expiry time should either be a time in the future, or the
* magic value @KEXP_FOREVER@ which means `never expire this
* key'. Be careful with `forever' keys. If I were you, I'd
* use a more sophisticated key management system than this for
* them.
*
- * The comment can be any old text not containing newlines or
- * nulls. This interface doesn't impose any length restrictions
- * on comment lengths.
+ * You have to set the actual key yourself.
*/
-extern key *key_new(key_file */*f*/, const char */*type*/,
- const void */*k*/, size_t /*ksz*/,
- time_t /*exp*/, const char */*c*/);
+extern key *key_new(key_file */*f*/, uint32 /*id*/, const char */*type*/,
+ time_t /*exp*/, int */*err*/);
/* --- @key_delete@ --- *
*
* Arguments: @key_file *f@ = pointer to file block
* @key *k@ = key to delete
*
- * Returns: ---
+ * 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,
* it's closed.)
*/
-extern void key_delete(key_file */*f*/, key */*k*/);
+extern int key_delete(key_file */*f*/, key */*k*/);
/* --- @key_expire@ --- *
*
* Arguments: @key_file *f@ = pointer to file block
* @key *k@ = pointer to key block
*
- * Returns: ---
+ * 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
* file must be writable.
*/
-extern void key_expire(key_file */*f*/, key */*k*/);
+extern int key_expire(key_file */*f*/, key */*k*/);
/* --- @key_used@ --- *
*
extern int key_used(key_file */*f*/, key */*k*/, time_t /*t*/);
+/*----- Setting and reading attributes ------------------------------------*/
+
+/* --- @key_chkident@ --- *
+ *
+ * Arguments: @const char *p@ = pointer to a type string
+ *
+ * Returns: Zero if OK, -1 on error.
+ *
+ * Use: Checks whether an identification component string is OK.
+ */
+
+extern int key_chkident(const char */*p*/);
+
+/* --- @key_chkcomment@ --- *
+ *
+ * Arguments: @const char *p@ = pointer to a comment string
+ *
+ * Returns: Zero if OK, -1 on error.
+ *
+ * Use: Checks whether a comment string is OK.
+ */
+
+extern int key_chkcomment(const char */*p*/);
+
+/* --- @key_setcomment@ --- *
+ *
+ * Arguments: @key_file *f@ = pointer to key file block
+ * @key *k@ = pointer to key block
+ * @const char *c@ = pointer to comment to set, or zero
+ *
+ * Returns: Error code (one of the @KERR@ constants).
+ *
+ * Use: Replaces the key's current comment with a new one.
+ */
+
+extern int key_setcomment(key_file */*f*/, key */*k*/, const char */*c*/);
+
+/* --- @key_settag@ --- *
+ *
+ * Arguments: @key_file *f@ = pointer to key file block
+ * @key *k@ = pointer to key block
+ * @const char *tag@ = pointer to comment to set, or zero
+ *
+ * Returns: Error code (one of the @KERR@ constants).
+ *
+ * Use: Replaces the key's current tag with a new one.
+ */
+
+extern int key_settag(key_file */*f*/, key */*k*/, const char */*tag*/);
+
+/* --- @key_fulltag@ --- *
+ *
+ * Arguments: @key *k@ = pointer to key
+ * @dstr *d@ = pointer to destination string
+ *
+ * Returns: ---
+ *
+ * Use: Emits the key's full tag, which has the form
+ * `ID:TYPE[:TAG]'. This is used in the textual file format,
+ * and to identify passphrases for locked keys.
+ */
+
+extern void key_fulltag(key */*k*/, dstr */*d*/);
+
+/* --- @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.
+ */
+
+extern int key_qtag(key_file */*f*/, const char */*tag*/,
+ dstr */*d*/, key **/*k*/, key_data **/*kd*/);
+
+/* --- @key_getattr@ --- *
+ *
+ * Arguments: @key_file *f@ = pointer to file
+ * @key *k@ = pointer to key
+ * @const char *n@ = pointer to attribute name
+ *
+ * Returns: Pointer to attribute value, or null if not found.
+ *
+ * Use: Returns the value of a key attribute.
+ */
+
+extern const char *key_getattr(key_file */*f*/, key */*k*/,
+ const char */*n*/);
+
+/* --- @key_putattr@ --- *
+ *
+ * Arguments: @key_file *f@ = pointer to file
+ * @key *k@ = pointer to key
+ * @const char *n@ = pointer to attribute name
+ * @const char *v@ = pointer to attribute value or null
+ *
+ * Returns: Error code (one of the @KERR@ constants).
+ *
+ * Use: Inserts an attribute on a key. If an attribute with the same
+ * name already exists, it is deleted. Setting a null value
+ * removes the attribute.
+ */
+
+extern int key_putattr(key_file */*f*/, key */*k*/,
+ const char */*n*/, const char */*v*/);
+
+/* --- @key_mkattriter@ --- *
+ *
+ * Arguments: @key_attriter *i@ = pointer to attribute iterator
+ * @key *k@ = pointer to key
+ *
+ * Returns: ---
+ *
+ * Use: Initializes an attribute iterator. The attributes are
+ * returned by @key_nextattr@.
+ */
+
+extern void key_mkattriter(key_attriter */*i*/, key */*k*/);
+
+/* --- @key_nextattr@ --- *
+ *
+ * Arguments: @key_attriter *i@ = pointer to attribute iterator
+ * @const char **n, **v@ = pointers to name and value
+ *
+ * Returns: Zero if no attribute available, or nonzero if returned OK.
+ *
+ * Use: Returns the next attribute.
+ */
+
+extern int key_nextattr(key_attriter */*i*/,
+ const char **/*n*/, const char **/*v*/);
+
+/*----- Searching and iterating -------------------------------------------*/
+
+/* --- @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.
+ */
+
+extern key *key_bytype(key_file */*f*/, const char */*type*/);
+
+/* --- @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.
+ */
+
+extern key *key_byid(key_file */*f*/, uint32 /*id*/);
+
+/* --- @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.
+ */
+
+extern key *key_bytag(key_file */*f*/, const char */*tag*/);
+
+/* --- @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@.
+ */
+
+extern void key_mkiter(key_iter */*i*/, key_file */*f*/);
+
+/* --- @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.
+ */
+
+extern key *key_next(key_iter */*i*/);
+
+/*----- Other functions ---------------------------------------------------*/
+
+/* --- @key_moan@ --- *
+ *
+ * Arguments: @const char *file@ = name of the file
+ * @int line@ = line number in file
+ * @const char *msg@ = error message
+ * @void *p@ = argument pointer
+ *
+ * Returns: ---
+ *
+ * Use: Reports an error message about loading a key file.
+ */
+
+extern void key_moan(const char */*file*/, int /*line*/,
+ const char */*msg*/, void */*p*/);
+
+/* --- @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.
+ */
+
+extern const char *key_strerror(int /*err*/);
+
/*----- That's all, folks -------------------------------------------------*/
#ifdef __cplusplus