| 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 | /*----- Protocol summary --------------------------------------------------* |
| 473 | * |
| 474 | * There are two protocol versions. The original version works as follows. |
| 475 | * |
| 476 | * * Request |
| 477 | * memz KEYTAG tag of wanted secret |
| 478 | * ge U public vector |
| 479 | * |
| 480 | * * Response |
| 481 | * ge V public vector: V = v P |
| 482 | * ge W encrypted clue: W = R - Y = r P - v U |
| 483 | * mem[TAGSZ] TAG MAC tag on ciphertext |
| 484 | * mem[KSZ] CT secret, encrypted with Z = r X |
| 485 | * |
| 486 | * The new version provides forward secrecy, which involves additional flows. |
| 487 | * |
| 488 | * * Greeting |
| 489 | * u8 0 marker byte for new protocol |
| 490 | * u8 1 packet type |
| 491 | * mem8 KEYTAG wanted secret tag |
| 492 | * |
| 493 | * * Challenge |
| 494 | * u8 17 packet type |
| 495 | * u32 REF server's reference |
| 496 | * ge R public DLIES vector: R = r P |
| 497 | * ge W masked DH vector: W = V - Y = v P - r X |
| 498 | * |
| 499 | * * Response |
| 500 | * u8 0 marker byte for new protocol |
| 501 | * u8 2 packet type |
| 502 | * mem8 KEYTAG wanted secret tag |
| 503 | * u32 REF reference from challenge |
| 504 | * ge U public DH vector |
| 505 | * mem[HASHSZ] H0 hash; H0||H1 = H(U, V, Z), where Z = v U |
| 506 | * |
| 507 | * * Reply |
| 508 | * u8 18 packet type |
| 509 | * mem[TAGSZ] TAG MAC tag on ciphertext |
| 510 | * mem[KSZ] CT secret, encrypted with H1 |
| 511 | */ |
| 512 | |
| 513 | #define FWS_GREET 0x01 |
| 514 | #define FWS_CHALL 0x11 |
| 515 | #define FWS_RESP 0x02 |
| 516 | #define FWS_REPLY 0x12 |
| 517 | |
| 518 | /*----- Listening for requests --------------------------------------------*/ |
| 519 | |
| 520 | /* Rate limiting parameters. |
| 521 | * |
| 522 | * There's a probabilistic rate-limiting mechanism. A counter starts at 0. |
| 523 | * Every time we process a request, we increment the counter. The counter |
| 524 | * drops by RATE_REFILL every second. If the counter is below RATE_CREDIT |
| 525 | * then the request is processed; otherwise it is processed with probability |
| 526 | * 1/(counter - RATE_CREDIT). |
| 527 | */ |
| 528 | #define RATE_REFILL 10 /* Credits per second. */ |
| 529 | #define RATE_CREDIT 1000 /* Initial credit. */ |
| 530 | |
| 531 | static time_t now; |
| 532 | |
| 533 | /* Secrets table. |
| 534 | * |
| 535 | * The server doesn't want to maintain state for each client. Instead, we |
| 536 | * generate a global secret, and derive per-client secrets from it. A secret |
| 537 | * needs to have an expiry time (at which point we won't use it for new |
| 538 | * requests) and a deletion time (at which point we just forget that it ever |
| 539 | * existed). This lets us roll over to a new secret without leaving existing |
| 540 | * clients completely in the lurch. |
| 541 | * |
| 542 | * Secrets are kept in a linked list, ordered by expiry time. At any given |
| 543 | * time there is at most one unexpired secret (because we only make a new one |
| 544 | * when the old one expires). |
| 545 | */ |
| 546 | |
| 547 | struct secret { |
| 548 | struct secret *next; |
| 549 | uint32 seq; |
| 550 | time_t t_exp, t_del; |
| 551 | octet x[32]; |
| 552 | }; |
| 553 | static struct secret *secrets = 0, *live_secret = 0; |
| 554 | static uint32 next_secret_seq = 0; |
| 555 | #define T_SECEXP 30 |
| 556 | #define T_SECDEL 45 |
| 557 | |
| 558 | static void kill_dead_secrets(void) |
| 559 | { |
| 560 | struct secret *s = secrets, *ss; |
| 561 | |
| 562 | for (s = secrets; s && s->t_del <= now; s = ss) { |
| 563 | ss = s->next; |
| 564 | DESTROY(s); |
| 565 | } |
| 566 | secrets = 0; |
| 567 | if (!s) live_secret = 0; |
| 568 | } |
| 569 | |
| 570 | static struct secret *find_secret(uint32 seq) |
| 571 | { |
| 572 | struct secret *s; |
| 573 | |
| 574 | kill_dead_secrets(); |
| 575 | for (s = secrets; s; s = s->next) |
| 576 | if (s->seq == seq) return (s); |
| 577 | return (0); |
| 578 | } |
| 579 | |
| 580 | static struct secret *fresh_secret(void) |
| 581 | { |
| 582 | struct secret *s; |
| 583 | |
| 584 | if (live_secret && live_secret->t_exp > now) return (live_secret); |
| 585 | kill_dead_secrets(); |
| 586 | |
| 587 | s = CREATE(struct secret); |
| 588 | s->seq = next_secret_seq++; |
| 589 | s->next = 0; |
| 590 | rand_get(RAND_GLOBAL, s->x, sizeof(s->x)); |
| 591 | s->t_exp = now + T_SECEXP; s->t_del = now + T_SECDEL; |
| 592 | if (live_secret) live_secret->next = s; |
| 593 | else secrets = s; |
| 594 | live_secret = s; |
| 595 | return (s); |
| 596 | } |
| 597 | |
| 598 | static int fetch_key(const char *tag, struct sockaddr_in *sin, |
| 599 | key **ky, struct kinfo *k) |
| 600 | { |
| 601 | dstr d = DSTR_INIT, dd = DSTR_INIT; |
| 602 | key_data **kkd; |
| 603 | char *p, *q; |
| 604 | const char *pp; |
| 605 | struct in_addr in; |
| 606 | int ch, mlen, rc = -1; |
| 607 | |
| 608 | /* Find the key. */ |
| 609 | kfupdate(); |
| 610 | if (key_qtag(kf, tag, &d, ky, &kkd)) { |
| 611 | complain(LOG_WARNING, "unknown key tag `%s' from %s:%d", |
| 612 | tag, inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); |
| 613 | goto done; |
| 614 | } |
| 615 | |
| 616 | /* And make sure that it has the right shape. */ |
| 617 | if (((*ky)->k->e & KF_ENCMASK) != KENC_BINARY) { |
| 618 | complain(LOG_ERR, "key %s is not plain binary data", d.buf); |
| 619 | goto done; |
| 620 | } |
| 621 | |
| 622 | /* Find the list of clients, and look up the caller's address in the |
| 623 | * list. Entries have the form ADDRESS[/LEN][=TAG] and are separated by |
| 624 | * `;'. |
| 625 | */ |
| 626 | if ((pp = key_getattr(kf, *ky, "clients")) == 0) { |
| 627 | complain(LOG_WARNING, |
| 628 | "key %s requested from %s:%d has no `clients' attribute", |
| 629 | d.buf, inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); |
| 630 | goto done; |
| 631 | } |
| 632 | dstr_puts(&dd, pp); |
| 633 | p = dd.buf; |
| 634 | while (*p) { |
| 635 | q = p; |
| 636 | while (isdigit((unsigned char)*q) || *q == '.') q++; |
| 637 | ch = *q; *q++ = 0; |
| 638 | if (!inet_aton(p, &in)) goto skip; |
| 639 | if (ch != '/') |
| 640 | mlen = 32; |
| 641 | else { |
| 642 | p = q; |
| 643 | while (isdigit((unsigned char)*q)) q++; |
| 644 | ch = *q; *q++ = 0; |
| 645 | mlen = atoi(p); |
| 646 | } |
| 647 | if (((sin->sin_addr.s_addr ^ in.s_addr) & |
| 648 | htonl(0xffffffff << (32 - mlen))) == 0) |
| 649 | goto match; |
| 650 | skip: |
| 651 | if (!ch) break; |
| 652 | p = q; |
| 653 | while (*p && *p != ';') p++; |
| 654 | if (*p) p++; |
| 655 | } |
| 656 | complain(LOG_WARNING, "access to key %s denied to %s:%d", |
| 657 | d.buf, inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); |
| 658 | goto done; |
| 659 | |
| 660 | match: |
| 661 | /* Build a tag name for the caller's KEM key, either from the client |
| 662 | * match or the source address. |
| 663 | */ |
| 664 | if (ch != '=') { |
| 665 | DRESET(&dd); |
| 666 | dstr_puts(&dd, "client-"); |
| 667 | dstr_puts(&dd, inet_ntoa(sin->sin_addr)); |
| 668 | p = dd.buf; |
| 669 | } else { |
| 670 | p = q; |
| 671 | while (*q && *q != ';') q++; |
| 672 | if (*q == ';') *q++ = 0; |
| 673 | } |
| 674 | |
| 675 | /* Report the match. */ |
| 676 | complain(LOG_NOTICE, "client %s:%d (`%s') requests key %s", |
| 677 | inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), p, d.buf); |
| 678 | |
| 679 | /* Load the KEM key. */ |
| 680 | if (loadkey(p, k, 0)) goto done; |
| 681 | D( debug_ge("X", k.g, k.X); ) |
| 682 | |
| 683 | /* All complete. */ |
| 684 | rc = 0; |
| 685 | |
| 686 | done: |
| 687 | /* Clean everything up. */ |
| 688 | dstr_destroy(&d); |
| 689 | dstr_destroy(&dd); |
| 690 | if (rc) k_free(k); |
| 691 | return (rc); |
| 692 | } |
| 693 | |
| 694 | static int respond_v0(buf *bin, buf *bout, struct sockaddr_in *sin) |
| 695 | { |
| 696 | ge *R = 0, *U = 0, *V = 0, *W = 0, *Y = 0, *Z = 0; |
| 697 | mp *r = MP_NEW, *v = MP_NEW; |
| 698 | octet *kk, *t, *tt; |
| 699 | char *p; |
| 700 | size_t sz; |
| 701 | ghash *h = 0; |
| 702 | gmac *m = 0; |
| 703 | gcipher *c = 0; |
| 704 | struct kinfo k; |
| 705 | key *ky; |
| 706 | size_t ksz; |
| 707 | int rc = -1; |
| 708 | |
| 709 | /* Clear out the key state. */ |
| 710 | k_init(&k); |
| 711 | |
| 712 | /* Extract the key tag name. */ |
| 713 | if ((p = buf_getmemz(bin, &sz)) == 0) { |
| 714 | complain(LOG_WARNING, "invalid key tag from %s:%d", |
| 715 | inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); |
| 716 | goto done; |
| 717 | } |
| 718 | |
| 719 | /* Find the client's key and check that it's allowed. */ |
| 720 | if (fetch_key(p, sin, &ky, &k)) goto done; |
| 721 | |
| 722 | /* Read the caller's ephemeral key. */ |
| 723 | R = G_CREATE(k.g); W = G_CREATE(k.g); |
| 724 | U = G_CREATE(k.g); V = G_CREATE(k.g); |
| 725 | Y = G_CREATE(k.g); Z = G_CREATE(k.g); |
| 726 | if (G_FROMBUF(k.g, bin, U)) { |
| 727 | complain(LOG_WARNING, "failed to read ephemeral vector from %s:%d", |
| 728 | inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); |
| 729 | goto done; |
| 730 | } |
| 731 | D( debug_ge("U", k.g, U); ) |
| 732 | if (BLEFT(bin)) { |
| 733 | complain(LOG_WARNING, "trailing junk in request from %s:%d", |
| 734 | inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); |
| 735 | goto done; |
| 736 | } |
| 737 | |
| 738 | /* Ephemeral Diffie--Hellman. Choose v in GF(q) at random; compute |
| 739 | * V = v P and -Y = (-v) U. |
| 740 | */ |
| 741 | v = mprand_range(v, k.g->r, &rand_global, 0); |
| 742 | G_EXP(k.g, V, k.g->g, v); |
| 743 | D( debug_mp("v", v); debug_ge("V", k.g, V); ) |
| 744 | v = mp_sub(v, k.g->r, v); |
| 745 | G_EXP(k.g, Y, U, v); |
| 746 | D( debug_ge("-Y", k.g, Y); ) |
| 747 | |
| 748 | /* DLIES. Choose r in GF(q) at random; compute R = r P and Z = r X. Mask |
| 749 | * the clue R as W = R - Y. (Doing the subtraction here makes life easier |
| 750 | * at the other end, since we can determine -Y by negating v whereas the |
| 751 | * recipient must subtract vectors which may be less efficient.) |
| 752 | */ |
| 753 | r = mprand_range(r, k.g->r, &rand_global, 0); |
| 754 | G_EXP(k.g, R, k.g->g, r); |
| 755 | D( debug_mp("r", r); debug_ge("R", k.g, R); ) |
| 756 | G_EXP(k.g, Z, k.X, r); |
| 757 | G_MUL(k.g, W, R, Y); |
| 758 | D( debug_ge("Z", k.g, Z); debug_ge("W", k.g, W); ) |
| 759 | |
| 760 | /* Derive encryption and integrity keys. */ |
| 761 | derive(&k, R, Z, "cipher", k.cc->name, k.cc->keysz, &kk, &ksz); |
| 762 | c = GC_INIT(k.cc, kk, ksz); |
| 763 | derive(&k, R, Z, "mac", k.mc->name, k.mc->keysz, &kk, &ksz); |
| 764 | m = GM_KEY(k.mc, kk, ksz); |
| 765 | |
| 766 | /* Build the ciphertext and compute a MAC tag over it. */ |
| 767 | rc = 0; |
| 768 | if (G_TOBUF(k.g, bout, V) || |
| 769 | G_TOBUF(k.g, bout, W)) |
| 770 | goto done; |
| 771 | if ((t = buf_get(bout, k.tagsz)) == 0) goto done; |
| 772 | sz = ky->k->u.k.sz; |
| 773 | if (BENSURE(bout, sz)) goto done; |
| 774 | GC_ENCRYPT(c, ky->k->u.k.k, BCUR(bout), sz); |
| 775 | h = GM_INIT(m); |
| 776 | GH_HASH(h, BCUR(bout), sz); |
| 777 | tt = GH_DONE(h, 0); memcpy(t, tt, k.tagsz); |
| 778 | BSTEP(bout, sz); |
| 779 | |
| 780 | done: |
| 781 | /* Clear everything up and go home. */ |
| 782 | if (R) G_DESTROY(k.g, R); |
| 783 | if (U) G_DESTROY(k.g, U); |
| 784 | if (V) G_DESTROY(k.g, V); |
| 785 | if (W) G_DESTROY(k.g, W); |
| 786 | if (Y) G_DESTROY(k.g, Y); |
| 787 | if (Z) G_DESTROY(k.g, Z); |
| 788 | if (c) GC_DESTROY(c); |
| 789 | if (m) GM_DESTROY(m); |
| 790 | if (h) GH_DESTROY(h); |
| 791 | if (r) MP_DROP(r); |
| 792 | if (v) MP_DROP(v); |
| 793 | k_free(&k); |
| 794 | return (rc); |
| 795 | } |
| 796 | |
| 797 | static int dolisten(int argc, char *argv[]) |
| 798 | { |
| 799 | int sk; |
| 800 | char *p; |
| 801 | char *aspec; |
| 802 | ssize_t n; |
| 803 | fd_set fdin; |
| 804 | struct sockaddr_in sin; |
| 805 | socklen_t len; |
| 806 | buf bin, bout; |
| 807 | FILE *fp = 0; |
| 808 | unsigned bucket = 0, toks; |
| 809 | time_t last = 0; |
| 810 | |
| 811 | /* Set up the socket address. */ |
| 812 | sin.sin_family = AF_INET; |
| 813 | aspec = xstrdup(argv[0]); |
| 814 | if ((p = strchr(aspec, ':')) == 0) { |
| 815 | p = aspec; |
| 816 | sin.sin_addr.s_addr = INADDR_ANY; |
| 817 | } else { |
| 818 | *p++ = 0; |
| 819 | resolve(aspec, &sin.sin_addr); |
| 820 | } |
| 821 | sin.sin_port = htons(getport(p)); |
| 822 | |
| 823 | /* Create and set up the socket itself. */ |
| 824 | if ((sk = socket(PF_INET, SOCK_DGRAM, 0)) < 0 || |
| 825 | fdflags(sk, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC) || |
| 826 | bind(sk, (struct sockaddr *)&sin, sizeof(sin))) |
| 827 | die(1, "failed to create socket: %s", strerror(errno)); |
| 828 | |
| 829 | /* That's enough initialization. If we should fork, then do that. */ |
| 830 | if (flags & f_daemon) { |
| 831 | if (pidfile && (fp = fopen(pidfile, "w")) == 0) |
| 832 | die(1, "failed to open pidfile `%s': %s", pidfile, strerror(errno)); |
| 833 | openlog(QUIS, LOG_PID, LOG_DAEMON); |
| 834 | if (daemonize()) |
| 835 | die(1, "failed to become background process: %s", strerror(errno)); |
| 836 | if (pidfile) { fprintf(fp, "%ld\n", (long)getpid()); fclose(fp); } |
| 837 | flags |= f_syslog; |
| 838 | } |
| 839 | |
| 840 | for (;;) { |
| 841 | |
| 842 | /* Wait for something to happen. */ |
| 843 | FD_ZERO(&fdin); |
| 844 | FD_SET(sk, &fdin); |
| 845 | if (select(sk + 1, &fdin, 0, 0, 0) < 0) |
| 846 | die(1, "select failed: %s", strerror(errno)); |
| 847 | noise_timer(RAND_GLOBAL); |
| 848 | |
| 849 | /* Fetch a packet. */ |
| 850 | len = sizeof(sin); |
| 851 | n = recvfrom(sk, ibuf, sizeof(ibuf), 0, (struct sockaddr *)&sin, &len); |
| 852 | if (n < 0) { |
| 853 | if (errno != EAGAIN && errno != EINTR) |
| 854 | complain(LOG_ERR, "unexpected receive error: %s", strerror(errno)); |
| 855 | continue; |
| 856 | } |
| 857 | |
| 858 | /* Refill the bucket, and see whether we should reject this packet. */ |
| 859 | now = time(0); |
| 860 | if (bucket && now != last) { |
| 861 | toks = (now - last)*RATE_REFILL; |
| 862 | bucket = bucket < toks ? 0 : bucket - toks; |
| 863 | } |
| 864 | last = now; |
| 865 | if (bucket > RATE_CREDIT && |
| 866 | grand_range(&rand_global, bucket - RATE_CREDIT)) |
| 867 | continue; |
| 868 | bucket++; |
| 869 | |
| 870 | /* Set up the input buffer for parsing the request. */ |
| 871 | buf_init(&bin, ibuf, n); |
| 872 | buf_init(&bout, obuf, sizeof(obuf)); |
| 873 | |
| 874 | /* Handle the client's message. */ |
| 875 | if (respond_v0(&bin, &bout, &sin)) continue; |
| 876 | |
| 877 | /* Send the reply packet back to the caller. */ |
| 878 | if (!BOK(&bout)) goto bad; |
| 879 | if (sendto(sk, BBASE(&bout), BLEN(&bout), 0, |
| 880 | (struct sockaddr *)&sin, len) < 0) { |
| 881 | complain(LOG_ERR, "failed to send response to %s:%d: %s", |
| 882 | inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), |
| 883 | strerror(errno)); |
| 884 | continue; |
| 885 | } |
| 886 | |
| 887 | continue; |
| 888 | |
| 889 | bad: |
| 890 | /* Report a problem building the reply. */ |
| 891 | complain(LOG_ERR, "failed to construct response to %s:%d", |
| 892 | inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); |
| 893 | } |
| 894 | |
| 895 | return (-1); |
| 896 | } |
| 897 | |
| 898 | /*----- Sending requests and processing responses -------------------------*/ |
| 899 | |
| 900 | struct query { |
| 901 | struct query *next; |
| 902 | const char *tag; |
| 903 | octet *k; |
| 904 | size_t sz; |
| 905 | struct server *s; |
| 906 | }; |
| 907 | |
| 908 | struct server { |
| 909 | struct server *next; |
| 910 | struct sockaddr_in sin; |
| 911 | struct kinfo k; |
| 912 | const struct client_protocol *proto; |
| 913 | mp *u; |
| 914 | ge *U; |
| 915 | octet *h; |
| 916 | }; |
| 917 | |
| 918 | struct client_protocol { |
| 919 | const char *name; |
| 920 | int (*setup)(struct query *, struct server *); |
| 921 | int (*receive)(struct query *, struct server *, buf *, buf *); |
| 922 | int (*retransmit)(struct query *, struct server *, buf *); |
| 923 | }; |
| 924 | |
| 925 | /* Record a successful fetch of key material for a query Q. The data starts |
| 926 | * at K and is SZ bytes long. The data is copied: it's safe to overwrite it. |
| 927 | */ |
| 928 | static int donequery(struct query *q, struct server *s, |
| 929 | const void *k, size_t sz) |
| 930 | { |
| 931 | octet *tt; |
| 932 | ghash *h = 0; |
| 933 | int diffp; |
| 934 | |
| 935 | /* If we have a hash, check that the fragment matches it. */ |
| 936 | if (s && s->h) { |
| 937 | h = GH_INIT(s->k.hc); |
| 938 | GH_HASH(h, k, sz); |
| 939 | tt = GH_DONE(h, 0); |
| 940 | diffp = memcmp(tt, s->h, h->ops->c->hashsz); |
| 941 | GH_DESTROY(h); |
| 942 | if (diffp) { |
| 943 | moan("response from %s:%d doesn't match hash", |
| 944 | inet_ntoa(s->sin.sin_addr), ntohs(s->sin.sin_port)); |
| 945 | return (-1); |
| 946 | } |
| 947 | } |
| 948 | |
| 949 | /* Stash a copy of the key fragment for later. */ |
| 950 | q->k = xmalloc(sz); |
| 951 | memcpy(q->k, k, sz); |
| 952 | q->sz = sz; nq--; |
| 953 | |
| 954 | /* All good. */ |
| 955 | return (0); |
| 956 | } |
| 957 | |
| 958 | static int setup_v0(struct query *q, struct server *s) |
| 959 | { |
| 960 | /* Choose an ephemeral private key u. Let x be our private key. We |
| 961 | * compute U = u P and transmit this. |
| 962 | */ |
| 963 | s->u = mprand_range(MP_NEW, s->k.g->r, &rand_global, 0); |
| 964 | s->U = G_CREATE(s->k.g); |
| 965 | G_EXP(s->k.g, s->U, s->k.g->g, s->u); |
| 966 | D( debug_mp("u", s->u); debug_ge("U", s->k.g, s->U); ) |
| 967 | |
| 968 | return (0); |
| 969 | } |
| 970 | |
| 971 | static int retransmit_v0(struct query *q, struct server *s, buf *bout) |
| 972 | { |
| 973 | buf_putstrz(bout, q->tag); |
| 974 | G_TOBUF(s->k.g, bout, s->U); |
| 975 | return (0); |
| 976 | } |
| 977 | |
| 978 | static int receive_v0(struct query *q, struct server *s, buf *bin, buf *bout) |
| 979 | { |
| 980 | ge *R, *V = 0, *W = 0, *Y = 0, *Z = 0; |
| 981 | octet *kk, *t, *tt; |
| 982 | gcipher *c = 0; |
| 983 | gmac *m = 0; |
| 984 | ghash *h = 0; |
| 985 | size_t n, ksz; |
| 986 | octet *p; |
| 987 | int rc = -1; |
| 988 | |
| 989 | R = G_CREATE(s->k.g); |
| 990 | V = G_CREATE(s->k.g); W = G_CREATE(s->k.g); |
| 991 | Y = G_CREATE(s->k.g); Z = G_CREATE(s->k.g); |
| 992 | if (G_FROMBUF(s->k.g, bin, V)) { |
| 993 | moan("invalid Diffie--Hellman vector from %s:%d", |
| 994 | inet_ntoa(s->sin.sin_addr), ntohs(s->sin.sin_port)); |
| 995 | goto done; |
| 996 | } |
| 997 | if (G_FROMBUF(s->k.g, bin, W)) { |
| 998 | moan("invalid clue vector from %s:%d", |
| 999 | inet_ntoa(s->sin.sin_addr), ntohs(s->sin.sin_port)); |
| 1000 | goto done; |
| 1001 | } |
| 1002 | D( debug_ge("V", s->k.g, V); debug_ge("W", s->k.g, W); ) |
| 1003 | |
| 1004 | /* We have V and W from the server; determine Y = u V, R = W + Y and |
| 1005 | * Z = x R, and then derive the symmetric keys. |
| 1006 | */ |
| 1007 | G_EXP(s->k.g, Y, V, s->u); |
| 1008 | G_MUL(s->k.g, R, W, Y); |
| 1009 | G_EXP(s->k.g, Z, R, s->k.x); |
| 1010 | D( debug_ge("R", s->k.g, R); |
| 1011 | debug_ge("Y", s->k.g, Y); |
| 1012 | debug_ge("Z", s->k.g, Z); ) |
| 1013 | derive(&s->k, R, Z, "cipher", s->k.cc->name, s->k.cc->keysz, &kk, &ksz); |
| 1014 | c = GC_INIT(s->k.cc, kk, ksz); |
| 1015 | derive(&s->k, R, Z, "mac", s->k.mc->name, s->k.mc->keysz, &kk, &ksz); |
| 1016 | m = GM_KEY(s->k.mc, kk, ksz); |
| 1017 | |
| 1018 | /* Find where the MAC tag is. */ |
| 1019 | if ((t = buf_get(bin, s->k.tagsz)) == 0) { |
| 1020 | moan("missing tag from %s:%d", |
| 1021 | inet_ntoa(s->sin.sin_addr), ntohs(s->sin.sin_port)); |
| 1022 | goto done; |
| 1023 | } |
| 1024 | |
| 1025 | /* Check the integrity of the ciphertext against the tag. */ |
| 1026 | p = BCUR(bin); n = BLEFT(bin); |
| 1027 | h = GM_INIT(m); |
| 1028 | GH_HASH(h, p, n); |
| 1029 | tt = GH_DONE(h, 0); |
| 1030 | if (!ct_memeq(t, tt, s->k.tagsz)) { |
| 1031 | moan("incorrect tag from %s:%d", |
| 1032 | inet_ntoa(s->sin.sin_addr), ntohs(s->sin.sin_port)); |
| 1033 | goto done; |
| 1034 | } |
| 1035 | |
| 1036 | /* Decrypt the result and declare this server done. */ |
| 1037 | GC_DECRYPT(c, p, p, n); |
| 1038 | rc = donequery(q, s, p, n); |
| 1039 | |
| 1040 | done: |
| 1041 | /* Clear up and go home. */ |
| 1042 | if (R) G_DESTROY(s->k.g, R); |
| 1043 | if (V) G_DESTROY(s->k.g, V); |
| 1044 | if (W) G_DESTROY(s->k.g, W); |
| 1045 | if (Y) G_DESTROY(s->k.g, Y); |
| 1046 | if (Z) G_DESTROY(s->k.g, Z); |
| 1047 | if (c) GC_DESTROY(c); |
| 1048 | if (m) GM_DESTROY(m); |
| 1049 | if (h) GH_DESTROY(h); |
| 1050 | return (rc); |
| 1051 | } |
| 1052 | |
| 1053 | static const struct client_protocol prototab[] = { |
| 1054 | { "v0", setup_v0, receive_v0, retransmit_v0 }, |
| 1055 | { 0 } |
| 1056 | }; |
| 1057 | |
| 1058 | /* Initialize a query to a remote server. */ |
| 1059 | static struct query *qinit_net(const char *tag, const char *spec) |
| 1060 | { |
| 1061 | struct query *q; |
| 1062 | struct server *s, **stail; |
| 1063 | dstr d = DSTR_INIT, dd = DSTR_INIT; |
| 1064 | const struct client_protocol *proto; |
| 1065 | hex_ctx hc; |
| 1066 | char *p, *pp, ch; |
| 1067 | |
| 1068 | /* Allocate the query block. */ |
| 1069 | q = CREATE(struct query); |
| 1070 | q->tag = tag; |
| 1071 | stail = &q->s; |
| 1072 | |
| 1073 | /* Put the spec somewhere we can hack at it. */ |
| 1074 | dstr_puts(&d, spec); |
| 1075 | p = d.buf; |
| 1076 | |
| 1077 | /* Parse the query spec. Entries have the form ADDRESS:PORT[=TAG][#HASH] |
| 1078 | * and are separated by `;'. |
| 1079 | */ |
| 1080 | while (*p) { |
| 1081 | |
| 1082 | /* Allocate a new server node. */ |
| 1083 | s = CREATE(struct server); |
| 1084 | s->sin.sin_family = AF_INET; |
| 1085 | |
| 1086 | /* Extract the server address. */ |
| 1087 | if ((pp = strchr(p, ':')) == 0) |
| 1088 | die(1, "invalid syntax: missing `:PORT'"); |
| 1089 | *pp++ = 0; |
| 1090 | resolve(p, &s->sin.sin_addr); |
| 1091 | |
| 1092 | /* Extract the port number. */ |
| 1093 | p = pp; |
| 1094 | while (isdigit((unsigned char)*pp)) pp++; |
| 1095 | ch = *pp; *pp++ = 0; |
| 1096 | s->sin.sin_port = htons(getport(p)); |
| 1097 | |
| 1098 | /* See if there's a protocol name. */ |
| 1099 | if (ch != '?') |
| 1100 | p = "v0"; |
| 1101 | else { |
| 1102 | p = pp; |
| 1103 | pp += strcspn(pp, ";#="); |
| 1104 | ch = *pp; *pp++ = 0; |
| 1105 | } |
| 1106 | for (proto = prototab; proto->name; proto++) |
| 1107 | if (strcmp(proto->name, p) == 0) goto found_proto; |
| 1108 | die(1, "unknown protocol name `%s'", p); |
| 1109 | found_proto: |
| 1110 | s->proto = proto; |
| 1111 | |
| 1112 | /* If there's a key tag then extract that; otherwise use a default. */ |
| 1113 | if (ch != '=') |
| 1114 | p = "udpkey-kem"; |
| 1115 | else { |
| 1116 | p = pp; |
| 1117 | pp += strcspn(pp, ";#"); |
| 1118 | ch = *pp; *pp++ = 0; |
| 1119 | } |
| 1120 | if (loadkey(p, &s->k, 1)) exit(1); |
| 1121 | D( debug_mp("x", s->k.x); debug_ge("X", s->k.g, s->k.X); ) |
| 1122 | |
| 1123 | /* Link the server on. */ |
| 1124 | *stail = s; stail = &s->next; |
| 1125 | |
| 1126 | /* If there's a trailing hash then extract it. */ |
| 1127 | if (ch != '#') |
| 1128 | s->h = 0; |
| 1129 | else { |
| 1130 | p = pp; |
| 1131 | while (*pp == '-' || isxdigit((unsigned char)*pp)) pp++; |
| 1132 | hex_init(&hc); |
| 1133 | DRESET(&dd); |
| 1134 | hex_decode(&hc, p, pp - p, &dd); |
| 1135 | if (dd.len != s->k.hc->hashsz) die(1, "incorrect hash length"); |
| 1136 | s->h = xmalloc(dd.len); |
| 1137 | memcpy(s->h, dd.buf, dd.len); |
| 1138 | ch = *pp++; |
| 1139 | } |
| 1140 | |
| 1141 | /* Initialize the protocol. */ |
| 1142 | if (s->proto->setup(q, s)) die(1, "failed to initialize protocol"); |
| 1143 | |
| 1144 | /* If there are more servers, then continue parsing. */ |
| 1145 | if (!ch) break; |
| 1146 | else if (ch != ';') die(1, "invalid syntax: expected `;'"); |
| 1147 | p = pp; |
| 1148 | } |
| 1149 | |
| 1150 | /* Terminate the server list and return. */ |
| 1151 | *stail = 0; |
| 1152 | q->k = 0; |
| 1153 | dstr_destroy(&d); |
| 1154 | dstr_destroy(&dd); |
| 1155 | return (q); |
| 1156 | } |
| 1157 | |
| 1158 | /* Handle a `query' to a local file. */ |
| 1159 | static struct query *qinit_file(const char *tag, const char *file) |
| 1160 | { |
| 1161 | struct query *q; |
| 1162 | void *k; |
| 1163 | size_t sz; |
| 1164 | |
| 1165 | /* Snarf the file. */ |
| 1166 | q = CREATE(struct query); |
| 1167 | if (snarf(file, &k, &sz)) |
| 1168 | die(1, "failed to read `%s': %s", file, strerror(errno)); |
| 1169 | q->s = 0; |
| 1170 | donequery(q, 0, k, sz); |
| 1171 | return (q); |
| 1172 | } |
| 1173 | |
| 1174 | /* Retransmission and timeout parameters. */ |
| 1175 | #define TO_NEXT(t) (((t) + 2)*4/3) /* Timeout growth function */ |
| 1176 | #define TO_MAX 30 /* When to give up */ |
| 1177 | |
| 1178 | static int doquery(int argc, char *argv[]) |
| 1179 | { |
| 1180 | struct query *q = 0, *qq, **qtail = &qq; |
| 1181 | struct server *s = 0; |
| 1182 | const char *tag = argv[0]; |
| 1183 | octet *p; |
| 1184 | int i; |
| 1185 | int sk; |
| 1186 | fd_set fdin; |
| 1187 | struct timeval now, when, tv; |
| 1188 | struct sockaddr_in sin; |
| 1189 | socklen_t len; |
| 1190 | unsigned next = 0; |
| 1191 | buf bin, bout; |
| 1192 | size_t n, j; |
| 1193 | ssize_t nn; |
| 1194 | |
| 1195 | /* Create a socket. We just use the one socket for everything. We don't |
| 1196 | * care which port we get allocated. |
| 1197 | */ |
| 1198 | if ((sk = socket(PF_INET, SOCK_DGRAM, 0)) < 0 || |
| 1199 | fdflags(sk, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC)) |
| 1200 | die(1, "failed to create socket: %s", strerror(errno)); |
| 1201 | |
| 1202 | /* Parse the query target specifications. The adjustments of `nq' aren't |
| 1203 | * in the right order but that doesn't matter. |
| 1204 | */ |
| 1205 | for (i = 1; i < argc; i++) { |
| 1206 | if (*argv[i] == '.' || *argv[i] == '/') q = qinit_file(tag, argv[i]); |
| 1207 | else if (strchr(argv[i], ':')) q = qinit_net(tag, argv[i]); |
| 1208 | else die(1, "unrecognized query target `%s'", argv[i]); |
| 1209 | *qtail = q; qtail = &q->next; nq++; |
| 1210 | } |
| 1211 | *qtail = 0; |
| 1212 | |
| 1213 | /* Find the current time so we can compute retransmission times properly. |
| 1214 | */ |
| 1215 | gettimeofday(&now, 0); |
| 1216 | when = now; |
| 1217 | |
| 1218 | /* Continue retransmitting until we have all the answers. */ |
| 1219 | while (nq) { |
| 1220 | |
| 1221 | /* Work out when we next want to wake up. */ |
| 1222 | if (TV_CMP(&now, >=, &when)) { |
| 1223 | do { |
| 1224 | if (next >= TO_MAX) die(1, "no responses: giving up"); |
| 1225 | next = TO_NEXT(next); |
| 1226 | TV_ADDL(&when, &when, next, 0); |
| 1227 | } while (TV_CMP(&when, <=, &now)); |
| 1228 | for (q = qq; q; q = q->next) { |
| 1229 | if (q->k) continue; |
| 1230 | for (s = q->s; s; s = s->next) { |
| 1231 | buf_init(&bout, obuf, sizeof(obuf)); |
| 1232 | if (s->proto->retransmit(q, s, &bout)) continue; |
| 1233 | if (BBAD(&bout)) { |
| 1234 | moan("overflow while constructing request!"); |
| 1235 | continue; |
| 1236 | } |
| 1237 | sendto(sk, BBASE(&bout), BLEN(&bout), 0, |
| 1238 | (struct sockaddr *)&s->sin, sizeof(s->sin)); |
| 1239 | } |
| 1240 | } |
| 1241 | } |
| 1242 | |
| 1243 | /* Wait until something interesting happens. */ |
| 1244 | FD_ZERO(&fdin); |
| 1245 | FD_SET(sk, &fdin); |
| 1246 | TV_SUB(&tv, &when, &now); |
| 1247 | if (select(sk + 1, &fdin, 0, 0, &tv) < 0) |
| 1248 | die(1, "select failed: %s", strerror(errno)); |
| 1249 | gettimeofday(&now, 0); |
| 1250 | |
| 1251 | /* If we have an input event, process incoming packets. */ |
| 1252 | if (FD_ISSET(sk, &fdin)) { |
| 1253 | for (;;) { |
| 1254 | |
| 1255 | /* Read a packet and capture its address. */ |
| 1256 | len = sizeof(sin); |
| 1257 | nn = recvfrom(sk, ibuf, sizeof(ibuf), 0, |
| 1258 | (struct sockaddr *)&sin, &len); |
| 1259 | if (nn < 0) { |
| 1260 | if (errno == EAGAIN) break; |
| 1261 | else if (errno == EINTR) continue; |
| 1262 | else { |
| 1263 | moan("error receiving reply: %s", strerror(errno)); |
| 1264 | continue; |
| 1265 | } |
| 1266 | } |
| 1267 | |
| 1268 | /* See whether this corresponds to any of our servers. Don't just |
| 1269 | * check the active servers, since this may be late a reply caused by |
| 1270 | * retransmissions or similar. |
| 1271 | */ |
| 1272 | for (q = qq; q; q = q->next) { |
| 1273 | for (s = q->s; s; s = s->next) { |
| 1274 | if (s->sin.sin_addr.s_addr == sin.sin_addr.s_addr && |
| 1275 | s->sin.sin_port == sin.sin_port) |
| 1276 | goto found; |
| 1277 | } |
| 1278 | } |
| 1279 | moan("received reply from unexpected source %s:%d", |
| 1280 | inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); |
| 1281 | continue; |
| 1282 | |
| 1283 | found: |
| 1284 | /* If the query we found has now been satisfied, ignore this packet. |
| 1285 | */ |
| 1286 | if (q->k) continue; |
| 1287 | |
| 1288 | /* Parse the reply, and either finish the job or get a message to |
| 1289 | * send back to the server. |
| 1290 | */ |
| 1291 | buf_init(&bin, ibuf, nn); |
| 1292 | buf_init(&bout, obuf, sizeof(obuf)); |
| 1293 | if (s->proto->receive(q, s, &bin, &bout)) continue; |
| 1294 | if (q->k) continue; |
| 1295 | if (!BLEN(&bout) && s->proto->retransmit(q, s, &bout)) continue; |
| 1296 | if (BBAD(&bout)) { |
| 1297 | moan("overflow while constructing request!"); |
| 1298 | continue; |
| 1299 | } |
| 1300 | sendto(sk, BBASE(&bout), BLEN(&bout), 0, |
| 1301 | (struct sockaddr *)&s->sin, sizeof(s->sin)); |
| 1302 | } |
| 1303 | } |
| 1304 | } |
| 1305 | |
| 1306 | /* Check that all of the responses match up and XOR them together. */ |
| 1307 | n = qq->sz; |
| 1308 | if (n > BUFSZ) die(1, "response too large"); |
| 1309 | memset(obuf, 0, n); |
| 1310 | for (q = qq; q; q = q->next) { |
| 1311 | if (!q->k) die(1, "INTERNAL: query not complete"); |
| 1312 | if (q->sz != n) die(1, "inconsistent response sizes"); |
| 1313 | for (j = 0; j < n; j++) obuf[j] ^= q->k[j]; |
| 1314 | } |
| 1315 | |
| 1316 | /* Write out the completed answer. */ |
| 1317 | p = obuf; |
| 1318 | while (n) { |
| 1319 | if ((nn = write(STDOUT_FILENO, p, n)) < 0) |
| 1320 | die(1, "error writing response: %s", strerror(errno)); |
| 1321 | p += nn; n -= nn; |
| 1322 | } |
| 1323 | return (0); |
| 1324 | } |
| 1325 | |
| 1326 | /*----- Main program ------------------------------------------------------*/ |
| 1327 | |
| 1328 | static void usage(FILE *fp) |
| 1329 | { |
| 1330 | pquis(fp, "Usage: \n\ |
| 1331 | $ [-OPTS] LABEL {ADDR:PORT[=TAG][#HASH];... | FILE} ...\n\ |
| 1332 | $ [-OPTS] -l [ADDR:]PORT\n\ |
| 1333 | "); |
| 1334 | } |
| 1335 | |
| 1336 | static void version(FILE *fp) |
| 1337 | { pquis(fp, "$, version " VERSION "\n"); } |
| 1338 | |
| 1339 | static void help(FILE *fp) |
| 1340 | { |
| 1341 | version(fp); |
| 1342 | putc('\n', fp); |
| 1343 | usage(fp); |
| 1344 | fputs("\n\ |
| 1345 | Options:\n\ |
| 1346 | \n\ |
| 1347 | -d, --daemon Run in the background while listening.\n\ |
| 1348 | -k, --keyring=FILE Read keys from FILE. [default = `keyring']\n\ |
| 1349 | -l, --listen Listen for incoming requests and serve keys.\n\ |
| 1350 | -p, --pidfile=FILE Write process id to FILE if in daemon mode.\n\ |
| 1351 | -r, --random=FILE Key random number generator with contents of FILE.\n\ |
| 1352 | ", fp); |
| 1353 | } |
| 1354 | |
| 1355 | int main(int argc, char *argv[]) |
| 1356 | { |
| 1357 | int argmin, argmax; |
| 1358 | void *k; |
| 1359 | size_t sz; |
| 1360 | |
| 1361 | ego(argv[0]); |
| 1362 | for (;;) { |
| 1363 | static const struct option opts[] = { |
| 1364 | { "help", 0, 0, 'h' }, |
| 1365 | { "version", 0, 0, 'v' }, |
| 1366 | { "usage", 0, 0, 'u' }, |
| 1367 | { "daemon", 0, 0, 'd' }, |
| 1368 | { "keyfile", OPTF_ARGREQ, 0, 'k' }, |
| 1369 | { "listen", 0, 0, 'l' }, |
| 1370 | { "pidfile", OPTF_ARGREQ, 0, 'p' }, |
| 1371 | { "random", OPTF_ARGREQ, 0, 'r' }, |
| 1372 | { 0 } |
| 1373 | }; |
| 1374 | |
| 1375 | int i = mdwopt(argc, argv, "hvu" "dk:lp:r:", opts, 0, 0, 0); |
| 1376 | if (i < 0) break; |
| 1377 | |
| 1378 | switch (i) { |
| 1379 | case 'h': help(stdout); exit(0); |
| 1380 | case 'v': version(stdout); exit(0); |
| 1381 | case 'u': usage(stdout); exit(0); |
| 1382 | |
| 1383 | case 'd': flags |= f_daemon; break; |
| 1384 | case 'k': kfname = optarg; break; |
| 1385 | case 'l': flags |= f_listen; break; |
| 1386 | case 'p': pidfile = optarg; break; |
| 1387 | case 'r': |
| 1388 | if (snarf(optarg, &k, &sz)) |
| 1389 | die(1, "failed to read `%s': %s", optarg, strerror(errno)); |
| 1390 | rand_key(RAND_GLOBAL, k, sz); |
| 1391 | break; |
| 1392 | |
| 1393 | default: flags |= f_bogus; break; |
| 1394 | } |
| 1395 | } |
| 1396 | |
| 1397 | argv += optind; argc -= optind; |
| 1398 | if (flags & f_listen) argmin = argmax = 1; |
| 1399 | else argmin = 2, argmax = -1; |
| 1400 | if ((flags & f_bogus) || argc < argmin || (argmax >= 0 && argc > argmax)) |
| 1401 | { usage(stderr); exit(1); } |
| 1402 | |
| 1403 | fwatch_init(&kfwatch, kfname); |
| 1404 | kf = CREATE(key_file); |
| 1405 | if (key_open(kf, kfname, KOPEN_READ, keymoan, 0)) |
| 1406 | die(1, "failed to open keyring file `%s'", kfname); |
| 1407 | |
| 1408 | rand_noisesrc(RAND_GLOBAL, &noise_source); |
| 1409 | rand_seed(RAND_GLOBAL, 512); |
| 1410 | |
| 1411 | if (flags & f_listen) return dolisten(argc, argv); |
| 1412 | else return doquery(argc, argv); |
| 1413 | } |
| 1414 | |
| 1415 | /*----- That's all, folks -------------------------------------------------*/ |