| 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/ct.h> |
| 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 | */ |
| 153 | static void PRINTF_LIKE(2, 3) complain(int sev, const char *msg, ...) |
| 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) { |
| 368 | complain(LOG_ERR, "bad tag size `%s' for mac `%s' in key %s", |
| 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 process 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 | /* Retransmission 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 a reply 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); |
| 1048 | if (!ct_memeq(t, tt, s->k.tagsz)) { |
| 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) |
| 1114 | { pquis(fp, "$, version " VERSION "\n"); } |
| 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 -------------------------------------------------*/ |