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