X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/ba6e6b64033b1f9de49feccb5c9cd438354481f7..0f00dc4c8eb47e67bc0f148c2dd109f73a451e0a:/key/key-data.c diff --git a/key/key-data.c b/key/key-data.c new file mode 100644 index 0000000..8b01d72 --- /dev/null +++ b/key/key-data.c @@ -0,0 +1,467 @@ +/* -*-c-*- + * + * 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. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "key-data.h" +#include "mp.h" +#include "mptext.h" + +/*----- Reference counting stuff ------------------------------------------*/ + +/* --- @key_incref@ --- * + * + * Arguments: @key_data *k@ = pointer to key data + * + * Returns: --- + * + * Use: Increments the refcount on a key data block. + */ + +void key_incref(key_data *k) { KEY_INCREF(k); } + +/* --- @key_destroy@ --- * + * + * Arguments: @key_data *k@ = pointer to key data to destroy + * + * Returns: --- + * + * Use: Destroys a block of key data, regardless of reference count. + * Don't use this unless you know what you're doing. + */ + +void key_destroy(key_data *k) +{ + switch (k->e & KF_ENCMASK) { + case KENC_BINARY: + case KENC_ENCRYPT: + if (k->u.k.k) { + 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_STRING: + xfree(k->u.p); + break; + case KENC_EC: + EC_DESTROY(&k->u.e); + break; + case KENC_STRUCT: { + key_data *kd; + key_subkeyiter i; + + for (key_mksubkeyiter(&i, k); key_nextsubkey(&i, 0, &kd); ) + KEY_DROP(kd); + sym_destroy(&k->u.s); + } break; + default: + abort(); + } + DESTROY(k); +} + +/* --- @key_drop@ --- * + * + * Arguments: @key_data *k@ = pointer to key data to destroy + * + * Returns: --- + * + * Use: Drops a reference to key data, destroying it if necessary. + */ + +void key_drop(key_data *k) { KEY_DROP(k); } + +/* --- @key_split@ --- * + * + * Arguments: @key_data **kk@ = address of pointer to key data block + * + * Returns: --- + * + * Use: Replaces @*kk@ with a pointer to the same key data, but with + * just one reference. + */ + +void key_split(key_data **kk) +{ + key_data *k = *kk; + + if (k->ref == 1) + return; + switch (k->e & KF_ENCMASK) { + case KENC_BINARY: + *kk = key_newbinary(k->e, k->u.k.k, k->u.k.sz); + break; + case KENC_ENCRYPT: + *kk = key_newencrypted(k->e, k->u.k.k, k->u.k.sz); + break; + case KENC_MP: + *kk = key_newmp(k->e, k->u.m); + break; + case KENC_STRING: + *kk = key_newstring(k->e, k->u.p); + break; + case KENC_EC: + *kk = key_newec(k->e, &k->u.e); + break; + case KENC_STRUCT: { + key_subkeyiter i; + const char *tag; + key_data *kd; + + *kk = key_newstruct(); + for (key_mksubkeyiter(&i, k); key_nextsubkey(&i, &tag, &kd); ) + key_structset(*kk, tag, kd); + } break; + default: + abort(); + } +} + +/*----- Setting new values ------------------------------------------------*/ + +/* --- @key_newraw@ --- * + * + * Arguments: @unsigned e@ = encoding type to set + * + * Returns: New key block, not filled in. + */ + +key_data *key_newraw(unsigned e) +{ + key_data *k = CREATE(key_data); + k->e = e; + k->ref = 1; + return (k); +} + +/* --- @key_newbinary@ --- * + * + * Arguments: @unsigned e@ = other encoding flags + * @const void *p@ = pointer to key data + * @size_t sz@ = size of the key data + * + * Returns: New key data object. + */ + +key_data *key_newbinary(unsigned e, const void *p, size_t sz) +{ + key_data *k = key_newraw(KENC_BINARY | e); + k->u.k.k = sub_alloc(sz); + memcpy(k->u.k.k, p, sz); + k->u.k.sz = sz; + return (k); +} + +/* --- @key_newencrypted@ --- * + * + * Arguments: @unsigned e@ = other encoding flags + * @const void *p@ = pointer to key data + * @size_t sz@ = size of the key data + * + * Returns: New key data object. + */ + +key_data *key_newencrypted(unsigned e, const void *p, size_t sz) +{ + key_data *k = key_newraw(KENC_ENCRYPT | e); + k->u.k.k = sub_alloc(sz); + memcpy(k->u.k.k, p, sz); + k->u.k.sz = sz; + return (k); +} + +/* --- @key_newmp@ --- * + * + * Arguments: @unsigned e@ = other encoding flags + * @mp *m@ = pointer to the value to set + * + * Returns: New key data object. + */ + +key_data *key_newmp(unsigned e, mp *m) +{ + key_data *k = key_newraw(KENC_MP | e); + k->u.m = MP_COPY(m); + return (k); +} + +/* --- @key_newstring@ --- * + * + * Arguments: @unsigned e@ = other encoding flags + * @const char *p@ = pointer to the value to set + * + * Returns: New key data object. + */ + +key_data *key_newstring(unsigned e, const char *p) +{ + key_data *k = key_newraw(KENC_STRING | e); + k->u.p = xstrdup(p); + return (k); +} + +/* --- @key_newec@ --- * + * + * Arguments: @unsigned e@ = other encoding flags + * @const ec *pt@ = pointer to the value to set + * + * Returns: New key data object. + */ + +key_data *key_newec(unsigned e, const ec *pt) +{ + key_data *k = key_newraw(KENC_EC | e); + EC_CREATE(&k->u.e); + EC_COPY(&k->u.e, pt); + return (k); +} + +/* --- @key_newstruct@ --- * + * + * Arguments: --- + * + * Returns: New key data object. + */ + +key_data *key_newstruct(void) +{ + key_data *k = key_newraw(KENC_STRUCT); + sym_create(&k->u.s); + return (k); +} + +/* --- @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 & KF_ENCMASK) == KENC_STRUCT)); + ks = sym_find(&k->u.s, tag, -1, 0, 0); + if (!ks) + return (0); + return (ks->k); +} + +/* --- @key_mksubkeyiter@ --- * + * + * Arguments: @key_subkeyiter *i@ = pointer to iterator block + * @key_data *k@ = pointer to key data block + * + * Returns: --- + * + * Use: Initializes a subkey iterator. + */ + +void key_mksubkeyiter(key_subkeyiter *i, key_data *k) +{ + assert(((void)"Key is not structured", + (k->e & KF_ENCMASK) == KENC_STRUCT)); + sym_mkiter(&i->i, &k->u.s); +} + +/* --- @key_nextsubkey@ --- * + * + * Arguments: @key_structiter *i@ = pointer to iterator block + * @const char **tag@ = where to put the tag pointer, or null + * @key_data **kd@ = where to put the key data pointer, or null + * + * Returns: Nonzero if there was another item, zero if we hit the + * end-stop. + * + * Use: Collects the next subkey of a structured key. + */ + +int key_nextsubkey(key_subkeyiter *i, const char **tag, key_data **kd) +{ + key_struct *ks; + + if ((ks = sym_next(&i->i)) == 0) + return (0); + if (tag) *tag = SYM_NAME(ks); + if (kd) *kd = ks->k; + return (1); +} + +/* --- @key_structset@, @key_structsteal@ --- * + * + * Arguments: @key_data *k@ = pointer to key data block + * @const char *tag@ = pointer to tag string + * @key_data *kd@ = new key data to store + * + * Returns: --- + * + * Use: Creates a new subkey. Stealing doesn't affect @kd@'s + * refcount. If @kd@ is null, the subkey is deleted. + */ + +static void structset(key_data *k, int stealp, + const char *tag, key_data *kd) +{ + key_struct *ks; + unsigned f; + + assert(((void)"Key is not structured", k->e == KENC_STRUCT)); + assert(((void)"Key has multiple references", k->ref == 1)); + if (!kd) { + ks = sym_find(&k->u.s, tag, -1, 0, 0); + if (ks) sym_remove(&k->u.s, ks); + } else { + ks = sym_find(&k->u.s, tag, -1, sizeof(*ks), &f); + if (f) + key_drop(ks->k); + if (!stealp) KEY_INCREF(kd); + ks->k = kd; + } +} + +void key_structset(key_data *k, const char *tag, key_data *kd) + { structset(k, 0, tag, kd); } +void key_structsteal(key_data *k, const char *tag, key_data *kd) + { structset(k, 1, tag, kd); } + +/*----- Miscellaneous operations ------------------------------------------*/ + +/* --- @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 { + key_subkeyiter i; + const char *tag; + size_t n = 0; + int rc; + + if (d) + n = d->len; + for (key_mksubkeyiter(&i, k); key_nextsubkey(&i, &tag, &k); ) { + if (d) { + d->len = n; + dstr_putf(d, ".%s", tag); + } + if ((rc = key_do(k, kf, d, func, p)) != 0) + return (rc); + } + return (0); + } +} + +/* --- @key_copydata@ --- * + * + * Arguments: @key_data *k@ = key data to copy + * @const key_filter *kf@ = pointer to filter block + * + * Returns: Pointer to a copy of the data, or null if the root subkey + * didn't match the filter. + * + * Use: Copies a chunk of key data. Subkeys, whether they're + * structured or leaves, which don't match the filter aren't + * copied. The copy may or may not have structure in common + * with the original. + */ + +static int structmatchp(key_data *k, const key_filter *kf) +{ + key_subkeyiter i; + + if (!KEY_MATCH(k, kf)) return (0); + else if ((k->e & KF_ENCMASK) == KENC_STRUCT) return (1); + else { + for (key_mksubkeyiter(&i, k); key_nextsubkey(&i, 0, &k); ) + if (!structmatchp(k, kf)) return (0); + return (1); + } +} + +key_data *key_copydata(key_data *k, const key_filter *kf) +{ + key_subkeyiter i; + const char *tag; + key_data *kd, *kkd; + + /* --- Trivial cases --- */ + + if (!KEY_MATCH(k, kf)) + return (0); + else if (structmatchp(k, kf)) { + key_incref(k); + return (k); + } + + /* --- Copy a structured key recursively --- */ + + kkd = key_newstruct(); + for (key_mksubkeyiter(&i, k); key_nextsubkey(&i, &tag, &kd); ) { + if ((kd = key_copydata(kd, kf)) != 0) + key_structsteal(kkd, tag, kd); + } + + /* --- Done --- */ + + return (kkd); +} + +/*----- That's all, folks -------------------------------------------------*/