1 /* $OpenBSD: relay_http.c,v 1.55 2015/12/15 10:36:59 reyk Exp $ */ 2 3 /* 4 * Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/queue.h> 21 #include <sys/time.h> 22 #include <sys/socket.h> 23 #include <sys/tree.h> 24 25 #include <netinet/in.h> 26 #include <arpa/inet.h> 27 28 #include <limits.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <errno.h> 32 #include <string.h> 33 #include <time.h> 34 #include <event.h> 35 #include <fnmatch.h> 36 #include <siphash.h> 37 #include <imsg.h> 38 #include <unistd.h> 39 40 #include "relayd.h" 41 #include "http.h" 42 43 static int _relay_lookup_url(struct ctl_relay_event *, char *, char *, 44 char *, struct kv *); 45 int relay_lookup_url(struct ctl_relay_event *, 46 const char *, struct kv *); 47 int relay_lookup_query(struct ctl_relay_event *, struct kv *); 48 int relay_lookup_cookie(struct ctl_relay_event *, const char *, 49 struct kv *); 50 void relay_read_httpcontent(struct bufferevent *, void *); 51 void relay_read_httpchunks(struct bufferevent *, void *); 52 char *relay_expand_http(struct ctl_relay_event *, char *, 53 char *, size_t); 54 int relay_writeheader_kv(struct ctl_relay_event *, struct kv *); 55 int relay_writeheader_http(struct ctl_relay_event *, 56 struct ctl_relay_event *); 57 int relay_writerequest_http(struct ctl_relay_event *, 58 struct ctl_relay_event *); 59 int relay_writeresponse_http(struct ctl_relay_event *, 60 struct ctl_relay_event *); 61 void relay_reset_http(struct ctl_relay_event *); 62 static int relay_httpmethod_cmp(const void *, const void *); 63 static int relay_httperror_cmp(const void *, const void *); 64 int relay_httpquery_test(struct ctl_relay_event *, 65 struct relay_rule *, struct kvlist *); 66 int relay_httpheader_test(struct ctl_relay_event *, 67 struct relay_rule *, struct kvlist *); 68 int relay_httppath_test(struct ctl_relay_event *, 69 struct relay_rule *, struct kvlist *); 70 int relay_httpurl_test(struct ctl_relay_event *, 71 struct relay_rule *, struct kvlist *); 72 int relay_httpcookie_test(struct ctl_relay_event *, 73 struct relay_rule *, struct kvlist *); 74 int relay_apply_actions(struct ctl_relay_event *, struct kvlist *); 75 int relay_match_actions(struct ctl_relay_event *, 76 struct relay_rule *, struct kvlist *, struct kvlist *); 77 void relay_httpdesc_free(struct http_descriptor *); 78 79 static struct relayd *env = NULL; 80 81 static struct http_method http_methods[] = HTTP_METHODS; 82 static struct http_error http_errors[] = HTTP_ERRORS; 83 84 void 85 relay_http(struct relayd *x_env) 86 { 87 if (x_env != NULL) 88 env = x_env; 89 90 DPRINTF("%s: sorting lookup tables, pid %d", __func__, getpid()); 91 92 /* Sort the HTTP lookup arrays */ 93 qsort(http_methods, sizeof(http_methods) / 94 sizeof(http_methods[0]) - 1, 95 sizeof(http_methods[0]), relay_httpmethod_cmp); 96 qsort(http_errors, sizeof(http_errors) / 97 sizeof(http_errors[0]) - 1, 98 sizeof(http_errors[0]), relay_httperror_cmp); 99 } 100 101 void 102 relay_http_init(struct relay *rlay) 103 { 104 rlay->rl_proto->close = relay_close_http; 105 106 relay_http(NULL); 107 108 /* Calculate skip step for the filter rules (may take a while) */ 109 relay_calc_skip_steps(&rlay->rl_proto->rules); 110 } 111 112 int 113 relay_httpdesc_init(struct ctl_relay_event *cre) 114 { 115 struct http_descriptor *desc; 116 117 if ((desc = calloc(1, sizeof(*desc))) == NULL) 118 return (-1); 119 120 RB_INIT(&desc->http_headers); 121 cre->desc = desc; 122 123 return (0); 124 } 125 126 void 127 relay_httpdesc_free(struct http_descriptor *desc) 128 { 129 free(desc->http_path); 130 desc->http_path = NULL; 131 free(desc->http_query); 132 desc->http_query = NULL; 133 free(desc->http_version); 134 desc->http_version = NULL; 135 free(desc->query_key); 136 desc->query_key = NULL; 137 free(desc->query_val); 138 desc->query_val = NULL; 139 kv_purge(&desc->http_headers); 140 desc->http_lastheader = NULL; 141 } 142 143 void 144 relay_read_http(struct bufferevent *bev, void *arg) 145 { 146 struct ctl_relay_event *cre = arg; 147 struct http_descriptor *desc = cre->desc; 148 struct rsession *con = cre->con; 149 struct relay *rlay = con->se_relay; 150 struct protocol *proto = rlay->rl_proto; 151 struct evbuffer *src = EVBUFFER_INPUT(bev); 152 char *line = NULL, *key, *value; 153 int action; 154 const char *errstr; 155 size_t size, linelen; 156 struct kv *hdr = NULL; 157 158 getmonotime(&con->se_tv_last); 159 cre->timedout = 0; 160 161 size = EVBUFFER_LENGTH(src); 162 DPRINTF("%s: session %d: size %lu, to read %lld", 163 __func__, con->se_id, size, cre->toread); 164 if (!size) { 165 if (cre->dir == RELAY_DIR_RESPONSE) 166 return; 167 cre->toread = TOREAD_HTTP_HEADER; 168 goto done; 169 } 170 171 while (!cre->done && (line = evbuffer_readline(src)) != NULL) { 172 linelen = strlen(line); 173 174 /* 175 * An empty line indicates the end of the request. 176 * libevent already stripped the \r\n for us. 177 */ 178 if (!linelen) { 179 cre->done = 1; 180 free(line); 181 break; 182 } 183 key = line; 184 185 /* Limit the total header length minus \r\n */ 186 cre->headerlen += linelen; 187 if (cre->headerlen > RELAY_MAXHEADERLENGTH) { 188 free(line); 189 relay_abort_http(con, 413, "request too large", 0); 190 return; 191 } 192 193 /* 194 * The first line is the GET/POST/PUT/... request, 195 * subsequent lines are HTTP headers. 196 */ 197 if (++cre->line == 1) 198 value = strchr(key, ' '); 199 else if (*key == ' ' || *key == '\t') 200 /* Multiline headers wrap with a space or tab */ 201 value = NULL; 202 else 203 value = strchr(key, ':'); 204 if (value == NULL) { 205 if (cre->line <= 2) { 206 free(line); 207 relay_abort_http(con, 400, "malformed", 0); 208 return; 209 } 210 211 /* Append line to the last header, if present */ 212 if (kv_extend(&desc->http_headers, 213 desc->http_lastheader, line) == NULL) { 214 free(line); 215 goto fail; 216 } 217 218 free(line); 219 continue; 220 } 221 if (*value == ':') { 222 *value++ = '\0'; 223 value += strspn(value, " \t\r\n"); 224 } else { 225 *value++ = '\0'; 226 } 227 228 DPRINTF("%s: session %d: header '%s: %s'", __func__, 229 con->se_id, key, value); 230 231 /* 232 * Identify and handle specific HTTP request methods 233 */ 234 if (cre->line == 1 && cre->dir == RELAY_DIR_RESPONSE) { 235 desc->http_method = HTTP_METHOD_RESPONSE; 236 /* 237 * Decode response path and query 238 */ 239 desc->http_version = strdup(line); 240 if (desc->http_version == NULL) { 241 free(line); 242 goto fail; 243 } 244 desc->http_rescode = strdup(value); 245 if (desc->http_rescode == NULL) { 246 free(line); 247 goto fail; 248 } 249 desc->http_resmesg = strchr(desc->http_rescode, ' '); 250 if (desc->http_resmesg == NULL) { 251 free(line); 252 goto fail; 253 } 254 *desc->http_resmesg++ = '\0'; 255 if ((desc->http_resmesg = strdup(desc->http_resmesg)) 256 == NULL) { 257 free(line); 258 goto fail; 259 } 260 DPRINTF("http_version %s http_rescode %s " 261 "http_resmesg %s", desc->http_version, 262 desc->http_rescode, desc->http_resmesg); 263 goto lookup; 264 } else if (cre->line == 1 && cre->dir == RELAY_DIR_REQUEST) { 265 if ((desc->http_method = relay_httpmethod_byname(key)) 266 == HTTP_METHOD_NONE) { 267 free(line); 268 goto fail; 269 } 270 /* 271 * Decode request path and query 272 */ 273 desc->http_path = strdup(value); 274 if (desc->http_path == NULL) { 275 free(line); 276 goto fail; 277 } 278 desc->http_version = strchr(desc->http_path, ' '); 279 if (desc->http_version == NULL) { 280 free(line); 281 goto fail; 282 } 283 *desc->http_version++ = '\0'; 284 desc->http_query = strchr(desc->http_path, '?'); 285 if (desc->http_query != NULL) 286 *desc->http_query++ = '\0'; 287 288 /* 289 * Have to allocate the strings because they could 290 * be changed independently by the filters later. 291 */ 292 if ((desc->http_version = 293 strdup(desc->http_version)) == NULL) { 294 free(line); 295 goto fail; 296 } 297 if (desc->http_query != NULL && 298 (desc->http_query = 299 strdup(desc->http_query)) == NULL) { 300 free(line); 301 goto fail; 302 } 303 } else if (desc->http_method != HTTP_METHOD_NONE && 304 strcasecmp("Content-Length", key) == 0) { 305 if (desc->http_method == HTTP_METHOD_TRACE || 306 desc->http_method == HTTP_METHOD_CONNECT) { 307 /* 308 * These method should not have a body 309 * and thus no Content-Length header. 310 */ 311 relay_abort_http(con, 400, "malformed", 0); 312 goto abort; 313 } 314 315 /* 316 * Need to read data from the client after the 317 * HTTP header. 318 * XXX What about non-standard clients not using 319 * the carriage return? And some browsers seem to 320 * include the line length in the content-length. 321 */ 322 cre->toread = strtonum(value, 0, LLONG_MAX, 323 &errstr); 324 if (errstr) { 325 relay_abort_http(con, 500, errstr, 0); 326 goto abort; 327 } 328 } 329 lookup: 330 if (strcasecmp("Transfer-Encoding", key) == 0 && 331 strcasecmp("chunked", value) == 0) 332 desc->http_chunked = 1; 333 334 if (cre->line != 1) { 335 if ((hdr = kv_add(&desc->http_headers, key, 336 value)) == NULL) { 337 free(line); 338 goto fail; 339 } 340 desc->http_lastheader = hdr; 341 } 342 343 free(line); 344 } 345 if (cre->done) { 346 if (desc->http_method == HTTP_METHOD_NONE) { 347 relay_abort_http(con, 406, "no method", 0); 348 return; 349 } 350 351 action = relay_test(proto, cre); 352 if (action == RES_FAIL) { 353 relay_close(con, "filter rule failed"); 354 return; 355 } else if (action != RES_PASS) { 356 relay_abort_http(con, 403, "Forbidden", con->se_label); 357 return; 358 } 359 360 switch (desc->http_method) { 361 case HTTP_METHOD_CONNECT: 362 /* Data stream */ 363 cre->toread = TOREAD_UNLIMITED; 364 bev->readcb = relay_read; 365 break; 366 case HTTP_METHOD_DELETE: 367 case HTTP_METHOD_GET: 368 case HTTP_METHOD_HEAD: 369 case HTTP_METHOD_OPTIONS: 370 cre->toread = 0; 371 break; 372 case HTTP_METHOD_PATCH: 373 case HTTP_METHOD_POST: 374 case HTTP_METHOD_PUT: 375 case HTTP_METHOD_RESPONSE: 376 /* HTTP request payload */ 377 if (cre->toread > 0) 378 bev->readcb = relay_read_httpcontent; 379 380 /* Single-pass HTTP body */ 381 if (cre->toread < 0) { 382 cre->toread = TOREAD_UNLIMITED; 383 bev->readcb = relay_read; 384 } 385 break; 386 default: 387 /* HTTP handler */ 388 cre->toread = TOREAD_HTTP_HEADER; 389 bev->readcb = relay_read_http; 390 break; 391 } 392 if (desc->http_chunked) { 393 /* Chunked transfer encoding */ 394 cre->toread = TOREAD_HTTP_CHUNK_LENGTH; 395 bev->readcb = relay_read_httpchunks; 396 } 397 398 if (cre->dir == RELAY_DIR_REQUEST) { 399 if (relay_writerequest_http(cre->dst, cre) == -1) 400 goto fail; 401 } else { 402 if (relay_writeresponse_http(cre->dst, cre) == -1) 403 goto fail; 404 } 405 if (relay_bufferevent_print(cre->dst, "\r\n") == -1 || 406 relay_writeheader_http(cre->dst, cre) == -1 || 407 relay_bufferevent_print(cre->dst, "\r\n") == -1) 408 goto fail; 409 410 relay_reset_http(cre); 411 done: 412 if (cre->dir == RELAY_DIR_REQUEST && cre->toread <= 0 && 413 cre->dst->state != STATE_CONNECTED) { 414 if (rlay->rl_conf.fwdmode == FWD_TRANS) { 415 relay_bindanyreq(con, 0, IPPROTO_TCP); 416 return; 417 } 418 if (relay_connect(con) == -1) { 419 relay_abort_http(con, 502, "session failed", 0); 420 return; 421 } 422 } 423 } 424 if (con->se_done) { 425 relay_close(con, "last http read (done)"); 426 return; 427 } 428 switch (relay_splice(cre)) { 429 case -1: 430 relay_close(con, strerror(errno)); 431 case 1: 432 return; 433 case 0: 434 break; 435 } 436 bufferevent_enable(bev, EV_READ); 437 if (EVBUFFER_LENGTH(src) && bev->readcb != relay_read_http) 438 bev->readcb(bev, arg); 439 /* The callback readcb() might have freed the session. */ 440 return; 441 fail: 442 relay_abort_http(con, 500, strerror(errno), 0); 443 return; 444 abort: 445 free(line); 446 } 447 448 void 449 relay_read_httpcontent(struct bufferevent *bev, void *arg) 450 { 451 struct ctl_relay_event *cre = arg; 452 struct rsession *con = cre->con; 453 struct protocol *proto = con->se_relay->rl_proto; 454 455 struct evbuffer *src = EVBUFFER_INPUT(bev); 456 size_t size; 457 458 getmonotime(&con->se_tv_last); 459 cre->timedout = 0; 460 461 size = EVBUFFER_LENGTH(src); 462 DPRINTF("%s: session %d: size %lu, to read %lld", __func__, 463 con->se_id, size, cre->toread); 464 if (!size) 465 return; 466 if (relay_spliceadjust(cre) == -1) 467 goto fail; 468 469 if (cre->toread > 0) { 470 /* Read content data */ 471 if ((off_t)size > cre->toread) { 472 size = cre->toread; 473 if (relay_bufferevent_write_chunk(cre->dst, src, size) 474 == -1) 475 goto fail; 476 cre->toread = 0; 477 } else { 478 if (relay_bufferevent_write_buffer(cre->dst, src) == -1) 479 goto fail; 480 cre->toread -= size; 481 } 482 DPRINTF("%s: done, size %lu, to read %lld", __func__, 483 size, cre->toread); 484 } 485 if (cre->toread == 0) { 486 cre->toread = TOREAD_HTTP_HEADER; 487 bev->readcb = relay_read_http; 488 } 489 if (con->se_done) 490 goto done; 491 bufferevent_enable(bev, EV_READ); 492 493 if (cre->dst->bev && EVBUFFER_LENGTH(EVBUFFER_OUTPUT(cre->dst->bev)) > 494 (size_t)RELAY_MAX_PREFETCH * proto->tcpbufsiz) 495 bufferevent_disable(cre->bev, EV_READ); 496 497 if (bev->readcb != relay_read_httpcontent) 498 bev->readcb(bev, arg); 499 /* The callback readcb() might have freed the session. */ 500 return; 501 done: 502 relay_close(con, "last http content read"); 503 return; 504 fail: 505 relay_close(con, strerror(errno)); 506 } 507 508 void 509 relay_read_httpchunks(struct bufferevent *bev, void *arg) 510 { 511 struct ctl_relay_event *cre = arg; 512 struct rsession *con = cre->con; 513 struct protocol *proto = con->se_relay->rl_proto; 514 struct evbuffer *src = EVBUFFER_INPUT(bev); 515 char *line; 516 long long llval; 517 size_t size; 518 519 getmonotime(&con->se_tv_last); 520 cre->timedout = 0; 521 522 size = EVBUFFER_LENGTH(src); 523 DPRINTF("%s: session %d: size %lu, to read %lld", __func__, 524 con->se_id, size, cre->toread); 525 if (!size) 526 return; 527 if (relay_spliceadjust(cre) == -1) 528 goto fail; 529 530 if (cre->toread > 0) { 531 /* Read chunk data */ 532 if ((off_t)size > cre->toread) { 533 size = cre->toread; 534 if (relay_bufferevent_write_chunk(cre->dst, src, size) 535 == -1) 536 goto fail; 537 cre->toread = 0; 538 } else { 539 if (relay_bufferevent_write_buffer(cre->dst, src) == -1) 540 goto fail; 541 cre->toread -= size; 542 } 543 DPRINTF("%s: done, size %lu, to read %lld", __func__, 544 size, cre->toread); 545 } 546 switch (cre->toread) { 547 case TOREAD_HTTP_CHUNK_LENGTH: 548 line = evbuffer_readline(src); 549 if (line == NULL) { 550 /* Ignore empty line, continue */ 551 bufferevent_enable(bev, EV_READ); 552 return; 553 } 554 if (strlen(line) == 0) { 555 free(line); 556 goto next; 557 } 558 559 /* 560 * Read prepended chunk size in hex, ignore the trailer. 561 * The returned signed value must not be negative. 562 */ 563 if (sscanf(line, "%llx", &llval) != 1 || llval < 0) { 564 free(line); 565 relay_close(con, "invalid chunk size"); 566 return; 567 } 568 569 if (relay_bufferevent_print(cre->dst, line) == -1 || 570 relay_bufferevent_print(cre->dst, "\r\n") == -1) { 571 free(line); 572 goto fail; 573 } 574 free(line); 575 576 if ((cre->toread = llval) == 0) { 577 DPRINTF("%s: last chunk", __func__); 578 cre->toread = TOREAD_HTTP_CHUNK_TRAILER; 579 } 580 break; 581 case TOREAD_HTTP_CHUNK_TRAILER: 582 /* Last chunk is 0 bytes followed by trailer and empty line */ 583 line = evbuffer_readline(src); 584 if (line == NULL) { 585 /* Ignore empty line, continue */ 586 bufferevent_enable(bev, EV_READ); 587 return; 588 } 589 if (relay_bufferevent_print(cre->dst, line) == -1 || 590 relay_bufferevent_print(cre->dst, "\r\n") == -1) { 591 free(line); 592 goto fail; 593 } 594 if (strlen(line) == 0) { 595 /* Switch to HTTP header mode */ 596 cre->toread = TOREAD_HTTP_HEADER; 597 bev->readcb = relay_read_http; 598 } 599 free(line); 600 break; 601 case 0: 602 /* Chunk is terminated by an empty newline */ 603 line = evbuffer_readline(src); 604 free(line); 605 if (relay_bufferevent_print(cre->dst, "\r\n") == -1) 606 goto fail; 607 cre->toread = TOREAD_HTTP_CHUNK_LENGTH; 608 break; 609 } 610 611 next: 612 if (con->se_done) 613 goto done; 614 bufferevent_enable(bev, EV_READ); 615 616 if (cre->dst->bev && EVBUFFER_LENGTH(EVBUFFER_OUTPUT(cre->dst->bev)) > 617 (size_t)RELAY_MAX_PREFETCH * proto->tcpbufsiz) 618 bufferevent_disable(cre->bev, EV_READ); 619 620 if (EVBUFFER_LENGTH(src)) 621 bev->readcb(bev, arg); 622 /* The callback readcb() might have freed the session. */ 623 return; 624 625 done: 626 relay_close(con, "last http chunk read (done)"); 627 return; 628 fail: 629 relay_close(con, strerror(errno)); 630 } 631 632 void 633 relay_reset_http(struct ctl_relay_event *cre) 634 { 635 struct http_descriptor *desc = cre->desc; 636 637 relay_httpdesc_free(desc); 638 if (cre->buf != NULL) { 639 free(cre->buf); 640 cre->buf = NULL; 641 cre->buflen = 0; 642 } 643 desc->http_method = 0; 644 desc->http_chunked = 0; 645 cre->headerlen = 0; 646 cre->line = 0; 647 cre->done = 0; 648 } 649 650 static int 651 _relay_lookup_url(struct ctl_relay_event *cre, char *host, char *path, 652 char *query, struct kv *kv) 653 { 654 struct rsession *con = cre->con; 655 char *val, *md = NULL; 656 int ret = RES_FAIL; 657 const char *str = NULL; 658 659 if (asprintf(&val, "%s%s%s%s", 660 host, path, 661 query == NULL ? "" : "?", 662 query == NULL ? "" : query) == -1) { 663 relay_abort_http(con, 500, "failed to allocate URL", 0); 664 return (RES_FAIL); 665 } 666 667 switch (kv->kv_digest) { 668 case DIGEST_SHA1: 669 case DIGEST_MD5: 670 if ((md = digeststr(kv->kv_digest, 671 val, strlen(val), NULL)) == NULL) { 672 relay_abort_http(con, 500, 673 "failed to allocate digest", 0); 674 goto fail; 675 } 676 str = md; 677 break; 678 case DIGEST_NONE: 679 str = val; 680 break; 681 } 682 683 DPRINTF("%s: session %d: %s, %s: %d", __func__, con->se_id, 684 str, kv->kv_key, strcasecmp(kv->kv_key, str)); 685 686 if (strcasecmp(kv->kv_key, str) == 0) { 687 ret = RES_DROP; 688 goto fail; 689 } 690 691 ret = RES_PASS; 692 fail: 693 free(md); 694 free(val); 695 return (ret); 696 } 697 698 int 699 relay_lookup_url(struct ctl_relay_event *cre, const char *host, struct kv *kv) 700 { 701 struct rsession *con = cre->con; 702 struct http_descriptor *desc = (struct http_descriptor *)cre->desc; 703 int i, j, dots; 704 char *hi[RELAY_MAXLOOKUPLEVELS], *p, *pp, *c, ch; 705 char ph[HOST_NAME_MAX+1]; 706 int ret; 707 708 if (desc->http_path == NULL) 709 return (RES_PASS); 710 711 /* 712 * This is an URL lookup algorithm inspired by 713 * http://code.google.com/apis/safebrowsing/ 714 * developers_guide.html#PerformingLookups 715 */ 716 717 DPRINTF("%s: session %d: host '%s', path '%s', query '%s'", 718 __func__, con->se_id, host, desc->http_path, 719 desc->http_query == NULL ? "" : desc->http_query); 720 721 if (canonicalize_host(host, ph, sizeof(ph)) == NULL) { 722 relay_abort_http(con, 400, "invalid host name", 0); 723 return (RES_FAIL); 724 } 725 726 bzero(hi, sizeof(hi)); 727 for (dots = -1, i = strlen(ph) - 1; i > 0; i--) { 728 if (ph[i] == '.' && ++dots) 729 hi[dots - 1] = &ph[i + 1]; 730 if (dots > (RELAY_MAXLOOKUPLEVELS - 2)) 731 break; 732 } 733 if (dots == -1) 734 dots = 0; 735 hi[dots] = ph; 736 737 if ((pp = strdup(desc->http_path)) == NULL) { 738 relay_abort_http(con, 500, "failed to allocate path", 0); 739 return (RES_FAIL); 740 } 741 for (i = (RELAY_MAXLOOKUPLEVELS - 1); i >= 0; i--) { 742 if (hi[i] == NULL) 743 continue; 744 745 /* 1. complete path with query */ 746 if (desc->http_query != NULL) 747 if ((ret = _relay_lookup_url(cre, hi[i], 748 pp, desc->http_query, kv)) != RES_PASS) 749 goto done; 750 751 /* 2. complete path without query */ 752 if ((ret = _relay_lookup_url(cre, hi[i], 753 pp, NULL, kv)) != RES_PASS) 754 goto done; 755 756 /* 3. traverse path */ 757 for (j = 0, p = strchr(pp, '/'); 758 p != NULL; p = strchr(p, '/'), j++) { 759 if (j > (RELAY_MAXLOOKUPLEVELS - 2) || *(++p) == '\0') 760 break; 761 c = &pp[p - pp]; 762 ch = *c; 763 *c = '\0'; 764 if ((ret = _relay_lookup_url(cre, hi[i], 765 pp, NULL, kv)) != RES_PASS) 766 goto done; 767 *c = ch; 768 } 769 } 770 771 ret = RES_PASS; 772 done: 773 free(pp); 774 return (ret); 775 } 776 777 int 778 relay_lookup_cookie(struct ctl_relay_event *cre, const char *str, 779 struct kv *kv) 780 { 781 struct rsession *con = cre->con; 782 char *val, *ptr, *key, *value; 783 int ret; 784 785 if ((val = strdup(str)) == NULL) { 786 relay_abort_http(con, 500, "failed to allocate cookie", 0); 787 return (RES_FAIL); 788 } 789 790 for (ptr = val; ptr != NULL && strlen(ptr);) { 791 if (*ptr == ' ') 792 *ptr++ = '\0'; 793 key = ptr; 794 if ((ptr = strchr(ptr, ';')) != NULL) 795 *ptr++ = '\0'; 796 /* 797 * XXX We do not handle attributes 798 * ($Path, $Domain, or $Port) 799 */ 800 if (*key == '$') 801 continue; 802 803 if ((value = 804 strchr(key, '=')) == NULL || 805 strlen(value) < 1) 806 continue; 807 *value++ = '\0'; 808 if (*value == '"') 809 *value++ = '\0'; 810 if (value[strlen(value) - 1] == '"') 811 value[strlen(value) - 1] = '\0'; 812 813 DPRINTF("%s: session %d: %s = %s, %s = %s : %d", 814 __func__, con->se_id, 815 key, value, kv->kv_key, kv->kv_value, 816 strcasecmp(kv->kv_key, key)); 817 818 if (strcasecmp(kv->kv_key, key) == 0 && 819 ((kv->kv_value == NULL) || 820 (fnmatch(kv->kv_value, value, 821 FNM_CASEFOLD) != FNM_NOMATCH))) { 822 ret = RES_DROP; 823 goto done; 824 } 825 } 826 827 ret = RES_PASS; 828 829 done: 830 free(val); 831 return (ret); 832 } 833 834 int 835 relay_lookup_query(struct ctl_relay_event *cre, struct kv *kv) 836 { 837 struct http_descriptor *desc = cre->desc; 838 struct kv *match = &desc->http_matchquery; 839 char *val, *ptr, *tmpkey = NULL, *tmpval = NULL; 840 int ret = -1; 841 842 if (desc->http_query == NULL) 843 return (-1); 844 if ((val = strdup(desc->http_query)) == NULL) { 845 relay_abort_http(cre->con, 500, "failed to allocate query", 0); 846 return (-1); 847 } 848 849 ptr = val; 850 while (ptr != NULL && strlen(ptr)) { 851 tmpkey = ptr; 852 if ((ptr = strchr(ptr, '&')) != NULL) 853 *ptr++ = '\0'; 854 if ((tmpval = strchr(tmpkey, '=')) == NULL || strlen(tmpval) 855 < 1) 856 continue; 857 *tmpval++ = '\0'; 858 859 if (fnmatch(kv->kv_key, tmpkey, 0) != FNM_NOMATCH && 860 (kv->kv_value == NULL || fnmatch(kv->kv_value, tmpval, 0) 861 != FNM_NOMATCH)) 862 break; 863 else 864 tmpkey = NULL; 865 } 866 867 if (tmpkey == NULL || tmpval == NULL) 868 goto done; 869 870 match->kv_key = strdup(tmpkey); 871 if (match->kv_key == NULL) 872 goto done; 873 match->kv_value = strdup(tmpval); 874 if (match->kv_key == NULL) 875 goto done; 876 ret = 0; 877 878 done: 879 free(val); 880 return (ret); 881 } 882 883 ssize_t 884 relay_http_time(time_t t, char *tmbuf, size_t len) 885 { 886 struct tm tm; 887 888 /* New HTTP/1.1 RFC 7231 prefers IMF-fixdate from RFC 5322 */ 889 if (t == -1 || gmtime_r(&t, &tm) == NULL) 890 return (-1); 891 else 892 return (strftime(tmbuf, len, "%a, %d %h %Y %T %Z", &tm)); 893 } 894 895 void 896 relay_abort_http(struct rsession *con, u_int code, const char *msg, 897 u_int16_t labelid) 898 { 899 struct relay *rlay = con->se_relay; 900 struct bufferevent *bev = con->se_in.bev; 901 const char *httperr = NULL, *text = ""; 902 char *httpmsg, *body = NULL; 903 char tmbuf[32], hbuf[128]; 904 const char *style, *label = NULL; 905 int bodylen; 906 907 if ((httperr = relay_httperror_byid(code)) == NULL) 908 httperr = "Unknown Error"; 909 910 if (labelid != 0) 911 label = label_id2name(labelid); 912 913 /* In some cases this function may be called from generic places */ 914 if (rlay->rl_proto->type != RELAY_PROTO_HTTP || 915 (rlay->rl_proto->flags & F_RETURN) == 0) { 916 relay_close(con, msg); 917 return; 918 } 919 920 if (bev == NULL) 921 goto done; 922 923 /* Some system information */ 924 if (print_host(&rlay->rl_conf.ss, hbuf, sizeof(hbuf)) == NULL) 925 goto done; 926 927 if (relay_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0) 928 goto done; 929 930 /* Do not send details of the Internal Server Error */ 931 switch (code) { 932 case 500: 933 break; 934 default: 935 text = msg; 936 break; 937 } 938 939 /* A CSS stylesheet allows minimal customization by the user */ 940 style = (rlay->rl_proto->style != NULL) ? rlay->rl_proto->style : 941 "body { background-color: #a00000; color: white; font-family: " 942 "'Comic Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif; }\n" 943 "hr { border: 0; border-bottom: 1px dashed; }\n"; 944 945 /* Generate simple HTTP+HTML error document */ 946 if ((bodylen = asprintf(&body, 947 "<!DOCTYPE html>\n" 948 "<html>\n" 949 "<head>\n" 950 "<title>%03d %s</title>\n" 951 "<style type=\"text/css\"><!--\n%s\n--></style>\n" 952 "</head>\n" 953 "<body>\n" 954 "<h1>%s</h1>\n" 955 "<div id='m'>%s</div>\n" 956 "<div id='l'>%s</div>\n" 957 "<hr><address>%s at %s port %d</address>\n" 958 "</body>\n" 959 "</html>\n", 960 code, httperr, style, httperr, text, 961 label == NULL ? "" : label, 962 RELAYD_SERVERNAME, hbuf, ntohs(rlay->rl_conf.port))) == -1) 963 goto done; 964 965 /* Generate simple HTTP+HTML error document */ 966 if (asprintf(&httpmsg, 967 "HTTP/1.0 %03d %s\r\n" 968 "Date: %s\r\n" 969 "Server: %s\r\n" 970 "Connection: close\r\n" 971 "Content-Type: text/html\r\n" 972 "Content-Length: %d\r\n" 973 "\r\n" 974 "%s", 975 code, httperr, tmbuf, RELAYD_SERVERNAME, bodylen, body) == -1) 976 goto done; 977 978 /* Dump the message without checking for success */ 979 relay_dump(&con->se_in, httpmsg, strlen(httpmsg)); 980 free(httpmsg); 981 982 done: 983 free(body); 984 if (asprintf(&httpmsg, "%s (%03d %s)", msg, code, httperr) == -1) 985 relay_close(con, msg); 986 else { 987 relay_close(con, httpmsg); 988 free(httpmsg); 989 } 990 } 991 992 void 993 relay_close_http(struct rsession *con) 994 { 995 struct http_descriptor *desc[2] = { 996 con->se_in.desc, con->se_out.desc 997 }; 998 int i; 999 1000 for (i = 0; i < 2; i++) { 1001 if (desc[i] == NULL) 1002 continue; 1003 relay_httpdesc_free(desc[i]); 1004 free(desc[i]); 1005 } 1006 } 1007 1008 char * 1009 relay_expand_http(struct ctl_relay_event *cre, char *val, char *buf, 1010 size_t len) 1011 { 1012 struct rsession *con = cre->con; 1013 struct relay *rlay = con->se_relay; 1014 char ibuf[128]; 1015 1016 if (strlcpy(buf, val, len) >= len) 1017 return (NULL); 1018 1019 if (strstr(val, "$REMOTE_") != NULL) { 1020 if (strstr(val, "$REMOTE_ADDR") != NULL) { 1021 if (print_host(&cre->ss, ibuf, sizeof(ibuf)) == NULL) 1022 return (NULL); 1023 if (expand_string(buf, len, 1024 "$REMOTE_ADDR", ibuf) != 0) 1025 return (NULL); 1026 } 1027 if (strstr(val, "$REMOTE_PORT") != NULL) { 1028 snprintf(ibuf, sizeof(ibuf), "%u", ntohs(cre->port)); 1029 if (expand_string(buf, len, 1030 "$REMOTE_PORT", ibuf) != 0) 1031 return (NULL); 1032 } 1033 } 1034 if (strstr(val, "$SERVER_") != NULL) { 1035 if (strstr(val, "$SERVER_ADDR") != NULL) { 1036 if (print_host(&rlay->rl_conf.ss, 1037 ibuf, sizeof(ibuf)) == NULL) 1038 return (NULL); 1039 if (expand_string(buf, len, 1040 "$SERVER_ADDR", ibuf) != 0) 1041 return (NULL); 1042 } 1043 if (strstr(val, "$SERVER_PORT") != NULL) { 1044 snprintf(ibuf, sizeof(ibuf), "%u", 1045 ntohs(rlay->rl_conf.port)); 1046 if (expand_string(buf, len, 1047 "$SERVER_PORT", ibuf) != 0) 1048 return (NULL); 1049 } 1050 if (strstr(val, "$SERVER_NAME") != NULL) { 1051 if (expand_string(buf, len, 1052 "$SERVER_NAME", RELAYD_SERVERNAME) != 0) 1053 return (NULL); 1054 } 1055 } 1056 if (strstr(val, "$TIMEOUT") != NULL) { 1057 snprintf(ibuf, sizeof(ibuf), "%lld", 1058 (long long)rlay->rl_conf.timeout.tv_sec); 1059 if (expand_string(buf, len, "$TIMEOUT", ibuf) != 0) 1060 return (NULL); 1061 } 1062 1063 return (buf); 1064 } 1065 1066 int 1067 relay_writerequest_http(struct ctl_relay_event *dst, 1068 struct ctl_relay_event *cre) 1069 { 1070 struct http_descriptor *desc = (struct http_descriptor *)cre->desc; 1071 const char *name = NULL; 1072 1073 if ((name = relay_httpmethod_byid(desc->http_method)) == NULL) 1074 return (-1); 1075 1076 if (relay_bufferevent_print(dst, name) == -1 || 1077 relay_bufferevent_print(dst, " ") == -1 || 1078 relay_bufferevent_print(dst, desc->http_path) == -1 || 1079 (desc->http_query != NULL && 1080 (relay_bufferevent_print(dst, "?") == -1 || 1081 relay_bufferevent_print(dst, desc->http_query) == -1)) || 1082 relay_bufferevent_print(dst, " ") == -1 || 1083 relay_bufferevent_print(dst, desc->http_version) == -1) 1084 return (-1); 1085 1086 return (0); 1087 } 1088 1089 int 1090 relay_writeresponse_http(struct ctl_relay_event *dst, 1091 struct ctl_relay_event *cre) 1092 { 1093 struct http_descriptor *desc = (struct http_descriptor *)cre->desc; 1094 1095 DPRINTF("version: %s rescode: %s resmsg: %s", desc->http_version, 1096 desc->http_rescode, desc->http_resmesg); 1097 1098 if (relay_bufferevent_print(dst, desc->http_version) == -1 || 1099 relay_bufferevent_print(dst, " ") == -1 || 1100 relay_bufferevent_print(dst, desc->http_rescode) == -1 || 1101 relay_bufferevent_print(dst, " ") == -1 || 1102 relay_bufferevent_print(dst, desc->http_resmesg) == -1) 1103 return (-1); 1104 1105 return (0); 1106 } 1107 1108 int 1109 relay_writeheader_kv(struct ctl_relay_event *dst, struct kv *hdr) 1110 { 1111 char *ptr; 1112 const char *key; 1113 1114 if (hdr->kv_flags & KV_FLAG_INVALID) 1115 return (0); 1116 1117 /* The key might have been updated in the parent */ 1118 if (hdr->kv_parent != NULL && hdr->kv_parent->kv_key != NULL) 1119 key = hdr->kv_parent->kv_key; 1120 else 1121 key = hdr->kv_key; 1122 1123 ptr = hdr->kv_value; 1124 if (relay_bufferevent_print(dst, key) == -1 || 1125 (ptr != NULL && 1126 (relay_bufferevent_print(dst, ": ") == -1 || 1127 relay_bufferevent_print(dst, ptr) == -1 || 1128 relay_bufferevent_print(dst, "\r\n") == -1))) 1129 return (-1); 1130 DPRINTF("%s: %s: %s", __func__, key, 1131 hdr->kv_value == NULL ? "" : hdr->kv_value); 1132 1133 return (0); 1134 } 1135 1136 int 1137 relay_writeheader_http(struct ctl_relay_event *dst, struct ctl_relay_event 1138 *cre) 1139 { 1140 struct kv *hdr, *kv; 1141 struct http_descriptor *desc = (struct http_descriptor *)cre->desc; 1142 1143 RB_FOREACH(hdr, kvtree, &desc->http_headers) { 1144 if (relay_writeheader_kv(dst, hdr) == -1) 1145 return (-1); 1146 TAILQ_FOREACH(kv, &hdr->kv_children, kv_entry) { 1147 if (relay_writeheader_kv(dst, kv) == -1) 1148 return (-1); 1149 } 1150 } 1151 1152 return (0); 1153 } 1154 1155 enum httpmethod 1156 relay_httpmethod_byname(const char *name) 1157 { 1158 enum httpmethod id = HTTP_METHOD_NONE; 1159 struct http_method method, *res = NULL; 1160 1161 /* Set up key */ 1162 method.method_name = name; 1163 1164 if ((res = bsearch(&method, http_methods, 1165 sizeof(http_methods) / sizeof(http_methods[0]) - 1, 1166 sizeof(http_methods[0]), relay_httpmethod_cmp)) != NULL) 1167 id = res->method_id; 1168 1169 return (id); 1170 } 1171 1172 const char * 1173 relay_httpmethod_byid(u_int id) 1174 { 1175 const char *name = NULL; 1176 int i; 1177 1178 for (i = 0; http_methods[i].method_name != NULL; i++) { 1179 if (http_methods[i].method_id == id) { 1180 name = http_methods[i].method_name; 1181 break; 1182 } 1183 } 1184 1185 return (name); 1186 } 1187 1188 static int 1189 relay_httpmethod_cmp(const void *a, const void *b) 1190 { 1191 const struct http_method *ma = a; 1192 const struct http_method *mb = b; 1193 1194 /* 1195 * RFC 2616 section 5.1.1 says that the method is case 1196 * sensitive so we don't do a strcasecmp here. 1197 */ 1198 return (strcmp(ma->method_name, mb->method_name)); 1199 } 1200 1201 const char * 1202 relay_httperror_byid(u_int id) 1203 { 1204 struct http_error error, *res = NULL; 1205 1206 /* Set up key */ 1207 error.error_code = (int)id; 1208 1209 res = bsearch(&error, http_errors, 1210 sizeof(http_errors) / sizeof(http_errors[0]) - 1, 1211 sizeof(http_errors[0]), relay_httperror_cmp); 1212 1213 return (res->error_name); 1214 } 1215 1216 static int 1217 relay_httperror_cmp(const void *a, const void *b) 1218 { 1219 const struct http_error *ea = a; 1220 const struct http_error *eb = b; 1221 return (ea->error_code - eb->error_code); 1222 } 1223 1224 int 1225 relay_httpquery_test(struct ctl_relay_event *cre, struct relay_rule *rule, 1226 struct kvlist *actions) 1227 { 1228 struct http_descriptor *desc = cre->desc; 1229 struct kv *match = &desc->http_matchquery; 1230 struct kv *kv = &rule->rule_kv[KEY_TYPE_QUERY]; 1231 1232 if (cre->dir == RELAY_DIR_RESPONSE || kv->kv_type != KEY_TYPE_QUERY) 1233 return (0); 1234 else if (kv->kv_key == NULL) 1235 return (0); 1236 else if (relay_lookup_query(cre, kv)) 1237 return (-1); 1238 1239 relay_match(actions, kv, match, NULL); 1240 1241 return (0); 1242 } 1243 1244 int 1245 relay_httpheader_test(struct ctl_relay_event *cre, struct relay_rule *rule, 1246 struct kvlist *actions) 1247 { 1248 struct http_descriptor *desc = cre->desc; 1249 struct kv *kv = &rule->rule_kv[KEY_TYPE_HEADER]; 1250 struct kv *match; 1251 1252 if (kv->kv_type != KEY_TYPE_HEADER) 1253 return (0); 1254 1255 match = kv_find(&desc->http_headers, kv); 1256 1257 if (kv->kv_option == KEY_OPTION_APPEND || 1258 kv->kv_option == KEY_OPTION_SET) { 1259 /* header can be NULL and will be added later */ 1260 } else if (match == NULL) { 1261 /* Fail if header doesn't exist */ 1262 return (-1); 1263 } else { 1264 if (fnmatch(kv->kv_key, match->kv_key, 1265 FNM_CASEFOLD) == FNM_NOMATCH) 1266 return (-1); 1267 if (kv->kv_value != NULL && 1268 match->kv_value != NULL && 1269 fnmatch(kv->kv_value, match->kv_value, 0) == FNM_NOMATCH) 1270 return (-1); 1271 } 1272 1273 relay_match(actions, kv, match, &desc->http_headers); 1274 1275 return (0); 1276 } 1277 1278 int 1279 relay_httppath_test(struct ctl_relay_event *cre, struct relay_rule *rule, 1280 struct kvlist *actions) 1281 { 1282 struct http_descriptor *desc = cre->desc; 1283 struct kv *kv = &rule->rule_kv[KEY_TYPE_PATH]; 1284 struct kv *match = &desc->http_pathquery; 1285 const char *query; 1286 1287 if (cre->dir == RELAY_DIR_RESPONSE || kv->kv_type != KEY_TYPE_PATH) 1288 return (0); 1289 else if (kv->kv_key == NULL) 1290 return (0); 1291 else if (fnmatch(kv->kv_key, desc->http_path, 0) == FNM_NOMATCH) 1292 return (-1); 1293 else if (kv->kv_value != NULL && kv->kv_option == KEY_OPTION_NONE) { 1294 query = desc->http_query == NULL ? "" : desc->http_query; 1295 if (fnmatch(kv->kv_value, query, FNM_CASEFOLD) == FNM_NOMATCH) 1296 return (-1); 1297 } 1298 1299 relay_match(actions, kv, match, NULL); 1300 1301 return (0); 1302 } 1303 1304 int 1305 relay_httpurl_test(struct ctl_relay_event *cre, struct relay_rule *rule, 1306 struct kvlist *actions) 1307 { 1308 struct http_descriptor *desc = cre->desc; 1309 struct kv *host, key; 1310 struct kv *kv = &rule->rule_kv[KEY_TYPE_URL]; 1311 struct kv *match = &desc->http_pathquery; 1312 1313 if (cre->dir == RELAY_DIR_RESPONSE || kv->kv_type != KEY_TYPE_URL || 1314 kv->kv_key == NULL) 1315 return (0); 1316 1317 key.kv_key = "Host"; 1318 host = kv_find(&desc->http_headers, &key); 1319 1320 if (host == NULL || host->kv_value == NULL) 1321 return (0); 1322 else if (rule->rule_action != RULE_ACTION_BLOCK && 1323 kv->kv_option == KEY_OPTION_LOG && 1324 fnmatch(kv->kv_key, match->kv_key, FNM_CASEFOLD) != FNM_NOMATCH) { 1325 /* fnmatch url only for logging */ 1326 } else if (relay_lookup_url(cre, host->kv_value, kv) != 0) 1327 return (-1); 1328 1329 relay_match(actions, kv, match, NULL); 1330 1331 return (0); 1332 } 1333 1334 int 1335 relay_httpcookie_test(struct ctl_relay_event *cre, struct relay_rule *rule, 1336 struct kvlist *actions) 1337 { 1338 struct http_descriptor *desc = cre->desc; 1339 struct kv *kv = &rule->rule_kv[KEY_TYPE_COOKIE], key; 1340 struct kv *match = NULL; 1341 1342 if (kv->kv_type != KEY_TYPE_COOKIE) 1343 return (0); 1344 1345 switch (cre->dir) { 1346 case RELAY_DIR_REQUEST: 1347 key.kv_key = "Cookie"; 1348 break; 1349 case RELAY_DIR_RESPONSE: 1350 key.kv_key = "Set-Cookie"; 1351 break; 1352 default: 1353 return (0); 1354 /* NOTREACHED */ 1355 break; 1356 } 1357 1358 if (kv->kv_option == KEY_OPTION_APPEND || 1359 kv->kv_option == KEY_OPTION_SET) { 1360 /* no cookie, can be NULL and will be added later */ 1361 } else { 1362 match = kv_find(&desc->http_headers, &key); 1363 if (match == NULL) 1364 return (-1); 1365 if (kv->kv_key == NULL || match->kv_value == NULL) 1366 return (0); 1367 else if (relay_lookup_cookie(cre, match->kv_value, kv) != 0) 1368 return (-1); 1369 } 1370 1371 relay_match(actions, kv, match, &desc->http_headers); 1372 1373 return (0); 1374 } 1375 1376 int 1377 relay_match_actions(struct ctl_relay_event *cre, struct relay_rule *rule, 1378 struct kvlist *matches, struct kvlist *actions) 1379 { 1380 struct rsession *con = cre->con; 1381 struct kv *kv, *tmp; 1382 1383 /* 1384 * Apply the following options instantly (action per match). 1385 */ 1386 if (rule->rule_table != NULL) 1387 con->se_table = rule->rule_table; 1388 1389 if (rule->rule_tag != 0) 1390 con->se_tag = rule->rule_tag == -1 ? 0 : rule->rule_tag; 1391 1392 if (rule->rule_label != 0) 1393 con->se_label = rule->rule_label == -1 ? 0 : rule->rule_label; 1394 1395 /* 1396 * Apply the remaining options once after evaluation. 1397 */ 1398 if (matches == NULL) { 1399 /* 'pass' or 'block' rule */ 1400 TAILQ_FOREACH_SAFE(kv, &rule->rule_kvlist, kv_rule_entry, tmp) { 1401 TAILQ_INSERT_TAIL(actions, kv, kv_action_entry); 1402 TAILQ_REMOVE(&rule->rule_kvlist, kv, kv_rule_entry); 1403 } 1404 } else { 1405 /* 'match' rule */ 1406 TAILQ_FOREACH(kv, matches, kv_match_entry) { 1407 TAILQ_INSERT_TAIL(actions, kv, kv_action_entry); 1408 } 1409 } 1410 1411 return (0); 1412 } 1413 1414 int 1415 relay_apply_actions(struct ctl_relay_event *cre, struct kvlist *actions) 1416 { 1417 struct rsession *con = cre->con; 1418 struct http_descriptor *desc = cre->desc; 1419 struct kv *host = NULL; 1420 const char *value; 1421 struct kv *kv, *match, *kp, *mp, kvcopy, matchcopy, key; 1422 int addkv, ret; 1423 char buf[IBUF_READ_SIZE], *ptr; 1424 char *msg = NULL; 1425 const char *meth = NULL; 1426 1427 memset(&kvcopy, 0, sizeof(kvcopy)); 1428 memset(&matchcopy, 0, sizeof(matchcopy)); 1429 1430 ret = -1; 1431 kp = mp = NULL; 1432 TAILQ_FOREACH(kv, actions, kv_action_entry) { 1433 kp = NULL; 1434 match = kv->kv_match; 1435 addkv = 0; 1436 1437 /* 1438 * Although marked as deleted, give a chance to non-critical 1439 * actions, ie. log, to be performed 1440 */ 1441 if (match != NULL && (match->kv_flags & KV_FLAG_INVALID)) 1442 goto matchdel; 1443 1444 switch (kv->kv_option) { 1445 case KEY_OPTION_APPEND: 1446 case KEY_OPTION_SET: 1447 switch (kv->kv_type) { 1448 case KEY_TYPE_PATH: 1449 if (kv->kv_option == KEY_OPTION_APPEND) { 1450 if (kv_setkey(match, "%s%s", 1451 match->kv_key, kv->kv_key) == -1) 1452 goto fail; 1453 } else { 1454 if (kv_setkey(match, "%s", 1455 kv->kv_value) == -1) 1456 goto fail; 1457 } 1458 break; 1459 case KEY_TYPE_COOKIE: 1460 kp = &kvcopy; 1461 if (kv_inherit(kp, kv) == NULL) 1462 goto fail; 1463 if (kv_set(kp, "%s=%s;", kp->kv_key, 1464 kp->kv_value) == -1) 1465 goto fail; 1466 if (kv_setkey(kp, "%s", cre->dir == 1467 RELAY_DIR_REQUEST ? 1468 "Cookie" : "Set-Cookie") == -1) 1469 goto fail; 1470 /* FALLTHROUGH cookie is a header */ 1471 case KEY_TYPE_HEADER: 1472 if (match == NULL) { 1473 addkv = 1; 1474 break; 1475 } 1476 if (match->kv_value == NULL || 1477 kv->kv_option == KEY_OPTION_SET) { 1478 if (kv_set(match, "%s", 1479 kv->kv_value) == -1) 1480 goto fail; 1481 } else 1482 addkv = 1; 1483 break; 1484 default: 1485 /* query, url not supported */ 1486 break; 1487 } 1488 break; 1489 case KEY_OPTION_REMOVE: 1490 switch (kv->kv_type) { 1491 case KEY_TYPE_PATH: 1492 if (kv_setkey(match, "/") == -1) 1493 goto fail; 1494 break; 1495 case KEY_TYPE_COOKIE: 1496 case KEY_TYPE_HEADER: 1497 if (kv->kv_matchtree != NULL) 1498 match->kv_flags |= KV_FLAG_INVALID; 1499 else 1500 kv_free(match); 1501 match = kv->kv_match = NULL; 1502 break; 1503 default: 1504 /* query and url not supported */ 1505 break; 1506 } 1507 break; 1508 case KEY_OPTION_HASH: 1509 switch (kv->kv_type) { 1510 case KEY_TYPE_PATH: 1511 value = match->kv_key; 1512 break; 1513 default: 1514 value = match->kv_value; 1515 break; 1516 } 1517 SipHash24_Update(&con->se_siphashctx, 1518 value, strlen(value)); 1519 break; 1520 case KEY_OPTION_LOG: 1521 /* perform this later */ 1522 break; 1523 default: 1524 fatalx("relay_action: invalid action"); 1525 /* NOTREACHED */ 1526 } 1527 1528 /* from now on, reads from kp writes to kv */ 1529 if (kp == NULL) 1530 kp = kv; 1531 if (addkv && kv->kv_matchtree != NULL) { 1532 /* Add new entry to the list (eg. new HTTP header) */ 1533 if ((match = kv_add(kv->kv_matchtree, kp->kv_key, 1534 kp->kv_value)) == NULL) 1535 goto fail; 1536 match->kv_option = kp->kv_option; 1537 match->kv_type = kp->kv_type; 1538 kv->kv_match = match; 1539 } 1540 if (match != NULL && kp->kv_flags & KV_FLAG_MACRO) { 1541 bzero(buf, sizeof(buf)); 1542 if ((ptr = relay_expand_http(cre, kp->kv_value, buf, 1543 sizeof(buf))) == NULL) 1544 goto fail; 1545 if (kv_set(match, ptr) == -1) 1546 goto fail; 1547 } 1548 1549 matchdel: 1550 switch (kv->kv_option) { 1551 case KEY_OPTION_LOG: 1552 if (match == NULL) 1553 break; 1554 mp = &matchcopy; 1555 if (kv_inherit(mp, match) == NULL) 1556 goto fail; 1557 if (mp->kv_flags & KV_FLAG_INVALID) { 1558 if (kv_set(mp, "%s (removed)", 1559 mp->kv_value) == -1) 1560 goto fail; 1561 } 1562 switch (kv->kv_type) { 1563 case KEY_TYPE_URL: 1564 key.kv_key = "Host"; 1565 host = kv_find(&desc->http_headers, &key); 1566 switch (kv->kv_digest) { 1567 case DIGEST_NONE: 1568 if (host == NULL || 1569 host->kv_value == NULL) 1570 break; 1571 if (kv_setkey(mp, "%s%s", 1572 host->kv_value, mp->kv_key) == 1573 -1) 1574 goto fail; 1575 break; 1576 default: 1577 if (kv_setkey(mp, "%s", kv->kv_key) 1578 == -1) 1579 goto fail; 1580 break; 1581 } 1582 break; 1583 default: 1584 break; 1585 } 1586 if (kv_log(con, mp, con->se_label, cre->dir) 1587 == -1) 1588 goto fail; 1589 break; 1590 default: 1591 break; 1592 } 1593 1594 /* actions applied, cleanup kv */ 1595 kv->kv_match = NULL; 1596 kv->kv_matchtree = NULL; 1597 TAILQ_REMOVE(actions, kv, kv_match_entry); 1598 1599 kv_free(&kvcopy); 1600 kv_free(&matchcopy); 1601 } 1602 1603 /* 1604 * log tag for request and response, request method 1605 * and end of request marker "," 1606 */ 1607 if ((con->se_log != NULL) && 1608 ((meth = relay_httpmethod_byid(desc->http_method)) != NULL) && 1609 (asprintf(&msg, " %s",meth) >= 0)) 1610 evbuffer_add(con->se_log, msg, strlen(msg)); 1611 free(msg); 1612 relay_log(con, cre->dir == RELAY_DIR_REQUEST ? "" : ";"); 1613 ret = 0; 1614 fail: 1615 kv_free(&kvcopy); 1616 kv_free(&matchcopy); 1617 1618 return (ret); 1619 } 1620 1621 #define RELAY_GET_SKIP_STEP(i) \ 1622 do { \ 1623 r = r->rule_skip[i]; \ 1624 DPRINTF("%s:%d: skip %d rules", __func__, __LINE__, i); \ 1625 } while (0) 1626 1627 #define RELAY_GET_NEXT_STEP \ 1628 do { \ 1629 DPRINTF("%s:%d: next rule", __func__, __LINE__); \ 1630 goto nextrule; \ 1631 } while (0) 1632 1633 int 1634 relay_test(struct protocol *proto, struct ctl_relay_event *cre) 1635 { 1636 struct rsession *con; 1637 struct http_descriptor *desc = cre->desc; 1638 struct relay_rule *r = NULL, *rule = NULL; 1639 u_int cnt = 0; 1640 u_int action = RES_PASS; 1641 struct kvlist actions, matches; 1642 struct kv *kv; 1643 1644 con = cre->con; 1645 TAILQ_INIT(&actions); 1646 1647 r = TAILQ_FIRST(&proto->rules); 1648 while (r != NULL) { 1649 cnt++; 1650 TAILQ_INIT(&matches); 1651 TAILQ_INIT(&r->rule_kvlist); 1652 if (r->rule_dir && r->rule_dir != cre->dir) 1653 RELAY_GET_SKIP_STEP(RULE_SKIP_DIR); 1654 else if (proto->type != r->rule_proto) 1655 RELAY_GET_SKIP_STEP(RULE_SKIP_PROTO); 1656 else if (r->rule_af != AF_UNSPEC && 1657 (cre->ss.ss_family != r->rule_af || 1658 cre->dst->ss.ss_family != r->rule_af)) 1659 RELAY_GET_SKIP_STEP(RULE_SKIP_AF); 1660 else if (RELAY_ADDR_CMP(&r->rule_src, &cre->ss) != 0) 1661 RELAY_GET_SKIP_STEP(RULE_SKIP_SRC); 1662 else if (RELAY_ADDR_CMP(&r->rule_dst, &cre->dst->ss) != 0) 1663 RELAY_GET_SKIP_STEP(RULE_SKIP_DST); 1664 else if (r->rule_method != HTTP_METHOD_NONE && 1665 (desc->http_method == HTTP_METHOD_RESPONSE || 1666 desc->http_method != r->rule_method)) 1667 RELAY_GET_SKIP_STEP(RULE_SKIP_METHOD); 1668 else if (r->rule_tagged && con->se_tag != r->rule_tagged) 1669 RELAY_GET_NEXT_STEP; 1670 else if (relay_httpheader_test(cre, r, &matches) != 0) 1671 RELAY_GET_NEXT_STEP; 1672 else if (relay_httpquery_test(cre, r, &matches) != 0) 1673 RELAY_GET_NEXT_STEP; 1674 else if (relay_httppath_test(cre, r, &matches) != 0) 1675 RELAY_GET_NEXT_STEP; 1676 else if (relay_httpurl_test(cre, r, &matches) != 0) 1677 RELAY_GET_NEXT_STEP; 1678 else if (relay_httpcookie_test(cre, r, &matches) != 0) 1679 RELAY_GET_NEXT_STEP; 1680 else { 1681 DPRINTF("%s: session %d: matched rule %d", 1682 __func__, con->se_id, r->rule_id); 1683 1684 if (r->rule_action == RULE_ACTION_MATCH) { 1685 if (relay_match_actions(cre, r, &matches, 1686 &actions) != 0) { 1687 /* Something bad happened, drop */ 1688 action = RES_DROP; 1689 break; 1690 } 1691 RELAY_GET_NEXT_STEP; 1692 } else if (r->rule_action == RULE_ACTION_BLOCK) 1693 action = RES_DROP; 1694 else if (r->rule_action == RULE_ACTION_PASS) 1695 action = RES_PASS; 1696 1697 /* Rule matched */ 1698 rule = r; 1699 1700 /* Temporarily save actions */ 1701 TAILQ_FOREACH(kv, &matches, kv_match_entry) { 1702 TAILQ_INSERT_TAIL(&rule->rule_kvlist, 1703 kv, kv_rule_entry); 1704 } 1705 1706 if (rule->rule_flags & RULE_FLAG_QUICK) 1707 break; 1708 1709 nextrule: 1710 /* Continue to find last matching policy */ 1711 r = TAILQ_NEXT(r, rule_entry); 1712 } 1713 } 1714 1715 if (rule != NULL && 1716 relay_match_actions(cre, rule, NULL, &actions) != 0) { 1717 /* Something bad happened, drop */ 1718 action = RES_DROP; 1719 } 1720 1721 if (relay_apply_actions(cre, &actions) != 0) { 1722 /* Something bad happened, drop */ 1723 action = RES_DROP; 1724 } 1725 1726 DPRINTF("%s: session %d: action %d", __func__, 1727 con->se_id, action); 1728 1729 return (action); 1730 } 1731 1732 #define RELAY_SET_SKIP_STEPS(i) \ 1733 do { \ 1734 while (head[i] != cur) { \ 1735 head[i]->rule_skip[i] = cur; \ 1736 head[i] = TAILQ_NEXT(head[i], rule_entry); \ 1737 } \ 1738 } while (0) 1739 1740 /* This code is derived from pf_calc_skip_steps() from pf.c */ 1741 void 1742 relay_calc_skip_steps(struct relay_rules *rules) 1743 { 1744 struct relay_rule *head[RULE_SKIP_COUNT], *cur, *prev; 1745 int i; 1746 1747 cur = TAILQ_FIRST(rules); 1748 prev = cur; 1749 for (i = 0; i < RULE_SKIP_COUNT; ++i) 1750 head[i] = cur; 1751 while (cur != NULL) { 1752 if (cur->rule_dir != prev->rule_dir) 1753 RELAY_SET_SKIP_STEPS(RULE_SKIP_DIR); 1754 else if (cur->rule_proto != prev->rule_proto) 1755 RELAY_SET_SKIP_STEPS(RULE_SKIP_PROTO); 1756 else if (cur->rule_af != prev->rule_af) 1757 RELAY_SET_SKIP_STEPS(RULE_SKIP_AF); 1758 else if (RELAY_ADDR_NEQ(&cur->rule_src, &prev->rule_src)) 1759 RELAY_SET_SKIP_STEPS(RULE_SKIP_SRC); 1760 else if (RELAY_ADDR_NEQ(&cur->rule_dst, &prev->rule_dst)) 1761 RELAY_SET_SKIP_STEPS(RULE_SKIP_DST); 1762 else if (cur->rule_method != prev->rule_method) 1763 RELAY_SET_SKIP_STEPS(RULE_SKIP_METHOD); 1764 1765 prev = cur; 1766 cur = TAILQ_NEXT(cur, rule_entry); 1767 } 1768 for (i = 0; i < RULE_SKIP_COUNT; ++i) 1769 RELAY_SET_SKIP_STEPS(i); 1770 } 1771 1772 void 1773 relay_match(struct kvlist *actions, struct kv *kv, struct kv *match, 1774 struct kvtree *matchtree) 1775 { 1776 if (kv->kv_option != KEY_OPTION_NONE) { 1777 kv->kv_match = match; 1778 kv->kv_matchtree = matchtree; 1779 TAILQ_INSERT_TAIL(actions, kv, kv_match_entry); 1780 } 1781 } 1782