Commit | Line | Data |
---|---|---|
247f344a MW |
1 | /* -*-c-*- |
2 | * | |
3 | * Request a key over UDP, or respond to such a request | |
4 | * | |
5 | * (c) 2012 Mark Wooding | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of udpkey. | |
11 | * | |
12 | * The udpkey program 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 | * The udpkey program 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 udpkey; 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 <ctype.h> | |
30 | #include <errno.h> | |
31 | #include <stdio.h> | |
32 | #include <stdlib.h> | |
33 | #include <string.h> | |
34 | #include <time.h> | |
35 | ||
36 | #include <sys/types.h> | |
37 | #include <sys/time.h> | |
38 | #include <unistd.h> | |
39 | #include <fcntl.h> | |
40 | ||
41 | #include <syslog.h> | |
42 | ||
43 | #include <sys/socket.h> | |
44 | #include <arpa/inet.h> | |
45 | #include <netinet/in.h> | |
46 | #include <netdb.h> | |
47 | ||
48 | #include <mLib/alloc.h> | |
49 | #include <mLib/buf.h> | |
50 | #include <mLib/daemonize.h> | |
51 | #include <mLib/dstr.h> | |
52 | #include <mLib/fdflags.h> | |
53 | #include <mLib/fwatch.h> | |
54 | #include <mLib/hex.h> | |
55 | #include <mLib/mdwopt.h> | |
56 | #include <mLib/quis.h> | |
57 | #include <mLib/report.h> | |
58 | #include <mLib/sub.h> | |
59 | #include <mLib/tv.h> | |
60 | ||
61 | #include <catacomb/buf.h> | |
a5f873be | 62 | #include <catacomb/ct.h> |
247f344a MW |
63 | #include <catacomb/dh.h> |
64 | #include <catacomb/ec.h> | |
65 | #include <catacomb/ec-keys.h> | |
66 | #include <catacomb/gcipher.h> | |
67 | #include <catacomb/gmac.h> | |
68 | #include <catacomb/group.h> | |
69 | #include <catacomb/key.h> | |
70 | #include <catacomb/mp.h> | |
71 | #include <catacomb/mprand.h> | |
72 | #include <catacomb/noise.h> | |
73 | #include <catacomb/rand.h> | |
74 | ||
75 | #include <catacomb/rijndael-counter.h> | |
76 | #include <catacomb/sha256.h> | |
77 | ||
78 | #ifdef DEBUG | |
79 | # define D(x) x | |
80 | #else | |
81 | # define D(x) | |
82 | #endif | |
83 | ||
84 | /*---- Static variables ---------------------------------------------------*/ | |
85 | ||
86 | static unsigned flags = 0; | |
87 | #define f_bogus 1u | |
88 | #define f_listen 2u | |
89 | #define f_daemon 4u | |
90 | #define f_syslog 8u | |
91 | ||
92 | #define BUFSZ 65536 | |
93 | static unsigned char ibuf[BUFSZ], obuf[BUFSZ]; | |
94 | ||
95 | static key_file *kf; | |
96 | static const char *kfname = "keyring"; | |
97 | static const char *pidfile; | |
98 | static fwatch kfwatch; | |
99 | static unsigned nq; | |
100 | ||
101 | /*----- Miscellaneous utilities -------------------------------------------*/ | |
102 | ||
103 | /* Resolve NAME, storing the address in *ADDR. Exit on error. */ | |
104 | static void resolve(const char *name, struct in_addr *addr) | |
105 | { | |
106 | struct hostent *h; | |
107 | ||
108 | if ((h = gethostbyname(name)) == 0) | |
109 | die(1, "failed to resolve `%s': %s", name, hstrerror(h_errno)); | |
110 | if (h->h_addrtype != AF_INET) | |
111 | die(1, "unexpected address type %d", h->h_addrtype); | |
112 | memcpy(addr, h->h_addr, sizeof(struct in_addr)); | |
113 | } | |
114 | ||
115 | /* Convert PORT to a port number (in host byte order). Exit on error. */ | |
116 | static unsigned short getport(const char *port) | |
117 | { | |
118 | unsigned long i = 0; | |
119 | char *q; | |
120 | int e = errno; | |
121 | ||
122 | errno = 0; | |
123 | if (!isdigit(*port) || | |
124 | (i = strtoul(port, &q, 0)) == 0 || | |
125 | i >= 65536 || *q || errno) | |
126 | die(1, "invalid port number `%s'", port); | |
127 | errno = e; | |
128 | return ((unsigned short)i); | |
129 | } | |
130 | ||
131 | /* Read the file named by NAME into a buffer -- or at least an initial | |
132 | * portion of it; set *P to the start and *SZ to the length. Return -1 if it | |
133 | * didn't work. The buffer doesn't need to be freed: the data is stashed in | |
134 | * ibuf. | |
135 | */ | |
136 | static int snarf(const char *name, void **p, size_t *sz) | |
137 | { | |
138 | ssize_t n; | |
139 | int fd; | |
140 | ||
141 | if ((fd = open(name, O_RDONLY)) < 0) return (-1); | |
142 | n = read(fd, ibuf, sizeof(ibuf)); | |
143 | close(fd); | |
144 | if (n < 0) return (-1); | |
145 | *p = ibuf; *sz = n; | |
146 | return (0); | |
147 | } | |
148 | ||
149 | /* Complain about something. If f_syslog is set then complain to that; | |
150 | * otherwise write to stderr. Don't use `%m' because that won't work when | |
151 | * writing to stderr. | |
152 | */ | |
92469bdf | 153 | static void PRINTF_LIKE(2, 3) complain(int sev, const char *msg, ...) |
247f344a MW |
154 | { |
155 | va_list ap; | |
156 | ||
157 | va_start(ap, msg); | |
158 | if (flags & f_syslog) | |
159 | vsyslog(sev, msg, ap); | |
160 | else { | |
161 | fprintf(stderr, "%s: ", QUIS); | |
162 | vfprintf(stderr, msg, ap); | |
163 | fputc('\n', stderr); | |
164 | } | |
165 | } | |
166 | ||
167 | /*----- Reading key data --------------------------------------------------*/ | |
168 | ||
169 | struct kinfo { | |
170 | group *g; | |
171 | ge *X; | |
172 | mp *x; | |
173 | const gccipher *cc; | |
174 | const gcmac *mc; size_t tagsz; | |
175 | const gchash *hc; | |
176 | }; | |
177 | ||
178 | /* Clear a kinfo structure so it can be freed without trouble. */ | |
179 | static void k_init(struct kinfo *k) { k->g = 0; k->x = 0; k->X = 0; } | |
180 | ||
181 | /* Free a kinfo structure. This is safe on any initialized kinfo | |
182 | * structure. | |
183 | */ | |
184 | static void k_free(struct kinfo *k) | |
185 | { | |
186 | if (k->X) { G_DESTROY(k->g, k->X); k->X = 0; } | |
187 | if (k->x) { MP_DROP(k->x); k->x = 0; } | |
188 | if (k->g) { G_DESTROYGROUP(k->g); k->g = 0; } | |
189 | } | |
190 | ||
191 | /* Empty macro arguments are forbidden. But arguments are expended during | |
192 | * replacement, not while the call is being processed, so this hack is OK. | |
193 | * Unfortunately, if a potentially empty argument is passed on to another | |
194 | * macro then it needs to be guarded with a use of EMPTY too... | |
195 | */ | |
196 | #define EMPTY | |
197 | ||
198 | /* Table of key types. Entries have the form | |
199 | * | |
200 | * _(name, NAME, SETGROUP, SETPRIV, SETPUB) | |
201 | * | |
202 | * The name and NAME are lower- and uppercase names for the type used for | |
203 | * constructing various type name constant names. The code fragment SETGROUP | |
204 | * initializes k->g given the name_{pub,priv} structure in p; SETPRIV and | |
205 | * SETPUB set up k->x and k->X respectively. (In this last case, k->X will | |
206 | * have been created as a group element already.) | |
207 | */ | |
208 | #define KEYTYPES(_) \ | |
209 | \ | |
210 | _(dh, DH, \ | |
211 | { k->g = group_prime(&p.dp); }, \ | |
212 | { k->x = MP_COPY(p.x); }, \ | |
213 | { if (G_FROMINT(k->g, k->X, p.y)) { \ | |
214 | complain(LOG_ERR, "bad public key in `%s'", t->buf); \ | |
215 | goto fail; \ | |
216 | } \ | |
217 | }) \ | |
218 | \ | |
219 | _(ec, EC, \ | |
220 | { ec_info ei; const char *e; \ | |
221 | if ((e = ec_getinfo(&ei, p.cstr)) != 0) { \ | |
222 | complain(LOG_ERR, "bad elliptic curve in `%s': %s", t->buf, e); \ | |
223 | goto fail; \ | |
224 | } \ | |
225 | k->g = group_ec(&ei); \ | |
226 | }, \ | |
227 | { k->x = MP_COPY(p.x); }, \ | |
228 | { if (G_FROMEC(k->g, k->X, &p.p)) { \ | |
229 | complain(LOG_ERR, "bad public point in `%s'", t->buf); \ | |
230 | goto fail; \ | |
231 | } \ | |
232 | }) | |
233 | ||
234 | /* Define load_tywhich, where which is `pub' or `priv', to load a public or | |
235 | * private key. Other parameters are as for the KEYTYPES list above. | |
236 | */ | |
237 | #define KLOAD(ty, TY, which, WHICH, setgroup, setpriv, setpub) \ | |
238 | static int load_##ty##which(key_data *kd, struct kinfo *k, dstr *t) \ | |
239 | { \ | |
240 | key_packstruct kps[TY##_##WHICH##FETCHSZ]; \ | |
241 | key_packdef *kp; \ | |
242 | ty##_##which p; \ | |
243 | int rc; \ | |
244 | \ | |
245 | /* Extract the key data from the keydata. */ \ | |
246 | kp = key_fetchinit(ty##_##which##fetch, kps, &p); \ | |
247 | if ((rc = key_unpack(kp, kd, t)) != 0) { \ | |
248 | complain(LOG_ERR, "failed to unpack key `%s': %s", \ | |
249 | t->buf, key_strerror(rc)); \ | |
250 | goto fail; \ | |
251 | } \ | |
252 | \ | |
253 | /* Extract the components as abstract group elements. */ \ | |
254 | setgroup; \ | |
255 | setpriv; \ | |
256 | k->X = G_CREATE(k->g); \ | |
257 | setpub; \ | |
258 | \ | |
259 | /* Dispose of stuff we don't need. */ \ | |
260 | key_fetchdone(kp); \ | |
261 | return (0); \ | |
262 | \ | |
263 | /* Tidy up after mishaps. */ \ | |
264 | fail: \ | |
265 | k_free(k); \ | |
266 | key_fetchdone(kp); \ | |
267 | return (-1); \ | |
268 | } | |
269 | ||
270 | /* Map over the KEYTYPES to declare the load_tywhich functions using KLOAD | |
271 | * above. | |
272 | */ | |
273 | #define KEYTYPE_KLOAD(ty, TY, setgroup, setpriv, setpub) \ | |
274 | KLOAD(ty, TY, priv, PRIV, setgroup, setpriv, \ | |
275 | { G_EXP(k->g, k->X, k->g->g, k->x); }) \ | |
276 | KLOAD(ty, TY, pub, PUB, setgroup, { }, setpub) | |
277 | KEYTYPES(KEYTYPE_KLOAD) | |
278 | ||
279 | /* Define a table of group key-loading operations. */ | |
280 | struct kload_ops { | |
281 | const char *name; | |
282 | int (*loadpriv)(key_data *, struct kinfo *, dstr *); | |
283 | int (*loadpub)(key_data *, struct kinfo *, dstr *); | |
284 | }; | |
285 | ||
286 | static const struct kload_ops kload_ops[] = { | |
287 | #define KEYTYPE_OPS(ty, TY, setgroup, setpriv, setpub) \ | |
288 | { #ty, load_##ty##priv, load_##ty##pub }, | |
289 | KEYTYPES(KEYTYPE_OPS) | |
290 | { 0 } | |
291 | }; | |
292 | ||
293 | /* Load a private or public (indicated by PRIVP) key named TAG into a kinfo | |
294 | * structure K. Also fill in the cipher suite selections extracted from the | |
295 | * key attributes. | |
296 | */ | |
297 | static int loadkey(const char *tag, struct kinfo *k, int privp) | |
298 | { | |
299 | const struct kload_ops *ops; | |
300 | dstr d = DSTR_INIT, dd = DSTR_INIT; | |
301 | key *ky; | |
302 | key_data **kd; | |
303 | const char *ty, *p; | |
304 | char *q; | |
305 | int tsz; | |
306 | int rc; | |
307 | ||
308 | /* Find the key data. */ | |
309 | if (key_qtag(kf, tag, &d, &ky, &kd)) { | |
310 | complain(LOG_ERR, "unknown key tag `%s'", tag); | |
311 | goto fail; | |
312 | } | |
313 | ||
314 | /* Find the key's group type and locate the group operations. */ | |
315 | ty = key_getattr(kf, ky, "group"); | |
316 | if (!ty && strncmp(ky->type, "udpkey-", 7) == 0) ty = ky->type + 7; | |
317 | if (!ty) { | |
318 | complain(LOG_ERR, "no group type for key %s", d.buf); | |
319 | goto fail; | |
320 | } | |
321 | for (ops = kload_ops; ops->name; ops++) { | |
322 | if (strcmp(ty, ops->name) == 0) | |
323 | goto found; | |
324 | } | |
325 | complain(LOG_ERR, "unknown group type `%s' in key %s", ty, d.buf); | |
326 | goto fail; | |
327 | ||
328 | found: | |
329 | /* Extract the key data into an appropriately abstract form. */ | |
330 | k->g = 0; k->x = 0; k->X = 0; | |
331 | if ((rc = (privp ? ops->loadpriv : ops->loadpub)(*kd, k, &d)) != 0) | |
332 | goto fail; | |
333 | ||
334 | /* Extract the chosen symmetric cipher. */ | |
335 | if ((p = key_getattr(kf, ky, "cipher")) == 0) | |
336 | k->cc = &rijndael_counter; | |
337 | else if ((k->cc = gcipher_byname(p)) == 0) { | |
338 | complain(LOG_ERR, "unknown cipher `%s' in key %s", p, d.buf); | |
339 | goto fail; | |
340 | } | |
341 | ||
342 | /* And the chosen hash function. */ | |
343 | if ((p = key_getattr(kf, ky, "hash")) == 0) | |
344 | k->hc = &sha256; | |
345 | else if ((k->hc = ghash_byname(p)) == 0) { | |
346 | complain(LOG_ERR, "unknown hash `%s' in key %s", p, d.buf); | |
347 | goto fail; | |
348 | } | |
349 | ||
350 | /* And finally a MAC. This is more fiddly because we must handle (a) | |
351 | * truncation and (b) defaulting based on the hash. | |
352 | */ | |
353 | if ((p = key_getattr(kf, ky, "mac")) == 0) | |
354 | dstr_putf(&dd, "%s-hmac", k->hc->name); | |
355 | else | |
356 | dstr_puts(&dd, p); | |
357 | if ((q = strchr(dd.buf, '/')) != 0) *q++ = 0; | |
358 | else q = 0; | |
359 | if ((k->mc = gmac_byname(dd.buf)) == 0) { | |
360 | complain(LOG_ERR, "unknown mac `%s' in key %s", dd.buf, d.buf); | |
361 | goto fail; | |
362 | } | |
363 | if (!q) | |
364 | k->tagsz = k->mc->hashsz/2; | |
365 | else { | |
366 | tsz = atoi(q); | |
367 | if (tsz <= 0 || tsz%8 || tsz/8 > k->mc->hashsz) { | |
46716e8f | 368 | complain(LOG_ERR, "bad tag size `%s' for mac `%s' in key %s", |
247f344a MW |
369 | q, k->mc->name, d.buf); |
370 | goto fail; | |
371 | } | |
372 | k->tagsz = tsz/8; | |
373 | } | |
374 | ||
375 | /* Done. */ | |
376 | rc = 0; | |
377 | goto done; | |
378 | ||
379 | fail: | |
380 | rc = -1; | |
381 | done: | |
382 | dstr_destroy(&d); | |
383 | dstr_destroy(&dd); | |
384 | return (rc); | |
385 | } | |
386 | ||
387 | static void keymoan(const char *file, int line, const char *err, void *p) | |
388 | { complain(LOG_ERR, "%s:%d: %s", file, line, err); } | |
389 | ||
390 | /* Update the keyring `kf' if the file has been changed since we last looked. | |
391 | */ | |
392 | static void kfupdate(void) | |
393 | { | |
394 | key_file *kfnew; | |
395 | ||
396 | if (!fwatch_update(&kfwatch, kfname)) return; | |
397 | kfnew = CREATE(key_file); | |
398 | if (key_open(kfnew, kfname, KOPEN_READ, keymoan, 0)) { | |
399 | DESTROY(kfnew); | |
400 | return; | |
401 | } | |
402 | key_close(kf); | |
403 | DESTROY(kf); | |
404 | kf = kfnew; | |
405 | } | |
406 | ||
407 | /*----- Low-level crypto operations ---------------------------------------*/ | |
408 | ||
409 | /* Derive a key, writing its address to *KK and size to *N. The size is | |
410 | * compatible with the keysz rules KSZ. It is generated for the purpose of | |
411 | * keying a WHAT (used for key separation and in error messages), and NAME is | |
412 | * the name of the specific instance (e.g., `twofish-counter') from the class | |
413 | * name. The kinfo structure K tells us which algorithms to use for the | |
414 | * derivation. The group elements U and Z are the cryptographic inputs | |
415 | * for the derivation. | |
416 | * | |
417 | * Basically all we do is compute H(what || U || Z). | |
418 | */ | |
419 | static int derive(struct kinfo *k, ge *U, ge *Z, | |
420 | const char *what, const char *name, const octet *ksz, | |
421 | octet **kk, size_t *n) | |
422 | { | |
423 | buf b; | |
424 | ghash *h; | |
425 | octet *p; | |
426 | ||
427 | /* Find a suitable key size. */ | |
428 | if ((*n = keysz(k->hc->hashsz, ksz)) == 0) { | |
429 | complain(LOG_ERR, | |
430 | "failed to find suitable key size for %s `%s' and hash `%s'", | |
431 | what, name, k->hc->name); | |
432 | return (-1); | |
433 | } | |
434 | ||
435 | /* Build the hash preimage. */ | |
436 | buf_init(&b, obuf, sizeof(obuf)); | |
437 | buf_put(&b, "udpkey-", 7); | |
438 | buf_putstrz(&b, what); | |
439 | G_TORAW(k->g, &b, U); | |
440 | G_TORAW(k->g, &b, Z); | |
441 | if (BBAD(&b)) { | |
442 | complain(LOG_ERR, "overflow while deriving key (prepare preimage)!"); | |
443 | return (-1); | |
444 | } | |
445 | ||
446 | /* Derive the output key. */ | |
447 | h = GH_INIT(k->hc); | |
448 | GH_HASH(h, BBASE(&b), BLEN(&b)); | |
449 | buf_init(&b, obuf, sizeof(obuf)); | |
450 | if ((p = buf_get(&b, h->ops->c->hashsz)) == 0) { | |
451 | complain(LOG_ERR, "overflow while deriving key (output hash)!"); | |
452 | GH_DESTROY(h); | |
453 | return (-1); | |
454 | } | |
455 | GH_DONE(h, p); | |
456 | GH_DESTROY(h); | |
457 | *kk = p; | |
458 | return (0); | |
459 | } | |
460 | ||
461 | #ifdef DEBUG | |
462 | static void debug_mp(const char *what, mp *x) | |
463 | { fprintf(stderr, "%s: *** ", QUIS); MP_EPRINT(what, x); } | |
464 | static void debug_ge(const char *what, group *g, ge *X) | |
465 | { | |
466 | fprintf(stderr, "%s: *** %s = ", QUIS, what); | |
467 | group_writefile(g, X, stderr); | |
468 | fputc('\n', stderr); | |
469 | } | |
470 | #endif | |
471 | ||
472 | /*----- Listening for requests --------------------------------------------*/ | |
473 | ||
474 | /* Rate limiting parameters. | |
475 | * | |
476 | * There's a probabilistic rate-limiting mechanism. A counter starts at 0. | |
477 | * Every time we oricess a request, we increment the counter. The counter | |
478 | * drops by RATE_REFILL every second. If the counter is below RATE_CREDIT | |
479 | * then the request is processed; otherwise it is processed with probability | |
480 | * 1/(counter - RATE_CREDIT). | |
481 | */ | |
482 | #define RATE_REFILL 10 /* Credits per second. */ | |
483 | #define RATE_CREDIT 1000 /* Initial credit. */ | |
484 | ||
485 | static int dolisten(int argc, char *argv[]) | |
486 | { | |
487 | int sk; | |
488 | char *p, *q, ch; | |
489 | const char *pp; | |
490 | char *aspec; | |
491 | ssize_t n; | |
492 | size_t sz; | |
493 | fd_set fdin; | |
494 | struct sockaddr_in sin; | |
495 | struct in_addr in; | |
496 | int mlen; | |
497 | socklen_t len; | |
498 | buf bin, bout; | |
499 | dstr d = DSTR_INIT, dd = DSTR_INIT; | |
500 | FILE *fp = 0; | |
501 | key *ky; | |
502 | key_data **kkd; | |
503 | mp *r = MP_NEW, *v = MP_NEW; | |
504 | ge *R = 0, *U = 0, *V = 0, *W = 0, *Y = 0, *Z = 0; | |
505 | ghash *h = 0; | |
506 | gmac *m = 0; | |
507 | gcipher *c = 0; | |
508 | octet *kk, *t, *tt; | |
509 | size_t ksz; | |
510 | struct kinfo k; | |
511 | unsigned bucket = 0, toks; | |
512 | time_t last = 0, now; | |
513 | ||
514 | /* Set up the socket address. */ | |
515 | sin.sin_family = AF_INET; | |
516 | aspec = xstrdup(argv[0]); | |
517 | if ((p = strchr(aspec, ':')) == 0) { | |
518 | p = aspec; | |
519 | sin.sin_addr.s_addr = INADDR_ANY; | |
520 | } else { | |
521 | *p++ = 0; | |
522 | resolve(aspec, &sin.sin_addr); | |
523 | } | |
524 | sin.sin_port = htons(getport(p)); | |
525 | ||
526 | /* Create and set up the socket itself. */ | |
527 | if ((sk = socket(PF_INET, SOCK_DGRAM, 0)) < 0 || | |
528 | fdflags(sk, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC) || | |
529 | bind(sk, (struct sockaddr *)&sin, sizeof(sin))) | |
530 | die(1, "failed to create socket: %s", strerror(errno)); | |
531 | ||
532 | /* That's enough initialization. If we should fork, then do that. */ | |
533 | if (flags & f_daemon) { | |
534 | if (pidfile && (fp = fopen(pidfile, "w")) == 0) | |
535 | die(1, "failed to open pidfile `%s': %s", pidfile, strerror(errno)); | |
536 | openlog(QUIS, LOG_PID, LOG_DAEMON); | |
537 | if (daemonize()) | |
538 | die(1, "failed to become background process: %s", strerror(errno)); | |
539 | if (pidfile) { fprintf(fp, "%ld\n", (long)getpid()); fclose(fp); } | |
540 | flags |= f_syslog; | |
541 | } | |
542 | ||
543 | for (;;) { | |
544 | ||
545 | /* Clear out the key state. */ | |
546 | k_init(&k); | |
547 | ||
548 | /* Wait for something to happen. */ | |
549 | FD_ZERO(&fdin); | |
550 | FD_SET(sk, &fdin); | |
551 | if (select(sk + 1, &fdin, 0, 0, 0) < 0) | |
552 | die(1, "select failed: %s", strerror(errno)); | |
553 | noise_timer(RAND_GLOBAL); | |
554 | ||
555 | /* Fetch a packet. */ | |
556 | len = sizeof(sin); | |
557 | n = recvfrom(sk, ibuf, sizeof(ibuf), 0, (struct sockaddr *)&sin, &len); | |
558 | if (n < 0) { | |
559 | if (errno != EAGAIN && errno != EINTR) | |
560 | complain(LOG_ERR, "unexpected receive error: %s", strerror(errno)); | |
561 | goto again; | |
562 | } | |
563 | ||
564 | /* Refill the bucket, and see whether we should reject this packet. */ | |
565 | now = time(0); | |
566 | if (bucket && now != last) { | |
567 | toks = (now - last)*RATE_REFILL; | |
568 | bucket = bucket < toks ? 0 : bucket - toks; | |
569 | } | |
570 | last = now; | |
571 | if (bucket > RATE_CREDIT && | |
572 | grand_range(&rand_global, bucket - RATE_CREDIT)) | |
573 | goto again; | |
574 | bucket++; | |
575 | ||
576 | /* Set up the input buffer for parsing the request. */ | |
577 | buf_init(&bin, ibuf, n); | |
578 | ||
579 | /* Extract the key tag name. */ | |
580 | if ((p = buf_getmemz(&bin, &sz)) == 0) { | |
581 | complain(LOG_WARNING, "invalid key tag from %s:%d", | |
582 | inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); | |
583 | goto again; | |
584 | } | |
585 | ||
586 | /* Find the key. */ | |
587 | kfupdate(); | |
588 | if (key_qtag(kf, p, &d, &ky, &kkd)) { | |
589 | complain(LOG_WARNING, "unknown key tag `%s' from %s:%d", | |
590 | p, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); | |
591 | goto again; | |
592 | } | |
593 | ||
594 | /* And make sure that it has the right shape. */ | |
595 | if ((ky->k->e & KF_ENCMASK) != KENC_BINARY) { | |
596 | complain(LOG_ERR, "key %s is not plain binary data", d.buf); | |
597 | goto again; | |
598 | } | |
599 | ||
600 | /* Find the list of clients, and look up the caller's address in the | |
601 | * list. Entries have the form ADDRESS[/LEN][=TAG] and are separated by | |
602 | * `;'. | |
603 | */ | |
604 | if ((pp = key_getattr(kf, ky, "clients")) == 0) { | |
605 | complain(LOG_WARNING, | |
606 | "key %s requested from %s:%d has no `clients' attribute", | |
607 | d.buf, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); | |
608 | goto again; | |
609 | } | |
610 | dstr_puts(&dd, pp); | |
611 | p = dd.buf; | |
612 | while (*p) { | |
613 | q = p; | |
614 | while (isdigit((unsigned char)*q) || *q == '.') q++; | |
615 | ch = *q; *q++ = 0; | |
616 | if (!inet_aton(p, &in)) goto skip; | |
617 | if (ch != '/') | |
618 | mlen = 32; | |
619 | else { | |
620 | p = q; | |
621 | while (isdigit((unsigned char)*q)) q++; | |
622 | ch = *q; *q++ = 0; | |
623 | mlen = atoi(p); | |
624 | } | |
625 | if (((sin.sin_addr.s_addr ^ in.s_addr) & | |
626 | (0xffffffff << (32 - mlen))) == 0) | |
627 | goto match; | |
628 | skip: | |
629 | if (!ch) break; | |
630 | p = q; | |
631 | while (*p && *p != ';') p++; | |
632 | if (*p) p++; | |
633 | } | |
634 | complain(LOG_WARNING, "access to key %s denied to %s:%d", | |
635 | d.buf, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); | |
636 | goto again; | |
637 | ||
638 | match: | |
639 | /* Build a tag name for the caller's KEM key, either from the client | |
640 | * match or the source address. | |
641 | */ | |
642 | if (ch != '=') { | |
643 | DRESET(&dd); | |
644 | dstr_puts(&dd, "client-"); | |
645 | dstr_puts(&dd, inet_ntoa(sin.sin_addr)); | |
646 | p = dd.buf; | |
647 | } else { | |
648 | p = q; | |
649 | while (*q && *q != ';') q++; | |
650 | if (*q == ';') *q++ = 0; | |
651 | } | |
652 | ||
653 | /* Report the match. */ | |
654 | complain(LOG_NOTICE, "client %s:%d (`%s') requests key %s", | |
655 | inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), p, d.buf); | |
656 | ||
657 | /* Load the KEM key. */ | |
658 | if (loadkey(p, &k, 0)) goto again; | |
659 | D( debug_ge("X", k.g, k.X); ) | |
660 | ||
661 | /* Read the caller's ephemeral key. */ | |
662 | R = G_CREATE(k.g); W = G_CREATE(k.g); | |
663 | U = G_CREATE(k.g); V = G_CREATE(k.g); | |
664 | Y = G_CREATE(k.g); Z = G_CREATE(k.g); | |
665 | if (G_FROMBUF(k.g, &bin, U)) { | |
666 | complain(LOG_WARNING, "failed to read ephemeral vector from %s:%d", | |
667 | inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); | |
668 | goto again; | |
669 | } | |
670 | D( debug_ge("U", k.g, U); ) | |
671 | if (BLEFT(&bin)) { | |
672 | complain(LOG_WARNING, "trailing junk in request from %s:%d", | |
673 | inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); | |
674 | goto again; | |
675 | } | |
676 | ||
677 | /* Ephemeral Diffie--Hellman. Choose v in GF(q) at random; compute | |
678 | * V = v P and -Y = (-v) U. | |
679 | */ | |
680 | v = mprand_range(v, k.g->r, &rand_global, 0); | |
681 | G_EXP(k.g, V, k.g->g, v); | |
682 | D( debug_mp("v", v); debug_ge("V", k.g, V); ) | |
683 | v = mp_sub(v, k.g->r, v); | |
684 | G_EXP(k.g, Y, U, v); | |
685 | D( debug_ge("-Y", k.g, Y); ) | |
686 | ||
687 | /* DLIES. Choose r in GF(q) at random; compute R = r P and Z = r X. | |
688 | * Mask the clue R as W = R - Y. (Doing the subtraction here makes life | |
689 | * easier at the other end, since we can determine -Y by negating v | |
690 | * whereas the recipient must subtract vectors which may be less | |
691 | * efficient.) | |
692 | */ | |
693 | r = mprand_range(r, k.g->r, &rand_global, 0); | |
694 | G_EXP(k.g, R, k.g->g, r); | |
695 | D( debug_mp("r", r); debug_ge("R", k.g, R); ) | |
696 | G_EXP(k.g, Z, k.X, r); | |
697 | G_MUL(k.g, W, R, Y); | |
698 | D( debug_ge("Z", k.g, Z); debug_ge("W", k.g, W); ) | |
699 | ||
700 | /* Derive encryption and integrity keys. */ | |
701 | derive(&k, R, Z, "cipher", k.cc->name, k.cc->keysz, &kk, &ksz); | |
702 | c = GC_INIT(k.cc, kk, ksz); | |
703 | derive(&k, R, Z, "mac", k.mc->name, k.mc->keysz, &kk, &ksz); | |
704 | m = GM_KEY(k.mc, kk, ksz); | |
705 | ||
706 | /* Build the ciphertext and compute a MAC tag over it. */ | |
707 | buf_init(&bout, obuf, sizeof(obuf)); | |
708 | if (G_TOBUF(k.g, &bout, V) || | |
709 | G_TOBUF(k.g, &bout, W)) | |
710 | goto bad; | |
711 | if ((t = buf_get(&bout, k.tagsz)) == 0) goto bad; | |
712 | sz = ky->k->u.k.sz; | |
713 | if (BENSURE(&bout, sz)) goto bad; | |
714 | GC_ENCRYPT(c, ky->k->u.k.k, BCUR(&bout), sz); | |
715 | h = GM_INIT(m); | |
716 | GH_HASH(h, BCUR(&bout), sz); | |
717 | tt = GH_DONE(h, 0); memcpy(t, tt, k.tagsz); | |
718 | BSTEP(&bout, sz); | |
719 | ||
720 | /* Send the reply packet back to the caller. */ | |
721 | if (sendto(sk, BBASE(&bout), BLEN(&bout), 0, | |
722 | (struct sockaddr *)&sin, len) < 0) { | |
723 | complain(LOG_ERR, "failed to send response to %s:%d: %s", | |
724 | inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), | |
725 | strerror(errno)); | |
726 | goto again; | |
727 | } | |
728 | ||
729 | goto again; | |
730 | ||
731 | bad: | |
732 | /* Report a problem building the reply. */ | |
733 | complain(LOG_ERR, "failed to construct response to %s:%d", | |
734 | inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); | |
735 | ||
736 | again: | |
737 | /* Free stuff for the next iteration. */ | |
738 | DRESET(&d); DRESET(&dd); | |
739 | if (R) { G_DESTROY(k.g, R); R = 0; } | |
740 | if (U) { G_DESTROY(k.g, U); U = 0; } | |
741 | if (V) { G_DESTROY(k.g, V); V = 0; } | |
742 | if (W) { G_DESTROY(k.g, W); W = 0; } | |
743 | if (Y) { G_DESTROY(k.g, Y); Y = 0; } | |
744 | if (Z) { G_DESTROY(k.g, Z); Z = 0; } | |
745 | if (c) { GC_DESTROY(c); c = 0; } | |
746 | if (m) { GM_DESTROY(m); m = 0; } | |
747 | if (h) { GH_DESTROY(h); h = 0; } | |
748 | k_free(&k); | |
749 | } | |
750 | ||
751 | return (-1); | |
752 | } | |
753 | ||
754 | /*----- Sending requests and processing responses -------------------------*/ | |
755 | ||
756 | struct query { | |
757 | struct query *next; | |
758 | octet *k; | |
759 | size_t sz; | |
760 | struct server *s; | |
761 | }; | |
762 | ||
763 | struct server { | |
764 | struct server *next; | |
765 | struct sockaddr_in sin; | |
766 | struct kinfo k; | |
767 | mp *u; | |
768 | ge *U; | |
769 | octet *h; | |
770 | }; | |
771 | ||
772 | /* Record a successful fetch of key material for a query Q. The data starts | |
773 | * at K and is SZ bytes long. The data is copied: it's safe to overwrite it. | |
774 | */ | |
775 | static void donequery(struct query *q, const void *k, size_t sz) | |
776 | { q->k = xmalloc(sz); memcpy(q->k, k, sz); q->sz = sz; nq--; } | |
777 | ||
778 | /* Initialize a query to a remote server. */ | |
779 | static struct query *qinit_net(const char *tag, const char *spec) | |
780 | { | |
781 | struct query *q; | |
782 | struct server *s, **stail; | |
783 | dstr d = DSTR_INIT, dd = DSTR_INIT; | |
784 | hex_ctx hc; | |
785 | char *p, *pp, ch; | |
786 | ||
787 | /* Allocate the query block. */ | |
788 | q = CREATE(struct query); | |
789 | stail = &q->s; | |
790 | ||
791 | /* Put the spec somewhere we can hack at it. */ | |
792 | dstr_puts(&d, spec); | |
793 | p = d.buf; | |
794 | ||
795 | /* Parse the query spec. Entries have the form ADDRESS:PORT[=TAG][#HASH] | |
796 | * and are separated by `;'. | |
797 | */ | |
798 | while (*p) { | |
799 | ||
800 | /* Allocate a new server node. */ | |
801 | s = CREATE(struct server); | |
802 | s->sin.sin_family = AF_INET; | |
803 | ||
804 | /* Extract the server address. */ | |
805 | if ((pp = strchr(p, ':')) == 0) | |
806 | die(1, "invalid syntax: missing `:PORT'"); | |
807 | *pp++ = 0; | |
808 | resolve(p, &s->sin.sin_addr); | |
809 | ||
810 | /* Extract the port number. */ | |
811 | p = pp; | |
812 | while (isdigit((unsigned char)*pp)) pp++; | |
813 | ch = *pp; *pp++ = 0; | |
814 | s->sin.sin_port = htons(getport(p)); | |
815 | ||
816 | /* If there's a key tag then extract that; otherwise use a default. */ | |
817 | if (ch != '=') | |
818 | p = "udpkey-kem"; | |
819 | else { | |
820 | p = pp; | |
821 | pp += strcspn(pp, ";#"); | |
822 | ch = *pp; *pp++ = 0; | |
823 | } | |
824 | if (loadkey(p, &s->k, 1)) exit(1); | |
825 | D( debug_mp("x", s->k.x); debug_ge("X", s->k.g, s->k.X); ) | |
826 | ||
827 | /* Choose an ephemeral private key u. Let x be our private key. We | |
828 | * compute U = u P and transmit this. | |
829 | */ | |
830 | s->u = mprand_range(MP_NEW, s->k.g->r, &rand_global, 0); | |
831 | s->U = G_CREATE(s->k.g); | |
832 | G_EXP(s->k.g, s->U, s->k.g->g, s->u); | |
833 | D( debug_mp("u", s->u); debug_ge("U", s->k.g, s->U); ) | |
834 | ||
835 | /* Link the server on. */ | |
836 | *stail = s; stail = &s->next; | |
837 | ||
838 | /* If there's a trailing hash then extract it. */ | |
839 | if (ch != '#') | |
840 | s->h = 0; | |
841 | else { | |
842 | p = pp; | |
843 | while (*pp == '-' || isxdigit((unsigned char)*pp)) pp++; | |
844 | hex_init(&hc); | |
845 | DRESET(&dd); | |
846 | hex_decode(&hc, p, pp - p, &dd); | |
847 | if (dd.len != s->k.hc->hashsz) die(1, "incorrect hash length"); | |
848 | s->h = xmalloc(dd.len); | |
849 | memcpy(s->h, dd.buf, dd.len); | |
850 | ch = *pp++; | |
851 | } | |
852 | ||
853 | /* If there are more servers, then continue parsing. */ | |
854 | if (!ch) break; | |
855 | else if (ch != ';') die(1, "invalid syntax: expected `;'"); | |
856 | p = pp; | |
857 | } | |
858 | ||
859 | /* Terminate the server list and return. */ | |
860 | *stail = 0; | |
861 | q->k = 0; | |
862 | dstr_destroy(&d); | |
863 | dstr_destroy(&dd); | |
864 | return (q); | |
865 | } | |
866 | ||
867 | /* Handle a `query' to a local file. */ | |
868 | static struct query *qinit_file(const char *tag, const char *file) | |
869 | { | |
870 | struct query *q; | |
871 | void *k; | |
872 | size_t sz; | |
873 | ||
874 | /* Snarf the file. */ | |
875 | q = CREATE(struct query); | |
876 | if (snarf(file, &k, &sz)) | |
877 | die(1, "failed to read `%s': %s", file, strerror(errno)); | |
878 | q->s = 0; | |
879 | donequery(q, k, sz); | |
880 | return (q); | |
881 | } | |
882 | ||
883 | /* Reransmission and timeout parameters. */ | |
884 | #define TO_NEXT(t) (((t) + 2)*4/3) /* Timeout growth function */ | |
885 | #define TO_MAX 30 /* When to give up */ | |
886 | ||
887 | static int doquery(int argc, char *argv[]) | |
888 | { | |
889 | struct query *q = 0, *qq, **qtail = &qq; | |
890 | struct server *s = 0; | |
891 | const char *tag = argv[0]; | |
892 | octet *p; | |
893 | int i; | |
894 | int sk; | |
895 | fd_set fdin; | |
896 | struct timeval now, when, tv; | |
897 | struct sockaddr_in sin; | |
898 | ge *R, *V = 0, *W = 0, *Y = 0, *Z = 0; | |
899 | octet *kk, *t, *tt; | |
900 | gcipher *c = 0; | |
901 | gmac *m = 0; | |
902 | ghash *h = 0; | |
903 | socklen_t len; | |
904 | unsigned next = 0; | |
905 | buf bin, bout; | |
906 | size_t n, j, ksz; | |
907 | ssize_t nn; | |
908 | ||
909 | /* Create a socket. We just use the one socket for everything. We don't | |
910 | * care which port we get allocated. | |
911 | */ | |
912 | if ((sk = socket(PF_INET, SOCK_DGRAM, 0)) < 0 || | |
913 | fdflags(sk, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC)) | |
914 | die(1, "failed to create socket: %s", strerror(errno)); | |
915 | ||
916 | /* Parse the query target specifications. The adjustments of `nq' aren't | |
917 | * in the right order but that doesn't matter. | |
918 | */ | |
919 | for (i = 1; i < argc; i++) { | |
920 | if (*argv[i] == '.' || *argv[i] == '/') q = qinit_file(tag, argv[i]); | |
921 | else if (strchr(argv[i], ':')) q = qinit_net(tag, argv[i]); | |
922 | else die(1, "unrecognized query target `%s'", argv[i]); | |
923 | *qtail = q; qtail = &q->next; nq++; | |
924 | } | |
925 | *qtail = 0; | |
926 | ||
927 | /* Find the current time so we can compute retransmission times properly. | |
928 | */ | |
929 | gettimeofday(&now, 0); | |
930 | when = now; | |
931 | ||
932 | /* Continue retransmitting until we have all the answers. */ | |
933 | while (nq) { | |
934 | ||
935 | /* Work out when we next want to wake up. */ | |
936 | if (TV_CMP(&now, >=, &when)) { | |
937 | do { | |
938 | if (next >= TO_MAX) die(1, "no responses: giving up"); | |
939 | next = TO_NEXT(next); | |
940 | TV_ADDL(&when, &when, next, 0); | |
941 | } while (TV_CMP(&when, <=, &now)); | |
942 | for (q = qq; q; q = q->next) { | |
943 | if (q->k) continue; | |
944 | for (s = q->s; s; s = s->next) { | |
945 | buf_init(&bout, obuf, sizeof(obuf)); | |
946 | buf_putstrz(&bout, tag); | |
947 | G_TOBUF(s->k.g, &bout, s->U); | |
948 | if (BBAD(&bout)) { | |
949 | moan("overflow while constructing request!"); | |
950 | continue; | |
951 | } | |
952 | sendto(sk, BBASE(&bout), BLEN(&bout), 0, | |
953 | (struct sockaddr *)&s->sin, sizeof(s->sin)); | |
954 | } | |
955 | } | |
956 | } | |
957 | ||
958 | /* Wait until something interesting happens. */ | |
959 | FD_ZERO(&fdin); | |
960 | FD_SET(sk, &fdin); | |
961 | TV_SUB(&tv, &when, &now); | |
962 | if (select(sk + 1, &fdin, 0, 0, &tv) < 0) | |
963 | die(1, "select failed: %s", strerror(errno)); | |
964 | gettimeofday(&now, 0); | |
965 | ||
966 | /* If we have an input event, process incoming packets. */ | |
967 | if (FD_ISSET(sk, &fdin)) { | |
968 | for (;;) { | |
969 | ||
970 | /* Read a packet and capture its address. */ | |
971 | len = sizeof(sin); | |
972 | nn = recvfrom(sk, ibuf, sizeof(ibuf), 0, | |
973 | (struct sockaddr *)&sin, &len); | |
974 | if (nn < 0) { | |
975 | if (errno == EAGAIN) break; | |
976 | else if (errno == EINTR) continue; | |
977 | else { | |
978 | moan("error receiving reply: %s", strerror(errno)); | |
979 | goto again; | |
980 | } | |
981 | } | |
982 | ||
983 | /* Wee whether this corresponds to any of our servers. Don't just | |
984 | * check the active servers, since this may be late replies caused by | |
985 | * retransmissions or similar. | |
986 | */ | |
987 | for (q = qq; q; q = q->next) { | |
988 | for (s = q->s; s; s = s->next) { | |
989 | if (s->sin.sin_addr.s_addr == sin.sin_addr.s_addr && | |
990 | s->sin.sin_port == sin.sin_port) | |
991 | goto found; | |
992 | } | |
993 | } | |
994 | moan("received reply from unexpected source %s:%d", | |
995 | inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); | |
996 | goto again; | |
997 | ||
998 | found: | |
999 | /* If the query we found has now been satisfied, ignore this packet. | |
1000 | */ | |
1001 | if (q->k) goto again; | |
1002 | ||
1003 | /* Start parsing the reply. */ | |
1004 | buf_init(&bin, ibuf, nn); | |
1005 | R = G_CREATE(s->k.g); | |
1006 | V = G_CREATE(s->k.g); W = G_CREATE(s->k.g); | |
1007 | Y = G_CREATE(s->k.g); Z = G_CREATE(s->k.g); | |
1008 | if (G_FROMBUF(s->k.g, &bin, V)) { | |
1009 | moan("invalid Diffie--Hellman vector from %s:%d", | |
1010 | inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); | |
1011 | goto again; | |
1012 | } | |
1013 | if (G_FROMBUF(s->k.g, &bin, W)) { | |
1014 | moan("invalid clue vector from %s:%d", | |
1015 | inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); | |
1016 | goto again; | |
1017 | } | |
1018 | D( debug_ge("V", s->k.g, V); debug_ge("W", s->k.g, W); ) | |
1019 | ||
1020 | /* We have V and W from the server; determine Y = u V, R = W + Y and | |
1021 | * Z = x R, and then derive the symmetric keys. | |
1022 | */ | |
1023 | G_EXP(s->k.g, Y, V, s->u); | |
1024 | G_MUL(s->k.g, R, W, Y); | |
1025 | G_EXP(s->k.g, Z, R, s->k.x); | |
1026 | D( debug_ge("R", s->k.g, R); | |
1027 | debug_ge("Y", s->k.g, Y); | |
1028 | debug_ge("Z", s->k.g, Z); ) | |
1029 | derive(&s->k, R, Z, "cipher", s->k.cc->name, s->k.cc->keysz, | |
1030 | &kk, &ksz); | |
1031 | c = GC_INIT(s->k.cc, kk, ksz); | |
1032 | derive(&s->k, R, Z, "mac", s->k.cc->name, s->k.cc->keysz, | |
1033 | &kk, &ksz); | |
1034 | m = GM_KEY(s->k.mc, kk, ksz); | |
1035 | ||
1036 | /* Find where the MAC tag is. */ | |
1037 | if ((t = buf_get(&bin, s->k.tagsz)) == 0) { | |
1038 | moan("missing tag from %s:%d", | |
1039 | inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); | |
1040 | goto again; | |
1041 | } | |
1042 | ||
1043 | /* Check the integrity of the ciphertext against the tag. */ | |
1044 | p = BCUR(&bin); n = BLEFT(&bin); | |
1045 | h = GM_INIT(m); | |
1046 | GH_HASH(h, p, n); | |
1047 | tt = GH_DONE(h, 0); | |
a5f873be | 1048 | if (!ct_memeq(t, tt, s->k.tagsz)) { |
247f344a MW |
1049 | moan("incorrect tag from %s:%d", |
1050 | inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); | |
1051 | goto again; | |
1052 | } | |
1053 | ||
1054 | /* Decrypt the result and declare this server done. */ | |
1055 | GC_DECRYPT(c, p, p, n); | |
1056 | if (s->h) { | |
1057 | GH_DESTROY(h); | |
1058 | h = GH_INIT(s->k.hc); | |
1059 | GH_HASH(h, p, n); | |
1060 | tt = GH_DONE(h, 0); | |
1061 | if (memcmp(tt, s->h, h->ops->c->hashsz) != 0) { | |
1062 | moan("response from %s:%d doesn't match hash", | |
1063 | inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); | |
1064 | goto again; | |
1065 | } | |
1066 | } | |
1067 | donequery(q, p, n); | |
1068 | ||
1069 | again: | |
1070 | /* Tidy things up for the next run through. */ | |
1071 | if (R) { G_DESTROY(s->k.g, R); R = 0; } | |
1072 | if (V) { G_DESTROY(s->k.g, V); V = 0; } | |
1073 | if (W) { G_DESTROY(s->k.g, W); W = 0; } | |
1074 | if (Y) { G_DESTROY(s->k.g, Y); Y = 0; } | |
1075 | if (Z) { G_DESTROY(s->k.g, Z); Z = 0; } | |
1076 | if (c) { GC_DESTROY(c); c = 0; } | |
1077 | if (m) { GM_DESTROY(m); m = 0; } | |
1078 | if (h) { GH_DESTROY(h); h = 0; } | |
1079 | } | |
1080 | } | |
1081 | } | |
1082 | ||
1083 | /* Check that all of the responses match up and XOR them together. */ | |
1084 | n = qq->sz; | |
1085 | if (n > BUFSZ) die(1, "response too large"); | |
1086 | memset(obuf, 0, n); | |
1087 | for (q = qq; q; q = q->next) { | |
1088 | if (!q->k) die(1, "INTERNAL: query not complete"); | |
1089 | if (q->sz != n) die(1, "inconsistent response sizes"); | |
1090 | for (j = 0; j < n; j++) obuf[j] ^= q->k[j]; | |
1091 | } | |
1092 | ||
1093 | /* Write out the completed answer. */ | |
1094 | p = obuf; | |
1095 | while (n) { | |
1096 | if ((nn = write(STDOUT_FILENO, p, n)) < 0) | |
1097 | die(1, "error writing response: %s", strerror(errno)); | |
1098 | p += nn; n -= nn; | |
1099 | } | |
1100 | return (0); | |
1101 | } | |
1102 | ||
1103 | /*----- Main program ------------------------------------------------------*/ | |
1104 | ||
1105 | static void usage(FILE *fp) | |
1106 | { | |
1107 | pquis(fp, "Usage: \n\ | |
1108 | $ [-OPTS] LABEL {ADDR:PORT | FILE} ...\n\ | |
1109 | $ [-OPTS] -l [ADDR:]PORT\n\ | |
1110 | "); | |
1111 | } | |
1112 | ||
1113 | static void version(FILE *fp) | |
a2fd0b74 | 1114 | { pquis(fp, "$, version " VERSION "\n"); } |
247f344a MW |
1115 | |
1116 | static void help(FILE *fp) | |
1117 | { | |
1118 | version(fp); | |
1119 | putc('\n', fp); | |
1120 | usage(fp); | |
1121 | fputs("\n\ | |
1122 | Options:\n\ | |
1123 | \n\ | |
1124 | -d, --daemon Run in the background while listening.\n\ | |
1125 | -k, --keyring=FILE Read keys from FILE. [default = `keyring']\n\ | |
1126 | -l, --listen Listen for incoming requests and serve keys.\n\ | |
1127 | -p, --pidfile=FILE Write process id to FILE if in daemon mode.\n\ | |
1128 | -r, --random=FILE Key random number generator with contents of FILE.\n\ | |
1129 | ", fp); | |
1130 | } | |
1131 | ||
1132 | int main(int argc, char *argv[]) | |
1133 | { | |
1134 | int argmin, argmax; | |
1135 | void *k; | |
1136 | size_t sz; | |
1137 | ||
1138 | ego(argv[0]); | |
1139 | for (;;) { | |
1140 | static const struct option opts[] = { | |
1141 | { "help", 0, 0, 'h' }, | |
1142 | { "version", 0, 0, 'v' }, | |
1143 | { "usage", 0, 0, 'u' }, | |
1144 | { "daemon", 0, 0, 'd' }, | |
1145 | { "keyfile", OPTF_ARGREQ, 0, 'k' }, | |
1146 | { "listen", 0, 0, 'l' }, | |
1147 | { "pidfile", OPTF_ARGREQ, 0, 'p' }, | |
1148 | { "random", OPTF_ARGREQ, 0, 'r' }, | |
1149 | { 0 } | |
1150 | }; | |
1151 | ||
1152 | int i = mdwopt(argc, argv, "hvu" "dk:lp:r:", opts, 0, 0, 0); | |
1153 | if (i < 0) break; | |
1154 | ||
1155 | switch (i) { | |
1156 | case 'h': help(stdout); exit(0); | |
1157 | case 'v': version(stdout); exit(0); | |
1158 | case 'u': usage(stdout); exit(0); | |
1159 | ||
1160 | case 'd': flags |= f_daemon; break; | |
1161 | case 'k': kfname = optarg; break; | |
1162 | case 'l': flags |= f_listen; break; | |
1163 | case 'p': pidfile = optarg; break; | |
1164 | case 'r': | |
1165 | if (snarf(optarg, &k, &sz)) | |
1166 | die(1, "failed to read `%s': %s", optarg, strerror(errno)); | |
1167 | rand_key(RAND_GLOBAL, k, sz); | |
1168 | break; | |
1169 | ||
1170 | default: flags |= f_bogus; break; | |
1171 | } | |
1172 | } | |
1173 | ||
1174 | argv += optind; argc -= optind; | |
1175 | if (flags & f_listen) argmin = argmax = 1; | |
1176 | else argmin = 2, argmax = -1; | |
1177 | if ((flags & f_bogus) || argc < argmin || (argmax >= 0 && argc > argmax)) | |
1178 | { usage(stderr); exit(1); } | |
1179 | ||
1180 | fwatch_init(&kfwatch, kfname); | |
1181 | kf = CREATE(key_file); | |
1182 | if (key_open(kf, kfname, KOPEN_READ, keymoan, 0)) | |
1183 | die(1, "failed to open keyring file `%s'", kfname); | |
1184 | ||
1185 | rand_noisesrc(RAND_GLOBAL, &noise_source); | |
1186 | rand_seed(RAND_GLOBAL, 512); | |
1187 | ||
1188 | if (flags & f_listen) return dolisten(argc, argv); | |
1189 | else return doquery(argc, argv); | |
1190 | } | |
1191 | ||
1192 | /*----- That's all, folks -------------------------------------------------*/ |