server/admin.c: Fix `=' vs `==' error in assertion.
[tripe] / server / keymgmt.c
1 /* -*-c-*-
2 *
3 * Key loading and storing
4 *
5 * (c) 2001 Straylight/Edgeware
6 */
7
8 /*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of Trivial IP Encryption (TrIPE).
11 *
12 * TrIPE is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * TrIPE 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 General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with TrIPE; if not, write to the Free Software Foundation,
24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
27 /*----- Header files ------------------------------------------------------*/
28
29 #include "tripe.h"
30
31 /*----- Algswitch stuff ---------------------------------------------------*/
32
33 /* --- @algs_get@ --- *
34 *
35 * Arguments: @algswitch *a@ = where to put the algorithms
36 * @dstr *e@ = where to write error tokens
37 * @key_file *kf@ = key file
38 * @key *k@ = key to inspect
39 *
40 * Returns: Zero if OK; nonzero on error.
41 *
42 * Use: Extracts an algorithm choice from a key.
43 */
44
45 static int algs_get(algswitch *a, dstr *e, key_file *kf, key *k)
46 {
47 const char *p;
48 const bulkops *bops;
49 dstr d = DSTR_INIT, dd = DSTR_INIT;
50 int rc = -1;
51
52 /* --- Hash function --- */
53
54 if ((p = key_getattr(kf, k, "hash")) == 0) p = "rmd160";
55 if ((a->h = ghash_byname(p)) == 0) {
56 a_format(e, "unknown-hash", "%s", p, A_END);
57 goto done;
58 }
59
60 /* --- Symmetric encryption for key derivation --- */
61
62 if ((p = key_getattr(kf, k, "mgf")) == 0) {
63 dstr_reset(&d);
64 dstr_putf(&d, "%s-mgf", a->h->name);
65 p = d.buf;
66 }
67 if ((a->mgf = gcipher_byname(p)) == 0) {
68 a_format(e, "unknown-mgf-cipher", "%s", p, A_END);
69 goto done;
70 }
71
72 /* --- Bulk crypto transform --- */
73
74 if ((p = key_getattr(kf, k, "bulk")) == 0) p = "v0";
75 for (bops = bulktab; bops->name && strcmp(p, bops->name) != 0; bops++);
76 if (!bops->name) {
77 a_format(e, "unknown-bulk-transform", "%s", p, A_END);
78 goto done;
79 }
80 if ((a->bulk = bops->getalgs(a, e, kf, k)) == 0) goto done;
81 a->bulk->ops = bops;
82
83 /* --- All done --- */
84
85 rc = 0;
86 done:
87 dstr_destroy(&d);
88 dstr_destroy(&dd);
89 return (rc);
90 }
91
92 /* --- @algs_check@ --- *
93 *
94 * Arguments: @algswitch *a@ = a choice of algorithms
95 * @dstr *e@ = where to write error tokens
96 * @const dhgrp *grp@ = the group we're working in
97 *
98 * Returns: Zero if OK; nonzero on error.
99 *
100 * Use: Checks an algorithm choice for sensibleness. This also
101 * derives some useful information from the choices, and you
102 * must call this before committing the algorithm selection
103 * for use by @keyset@ functions.
104 */
105
106 static int algs_check(algswitch *a, dstr *e, const dhgrp *grp)
107 {
108 a->hashsz = a->h->hashsz;
109
110 if (keysz(a->hashsz, a->mgf->keysz) != a->hashsz) {
111 a_format(e, "mgf", "%s", a->mgf->name,
112 "restrictive-key-schedule",
113 A_END);
114 return (-1);
115 }
116
117 if (a->bulk->ops->checkalgs(a->bulk, a, e)) return (-1);
118
119 return (0);
120 }
121
122 /* --- @km_samealgsp@ --- *
123 *
124 * Arguments: @const kdata *kdx, *kdy@ = two key data objects
125 *
126 * Returns: Nonzero if their two algorithm selections are the same.
127 *
128 * Use: Checks sameness of algorithm selections: used to ensure that
129 * peers are using sensible algorithms.
130 */
131
132 int km_samealgsp(const kdata *kdx, const kdata *kdy)
133 {
134 const algswitch *a = &kdx->algs, *aa = &kdy->algs;
135
136 return (kdx->grp->ops == kdy->grp->ops &&
137 kdx->grp->ops->samegrpp(kdx->grp, kdy->grp) &&
138 a->mgf == aa->mgf && a->h == aa->h &&
139 a->bulk->ops == aa->bulk->ops &&
140 a->bulk->ops->samealgsp(a->bulk, aa->bulk));
141 }
142
143 /*----- Key data and key nodes --------------------------------------------*/
144
145 typedef struct keyhalf {
146 const char *kind;
147 int (*load)(key_file *, key *, key_data *,
148 const dhops *, kdata *, dstr *, dstr *);
149 const char *kr;
150 key_file *kf;
151 fwatch w;
152 sym_table tab;
153 } keyhalf;
154
155 /* --- @kh_loadpub@, @kh_loadpriv@ --- *
156 *
157 * Arguments: @const dhops *dh@ = Diffie--Hellman operations for key type
158 * @key_file *kf@ = key file from which the key was loaded
159 * @key *k@ = the key object we're loading
160 * @key_data *d@ = the key data to load
161 * @kdata *kd@ = our key-data object to fill in
162 * @dstr *t@ = the key tag name
163 * @dstr *e@ = a string to write error tokens to
164 *
165 * Returns: Zero on success, @-1@ on error.
166 *
167 * Use: These functions handle the main difference between public and
168 * private key halves. They are responsible for setting @grp@,
169 * @k@ and @K@ appropriately in all keys, handling the mismatch
170 * between the largely half-indifferent calling code and the
171 * group-specific loading functions.
172 *
173 * The function @kh_loadpriv@ is also responsible for checking
174 * the group for goodness. We don't bother checking public
175 * keys, because each public key we actually end up using must
176 * share a group with a private key which we'll already have
177 * checked.
178 */
179
180 static int kh_loadpub(key_file *kf, key *k, key_data *d,
181 const dhops *dh, kdata *kd, dstr *t, dstr *e)
182 {
183 int rc;
184
185 if ((rc = dh->ldpub(kf, k, d, kd, t, e)) != 0)
186 goto fail_0;
187 kd->grp->ops = dh;
188 if (kd->grp->ops->checkge(kd->grp, kd->K)) {
189 a_format(e, "bad-public-group-element", A_END);
190 goto fail_1;
191 }
192 return (0);
193
194 fail_1:
195 kd->grp->ops->freege(kd->grp, kd->K);
196 kd->grp->ops->freegrp(kd->grp);
197 fail_0:
198 return (-1);
199 }
200
201 static int kh_loadpriv(key_file *kf, key *k, key_data *d,
202 const dhops *dh, kdata *kd, dstr *t, dstr *e)
203 {
204 int rc;
205 const char *err;
206 dhge *K;
207 int ok;
208
209 if ((rc = dh->ldpriv(kf, k, d, kd, t, e)) != 0)
210 goto fail_0;
211 kd->grp->ops = dh;
212 if ((err = kd->grp->ops->checkgrp(kd->grp)) != 0) {
213 a_format(e, "bad-group", "%s", err, A_END);
214 goto fail_1;
215 }
216 K = kd->grp->ops->mul(kd->grp, kd->k, 0);
217 ok = kd->grp->ops->eq(kd->grp, kd->K, K);
218 kd->grp->ops->freege(kd->grp, K);
219 if (!ok) {
220 a_format(e, "incorrect-public-key", A_END);
221 goto fail_1;
222 }
223 return (0);
224
225 fail_1:
226 kd->grp->ops->freesc(kd->grp, kd->k);
227 kd->grp->ops->freege(kd->grp, kd->K);
228 kd->grp->ops->freegrp(kd->grp);
229 fail_0:
230 return (-1);
231 }
232
233 static struct keyhalf
234 priv = { "private", kh_loadpriv },
235 pub = { "public", kh_loadpub };
236
237 /* --- @keymoan@ --- *
238 *
239 * Arguments: @const char *file@ = name of the file
240 * @int line@ = line number in file
241 * @const char *msg@ = error message
242 * @void *p@ = argument pointer (indicates which keyring)
243 *
244 * Returns: ---
245 *
246 * Use: Reports an error message about loading a key file.
247 */
248
249 static void keymoan(const char *file, int line, const char *msg, void *p)
250 {
251 keyhalf *kh = p;
252
253 if (!line) {
254 a_warn("KEYMGMT", "%s-keyring", kh->kind, "%s", file,
255 "io-error", "?ERRNO", A_END);
256 } else {
257 a_warn("KEYMGMT", "%s-keyring", kh->kind, "%s", file, "line", "%d", line,
258 "%s", msg, A_END);
259 }
260 }
261
262 /* --- @kh_reopen@ --- *
263 *
264 * Arguments: @keyhalf *kh@ = pointer to keyhalf structure
265 *
266 * Returns: Zero on success, @-1@ on error.
267 *
268 * Use: Reopens the key file for the appropriate key half. If this
269 * fails, everything is left as it was; if it succeeds, then the
270 * old file is closed (if it was non-null) and the new one put
271 * in its place.
272 */
273
274 static int kh_reopen(keyhalf *kh)
275 {
276 key_file *kf = CREATE(key_file);
277
278 if (key_open(kf, kh->kr, KOPEN_READ, keymoan, kh)) {
279 a_warn("KEYMGMT", "%s-keyring", kh->kind, "%s", kh->kr,
280 "io-error", "?ERRNO", A_END);
281 DESTROY(kf);
282 return (-1);
283 } else {
284 if (kh->kf) {
285 key_close(kh->kf);
286 DESTROY(kh->kf);
287 }
288 kh->kf = kf;
289 return (0);
290 }
291 }
292
293 /* --- @kh_init@ --- *
294 *
295 * Arguments: @keyhalf *kh@ = pointer to keyhalf structure to set up
296 * @const char *kr@ = name of the keyring file
297 *
298 * Returns: ---
299 *
300 * Use: Initialize a keyhalf structure, maintaining the private or
301 * public keys. Intended to be called during initialization:
302 * exits if there's some kind of problem.
303 */
304
305 static void kh_init(keyhalf *kh, const char *kr)
306 {
307 kh->kr = kr;
308 fwatch_init(&kh->w, kr);
309 sym_create(&kh->tab);
310 kh->kf = 0;
311
312 if (kh_reopen(kh))
313 die(EXIT_FAILURE, "failed to load %s keyring `%s'", kh->kind, kr);
314 }
315
316 /* --- @kh_load@ --- *
317 *
318 * Arguments: @keyhalf *kh@ = pointer to keyhalf
319 * @const char *tag@ = key tag to be loaded
320 * @int complainp@ = whether to complain about missing keys
321 *
322 * Returns: Pointer to a @kdata@ structure if successful, or null on
323 * failure.
324 *
325 * Use: Attempts to load a key from the current key file. This
326 * function always reads data from the file: it's used when
327 * there's a cache miss from @kh_find@, and when refreshing the
328 * known keys in @kh_refresh@. The returned kdata has a
329 * reference count of exactly 1, and has no home knode.
330 */
331
332 static kdata *kh_load(keyhalf *kh, const char *tag, int complainp)
333 {
334 dstr t = DSTR_INIT;
335 dstr e = DSTR_INIT;
336 key *k;
337 key_data **d;
338 kdata *kd;
339 const char *ty;
340 const dhops *dh;
341 T( const dhgrp *g; )
342
343 /* --- Find the key and grab its tag --- */
344
345 if (key_qtag(kh->kf, tag, &t, &k, &d)) {
346 if (complainp) {
347 a_warn("KEYMGMT", "%s-keyring", kh->kind, "%s", kh->kr,
348 "key-not-found", "%s", tag, A_END);
349 }
350 goto fail_0;
351 }
352
353 /* --- Find the key's group type and the appropriate operations --- *
354 *
355 * There are several places to look for the key type. The most obvious is
356 * the `kx-group' key attribute. But there's also the key type itself, for
357 * compatibility reasons.
358 */
359
360 ty = key_getattr(kh->kf, k, "kx-group");
361 if (!ty && strncmp(k->type, "tripe-", 6) == 0) ty = k->type + 6;
362 if (!ty) ty = "dh";
363
364 for (dh = dhtab; dh->name; dh++)
365 if (strcmp(dh->name, ty) == 0) goto founddh;
366 a_warn("KEYMGMT", "%s-keyring", kh->kind,
367 "%s", kh->kr, "key", "%s", t.buf,
368 "unknown-group-type", "%s", ty, A_END);
369 goto fail_0;
370
371 founddh:
372 kd = CREATE(kdata);
373 if (kh->load(kh->kf, k, *d, dh, kd, &t, &e)) {
374 a_warn("KEYMGMT", "%s-keyring", kh->kind,
375 "%s", kh->kr, "key", "%s", t.buf,
376 "*%s", e.buf, A_END);
377 goto fail_1;
378 }
379
380 if (algs_get(&kd->algs, &e, kh->kf, k) ||
381 (kd->k && algs_check(&kd->algs, &e, kd->grp))) {
382 a_warn("KEYMGMT", "%s-keyring", kh->kind,
383 "%s", kh->kr, "key", "%s", t.buf,
384 "*%s", e.buf, A_END);
385 goto fail_2;
386 }
387
388 kd->tag = xstrdup(t.buf);
389 kd->ref = 1;
390 kd->kn = 0;
391 kd->t_exp = k->exp;
392
393 IF_TRACING(T_KEYMGMT, {
394 trace(T_KEYMGMT, "keymgmt: loaded %s key `%s'", kh->kind, t.buf);
395 IF_TRACING(T_CRYPTO, {
396 g = kd->grp;
397 g->ops->tracegrp(g);
398 if (kd->k)
399 trace(T_CRYPTO, "crypto: k = %s", g->ops->scstr(g, kd->k));
400 trace(T_CRYPTO, "crypto: K = %s", g->ops->gestr(g, kd->K));
401 kd->algs.bulk->ops->tracealgs(kd->algs.bulk);
402 })
403 })
404
405 goto done;
406
407 fail_2:
408 if (kd->k) kd->grp->ops->freesc(kd->grp, kd->k);
409 kd->grp->ops->freege(kd->grp, kd->K);
410 kd->grp->ops->freegrp(kd->grp);
411 fail_1:
412 DESTROY(kd);
413 fail_0:
414 kd = 0;
415 done:
416 dstr_destroy(&t);
417 dstr_destroy(&e);
418 return (kd);
419 }
420
421 /* --- @kh_find@ --- *
422 *
423 * Arguments: @keyhalf *kh@ = pointer to the keyhalf
424 * @const char *tag@ = key to be obtained
425 * @int complainp@ = whether to complain about missing keys
426 *
427 * Returns: A pointer to the kdata, or null on error.
428 *
429 * Use: Obtains kdata, maybe from the cache. This won't update a
430 * stale cache entry, though @kh_refresh@ ought to have done
431 * that already. The returned kdata object may be shared with
432 * other users. (One of this function's responsibilities, over
433 * @kh_load@, is to set the home knode of a freshly loaded
434 * kdata.)
435 */
436
437 static kdata *kh_find(keyhalf *kh, const char *tag, int complainp)
438 {
439 knode *kn;
440 kdata *kd;
441 unsigned f;
442
443 kn = sym_find(&kh->tab, tag, -1, sizeof(knode), &f);
444
445 if (f) {
446 if (kn->f & KNF_BROKEN) {
447 T( if (complainp)
448 trace(T_KEYMGMT, "keymgmt: key `%s' marked as broken", tag); )
449 return (0);
450 }
451
452 kd = kn->kd;
453 if (kd) kd->ref++;
454 T( trace(T_KEYMGMT, "keymgmt: %scache hit for key `%s'",
455 kd ? "" : "negative ", tag); )
456 return (kd);
457 } else {
458 kd = kh_load(kh, tag, complainp);
459 kn->kd = kd;
460 kn->kh = kh;
461 kn->f = 0;
462 if (!kd)
463 kn->f |= KNF_BROKEN;
464 else {
465 kd->kn = kn;
466 kd->ref++;
467 }
468 return (kd);
469 }
470 }
471
472 /* --- @kh_refresh@ --- *
473 *
474 * Arguments: @keyhalf *kh@ = pointer to the keyhalf
475 *
476 * Returns: Zero if nothing needs to be done; nonzero if peers should
477 * refresh their keys.
478 *
479 * Use: Refreshes cached keys from files.
480 *
481 * Each active knode is examined to see if a new key is
482 * available: the return value is nonzero if any new keys are.
483 * A key is considered new if its algorithms, public key, or
484 * expiry time are/is different.
485 *
486 * Stub knodes (with no kdata attached) are removed, so that a
487 * later retry can succeed if the file has been fixed. (This
488 * doesn't count as a change, since no peers should be relying
489 * on a nonexistent key.)
490 */
491
492 static int kh_refresh(keyhalf *kh)
493 {
494 knode *kn;
495 kdata *kd;
496 sym_iter i;
497 int changep = 0;
498
499 if (!fwatch_update(&kh->w, kh->kr) || kh_reopen(kh))
500 return (0);
501
502 T( trace(T_KEYMGMT, "keymgmt: rescan %s keyring `%s'", kh->kind, kh->kr); )
503 for (sym_mkiter(&i, &kh->tab); (kn = sym_next(&i)) != 0; ) {
504 if (!kn->kd) {
505 T( trace(T_KEYMGMT, "keymgmt: discard stub entry for key `%s'",
506 SYM_NAME(kn)); )
507 sym_remove(&kh->tab, kn);
508 continue;
509 }
510 if ((kd = kh_load(kh, SYM_NAME(kn), 1)) == 0) {
511 if (!(kn->f & KNF_BROKEN)) {
512 T( trace(T_KEYMGMT, "keymgmt: failed to load new key `%s': "
513 "marking it as broken",
514 SYM_NAME(kn)); )
515 kn->f |= KNF_BROKEN;
516 }
517 continue;
518 }
519 kn->f &= ~KNF_BROKEN;
520 if (kd->t_exp == kn->kd->t_exp &&
521 km_samealgsp(kd, kn->kd) &&
522 kd->grp->ops->eq(kd->grp, kd->K, kn->kd->K)) {
523 T( trace(T_KEYMGMT, "keymgmt: key `%s' unchanged", SYM_NAME(kn)); )
524 continue;
525 }
526 T( trace(T_KEYMGMT, "keymgmt: loaded new version of key `%s'",
527 SYM_NAME(kn)); )
528 km_unref(kn->kd);
529 kd->kn = kn;
530 kn->kd = kd;
531 changep = 1;
532 }
533
534 return (changep);
535 }
536
537 /*----- Main code ---------------------------------------------------------*/
538
539 const char *tag_priv;
540 kdata *master;
541
542 /* --- @km_init@ --- *
543 *
544 * Arguments: @const char *privkr@ = private keyring file
545 * @const char *pubkr@ = public keyring file
546 * @const char *ptag@ = default private-key tag
547 *
548 * Returns: ---
549 *
550 * Use: Initializes the key-management machinery, loading the
551 * keyrings and so on.
552 */
553
554 void km_init(const char *privkr, const char *pubkr, const char *ptag)
555 {
556 const gchash *const *hh;
557
558 for (hh = ghashtab; *hh; hh++) {
559 if ((*hh)->hashsz > MAXHASHSZ) {
560 die(EXIT_FAILURE, "INTERNAL ERROR: %s hash length %lu > MAXHASHSZ %d",
561 (*hh)->name, (unsigned long)(*hh)->hashsz, MAXHASHSZ);
562 }
563 }
564
565 kh_init(&priv, privkr);
566 kh_init(&pub, pubkr);
567
568 tag_priv = ptag;
569 if ((master = km_findpriv(ptag)) == 0) exit(EXIT_FAILURE);
570 }
571
572 /* --- @km_reload@ --- *
573 *
574 * Arguments: ---
575 *
576 * Returns: Zero if OK, nonzero to force reloading of keys.
577 *
578 * Use: Checks the keyrings to see if they need reloading.
579 */
580
581 int km_reload(void)
582 {
583 int changep = 0;
584 kdata *kd;
585
586 if (kh_refresh(&priv)) {
587 changep = 1;
588 kd = master->kn->kd;
589 if (kd != master) {
590 km_unref(master);
591 km_ref(kd);
592 master = kd;
593 }
594 }
595 if (kh_refresh(&pub))
596 changep = 1;
597 return (changep);
598 }
599
600 /* --- @km_findpub@, @km_findpriv@ --- *
601 *
602 * Arguments: @const char *tag@ = key tag to load
603 *
604 * Returns: Pointer to the kdata object if successful, or null on error.
605 *
606 * Use: Fetches a public or private key from the keyring.
607 */
608
609 kdata *km_findpub(const char *tag) { return (kh_find(&pub, tag, 1)); }
610
611 kdata *km_findpriv(const char *tag)
612 {
613 kdata *kd;
614
615 /* Unpleasantness for the sake of compatibility. */
616 if (!tag && (kd = kh_find(&priv, "tripe", 0)) != 0) return (kd);
617 else return (kh_find(&priv, tag ? tag : "tripe-dh", 1));
618 }
619
620 /* --- @km_tag@ --- *
621 *
622 * Arguments: @kdata *kd@ - pointer to the kdata object
623 *
624 * Returns: A pointer to the short tag by which the kdata was loaded.
625 */
626
627 const char *km_tag(kdata *kd) { return (SYM_NAME(kd->kn)); }
628
629 /* --- @km_ref@ --- *
630 *
631 * Arguments: @kdata *kd@ = pointer to the kdata object
632 *
633 * Returns: ---
634 *
635 * Use: Claim a new reference to a kdata object.
636 */
637
638 void km_ref(kdata *kd) { kd->ref++; }
639
640 /* --- @km_unref@ --- *
641 *
642 * Arguments: @kdata *kd@ = pointer to the kdata object
643 *
644 * Returns: ---
645 *
646 * Use: Releases a reference to a kdata object.
647 */
648
649 void km_unref(kdata *kd)
650 {
651 if (--kd->ref) return;
652 if (kd->k) kd->grp->ops->freesc(kd->grp, kd->k);
653 kd->grp->ops->freege(kd->grp, kd->K);
654 kd->grp->ops->freegrp(kd->grp);
655 xfree(kd->tag);
656 DESTROY(kd);
657 }
658
659 /*----- That's all, folks -------------------------------------------------*/