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