1 /* $OpenBSD: server_http.c,v 1.110 2016/08/26 10:46:39 rzalamena Exp $ */ 2 3 /* 4 * Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/queue.h> 21 #include <sys/socket.h> 22 #include <sys/tree.h> 23 24 #include <netinet/in.h> 25 #include <arpa/inet.h> 26 27 #include <errno.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <unistd.h> 31 #include <limits.h> 32 #include <fnmatch.h> 33 #include <stdio.h> 34 #include <time.h> 35 #include <resolv.h> 36 #include <event.h> 37 #include <ctype.h> 38 #include <vis.h> 39 40 #include "httpd.h" 41 #include "http.h" 42 #include "patterns.h" 43 44 static int server_httpmethod_cmp(const void *, const void *); 45 static int server_httperror_cmp(const void *, const void *); 46 void server_httpdesc_free(struct http_descriptor *); 47 int server_http_authenticate(struct server_config *, 48 struct client *); 49 char *server_expand_http(struct client *, const char *, 50 char *, size_t); 51 52 static struct http_method http_methods[] = HTTP_METHODS; 53 static struct http_error http_errors[] = HTTP_ERRORS; 54 55 void 56 server_http(void) 57 { 58 DPRINTF("%s: sorting lookup tables, pid %d", __func__, getpid()); 59 60 /* Sort the HTTP lookup arrays */ 61 qsort(http_methods, sizeof(http_methods) / 62 sizeof(http_methods[0]) - 1, 63 sizeof(http_methods[0]), server_httpmethod_cmp); 64 qsort(http_errors, sizeof(http_errors) / 65 sizeof(http_errors[0]) - 1, 66 sizeof(http_errors[0]), server_httperror_cmp); 67 } 68 69 void 70 server_http_init(struct server *srv) 71 { 72 /* nothing */ 73 } 74 75 int 76 server_httpdesc_init(struct client *clt) 77 { 78 struct http_descriptor *desc; 79 80 if ((desc = calloc(1, sizeof(*desc))) == NULL) 81 return (-1); 82 RB_INIT(&desc->http_headers); 83 clt->clt_descreq = desc; 84 85 if ((desc = calloc(1, sizeof(*desc))) == NULL) { 86 /* req will be cleaned up later */ 87 return (-1); 88 } 89 RB_INIT(&desc->http_headers); 90 clt->clt_descresp = desc; 91 92 return (0); 93 } 94 95 void 96 server_httpdesc_free(struct http_descriptor *desc) 97 { 98 if (desc == NULL) 99 return; 100 101 free(desc->http_path); 102 desc->http_path = NULL; 103 free(desc->http_path_alias); 104 desc->http_path_alias = NULL; 105 free(desc->http_query); 106 desc->http_query = NULL; 107 free(desc->http_version); 108 desc->http_version = NULL; 109 free(desc->http_host); 110 desc->http_host = NULL; 111 112 kv_purge(&desc->http_headers); 113 desc->http_lastheader = NULL; 114 desc->http_method = 0; 115 desc->http_chunked = 0; 116 } 117 118 int 119 server_http_authenticate(struct server_config *srv_conf, struct client *clt) 120 { 121 char decoded[1024]; 122 FILE *fp = NULL; 123 struct http_descriptor *desc = clt->clt_descreq; 124 const struct auth *auth = srv_conf->auth; 125 struct kv *ba, key; 126 size_t linesize = 0; 127 ssize_t linelen; 128 int ret = -1; 129 char *line = NULL, *user = NULL, *pass = NULL; 130 char *clt_user = NULL, *clt_pass = NULL; 131 132 memset(decoded, 0, sizeof(decoded)); 133 key.kv_key = "Authorization"; 134 135 if ((ba = kv_find(&desc->http_headers, &key)) == NULL || 136 ba->kv_value == NULL) 137 goto done; 138 139 if (strncmp(ba->kv_value, "Basic ", strlen("Basic ")) != 0) 140 goto done; 141 142 if (b64_pton(strchr(ba->kv_value, ' ') + 1, (uint8_t *)decoded, 143 sizeof(decoded)) <= 0) 144 goto done; 145 146 if ((clt_pass = strchr(decoded, ':')) == NULL) 147 goto done; 148 149 clt_user = decoded; 150 *clt_pass++ = '\0'; 151 if ((clt->clt_remote_user = strdup(clt_user)) == NULL) 152 goto done; 153 154 if (clt_pass == NULL) 155 goto done; 156 157 if ((fp = fopen(auth->auth_htpasswd, "r")) == NULL) 158 goto done; 159 160 while ((linelen = getline(&line, &linesize, fp)) != -1) { 161 if (line[linelen - 1] == '\n') 162 line[linelen - 1] = '\0'; 163 user = line; 164 pass = strchr(line, ':'); 165 166 if (pass == NULL) { 167 explicit_bzero(line, linelen); 168 continue; 169 } 170 171 *pass++ = '\0'; 172 173 if (strcmp(clt_user, user) != 0) { 174 explicit_bzero(line, linelen); 175 continue; 176 } 177 178 if (crypt_checkpass(clt_pass, pass) == 0) { 179 explicit_bzero(line, linelen); 180 ret = 0; 181 break; 182 } 183 } 184 done: 185 free(line); 186 if (fp != NULL) 187 fclose(fp); 188 189 if (ba != NULL && ba->kv_value != NULL) { 190 explicit_bzero(ba->kv_value, strlen(ba->kv_value)); 191 explicit_bzero(decoded, sizeof(decoded)); 192 } 193 194 return (ret); 195 } 196 197 void 198 server_read_http(struct bufferevent *bev, void *arg) 199 { 200 struct client *clt = arg; 201 struct server_config *srv_conf = clt->clt_srv_conf; 202 struct http_descriptor *desc = clt->clt_descreq; 203 struct evbuffer *src = EVBUFFER_INPUT(bev); 204 char *line = NULL, *key, *value; 205 const char *errstr; 206 size_t size, linelen; 207 struct kv *hdr = NULL; 208 209 getmonotime(&clt->clt_tv_last); 210 211 size = EVBUFFER_LENGTH(src); 212 DPRINTF("%s: session %d: size %lu, to read %lld", 213 __func__, clt->clt_id, size, clt->clt_toread); 214 if (!size) { 215 clt->clt_toread = TOREAD_HTTP_HEADER; 216 goto done; 217 } 218 219 while (!clt->clt_done && (line = 220 evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT)) != NULL) { 221 linelen = strlen(line); 222 223 /* 224 * An empty line indicates the end of the request. 225 * libevent already stripped the \r\n for us. 226 */ 227 if (!linelen) { 228 clt->clt_done = 1; 229 free(line); 230 break; 231 } 232 key = line; 233 234 /* Limit the total header length minus \r\n */ 235 clt->clt_headerlen += linelen; 236 if (clt->clt_headerlen > SERVER_MAXHEADERLENGTH) { 237 server_abort_http(clt, 413, "request too large"); 238 goto abort; 239 } 240 241 /* 242 * The first line is the GET/POST/PUT/... request, 243 * subsequent lines are HTTP headers. 244 */ 245 if (++clt->clt_line == 1) 246 value = strchr(key, ' '); 247 else if (*key == ' ' || *key == '\t') 248 /* Multiline headers wrap with a space or tab */ 249 value = NULL; 250 else 251 value = strchr(key, ':'); 252 if (value == NULL) { 253 if (clt->clt_line == 1) { 254 server_abort_http(clt, 400, "malformed"); 255 goto abort; 256 } 257 258 /* Append line to the last header, if present */ 259 if (kv_extend(&desc->http_headers, 260 desc->http_lastheader, line) == NULL) 261 goto fail; 262 263 free(line); 264 continue; 265 } 266 if (*value == ':') { 267 *value++ = '\0'; 268 value += strspn(value, " \t\r\n"); 269 } else { 270 *value++ = '\0'; 271 } 272 273 DPRINTF("%s: session %d: header '%s: %s'", __func__, 274 clt->clt_id, key, value); 275 276 /* 277 * Identify and handle specific HTTP request methods 278 */ 279 if (clt->clt_line == 1) { 280 if ((desc->http_method = server_httpmethod_byname(key)) 281 == HTTP_METHOD_NONE) { 282 server_abort_http(clt, 400, "malformed"); 283 goto abort; 284 } 285 286 /* 287 * Decode request path and query 288 */ 289 desc->http_path = strdup(value); 290 if (desc->http_path == NULL) 291 goto fail; 292 293 desc->http_version = strchr(desc->http_path, ' '); 294 if (desc->http_version == NULL) { 295 server_abort_http(clt, 400, "malformed"); 296 goto abort; 297 } 298 299 *desc->http_version++ = '\0'; 300 desc->http_query = strchr(desc->http_path, '?'); 301 if (desc->http_query != NULL) 302 *desc->http_query++ = '\0'; 303 304 /* 305 * Have to allocate the strings because they could 306 * be changed independently by the filters later. 307 */ 308 if ((desc->http_version = 309 strdup(desc->http_version)) == NULL) 310 goto fail; 311 312 if (desc->http_query != NULL && 313 (desc->http_query = 314 strdup(desc->http_query)) == NULL) 315 goto fail; 316 317 } else if (desc->http_method != HTTP_METHOD_NONE && 318 strcasecmp("Content-Length", key) == 0) { 319 if (desc->http_method == HTTP_METHOD_TRACE || 320 desc->http_method == HTTP_METHOD_CONNECT) { 321 /* 322 * These method should not have a body 323 * and thus no Content-Length header. 324 */ 325 server_abort_http(clt, 400, "malformed"); 326 goto abort; 327 } 328 329 /* 330 * Need to read data from the client after the 331 * HTTP header. 332 * XXX What about non-standard clients not using 333 * the carriage return? And some browsers seem to 334 * include the line length in the content-length. 335 */ 336 clt->clt_toread = strtonum(value, 0, LLONG_MAX, 337 &errstr); 338 if (errstr) { 339 server_abort_http(clt, 500, errstr); 340 goto abort; 341 } 342 if ((size_t)clt->clt_toread > 343 srv_conf->maxrequestbody) { 344 server_abort_http(clt, 413, NULL); 345 goto abort; 346 } 347 } 348 349 if (strcasecmp("Transfer-Encoding", key) == 0 && 350 strcasecmp("chunked", value) == 0) 351 desc->http_chunked = 1; 352 353 if (clt->clt_line != 1) { 354 if ((hdr = kv_add(&desc->http_headers, key, 355 value)) == NULL) 356 goto fail; 357 358 desc->http_lastheader = hdr; 359 } 360 361 free(line); 362 } 363 if (clt->clt_done) { 364 if (desc->http_method == HTTP_METHOD_NONE) { 365 server_abort_http(clt, 406, "no method"); 366 return; 367 } 368 369 switch (desc->http_method) { 370 case HTTP_METHOD_CONNECT: 371 /* Data stream */ 372 clt->clt_toread = TOREAD_UNLIMITED; 373 bev->readcb = server_read; 374 break; 375 case HTTP_METHOD_DELETE: 376 case HTTP_METHOD_GET: 377 case HTTP_METHOD_HEAD: 378 /* WebDAV methods */ 379 case HTTP_METHOD_COPY: 380 case HTTP_METHOD_MOVE: 381 clt->clt_toread = 0; 382 break; 383 case HTTP_METHOD_OPTIONS: 384 case HTTP_METHOD_POST: 385 case HTTP_METHOD_PUT: 386 case HTTP_METHOD_RESPONSE: 387 /* WebDAV methods */ 388 case HTTP_METHOD_PROPFIND: 389 case HTTP_METHOD_PROPPATCH: 390 case HTTP_METHOD_MKCOL: 391 case HTTP_METHOD_LOCK: 392 case HTTP_METHOD_UNLOCK: 393 case HTTP_METHOD_VERSION_CONTROL: 394 case HTTP_METHOD_REPORT: 395 case HTTP_METHOD_CHECKOUT: 396 case HTTP_METHOD_CHECKIN: 397 case HTTP_METHOD_UNCHECKOUT: 398 case HTTP_METHOD_MKWORKSPACE: 399 case HTTP_METHOD_UPDATE: 400 case HTTP_METHOD_LABEL: 401 case HTTP_METHOD_MERGE: 402 case HTTP_METHOD_BASELINE_CONTROL: 403 case HTTP_METHOD_MKACTIVITY: 404 case HTTP_METHOD_ORDERPATCH: 405 case HTTP_METHOD_ACL: 406 case HTTP_METHOD_MKREDIRECTREF: 407 case HTTP_METHOD_UPDATEREDIRECTREF: 408 case HTTP_METHOD_SEARCH: 409 case HTTP_METHOD_PATCH: 410 /* HTTP request payload */ 411 if (clt->clt_toread > 0) 412 bev->readcb = server_read_httpcontent; 413 414 /* Single-pass HTTP body */ 415 if (clt->clt_toread < 0) { 416 clt->clt_toread = TOREAD_UNLIMITED; 417 bev->readcb = server_read; 418 } 419 break; 420 default: 421 server_abort_http(clt, 405, "method not allowed"); 422 return; 423 } 424 if (desc->http_chunked) { 425 /* Chunked transfer encoding */ 426 clt->clt_toread = TOREAD_HTTP_CHUNK_LENGTH; 427 bev->readcb = server_read_httpchunks; 428 } 429 430 done: 431 if (clt->clt_toread != 0) 432 bufferevent_disable(bev, EV_READ); 433 server_response(httpd_env, clt); 434 return; 435 } 436 if (clt->clt_done) { 437 server_close(clt, "done"); 438 return; 439 } 440 if (EVBUFFER_LENGTH(src) && bev->readcb != server_read_http) 441 bev->readcb(bev, arg); 442 bufferevent_enable(bev, EV_READ); 443 return; 444 fail: 445 server_abort_http(clt, 500, strerror(errno)); 446 abort: 447 free(line); 448 } 449 450 void 451 server_read_httpcontent(struct bufferevent *bev, void *arg) 452 { 453 struct client *clt = arg; 454 struct evbuffer *src = EVBUFFER_INPUT(bev); 455 size_t size; 456 457 getmonotime(&clt->clt_tv_last); 458 459 size = EVBUFFER_LENGTH(src); 460 DPRINTF("%s: session %d: size %lu, to read %lld", __func__, 461 clt->clt_id, size, clt->clt_toread); 462 if (!size) 463 return; 464 465 if (clt->clt_toread > 0) { 466 /* Read content data */ 467 if ((off_t)size > clt->clt_toread) { 468 size = clt->clt_toread; 469 if (fcgi_add_stdin(clt, src) == -1) 470 goto fail; 471 clt->clt_toread = 0; 472 } else { 473 if (fcgi_add_stdin(clt, src) == -1) 474 goto fail; 475 clt->clt_toread -= size; 476 } 477 DPRINTF("%s: done, size %lu, to read %lld", __func__, 478 size, clt->clt_toread); 479 } 480 if (clt->clt_toread == 0) { 481 fcgi_add_stdin(clt, NULL); 482 clt->clt_toread = TOREAD_HTTP_HEADER; 483 bufferevent_disable(bev, EV_READ); 484 bev->readcb = server_read_http; 485 return; 486 } 487 if (clt->clt_done) 488 goto done; 489 if (bev->readcb != server_read_httpcontent) 490 bev->readcb(bev, arg); 491 492 return; 493 done: 494 return; 495 fail: 496 server_close(clt, strerror(errno)); 497 } 498 499 void 500 server_read_httpchunks(struct bufferevent *bev, void *arg) 501 { 502 struct client *clt = arg; 503 struct evbuffer *src = EVBUFFER_INPUT(bev); 504 char *line; 505 long long llval; 506 size_t size; 507 508 getmonotime(&clt->clt_tv_last); 509 510 size = EVBUFFER_LENGTH(src); 511 DPRINTF("%s: session %d: size %lu, to read %lld", __func__, 512 clt->clt_id, size, clt->clt_toread); 513 if (!size) 514 return; 515 516 if (clt->clt_toread > 0) { 517 /* Read chunk data */ 518 if ((off_t)size > clt->clt_toread) { 519 size = clt->clt_toread; 520 if (server_bufferevent_write_chunk(clt, src, size) 521 == -1) 522 goto fail; 523 clt->clt_toread = 0; 524 } else { 525 if (server_bufferevent_write_buffer(clt, src) == -1) 526 goto fail; 527 clt->clt_toread -= size; 528 } 529 DPRINTF("%s: done, size %lu, to read %lld", __func__, 530 size, clt->clt_toread); 531 } 532 switch (clt->clt_toread) { 533 case TOREAD_HTTP_CHUNK_LENGTH: 534 line = evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT); 535 if (line == NULL) { 536 /* Ignore empty line, continue */ 537 bufferevent_enable(bev, EV_READ); 538 return; 539 } 540 if (strlen(line) == 0) { 541 free(line); 542 goto next; 543 } 544 545 /* 546 * Read prepended chunk size in hex, ignore the trailer. 547 * The returned signed value must not be negative. 548 */ 549 if (sscanf(line, "%llx", &llval) != 1 || llval < 0) { 550 free(line); 551 server_close(clt, "invalid chunk size"); 552 return; 553 } 554 555 if (server_bufferevent_print(clt, line) == -1 || 556 server_bufferevent_print(clt, "\r\n") == -1) { 557 free(line); 558 goto fail; 559 } 560 free(line); 561 562 if ((clt->clt_toread = llval) == 0) { 563 DPRINTF("%s: last chunk", __func__); 564 clt->clt_toread = TOREAD_HTTP_CHUNK_TRAILER; 565 } 566 break; 567 case TOREAD_HTTP_CHUNK_TRAILER: 568 /* Last chunk is 0 bytes followed by trailer and empty line */ 569 line = evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT); 570 if (line == NULL) { 571 /* Ignore empty line, continue */ 572 bufferevent_enable(bev, EV_READ); 573 return; 574 } 575 if (server_bufferevent_print(clt, line) == -1 || 576 server_bufferevent_print(clt, "\r\n") == -1) { 577 free(line); 578 goto fail; 579 } 580 if (strlen(line) == 0) { 581 /* Switch to HTTP header mode */ 582 clt->clt_toread = TOREAD_HTTP_HEADER; 583 bev->readcb = server_read_http; 584 } 585 free(line); 586 break; 587 case 0: 588 /* Chunk is terminated by an empty newline */ 589 line = evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT); 590 free(line); 591 if (server_bufferevent_print(clt, "\r\n") == -1) 592 goto fail; 593 clt->clt_toread = TOREAD_HTTP_CHUNK_LENGTH; 594 break; 595 } 596 597 next: 598 if (clt->clt_done) 599 goto done; 600 if (EVBUFFER_LENGTH(src)) 601 bev->readcb(bev, arg); 602 bufferevent_enable(bev, EV_READ); 603 return; 604 605 done: 606 server_close(clt, "last http chunk read (done)"); 607 return; 608 fail: 609 server_close(clt, strerror(errno)); 610 } 611 612 void 613 server_reset_http(struct client *clt) 614 { 615 struct server *srv = clt->clt_srv; 616 617 server_log(clt, NULL); 618 619 server_httpdesc_free(clt->clt_descreq); 620 server_httpdesc_free(clt->clt_descresp); 621 clt->clt_headerlen = 0; 622 clt->clt_line = 0; 623 clt->clt_done = 0; 624 clt->clt_chunk = 0; 625 free(clt->clt_remote_user); 626 clt->clt_remote_user = NULL; 627 clt->clt_bev->readcb = server_read_http; 628 clt->clt_srv_conf = &srv->srv_conf; 629 str_match_free(&clt->clt_srv_match); 630 } 631 632 ssize_t 633 server_http_time(time_t t, char *tmbuf, size_t len) 634 { 635 struct tm tm; 636 637 /* New HTTP/1.1 RFC 7231 prefers IMF-fixdate from RFC 5322 */ 638 if (t == -1 || gmtime_r(&t, &tm) == NULL) 639 return (-1); 640 else 641 return (strftime(tmbuf, len, "%a, %d %h %Y %T %Z", &tm)); 642 } 643 644 const char * 645 server_http_host(struct sockaddr_storage *ss, char *buf, size_t len) 646 { 647 char hbuf[HOST_NAME_MAX+1]; 648 in_port_t port; 649 650 if (print_host(ss, buf, len) == NULL) 651 return (NULL); 652 653 port = ntohs(server_socket_getport(ss)); 654 if (port == HTTP_PORT) 655 return (buf); 656 657 switch (ss->ss_family) { 658 case AF_INET: 659 if ((size_t)snprintf(hbuf, sizeof(hbuf), 660 "%s:%u", buf, port) >= sizeof(hbuf)) 661 return (NULL); 662 break; 663 case AF_INET6: 664 if ((size_t)snprintf(hbuf, sizeof(hbuf), 665 "[%s]:%u", buf, port) >= sizeof(hbuf)) 666 return (NULL); 667 break; 668 } 669 670 if (strlcpy(buf, hbuf, len) >= len) 671 return (NULL); 672 673 return (buf); 674 } 675 676 char * 677 server_http_parsehost(char *host, char *buf, size_t len, int *portval) 678 { 679 char *start, *end, *port; 680 const char *errstr = NULL; 681 682 if (strlcpy(buf, host, len) >= len) { 683 log_debug("%s: host name too long", __func__); 684 return (NULL); 685 } 686 687 start = buf; 688 end = port = NULL; 689 690 if (*start == '[' && (end = strchr(start, ']')) != NULL) { 691 /* Address enclosed in [] with port, eg. [2001:db8::1]:80 */ 692 start++; 693 *end++ = '\0'; 694 if ((port = strchr(end, ':')) == NULL || *port == '\0') 695 port = NULL; 696 else 697 port++; 698 memmove(buf, start, strlen(start) + 1); 699 } else if ((end = strchr(start, ':')) != NULL) { 700 /* Name or address with port, eg. www.example.com:80 */ 701 *end++ = '\0'; 702 port = end; 703 } else { 704 /* Name or address with default port, eg. www.example.com */ 705 port = NULL; 706 } 707 708 if (port != NULL) { 709 /* Save the requested port */ 710 *portval = strtonum(port, 0, 0xffff, &errstr); 711 if (errstr != NULL) { 712 log_debug("%s: invalid port: %s", __func__, 713 strerror(errno)); 714 return (NULL); 715 } 716 *portval = htons(*portval); 717 } else { 718 /* Port not given, indicate the default port */ 719 *portval = -1; 720 } 721 722 return (start); 723 } 724 725 void 726 server_abort_http(struct client *clt, unsigned int code, const char *msg) 727 { 728 struct server_config *srv_conf = clt->clt_srv_conf; 729 struct bufferevent *bev = clt->clt_bev; 730 struct http_descriptor *desc = clt->clt_descreq; 731 const char *httperr = NULL, *style; 732 char *httpmsg, *body = NULL, *extraheader = NULL; 733 char tmbuf[32], hbuf[128], *hstsheader = NULL; 734 char buf[IBUF_READ_SIZE]; 735 char *escapedmsg = NULL; 736 int bodylen; 737 738 if (code == 0) { 739 server_close(clt, "dropped"); 740 return; 741 } 742 743 if ((httperr = server_httperror_byid(code)) == NULL) 744 httperr = "Unknown Error"; 745 746 if (bev == NULL) 747 goto done; 748 749 if (server_log_http(clt, code, 0) == -1) 750 goto done; 751 752 /* Some system information */ 753 if (print_host(&srv_conf->ss, hbuf, sizeof(hbuf)) == NULL) 754 goto done; 755 756 if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0) 757 goto done; 758 759 /* Do not send details of the Internal Server Error */ 760 switch (code) { 761 case 301: 762 case 302: 763 case 303: 764 if (msg == NULL) 765 break; 766 memset(buf, 0, sizeof(buf)); 767 if (server_expand_http(clt, msg, buf, sizeof(buf)) == NULL) 768 goto done; 769 if (asprintf(&extraheader, "Location: %s\r\n", buf) == -1) { 770 code = 500; 771 extraheader = NULL; 772 } 773 msg = buf; 774 break; 775 case 401: 776 if (stravis(&escapedmsg, msg, VIS_DQ) == -1) { 777 code = 500; 778 extraheader = NULL; 779 } else if (asprintf(&extraheader, 780 "WWW-Authenticate: Basic realm=\"%s\"\r\n", escapedmsg) 781 == -1) { 782 code = 500; 783 extraheader = NULL; 784 } 785 break; 786 case 416: 787 if (asprintf(&extraheader, 788 "Content-Range: %s\r\n", msg) == -1) { 789 code = 500; 790 extraheader = NULL; 791 } 792 break; 793 default: 794 /* 795 * Do not send details of the error. Traditionally, 796 * web servers responsed with the request path on 40x 797 * errors which could be abused to inject JavaScript etc. 798 * Instead of sanitizing the path here, we just don't 799 * reprint it. 800 */ 801 break; 802 } 803 804 free(escapedmsg); 805 806 /* A CSS stylesheet allows minimal customization by the user */ 807 style = "body { background-color: white; color: black; font-family: " 808 "'Comic Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif; }\n" 809 "hr { border: 0; border-bottom: 1px dashed; }\n"; 810 811 /* Generate simple HTML error document */ 812 if ((bodylen = asprintf(&body, 813 "<!DOCTYPE html>\n" 814 "<html>\n" 815 "<head>\n" 816 "<meta http-equiv=\"Content-Type\" content=\"text/html; " 817 "charset=utf-8\"/>\n" 818 "<title>%03d %s</title>\n" 819 "<style type=\"text/css\"><!--\n%s\n--></style>\n" 820 "</head>\n" 821 "<body>\n" 822 "<h1>%03d %s</h1>\n" 823 "<hr>\n<address>%s</address>\n" 824 "</body>\n" 825 "</html>\n", 826 code, httperr, style, code, httperr, HTTPD_SERVERNAME)) == -1) { 827 body = NULL; 828 goto done; 829 } 830 831 if (srv_conf->flags & SRVFLAG_SERVER_HSTS) { 832 if (asprintf(&hstsheader, "Strict-Transport-Security: " 833 "max-age=%d%s%s\r\n", srv_conf->hsts_max_age, 834 srv_conf->hsts_flags & HSTSFLAG_SUBDOMAINS ? 835 "; includeSubDomains" : "", 836 srv_conf->hsts_flags & HSTSFLAG_PRELOAD ? 837 "; preload" : "") == -1) { 838 hstsheader = NULL; 839 goto done; 840 } 841 } 842 843 /* Add basic HTTP headers */ 844 if (asprintf(&httpmsg, 845 "HTTP/1.0 %03d %s\r\n" 846 "Date: %s\r\n" 847 "Server: %s\r\n" 848 "Connection: close\r\n" 849 "Content-Type: text/html\r\n" 850 "Content-Length: %d\r\n" 851 "%s" 852 "%s" 853 "\r\n" 854 "%s", 855 code, httperr, tmbuf, HTTPD_SERVERNAME, bodylen, 856 extraheader == NULL ? "" : extraheader, 857 hstsheader == NULL ? "" : hstsheader, 858 desc->http_method == HTTP_METHOD_HEAD ? "" : body) == -1) 859 goto done; 860 861 /* Dump the message without checking for success */ 862 server_dump(clt, httpmsg, strlen(httpmsg)); 863 free(httpmsg); 864 865 done: 866 free(body); 867 free(extraheader); 868 free(hstsheader); 869 if (msg == NULL) 870 msg = "\"\""; 871 if (asprintf(&httpmsg, "%s (%03d %s)", msg, code, httperr) == -1) { 872 server_close(clt, msg); 873 } else { 874 server_close(clt, httpmsg); 875 free(httpmsg); 876 } 877 } 878 879 void 880 server_close_http(struct client *clt) 881 { 882 struct http_descriptor *desc; 883 884 desc = clt->clt_descreq; 885 server_httpdesc_free(desc); 886 free(desc); 887 clt->clt_descreq = NULL; 888 889 desc = clt->clt_descresp; 890 server_httpdesc_free(desc); 891 free(desc); 892 clt->clt_descresp = NULL; 893 free(clt->clt_remote_user); 894 clt->clt_remote_user = NULL; 895 896 str_match_free(&clt->clt_srv_match); 897 } 898 899 char * 900 server_expand_http(struct client *clt, const char *val, char *buf, 901 size_t len) 902 { 903 struct http_descriptor *desc = clt->clt_descreq; 904 struct server_config *srv_conf = clt->clt_srv_conf; 905 char ibuf[128], *str, *path, *query; 906 const char *errstr = NULL, *p; 907 size_t size; 908 int n, ret; 909 910 if (strlcpy(buf, val, len) >= len) 911 return (NULL); 912 913 /* Find previously matched substrings by index */ 914 for (p = val; clt->clt_srv_match.sm_nmatch && 915 (p = strstr(p, "%")) != NULL; p++) { 916 if (!isdigit((unsigned char)*(p + 1))) 917 continue; 918 919 /* Copy number, leading '%' char and add trailing \0 */ 920 size = strspn(p + 1, "0123456789") + 2; 921 if (size >= sizeof(ibuf)) 922 return (NULL); 923 (void)strlcpy(ibuf, p, size); 924 n = strtonum(ibuf + 1, 0, 925 clt->clt_srv_match.sm_nmatch - 1, &errstr); 926 if (errstr != NULL) 927 return (NULL); 928 929 /* Expand variable with matched value */ 930 if ((str = url_encode(clt->clt_srv_match.sm_match[n])) == NULL) 931 return (NULL); 932 ret = expand_string(buf, len, ibuf, str); 933 free(str); 934 if (ret != 0) 935 return (NULL); 936 } 937 if (strstr(val, "$DOCUMENT_URI") != NULL) { 938 if ((path = url_encode(desc->http_path)) == NULL) 939 return (NULL); 940 ret = expand_string(buf, len, "$DOCUMENT_URI", path); 941 free(path); 942 if (ret != 0) 943 return (NULL); 944 } 945 if (strstr(val, "$QUERY_STRING") != NULL) { 946 if (desc->http_query == NULL) { 947 ret = expand_string(buf, len, "$QUERY_STRING", ""); 948 } else { 949 if ((query = url_encode(desc->http_query)) == NULL) 950 return (NULL); 951 ret = expand_string(buf, len, "$QUERY_STRING", query); 952 free(query); 953 } 954 if (ret != 0) 955 return (NULL); 956 } 957 if (strstr(val, "$REMOTE_") != NULL) { 958 if (strstr(val, "$REMOTE_ADDR") != NULL) { 959 if (print_host(&clt->clt_ss, 960 ibuf, sizeof(ibuf)) == NULL) 961 return (NULL); 962 if (expand_string(buf, len, 963 "$REMOTE_ADDR", ibuf) != 0) 964 return (NULL); 965 } 966 if (strstr(val, "$REMOTE_PORT") != NULL) { 967 snprintf(ibuf, sizeof(ibuf), 968 "%u", ntohs(clt->clt_port)); 969 if (expand_string(buf, len, 970 "$REMOTE_PORT", ibuf) != 0) 971 return (NULL); 972 } 973 if (strstr(val, "$REMOTE_USER") != NULL) { 974 if ((srv_conf->flags & SRVFLAG_AUTH) && 975 clt->clt_remote_user != NULL) { 976 if ((str = url_encode(clt->clt_remote_user)) 977 == NULL) 978 return (NULL); 979 } else 980 str = strdup(""); 981 ret = expand_string(buf, len, "$REMOTE_USER", str); 982 free(str); 983 if (ret != 0) 984 return (NULL); 985 } 986 } 987 if (strstr(val, "$REQUEST_URI") != NULL) { 988 if ((path = url_encode(desc->http_path)) == NULL) 989 return (NULL); 990 if (desc->http_query == NULL) { 991 str = path; 992 } else { 993 if ((query = url_encode(desc->http_query)) == NULL) { 994 free(path); 995 return (NULL); 996 } 997 ret = asprintf(&str, "%s?%s", path, query); 998 free(path); 999 free(query); 1000 if (ret == -1) 1001 return (NULL); 1002 } 1003 1004 ret = expand_string(buf, len, "$REQUEST_URI", str); 1005 free(str); 1006 if (ret != 0) 1007 return (NULL); 1008 } 1009 if (strstr(val, "$SERVER_") != NULL) { 1010 if (strstr(val, "$SERVER_ADDR") != NULL) { 1011 if (print_host(&srv_conf->ss, 1012 ibuf, sizeof(ibuf)) == NULL) 1013 return (NULL); 1014 if (expand_string(buf, len, 1015 "$SERVER_ADDR", ibuf) != 0) 1016 return (NULL); 1017 } 1018 if (strstr(val, "$SERVER_PORT") != NULL) { 1019 snprintf(ibuf, sizeof(ibuf), "%u", 1020 ntohs(srv_conf->port)); 1021 if (expand_string(buf, len, 1022 "$SERVER_PORT", ibuf) != 0) 1023 return (NULL); 1024 } 1025 if (strstr(val, "$SERVER_NAME") != NULL) { 1026 if ((str = url_encode(srv_conf->name)) 1027 == NULL) 1028 return (NULL); 1029 ret = expand_string(buf, len, "$SERVER_NAME", str); 1030 free(str); 1031 if (ret != 0) 1032 return (NULL); 1033 } 1034 } 1035 1036 return (buf); 1037 } 1038 1039 int 1040 server_response(struct httpd *httpd, struct client *clt) 1041 { 1042 char path[PATH_MAX]; 1043 char hostname[HOST_NAME_MAX+1]; 1044 struct http_descriptor *desc = clt->clt_descreq; 1045 struct http_descriptor *resp = clt->clt_descresp; 1046 struct server *srv = clt->clt_srv; 1047 struct server_config *srv_conf = &srv->srv_conf; 1048 struct kv *kv, key, *host; 1049 struct str_find sm; 1050 int portval = -1, ret; 1051 char *hostval; 1052 const char *errstr = NULL; 1053 1054 /* Canonicalize the request path */ 1055 if (desc->http_path == NULL || 1056 url_decode(desc->http_path) == NULL || 1057 canonicalize_path(desc->http_path, path, sizeof(path)) == NULL) 1058 goto fail; 1059 free(desc->http_path); 1060 if ((desc->http_path = strdup(path)) == NULL) 1061 goto fail; 1062 1063 key.kv_key = "Host"; 1064 if ((host = kv_find(&desc->http_headers, &key)) != NULL && 1065 host->kv_value == NULL) 1066 host = NULL; 1067 1068 if (strcmp(desc->http_version, "HTTP/1.1") == 0) { 1069 /* Host header is mandatory */ 1070 if (host == NULL) 1071 goto fail; 1072 1073 /* Is the connection persistent? */ 1074 key.kv_key = "Connection"; 1075 if ((kv = kv_find(&desc->http_headers, &key)) != NULL && 1076 strcasecmp("close", kv->kv_value) == 0) 1077 clt->clt_persist = 0; 1078 else 1079 clt->clt_persist++; 1080 } else { 1081 /* Is the connection persistent? */ 1082 key.kv_key = "Connection"; 1083 if ((kv = kv_find(&desc->http_headers, &key)) != NULL && 1084 strcasecmp("keep-alive", kv->kv_value) == 0) 1085 clt->clt_persist++; 1086 else 1087 clt->clt_persist = 0; 1088 } 1089 1090 if (clt->clt_persist >= srv_conf->maxrequests) 1091 clt->clt_persist = 0; 1092 1093 /* 1094 * Do we have a Host header and matching configuration? 1095 * XXX the Host can also appear in the URL path. 1096 */ 1097 if (host != NULL) { 1098 if ((hostval = server_http_parsehost(host->kv_value, 1099 hostname, sizeof(hostname), &portval)) == NULL) 1100 goto fail; 1101 1102 TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) { 1103 #ifdef DEBUG 1104 if ((srv_conf->flags & SRVFLAG_LOCATION) == 0) { 1105 DPRINTF("%s: virtual host \"%s:%u\"" 1106 " host \"%s\" (\"%s\")", 1107 __func__, srv_conf->name, 1108 ntohs(srv_conf->port), host->kv_value, 1109 hostname); 1110 } 1111 #endif 1112 if (srv_conf->flags & SRVFLAG_LOCATION) 1113 continue; 1114 else if (srv_conf->flags & SRVFLAG_SERVER_MATCH) { 1115 str_find(hostname, srv_conf->name, 1116 &sm, 1, &errstr); 1117 ret = errstr == NULL ? 0 : -1; 1118 } else { 1119 ret = fnmatch(srv_conf->name, 1120 hostname, FNM_CASEFOLD); 1121 } 1122 if (ret == 0 && 1123 (portval == -1 || 1124 (portval != -1 && portval == srv_conf->port))) { 1125 /* Replace host configuration */ 1126 clt->clt_srv_conf = srv_conf; 1127 srv_conf = NULL; 1128 break; 1129 } 1130 } 1131 } 1132 1133 if (srv_conf != NULL) { 1134 /* Use the actual server IP address */ 1135 if (server_http_host(&clt->clt_srv_ss, hostname, 1136 sizeof(hostname)) == NULL) 1137 goto fail; 1138 } else { 1139 /* Host header was valid and found */ 1140 if (strlcpy(hostname, host->kv_value, sizeof(hostname)) >= 1141 sizeof(hostname)) 1142 goto fail; 1143 srv_conf = clt->clt_srv_conf; 1144 } 1145 1146 if ((desc->http_host = strdup(hostname)) == NULL) 1147 goto fail; 1148 1149 /* Now fill in the mandatory parts of the response descriptor */ 1150 resp->http_method = desc->http_method; 1151 if ((resp->http_version = strdup(desc->http_version)) == NULL) 1152 goto fail; 1153 1154 /* Now search for the location */ 1155 srv_conf = server_getlocation(clt, desc->http_path); 1156 1157 if (srv_conf->flags & SRVFLAG_BLOCK) { 1158 server_abort_http(clt, srv_conf->return_code, 1159 srv_conf->return_uri); 1160 return (-1); 1161 } else if (srv_conf->flags & SRVFLAG_AUTH && 1162 server_http_authenticate(srv_conf, clt) == -1) { 1163 server_abort_http(clt, 401, srv_conf->auth_realm); 1164 return (-1); 1165 } else 1166 return (server_file(httpd, clt)); 1167 fail: 1168 server_abort_http(clt, 400, "bad request"); 1169 return (-1); 1170 } 1171 1172 const char * 1173 server_root_strip(const char *path, int n) 1174 { 1175 const char *p; 1176 1177 /* Strip strip leading directories. Leading '/' is ignored. */ 1178 for (; n > 0 && *path != '\0'; n--) 1179 if ((p = strchr(++path, '/')) == NULL) 1180 path = strchr(path, '\0'); 1181 else 1182 path = p; 1183 1184 return (path); 1185 } 1186 1187 struct server_config * 1188 server_getlocation(struct client *clt, const char *path) 1189 { 1190 struct server *srv = clt->clt_srv; 1191 struct server_config *srv_conf = clt->clt_srv_conf, *location; 1192 const char *errstr = NULL; 1193 int ret; 1194 1195 /* Now search for the location */ 1196 TAILQ_FOREACH(location, &srv->srv_hosts, entry) { 1197 #ifdef DEBUG 1198 if (location->flags & SRVFLAG_LOCATION) { 1199 DPRINTF("%s: location \"%s\" path \"%s\"", 1200 __func__, location->location, path); 1201 } 1202 #endif 1203 if ((location->flags & SRVFLAG_LOCATION) && 1204 location->parent_id == srv_conf->parent_id) { 1205 errstr = NULL; 1206 if (location->flags & SRVFLAG_LOCATION_MATCH) { 1207 ret = str_match(path, location->location, 1208 &clt->clt_srv_match, &errstr); 1209 } else { 1210 ret = fnmatch(location->location, 1211 path, FNM_CASEFOLD); 1212 } 1213 if (ret == 0 && errstr == NULL) { 1214 /* Replace host configuration */ 1215 clt->clt_srv_conf = srv_conf = location; 1216 break; 1217 } 1218 } 1219 } 1220 1221 return (srv_conf); 1222 } 1223 1224 int 1225 server_response_http(struct client *clt, unsigned int code, 1226 struct media_type *media, off_t size, time_t mtime) 1227 { 1228 struct server_config *srv_conf = clt->clt_srv_conf; 1229 struct http_descriptor *desc = clt->clt_descreq; 1230 struct http_descriptor *resp = clt->clt_descresp; 1231 const char *error; 1232 struct kv *ct, *cl; 1233 char tmbuf[32]; 1234 1235 if (desc == NULL || media == NULL || 1236 (error = server_httperror_byid(code)) == NULL) 1237 return (-1); 1238 1239 if (server_log_http(clt, code, size) == -1) 1240 return (-1); 1241 1242 /* Add error codes */ 1243 if (kv_setkey(&resp->http_pathquery, "%u", code) == -1 || 1244 kv_set(&resp->http_pathquery, "%s", error) == -1) 1245 return (-1); 1246 1247 /* Add headers */ 1248 if (kv_add(&resp->http_headers, "Server", HTTPD_SERVERNAME) == NULL) 1249 return (-1); 1250 1251 /* Is it a persistent connection? */ 1252 if (clt->clt_persist) { 1253 if (kv_add(&resp->http_headers, 1254 "Connection", "keep-alive") == NULL) 1255 return (-1); 1256 } else if (kv_add(&resp->http_headers, "Connection", "close") == NULL) 1257 return (-1); 1258 1259 /* Set media type */ 1260 if ((ct = kv_add(&resp->http_headers, "Content-Type", NULL)) == NULL || 1261 kv_set(ct, "%s/%s", media->media_type, media->media_subtype) == -1) 1262 return (-1); 1263 1264 /* Set content length, if specified */ 1265 if ((cl = 1266 kv_add(&resp->http_headers, "Content-Length", NULL)) == NULL || 1267 kv_set(cl, "%lld", (long long)size) == -1) 1268 return (-1); 1269 1270 /* Set last modification time */ 1271 if (server_http_time(mtime, tmbuf, sizeof(tmbuf)) <= 0 || 1272 kv_add(&resp->http_headers, "Last-Modified", tmbuf) == NULL) 1273 return (-1); 1274 1275 /* HSTS header */ 1276 if (srv_conf->flags & SRVFLAG_SERVER_HSTS) { 1277 if ((cl = 1278 kv_add(&resp->http_headers, "Strict-Transport-Security", 1279 NULL)) == NULL || 1280 kv_set(cl, "max-age=%d%s%s", srv_conf->hsts_max_age, 1281 srv_conf->hsts_flags & HSTSFLAG_SUBDOMAINS ? 1282 "; includeSubDomains" : "", 1283 srv_conf->hsts_flags & HSTSFLAG_PRELOAD ? 1284 "; preload" : "") == -1) 1285 return (-1); 1286 } 1287 1288 /* Date header is mandatory and should be added as late as possible */ 1289 if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0 || 1290 kv_add(&resp->http_headers, "Date", tmbuf) == NULL) 1291 return (-1); 1292 1293 /* Write completed header */ 1294 if (server_writeresponse_http(clt) == -1 || 1295 server_bufferevent_print(clt, "\r\n") == -1 || 1296 server_headers(clt, resp, server_writeheader_http, NULL) == -1 || 1297 server_bufferevent_print(clt, "\r\n") == -1) 1298 return (-1); 1299 1300 if (size == 0 || resp->http_method == HTTP_METHOD_HEAD) { 1301 bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE); 1302 if (clt->clt_persist) 1303 clt->clt_toread = TOREAD_HTTP_HEADER; 1304 else 1305 clt->clt_toread = TOREAD_HTTP_NONE; 1306 clt->clt_done = 0; 1307 return (0); 1308 } 1309 1310 return (1); 1311 } 1312 1313 int 1314 server_writeresponse_http(struct client *clt) 1315 { 1316 struct http_descriptor *desc = clt->clt_descresp; 1317 1318 DPRINTF("version: %s rescode: %s resmsg: %s", desc->http_version, 1319 desc->http_rescode, desc->http_resmesg); 1320 1321 if (server_bufferevent_print(clt, desc->http_version) == -1 || 1322 server_bufferevent_print(clt, " ") == -1 || 1323 server_bufferevent_print(clt, desc->http_rescode) == -1 || 1324 server_bufferevent_print(clt, " ") == -1 || 1325 server_bufferevent_print(clt, desc->http_resmesg) == -1) 1326 return (-1); 1327 1328 return (0); 1329 } 1330 1331 int 1332 server_writeheader_http(struct client *clt, struct kv *hdr, void *arg) 1333 { 1334 char *ptr; 1335 const char *key; 1336 1337 if (hdr->kv_flags & KV_FLAG_INVALID) 1338 return (0); 1339 1340 /* The key might have been updated in the parent */ 1341 if (hdr->kv_parent != NULL && hdr->kv_parent->kv_key != NULL) 1342 key = hdr->kv_parent->kv_key; 1343 else 1344 key = hdr->kv_key; 1345 1346 ptr = hdr->kv_value; 1347 if (server_bufferevent_print(clt, key) == -1 || 1348 (ptr != NULL && 1349 (server_bufferevent_print(clt, ": ") == -1 || 1350 server_bufferevent_print(clt, ptr) == -1 || 1351 server_bufferevent_print(clt, "\r\n") == -1))) 1352 return (-1); 1353 DPRINTF("%s: %s: %s", __func__, key, 1354 hdr->kv_value == NULL ? "" : hdr->kv_value); 1355 1356 return (0); 1357 } 1358 1359 int 1360 server_headers(struct client *clt, void *descp, 1361 int (*hdr_cb)(struct client *, struct kv *, void *), void *arg) 1362 { 1363 struct kv *hdr, *kv; 1364 struct http_descriptor *desc = descp; 1365 1366 RB_FOREACH(hdr, kvtree, &desc->http_headers) { 1367 if ((hdr_cb)(clt, hdr, arg) == -1) 1368 return (-1); 1369 TAILQ_FOREACH(kv, &hdr->kv_children, kv_entry) { 1370 if ((hdr_cb)(clt, kv, arg) == -1) 1371 return (-1); 1372 } 1373 } 1374 1375 return (0); 1376 } 1377 1378 enum httpmethod 1379 server_httpmethod_byname(const char *name) 1380 { 1381 enum httpmethod id = HTTP_METHOD_NONE; 1382 struct http_method method, *res = NULL; 1383 1384 /* Set up key */ 1385 method.method_name = name; 1386 1387 if ((res = bsearch(&method, http_methods, 1388 sizeof(http_methods) / sizeof(http_methods[0]) - 1, 1389 sizeof(http_methods[0]), server_httpmethod_cmp)) != NULL) 1390 id = res->method_id; 1391 1392 return (id); 1393 } 1394 1395 const char * 1396 server_httpmethod_byid(unsigned int id) 1397 { 1398 const char *name = "<UNKNOWN>"; 1399 int i; 1400 1401 for (i = 0; http_methods[i].method_name != NULL; i++) { 1402 if (http_methods[i].method_id == id) { 1403 name = http_methods[i].method_name; 1404 break; 1405 } 1406 } 1407 1408 return (name); 1409 } 1410 1411 static int 1412 server_httpmethod_cmp(const void *a, const void *b) 1413 { 1414 const struct http_method *ma = a; 1415 const struct http_method *mb = b; 1416 1417 /* 1418 * RFC 2616 section 5.1.1 says that the method is case 1419 * sensitive so we don't do a strcasecmp here. 1420 */ 1421 return (strcmp(ma->method_name, mb->method_name)); 1422 } 1423 1424 const char * 1425 server_httperror_byid(unsigned int id) 1426 { 1427 struct http_error error, *res; 1428 1429 /* Set up key */ 1430 error.error_code = (int)id; 1431 1432 if ((res = bsearch(&error, http_errors, 1433 sizeof(http_errors) / sizeof(http_errors[0]) - 1, 1434 sizeof(http_errors[0]), server_httperror_cmp)) != NULL) 1435 return (res->error_name); 1436 1437 return (NULL); 1438 } 1439 1440 static int 1441 server_httperror_cmp(const void *a, const void *b) 1442 { 1443 const struct http_error *ea = a; 1444 const struct http_error *eb = b; 1445 return (ea->error_code - eb->error_code); 1446 } 1447 1448 int 1449 server_log_http(struct client *clt, unsigned int code, size_t len) 1450 { 1451 static char tstamp[64]; 1452 static char ip[INET6_ADDRSTRLEN]; 1453 time_t t; 1454 struct kv key, *agent, *referrer; 1455 struct tm *tm; 1456 struct server_config *srv_conf; 1457 struct http_descriptor *desc; 1458 int ret = -1; 1459 char *user = NULL; 1460 char *path = NULL; 1461 char *query = NULL; 1462 char *version = NULL; 1463 char *referrer_v = NULL; 1464 char *agent_v = NULL; 1465 1466 if ((srv_conf = clt->clt_srv_conf) == NULL) 1467 return (-1); 1468 if ((srv_conf->flags & SRVFLAG_LOG) == 0) 1469 return (0); 1470 if ((desc = clt->clt_descreq) == NULL) 1471 return (-1); 1472 1473 if ((t = time(NULL)) == -1) 1474 return (-1); 1475 if ((tm = localtime(&t)) == NULL) 1476 return (-1); 1477 if (strftime(tstamp, sizeof(tstamp), "%d/%b/%Y:%H:%M:%S %z", tm) == 0) 1478 return (-1); 1479 1480 if (print_host(&clt->clt_ss, ip, sizeof(ip)) == NULL) 1481 return (-1); 1482 1483 /* 1484 * For details on common log format, see: 1485 * https://httpd.apache.org/docs/current/mod/mod_log_config.html 1486 * 1487 * httpd's format is similar to these Apache LogFormats: 1488 * "%v %h %l %u %t \"%r\" %>s %B" 1489 * "%v %h %l %u %t \"%r\" %>s %B \"%{Referer}i\" \"%{User-agent}i\"" 1490 */ 1491 switch (srv_conf->logformat) { 1492 case LOG_FORMAT_COMMON: 1493 /* Use vis to encode input values from the header */ 1494 if (clt->clt_remote_user && 1495 stravis(&user, clt->clt_remote_user, HTTPD_LOGVIS) == -1) 1496 goto done; 1497 if (desc->http_version && 1498 stravis(&version, desc->http_version, HTTPD_LOGVIS) == -1) 1499 goto done; 1500 1501 /* The following should be URL-encoded */ 1502 if (desc->http_path && 1503 (path = url_encode(desc->http_path)) == NULL) 1504 goto done; 1505 if (desc->http_query && 1506 (query = url_encode(desc->http_query)) == NULL) 1507 goto done; 1508 1509 ret = evbuffer_add_printf(clt->clt_log, 1510 "%s %s - %s [%s] \"%s %s%s%s%s%s\" %03d %zu\n", 1511 srv_conf->name, ip, clt->clt_remote_user == NULL ? "-" : 1512 user, tstamp, 1513 server_httpmethod_byid(desc->http_method), 1514 desc->http_path == NULL ? "" : path, 1515 desc->http_query == NULL ? "" : "?", 1516 desc->http_query == NULL ? "" : query, 1517 desc->http_version == NULL ? "" : " ", 1518 desc->http_version == NULL ? "" : version, 1519 code, len); 1520 1521 break; 1522 1523 case LOG_FORMAT_COMBINED: 1524 key.kv_key = "Referer"; /* sic */ 1525 if ((referrer = kv_find(&desc->http_headers, &key)) != NULL && 1526 referrer->kv_value == NULL) 1527 referrer = NULL; 1528 1529 key.kv_key = "User-Agent"; 1530 if ((agent = kv_find(&desc->http_headers, &key)) != NULL && 1531 agent->kv_value == NULL) 1532 agent = NULL; 1533 1534 /* Use vis to encode input values from the header */ 1535 if (clt->clt_remote_user && 1536 stravis(&user, clt->clt_remote_user, HTTPD_LOGVIS) == -1) 1537 goto done; 1538 if (desc->http_version && 1539 stravis(&version, desc->http_version, HTTPD_LOGVIS) == -1) 1540 goto done; 1541 if (agent && 1542 stravis(&agent_v, agent->kv_value, HTTPD_LOGVIS) == -1) 1543 goto done; 1544 1545 /* The following should be URL-encoded */ 1546 if (desc->http_path && 1547 (path = url_encode(desc->http_path)) == NULL) 1548 goto done; 1549 if (desc->http_query && 1550 (query = url_encode(desc->http_query)) == NULL) 1551 goto done; 1552 if (referrer && 1553 (referrer_v = url_encode(referrer->kv_value)) == NULL) 1554 goto done; 1555 1556 ret = evbuffer_add_printf(clt->clt_log, 1557 "%s %s - %s [%s] \"%s %s%s%s%s%s\"" 1558 " %03d %zu \"%s\" \"%s\"\n", 1559 srv_conf->name, ip, clt->clt_remote_user == NULL ? "-" : 1560 user, tstamp, 1561 server_httpmethod_byid(desc->http_method), 1562 desc->http_path == NULL ? "" : path, 1563 desc->http_query == NULL ? "" : "?", 1564 desc->http_query == NULL ? "" : query, 1565 desc->http_version == NULL ? "" : " ", 1566 desc->http_version == NULL ? "" : version, 1567 code, len, 1568 referrer == NULL ? "" : referrer_v, 1569 agent == NULL ? "" : agent_v); 1570 1571 break; 1572 1573 case LOG_FORMAT_CONNECTION: 1574 /* URL-encode the path */ 1575 if (desc->http_path && 1576 (path = url_encode(desc->http_path)) == NULL) 1577 goto done; 1578 1579 ret = evbuffer_add_printf(clt->clt_log, " [%s]", 1580 desc->http_path == NULL ? "" : path); 1581 1582 break; 1583 } 1584 1585 done: 1586 free(user); 1587 free(path); 1588 free(query); 1589 free(version); 1590 free(referrer_v); 1591 free(agent_v); 1592 1593 return (ret); 1594 } 1595