Commit | Line | Data |
---|---|---|
410c8acf | 1 | /* -*-c-*- |
410c8acf | 2 | * |
3 | * Key loading and storing | |
4 | * | |
5 | * (c) 2001 Straylight/Edgeware | |
6 | */ | |
7 | ||
e04c2d50 | 8 | /*----- Licensing notice --------------------------------------------------* |
410c8acf | 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. | |
e04c2d50 | 16 | * |
410c8acf | 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. | |
e04c2d50 | 21 | * |
410c8acf | 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 | ||
410c8acf | 27 | /*----- Header files ------------------------------------------------------*/ |
28 | ||
29 | #include "tripe.h" | |
30 | ||
52c03a2a | 31 | /*----- Key groups --------------------------------------------------------*/ |
32 | ||
799e58b9 MW |
33 | /* The key-loading functions here must fill in the kdata slot @g@ and |
34 | * either @kpriv@ or @kpub@ as appropriate. The caller will take care of | |
35 | * determining @kpub@ given a private key, and of ensuring that @kpriv@ is | |
36 | * null for a public key. | |
37 | */ | |
38 | ||
52c03a2a | 39 | typedef struct kgops { |
40 | const char *ty; | |
799e58b9 MW |
41 | int (*loadpriv)(key_data *, kdata *, dstr *, dstr *); |
42 | int (*loadpub)(key_data *, kdata *, dstr *, dstr *); | |
52c03a2a | 43 | } kgops; |
44 | ||
45 | /* --- Diffie-Hellman --- */ | |
46 | ||
799e58b9 | 47 | static int kgdh_priv(key_data *d, kdata *kd, dstr *t, dstr *e) |
52c03a2a | 48 | { |
49 | key_packstruct kps[DH_PRIVFETCHSZ]; | |
50 | key_packdef *kp; | |
51 | dh_priv dp; | |
52c03a2a | 52 | int rc; |
53 | ||
54 | kp = key_fetchinit(dh_privfetch, kps, &dp); | |
799e58b9 | 55 | if ((rc = key_unpack(kp, d, t)) != 0) { |
4d36660a MW |
56 | a_format(e, "unpack-failed", "%s", key_strerror(rc), A_END); |
57 | goto fail_0; | |
52c03a2a | 58 | } |
799e58b9 MW |
59 | kd->g = group_prime(&dp.dp); |
60 | kd->kpriv = MP_COPY(dp.x); | |
4d36660a MW |
61 | rc = 0; |
62 | goto done; | |
63 | fail_0: | |
64 | rc = -1; | |
52c03a2a | 65 | done: |
66 | key_fetchdone(kp); | |
4d36660a | 67 | return (rc); |
52c03a2a | 68 | } |
69 | ||
799e58b9 | 70 | static int kgdh_pub(key_data *d, kdata *kd, dstr *t, dstr *e) |
52c03a2a | 71 | { |
72 | key_packstruct kps[DH_PUBFETCHSZ]; | |
73 | key_packdef *kp; | |
74 | dh_pub dp; | |
52c03a2a | 75 | int rc; |
76 | ||
77 | kp = key_fetchinit(dh_pubfetch, kps, &dp); | |
799e58b9 | 78 | if ((rc = key_unpack(kp, d, t)) != 0) { |
4d36660a MW |
79 | a_format(e, "unpack-failed", "%s", key_strerror(rc), A_END); |
80 | goto fail_0; | |
52c03a2a | 81 | } |
799e58b9 MW |
82 | kd->g = group_prime(&dp.dp); |
83 | kd->kpub = G_CREATE(kd->g); | |
84 | if (G_FROMINT(kd->g, kd->kpub, dp.y)) { | |
4d36660a MW |
85 | a_format(e, "bad-public-vector", A_END); |
86 | goto fail_1; | |
52c03a2a | 87 | } |
4d36660a MW |
88 | rc = 0; |
89 | goto done; | |
90 | fail_1: | |
799e58b9 MW |
91 | G_DESTROY(kd->g, kd->kpub); |
92 | G_DESTROYGROUP(kd->g); | |
4d36660a MW |
93 | fail_0: |
94 | rc = -1; | |
52c03a2a | 95 | done: |
96 | key_fetchdone(kp); | |
4d36660a | 97 | return (rc); |
52c03a2a | 98 | } |
99 | ||
fc5f4823 | 100 | static const kgops kgdh_ops = { "dh", kgdh_priv, kgdh_pub }; |
52c03a2a | 101 | |
102 | /* --- Elliptic curve --- */ | |
103 | ||
799e58b9 | 104 | static int kgec_priv(key_data *d, kdata *kd, dstr *t, dstr *e) |
52c03a2a | 105 | { |
106 | key_packstruct kps[EC_PRIVFETCHSZ]; | |
107 | key_packdef *kp; | |
108 | ec_priv ep; | |
109 | ec_info ei; | |
4d36660a | 110 | const char *err; |
52c03a2a | 111 | int rc; |
112 | ||
113 | kp = key_fetchinit(ec_privfetch, kps, &ep); | |
799e58b9 | 114 | if ((rc = key_unpack(kp, d, t)) != 0) { |
4d36660a MW |
115 | a_format(e, "unpack-failed", "%s", key_strerror(rc), A_END); |
116 | goto fail_0; | |
117 | } | |
118 | if ((err = ec_getinfo(&ei, ep.cstr)) != 0) { | |
119 | a_format(e, "decode-failed", "%s", err, A_END); | |
120 | goto fail_0; | |
52c03a2a | 121 | } |
799e58b9 MW |
122 | kd->g = group_ec(&ei); |
123 | kd->kpriv = MP_COPY(ep.x); | |
4d36660a MW |
124 | rc = 0; |
125 | goto done; | |
126 | fail_0: | |
127 | rc = -1; | |
52c03a2a | 128 | done: |
129 | key_fetchdone(kp); | |
4d36660a | 130 | return (rc); |
52c03a2a | 131 | } |
132 | ||
799e58b9 | 133 | static int kgec_pub(key_data *d, kdata *kd, dstr *t, dstr *e) |
52c03a2a | 134 | { |
135 | key_packstruct kps[EC_PUBFETCHSZ]; | |
136 | key_packdef *kp; | |
137 | ec_pub ep; | |
138 | ec_info ei; | |
4d36660a | 139 | const char *err; |
52c03a2a | 140 | int rc; |
141 | ||
142 | kp = key_fetchinit(ec_pubfetch, kps, &ep); | |
799e58b9 | 143 | if ((rc = key_unpack(kp, d, t)) != 0) { |
4d36660a MW |
144 | a_format(e, "unpack-failed", "%s", key_strerror(rc), A_END); |
145 | goto fail_0; | |
146 | } | |
147 | if ((err = ec_getinfo(&ei, ep.cstr)) != 0) { | |
148 | a_format(e, "decode-failed", "%s", err, A_END); | |
149 | goto fail_0; | |
52c03a2a | 150 | } |
799e58b9 MW |
151 | kd->g = group_ec(&ei); |
152 | kd->kpub = G_CREATE(kd->g); | |
153 | if (G_FROMEC(kd->g, kd->kpub, &ep.p)) { | |
4d36660a MW |
154 | a_format(e, "bad-public-vector", A_END); |
155 | goto fail_1; | |
52c03a2a | 156 | } |
4d36660a MW |
157 | rc = 0; |
158 | goto done; | |
159 | fail_1: | |
799e58b9 MW |
160 | G_DESTROY(kd->g, kd->kpub); |
161 | G_DESTROYGROUP(kd->g); | |
4d36660a MW |
162 | fail_0: |
163 | rc = -1; | |
52c03a2a | 164 | done: |
165 | key_fetchdone(kp); | |
4d36660a | 166 | return (rc); |
52c03a2a | 167 | } |
168 | ||
fc5f4823 | 169 | static const kgops kgec_ops = { "ec", kgec_priv, kgec_pub }; |
52c03a2a | 170 | |
171 | /* --- Table of supported key types --- */ | |
172 | ||
173 | static const kgops *kgtab[] = { &kgdh_ops, &kgec_ops, 0 }; | |
174 | ||
b5c45da1 | 175 | /*----- Algswitch stuff ---------------------------------------------------*/ |
176 | ||
177 | /* --- @algs_get@ --- * | |
178 | * | |
179 | * Arguments: @algswitch *a@ = where to put the algorithms | |
4d36660a MW |
180 | * @dstr *e@ = where to write errror tokens |
181 | * @key_file *kf@ = key file | |
b5c45da1 | 182 | * @key *k@ = key to inspect |
183 | * | |
4d36660a | 184 | * Returns: Zero if OK; nonzero on error. |
b5c45da1 | 185 | * |
186 | * Use: Extracts an algorithm choice from a key. | |
187 | */ | |
188 | ||
4d36660a | 189 | static int algs_get(algswitch *a, dstr *e, key_file *kf, key *k) |
b5c45da1 | 190 | { |
191 | const char *p; | |
4d36660a | 192 | char *q, *qq; |
b5c45da1 | 193 | dstr d = DSTR_INIT; |
4d36660a | 194 | int rc = -1; |
b5c45da1 | 195 | |
4d36660a | 196 | /* --- Symmetric encryption for bulk data --- */ |
b5c45da1 | 197 | |
4d36660a MW |
198 | if ((p = key_getattr(kf, k, "cipher")) == 0) p = "blowfish-cbc"; |
199 | if ((a->c = gcipher_byname(p)) == 0) { | |
200 | a_format(e, "unknown-cipher", "%s", p, A_END); | |
201 | goto done; | |
202 | } | |
203 | ||
204 | /* --- Hash function --- */ | |
205 | ||
206 | if ((p = key_getattr(kf, k, "hash")) == 0) p = "rmd160"; | |
207 | if ((a->h = ghash_byname(p)) == 0) { | |
208 | a_format(e, "unknown-hash", "%s", p, A_END); | |
209 | goto done; | |
210 | } | |
b5c45da1 | 211 | |
4d36660a | 212 | /* --- Symmetric encryption for key derivation --- */ |
b5c45da1 | 213 | |
74ee77cb | 214 | if ((p = key_getattr(kf, k, "mgf")) == 0) { |
b5c45da1 | 215 | dstr_reset(&d); |
74ee77cb | 216 | dstr_putf(&d, "%s-mgf", a->h->name); |
b5c45da1 | 217 | p = d.buf; |
218 | } | |
4d36660a MW |
219 | if ((a->mgf = gcipher_byname(p)) == 0) { |
220 | a_format(e, "unknown-mgf-cipher", "%s", p, A_END); | |
221 | goto done; | |
222 | } | |
223 | ||
224 | /* --- Message authentication for bulk data --- */ | |
b5c45da1 | 225 | |
226 | if ((p = key_getattr(kf, k, "mac")) != 0) { | |
227 | dstr_reset(&d); | |
228 | dstr_puts(&d, p); | |
229 | if ((q = strchr(d.buf, '/')) != 0) | |
230 | *q++ = 0; | |
4d36660a MW |
231 | if ((a->m = gmac_byname(d.buf)) == 0) { |
232 | a_format(e, "unknown-mac", "%s", d.buf, A_END); | |
233 | goto done; | |
234 | } | |
b5c45da1 | 235 | if (!q) |
236 | a->tagsz = a->m->hashsz; | |
237 | else { | |
4d36660a MW |
238 | unsigned long n = strtoul(q, &qq, 0); |
239 | if (*qq) { | |
240 | a_format(e, "bad-tag-length-string", "%s", q, A_END); | |
241 | goto done; | |
242 | } | |
243 | if (n%8 || n/8 > a->m->hashsz) { | |
244 | a_format(e, "bad-tag-length", "%lu", n, A_END); | |
245 | goto done; | |
246 | } | |
b5c45da1 | 247 | a->tagsz = n/8; |
248 | } | |
249 | } else { | |
250 | dstr_reset(&d); | |
251 | dstr_putf(&d, "%s-hmac", a->h->name); | |
4d36660a MW |
252 | if ((a->m = gmac_byname(d.buf)) == 0) { |
253 | a_format(e, "no-hmac-for-hash", "%s", a->h->name, A_END); | |
254 | goto done; | |
255 | } | |
b5c45da1 | 256 | a->tagsz = a->h->hashsz/2; |
257 | } | |
258 | ||
4d36660a | 259 | rc = 0; |
b5c45da1 | 260 | done: |
261 | dstr_destroy(&d); | |
4d36660a | 262 | return (rc); |
b5c45da1 | 263 | } |
264 | ||
265 | /* --- @algs_check@ --- * | |
266 | * | |
267 | * Arguments: @algswitch *a@ = a choice of algorithms | |
4d36660a | 268 | * @dstr *e@ = where to write error tokens |
b5c45da1 | 269 | * @const group *g@ = the group we're working in |
270 | * | |
4d36660a | 271 | * Returns: Zero if OK; nonzero on error. |
b5c45da1 | 272 | * |
273 | * Use: Checks an algorithm choice for sensibleness. This also | |
274 | * derives some useful information from the choices, and you | |
275 | * must call this before committing the algorithm selection | |
276 | * for use by @keyset@ functions. | |
277 | */ | |
278 | ||
4d36660a | 279 | static int algs_check(algswitch *a, dstr *e, const group *g) |
b5c45da1 | 280 | { |
281 | /* --- Derive the key sizes --- * | |
282 | * | |
283 | * Must ensure that we have non-empty keys. This isn't ideal, but it | |
383a9d71 MW |
284 | * provides a handy sanity check. Also must be based on a 64- or 128-bit |
285 | * block cipher or we can't do the data expiry properly. | |
b5c45da1 | 286 | */ |
287 | ||
288 | a->hashsz = a->h->hashsz; | |
4d36660a MW |
289 | if ((a->cksz = keysz(a->hashsz, a->c->keysz)) == 0) { |
290 | a_format(e, "cipher", "%s", a->c->name, | |
291 | "no-key-size", "%lu", (unsigned long)a->hashsz, | |
292 | A_END); | |
293 | return (-1); | |
294 | } | |
295 | if ((a->mksz = keysz(a->hashsz, a->m->keysz)) == 0) { | |
296 | a_format(e, "mac", "%s", a->m->name, | |
297 | "no-key-size", "%lu", (unsigned long)a->hashsz, | |
298 | A_END); | |
299 | return (-1); | |
300 | } | |
b5c45da1 | 301 | |
383a9d71 MW |
302 | /* --- Derive the data limit --- */ |
303 | ||
304 | if (a->c->blksz < 16) a->expsz = MEG(64); | |
305 | else a->expsz = MEG(2048); | |
306 | ||
b5c45da1 | 307 | /* --- Ensure the MGF accepts hashes as keys --- */ |
308 | ||
4d36660a MW |
309 | if (keysz(a->hashsz, a->mgf->keysz) != a->hashsz) { |
310 | a_format(e, "mgf", "%s", a->mgf->name, | |
311 | "restrictive-key-schedule", | |
312 | A_END); | |
313 | return (-1); | |
314 | } | |
b5c45da1 | 315 | |
316 | /* --- All ship-shape and Bristol-fashion --- */ | |
317 | ||
318 | return (0); | |
319 | } | |
320 | ||
799e58b9 | 321 | /* --- @km_samealgsp@ --- * |
b5c45da1 | 322 | * |
799e58b9 | 323 | * Arguments: @const kdata *kdx, *kdy@ = two key data objects |
b5c45da1 | 324 | * |
799e58b9 | 325 | * Returns: Nonzero if their two algorithm selections are the same. |
b5c45da1 | 326 | * |
327 | * Use: Checks sameness of algorithm selections: used to ensure that | |
328 | * peers are using sensible algorithms. | |
329 | */ | |
330 | ||
799e58b9 | 331 | int km_samealgsp(const kdata *kdx, const kdata *kdy) |
b5c45da1 | 332 | { |
799e58b9 MW |
333 | const algswitch *a = &kdx->algs, *aa = &kdy->algs; |
334 | ||
335 | return (group_samep(kdx->g, kdy->g) && a->c == aa->c && | |
336 | a->mgf == aa->mgf && a->h == aa->h && | |
b5c45da1 | 337 | a->m == aa->m && a->tagsz == aa->tagsz); |
338 | } | |
339 | ||
799e58b9 MW |
340 | /*----- Key data and key nodes --------------------------------------------*/ |
341 | ||
342 | typedef struct keyhalf { | |
343 | const char *kind; | |
344 | int (*load)(const kgops *, key_data *, kdata *, dstr *, dstr *); | |
345 | const char *kr; | |
346 | key_file *kf; | |
347 | fwatch w; | |
348 | sym_table tab; | |
349 | } keyhalf; | |
350 | ||
351 | /* --- @kh_loadpub@, @kh_loadpriv@ --- * | |
352 | * | |
353 | * Arguments: @const kgops *ko@ = key-group operations for key type | |
354 | * @key_data *d@ = key data object as stored in keyring | |
355 | * @kdata *kd@ = our key-data object to fill in | |
356 | * @dstr *t@ = the key tag name | |
357 | * @dstr *e@ = a string to write error tokens to | |
358 | * | |
359 | * Returns: Zero on success, @-1@ on error. | |
360 | * | |
361 | * Use: These functions handle the main difference between public and | |
362 | * private key halves. They are responsible for setting @g@, | |
363 | * @kpriv@ and @kpub@ appropriately in all keys, handling the | |
364 | * mismatch between the largely half-indifferent calling code | |
365 | * and the group-specific loading functions. | |
366 | * | |
367 | * The function @kh_loadpriv@ is also responsible for checking | |
368 | * the group for goodness. We don't bother checking public | |
369 | * keys, because each public key we actually end up using must | |
370 | * share a group with a private key which we'll already have | |
371 | * checked. | |
372 | */ | |
373 | ||
374 | static int kh_loadpub(const kgops *ko, key_data *d, kdata *kd, | |
375 | dstr *t, dstr *e) | |
376 | { | |
377 | int rc; | |
378 | ||
379 | if ((rc = ko->loadpub(d, kd, t, e)) != 0) | |
380 | goto fail_0; | |
381 | if (group_check(kd->g, kd->kpub)) { | |
382 | a_format(e, "bad-public-group-element"); | |
383 | goto fail_1; | |
384 | } | |
385 | kd->kpriv = 0; | |
386 | return (0); | |
387 | ||
388 | fail_1: | |
389 | G_DESTROY(kd->g, kd->kpub); | |
390 | G_DESTROYGROUP(kd->g); | |
391 | fail_0: | |
392 | return (-1); | |
393 | } | |
394 | ||
395 | static int kh_loadpriv(const kgops *ko, key_data *d, kdata *kd, | |
396 | dstr *t, dstr *e) | |
397 | { | |
398 | int rc; | |
399 | const char *err; | |
400 | ||
401 | if ((rc = ko->loadpriv(d, kd, t, e)) != 0) | |
402 | goto fail_0; | |
403 | if ((err = G_CHECK(kd->g, &rand_global)) != 0) { | |
404 | a_format(e, "bad-group", "%s", err, A_END); | |
405 | goto fail_1; | |
406 | } | |
407 | kd->kpub = G_CREATE(kd->g); | |
408 | G_EXP(kd->g, kd->kpub, kd->g->g, kd->kpriv); | |
409 | return (0); | |
410 | ||
411 | fail_1: | |
412 | mp_drop(kd->kpriv); | |
413 | G_DESTROYGROUP(kd->g); | |
414 | fail_0: | |
415 | return (-1); | |
416 | } | |
417 | ||
418 | static struct keyhalf | |
419 | priv = { "private", kh_loadpriv }, | |
420 | pub = { "public", kh_loadpub }; | |
410c8acf | 421 | |
422 | /* --- @keymoan@ --- * | |
423 | * | |
56814747 | 424 | * Arguments: @const char *file@ = name of the file |
e04c2d50 MW |
425 | * @int line@ = line number in file |
426 | * @const char *msg@ = error message | |
4d36660a | 427 | * @void *p@ = argument pointer (indicates which keyring) |
410c8acf | 428 | * |
e04c2d50 | 429 | * Returns: --- |
410c8acf | 430 | * |
e04c2d50 | 431 | * Use: Reports an error message about loading a key file. |
410c8acf | 432 | */ |
433 | ||
434 | static void keymoan(const char *file, int line, const char *msg, void *p) | |
f43df819 | 435 | { |
799e58b9 | 436 | keyhalf *kh = p; |
4d36660a MW |
437 | |
438 | if (!line) { | |
799e58b9 | 439 | a_warn("KEYMGMT", "%s-keyring", kh->kind, "%s", file, |
4d36660a MW |
440 | "io-error", "?ERRNO", A_END); |
441 | } else { | |
799e58b9 | 442 | a_warn("KEYMGMT", "%s-keyring", kh->kind, "%s", file, "line", "%d", line, |
4d36660a MW |
443 | "%s", msg, A_END); |
444 | } | |
f43df819 | 445 | } |
410c8acf | 446 | |
799e58b9 | 447 | /* --- @kh_reopen@ --- * |
fc5f4823 | 448 | * |
799e58b9 | 449 | * Arguments: @keyhalf *kh@ = pointer to keyhalf structure |
fc5f4823 | 450 | * |
799e58b9 | 451 | * Returns: Zero on success, @-1@ on error. |
fc5f4823 | 452 | * |
799e58b9 MW |
453 | * Use: Reopens the key file for the appropriate key half. If this |
454 | * fails, everything is left as it was; if it succeeds, then the | |
455 | * old file is closed (if it was non-null) and the new one put | |
456 | * in its place. | |
fc5f4823 MW |
457 | */ |
458 | ||
799e58b9 | 459 | static int kh_reopen(keyhalf *kh) |
fc5f4823 | 460 | { |
799e58b9 | 461 | key_file *kf = CREATE(key_file); |
fc5f4823 | 462 | |
799e58b9 MW |
463 | if (key_open(kf, kh->kr, KOPEN_READ, keymoan, kh)) { |
464 | a_warn("KEYMGMT", "%s-keyring", kh->kind, "%s", kh->kr, | |
465 | "read-error", "?ERRNO", A_END); | |
466 | DESTROY(kf); | |
467 | return (-1); | |
468 | } else { | |
469 | if (kh->kf) { | |
470 | key_close(kh->kf); | |
471 | DESTROY(kh->kf); | |
472 | } | |
473 | kh->kf = kf; | |
474 | return (0); | |
475 | } | |
476 | } | |
fc5f4823 | 477 | |
799e58b9 MW |
478 | /* --- @kh_init@ --- * |
479 | * | |
480 | * Arguments: @keyhalf *kh@ = pointer to keyhalf structure to set up | |
481 | * @const char *kr@ = name of the keyring file | |
482 | * | |
483 | * Returns: --- | |
484 | * | |
485 | * Use: Initialize a keyhalf structure, maintaining the private or | |
486 | * public keys. Intended to be called during initialization: | |
487 | * exits if there's some kind of problem. | |
488 | */ | |
fc5f4823 | 489 | |
799e58b9 MW |
490 | static void kh_init(keyhalf *kh, const char *kr) |
491 | { | |
492 | kh->kr = kr; | |
493 | fwatch_init(&kh->w, kr); | |
494 | sym_create(&kh->tab); | |
495 | kh->kf = 0; | |
496 | ||
497 | if (kh_reopen(kh)) | |
498 | die(EXIT_FAILURE, "failed to load %s keyring `%s'", kh->kind, kr); | |
fc5f4823 MW |
499 | } |
500 | ||
799e58b9 | 501 | /* --- @kh_load@ --- * |
410c8acf | 502 | * |
799e58b9 MW |
503 | * Arguments: @keyhalf *kh@ = pointer to keyhalf |
504 | * @const char *tag@ = key tag to be loaded | |
505 | * @int complainp@ = whether to complain about missing keys | |
410c8acf | 506 | * |
799e58b9 MW |
507 | * Returns: Pointer to a @kdata@ structure if successful, or null on |
508 | * failure. | |
410c8acf | 509 | * |
799e58b9 MW |
510 | * Use: Attempts to load a key from the current key file. This |
511 | * function always reads data from the file: it's used when | |
512 | * there's a cache miss from @kh_find@, and when refreshing the | |
513 | * known keys in @kh_refresh@. The returned kdata has a | |
514 | * reference count of exactly 1, and has no home knode. | |
410c8acf | 515 | */ |
516 | ||
799e58b9 | 517 | static kdata *kh_load(keyhalf *kh, const char *tag, int complainp) |
410c8acf | 518 | { |
52c03a2a | 519 | dstr t = DSTR_INIT; |
4d36660a | 520 | dstr e = DSTR_INIT; |
799e58b9 MW |
521 | key *k; |
522 | key_data **d; | |
523 | kdata *kd; | |
524 | const char *ty; | |
525 | const kgops **ko; | |
52c03a2a | 526 | |
799e58b9 | 527 | /* --- Find the key and grab its tag --- */ |
52c03a2a | 528 | |
799e58b9 MW |
529 | if (key_qtag(kh->kf, tag, &t, &k, &d)) { |
530 | if (complainp) { | |
531 | a_warn("KEYMGMT", "%s-keyring", kh->kind, "%s", kh->kr, | |
532 | "key-not-found", "%s", tag, A_END); | |
533 | } | |
534 | goto fail_0; | |
52c03a2a | 535 | } |
536 | ||
799e58b9 MW |
537 | /* --- Find the key's group type and the appropriate operations --- * |
538 | * | |
539 | * There are several places to look for the key type. The most obvious is | |
540 | * the `kx-group' key attribute. But there's also the key type itself, for | |
541 | * compatibility reasons. | |
542 | */ | |
52c03a2a | 543 | |
799e58b9 MW |
544 | ty = key_getattr(kh->kf, k, "kx-group"); |
545 | if (!ty && strncmp(k->type, "tripe-", 6) == 0) ty = k->type + 6; | |
546 | if (!ty) ty = "dh"; | |
52c03a2a | 547 | |
799e58b9 MW |
548 | for (ko = kgtab; *ko; ko++) |
549 | if (strcmp((*ko)->ty, ty) == 0) goto foundko; | |
550 | a_warn("KEYMGMT", "%s-keyring", kh->kind, | |
551 | "%s", kh->kr, "key", "%s", t.buf, | |
552 | "unknown-group-type", "%s", ty, A_END); | |
553 | goto fail_0; | |
554 | ||
555 | foundko: | |
556 | kd = CREATE(kdata); | |
557 | if (kh->load(*ko, *d, kd, &t, &e)) { | |
558 | a_warn("KEYMGMT", "%s-keyring", kh->kind, | |
559 | "%s", kh->kr, "key" "%s", t.buf, | |
4d36660a | 560 | "*%s", e.buf, A_END); |
799e58b9 | 561 | goto fail_1; |
52c03a2a | 562 | } |
563 | ||
799e58b9 MW |
564 | if (algs_get(&kd->algs, &e, kh->kf, k) || |
565 | (kd->kpriv && algs_check(&kd->algs, &e, kd->g))) { | |
566 | a_warn("KEYMGMT", "%s-keyring", kh->kind, | |
567 | "%s", kh->kr, "key", "%s", t.buf, | |
4d36660a | 568 | "*%s", e.buf, A_END); |
799e58b9 | 569 | goto fail_2; |
410c8acf | 570 | } |
52c03a2a | 571 | |
799e58b9 MW |
572 | kd->tag = xstrdup(t.buf); |
573 | kd->indexsz = mp_octets(kd->g->r); | |
574 | kd->ref = 1; | |
575 | kd->kn = 0; | |
576 | kd->t_exp = k->exp; | |
52c03a2a | 577 | |
578 | IF_TRACING(T_KEYMGMT, { | |
799e58b9 | 579 | trace(T_KEYMGMT, "keymgmt: loaded %s key `%s'", kh->kind, t.buf); |
52c03a2a | 580 | IF_TRACING(T_CRYPTO, { |
799e58b9 MW |
581 | trace(T_CRYPTO, "crypto: r = %s", mpstr(kd->g->r)); |
582 | trace(T_CRYPTO, "crypto: h = %s", mpstr(kd->g->h)); | |
583 | if (kd->kpriv) | |
584 | trace(T_CRYPTO, "crypto: x = %s", mpstr(kd->kpriv)); | |
585 | trace(T_CRYPTO, "crypto: cipher = %s", kd->algs.c->name); | |
586 | trace(T_CRYPTO, "crypto: mgf = %s", kd->algs.mgf->name); | |
587 | trace(T_CRYPTO, "crypto: hash = %s", kd->algs.h->name); | |
b5c45da1 | 588 | trace(T_CRYPTO, "crypto: mac = %s/%lu", |
799e58b9 | 589 | kd->algs.m->name, (unsigned long)kd->algs.tagsz * 8); |
52c03a2a | 590 | }) |
591 | }) | |
592 | ||
799e58b9 | 593 | goto done; |
52c03a2a | 594 | |
799e58b9 MW |
595 | fail_2: |
596 | if (kd->kpriv) mp_drop(kd->kpriv); | |
597 | G_DESTROY(kd->g, kd->kpub); | |
598 | G_DESTROYGROUP(kd->g); | |
599 | fail_1: | |
600 | DESTROY(kd); | |
601 | fail_0: | |
602 | kd = 0; | |
603 | done: | |
52c03a2a | 604 | dstr_destroy(&t); |
4d36660a | 605 | dstr_destroy(&e); |
799e58b9 | 606 | return (kd); |
410c8acf | 607 | } |
608 | ||
799e58b9 | 609 | /* --- @kh_find@ --- * |
410c8acf | 610 | * |
799e58b9 MW |
611 | * Arguments: @keyhalf *kh@ = pointer to the keyhalf |
612 | * @const char *tag@ = key to be obtained | |
613 | * @int complainp@ = whether to complain about missing keys | |
410c8acf | 614 | * |
799e58b9 | 615 | * Returns: A pointer to the kdata, or null on error. |
410c8acf | 616 | * |
799e58b9 MW |
617 | * Use: Obtains kdata, maybe from the cache. This won't update a |
618 | * stale cache entry, though @kh_refresh@ ought to have done | |
619 | * that already. The returned kdata object may be shared with | |
620 | * other users. (One of this function's responsibilities, over | |
621 | * @kh_load@, is to set the home knode of a freshly loaded | |
622 | * kdata.) | |
410c8acf | 623 | */ |
624 | ||
799e58b9 | 625 | static kdata *kh_find(keyhalf *kh, const char *tag, int complainp) |
410c8acf | 626 | { |
799e58b9 MW |
627 | knode *kn; |
628 | kdata *kd; | |
629 | unsigned f; | |
410c8acf | 630 | |
799e58b9 MW |
631 | kn = sym_find(&kh->tab, tag, -1, sizeof(knode), &f); |
632 | ||
633 | if (f) { | |
634 | if (kn->f & KNF_BROKEN) { | |
635 | T( if (complainp) | |
636 | trace(T_KEYMGMT, "keymgmt: key `%s' marked as broken", tag); ) | |
637 | return (0); | |
638 | } | |
639 | ||
640 | kd = kn->kd; | |
641 | if (kd) kd->ref++; | |
642 | T( trace(T_KEYMGMT, "keymgmt: %scache hit for key `%s'", | |
643 | kd ? "" : "negative ", tag); ) | |
644 | return (kd); | |
645 | } else { | |
646 | kd = kh_load(kh, tag, complainp); | |
647 | kn->kd = kd; | |
648 | kn->kh = kh; | |
649 | kn->f = 0; | |
650 | if (!kd) | |
651 | kn->f |= KNF_BROKEN; | |
652 | else { | |
653 | kd->kn = kn; | |
654 | kd->ref++; | |
655 | } | |
656 | return (kd); | |
410c8acf | 657 | } |
410c8acf | 658 | } |
659 | ||
799e58b9 | 660 | /* --- @kh_refresh@ --- * |
410c8acf | 661 | * |
799e58b9 | 662 | * Arguments: @keyhalf *kh@ = pointer to the keyhalf |
410c8acf | 663 | * |
799e58b9 MW |
664 | * Returns: Zero if nothing needs to be done; nonzero if peers should |
665 | * refresh their keys. | |
410c8acf | 666 | * |
799e58b9 MW |
667 | * Use: Refreshes cached keys from files. |
668 | * | |
669 | * Each active knode is examined to see if a new key is | |
670 | * available: the return value is nonzero if any new keys are. | |
671 | * A key is considered new if its algorithms, public key, or | |
672 | * expiry time are/is different. | |
673 | * | |
674 | * Stub knodes (with no kdata attached) are removed, so that a | |
675 | * later retry can succeed if the file has been fixed. (This | |
676 | * doesn't count as a change, since no peers should be relying | |
677 | * on a nonexistent key.) | |
410c8acf | 678 | */ |
679 | ||
799e58b9 | 680 | static int kh_refresh(keyhalf *kh) |
410c8acf | 681 | { |
799e58b9 MW |
682 | knode *kn; |
683 | kdata *kd; | |
684 | sym_iter i; | |
685 | int changep = 0; | |
686 | ||
687 | if (!fwatch_update(&kh->w, kh->kr) || kh_reopen(kh)) | |
688 | return (0); | |
689 | ||
690 | T( trace(T_KEYMGMT, "keymgmt: rescan %s keyring `%s'", kh->kind, kh->kr); ) | |
691 | for (sym_mkiter(&i, &kh->tab); (kn = sym_next(&i)) != 0; ) { | |
692 | if (!kn->kd) { | |
693 | T( trace(T_KEYMGMT, "keymgmt: discard stub entry for key `%s'", | |
694 | SYM_NAME(kn)); ) | |
695 | sym_remove(&kh->tab, kn); | |
696 | continue; | |
697 | } | |
698 | if ((kd = kh_load(kh, SYM_NAME(kn), 1)) == 0) { | |
699 | if (!(kn->f & KNF_BROKEN)) { | |
700 | T( trace(T_KEYMGMT, "keymgmt: failed to load new key `%s': " | |
701 | "marking it as broken", | |
702 | SYM_NAME(kn)); ) | |
703 | kn->f |= KNF_BROKEN; | |
704 | } | |
705 | continue; | |
706 | } | |
707 | kn->f &= ~KNF_BROKEN; | |
708 | if (kd->t_exp == kn->kd->t_exp && | |
709 | km_samealgsp(kd, kn->kd) && | |
710 | G_EQ(kd->g, kd->kpub, kn->kd->kpub)) { | |
711 | T( trace(T_KEYMGMT, "keymgmt: key `%s' unchanged", SYM_NAME(kn)); ) | |
712 | continue; | |
713 | } | |
714 | T( trace(T_KEYMGMT, "keymgmt: loaded new version of key `%s'", | |
715 | SYM_NAME(kn)); ) | |
716 | km_unref(kn->kd); | |
717 | kd->kn = kn; | |
718 | kn->kd = kd; | |
719 | changep = 1; | |
720 | } | |
410c8acf | 721 | |
799e58b9 MW |
722 | return (changep); |
723 | } | |
410c8acf | 724 | |
799e58b9 | 725 | /*----- Main code ---------------------------------------------------------*/ |
410c8acf | 726 | |
799e58b9 MW |
727 | const char *tag_priv; |
728 | kdata *master; | |
410c8acf | 729 | |
410c8acf | 730 | /* --- @km_init@ --- * |
731 | * | |
799e58b9 MW |
732 | * Arguments: @const char *privkr@ = private keyring file |
733 | * @const char *pubkr@ = public keyring file | |
734 | * @const char *ptag@ = default private-key tag | |
410c8acf | 735 | * |
736 | * Returns: --- | |
737 | * | |
799e58b9 MW |
738 | * Use: Initializes the key-management machinery, loading the |
739 | * keyrings and so on. | |
410c8acf | 740 | */ |
741 | ||
799e58b9 | 742 | void km_init(const char *privkr, const char *pubkr, const char *ptag) |
410c8acf | 743 | { |
b5c45da1 | 744 | const gchash *const *hh; |
410c8acf | 745 | |
b5c45da1 | 746 | for (hh = ghashtab; *hh; hh++) { |
747 | if ((*hh)->hashsz > MAXHASHSZ) { | |
748 | die(EXIT_FAILURE, "INTERNAL ERROR: %s hash length %lu > MAXHASHSZ %d", | |
749 | (*hh)->name, (unsigned long)(*hh)->hashsz, MAXHASHSZ); | |
750 | } | |
751 | } | |
752 | ||
799e58b9 MW |
753 | kh_init(&priv, privkr); |
754 | kh_init(&pub, pubkr); | |
755 | ||
756 | tag_priv = ptag; | |
757 | if ((master = km_findpriv(ptag)) == 0) exit(EXIT_FAILURE); | |
410c8acf | 758 | } |
759 | ||
799e58b9 | 760 | /* --- @km_reload@ --- * |
410c8acf | 761 | * |
799e58b9 | 762 | * Arguments: --- |
410c8acf | 763 | * |
799e58b9 | 764 | * Returns: Zero if OK, nonzero to force reloading of keys. |
410c8acf | 765 | * |
799e58b9 | 766 | * Use: Checks the keyrings to see if they need reloading. |
410c8acf | 767 | */ |
768 | ||
799e58b9 | 769 | int km_reload(void) |
410c8acf | 770 | { |
799e58b9 MW |
771 | int changep = 0; |
772 | kdata *kd; | |
773 | ||
774 | if (kh_refresh(&priv)) { | |
775 | changep = 1; | |
776 | kd = master->kn->kd; | |
35c8b547 | 777 | if (kd != master) { |
799e58b9 MW |
778 | km_unref(master); |
779 | km_ref(kd); | |
780 | master = kd; | |
781 | } | |
782 | } | |
783 | if (kh_refresh(&pub)) | |
784 | changep = 1; | |
785 | return (changep); | |
786 | } | |
52c03a2a | 787 | |
799e58b9 MW |
788 | /* --- @km_findpub@, @km_findpriv@ --- * |
789 | * | |
790 | * Arguments: @const char *tag@ = key tag to load | |
791 | * | |
792 | * Returns: Pointer to the kdata object if successful, or null on error. | |
793 | * | |
794 | * Use: Fetches a public or private key from the keyring. | |
795 | */ | |
52c03a2a | 796 | |
799e58b9 | 797 | kdata *km_findpub(const char *tag) { return (kh_find(&pub, tag, 1)); } |
410c8acf | 798 | |
799e58b9 MW |
799 | kdata *km_findpriv(const char *tag) |
800 | { | |
801 | kdata *kd; | |
52c03a2a | 802 | |
799e58b9 MW |
803 | /* Unpleasantness for the sake of compatibility. */ |
804 | if (!tag && (kd = kh_find(&priv, "tripe", 0)) != 0) return (kd); | |
805 | else return (kh_find(&priv, tag ? tag : "tripe-dh", 1)); | |
806 | } | |
52c03a2a | 807 | |
799e58b9 MW |
808 | /* --- @km_tag@ --- * |
809 | * | |
810 | * Arguments: @kdata *kd@ - pointer to the kdata object | |
811 | * | |
812 | * Returns: A pointer to the short tag by which the kdata was loaded. | |
813 | */ | |
52c03a2a | 814 | |
799e58b9 | 815 | const char *km_tag(kdata *kd) { return (SYM_NAME(kd->kn)); } |
52c03a2a | 816 | |
799e58b9 MW |
817 | /* --- @km_ref@ --- * |
818 | * | |
819 | * Arguments: @kdata *kd@ = pointer to the kdata object | |
820 | * | |
821 | * Returns: --- | |
822 | * | |
823 | * Use: Claim a new reference to a kdata object. | |
824 | */ | |
52c03a2a | 825 | |
799e58b9 | 826 | void km_ref(kdata *kd) { kd->ref++; } |
52c03a2a | 827 | |
799e58b9 MW |
828 | /* --- @km_unref@ --- * |
829 | * | |
830 | * Arguments: @kdata *kd@ = pointer to the kdata object | |
831 | * | |
832 | * Returns: --- | |
833 | * | |
834 | * Use: Releases a reference to a kdata object. | |
835 | */ | |
52c03a2a | 836 | |
799e58b9 MW |
837 | void km_unref(kdata *kd) |
838 | { | |
839 | if (--kd->ref) return; | |
840 | if (kd->kpriv) mp_drop(kd->kpriv); | |
841 | G_DESTROY(kd->g, kd->kpub); | |
842 | xfree(kd->tag); | |
843 | G_DESTROYGROUP(kd->g); | |
844 | } | |
845 | ||
410c8acf | 846 | /*----- That's all, folks -------------------------------------------------*/ |