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