3 * $Id: key.c,v 1.1 1999/09/03 08:41:12 mdw Exp $
5 * Simple key management
7 * (c) 1999 Mark Wooding
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of Catacomb.
14 * Catacomb is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
19 * Catacomb is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
24 * You should have received a copy of the GNU Library General Public
25 * License along with Catacomb; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
30 /*----- Revision history --------------------------------------------------*
33 * Revision 1.1 1999/09/03 08:41:12 mdw
38 /*----- Header files ------------------------------------------------------*/
47 #include <sys/types.h>
53 #include <mLib/alloc.h>
54 #include <mLib/base64.h>
55 #include <mLib/bits.h>
56 #include <mLib/crc32.h>
57 #include <mLib/dstr.h>
58 #include <mLib/hash.h>
59 #include <mLib/lock.h>
60 #include <mLib/report.h>
68 /*----- Useful macros -----------------------------------------------------*/
74 # define KEY_WRITE(f, func, val) do { \
75 if (!(f)->f & KF_WRITE) { \
76 moan(#func " [caller error]: keyfile is readonly"); \
82 # define KEY_WRITE(f, func) do { ; } while (0)
85 #define KEY_MODIFY(f) do { (f)->f |= KF_MODIFIED; } while (0)
87 #define KEY_LOAD(n) ((n) * 2)
89 /*----- Sanity checking of values -----------------------------------------*/
91 /* --- @key_chktype@ --- *
93 * Arguments: @const char *type@ = pointer to a type string
95 * Returns: Zero if OK, -1 on error.
97 * Use: Checks whether a type string is OK.
100 int key_chktype(const char *type)
105 if (isspace((unsigned char)*type))
116 /* --- @key_chkcomment@ --- *
118 * Arguments: @const char *comment@ = pointer to a comment string
120 * Returns: Zero if OK, -1 on error.
122 * Use: Checks whether a comment string is OK.
125 int key_chkcomment(const char *c)
143 /*----- Low-level fiddling ------------------------------------------------*/
145 /* --- @insert@ --- *
147 * Arguments: @key_file *f@ = pointer to file structure
148 * @const char *type@ = type of key to insert
149 * @const void *k@ = pointer to key data
150 * @size_t ksz@ = size of key data
151 * @time_t exp@ = expiry time for key
152 * @time_t del@ = deletion time for key
154 * Returns: Pointer to key block to fill in the rest of, or zero.
156 * Use: Inserts a key into a key file.
159 static key *insert(key_file *f,
161 const void *k, size_t ksz,
162 time_t exp, time_t del)
168 /* --- Sanity preservatives --- */
170 if (key_chktype(type))
173 /* --- Insert into the id table --- */
176 hash_base **bin, **b;
178 CRC32(id, 0, k, ksz);
179 bin = HASH_BIN(&f->byid, id);
180 for (b = bin; *b; b = &(*b)->next) {
181 if ((*b)->hash == id) {
193 /* --- Extend the table --- */
197 else if (hash_extend(&f->byid))
198 f->idload = KEY_LOAD(f->byid.mask / 2);
200 /* --- Initialize the key block --- */
203 kk->k = sub_alloc(ksz);
204 memcpy(kk->k, k, ksz);
206 kk->type = xstrdup(type);
212 /* --- Insert into the type table --- */
216 t = sym_find(&f->bytype, type, -1, sizeof(*t), &found);
222 if (exp != KEXP_FOREVER) {
223 while (*p && (*p)->exp != KEXP_EXPIRE && (*p)->exp > exp)
234 /*----- Iteration and iterators -------------------------------------------*/
236 /* --- @key_mkiter@ --- *
238 * Arguments: @key_iter *i@ = pointer to iterator object
239 * @key_file *f@ = pointer to file structure
243 * Use: Initializes a key iterator. The keys are returned by
247 void key_mkiter(key_iter *i, key_file *f)
249 HASH_MKITER(&i->i, &f->byid);
253 /* --- @key_next@ --- *
255 * Arguments: @key_iter *i@ = pointer to iterator object
257 * Returns: Pointer to next key, or null.
259 * Use: Returns the next key in some arbitrary sequence.
262 key *key_next(key_iter *i)
269 } while (k && KEY_EXPIRED(i->t, k->exp) && KEY_DELETED(i->t, k->del));
273 /* --- @key_mkattriter@ --- *
275 * Arguments: @key_attriter *i@ = pointer to attribute iterator
276 * @key_file *f@ = pointer to key file
277 * @key *k@ = pointer to key
281 * Use: Initializes an attribute iterator. The attributes are
282 * returned by @key_nextattr@.
285 void key_mkattriter(key_attriter *i, key_file *f, key *k)
287 sym_mkiter(&i->i, &k->a);
290 /* --- @key_nextattr@ --- *
292 * Arguments: @key_attriter *i@ = pointer to attribute iterator
293 * @const char **n, **v@ = pointers to name and value
295 * Returns: Zero if no attribute available, or nonzero if returned OK.
297 * Use: Returns the next attribute.
300 int key_nextattr(key_attriter *i, const char **n, const char **v)
302 key_attr *a = sym_next(&i->i);
310 /*----- Lookup ------------------------------------------------------------*/
312 /* --- @key_bytype@ --- *
314 * Arguments: @key_file *f@ = key file we want a key from
315 * @const char *type@ = type string for desired key
317 * Returns: Pointer to the best key to use, or null.
319 * Use: Looks up a key by its type. Returns the key with the latest
320 * expiry time. This function will not return an expired key.
323 key *key_bytype(key_file *f, const char *type)
325 time_t now = time(0);
329 if ((t = sym_find(&f->bytype, type, -1, 0, 0)) == 0)
331 for (k = t->k; k && KEY_EXPIRED(now, k->exp); k = k->next)
336 /* --- @key_byid@ --- *
338 * Arguments: @key_file *f@ = key file to find a key from
339 * @uint32 id@ = id to look for
341 * Returns: Key with matching id.
343 * Use: Returns a key given its id. This function will return an
344 * expired key, but not a deleted one.
347 key *key_byid(key_file *f, uint32 id)
352 bin = HASH_BIN(&f->byid, id);
353 for (b = *bin; b; b = b->next) {
356 if (KEY_EXPIRED(t, k->exp) && KEY_DELETED(t, k->del))
364 /*----- Attributes --------------------------------------------------------*/
366 /* --- @key_getattr@ --- *
368 * Arguments: @key_file *f@ = pointer to file
369 * @key *k@ = pointer to key
370 * @const char *n@ = pointer to attribute name
372 * Returns: Pointer to attribute value, or null if not found.
374 * Use: Returns the value of a key attribute.
377 const char *key_getattr(key_file *f, key *k, const char *n)
380 if ((a = sym_find(&k->a, n, -1, 0, 0)) == 0)
385 /* --- @key_putattr@ --- *
387 * Arguments: @key_file *f@ = pointer to file
388 * @key *k@ = pointer to key
389 * @const char *n@ = pointer to attribute name
390 * @const char *v@ = pointer to attribute value or null
394 * Use: Inserts an attribute on a key. If an attribute with the same
395 * name already exists, it is deleted. Setting a null value
396 * removes the attribute.
399 void key_putattr(key_file *f, key *k, const char *n, const char *v)
404 KEY_WRITE(f, key_putattr, NOTHING);
407 a = sym_find(&k->a, n, -1, sizeof(*a), &found);
411 } else if ((a = sym_find(&k->a, n, -1, 0, 0)) != 0) {
413 sym_remove(&k->a, a);
419 /* --- @key_setcomment@ --- *
421 * Arguments: @key_file *f@ = pointer to key file block
422 * @key *k@ = pointer to key block
423 * @const char *c@ = pointer to comment to set, or zero
427 * Use: Replaces the key's current comment with a new one.
430 void key_setcomment(key_file *f, key *k, const char *c)
432 KEY_WRITE(f, key_setcomment, NOTHING);
433 if (key_chkcomment(c))
444 /*----- Low-level file I/O ------------------------------------------------*/
446 /* --- @key_merge@ --- *
448 * Arguments: @key_file *f@ = pointer to file structure
449 * @const char *file@ = name of file (for error messages)
450 * @FILE *fp@ = file handle to read from
454 * Use: Reads keys from a file, and inserts them into the file.
457 void key_merge(key_file *f, const char *file, FILE *fp)
462 dstr n = DSTR_INIT, v = DSTR_INIT;
464 KEY_WRITE(f, key_merge, NOTHING);
466 for (; dstr_putline(&l, fp) != EOF; DRESET(&l)) {
471 /* --- Skip blank lines and comments --- *
473 * Quite what they're doing in what ought to be an automatically-
474 * maintained file I don't know.
478 while (isspace((unsigned char)*p))
480 if (!*p || *p == '#')
483 /* --- Break the line into fields --- *
485 * There are currently six fields of interest:
487 * * The key's type tag.
488 * * The actual key data itself.
489 * * The key expiry time.
490 * * The key deletion time.
491 * * The attributes field.
492 * * Any further comments.
494 * All but the last field can contain no spaces.
498 int n = str_split(p, vf, 5, &vf[5]);
500 moan("key file `%s', line %i: too few fields", file, line);
505 /* --- Decode various bits and insert the key --- */
512 base64_decode(&b, vf[1], strlen(vf[1]), &d);
513 base64_decode(&b, 0, 0, &d);
515 exp = (time_t)atol(vf[2]);
516 del = (time_t)atol(vf[3]);
518 if ((k = insert(f, vf[0], d.buf, d.len, exp, del)) == 0)
523 /* --- Parse up the attributes, if specified --- */
527 for (url_initdec(&uc, vf[4]); url_dec(&uc, &n, &v); ) {
528 key_putattr(f, k, n.buf, v.buf);
529 DRESET(&n); DRESET(&v);
533 /* --- Insert the comment --- */
536 k->c = xstrdup(vf[5]);
539 /* --- Extensive tidying up now required --- */
548 /* --- @key_extract@ --- *
550 * Arguments: @key_file *f@ = pointer to file structure
551 * @key *k@ = key to extract
552 * @FILE *fp@ = file to write on
554 * Returns: Zero if OK, EOF on error.
556 * Use: Extracts a key to an ouptut file.
559 int key_extract(key_file *f, key *k, FILE *fp)
563 /* --- Encode the key and write the easy stuff --- */
569 base64_encode(&b, k->k, k->ksz, &d);
570 base64_encode(&b, 0, 0, &d);
572 fprintf(fp, "%s %s %li %li ",
573 k->type, d.buf, (long)k->exp, (long)k->del);
577 /* --- Output the attributes --- */
586 for (sym_mkiter(&i, &k->a); (a = sym_next(&i)) != 0; ) {
588 url_enc(&uc, &d, SYM_NAME(a), a->p);
601 return (ferror(fp) ? EOF : 0);
604 /* --- @fdcopy@ --- *
606 * Arguments: @int source@ = source file descriptor
607 * @int dest@ = destination file descriptor
609 * Returns: Zero if OK, nonzero otherwise.
611 * Use: Copies data from one file descriptor to another.
614 static int fdcopy(int source, int dest)
618 if (lseek(source, 0, SEEK_SET) < 0||
619 lseek(dest, 0, SEEK_SET) < 0 ||
620 ftruncate(dest, 0) < 0)
623 int n = read(source, buf, sizeof(buf));
628 else if (write(dest, buf, n) < 0)
634 /* --- @key_write@ --- *
636 * Arguments: @key_file *f@ = pointer to key file block
638 * Returns: A @KWRITE_@ code indicating how well it worked.
640 * Use: Writes a key file's data back to the actual file. This code
641 * is extremely careful about error handling. It should usually
642 * be able to back out somewhere sensible, but it can tell when
643 * it's got itself into a real pickle and starts leaving well
646 * Callers, please make sure that you ring alarm bells when this
647 * function returns @KWRITE_BROKEN@.
650 int key_write(key_file *f)
652 dstr n_older = DSTR_INIT, n_old = DSTR_INIT, n_new = DSTR_INIT;
653 int rc = KWRITE_FAIL;
655 if (!(f->f & KF_MODIFIED))
658 /* --- Write a new key file out --- *
660 * Check for an error after each key line. This ought to be enough.
661 * Checking after each individual byte write and @fprintf@ isn't much fun.
664 dstr_putf(&n_new, "%s.new", f->name);
671 if ((fp = fopen(n_new.buf, "w")) == 0)
674 for (key_mkiter(&i, f); (k = key_next(&i)) != 0; ) {
675 if (key_extract(f, k, fp)) {
685 /* --- Set up the other filenames --- */
687 dstr_putf(&n_older, "%s.older", f->name);
688 dstr_putf(&n_old, "%s.old", f->name);
690 /* --- Move the current backup on one --- *
692 * If the `older' file exists, then we're in need of attention.
697 if (stat(n_older.buf, &st) == 0 || errno != ENOENT) {
702 if (rename(n_old.buf, n_older.buf) && errno != ENOENT)
706 /* --- Copy the current file to the backup --- */
710 if ((fd = open(n_old.buf, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0)
712 if (fdcopy(f->fd, fd)) {
720 /* --- Copy the newly created file to the current one --- *
722 * This is the dangerous bit.
727 if ((fd = open(n_new.buf, O_RDONLY)) < 0)
729 if (fdcopy(fd, f->fd)) {
738 /* --- Clean up --- *
740 * Remove the `new' file and the `older' backup. Then we're done.
747 /* --- Failure while writing the new key file --- *
749 * I need to copy the backup back. If that fails then I'm really stuffed.
750 * If not, then I might as well try to get the backups sorted back out
759 if ((fd = open(n_old.buf, O_RDONLY)) < 0)
761 else if (fdcopy(fd, f->fd)) {
771 if (rc == KWRITE_BROKEN)
774 /* Now drop through */
776 /* --- Failure while writing the new backup --- *
778 * The new backup isn't any use. Try to recover the old one.
785 if (rename(n_older.buf, n_old.buf) && errno != ENOENT)
789 /* Now drop through */
791 /* --- Failure while demoting the current backup --- *
793 * Leave the completed output file there for the operator in case he wants
798 dstr_destroy(&n_new);
799 dstr_destroy(&n_old);
800 dstr_destroy(&n_older);
803 /* --- Failure during write of new data --- *
805 * Clean up the new file and return. These errors can never cause
812 dstr_destroy(&n_new);
816 /*----- Opening and closing files -----------------------------------------*/
818 /* --- @key_open@ --- *
820 * Arguments: @key_file *f@ = pointer to file structure to initialize
821 * @const char *file@ = pointer to the file name
822 * @int how@ = opening options (@KOPEN_*@).
824 * Returns: Zero if it worked, nonzero otherwise.
826 * Use: Opens a key file, reads its contents, and stores them in a
827 * structure. The file is locked appropriately until closed
828 * using @key_close@. On an error, everything is cleared away
829 * tidily. If the file is opened with @KOPEN_WRITE@, it's
830 * created if necessary, with read and write permissions for its
834 int key_open(key_file *f, const char *file, int how)
838 /* --- Trivial bits of initialization --- */
841 f->name = xstrdup(file);
843 /* --- Open the file and get the lock --- */
850 /* --- Lots of things depend on whether we're writing --- */
859 of = O_RDWR | O_CREAT;
868 if ((fd = open(file, of, 0600)) < 0)
870 if (fcntl(fd, F_SETFD, 1) < 0 ||
871 lock_file(fd, lf) < 0 || (fp = fdopen(fd, ff)) == 0) {
878 /* --- Read the file of keys into the table --- */
880 hash_create(&f->byid, 64);
881 f->idload = KEY_LOAD(64);
882 sym_create(&f->bytype);
884 key_merge(f, file, fp);
885 if (how == KOPEN_READ)
886 f->f &= ~(KF_WRITE | KF_MODIFIED);
888 f->f &= ~KF_MODIFIED;
890 /* --- Close the file if only needed for reading --- */
892 if (how == KOPEN_READ) {
901 /* --- @key_close@ --- *
903 * Arguments: @key_file *f@ = pointer to key file block
905 * Returns: A @KWRITE_@ code indicating how it went.
907 * Use: Frees all the key data, writes any changes. Make sure that
908 * all hell breaks loose if this returns @KWRITE_BROKEN@.
911 int key_close(key_file *f)
917 if ((e = key_write(f)) != KWRITE_OK)
920 /* --- Free all the individual keys --- */
922 for (hash_mkiter(&i, &f->byid); (b = hash_next(&i)) != 0; ) {
927 sub_free(k->k, k->ksz);
931 for (sym_mkiter(&j, &k->a); (a = sym_next(&j)) != 0; )
936 hash_destroy(&f->byid);
937 sym_destroy(&f->bytype);
945 /*----- Miscellaneous functions -------------------------------------------*/
949 * Arguments: @key_file *f@ = pointer to key file
950 * @const char *type@ = the type of this key
951 * @const void *k@ = pointer to key data
952 * @size_t ksz@ = size of key data
953 * @time_t exp@ = when the key expires
954 * @const char *c@ = textual comment to attach
956 * Returns: Key block containing new data, or null if it couldn't be
959 * Use: Attaches a new key to a key file. You must have a writable
960 * key file for this to work.
962 * The type is a key type string. This interface doesn't care
963 * about how type strings are formatted: it just treats them as
964 * opaque gobs of text. Clients are advised to choose some
965 * standard for representing key types, though.
967 * The key can be any old binary mess.
969 * The expiry time should either be a time in the future, or the
970 * magic value @KEXP_FOREVER@ which means `never expire this
971 * key'. Be careful with `forever' keys. If I were you, I'd
972 * use a more sophisticated key management system than this for
975 * The comment can be any old text not containing newlines or
976 * nulls. This interface doesn't impose any length restrictions
977 * on comment lengths.
980 key *key_new(key_file *f, const char *type,
981 const void *k, size_t ksz,
982 time_t exp, const char *c)
987 KEY_WRITE(f, key_new, 0);
989 if (KEY_EXPIRED(t, exp) ||
990 key_chktype(type) || key_chkcomment(c) ||
991 (kk = insert(f, type, k, ksz, exp, KEXP_UNUSED)) == 0)
999 /* --- @key_delete@ --- *
1001 * Arguments: @key_file *f@ = pointer to file block
1002 * @key *k@ = key to delete
1006 * Use: Removes the given key from the list. The key file must be
1007 * writable. (Due to the horridness of the data structures,
1008 * deleted keys aren't actually removed, just marked so that
1009 * they can't be looked up or iterated over. One upshot of
1010 * this is that they don't get written back to the file when
1014 void key_delete(key_file *f, key *k)
1016 KEY_WRITE(f, key_delete, NOTHING);
1017 k->exp = KEXP_EXPIRE;
1018 k->del = KEXP_UNUSED;
1022 /* --- @key_expire@ --- *
1024 * Arguments: @key_file *f@ = pointer to file block
1025 * @key *k@ = pointer to key block
1029 * Use: Immediately marks the key as expired. It may be removed
1030 * immediately, if it is no longer required, and will be removed
1031 * by a tidy operation when it is no longer required. The key
1032 * file must be writable.
1035 void key_expire(key_file *f, key *k)
1037 KEY_WRITE(f, key_expire, NOTHING);
1038 k->exp = KEXP_EXPIRE;
1039 if (k->del == KEXP_FOREVER)
1040 k->del = KEXP_UNUSED;
1044 /* --- @key_used@ --- *
1046 * Arguments: @key_file *f@ = pointer to key file
1047 * @key *k@ = pointer to key block
1048 * @time_t t@ = when key can be removed
1050 * Returns: Zero if OK, nonzero on failure.
1052 * Use: Marks a key as being required until a given time. Even
1053 * though the key may expire before then (and won't be returned
1054 * by type after that time), it will still be available when
1055 * requested explicitly by id. The key file must be writable.
1057 * The only (current) reason for failure is attempting to use
1058 * a key which can expire for something which can't.
1061 int key_used(key_file *f, key *k, time_t t)
1063 KEY_WRITE(f, key_used, -1);
1064 if (t == KEXP_FOREVER) {
1065 if (k->exp != KEXP_FOREVER) {
1069 } else if (k->del >= t)
1077 /*----- That's all, folks -------------------------------------------------*/