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