1 #include "first.h" 2 3 #include "request.h" 4 #include "burl.h" 5 #include "http_header.h" 6 #include "http_kv.h" 7 #include "log.h" 8 #include "sock_addr.h" 9 10 #include <limits.h> 11 #include <stdlib.h> 12 #include <string.h> 13 14 static int request_check_hostname(buffer * const host) { 15 enum { DOMAINLABEL, TOPLABEL } stage = TOPLABEL; 16 size_t i; 17 int label_len = 0; 18 size_t host_len, hostport_len; 19 char *colon; 20 int is_ip = -1; /* -1 don't know yet, 0 no, 1 yes */ 21 int level = 0; 22 23 /* 24 * hostport = host [ ":" port ] 25 * host = hostname | IPv4address | IPv6address 26 * hostname = *( domainlabel "." ) toplabel [ "." ] 27 * domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum 28 * toplabel = alpha | alpha *( alphanum | "-" ) alphanum 29 * IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit 30 * IPv6address = "[" ... "]" 31 * port = *digit 32 */ 33 34 /* IPv6 adress */ 35 if (host->ptr[0] == '[') { 36 char *c = host->ptr + 1; 37 int colon_cnt = 0; 38 39 /* check the address inside [...] */ 40 for (; *c && *c != ']'; c++) { 41 if (*c == ':') { 42 if (++colon_cnt > 7) { 43 return -1; 44 } 45 } else if (!light_isxdigit(*c) && '.' != *c) { 46 return -1; 47 } 48 } 49 50 /* missing ] */ 51 if (!*c) { 52 return -1; 53 } 54 55 /* check port */ 56 if (*(c+1) == ':') { 57 for (c += 2; *c; c++) { 58 if (!light_isdigit(*c)) { 59 return -1; 60 } 61 } 62 } 63 else if ('\0' != *(c+1)) { 64 /* only a port is allowed to follow [...] */ 65 return -1; 66 } 67 return 0; 68 } 69 70 hostport_len = host_len = buffer_string_length(host); 71 72 if (NULL != (colon = memchr(host->ptr, ':', host_len))) { 73 char *c = colon + 1; 74 75 /* check portnumber */ 76 for (; *c; c++) { 77 if (!light_isdigit(*c)) return -1; 78 } 79 80 /* remove the port from the host-len */ 81 host_len = colon - host->ptr; 82 } 83 84 /* Host is empty */ 85 if (host_len == 0) return -1; 86 87 /* if the hostname ends in a "." strip it */ 88 if (host->ptr[host_len-1] == '.') { 89 /* shift port info one left */ 90 if (NULL != colon) memmove(colon-1, colon, hostport_len - host_len); 91 buffer_string_set_length(host, --hostport_len); 92 if (--host_len == 0) return -1; 93 } 94 95 96 /* scan from the right and skip the \0 */ 97 for (i = host_len; i-- > 0; ) { 98 const char c = host->ptr[i]; 99 100 switch (stage) { 101 case TOPLABEL: 102 if (c == '.') { 103 /* only switch stage, if this is not the last character */ 104 if (i != host_len - 1) { 105 if (label_len == 0) { 106 return -1; 107 } 108 109 /* check the first character at right of the dot */ 110 if (is_ip == 0) { 111 if (!light_isalnum(host->ptr[i+1])) { 112 return -1; 113 } 114 } else if (!light_isdigit(host->ptr[i+1])) { 115 is_ip = 0; 116 } else if ('-' == host->ptr[i+1]) { 117 return -1; 118 } else { 119 /* just digits */ 120 is_ip = 1; 121 } 122 123 stage = DOMAINLABEL; 124 125 label_len = 0; 126 level++; 127 } else if (i == 0) { 128 /* just a dot and nothing else is evil */ 129 return -1; 130 } 131 } else if (i == 0) { 132 /* the first character of the hostname */ 133 if (!light_isalnum(c)) { 134 return -1; 135 } 136 label_len++; 137 } else { 138 if (c != '-' && !light_isalnum(c)) { 139 return -1; 140 } 141 if (is_ip == -1) { 142 if (!light_isdigit(c)) is_ip = 0; 143 } 144 label_len++; 145 } 146 147 break; 148 case DOMAINLABEL: 149 if (is_ip == 1) { 150 if (c == '.') { 151 if (label_len == 0) { 152 return -1; 153 } 154 155 label_len = 0; 156 level++; 157 } else if (!light_isdigit(c)) { 158 return -1; 159 } else { 160 label_len++; 161 } 162 } else { 163 if (c == '.') { 164 if (label_len == 0) { 165 return -1; 166 } 167 168 /* c is either - or alphanum here */ 169 if ('-' == host->ptr[i+1]) { 170 return -1; 171 } 172 173 label_len = 0; 174 level++; 175 } else if (i == 0) { 176 if (!light_isalnum(c)) { 177 return -1; 178 } 179 label_len++; 180 } else { 181 if (c != '-' && !light_isalnum(c)) { 182 return -1; 183 } 184 label_len++; 185 } 186 } 187 188 break; 189 } 190 } 191 192 /* a IP has to consist of 4 parts */ 193 if (is_ip == 1 && level != 3) { 194 return -1; 195 } 196 197 if (label_len == 0) { 198 return -1; 199 } 200 201 return 0; 202 } 203 204 int http_request_host_normalize(buffer * const b, const int scheme_port) { 205 /* 206 * check for and canonicalize numeric IP address and portnum (optional) 207 * (IP address may be followed by ":portnum" (optional)) 208 * - IPv6: "[...]" 209 * - IPv4: "x.x.x.x" 210 * - IPv4: 12345678 (32-bit decimal number) 211 * - IPv4: 012345678 (32-bit octal number) 212 * - IPv4: 0x12345678 (32-bit hex number) 213 * 214 * allow any chars (except ':' and '\0' and stray '[' or ']') 215 * (other code may check chars more strictly or more pedantically) 216 * ':' delimits (optional) port at end of string 217 * "[]" wraps IPv6 address literal 218 * '\0' should have been rejected earlier were it present 219 * 220 * any chars includes, but is not limited to: 221 * - allow '-' any where, even at beginning of word 222 * (security caution: might be confused for cmd flag if passed to shell) 223 * - allow all-digit TLDs 224 * (might be mistaken for IPv4 addr by inet_aton() 225 * unless non-digits appear in subdomain) 226 */ 227 228 /* Note: not using getaddrinfo() since it does not support "[]" around IPv6 229 * and is not as lenient as inet_aton() and inet_addr() for IPv4 strings. 230 * Not using inet_pton() (when available) on IPv4 for similar reasons. */ 231 232 const char * const p = b->ptr; 233 const size_t blen = buffer_string_length(b); 234 long port = 0; 235 236 if (*p != '[') { 237 char * const colon = (char *)memchr(p, ':', blen); 238 if (colon) { 239 if (*p == ':') return -1; /*(empty host then port, or naked IPv6)*/ 240 if (colon[1] != '\0') { 241 char *e; 242 port = strtol(colon+1, &e, 0); /*(allow decimal, octal, hex)*/ 243 if (0 < port && port <= USHRT_MAX && *e == '\0') { 244 /* valid port */ 245 } else { 246 return -1; 247 } 248 } /*(else ignore stray colon at string end)*/ 249 buffer_string_set_length(b, (size_t)(colon - p)); /*(remove port str)*/ 250 } 251 252 if (light_isdigit(*p)) do { 253 /* (IPv4 address literal or domain starting w/ digit (e.g. 3com))*/ 254 /* (check one-element cache of normalized IPv4 address string) */ 255 static struct { char s[INET_ADDRSTRLEN]; size_t n; } laddr; 256 size_t n = colon ? (size_t)(colon - p) : blen; 257 sock_addr addr; 258 if (n == laddr.n && 0 == memcmp(p, laddr.s, n)) break; 259 if (1 == sock_addr_inet_pton(&addr, p, AF_INET, 0)) { 260 sock_addr_inet_ntop_copy_buffer(b, &addr); 261 n = buffer_string_length(b); 262 if (n < sizeof(laddr.s)) memcpy(laddr.s, b->ptr, (laddr.n = n)); 263 } 264 } while (0); 265 } else do { /* IPv6 addr */ 266 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON) 267 268 /* (check one-element cache of normalized IPv4 address string) */ 269 static struct { char s[INET6_ADDRSTRLEN]; size_t n; } laddr; 270 sock_addr addr; 271 char *bracket = b->ptr+blen-1; 272 char *percent = strchr(b->ptr+1, '%'); 273 size_t len; 274 int rc; 275 char buf[INET6_ADDRSTRLEN+16]; /*(+16 for potential %interface name)*/ 276 if (blen <= 2) return -1; /*(invalid "[]")*/ 277 if (*bracket != ']') { 278 bracket = (char *)memchr(b->ptr+1, ']', blen-1); 279 if (NULL == bracket || bracket[1] != ':' || bracket - b->ptr == 1){ 280 return -1; 281 } 282 if (bracket[2] != '\0') { /*(ignore stray colon at string end)*/ 283 char *e; 284 port = strtol(bracket+2, &e, 0); /*(allow decimal, octal, hex)*/ 285 if (0 < port && port <= USHRT_MAX && *e == '\0') { 286 /* valid port */ 287 } else { 288 return -1; 289 } 290 } 291 } 292 293 len = (size_t)((percent ? percent : bracket) - (b->ptr+1)); 294 if (laddr.n == len && 0 == memcmp(laddr.s, b->ptr+1, len)) { 295 /* truncate after ']' and re-add normalized port, if needed */ 296 buffer_string_set_length(b, (size_t)(bracket - b->ptr + 1)); 297 break; 298 } 299 300 *bracket = '\0';/*(terminate IPv6 string)*/ 301 if (percent) *percent = '\0'; /*(remove %interface from address)*/ 302 rc = sock_addr_inet_pton(&addr, b->ptr+1, AF_INET6, 0); 303 if (percent) *percent = '%'; /*(restore %interface)*/ 304 *bracket = ']'; /*(restore bracket)*/ 305 if (1 != rc) return -1; 306 307 sock_addr_inet_ntop(&addr, buf, sizeof(buf)); 308 len = strlen(buf); 309 if (percent) { 310 if (percent > bracket) return -1; 311 if (len + (size_t)(bracket - percent) >= sizeof(buf)) return -1; 312 if (len < sizeof(laddr.s)) memcpy(laddr.s, buf, (laddr.n = len)); 313 memcpy(buf+len, percent, (size_t)(bracket - percent)); 314 len += (size_t)(bracket - percent); 315 } 316 buffer_string_set_length(b, 1); /* truncate after '[' */ 317 buffer_append_string_len(b, buf, len); 318 buffer_append_string_len(b, CONST_STR_LEN("]")); 319 320 #else 321 322 return -1; 323 324 #endif 325 } while (0); 326 327 if (0 != port && port != scheme_port) { 328 buffer_append_string_len(b, CONST_STR_LEN(":")); 329 buffer_append_int(b, (int)port); 330 } 331 332 return 0; 333 } 334 335 int http_request_host_policy (buffer * const b, const unsigned int http_parseopts, const int scheme_port) { 336 return (((http_parseopts & HTTP_PARSEOPT_HOST_STRICT) 337 && 0 != request_check_hostname(b)) 338 || ((http_parseopts & HTTP_PARSEOPT_HOST_NORMALIZE) 339 && 0 != http_request_host_normalize(b, scheme_port))); 340 } 341 342 __attribute_pure__ /*(could be even more strict and use __attribute_const__)*/ 343 static int request_uri_is_valid_char(const unsigned char c) { 344 return (c > 32 && c != 127 && c != 255); 345 } 346 347 __attribute_cold__ 348 __attribute_noinline__ 349 static int http_request_header_line_invalid(request_st * const restrict r, const int status, const char * const restrict msg) { 350 if (r->conf.log_request_header_on_error) { 351 if (msg) log_error(r->conf.errh, __FILE__, __LINE__, "%s", msg); 352 } 353 return status; 354 } 355 356 __attribute_cold__ 357 __attribute_noinline__ 358 static int http_request_header_char_invalid(request_st * const restrict r, const char ch, const char * const restrict msg) { 359 if (r->conf.log_request_header_on_error) { 360 if ((unsigned char)ch > 32 && ch != 127) { 361 log_error(r->conf.errh, __FILE__, __LINE__, "%s ('%c')", msg, ch); 362 } 363 else { 364 log_error(r->conf.errh, __FILE__, __LINE__, "%s (0x%x)", msg, ch); 365 } 366 } 367 return 400; 368 } 369 370 /* add header to list of headers 371 * certain headers are also parsed 372 * might drop a header if deemed unnecessary/broken 373 * 374 * returns 0 on success, HTTP status on error 375 */ 376 static int http_request_parse_single_header(request_st * const restrict r, const enum http_header_e id, const char * const restrict k, const size_t klen, const char * const restrict v, const size_t vlen) { 377 buffer **saveb = NULL; 378 379 /* 380 * Note: k might not be '\0'-terminated 381 * Note: v is not '\0'-terminated, and ends with whitespace 382 * (one of '\r' '\n' ' ' '\t') 383 */ 384 385 switch (id) { 386 /*case HTTP_HEADER_OTHER:*/ 387 default: 388 break; 389 case HTTP_HEADER_HOST: 390 if (!(r->rqst_htags & HTTP_HEADER_HOST)) { 391 saveb = &r->http_host; 392 if (vlen >= 1024) { /*(expecting < 256)*/ 393 return http_request_header_line_invalid(r, 400, "uri-authority too long -> 400"); 394 } 395 } 396 else if (NULL != r->http_host 397 && buffer_is_equal_string(r->http_host, v, vlen)) { 398 /* ignore all Host: headers if match authority in request line */ 399 return 0; /* ignore header */ 400 } 401 else { 402 return http_request_header_line_invalid(r, 400, "duplicate Host header -> 400"); 403 } 404 break; 405 case HTTP_HEADER_CONNECTION: 406 /* "Connection: close" is common case if header is present */ 407 if ((vlen == 5 && buffer_eq_icase_ssn(v, CONST_STR_LEN("close"))) 408 || http_header_str_contains_token(v,vlen,CONST_STR_LEN("close"))) { 409 r->keep_alive = 0; 410 break; 411 } 412 if (http_header_str_contains_token(v,vlen,CONST_STR_LEN("keep-alive"))){ 413 r->keep_alive = 1; 414 break; 415 } 416 break; 417 case HTTP_HEADER_CONTENT_TYPE: 418 if (r->rqst_htags & HTTP_HEADER_CONTENT_TYPE) { 419 return http_request_header_line_invalid(r, 400, "duplicate Content-Type header -> 400"); 420 } 421 break; 422 case HTTP_HEADER_IF_NONE_MATCH: 423 /* if dup, only the first one will survive */ 424 if (r->rqst_htags & HTTP_HEADER_IF_NONE_MATCH) { 425 return 0; /* ignore header */ 426 } 427 break; 428 case HTTP_HEADER_CONTENT_LENGTH: 429 if (!(r->rqst_htags & HTTP_HEADER_CONTENT_LENGTH)) { 430 /*(trailing whitespace was removed from vlen)*/ 431 char *err; 432 off_t clen = strtoll(v, &err, 10); 433 if (clen >= 0 && err == v+vlen) { 434 /* (set only if not set to -1 by Transfer-Encoding: chunked) */ 435 if (0 == r->reqbody_length) r->reqbody_length = clen; 436 } 437 else { 438 return http_request_header_line_invalid(r, 400, "invalid Content-Length header -> 400"); 439 } 440 } 441 else { 442 return http_request_header_line_invalid(r, 400, "duplicate Content-Length header -> 400"); 443 } 444 break; 445 case HTTP_HEADER_IF_MODIFIED_SINCE: 446 if (r->rqst_htags & HTTP_HEADER_IF_MODIFIED_SINCE) { 447 /* Proxies sometimes send dup headers 448 * if they are the same we ignore the second 449 * if not, we raise an error */ 450 const buffer *vb = 451 http_header_request_get(r, HTTP_HEADER_IF_MODIFIED_SINCE, 452 CONST_STR_LEN("If-Modified-Since")); 453 if (vb && buffer_is_equal_caseless_string(vb, v, vlen)) { 454 /* ignore it if they are the same */ 455 return 0; /* ignore header */ 456 } 457 else { 458 return http_request_header_line_invalid(r, 400, "duplicate If-Modified-Since header -> 400"); 459 } 460 } 461 break; 462 case HTTP_HEADER_TRANSFER_ENCODING: 463 if (HTTP_VERSION_1_0 == r->http_version) { 464 return http_request_header_line_invalid(r, 400, "HTTP/1.0 with Transfer-Encoding (bad HTTP/1.0 proxy?) -> 400"); 465 } 466 467 if (!buffer_eq_icase_ss(v, vlen, CONST_STR_LEN("chunked"))) { 468 /* Transfer-Encoding might contain additional encodings, 469 * which are not currently supported by lighttpd */ 470 return http_request_header_line_invalid(r, 501, NULL); /* Not Implemented */ 471 } 472 r->reqbody_length = -1; 473 474 /* Transfer-Encoding is a hop-by-hop header, 475 * which must not be blindly forwarded to backends */ 476 return 0; /* skip header */ 477 } 478 479 http_header_request_append(r, id, k, klen, v, vlen); 480 481 if (saveb) { 482 *saveb = http_header_request_get(r, id, k, klen); 483 } 484 485 return 0; 486 } 487 488 __attribute_cold__ 489 static int http_request_parse_proto_loose(request_st * const restrict r, const char * const restrict ptr, const size_t len, const unsigned int http_parseopts) { 490 const char * proto = memchr(ptr, ' ', len); 491 if (NULL == proto) 492 return http_request_header_line_invalid(r, 400, "incomplete request line -> 400"); 493 proto = memchr(proto+1, ' ', len - (proto+1 - ptr)); 494 if (NULL == proto) 495 return http_request_header_line_invalid(r, 400, "incomplete request line -> 400"); 496 ++proto; 497 498 if (proto[0]=='H' && proto[1]=='T' && proto[2]=='T' && proto[3]=='P' && proto[4] == '/') { 499 if (proto[5] == '1' && proto[6] == '.' && (proto[7] == '1' || proto[7] == '0')) { 500 /* length already checked before calling this routine */ 501 /* (len != (size_t)(proto - ptr + 8)) */ 502 if (http_parseopts & HTTP_PARSEOPT_HEADER_STRICT) /*(http_header_strict)*/ 503 return http_request_header_line_invalid(r, 400, "incomplete request line -> 400"); 504 r->http_version = (proto[7] == '1') ? HTTP_VERSION_1_1 : HTTP_VERSION_1_0; 505 } 506 else 507 return http_request_header_line_invalid(r, 505, "unknown HTTP version -> 505"); 508 } 509 else 510 return http_request_header_line_invalid(r, 400, "unknown protocol -> 400"); 511 512 /* keep-alive default: HTTP/1.1 -> true; HTTP/1.0 -> false */ 513 r->keep_alive = (HTTP_VERSION_1_0 != r->http_version); 514 515 return 0; 516 } 517 518 __attribute_cold__ 519 static const char * http_request_parse_reqline_uri(request_st * const restrict r, const char * const restrict uri, const size_t len, const unsigned int http_parseopts) { 520 const char *nuri; 521 if ((len > 7 && buffer_eq_icase_ssn(uri, "http://", 7) 522 && NULL != (nuri = memchr(uri + 7, '/', len-7))) 523 || 524 (len > 8 && buffer_eq_icase_ssn(uri, "https://", 8) 525 && NULL != (nuri = memchr(uri + 8, '/', len-8)))) { 526 const char * const host = uri + (uri[4] == ':' ? 7 : 8); 527 const size_t hostlen = nuri - host; 528 if (0 == hostlen || hostlen >= 1024) { /*(expecting < 256)*/ 529 http_request_header_line_invalid(r, 400, "uri-authority empty or too long -> 400"); 530 return NULL; 531 } 532 /* Insert as host header */ 533 http_header_request_set(r, HTTP_HEADER_HOST, CONST_STR_LEN("Host"), host, hostlen); 534 r->http_host = http_header_request_get(r, HTTP_HEADER_HOST, CONST_STR_LEN("Host")); 535 return nuri; 536 } else if (!(http_parseopts & HTTP_PARSEOPT_HEADER_STRICT) /*(!http_header_strict)*/ 537 || (HTTP_METHOD_CONNECT == r->http_method && (uri[0] == ':' || light_isdigit(uri[0]))) 538 || (HTTP_METHOD_OPTIONS == r->http_method && uri[0] == '*' && 1 == len)) { 539 /* (permitted) */ 540 return uri; 541 } else { 542 http_request_header_line_invalid(r, 400, "request-URI parse error -> 400"); 543 return NULL; 544 } 545 } 546 547 static int http_request_parse_reqline(request_st * const restrict r, const char * const restrict ptr, const unsigned short * const restrict hoff, const unsigned int http_parseopts) { 548 size_t len = hoff[2]; 549 550 /* parse the first line of the request 551 * <method> <uri> <protocol>\r\n 552 * */ 553 if (len < 13) /* minimum len with (!http_header_strict): "x x HTTP/1.0\n" */ 554 return http_request_header_line_invalid(r, 400, "invalid request line (too short) -> 400"); 555 if (ptr[len-2] == '\r') 556 len-=2; 557 else if (!(http_parseopts & HTTP_PARSEOPT_HEADER_STRICT)) /*(!http_header_strict)*/ 558 len-=1; 559 else 560 return http_request_header_line_invalid(r, 400, "missing CR before LF in header -> 400"); 561 562 /* 563 * RFC7230: 564 * HTTP-version = HTTP-name "/" DIGIT "." DIGIT 565 * HTTP-name = %x48.54.54.50 ; "HTTP", case-sensitive 566 */ 567 568 /* protocol is expected to be " HTTP/1.1" or " HTTP/1.0" at end of line */ 569 union proto_un { 570 char c[8]; 571 uint64_t u; 572 }; 573 static const union proto_un http_1_1 = {{'H','T','T','P','/','1','.','1'}}; 574 static const union proto_un http_1_0 = {{'H','T','T','P','/','1','.','0'}}; 575 const char *p = ptr + len - 8; 576 union proto_un proto8; 577 proto8.c[0]=p[0]; proto8.c[1]=p[1]; proto8.c[2]=p[2]; proto8.c[3]=p[3]; 578 proto8.c[4]=p[4]; proto8.c[5]=p[5]; proto8.c[6]=p[6]; proto8.c[7]=p[7]; 579 if (p[-1] == ' ' && http_1_1.u == proto8.u) { 580 r->http_version = HTTP_VERSION_1_1; 581 r->keep_alive = 1; /* keep-alive default: HTTP/1.1 -> true */ 582 } 583 else if (p[-1] == ' ' && http_1_0.u == proto8.u) { 584 r->http_version = HTTP_VERSION_1_0; 585 r->keep_alive = 0; /* keep-alive default: HTTP/1.0 -> false */ 586 } 587 else { 588 int status = http_request_parse_proto_loose(r,ptr,len,http_parseopts); 589 if (0 != status) return status; 590 /*(space char must exist if http_request_parse_proto_loose() succeeds)*/ 591 for (p = ptr + len - 9; p[-1] != ' '; --p) ; 592 } 593 594 /* method is expected to be a short string in the general case */ 595 size_t i = 0; 596 while (ptr[i] != ' ') ++i; 597 #if 0 /*(space must exist if protocol was parsed successfully)*/ 598 while (i < len && ptr[i] != ' ') ++i; 599 if (ptr[i] != ' ') 600 return http_request_header_line_invalid(r, 400, "incomplete request line -> 400"); 601 #endif 602 603 r->http_method = get_http_method_key(ptr, i); 604 if (HTTP_METHOD_UNSET == r->http_method) 605 return http_request_header_line_invalid(r, 501, "unknown http-method -> 501"); 606 607 const char *uri = ptr + i + 1; 608 609 if (uri == p) 610 return http_request_header_line_invalid(r, 400, "no uri specified -> 400"); 611 len = (size_t)(p - uri - 1); 612 613 if (*uri != '/') { /* (common case: (*uri == '/')) */ 614 uri = http_request_parse_reqline_uri(r, uri, len, http_parseopts); 615 if (NULL == uri) return 400; 616 len = (size_t)(p - uri - 1); 617 } 618 619 if (0 == len) 620 return http_request_header_line_invalid(r, 400, "no uri specified -> 400"); 621 622 /* check uri for invalid characters */ 623 if (http_parseopts & HTTP_PARSEOPT_HEADER_STRICT) { /* http_header_strict */ 624 if ((http_parseopts & HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT)) { 625 /* URI will be checked in http_response_prepare() */ 626 } 627 else { 628 for (i = 0; i < len; ++i) { 629 if (!request_uri_is_valid_char(uri[i])) 630 return http_request_header_char_invalid(r, uri[i], "invalid character in URI -> 400"); 631 } 632 } 633 } 634 else { 635 /* check entire set of request headers for '\0' */ 636 if (NULL != memchr(ptr, '\0', hoff[hoff[0]])) 637 return http_request_header_char_invalid(r, '\0', "invalid character in header -> 400"); 638 } 639 640 buffer_copy_string_len(&r->target, uri, len); 641 buffer_copy_string_len(&r->target_orig, uri, len); 642 return 0; 643 } 644 645 int http_request_parse_target(request_st * const r, int scheme_port) { 646 /* URI is parsed into components at start of request and may 647 * also be re-parsed upon HANDLER_COMEBACK during the request 648 * r->target is expected to be a "/url-part?query-part" 649 * (and *not* a fully-qualified URI starting https://...) 650 * r->uri.authority is expected to be parsed elsewhere into r->http_host 651 */ 652 653 /** 654 * prepare strings 655 * 656 * - uri.path 657 * - uri.query 658 * 659 */ 660 661 /** 662 * Name according to RFC 2396 663 * 664 * - scheme 665 * - authority 666 * - path 667 * - query 668 * 669 * (scheme)://(authority)(path)?(query)#fragment 670 * 671 */ 672 673 /* take initial scheme value from connection-level state 674 * (request r->uri.scheme can be overwritten for later, 675 * for example by mod_extforward or mod_magnet) */ 676 if (scheme_port == 443) 677 buffer_copy_string_len(&r->uri.scheme, CONST_STR_LEN("https")); 678 else 679 buffer_copy_string_len(&r->uri.scheme, CONST_STR_LEN("http")); 680 681 buffer * const target = &r->target; 682 if (r->http_method == HTTP_METHOD_CONNECT 683 || (r->http_method == HTTP_METHOD_OPTIONS 684 && target->ptr[0] == '*' 685 && target->ptr[1] == '\0')) { 686 /* CONNECT ... (or) OPTIONS * ... */ 687 buffer_copy_buffer(&r->uri.path, target); 688 buffer_clear(&r->uri.query); 689 return 0; 690 } 691 692 char *qstr; 693 if (r->conf.http_parseopts & HTTP_PARSEOPT_URL_NORMALIZE) { 694 /*uint32_t len = (uint32_t)buffer_string_length(target);*/ 695 int qs = burl_normalize(target, r->tmp_buf, r->conf.http_parseopts); 696 if (-2 == qs) { 697 log_error(r->conf.errh, __FILE__, __LINE__, 698 "invalid character in URI -> 400 %s", 699 target->ptr); 700 return 400; /* Bad Request */ 701 } 702 qstr = (-1 == qs) ? NULL : target->ptr+qs; 703 #if 0 /* future: might enable here, or below for all requests */ 704 /* (Note: total header size not recalculated on HANDLER_COMEBACK 705 * even if other request headers changed during processing) 706 * (If (0 != r->loops_per_request), then the generated 707 * request is too large. Should a different error be returned?) */ 708 r->rqst_header_len -= len; 709 len = buffer_string_length(target); 710 r->rqst_header_len += len; 711 if (len > MAX_HTTP_REQUEST_URI) { 712 return 414; /* 414 Request-URI Too Long */ 713 } 714 if (r->rqst_header_len > MAX_HTTP_REQUEST_HEADER) { 715 log_error(r->conf.errh, __FILE__, __LINE__, 716 "request header fields too large: %u -> 431", 717 r->rqst_header_len); 718 return 431; /* Request Header Fields Too Large */ 719 } 720 #endif 721 } 722 else { 723 size_t rlen = buffer_string_length(target); 724 qstr = memchr(target->ptr, '#', rlen);/* discard fragment */ 725 if (qstr) { 726 rlen = (size_t)(qstr - target->ptr); 727 buffer_string_set_length(target, rlen); 728 } 729 qstr = memchr(target->ptr, '?', rlen); 730 } 731 732 /** extract query string from target */ 733 const char * const pstr = target->ptr; 734 const uint32_t rlen = buffer_string_length(target); 735 uint32_t plen; 736 if (NULL != qstr) { 737 plen = (uint32_t)(qstr - pstr); 738 buffer_copy_string_len(&r->uri.query, qstr + 1, rlen - plen - 1); 739 } 740 else { 741 plen = rlen; 742 buffer_clear(&r->uri.query); 743 } 744 buffer_copy_string_len(&r->uri.path, pstr, plen); 745 746 /* decode url to path 747 * 748 * - decode url-encodings (e.g. %20 -> ' ') 749 * - remove path-modifiers (e.g. /../) 750 */ 751 752 buffer_urldecode_path(&r->uri.path); 753 buffer_path_simplify(&r->uri.path, &r->uri.path); 754 if (r->uri.path.ptr[0] != '/') { 755 log_error(r->conf.errh, __FILE__, __LINE__, 756 "uri-path does not begin with '/': %s -> 400", r->uri.path.ptr); 757 return 400; /* Bad Request */ 758 } 759 760 return 0; 761 } 762 763 __attribute_cold__ 764 __attribute_noinline__ 765 static int http_request_parse_header_other(request_st * const restrict r, const char * const restrict k, const int klen, const unsigned int http_header_strict) { 766 for (int i = 0; i < klen; ++i) { 767 if (light_isalpha(k[i]) || k[i] == '-') continue; /*(common cases)*/ 768 /** 769 * 1*<any CHAR except CTLs or separators> 770 * CTLs == 0-31 + 127, CHAR = 7-bit ascii (0..127) 771 * 772 */ 773 switch(k[i]) { 774 case ' ': 775 case '\t': 776 return http_request_header_line_invalid(r, 400, "WS character in key -> 400"); 777 case '(': 778 case ')': 779 case '<': 780 case '>': 781 case '@': 782 case ',': 783 case ';': 784 case '\\': 785 case '\"': 786 case '/': 787 case '[': 788 case ']': 789 case '?': 790 case '=': 791 case '{': 792 case '}': 793 return http_request_header_char_invalid(r, k[i], "invalid character in header key -> 400"); 794 default: 795 if (http_header_strict ? (k[i] < 32 || ((unsigned char *)k)[i] >= 127) : k[i] == '\0') 796 return http_request_header_char_invalid(r, k[i], "invalid character in header key -> 400"); 797 break; /* ok */ 798 } 799 } 800 return 0; 801 } 802 803 static int http_request_parse_headers(request_st * const restrict r, char * const restrict ptr, const unsigned short * const restrict hoff, const unsigned int http_parseopts) { 804 const unsigned int http_header_strict = (http_parseopts & HTTP_PARSEOPT_HEADER_STRICT); 805 806 #if 0 /*(not checked here; will later result in invalid label for HTTP header)*/ 807 int i = hoff[2]; 808 809 if (ptr[i] == ' ' || ptr[i] == '\t') { 810 return http_request_header_line_invalid(r, 400, "WS at the start of first line -> 400"); 811 } 812 #endif 813 814 for (int i = 2; i < hoff[0]; ++i) { 815 const char *k = ptr + hoff[i]; 816 /* one past last line hoff[hoff[0]] is to final "\r\n" */ 817 char *end = ptr + hoff[i+1]; 818 819 const char *colon = memchr(k, ':', end - k); 820 if (NULL == colon) 821 return http_request_header_line_invalid(r, 400, "invalid header missing ':' -> 400"); 822 823 const char *v = colon + 1; 824 825 /* RFC7230 Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing 826 * 3.2.4. Field Parsing 827 * [...] 828 * No whitespace is allowed between the header field-name and colon. In 829 * the past, differences in the handling of such whitespace have led to 830 * security vulnerabilities in request routing and response handling. A 831 * server MUST reject any received request message that contains 832 * whitespace between a header field-name and colon with a response code 833 * of 400 (Bad Request). A proxy MUST remove any such whitespace from a 834 * response message before forwarding the message downstream. 835 */ 836 /* (line k[-1] is always preceded by a '\n', 837 * including first header after request-line, 838 * so no need to check colon != k) */ 839 if (colon[-1] == ' ' || colon[-1] == '\t') { 840 if (http_header_strict) { 841 return http_request_header_line_invalid(r, 400, "invalid whitespace between field-name and colon -> 400"); 842 } 843 else { 844 /* remove trailing whitespace from key(if !http_header_strict)*/ 845 do { --colon; } while (colon[-1] == ' ' || colon[-1] == '\t'); 846 } 847 } 848 849 const int klen = (int)(colon - k); 850 if (0 == klen) 851 return http_request_header_line_invalid(r, 400, "invalid header key -> 400"); 852 const enum http_header_e id = http_header_hkey_get(k, klen); 853 854 if (id == HTTP_HEADER_OTHER) { 855 for (int j = 0; j < klen; ++j) { 856 if (light_isalpha(k[j]) || k[j] == '-') continue; /*(common cases)*/ 857 if (0 != http_request_parse_header_other(r, k+j, klen-j, http_header_strict)) 858 return 400; 859 break; 860 } 861 } 862 863 /* remove leading whitespace from value */ 864 while (*v == ' ' || *v == '\t') ++v; 865 866 for (; i+1 <= hoff[0]; ++i) { 867 end = ptr + hoff[i+1]; 868 if (end[0] != ' ' && end[0] != '\t') break; 869 870 /* line folding */ 871 #ifdef __COVERITY__ 872 force_assert(end - k >= 2); 873 #endif 874 if (end[-2] == '\r') 875 end[-2] = ' '; 876 else if (http_header_strict) 877 return http_request_header_line_invalid(r, 400, "missing CR before LF in header -> 400"); 878 end[-1] = ' '; 879 } 880 #ifdef __COVERITY__ 881 /*(buf holding k has non-zero request-line, so end[-2] valid)*/ 882 force_assert(end >= k + 2); 883 #endif 884 if (end[-2] == '\r') 885 --end; 886 else if (http_header_strict) 887 return http_request_header_line_invalid(r, 400, "missing CR before LF in header -> 400"); 888 /* remove trailing whitespace from value (+ remove '\r\n') */ 889 /* (line k[-1] is always preceded by a '\n', 890 * including first header after request-line, 891 * so no need to check (end != k)) */ 892 do { --end; } while (end[-1] == ' ' || end[-1] == '\t'); 893 894 const int vlen = (int)(end - v); 895 /* empty header-fields are not allowed by HTTP-RFC, we just ignore them */ 896 if (vlen <= 0) continue; /* ignore header */ 897 898 if (http_header_strict) { 899 for (int j = 0; j < vlen; ++j) { 900 if ((((unsigned char *)v)[j] < 32 && v[j] != '\t') || v[j]==127) 901 return http_request_header_char_invalid(r, v[j], "invalid character in header -> 400"); 902 } 903 } /* else URI already checked in http_request_parse_reqline() for any '\0' */ 904 905 int status = http_request_parse_single_header(r, id, k, (size_t)klen, v, (size_t)vlen); 906 if (0 != status) return status; 907 } 908 909 return 0; 910 } 911 912 int http_request_parse(request_st * const restrict r, char * const restrict hdrs, const unsigned short * const restrict hoff, const int scheme_port) { 913 /* 914 * Request: "^(GET|POST|HEAD|...) ([^ ]+(\\?[^ ]+|)) (HTTP/1\\.[01])$" 915 * Header : "^([-a-zA-Z]+): (.+)$" 916 * End : "^$" 917 */ 918 919 int status; 920 const unsigned int http_parseopts = r->conf.http_parseopts; 921 922 status = http_request_parse_reqline(r, hdrs, hoff, http_parseopts); 923 if (0 != status) return status; 924 925 status = http_request_parse_target(r, scheme_port); 926 if (0 != status) return status; 927 928 status = http_request_parse_headers(r, hdrs, hoff, http_parseopts); 929 if (0 != status) return status; 930 931 /*(r->http_host might not be set until after parsing request headers)*/ 932 buffer_copy_buffer(&r->uri.authority, r->http_host);/*(copy even if empty)*/ 933 buffer_to_lower(&r->uri.authority); 934 935 /* post-processing */ 936 937 /* check hostname field if it is set */ 938 if (r->http_host) { 939 if (0 != http_request_host_policy(r->http_host, 940 http_parseopts, scheme_port)) 941 return http_request_header_line_invalid(r, 400, "Invalid Hostname -> 400"); 942 } 943 else { 944 if (r->http_version == HTTP_VERSION_1_1) 945 return http_request_header_line_invalid(r, 400, "HTTP/1.1 but Host missing -> 400"); 946 } 947 948 if (0 == r->reqbody_length) { 949 /* POST requires Content-Length (or Transfer-Encoding) 950 * (-1 == r->reqbody_length when Transfer-Encoding: chunked)*/ 951 if (HTTP_METHOD_POST == r->http_method 952 && !(r->rqst_htags & HTTP_HEADER_CONTENT_LENGTH)) { 953 return http_request_header_line_invalid(r, 411, "POST-request, but content-length missing -> 411"); 954 } 955 } 956 else { 957 /* (-1 == r->reqbody_length when Transfer-Encoding: chunked)*/ 958 if (-1 == r->reqbody_length 959 && (r->rqst_htags & HTTP_HEADER_CONTENT_LENGTH)) { 960 /* RFC7230 Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing 961 * 3.3.3. Message Body Length 962 * [...] 963 * If a message is received with both a Transfer-Encoding and a 964 * Content-Length header field, the Transfer-Encoding overrides the 965 * Content-Length. Such a message might indicate an attempt to 966 * perform request smuggling (Section 9.5) or response splitting 967 * (Section 9.4) and ought to be handled as an error. A sender MUST 968 * remove the received Content-Length field prior to forwarding such 969 * a message downstream. 970 */ 971 const unsigned int http_header_strict = 972 (http_parseopts & HTTP_PARSEOPT_HEADER_STRICT); 973 if (http_header_strict) { 974 return http_request_header_line_invalid(r, 400, "invalid Transfer-Encoding + Content-Length -> 400"); 975 } 976 else { 977 /* ignore Content-Length */ 978 http_header_request_unset(r, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length")); 979 } 980 } 981 if (http_method_get_or_head(r->http_method) 982 && !(http_parseopts & HTTP_PARSEOPT_METHOD_GET_BODY)) { 983 return http_request_header_line_invalid(r, 400, "GET/HEAD with content-length -> 400"); 984 } 985 } 986 987 return 0; 988 } 989