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