math/mpreduce.h: Missing include files.
[u/mdw/catacomb] / key / key-io.c
1 /* -*-c-*-
2 *
3 * Adding new keys to a key file
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 <ctype.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <time.h>
36
37 #include <mLib/bits.h>
38 #include <mLib/crc32.h>
39 #include <mLib/dstr.h>
40 #include <mLib/hash.h>
41 #include <mLib/str.h>
42 #include <mLib/sub.h>
43 #include <mLib/sym.h>
44 #include <mLib/url.h>
45
46 #include "key.h"
47
48 /*----- Tweakable macros --------------------------------------------------*/
49
50 #define KEY_INITSZ 16
51
52 /*----- Low-level functions -----------------------------------------------*/
53
54 /* --- @insert@ --- *
55 *
56 * Arguments: @key_file *f@ = pointer to file structure
57 * @key *k@ = pointer to key block to insert
58 *
59 * Returns: Error code (one of the @KERR@ code).
60 *
61 * Use: Links a new key block into the complicated data structure
62 * which is a keyring file.
63 */
64
65 static int insert(key_file *f, key *k)
66 {
67 key_ref *kr = 0;
68 unsigned found;
69
70 /* --- Sanity preservatives --- */
71
72 if (key_chkident(k->type))
73 return (KERR_BADTYPE);
74 else if (k->tag && key_chkident(k->tag))
75 return (KERR_BADTAG);
76
77 /* --- Insert into the tag table --- */
78
79 if (k->tag) {
80 kr = sym_find(&f->bytag, k->tag, -1, sizeof(*kr), &found);
81 if (found)
82 return (KERR_DUPTAG);
83 kr->k = k;
84 }
85
86 /* --- Insert into the id table --- */
87
88 {
89 hash_base **bin, *b;
90
91 bin = HASH_BIN(&f->byid, k->id);
92 for (b = *bin; b; b = b->next) {
93 if (b->hash == k->id) {
94 if (kr)
95 sym_remove(&f->bytag, kr);
96 return (KERR_DUPID);
97 }
98 }
99
100 k->_b.next = *bin;
101 *bin = &k->_b;
102 k->_b.hash = k->id;
103 }
104
105 /* --- Extend the table --- */
106
107 if (f->idload > 0)
108 f->idload--;
109 else if (hash_extend(&f->byid))
110 f->idload = SYM_LIMIT(f->byid.mask / 2);
111
112 /* --- Insert into the type table --- */
113
114 kr = sym_find(&f->bytype, k->type, -1, sizeof(*kr), &found);
115 if (!found) {
116 kr->k = k;
117 k->next = 0;
118 } else {
119 key **p = &kr->k;
120 if (k->exp != KEXP_FOREVER) {
121 while (*p && (*p)->exp != KEXP_EXPIRE && (*p)->exp > k->exp)
122 p = &(*p)->next;
123 }
124 k->next = *p;
125 *p = k;
126 }
127
128 return (KERR_OK);
129 }
130
131 /*----- Reading and writing keys ------------------------------------------*/
132
133 /* --- @exptime@ --- *
134 *
135 * Arguments: @const char *p@ = pointer to string
136 *
137 * Returns: Time value.
138 *
139 * Use: Translates an expiry or deletion time.
140 */
141
142 time_t exptime(const char *p)
143 {
144 size_t sz = strlen(p);
145 if (strncmp(p, "expired", sz) == 0)
146 return (KEXP_EXPIRE);
147 else if (strncmp(p, "forever", sz) == 0)
148 return (KEXP_FOREVER);
149 else
150 return (atol(p));
151 }
152
153 /* --- @key_merge@ --- *
154 *
155 * Arguments: @key_file *f@ = pointer to file structure
156 * @const char *file@ = name of file (for error messages)
157 * @FILE *fp@ = file handle to read from
158 * @key_reporter *rep@ = error reporting function
159 * @void *arg@ = argument for function
160 *
161 * Returns: Error code (one of the @KERR@ constants).
162 *
163 * Use: Reads keys from a file, and inserts them into the file.
164 */
165
166 int key_merge(key_file *f, const char *file, FILE *fp,
167 key_reporter *rep, void *arg)
168 {
169 int line = 0;
170 dstr l = DSTR_INIT;
171 dstr n = DSTR_INIT, v = DSTR_INIT;
172
173 if (!(f->f & KF_WRITE))
174 return (KERR_READONLY);
175
176 for (; dstr_putline(&l, fp) != EOF; DRESET(&l)) {
177 char *vf[6];
178 char *p = l.buf;
179 key *k;
180
181 /* --- Skip blank lines and comments --- *
182 *
183 * Quite what they're doing in what ought to be an automatically-
184 * maintained file I don't know.
185 */
186
187 line++;
188 while (isspace((unsigned char)*p))
189 p++;
190 if (!*p || *p == '#')
191 continue;
192
193 /* --- Break the line into fields --- *
194 *
195 * There are currently six fields of interest:
196 *
197 * * The key's identification (id, tag and type).
198 * * The actual key data itself.
199 * * The key expiry time.
200 * * The key deletion time.
201 * * The attributes field.
202 * * Any further comments.
203 *
204 * All but the last field can contain no spaces.
205 */
206
207 {
208 int n = str_split(p, vf, 5, &vf[5]);
209 if (n < 4) {
210 if (rep)
211 rep(file, line, "too few fields", arg);
212 goto skip_0;
213 }
214 }
215
216 /* --- Allocate a new key block --- */
217
218 k = CREATE(key);
219
220 /* --- Extract the key data into the block --- */
221
222 if ((k->k = key_read(vf[1], 0)) == 0) {
223 if (rep)
224 rep(file, line, "bad key data", arg);
225 goto skip_1;
226 }
227
228 /* --- Decode the identification field --- *
229 *
230 * For compatibility, derive a keyid from the key data. This can only be
231 * done if the key encoding is binary (and presumably old-encoding binary
232 * at that).
233 */
234
235 {
236 char *q = strchr(vf[0], ':');
237 char *qq;
238
239 if (!q) {
240 if (k->k->e != KENC_BINARY) {
241 if (rep)
242 rep(file, line, "new-style key encoding but no keyid", arg);
243 goto skip_2;
244 }
245 k->id = crc32(0, k->k->u.k.k, k->k->u.k.sz);
246 k->type = xstrdup(vf[0]);
247 k->tag = 0;
248 } else {
249 *q++ = 0;
250 k->id = strtoul(p, 0, 16);
251 if ((qq = strchr(q, ':')) == 0 || !qq[1]) {
252 if (qq)
253 *qq = 0;
254 k->tag = 0;
255 } else {
256 *qq++ = 0;
257 k->tag = xstrdup(qq);
258 }
259 k->type = xstrdup(q);
260 }
261 }
262
263 /* --- Get a key block for the new key --- */
264
265 k->exp = exptime(vf[2]);
266 k->del = exptime(vf[3]);
267
268 /* --- Insert the key block into the table --- */
269
270 {
271 int err;
272
273 again:
274 if ((err = insert(f, k)) < 0) {
275 if (err == KERR_DUPTAG) {
276 if (rep)
277 rep(file, line, "duplicate key tag stripped", arg);
278 xfree(k->tag);
279 k->tag = 0;
280 goto again;
281 }
282 if (rep)
283 rep(file, line, key_strerror(err), arg);
284 goto skip_3;
285 }
286 }
287
288 /* --- Parse up the attributes, if specified --- */
289
290 sym_create(&k->a);
291 if (vf[4] && strcmp(vf[4], "-") != 0) {
292 url_dctx uc;
293 for (url_initdec(&uc, vf[4]); url_dec(&uc, &n, &v); ) {
294 key_putattr(f, k, n.buf, v.buf);
295 DRESET(&n); DRESET(&v);
296 }
297 }
298
299 /* --- Insert the comment --- */
300
301 if (vf[5])
302 k->c = xstrdup(vf[5]);
303 else
304 k->c = 0;
305 continue;
306
307 /* --- Tidy up after something going wrong --- */
308
309 skip_3:
310 if (k->tag)
311 xfree(k->tag);
312 xfree(k->type);
313 skip_2:
314 key_drop(k->k);
315 skip_1:
316 DESTROY(k);
317 skip_0:;
318 }
319
320 /* --- Extensive tidying up now required --- */
321
322 dstr_destroy(&l);
323 dstr_destroy(&n);
324 dstr_destroy(&v);
325 f->f |= KF_MODIFIED;
326 return (0);
327 }
328
329 /* --- @key_extract@ --- *
330 *
331 * Arguments: @key_file *f@ = pointer to file structure
332 * @key *k@ = key to extract
333 * @FILE *fp@ = file to write on
334 * @const key_filter *kf@ = pointer to key selection block
335 *
336 * Returns: Zero if OK, EOF on error.
337 *
338 * Use: Extracts a key to an ouptut file.
339 */
340
341 int key_extract(key_file *f, key *k, FILE *fp, const key_filter *kf)
342 {
343 dstr d = DSTR_INIT;
344 time_t t = time(0);
345
346 /* --- Skip the key if it's deleted or unselected--- */
347
348 if (KEY_EXPIRED(t, k->del) || !key_match(k->k, kf))
349 return (0);
350
351 /* --- Encode the key and write the easy stuff --- */
352
353 key_fulltag(k, &d);
354 DPUTC(&d, ' ');
355 key_write(k->k, &d, kf);
356 DPUTC(&d, ' ');
357 dstr_write(&d, fp);
358 DRESET(&d);
359
360 /* --- Write out the expiry and deletion times --- */
361
362 if (KEY_EXPIRED(t, k->exp))
363 fputs("expired ", fp);
364 else if (k->exp == KEXP_FOREVER)
365 fputs("forever ", fp);
366 else
367 fprintf(fp, "%li ", (long)k->exp);
368
369 if (k->del == KEXP_FOREVER)
370 fputs("forever ", fp);
371 else
372 fprintf(fp, "%li ", (long)k->del);
373
374 /* --- Output the attributes --- */
375
376 {
377 int none = 1;
378 sym_iter i;
379 key_attr *a;
380 url_ectx uc;
381
382 url_initenc(&uc);
383 for (sym_mkiter(&i, &k->a); (a = sym_next(&i)) != 0; ) {
384 none = 0;
385 url_enc(&uc, &d, SYM_NAME(a), a->p);
386 }
387 if (none)
388 DPUTS(&d, "-");
389 DWRITE(&d, fp);
390 }
391
392 dstr_destroy(&d);
393 if (k->c) {
394 putc(' ', fp);
395 fputs(k->c, fp);
396 }
397 putc('\n', fp);
398 return (ferror(fp) ? EOF : 0);
399 }
400
401 /*----- Opening and closing files -----------------------------------------*/
402
403 /* --- @key_open@ --- *
404 *
405 * Arguments: @key_file *f@ = pointer to file structure to initialize
406 * @const char *file@ = pointer to the file name
407 * @unsigned how@ = opening options (@KOPEN_*@).
408 * @key_reporter *rep@ = error reporting function
409 * @void *arg@ = argument for function
410 *
411 * Returns: Zero if it worked, nonzero otherwise.
412 *
413 * Use: Opens a key file, reads its contents, and stores them in a
414 * structure. The file is locked appropriately until closed
415 * using @key_close@. On an error, everything is cleared away
416 * tidily. If the file is opened with @KOPEN_WRITE@, it's
417 * created if necessary, with read and write permissions for its
418 * owner only.
419 */
420
421 int key_open(key_file *f, const char *file, unsigned how,
422 key_reporter *rep, void *arg)
423 {
424 if (key_lockfile(f, file, how)) {
425 rep(file, 0, strerror(errno), arg);
426 return (-1);
427 }
428 f->f = 0;
429 f->name = xstrdup(file);
430
431 hash_create(&f->byid, KEY_INITSZ);
432 f->idload = SYM_LIMIT(KEY_INITSZ);
433 sym_create(&f->bytype);
434 sym_create(&f->bytag);
435 f->f |= KF_WRITE;
436 if (f->fp)
437 key_merge(f, file, f->fp, rep, arg);
438 f->f &= ~KF_MODIFIED;
439
440 if ((how & KOPEN_MASK) == KOPEN_READ) {
441 f->f &= ~KF_WRITE;
442 fclose(f->fp);
443 f->fp = 0;
444 }
445
446 return (0);
447 }
448
449 /* --- @key_discard@ --- *
450 *
451 * Arguments: @key_file *f@ = pointer to key file block
452 *
453 * Returns: ---
454 *
455 * Use: Frees all the key data, without writing changes.
456 */
457
458 void key_discard(key_file *f)
459 {
460 hash_base *b;
461 hash_iter i;
462
463 /* --- Free all the individual keys --- */
464
465 for (hash_mkiter(&i, &f->byid); (b = hash_next(&i)) != 0; ) {
466 sym_iter j;
467 key_attr *a;
468 key *k = (key *)b;
469
470 if (k->k) key_drop(k->k);
471 xfree(k->type);
472 xfree(k->tag);
473 if (k->c)
474 xfree(k->c);
475 for (sym_mkiter(&j, &k->a); (a = sym_next(&j)) != 0; )
476 xfree(a->p);
477 sym_destroy(&k->a);
478 DESTROY(k);
479 }
480 hash_destroy(&f->byid);
481 sym_destroy(&f->bytype);
482 sym_destroy(&f->bytag);
483
484 if (f->fp)
485 fclose(f->fp);
486 xfree(f->name);
487 }
488
489 /* --- @key_close@ --- *
490 *
491 * Arguments: @key_file *f@ = pointer to key file block
492 *
493 * Returns: A @KWRITE_@ code indicating how it went.
494 *
495 * Use: Frees all the key data, writes any changes. Make sure that
496 * all hell breaks loose if this returns @KWRITE_BROKEN@.
497 */
498
499 int key_close(key_file *f)
500 {
501 int e;
502
503 if (f->fp && (e = key_save(f)) != KWRITE_OK)
504 return (e);
505 key_discard(f);
506 return (KWRITE_OK);
507 }
508
509 /* --- @key_new@ ---
510 *
511 * Arguments: @key_file *f@ = pointer to key file
512 * @uint32 id@ = keyid to set
513 * @const char *type@ = the type of this key
514 * @time_t exp@ = when the key expires
515 * @key *kk@ = where to put the key pointer
516 *
517 * Returns: Error code (one of the @KERR@ constants).
518 *
519 * Use: Attaches a new key to a key file. You must have a writable
520 * key file for this to work.
521 *
522 * The type is a key type string. This interface doesn't care
523 * about how type strings are formatted: it just treats them as
524 * opaque gobs of text. Clients are advised to choose some
525 * standard for representing key types, though.
526 *
527 * The expiry time should either be a time in the future, or the
528 * magic value @KEXP_FOREVER@ which means `never expire this
529 * key'. Be careful with `forever' keys. If I were you, I'd
530 * use a more sophisticated key management system than this for
531 * them.
532 */
533
534 int key_new(key_file *f, uint32 id, const char *type, time_t exp, key **kk)
535 {
536 key *k = 0;
537 time_t t = time(0);
538 int e = KERR_OK;
539
540 /* --- Make sure the file is writable --- */
541
542 if (!(f->f & KF_WRITE))
543 e = KERR_READONLY;
544 else if (KEY_EXPIRED(t, exp))
545 e = KERR_EXPIRED;
546 else if (key_chkident(type))
547 e = KERR_BADTYPE;
548 else {
549 k = CREATE(key);
550 k->id = id;
551 k->tag = 0;
552 k->exp = k->del = exp;
553 k->c = 0;
554 k->type = (char *)type; /* temporarily */
555 sym_create(&k->a);
556 if ((e = insert(f, k)) != 0)
557 DESTROY(k);
558 else {
559 k->k = key_newstring(KCAT_SHARE, "<unset>");
560 k->type = xstrdup(type);
561 *kk = k;
562 f->f |= KF_MODIFIED;
563 }
564 }
565
566 return (e);
567 }
568
569 /*----- That's all, folks -------------------------------------------------*/