chiark / gitweb /
c3442f281cd86020c78c7c17a1c999a0d234fb70
[catacomb] / key / key-misc.c
1 /* -*-c-*-
2  *
3  * Simple key management
4  *
5  * (c) 1999 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of Catacomb.
11  *
12  * Catacomb is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU Library General Public License as
14  * published by the Free Software Foundation; either version 2 of the
15  * License, or (at your option) any later version.
16  *
17  * Catacomb is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License along with Catacomb; if not, write to the Free
24  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25  * MA 02111-1307, USA.
26  */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <time.h>
34
35 #include <mLib/bits.h>
36 #include <mLib/hash.h>
37 #include <mLib/sub.h>
38 #include <mLib/sym.h>
39
40 #include "key.h"
41
42 /*----- Useful macros -----------------------------------------------------*/
43
44 #define KEY_WRITE(f) do {                                               \
45      if (!((f)->f & KF_WRITE))                                          \
46        return (KERR_READONLY);                                          \
47    } while (0)
48
49 #define KEY_MODIFY(f) do { (f)->f |= KF_MODIFIED; } while (0)
50
51 #define KEY_LOAD(n) ((n) * 2)
52
53 /*----- Iteration and iterators -------------------------------------------*/
54
55 /* --- @key_mkiter@ --- *
56  *
57  * Arguments:   @key_iter *i@ = pointer to iterator object
58  *              @key_file *f@ = pointer to file structure
59  *
60  * Returns:     ---
61  *
62  * Use:         Initializes a key iterator.  The keys are returned by
63  *              @key_next@.
64  */
65
66 void key_mkiter(key_iter *i, key_file *f)
67 {
68   HASH_MKITER(&i->i, &f->byid);
69   i->t = time(0);
70 }
71
72 /* --- @key_next@ --- *
73  *
74  * Arguments:   @key_iter *i@ = pointer to iterator object
75  *
76  * Returns:     Pointer to next key, or null.
77  *
78  * Use:         Returns the next key in some arbitrary sequence.
79  */
80
81 key *key_next(key_iter *i)
82 {
83   hash_base *b;
84   key *k;
85   do {
86     HASH_NEXT(&i->i, b);
87     k = (key *)b;
88   } while (k && KEY_EXPIRED(i->t, k->exp) && KEY_EXPIRED(i->t, k->del));
89   return (k);
90 }
91
92 /*----- Lookup ------------------------------------------------------------*/
93
94 /* --- @key_bytype@ --- *
95  *
96  * Arguments:   @key_file *f@ = key file we want a key from
97  *              @const char *type@ = type string for desired key
98  *
99  * Returns:     Pointer to the best key to use, or null.
100  *
101  * Use:         Looks up a key by its type.  Returns the key with the latest
102  *              expiry time.  This function will not return an expired key.
103  */
104
105 key *key_bytype(key_file *f, const char *type)
106 {
107   time_t now = time(0);
108   key *k;
109   key_ref *kr;
110
111   if ((kr = sym_find(&f->bytype, type, -1, 0, 0)) == 0)
112     return (0);
113   for (k = kr->k; k && KEY_EXPIRED(now, k->exp); k = k->next)
114     ;
115   return (k);
116 }
117
118 /* --- @key_byid@ --- *
119  *
120  * Arguments:   @key_file *f@ = key file to find a key from
121  *              @uint32 id@ = id to look for
122  *
123  * Returns:     Key with matching id.
124  *
125  * Use:         Returns a key given its id.  This function will return an
126  *              expired key, but not a deleted one.
127  */
128
129 key *key_byid(key_file *f, uint32 id)
130 {
131   time_t t = time(0);
132   hash_base **bin, *b;
133
134   bin = HASH_BIN(&f->byid, id);
135   for (b = *bin; b; b = b->next) {
136     if (b->hash == id) {
137       key *k = (key *)b;
138       if (KEY_EXPIRED(t, k->exp) && KEY_EXPIRED(t, k->del))
139         return (0);
140       return (k);
141     }
142   }
143   return (0);
144 }
145
146 /* --- @key_bytag@ --- *
147  *
148  * Arguments:   @key_file *f@ = key file to find a key from
149  *              @const char *tag@ = pointer to tag string
150  *
151  * Returns:     Key with matching id or tag.
152  *
153  * Use:         Returns a key given its tag or id.  This function will return
154  *              an expired key, but not a deleted one.
155  */
156
157 key *key_bytag(key_file *f, const char *tag)
158 {
159   time_t t = time(0);
160   char *p;
161   uint32 id;
162   key_ref *kr = sym_find(&f->bytag, tag, -1, 0, 0);
163
164   if (kr && !(KEY_EXPIRED(t, kr->k->exp) && KEY_EXPIRED(t, kr->k->del)))
165     return (kr->k);
166   id = strtoul(tag, &p, 16);
167   if (!*p)
168     return (key_byid(f, id));
169   return (key_bytype(f, tag));
170 }
171
172 /* --- @key_qtag@ --- *
173  *
174  * Arguments:   @key_file *f@ = key file to find a key from
175  *              @const char *tag@ = pointer to tag string
176  *              @dstr *d@ = pointer to string for full tag name
177  *              @key **k@ = where to store the key pointer
178  *              @key_data ***kd@ = where to store the key data pointer
179  *
180  * Returns:     Zero if OK, nonzero if it failed.
181  *
182  * Use:         Performs a full lookup on a qualified tag name.  The tag is
183  *              qualified by the names of subkeys, separated by dots.  Hence,
184  *              a qualified tag is ID|TAG[.TAG...].  The various result
185  *              pointers can be null to indicate that the result isn't
186  *              interesting.
187  */
188
189 int key_qtag(key_file *f, const char *tag, dstr *d, key **k, key_data ***kd)
190 {
191   dstr dd = DSTR_INIT;
192   const char *q;
193   key *kk;
194   key_data **kkd;
195
196   /* --- Find the end of the base tag --- */
197
198   if ((q = strchr(tag, '.')) == 0)
199     DPUTS(&dd, tag);
200   else {
201     DPUTM(&dd, tag, q - tag);
202     DPUTZ(&dd);
203     q++;
204   }
205
206   /* --- Look up the key tag --- */
207
208   if ((kk = key_bytag(f, dd.buf)) == 0) {
209     dstr_destroy(&dd);
210     return (-1);
211   }
212
213   /* --- Set the various initial bits of result up --- */
214
215   if (d)
216     key_fulltag(kk, d);
217   if (k)
218     *k = kk;
219   kkd = &kk->k;
220
221   /* --- Now dig through the rest of the tag --- */
222
223   if (q) {
224     while (*q) {
225       key_struct *ks;
226
227       /* --- Stick on the next bit of the fullqtag --- */
228
229       DRESET(&dd);
230       while (*q && *q != '.') {
231         DPUTC(&dd, *q);
232         q++;
233       }
234       DPUTZ(&dd);
235       if (d) {
236         DPUTC(d, '.');
237         DPUTD(d, &dd);
238       }
239
240       /* --- Look up the subkey --- */
241
242       if ((*kkd)->e != KENC_STRUCT) {
243         kkd = 0;
244         break;
245       }
246       if ((ks = sym_find(&(*kkd)->u.s, dd.buf, -1, 0, 0)) == 0) {
247         kkd = 0;
248         break;
249       }
250       kkd = &ks->k;
251     }
252   }
253
254   /* --- Return the results --- */
255
256   dstr_destroy(&dd);
257   if (!kkd)
258     return (-1);
259   if (kd)
260     *kd = kkd;
261   return (0);
262 }
263
264 /*----- Miscellaneous functions -------------------------------------------*/
265
266 /* --- @key_delete@ --- *
267  *
268  * Arguments:   @key_file *f@ = pointer to file block
269  *              @key *k@ = key to delete
270  *
271  * Returns:     Error code (one of the @KERR@ constants).
272  *
273  * Use:         Removes the given key from the list.  The key file must be
274  *              writable.  (Due to the horridness of the data structures,
275  *              deleted keys aren't actually removed, just marked so that
276  *              they can't be looked up or iterated over.  One upshot of
277  *              this is that they don't get written back to the file when
278  *              it's closed.)
279  */
280
281 int key_delete(key_file *f, key *k)
282 {
283   KEY_WRITE(f);
284   k->exp = KEXP_EXPIRE;
285   k->del = KEXP_EXPIRE;
286   KEY_MODIFY(f);
287   return (0);
288 }
289
290 /* --- @key_expired@ --- *
291  *
292  * Arguments:   @key *k@ = pointer to key block
293  *
294  * Returns:     Zero if the key is OK, nonzero if it's expired.
295  */
296
297 int key_expired(key *k)
298 {
299   time_t now = time(0);
300   return (KEY_EXPIRED(now, k->exp) || KEY_EXPIRED(now, k->del));
301 }
302
303 /* --- @key_expire@ --- *
304  *
305  * Arguments:   @key_file *f@ = pointer to file block
306  *              @key *k@ = pointer to key block
307  *
308  * Returns:     Error code (one of the @KERR@ constants).
309  *
310  * Use:         Immediately marks the key as expired.  It may be removed
311  *              immediately, if it is no longer required, and will be removed
312  *              by a tidy operation when it is no longer required.  The key
313  *              file must be writable.
314  */
315
316 int key_expire(key_file *f, key *k)
317 {
318   KEY_WRITE(f);
319   k->exp = KEXP_EXPIRE;
320   if (k->del == KEXP_FOREVER)
321     k->del = KEXP_EXPIRE;
322   KEY_MODIFY(f);
323   return (0);
324 }
325
326 /* --- @key_used@ --- *
327  *
328  * Arguments:   @key_file *f@ = pointer to key file
329  *              @key *k@ = pointer to key block
330  *              @time_t t@ = when key can be removed
331  *
332  * Returns:     Zero if OK, nonzero on failure.
333  *
334  * Use:         Marks a key as being required until a given time.  Even
335  *              though the key may expire before then (and won't be returned
336  *              by type after that time), it will still be available when
337  *              requested explicitly by id.  The key file must be writable.
338  *
339  *              The only (current) reason for failure is attempting to use
340  *              a key which can expire for something which can't.
341  */
342
343 int key_used(key_file *f, key *k, time_t t)
344 {
345   KEY_WRITE(f);
346   if (t == KEXP_FOREVER) {
347     if (k->exp != KEXP_FOREVER)
348       return (KERR_WILLEXPIRE);
349   } else if (k->del >= t)
350     return (0);
351
352   k->del = t;
353   KEY_MODIFY(f);
354   return (0);
355 }
356
357 /* --- @key_fingerprint@ --- *
358  *
359  * Arguments:   @key *k@ = the key to fingerprint
360  *              @ghash *h@ = the hash to use
361  *              @const key_filter *kf@ = filter to apply
362  *
363  * Returns:     Nonzero if the key slightly matched the filter.
364  *
365  * Use:         Updates the hash context with the key contents.
366  */
367
368 static int abyname(const void *a, const void *b) {
369   key_attr *const *x = a, *const *y = b;
370   return (strcmp(SYM_NAME(*x), SYM_NAME(*y)));
371 }
372
373 int key_fingerprint(key *k, ghash *h, const key_filter *kf)
374 {
375   dstr d = DSTR_INIT;
376   int rc = 0;
377   key_attr *a, **v;
378   size_t n, i;
379   sym_iter ai;
380
381   if (!key_encode(k->k, &d, kf))
382     goto done;
383   rc = 1;
384   GH_HASHSTR(h, "catacomb-key-fingerprint:");
385   GH_HASHU32(h, k->id);
386   GH_HASHSTR8(h, k->type);
387   GH_HASH(h, d.buf, d.len);
388   for (n = 0, sym_mkiter(&ai, &k->a); (a = sym_next(&ai)) != 0; n++);
389   if (n) {
390     v = xmalloc(n * sizeof(*v));
391     for (i = 0, sym_mkiter(&ai, &k->a); (a = sym_next(&ai)) != 0; i++)
392       v[i] = a;
393     qsort(v, n, sizeof(*v), abyname);
394     for (i = 0; i < n; i++) {
395       GH_HASHSTR8(h, SYM_NAME(v[i]));
396       GH_HASHSTR16(h, v[i]->p);
397     }
398     xfree(v);
399   }
400 done:
401   dstr_destroy(&d);
402   return (rc);
403 }
404
405 /*----- That's all, folks -------------------------------------------------*/