1 #include "first.h" 2 3 #include "base.h" 4 #include "log.h" 5 #include "buffer.h" 6 #include "http_header.h" 7 #include "request.h" 8 #include "sock_addr.h" 9 10 #include "plugin.h" 11 12 #include <limits.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <errno.h> 16 17 #include "sys-socket.h" 18 19 /** 20 * mod_extforward.c for lighttpd, by comman.kang <at> gmail <dot> com 21 * extended, modified by Lionel Elie Mamane (LEM), lionel <at> mamane <dot> lu 22 * support chained proxies by [email protected], #1528 23 * 24 * Config example: 25 * 26 * Trust proxy 10.0.0.232 and 10.0.0.232 27 * extforward.forwarder = ( "10.0.0.232" => "trust", 28 * "10.0.0.233" => "trust" ) 29 * 30 * Trust all proxies (NOT RECOMMENDED!) 31 * extforward.forwarder = ( "all" => "trust") 32 * 33 * Note that "all" has precedence over specific entries, 34 * so "all except" setups will not work. 35 * 36 * In case you have chained proxies, you can add all their IP's to the 37 * config. However "all" has effect only on connecting IP, as the 38 * X-Forwarded-For header can not be trusted. 39 * 40 * Note: The effect of this module is variable on $HTTP["remotip"] directives and 41 * other module's remote ip dependent actions. 42 * Things done by modules before we change the remoteip or after we reset it will match on the proxy's IP. 43 * Things done in between these two moments will match on the real client's IP. 44 * The moment things are done by a module depends on in which hook it does things and within the same hook 45 * on whether they are before/after us in the module loading order 46 * (order in the server.modules directive in the config file). 47 * 48 * Tested behaviours: 49 * 50 * mod_access: Will match on the real client. 51 * 52 * mod_accesslog: 53 * In order to see the "real" ip address in access log , 54 * you'll have to load mod_extforward after mod_accesslog. 55 * like this: 56 * 57 * server.modules = ( 58 * ..... 59 * mod_accesslog, 60 * mod_extforward 61 * ) 62 */ 63 64 65 typedef enum { 66 PROXY_FORWARDED_NONE = 0x00, 67 PROXY_FORWARDED_FOR = 0x01, 68 PROXY_FORWARDED_PROTO = 0x02, 69 PROXY_FORWARDED_HOST = 0x04, 70 PROXY_FORWARDED_BY = 0x08, 71 PROXY_FORWARDED_REMOTE_USER = 0x10 72 } proxy_forwarded_t; 73 74 struct sock_addr_mask { 75 sock_addr addr; 76 int bits; 77 }; 78 79 struct forwarder_cfg { 80 const array *forwarder; 81 int forward_all; 82 uint32_t addrs_used; 83 #if defined(__STDC_VERSION__) && __STDC_VERSION__-0 >= 199901L /* C99 */ 84 struct sock_addr_mask addrs[]; 85 #else 86 struct sock_addr_mask addrs[1]; 87 #endif 88 }; 89 90 typedef struct { 91 const array *forwarder; 92 int forward_all; 93 uint32_t forward_masks_used; 94 const struct sock_addr_mask *forward_masks; 95 const array *headers; 96 unsigned int opts; 97 char hap_PROXY; 98 char hap_PROXY_ssl_client_verify; 99 } plugin_config; 100 101 typedef struct { 102 PLUGIN_DATA; 103 plugin_config defaults; 104 plugin_config conf; 105 array *default_headers; 106 } plugin_data; 107 108 static plugin_data *mod_extforward_plugin_data_singleton; 109 static int extforward_check_proxy; 110 111 112 /* context , used for restore remote ip */ 113 114 typedef struct { 115 /* per-request state */ 116 sock_addr saved_remote_addr; 117 buffer *saved_remote_addr_buf; 118 119 /* hap-PROXY protocol prior to receiving first request */ 120 int(*saved_network_read)(connection *, chunkqueue *, off_t); 121 122 /* connection-level state applied to requests in handle_request_env */ 123 array *env; 124 int ssl_client_verify; 125 } handler_ctx; 126 127 128 static handler_ctx * handler_ctx_init(void) { 129 handler_ctx * hctx; 130 hctx = calloc(1, sizeof(*hctx)); 131 force_assert(hctx); 132 return hctx; 133 } 134 135 static void handler_ctx_free(handler_ctx *hctx) { 136 free(hctx); 137 } 138 139 INIT_FUNC(mod_extforward_init) { 140 return calloc(1, sizeof(plugin_data)); 141 } 142 143 FREE_FUNC(mod_extforward_free) { 144 plugin_data * const p = p_d; 145 array_free(p->default_headers); 146 if (NULL == p->cvlist) return; 147 /* (init i to 0 if global context; to 1 to skip empty global context) */ 148 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { 149 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 150 for (; -1 != cpv->k_id; ++cpv) { 151 switch (cpv->k_id) { 152 case 0: /* extforward.forwarder */ 153 if (cpv->vtype == T_CONFIG_LOCAL) free(cpv->v.v); 154 break; 155 default: 156 break; 157 } 158 } 159 } 160 } 161 162 static void mod_extforward_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { 163 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 164 case 0: /* extforward.forwarder */ 165 if (cpv->vtype == T_CONFIG_LOCAL) { 166 const struct forwarder_cfg * const fwd = cpv->v.v; 167 pconf->forwarder = fwd->forwarder; 168 pconf->forward_all = fwd->forward_all; 169 pconf->forward_masks_used = fwd->addrs_used; 170 pconf->forward_masks = fwd->addrs; 171 } 172 break; 173 case 1: /* extforward.headers */ 174 pconf->headers = cpv->v.a; 175 break; 176 case 2: /* extforward.params */ 177 if (cpv->vtype == T_CONFIG_LOCAL) 178 pconf->opts = cpv->v.u; 179 break; 180 case 3: /* extforward.hap-PROXY */ 181 pconf->hap_PROXY = (char)cpv->v.u; 182 break; 183 case 4: /* extforward.hap-PROXY-ssl-client-verify */ 184 pconf->hap_PROXY_ssl_client_verify = (char)cpv->v.u; 185 break; 186 default:/* should not happen */ 187 return; 188 } 189 } 190 191 static void mod_extforward_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { 192 do { 193 mod_extforward_merge_config_cpv(pconf, cpv); 194 } while ((++cpv)->k_id != -1); 195 } 196 197 static void mod_extforward_patch_config(request_st * const r, plugin_data * const p) { 198 memcpy(&p->conf, &p->defaults, sizeof(plugin_config)); 199 for (int i = 1, used = p->nconfig; i < used; ++i) { 200 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 201 mod_extforward_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]); 202 } 203 } 204 205 static void * mod_extforward_parse_forwarder(server *srv, const array *forwarder) { 206 const data_string * const allds = (const data_string *) 207 array_get_element_klen(forwarder, CONST_STR_LEN("all")); 208 const int forward_all = (NULL == allds) 209 ? 0 210 : buffer_eq_icase_slen(&allds->value, CONST_STR_LEN("trust")) ? 1 : -1; 211 uint32_t nmasks = 0; 212 for (uint32_t j = 0; j < forwarder->used; ++j) { 213 data_string * const ds = (data_string *)forwarder->data[j]; 214 char * const nm_slash = strchr(ds->key.ptr, '/'); 215 if (NULL != nm_slash) ++nmasks; 216 if (!buffer_eq_icase_slen(&ds->value, CONST_STR_LEN("trust"))) { 217 if (!buffer_eq_icase_slen(&ds->value, CONST_STR_LEN("untrusted"))) 218 log_error(srv->errh, __FILE__, __LINE__, 219 "ERROR: expect \"trust\", not \"%s\" => \"%s\"; " 220 "treating as untrusted", ds->key.ptr, ds->value.ptr); 221 if (NULL != nm_slash) { 222 /* future: consider adding member next to bits in sock_addr_mask 223 * with bool trusted/untrusted member */ 224 --nmasks; 225 log_error(srv->errh, __FILE__, __LINE__, 226 "ERROR: untrusted CIDR masks are ignored (\"%s\" => \"%s\")", 227 ds->key.ptr, ds->value.ptr); 228 } 229 buffer_clear(&ds->value); /* empty is untrusted */ 230 continue; 231 } 232 } 233 234 struct forwarder_cfg * const fwd = 235 malloc(sizeof(struct forwarder_cfg)+sizeof(struct sock_addr_mask)*nmasks); 236 force_assert(fwd); 237 memset(fwd, 0, 238 sizeof(struct forwarder_cfg) + sizeof(struct sock_addr_mask)*nmasks); 239 fwd->forwarder = forwarder; 240 fwd->forward_all = forward_all; 241 fwd->addrs_used = 0; 242 for (uint32_t j = 0; j < forwarder->used; ++j) { 243 data_string * const ds = (data_string *)forwarder->data[j]; 244 char * const nm_slash = strchr(ds->key.ptr, '/'); 245 if (NULL == nm_slash) continue; 246 if (buffer_string_is_empty(&ds->value)) continue; /* ignored */ 247 248 char *err; 249 const int nm_bits = strtol(nm_slash + 1, &err, 10); 250 int rc; 251 if (*err || nm_bits <= 0) { 252 log_error(srv->errh, __FILE__, __LINE__, 253 "ERROR: invalid netmask: %s %s", ds->key.ptr, err); 254 free(fwd); 255 return NULL; 256 } 257 struct sock_addr_mask * const sm = fwd->addrs + fwd->addrs_used++; 258 sm->bits = nm_bits; 259 *nm_slash = '\0'; 260 rc = sock_addr_from_str_numeric(&sm->addr, ds->key.ptr, srv->errh); 261 *nm_slash = '/'; 262 if (1 != rc) { 263 free(fwd); 264 return NULL; 265 } 266 buffer_clear(&ds->value); 267 /* empty is untrusted, 268 * e.g. if subnet (incorrectly) appears in X-Forwarded-For */ 269 } 270 271 return fwd; 272 } 273 274 static unsigned int mod_extforward_parse_opts(server *srv, const array *opts_params) { 275 unsigned int opts = 0; 276 for (uint32_t j = 0, used = opts_params->used; j < used; ++j) { 277 proxy_forwarded_t param; 278 data_unset *du = opts_params->data[j]; 279 #if 0 /*("for" and "proto" historical behavior: always enabled)*/ 280 if (buffer_eq_slen(&du->key, CONST_STR_LEN("by"))) 281 param = PROXY_FORWARDED_BY; 282 else if (buffer_eq_slen(&du->key, CONST_STR_LEN("for"))) 283 param = PROXY_FORWARDED_FOR; 284 else 285 #endif 286 if (buffer_eq_slen(&du->key, CONST_STR_LEN("host"))) 287 param = PROXY_FORWARDED_HOST; 288 #if 0 289 else if (buffer_eq_slen(&du->key, CONST_STR_LEN("proto"))) 290 param = PROXY_FORWARDED_PROTO; 291 #endif 292 else if (buffer_eq_slen(&du->key, CONST_STR_LEN("remote_user"))) 293 param = PROXY_FORWARDED_REMOTE_USER; 294 else { 295 log_error(srv->errh, __FILE__, __LINE__, 296 "extforward.params keys must be one of: " 297 "host, remote_user, but not: %s", du->key.ptr); 298 return HANDLER_ERROR; 299 } 300 301 if (du->type == TYPE_STRING) { 302 data_string *ds = (data_string *)du; 303 if (buffer_eq_slen(&ds->value, CONST_STR_LEN("enable"))) { 304 opts |= param; 305 } 306 else if (!buffer_eq_slen(&ds->value, CONST_STR_LEN("disable"))) { 307 log_error(srv->errh, __FILE__, __LINE__, 308 "extforward.params values must be one of: " 309 "0, 1, enable, disable; error for key: %s", du->key.ptr); 310 return UINT_MAX; 311 } 312 } 313 else if (du->type == TYPE_INTEGER) { 314 data_integer *di = (data_integer *)du; 315 if (di->value) opts |= param; 316 } 317 else { 318 log_error(srv->errh, __FILE__, __LINE__, 319 "extforward.params values must be one of: " 320 "0, 1, enable, disable; error for key: %s", du->key.ptr); 321 return UINT_MAX; 322 } 323 } 324 return opts; 325 } 326 327 SETDEFAULTS_FUNC(mod_extforward_set_defaults) { 328 static const config_plugin_keys_t cpk[] = { 329 { CONST_STR_LEN("extforward.forwarder"), 330 T_CONFIG_ARRAY_KVSTRING, 331 T_CONFIG_SCOPE_CONNECTION } 332 ,{ CONST_STR_LEN("extforward.headers"), 333 T_CONFIG_ARRAY_VLIST, 334 T_CONFIG_SCOPE_CONNECTION } 335 ,{ CONST_STR_LEN("extforward.params"), 336 T_CONFIG_ARRAY_KVANY, 337 T_CONFIG_SCOPE_CONNECTION } 338 ,{ CONST_STR_LEN("extforward.hap-PROXY"), 339 T_CONFIG_BOOL, 340 T_CONFIG_SCOPE_CONNECTION } 341 ,{ CONST_STR_LEN("extforward.hap-PROXY-ssl-client-verify"), 342 T_CONFIG_BOOL, 343 T_CONFIG_SCOPE_CONNECTION } 344 ,{ NULL, 0, 345 T_CONFIG_UNSET, 346 T_CONFIG_SCOPE_UNSET } 347 }; 348 349 plugin_data * const p = p_d; 350 if (!config_plugin_values_init(srv, p, cpk, "mod_extforward")) 351 return HANDLER_ERROR; 352 353 int hap_PROXY = 0; 354 355 /* process and validate config directives 356 * (init i to 0 if global context; to 1 to skip empty global context) */ 357 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 358 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 359 for (; -1 != cpv->k_id; ++cpv) { 360 switch (cpv->k_id) { 361 case 0: /* extforward.forwarder */ 362 cpv->v.v = mod_extforward_parse_forwarder(srv, cpv->v.a); 363 if (NULL == cpv->v.v) { 364 log_error(srv->errh, __FILE__, __LINE__, 365 "unexpected value for %s", cpk[cpv->k_id].k); 366 return HANDLER_ERROR; 367 } 368 cpv->vtype = T_CONFIG_LOCAL; 369 break; 370 case 1: /* extforward.headers */ 371 break; 372 case 2: /* extforward.params */ 373 cpv->v.u = mod_extforward_parse_opts(srv, cpv->v.a); 374 if (UINT_MAX == cpv->v.u) 375 return HANDLER_ERROR; 376 break; 377 case 3: /* extforward.hap-PROXY */ 378 if (cpv->v.u) hap_PROXY = 1; 379 break; 380 case 4: /* extforward.hap-PROXY-ssl-client-verify */ 381 break; 382 default:/* should not happen */ 383 break; 384 } 385 } 386 } 387 388 mod_extforward_plugin_data_singleton = p; 389 p->defaults.opts = PROXY_FORWARDED_NONE; 390 391 /* initialize p->defaults from global config context */ 392 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 393 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 394 if (-1 != cpv->k_id) 395 mod_extforward_merge_config(&p->defaults, cpv); 396 } 397 398 /* default to "X-Forwarded-For" or "Forwarded-For" if extforward.headers 399 * is not specified or is empty (and not using hap_PROXY) */ 400 if (!p->defaults.hap_PROXY 401 && (NULL == p->defaults.headers || 0 == p->defaults.headers->used)) { 402 p->defaults.headers = p->default_headers = array_init(2); 403 array_insert_value(p->default_headers,CONST_STR_LEN("X-Forwarded-For")); 404 array_insert_value(p->default_headers,CONST_STR_LEN("Forwarded-For")); 405 } 406 407 /* attempt to warn if mod_extforward is not last module loaded to hook 408 * handle_connection_accept. (Nice to have, but remove this check if 409 * it reaches too far into internals and prevents other code changes.) 410 * While it would be nice to check connection_handle_accept plugin slot 411 * to make sure mod_extforward is last, that info is private to plugin.c 412 * so merely warn if mod_openssl is loaded after mod_extforward, though 413 * future modules which hook connection_handle_accept might be missed.*/ 414 if (hap_PROXY) { 415 uint32_t i; 416 for (i = 0; i < srv->srvconf.modules->used; ++i) { 417 data_string *ds = (data_string *)srv->srvconf.modules->data[i]; 418 if (buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_extforward"))) 419 break; 420 } 421 for (; i < srv->srvconf.modules->used; ++i) { 422 data_string *ds = (data_string *)srv->srvconf.modules->data[i]; 423 if (buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_openssl"))) { 424 log_error(srv->errh, __FILE__, __LINE__, 425 "mod_extforward must be loaded after mod_openssl in " 426 "server.modules when extforward.hap-PROXY = \"enable\""); 427 break; 428 } 429 } 430 } 431 432 for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) { 433 data_string *ds = (data_string *)srv->srvconf.modules->data[i]; 434 if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("mod_proxy"))) { 435 extforward_check_proxy = 1; 436 break; 437 } 438 } 439 440 return HANDLER_GO_ON; 441 } 442 443 444 /* 445 extract a forward array from the environment 446 */ 447 static array *extract_forward_array(const buffer *pbuffer) 448 { 449 array *result = array_init(8); 450 if (!buffer_string_is_empty(pbuffer)) { 451 const char *base, *curr; 452 /* state variable, 0 means not in string, 1 means in string */ 453 int in_str = 0; 454 for (base = pbuffer->ptr, curr = pbuffer->ptr; *curr; curr++) { 455 int hex_or_colon = (light_isxdigit(*curr) || *curr == ':'); 456 if (in_str) { 457 if (!hex_or_colon && *curr != '.') { 458 /* found an separator , insert value into result array */ 459 array_insert_value(result, base, curr - base); 460 /* change state to not in string */ 461 in_str = 0; 462 } 463 } else { 464 if (hex_or_colon) { 465 /* found leading char of an IP address, move base pointer and change state */ 466 base = curr; 467 in_str = 1; 468 } 469 } 470 } 471 /* if breaking out while in str, we got to the end of string, so add it */ 472 if (in_str) { 473 array_insert_value(result, base, curr - base); 474 } 475 } 476 return result; 477 } 478 479 /* 480 * check whether ip is trusted, return 1 for trusted , 0 for untrusted 481 */ 482 static int is_proxy_trusted(plugin_data *p, const char * const ip, size_t iplen) 483 { 484 const data_string *ds = 485 (const data_string *)array_get_element_klen(p->conf.forwarder, ip, iplen); 486 if (NULL != ds) return !buffer_string_is_empty(&ds->value); 487 488 if (p->conf.forward_masks_used) { 489 const struct sock_addr_mask * const addrs = p->conf.forward_masks; 490 const uint32_t aused = p->conf.forward_masks_used; 491 sock_addr addr; 492 /* C funcs inet_aton(), inet_pton() require '\0'-terminated IP str */ 493 char addrstr[64]; /*(larger than INET_ADDRSTRLEN and INET6_ADDRSTRLEN)*/ 494 if (iplen >= sizeof(addrstr)) return 0; 495 memcpy(addrstr, ip, iplen); 496 addrstr[iplen] = '\0'; 497 498 if (1 != sock_addr_inet_pton(&addr, addrstr, AF_INET, 0) 499 && 1 != sock_addr_inet_pton(&addr, addrstr, AF_INET6, 0)) return 0; 500 501 for (uint32_t i = 0; i < aused; ++i) { 502 if (sock_addr_is_addr_eq_bits(&addr, &addrs[i].addr, addrs[i].bits)) 503 return 1; 504 } 505 } 506 507 return 0; 508 } 509 510 static int is_connection_trusted(connection * const con, plugin_data *p) 511 { 512 if (p->conf.forward_all) return (1 == p->conf.forward_all); 513 return is_proxy_trusted(p, CONST_BUF_LEN(con->dst_addr_buf)); 514 } 515 516 /* 517 * Return last address of proxy that is not trusted. 518 * Do not accept "all" keyword here. 519 */ 520 static const char *last_not_in_array(array *a, plugin_data *p) 521 { 522 int i; 523 524 for (i = a->used - 1; i >= 0; i--) { 525 data_string *ds = (data_string *)a->data[i]; 526 if (!is_proxy_trusted(p, CONST_BUF_LEN(&ds->value))) { 527 return ds->value.ptr; 528 } 529 } 530 return NULL; 531 } 532 533 static int mod_extforward_set_addr(request_st * const r, plugin_data *p, const char *addr) { 534 connection * const con = r->con; 535 sock_addr sock; 536 handler_ctx *hctx = r->plugin_ctx[p->id]; 537 538 if (r->conf.log_request_handling) { 539 log_error(r->conf.errh, __FILE__, __LINE__, "using address: %s", addr); 540 } 541 542 sock.plain.sa_family = AF_UNSPEC; 543 if (1 != sock_addr_from_str_numeric(&sock, addr, r->conf.errh)) return 0; 544 if (sock.plain.sa_family == AF_UNSPEC) return 0; 545 546 /* we found the remote address, modify current connection and save the old address */ 547 if (hctx) { 548 if (hctx->saved_remote_addr_buf) { 549 if (r->conf.log_request_handling) { 550 log_error(r->conf.errh, __FILE__, __LINE__, 551 "-- mod_extforward_uri_handler already patched this connection, resetting state"); 552 } 553 con->dst_addr = hctx->saved_remote_addr; 554 buffer_free(con->dst_addr_buf); 555 con->dst_addr_buf = hctx->saved_remote_addr_buf; 556 hctx->saved_remote_addr_buf = NULL; 557 } 558 } else { 559 r->plugin_ctx[p->id] = hctx = handler_ctx_init(); 560 } 561 /* save old address */ 562 if (extforward_check_proxy) { 563 http_header_env_set(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_FOR"), CONST_BUF_LEN(con->dst_addr_buf)); 564 } 565 hctx->saved_remote_addr = con->dst_addr; 566 hctx->saved_remote_addr_buf = con->dst_addr_buf; 567 /* patch connection address */ 568 con->dst_addr = sock; 569 con->dst_addr_buf = buffer_init_string(addr); 570 571 if (r->conf.log_request_handling) { 572 log_error(r->conf.errh, __FILE__, __LINE__, 573 "patching con->dst_addr_buf for the accesslog: %s", addr); 574 } 575 576 /* Now, clean the conf_cond cache, because we may have changed the results of tests */ 577 config_cond_cache_reset_item(r, COMP_HTTP_REMOTE_IP); 578 579 return 1; 580 } 581 582 static void mod_extforward_set_proto(request_st * const r, const char * const proto, size_t protolen) { 583 if (0 != protolen && !buffer_is_equal_caseless_string(&r->uri.scheme, proto, protolen)) { 584 /* update scheme if X-Forwarded-Proto is set 585 * Limitations: 586 * - Only "http" or "https" are currently accepted since the request to lighttpd currently has to 587 * be HTTP/1.0 or HTTP/1.1 using http or https. If this is changed, then the scheme from this 588 * untrusted header must be checked to contain only alphanumeric characters, and to be a 589 * reasonable length, e.g. < 256 chars. 590 * - r->uri.scheme is not reset in mod_extforward_restore() but is currently not an issues since 591 * r->uri.scheme will be reset by next request. If a new module uses r->uri.scheme in the 592 * handle_request_done hook, then should evaluate if that module should use the forwarded value 593 * (probably) or the original value. 594 */ 595 if (extforward_check_proxy) { 596 http_header_env_set(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_PROTO"), CONST_BUF_LEN(&r->uri.scheme)); 597 } 598 if (buffer_eq_icase_ss(proto, protolen, CONST_STR_LEN("https"))) { 599 buffer_copy_string_len(&r->uri.scheme, CONST_STR_LEN("https")); 600 config_cond_cache_reset_item(r, COMP_HTTP_SCHEME); 601 } else if (buffer_eq_icase_ss(proto, protolen, CONST_STR_LEN("http"))) { 602 buffer_copy_string_len(&r->uri.scheme, CONST_STR_LEN("http")); 603 config_cond_cache_reset_item(r, COMP_HTTP_SCHEME); 604 } 605 } 606 } 607 608 static handler_t mod_extforward_X_Forwarded_For(request_st * const r, plugin_data * const p, const buffer * const x_forwarded_for) { 609 /* build forward_array from forwarded data_string */ 610 array *forward_array = extract_forward_array(x_forwarded_for); 611 const char *real_remote_addr = last_not_in_array(forward_array, p); 612 if (real_remote_addr != NULL) { /* parsed */ 613 /* get scheme if X-Forwarded-Proto is set 614 * Limitations: 615 * - X-Forwarded-Proto may or may not be set by proxies, even if X-Forwarded-For is set 616 * - X-Forwarded-Proto may be a comma-separated list if there are multiple proxies, 617 * but the historical behavior of the code below only honored it if there was exactly one value 618 * (not done: walking backwards in X-Forwarded-Proto the same num of steps 619 * as in X-Forwarded-For to find proto set by last trusted proxy) 620 */ 621 const buffer *x_forwarded_proto = http_header_request_get(r, HTTP_HEADER_X_FORWARDED_PROTO, CONST_STR_LEN("X-Forwarded-Proto")); 622 if (mod_extforward_set_addr(r, p, real_remote_addr) && NULL != x_forwarded_proto) { 623 mod_extforward_set_proto(r, CONST_BUF_LEN(x_forwarded_proto)); 624 } 625 } 626 array_free(forward_array); 627 return HANDLER_GO_ON; 628 } 629 630 static int find_end_quoted_string (const char * const s, int i) { 631 do { 632 ++i; 633 } while (s[i] != '"' && s[i] != '\0' && (s[i] != '\\' || s[++i] != '\0')); 634 return i; 635 } 636 637 static int find_next_semicolon_or_comma_or_eq (const char * const s, int i) { 638 for (; s[i] != '=' && s[i] != ';' && s[i] != ',' && s[i] != '\0'; ++i) { 639 if (s[i] == '"') { 640 i = find_end_quoted_string(s, i); 641 if (s[i] == '\0') return -1; 642 } 643 } 644 return i; 645 } 646 647 static int find_next_semicolon_or_comma (const char * const s, int i) { 648 for (; s[i] != ';' && s[i] != ',' && s[i] != '\0'; ++i) { 649 if (s[i] == '"') { 650 i = find_end_quoted_string(s, i); 651 if (s[i] == '\0') return -1; 652 } 653 } 654 return i; 655 } 656 657 static int buffer_backslash_unescape (buffer * const b) { 658 /* (future: might move to buffer.c) */ 659 size_t j = 0; 660 size_t len = buffer_string_length(b); 661 char *p = memchr(b->ptr, '\\', len); 662 663 if (NULL == p) return 1; /*(nothing to do)*/ 664 665 len -= (size_t)(p - b->ptr); 666 for (size_t i = 0; i < len; ++i) { 667 if (p[i] == '\\') { 668 if (++i == len) return 0; /*(invalid trailing backslash)*/ 669 } 670 p[j++] = p[i]; 671 } 672 buffer_string_set_length(b, (size_t)(p+j - b->ptr)); 673 return 1; 674 } 675 676 static handler_t mod_extforward_Forwarded (request_st * const r, plugin_data * const p, const buffer * const forwarded) { 677 /* HTTP list need not consist of param=value tokens, 678 * but this routine expect such for HTTP Forwarded header 679 * Since info in each set of params is only used if from 680 * admin-specified trusted proxy: 681 * - invalid param=value tokens are ignored and skipped 682 * - not checking "for" exists in each set of params 683 * - not checking for duplicated params in each set of params 684 * - not checking canonical form of addr (also might be obfuscated) 685 * - obfuscated tokens permitted in chain, though end of trust is expected 686 * to be non-obfuscated IP for mod_extforward to masquerade as remote IP 687 * future: since (potentially) trusted proxies begin at end of string, 688 * it might be better to parse from end of string rather than parsing from 689 * beginning. Doing so would also allow reducing arbitrary param limit 690 * to number of params permitted per proxy. 691 */ 692 char * const s = forwarded->ptr; 693 int i = 0, j = -1, v, vlen, k, klen; 694 int used = (int)buffer_string_length(forwarded); 695 int ofor = -1, oproto, ohost, oby, oremote_user; 696 int offsets[256];/*(~50 params is more than reasonably expected to handle)*/ 697 while (i < used) { 698 while (s[i] == ' ' || s[i] == '\t') ++i; 699 if (s[i] == ';') { ++i; continue; } 700 if (s[i] == ',') { 701 if (j >= (int)(sizeof(offsets)/sizeof(int))) break; 702 offsets[++j] = -1; /*("offset" separating params from next proxy)*/ 703 ++i; 704 continue; 705 } 706 if (s[i] == '\0') break; 707 708 k = i; 709 i = find_next_semicolon_or_comma_or_eq(s, i); 710 if (i < 0) { 711 /*(reject IP spoofing if attacker sets improper quoted-string)*/ 712 log_error(r->conf.errh, __FILE__, __LINE__, 713 "invalid quoted-string in Forwarded header"); 714 r->http_status = 400; /* Bad Request */ 715 r->handler_module = NULL; 716 return HANDLER_FINISHED; 717 } 718 if (s[i] != '=') continue; 719 klen = i - k; 720 v = ++i; 721 i = find_next_semicolon_or_comma(s, i); 722 if (i < 0) { 723 /*(reject IP spoofing if attacker sets improper quoted-string)*/ 724 log_error(r->conf.errh, __FILE__, __LINE__, 725 "invalid quoted-string in Forwarded header"); 726 r->http_status = 400; /* Bad Request */ 727 r->handler_module = NULL; 728 return HANDLER_FINISHED; 729 } 730 vlen = i - v; /* might be 0 */ 731 732 /* have k, klen, v, vlen 733 * (might contain quoted string) (contents not validated or decoded) 734 * (might be repeated k) 735 */ 736 if (0 == klen) continue; /* invalid k */ 737 if (j >= (int)(sizeof(offsets)/sizeof(int))-4) break; 738 offsets[j+1] = k; 739 offsets[j+2] = klen; 740 offsets[j+3] = v; 741 offsets[j+4] = vlen; 742 j += 4; 743 } 744 745 if (j >= (int)(sizeof(offsets)/sizeof(int))-4) { 746 /* error processing Forwarded; too many params; fail closed */ 747 log_error(r->conf.errh, __FILE__, __LINE__, 748 "Too many params in Forwarded header"); 749 r->http_status = 400; /* Bad Request */ 750 r->handler_module = NULL; 751 return HANDLER_FINISHED; 752 } 753 754 if (-1 == j) return HANDLER_GO_ON; /* make no changes */ 755 used = j+1; 756 offsets[used] = -1; /* mark end of last set of params */ 757 758 while (j >= 4) { /*(param=value pairs)*/ 759 if (-1 == offsets[j]) { --j; continue; } 760 do { 761 j -= 3; /*(k, klen, v, vlen come in sets of 4)*/ 762 } while ((3 != offsets[j+1] /* 3 == sizeof("for")-1 */ 763 || !buffer_eq_icase_ssn(s+offsets[j], "for", 3)) 764 && 0 != j-- && -1 != offsets[j]); 765 if (j < 0) break; 766 if (-1 == offsets[j]) { --j; continue; } 767 768 /* remove trailing spaces/tabs and double-quotes from string 769 * (note: not unescaping backslash escapes in quoted string) */ 770 v = offsets[j+2]; 771 vlen = v + offsets[j+3]; 772 while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen; 773 if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') { 774 offsets[j+2] = ++v; 775 --vlen; 776 if (s[v] == '[') { 777 /* remove "[]" surrounding IPv6, as well as (optional) port 778 * (assumes properly formatted IPv6 addr from trusted proxy) */ 779 ++v; 780 do { --vlen; } while (vlen > v && s[vlen] != ']'); 781 if (v == vlen) { 782 log_error(r->conf.errh, __FILE__, __LINE__, 783 "Invalid IPv6 addr in Forwarded header"); 784 r->http_status = 400; /* Bad Request */ 785 r->handler_module = NULL; 786 return HANDLER_FINISHED; 787 } 788 } 789 else if (s[v] != '_' && s[v] != '/' && s[v] != 'u') { 790 /* remove (optional) port from non-obfuscated IPv4 */ 791 for (klen=vlen, vlen=v; vlen < klen && s[vlen] != ':'; ++vlen) ; 792 } 793 offsets[j+2] = v; 794 } 795 offsets[j+3] = vlen - v; 796 797 /* obfuscated ipstr and obfuscated port are also accepted here, as 798 * is path to unix domain socket, but note that backslash escapes 799 * in quoted-string were not unescaped above. Also, if obfuscated 800 * identifiers are rotated by proxies as recommended by RFC, then 801 * maintaining list of trusted identifiers is non-trivial and is not 802 * attempted by this module. */ 803 804 if (v != vlen) { 805 int trusted = is_proxy_trusted(p, s+v, vlen-v); 806 807 if (s[v] != '_' && s[v] != '/' 808 && (7 != (vlen - v) || 0 != memcmp(s+v, "unknown", 7))) { 809 ofor = j; /* save most recent non-obfuscated ipstr */ 810 } 811 812 if (!trusted) break; 813 } 814 815 do { --j; } while (j > 0 && -1 != offsets[j]); 816 if (j <= 0) break; 817 --j; 818 } 819 820 if (-1 != ofor) { 821 /* C funcs getaddrinfo(), inet_addr() require '\0'-terminated IP str */ 822 char *ipend = s+offsets[ofor+2]+offsets[ofor+3]; 823 char c = *ipend; 824 int rc; 825 *ipend = '\0'; 826 rc = mod_extforward_set_addr(r, p, s+offsets[ofor+2]); 827 *ipend = c; 828 if (!rc) return HANDLER_GO_ON; /* invalid addr; make no changes */ 829 } 830 else { 831 return HANDLER_GO_ON; /* make no changes */ 832 } 833 834 /* parse out params associated with for=<ip> addr set above */ 835 oproto = ohost = oby = oremote_user = -1; 836 j = ofor; 837 if (j > 0) { do { --j; } while (j > 0 && -1 != offsets[j]); } 838 if (-1 == offsets[j]) ++j; 839 if (j == ofor) j += 4; 840 for (; -1 != offsets[j]; j+=4) { /*(k, klen, v, vlen come in sets of 4)*/ 841 switch (offsets[j+1]) { 842 #if 0 843 case 2: 844 if (buffer_eq_icase_ssn(s+offsets[j], "by", 2)) 845 oby = j; 846 break; 847 #endif 848 #if 0 849 /*(already handled above to find IP prior to earliest trusted proxy)*/ 850 case 3: 851 if (buffer_eq_icase_ssn(s+offsets[j], "for", 3)) 852 ofor = j; 853 break; 854 #endif 855 case 4: 856 if (buffer_eq_icase_ssn(s+offsets[j], "host", 4)) 857 ohost = j; 858 break; 859 case 5: 860 if (buffer_eq_icase_ssn(s+offsets[j], "proto", 5)) 861 oproto = j; 862 break; 863 case 11: 864 if (buffer_eq_icase_ssn(s+offsets[j], "remote_user", 11)) 865 oremote_user = j; 866 break; 867 default: 868 break; 869 } 870 } 871 i = ++j; 872 873 if (-1 != oproto) { 874 /* remove trailing spaces/tabs, and double-quotes from proto 875 * (note: not unescaping backslash escapes in quoted string) */ 876 v = offsets[oproto+2]; 877 vlen = v + offsets[oproto+3]; 878 while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen; 879 if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') { ++v; --vlen; } 880 mod_extforward_set_proto(r, s+v, vlen-v); 881 } 882 883 if (p->conf.opts & PROXY_FORWARDED_HOST) { 884 /* Limitations: 885 * - r->http_host is not reset in mod_extforward_restore() 886 * but is currently not an issues since r->http_host will be 887 * reset by next request. If a new module uses r->http_host 888 * in the handle_request_done hook, then should evaluate if that 889 * module should use the forwarded value (probably) or original value. 890 * - due to need to decode and unescape host=..., some extra work is 891 * done in the case where host matches current Host header. 892 * future: might add code to check if Host has actually changed or not 893 * 894 * note: change host after mod_extforward_set_proto() since that may 895 * affect scheme port used in http_request_host_policy() host 896 * normalization 897 */ 898 899 /* find host param set by earliest trusted proxy in proxy chain 900 * (host might be changed anywhere along the chain) */ 901 for (j = i; j < used && -1 == ohost; ) { 902 if (-1 == offsets[j]) { ++j; continue; } 903 if (4 == offsets[j+1] 904 && buffer_eq_icase_ssn(s+offsets[j], "host", 4)) 905 ohost = j; 906 j += 4; /*(k, klen, v, vlen come in sets of 4)*/ 907 } 908 if (-1 != ohost) { 909 if (extforward_check_proxy 910 && !buffer_string_is_empty(r->http_host)) { 911 http_header_env_set(r, 912 CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_HOST"), 913 CONST_BUF_LEN(r->http_host)); 914 } 915 /* remove trailing spaces/tabs, and double-quotes from host */ 916 v = offsets[ohost+2]; 917 vlen = v + offsets[ohost+3]; 918 while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen; 919 if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') { 920 ++v; --vlen; 921 buffer_copy_string_len(r->http_host, s+v, vlen-v); 922 if (!buffer_backslash_unescape(r->http_host)) { 923 log_error(r->conf.errh, __FILE__, __LINE__, 924 "invalid host= value in Forwarded header"); 925 r->http_status = 400; /* Bad Request */ 926 r->handler_module = NULL; 927 return HANDLER_FINISHED; 928 } 929 } 930 else { 931 buffer_copy_string_len(r->http_host, s+v, vlen-v); 932 } 933 934 int scheme_port = 935 buffer_eq_slen(&r->uri.scheme,CONST_STR_LEN("https")) ? 443 : 80; 936 if (0 != http_request_host_policy(r->http_host, 937 r->conf.http_parseopts, 938 scheme_port)) { 939 /*(reject invalid chars in Host)*/ 940 log_error(r->conf.errh, __FILE__, __LINE__, 941 "invalid host= value in Forwarded header"); 942 r->http_status = 400; /* Bad Request */ 943 r->handler_module = NULL; 944 return HANDLER_FINISHED; 945 } 946 947 config_cond_cache_reset_item(r, COMP_HTTP_HOST); 948 } 949 } 950 951 if (p->conf.opts & PROXY_FORWARDED_REMOTE_USER) { 952 /* find remote_user param set by closest proxy 953 * (auth may have been handled by any trusted proxy in proxy chain) */ 954 for (j = i; j < used; ) { 955 if (-1 == offsets[j]) { ++j; continue; } 956 if (11 == offsets[j+1] 957 && buffer_eq_icase_ssn(s+offsets[j], "remote_user", 11)) 958 oremote_user = j; 959 j += 4; /*(k, klen, v, vlen come in sets of 4)*/ 960 } 961 if (-1 != oremote_user) { 962 /* ???: should we also support param for auth_type ??? */ 963 /* remove trailing spaces/tabs, and double-quotes from remote_user*/ 964 v = offsets[oremote_user+2]; 965 vlen = v + offsets[oremote_user+3]; 966 while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen; 967 if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') { 968 buffer *euser; 969 ++v; --vlen; 970 http_header_env_set(r, 971 CONST_STR_LEN("REMOTE_USER"), s+v, vlen-v); 972 euser = http_header_env_get(r, CONST_STR_LEN("REMOTE_USER")); 973 force_assert(NULL != euser); 974 if (!buffer_backslash_unescape(euser)) { 975 log_error(r->conf.errh, __FILE__, __LINE__, 976 "invalid remote_user= value in Forwarded header"); 977 r->http_status = 400; /* Bad Request */ 978 r->handler_module = NULL; 979 return HANDLER_FINISHED; 980 } 981 } 982 else { 983 http_header_env_set(r, 984 CONST_STR_LEN("REMOTE_USER"), s+v, vlen-v); 985 } 986 } 987 } 988 989 #if 0 990 if ((p->conf.opts & PROXY_FORWARDED_CREATE_XFF) 991 && NULL == http_header_request_get(r, HTTP_HEADER_X_FORWARDED_FOR, CONST_STR_LEN("X-Forwarded-For"))) { 992 /* create X-Forwarded-For if not present 993 * (and at least original connecting IP is a trusted proxy) */ 994 buffer *xff = r->tmp_buf; 995 buffer_clear(xff); 996 for (j = 0; j < used; ) { 997 if (-1 == offsets[j]) { ++j; continue; } 998 if (3 == offsets[j+1] 999 && buffer_eq_icase_ssn(s+offsets[j], "for", 3)) { 1000 if (!buffer_string_is_empty(xff)) 1001 buffer_append_string_len(xff, CONST_STR_LEN(", ")); 1002 /* quoted-string, IPv6 brackets, and :port already removed */ 1003 v = offsets[j+2]; 1004 vlen = offsets[j+3]; 1005 buffer_append_string_len(xff, s+v, vlen); 1006 if (s[v-1] != '=') { /*(must have been quoted-string)*/ 1007 char *x = 1008 memchr(xff->ptr+buffer_string_length(xff)-vlen,'\\',vlen); 1009 if (NULL != x) { /* backslash unescape in-place */ 1010 for (v = 0; x[v]; ++x) { 1011 if (x[v] == '\\' && x[++v] == '\0') 1012 break; /*(invalid trailing backslash)*/ 1013 *x = x[v]; 1014 } 1015 buffer_string_set_length(xff, x - xff->ptr); 1016 } 1017 } 1018 /* skip to next group; take first "for=..." in group 1019 * (should be 0 or 1 "for=..." per group, but not trusted) */ 1020 do { j += 4; } while (-1 != offsets[j]); 1021 ++j; 1022 continue; 1023 } 1024 j += 4; /*(k, klen, v, vlen come in sets of 4)*/ 1025 } 1026 http_header_request_set(r, HTTP_HEADER_X_FORWARDED_FOR, CONST_STR_LEN("X-Forwarded-For"), CONST_BUF_LEN(xff)); 1027 } 1028 #endif 1029 1030 return HANDLER_GO_ON; 1031 } 1032 1033 URIHANDLER_FUNC(mod_extforward_uri_handler) { 1034 plugin_data *p = p_d; 1035 const buffer *forwarded = NULL; 1036 handler_ctx *hctx = r->plugin_ctx[p->id]; 1037 int is_forwarded_header = 0; 1038 1039 mod_extforward_patch_config(r, p); 1040 1041 if (r->conf.log_request_handling) { 1042 log_error(r->conf.errh, __FILE__, __LINE__, 1043 "-- mod_extforward_uri_handler called"); 1044 } 1045 1046 if (p->conf.hap_PROXY_ssl_client_verify) { 1047 const data_string *ds; 1048 if (NULL != hctx && hctx->ssl_client_verify && NULL != hctx->env 1049 && NULL != (ds = (const data_string *)array_get_element_klen(hctx->env, CONST_STR_LEN("SSL_CLIENT_S_DN_CN")))) { 1050 http_header_env_set(r, 1051 CONST_STR_LEN("SSL_CLIENT_VERIFY"), 1052 CONST_STR_LEN("SUCCESS")); 1053 http_header_env_set(r, 1054 CONST_STR_LEN("REMOTE_USER"), 1055 CONST_BUF_LEN(&ds->value)); 1056 http_header_env_set(r, 1057 CONST_STR_LEN("AUTH_TYPE"), 1058 CONST_STR_LEN("SSL_CLIENT_VERIFY")); 1059 } else { 1060 http_header_env_set(r, 1061 CONST_STR_LEN("SSL_CLIENT_VERIFY"), 1062 CONST_STR_LEN("NONE")); 1063 } 1064 } 1065 1066 if (NULL == p->conf.forwarder) return HANDLER_GO_ON; 1067 if (NULL == p->conf.headers) return HANDLER_GO_ON; 1068 for (uint32_t k = 0; k < p->conf.headers->used && NULL == forwarded; ++k) { 1069 buffer *hdr = &((data_string *)p->conf.headers->data[k])->value; 1070 forwarded = http_header_request_get(r, HTTP_HEADER_UNSPECIFIED, CONST_BUF_LEN(hdr)); 1071 if (forwarded) { 1072 is_forwarded_header = buffer_is_equal_caseless_string(hdr, CONST_STR_LEN("Forwarded")); 1073 break; 1074 } 1075 } 1076 if (NULL == forwarded) { 1077 if (r->conf.log_request_handling) { 1078 log_error(r->conf.errh, __FILE__, __LINE__, 1079 "no forward header found, skipping"); 1080 } 1081 1082 return HANDLER_GO_ON; 1083 } 1084 1085 /* if the remote ip itself is not trusted, then do nothing */ 1086 if (!is_connection_trusted(r->con, p)) { 1087 if (r->conf.log_request_handling) { 1088 log_error(r->conf.errh, __FILE__, __LINE__, 1089 "remote address %s is NOT a trusted proxy, skipping", 1090 r->con->dst_addr_buf->ptr); 1091 } 1092 1093 return HANDLER_GO_ON; 1094 } 1095 1096 if (is_forwarded_header) { 1097 return mod_extforward_Forwarded(r, p, forwarded); 1098 } 1099 1100 return mod_extforward_X_Forwarded_For(r, p, forwarded); 1101 } 1102 1103 1104 REQUEST_FUNC(mod_extforward_handle_request_env) { 1105 plugin_data *p = p_d; 1106 handler_ctx *hctx = r->plugin_ctx[p->id]; 1107 if (NULL == hctx || NULL == hctx->env) return HANDLER_GO_ON; 1108 for (uint32_t i=0; i < hctx->env->used; ++i) { 1109 /* note: replaces values which may have been set by mod_openssl 1110 * (when mod_extforward is listed after mod_openssl in server.modules)*/ 1111 data_string *ds = (data_string *)hctx->env->data[i]; 1112 http_header_env_set(r, 1113 CONST_BUF_LEN(&ds->key), CONST_BUF_LEN(&ds->value)); 1114 } 1115 return HANDLER_GO_ON; 1116 } 1117 1118 1119 REQUEST_FUNC(mod_extforward_restore) { 1120 /* XXX: should change this to not occur at request reset, 1121 * but instead at connection reset */ 1122 plugin_data *p = p_d; 1123 handler_ctx *hctx = r->plugin_ctx[p->id]; 1124 1125 if (!hctx) return HANDLER_GO_ON; 1126 1127 connection * const con = r->con; 1128 if (NULL != hctx->saved_network_read) { 1129 con->network_read = hctx->saved_network_read; 1130 hctx->saved_network_read = NULL; 1131 } 1132 1133 if (NULL != hctx->saved_remote_addr_buf) { 1134 con->dst_addr = hctx->saved_remote_addr; 1135 buffer_free(con->dst_addr_buf); 1136 con->dst_addr_buf = hctx->saved_remote_addr_buf; 1137 hctx->saved_remote_addr_buf = NULL; 1138 /* Now, clean the conf_cond cache, because we may have changed the results of tests */ 1139 config_cond_cache_reset_item(r, COMP_HTTP_REMOTE_IP); 1140 } 1141 1142 if (NULL == hctx->env) { 1143 handler_ctx_free(hctx); 1144 r->plugin_ctx[p->id] = NULL; 1145 } 1146 1147 return HANDLER_GO_ON; 1148 } 1149 1150 1151 CONNECTION_FUNC(mod_extforward_handle_con_close) 1152 { 1153 request_st * const r = &con->request; 1154 plugin_data *p = p_d; 1155 handler_ctx *hctx = r->plugin_ctx[p->id]; 1156 if (NULL != hctx) { 1157 if (NULL != hctx->saved_network_read) { 1158 con->network_read = hctx->saved_network_read; 1159 } 1160 if (NULL != hctx->saved_remote_addr_buf) { 1161 con->dst_addr = hctx->saved_remote_addr; 1162 buffer_free(con->dst_addr_buf); 1163 con->dst_addr_buf = hctx->saved_remote_addr_buf; 1164 } 1165 if (NULL != hctx->env) { 1166 array_free(hctx->env); 1167 } 1168 handler_ctx_free(hctx); 1169 r->plugin_ctx[p->id] = NULL; 1170 } 1171 1172 return HANDLER_GO_ON; 1173 } 1174 1175 1176 static int mod_extforward_network_read (connection *con, chunkqueue *cq, off_t max_bytes); 1177 1178 CONNECTION_FUNC(mod_extforward_handle_con_accept) 1179 { 1180 request_st * const r = &con->request; 1181 plugin_data *p = p_d; 1182 mod_extforward_patch_config(r, p); 1183 if (!p->conf.hap_PROXY) return HANDLER_GO_ON; 1184 if (NULL == p->conf.forwarder) return HANDLER_GO_ON; 1185 if (is_connection_trusted(con, p)) { 1186 handler_ctx *hctx = handler_ctx_init(); 1187 r->plugin_ctx[p->id] = hctx; 1188 hctx->saved_network_read = con->network_read; 1189 con->network_read = mod_extforward_network_read; 1190 } 1191 else { 1192 if (r->conf.log_request_handling) { 1193 log_error(r->conf.errh, __FILE__, __LINE__, 1194 "remote address %s is NOT a trusted proxy, skipping", 1195 con->dst_addr_buf->ptr); 1196 } 1197 } 1198 return HANDLER_GO_ON; 1199 } 1200 1201 1202 int mod_extforward_plugin_init(plugin *p); 1203 int mod_extforward_plugin_init(plugin *p) { 1204 p->version = LIGHTTPD_VERSION_ID; 1205 p->name = "extforward"; 1206 1207 p->init = mod_extforward_init; 1208 p->handle_connection_accept = mod_extforward_handle_con_accept; 1209 p->handle_uri_raw = mod_extforward_uri_handler; 1210 p->handle_request_env = mod_extforward_handle_request_env; 1211 p->handle_request_done = mod_extforward_restore; 1212 p->connection_reset = mod_extforward_restore; 1213 p->handle_connection_close = mod_extforward_handle_con_close; 1214 p->set_defaults = mod_extforward_set_defaults; 1215 p->cleanup = mod_extforward_free; 1216 1217 return 0; 1218 } 1219 1220 1221 1222 1223 /* Modified from: 1224 * http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt 1225 * 1226 9. Sample code 1227 1228 The code below is an example of how a receiver may deal with both versions of 1229 the protocol header for TCP over IPv4 or IPv6. The function is supposed to be 1230 called upon a read event. Addresses may be directly copied into their final 1231 memory location since they're transported in network byte order. The sending 1232 side is even simpler and can easily be deduced from this sample code. 1233 * 1234 */ 1235 1236 union hap_PROXY_hdr { 1237 struct { 1238 char line[108]; 1239 } v1; 1240 struct { 1241 uint8_t sig[12]; 1242 uint8_t ver_cmd; 1243 uint8_t fam; 1244 uint16_t len; 1245 union { 1246 struct { /* for TCP/UDP over IPv4, len = 12 */ 1247 uint32_t src_addr; 1248 uint32_t dst_addr; 1249 uint16_t src_port; 1250 uint16_t dst_port; 1251 } ip4; 1252 struct { /* for TCP/UDP over IPv6, len = 36 */ 1253 uint8_t src_addr[16]; 1254 uint8_t dst_addr[16]; 1255 uint16_t src_port; 1256 uint16_t dst_port; 1257 } ip6; 1258 struct { /* for AF_UNIX sockets, len = 216 */ 1259 uint8_t src_addr[108]; 1260 uint8_t dst_addr[108]; 1261 } unx; 1262 } addr; 1263 } v2; 1264 }; 1265 1266 /* 1267 If the length specified in the PROXY protocol header indicates that additional 1268 bytes are part of the header beyond the address information, a receiver may 1269 choose to skip over and ignore those bytes, or attempt to interpret those 1270 bytes. 1271 1272 The information in those bytes will be arranged in Type-Length-Value (TLV 1273 vectors) in the following format. The first byte is the Type of the vector. 1274 The second two bytes represent the length in bytes of the value (not included 1275 the Type and Length bytes), and following the length field is the number of 1276 bytes specified by the length. 1277 */ 1278 struct pp2_tlv { 1279 uint8_t type; 1280 uint8_t length_hi; 1281 uint8_t length_lo; 1282 /*uint8_t value[0];*//* C99 zero-length array */ 1283 }; 1284 1285 /* 1286 The following types have already been registered for the <type> field : 1287 */ 1288 1289 #define PP2_TYPE_ALPN 0x01 1290 #define PP2_TYPE_AUTHORITY 0x02 1291 #define PP2_TYPE_CRC32C 0x03 1292 #define PP2_TYPE_NOOP 0x04 1293 #define PP2_TYPE_SSL 0x20 1294 #define PP2_SUBTYPE_SSL_VERSION 0x21 1295 #define PP2_SUBTYPE_SSL_CN 0x22 1296 #define PP2_SUBTYPE_SSL_CIPHER 0x23 1297 #define PP2_SUBTYPE_SSL_SIG_ALG 0x24 1298 #define PP2_SUBTYPE_SSL_KEY_ALG 0x25 1299 #define PP2_TYPE_NETNS 0x30 1300 1301 /* 1302 For the type PP2_TYPE_SSL, the value is itselv a defined like this : 1303 */ 1304 1305 struct pp2_tlv_ssl { 1306 uint8_t client; 1307 uint32_t verify; 1308 /*struct pp2_tlv sub_tlv[0];*//* C99 zero-length array */ 1309 }; 1310 1311 /* 1312 And the <client> field is made of a bit field from the following values, 1313 indicating which element is present : 1314 */ 1315 1316 #define PP2_CLIENT_SSL 0x01 1317 #define PP2_CLIENT_CERT_CONN 0x02 1318 #define PP2_CLIENT_CERT_SESS 0x04 1319 1320 1321 1322 1323 #ifndef MSG_DONTWAIT 1324 #define MSG_DONTWAIT 0 1325 #endif 1326 #ifndef MSG_NOSIGNAL 1327 #define MSG_NOSIGNAL 0 1328 #endif 1329 1330 /* returns 0 if needs to poll, <0 upon error or >0 is protocol vers (success) */ 1331 static int hap_PROXY_recv (const int fd, union hap_PROXY_hdr * const hdr, const int family, const int so_type) 1332 { 1333 static const char v2sig[12] = 1334 "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"; 1335 1336 ssize_t ret; 1337 size_t sz; 1338 int ver; 1339 1340 do { 1341 ret = recv(fd, hdr, sizeof(*hdr), MSG_PEEK|MSG_DONTWAIT|MSG_NOSIGNAL); 1342 } while (-1 == ret && errno == EINTR); 1343 1344 if (-1 == ret) 1345 return (errno == EAGAIN 1346 #ifdef EWOULDBLOCK 1347 #if EAGAIN != EWOULDBLOCK 1348 || errno == EWOULDBLOCK 1349 #endif 1350 #endif 1351 ) ? 0 : -1; 1352 1353 if (ret >= 16 && 0 == memcmp(&hdr->v2, v2sig, 12) 1354 && (hdr->v2.ver_cmd & 0xF0) == 0x20) { 1355 ver = 2; 1356 sz = 16 + (size_t)ntohs(hdr->v2.len); 1357 if ((size_t)ret < sz) 1358 return -2; /* truncated or too large header */ 1359 1360 switch (hdr->v2.ver_cmd & 0xF) { 1361 case 0x01: break; /* PROXY command */ 1362 case 0x00: break; /* LOCAL command */ 1363 default: return -2; /* not a supported command */ 1364 } 1365 } 1366 else if (ret >= 8 && 0 == memcmp(hdr->v1.line, "PROXY", 5)) { 1367 const char *end = memchr(hdr->v1.line, '\r', ret - 1); 1368 if (!end || end[1] != '\n') 1369 return -2; /* partial or invalid header */ 1370 ver = 1; 1371 sz = (size_t)(end + 2 - hdr->v1.line); /* skip header + CRLF */ 1372 } 1373 else { 1374 /* Wrong protocol */ 1375 return -2; 1376 } 1377 1378 /* we need to consume the appropriate amount of data from the socket 1379 * (overwrites existing contents of hdr with same data) */ 1380 UNUSED(family); 1381 UNUSED(so_type); 1382 do { 1383 #if defined(MSG_TRUNC) && defined(__linux__) 1384 if ((family==AF_INET || family==AF_INET6) && so_type == SOCK_STREAM) { 1385 ret = recv(fd, hdr, sz, MSG_TRUNC|MSG_DONTWAIT|MSG_NOSIGNAL); 1386 if (ret >= 0 || errno != EINVAL) continue; 1387 } 1388 #endif 1389 ret = recv(fd, hdr, sz, MSG_DONTWAIT|MSG_NOSIGNAL); 1390 } while (-1 == ret && errno == EINTR); 1391 if (ret < 0) return -1; 1392 if (ret != (ssize_t)sz) { 1393 errno = EIO; /*(partial read; valid but unexpected; not handled)*/ 1394 return -1; 1395 } 1396 if (1 == ver) hdr->v1.line[sz-2] = '\0'; /*terminate str to ease parsing*/ 1397 return ver; 1398 } 1399 1400 1401 static int mod_extforward_hap_PROXY_v1 (connection * const con, 1402 union hap_PROXY_hdr * const hdr) 1403 { 1404 #ifdef __COVERITY__ 1405 __coverity_tainted_data_sink__(hdr); 1406 #endif 1407 1408 /* samples 1409 * "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n" 1410 * "PROXY TCP6 ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n" 1411 * "PROXY UNKNOWN\r\n" 1412 * "PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n" 1413 */ 1414 char *s = hdr->v1.line + sizeof("PROXY")-1; /*checked in hap_PROXY_recv()*/ 1415 char *src_addr, *dst_addr, *src_port, *dst_port, *e; 1416 int family; 1417 long src_lport, dst_lport; 1418 if (*s != ' ') return -1; 1419 ++s; 1420 if (s[0] == 'T' && s[1] == 'C' && s[2] == 'P' && s[4] == ' ') { 1421 if (s[3] == '4') { 1422 family = AF_INET; 1423 } else if (s[3] == '6') { 1424 family = AF_INET6; 1425 } 1426 else { 1427 return -1; 1428 } 1429 s += 5; 1430 } 1431 else if (0 == memcmp(s, "UNKNOWN", sizeof("UNKNOWN")-1) 1432 && (s[7] == '\0' || s[7] == ' ')) { 1433 return 0; /* keep local connection address */ 1434 } 1435 else { 1436 return -1; 1437 } 1438 1439 /*(strsep() should be fairly portable, but is not standard)*/ 1440 src_addr = s; 1441 dst_addr = strchr(src_addr, ' '); 1442 if (NULL == dst_addr) return -1; 1443 *dst_addr++ = '\0'; 1444 src_port = strchr(dst_addr, ' '); 1445 if (NULL == src_port) return -1; 1446 *src_port++ = '\0'; 1447 dst_port = strchr(src_port, ' '); 1448 if (NULL == dst_port) return -1; 1449 *dst_port++ = '\0'; 1450 1451 src_lport = strtol(src_port, &e, 10); 1452 if (src_lport <= 0 || src_lport > USHRT_MAX || *e != '\0') return -1; 1453 dst_lport = strtol(dst_port, &e, 10); 1454 if (dst_lport <= 0 || dst_lport > USHRT_MAX || *e != '\0') return -1; 1455 1456 if (1 != sock_addr_inet_pton(&con->dst_addr, 1457 src_addr, family, (unsigned short)src_lport)) 1458 return -1; 1459 /* Forwarded by=... could be saved here. 1460 * (see additional comments in mod_extforward_hap_PROXY_v2()) */ 1461 1462 /* re-parse addr to string to normalize 1463 * (instead of trusting PROXY to provide canonicalized src_addr string) 1464 * (should prefer PROXY v2 protocol if concerned about performance) */ 1465 sock_addr_inet_ntop_copy_buffer(con->dst_addr_buf, &con->dst_addr); 1466 1467 return 0; 1468 } 1469 1470 1471 static int mod_extforward_hap_PROXY_v2 (connection * const con, 1472 union hap_PROXY_hdr * const hdr) 1473 { 1474 #ifdef __COVERITY__ 1475 __coverity_tainted_data_sink__(hdr); 1476 #endif 1477 1478 /* If HAProxy-PROXY protocol used, then lighttpd acts as transparent proxy, 1479 * masquerading as servicing the client IP provided in by HAProxy-PROXY hdr. 1480 * The connecting con->dst_addr and con->dst_addr_buf are not saved here, 1481 * so that info is lost unless getsockname() and getpeername() are used. 1482 * One result is that mod_proxy will use the masqueraded IP instead of the 1483 * actual IP when updated Forwarded and X-Forwarded-For (but if actual 1484 * connection IPs needed, better to save the info here rather than use 1485 * syscalls to retrieve the info later). 1486 * (Exception: con->dst_addr can be further changed if mod_extforward parses 1487 * Forwaded or X-Forwarded-For request headers later, after request headers 1488 * have been received.) 1489 */ 1490 1491 /* Forwarded by=... could be saved here. The by param is for backends to be 1492 * able to construct URIs for that interface (interface on server which 1493 * received request and made PROXY connection here), though that server 1494 * should provide that information in updated Forwarded or X-Forwarded-For 1495 * HTTP headers */ 1496 /*struct sockaddr_storage by;*/ 1497 1498 /* Addresses provided by HAProxy-PROXY protocol are in network byte order. 1499 * Note: addr info is not validated, so do not accept HAProxy-PROXY 1500 * protocol from untrusted servers. For example, untrusted servers from 1501 * which HAProxy-PROXY protocol is accepted (don't do that) could pretend 1502 * to be from the internal network and might thereby bypass security policy. 1503 */ 1504 1505 /* (Clear con->dst_addr with memset() in case actual and proxies IPs 1506 * are different domains, e.g. one is IPv4 and the other is IPv6) */ 1507 1508 struct pp2_tlv *tlv; 1509 uint32_t sz = ntohs(hdr->v2.len); 1510 uint32_t len = 0; 1511 1512 switch (hdr->v2.ver_cmd & 0xF) { 1513 case 0x01: break; /* PROXY command */ 1514 case 0x00: return 0;/* LOCAL command; keep local connection address */ 1515 default: return -1;/* should not happen; validated in hap_PROXY_recv()*/ 1516 } 1517 1518 /* PROXY command */ 1519 1520 switch (hdr->v2.fam) { 1521 case 0x11: /* TCPv4 */ 1522 sock_addr_assign(&con->dst_addr, AF_INET, hdr->v2.addr.ip4.src_port, 1523 &hdr->v2.addr.ip4.src_addr); 1524 sock_addr_inet_ntop_copy_buffer(con->dst_addr_buf, &con->dst_addr); 1525 #if 0 1526 ((struct sockaddr_in *)&by)->sin_family = AF_INET; 1527 ((struct sockaddr_in *)&by)->sin_addr.s_addr = 1528 hdr->v2.addr.ip4.dst_addr; 1529 ((struct sockaddr_in *)&by)->sin_port = 1530 hdr->v2.addr.ip4.dst_port; 1531 #endif 1532 len = (uint32_t)sizeof(hdr->v2.addr.ip4); 1533 break; 1534 #ifdef HAVE_IPV6 1535 case 0x21: /* TCPv6 */ 1536 sock_addr_assign(&con->dst_addr, AF_INET6, hdr->v2.addr.ip6.src_port, 1537 &hdr->v2.addr.ip6.src_addr); 1538 sock_addr_inet_ntop_copy_buffer(con->dst_addr_buf, &con->dst_addr); 1539 #if 0 1540 ((struct sockaddr_in6 *)&by)->sin6_family = AF_INET6; 1541 memcpy(&((struct sockaddr_in6 *)&by)->sin6_addr, 1542 hdr->v2.addr.ip6.dst_addr, 16); 1543 ((struct sockaddr_in6 *)&by)->sin6_port = 1544 hdr->v2.addr.ip6.dst_port; 1545 #endif 1546 len = (uint32_t)sizeof(hdr->v2.addr.ip6); 1547 break; 1548 #endif 1549 #ifdef HAVE_SYS_UN_H 1550 case 0x31: /* UNIX domain socket */ 1551 { 1552 char *src_addr = (char *)hdr->v2.addr.unx.src_addr; 1553 char *z = memchr(src_addr, '\0', UNIX_PATH_MAX); 1554 if (NULL == z) return -1; /* invalid addr; too long */ 1555 len = (uint32_t)(z - src_addr + 1); /*(+1 for '\0')*/ 1556 sock_addr_assign(&con->dst_addr, AF_UNIX, 0, src_addr); 1557 buffer_copy_string_len(con->dst_addr_buf, src_addr, len); 1558 } 1559 #if 0 /*(dst_addr should be identical to src_addr for AF_UNIX)*/ 1560 ((struct sockaddr_un *)&by)->sun_family = AF_UNIX; 1561 memcpy(&((struct sockaddr_un *)&by)->sun_path, 1562 hdr->v2.addr.unx.dst_addr, 108); 1563 #endif 1564 len = (uint32_t)sizeof(hdr->v2.addr.unx); 1565 break; 1566 #endif 1567 default: /* keep local connection address; unsupported protocol */ 1568 return 0; 1569 } 1570 1571 /* (optional) Type-Length-Value (TLV vectors) follow addresses */ 1572 1573 tlv = (struct pp2_tlv *)((char *)hdr + 16); 1574 for (sz -= len, len -= 3; sz >= 3; sz -= 3 + len) { 1575 tlv = (struct pp2_tlv *)((char *)tlv + 3 + len); 1576 len = ((uint32_t)tlv->length_hi << 8) | tlv->length_lo; 1577 if (3 + len > sz) break; /*(invalid TLV)*/ 1578 switch (tlv->type) { 1579 #if 0 /*(not implemented here)*/ 1580 case PP2_TYPE_ALPN: 1581 case PP2_TYPE_AUTHORITY: 1582 case PP2_TYPE_CRC32C: 1583 #endif 1584 case PP2_TYPE_SSL: { 1585 request_st * const r = &con->request; 1586 static const uint32_t zero = 0; 1587 handler_ctx *hctx = 1588 r->plugin_ctx[mod_extforward_plugin_data_singleton->id]; 1589 struct pp2_tlv_ssl *tlv_ssl = 1590 (struct pp2_tlv_ssl *)(void *)((char *)tlv+3); 1591 struct pp2_tlv *subtlv = tlv; 1592 if (tlv_ssl->client & PP2_CLIENT_SSL) { 1593 con->proto_default_port = 443; /* "https" */ 1594 } 1595 if ((tlv_ssl->client & (PP2_CLIENT_CERT_CONN|PP2_CLIENT_CERT_SESS)) 1596 && 0 == memcmp(&tlv_ssl->verify, &zero, 4)) { /* misaligned */ 1597 hctx->ssl_client_verify = 1; 1598 } 1599 for (uint32_t subsz = len-5, n = 5; subsz >= 3; subsz -= 3 + n) { 1600 subtlv = (struct pp2_tlv *)((char *)subtlv + 3 + n); 1601 n = ((uint32_t)subtlv->length_hi << 8) | subtlv->length_lo; 1602 if (3 + n > subsz) break; /*(invalid TLV)*/ 1603 if (NULL == hctx->env) hctx->env = array_init(8); 1604 switch (subtlv->type) { 1605 case PP2_SUBTYPE_SSL_VERSION: 1606 array_set_key_value(hctx->env, 1607 CONST_STR_LEN("SSL_PROTOCOL"), 1608 (char *)subtlv+3, n); 1609 break; 1610 case PP2_SUBTYPE_SSL_CN: 1611 /* (tlv_ssl->client & PP2_CLIENT_CERT_CONN) 1612 * or 1613 * (tlv_ssl->client & PP2_CLIENT_CERT_SESS) */ 1614 array_set_key_value(hctx->env, 1615 CONST_STR_LEN("SSL_CLIENT_S_DN_CN"), 1616 (char *)subtlv+3, n); 1617 break; 1618 case PP2_SUBTYPE_SSL_CIPHER: 1619 array_set_key_value(hctx->env, 1620 CONST_STR_LEN("SSL_CIPHER"), 1621 (char *)subtlv+3, n); 1622 break; 1623 case PP2_SUBTYPE_SSL_SIG_ALG: 1624 array_set_key_value(hctx->env, 1625 CONST_STR_LEN("SSL_SERVER_A_SIG"), 1626 (char *)subtlv+3, n); 1627 break; 1628 case PP2_SUBTYPE_SSL_KEY_ALG: 1629 array_set_key_value(hctx->env, 1630 CONST_STR_LEN("SSL_SERVER_A_KEY"), 1631 (char *)subtlv+3, n); 1632 break; 1633 default: 1634 break; 1635 } 1636 } 1637 break; 1638 } 1639 #if 0 /*(not implemented here)*/ 1640 case PP2_TYPE_NETNS: 1641 #endif 1642 /*case PP2_TYPE_NOOP:*//* no-op */ 1643 default: 1644 break; 1645 } 1646 } 1647 1648 return 0; 1649 } 1650 1651 1652 static int mod_extforward_network_read (connection *con, 1653 chunkqueue *cq, off_t max_bytes) 1654 { 1655 /* XXX: when using hap-PROXY protocol, currently avoid overhead of setting 1656 * _L_ environment variables for mod_proxy to accurately set Forwarded hdr 1657 * In the future, might add config switch to enable doing this extra work */ 1658 1659 union hap_PROXY_hdr hdr; 1660 log_error_st *errh; 1661 int rc = hap_PROXY_recv(con->fd, &hdr, 1662 con->dst_addr.plain.sa_family, SOCK_STREAM); 1663 switch (rc) { 1664 case 2: rc = mod_extforward_hap_PROXY_v2(con, &hdr); break; 1665 case 1: rc = mod_extforward_hap_PROXY_v1(con, &hdr); break; 1666 case 0: return 0; /*(errno == EAGAIN || errno == EWOULDBLOCK)*/ 1667 case -1: errh = con->request.conf.errh; 1668 log_perror(errh,__FILE__,__LINE__,"hap-PROXY recv()"); 1669 rc = -1; break; 1670 case -2: errh = con->request.conf.errh; 1671 log_error(errh,__FILE__,__LINE__, 1672 "hap-PROXY proto received invalid/unsupported request"); 1673 /* fall through */ 1674 default: rc = -1; break; 1675 } 1676 1677 request_st * const r = &con->request; 1678 mod_extforward_restore(r, mod_extforward_plugin_data_singleton); 1679 return (0 == rc) ? con->network_read(con, cq, max_bytes) : rc; 1680 } 1681