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