diff --git a/include/http_protocol.h b/include/http_protocol.h index a9e0990..641ede1 100644 --- a/include/http_protocol.h +++ b/include/http_protocol.h @@ -672,6 +672,19 @@ AP_DECLARE(const char *) ap_method_name_of(apr_pool_t *p, int methnum); AP_DECLARE_HOOK(void,pre_read_request,(request_rec *r, conn_rec *c)) /* + * post_headers_parsed --- run right before ap_update_vhost_from_headers(), + * and not run during any subrequests. + */ +/** + * This hook allows modules to affect the request or connection immediately after + * request headers are parsed and verified, but before the VirtualHost has been + * resolved. + * @param r The current request + * @return OK or DECLINED + */ +AP_DECLARE_HOOK(int,post_headers_parsed,(request_rec *r)) + +/* * post_read_request --- run right after read_request or internal_redirect, * and not run during any subrequests. */ diff --git a/modules/http2/h2_request.c b/modules/http2/h2_request.c index b6f13c4..597ff4f 100644 --- a/modules/http2/h2_request.c +++ b/modules/http2/h2_request.c @@ -282,6 +282,18 @@ request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c) * otherwise we get complains about port numbers. */ r->hostname = NULL; + + if (access_status = ap_run_post_headers_parsed(r)) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + "post_headers_parsed hooks returned failure"); + ap_die(access_status, r); + ap_update_child_status(c->sbh, SERVER_BUSY_LOG, r); + ap_run_log_transaction(r); + r = NULL; + goto traceout; + } + + ap_update_vhost_from_headers(r); /* we may have switched to another server */ diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c index dcf7988..6a38c72 100644 --- a/modules/mappers/mod_rewrite.c +++ b/modules/mappers/mod_rewrite.c @@ -416,7 +416,6 @@ static const char *rewritemap_mutex_type = "rewrite-map"; /* Optional functions imported from mod_ssl when loaded: */ static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *rewrite_ssl_lookup = NULL; -static APR_OPTIONAL_FN_TYPE(ssl_is_https) *rewrite_is_https = NULL; static char *escape_uri(apr_pool_t *p, const char *path); /* @@ -1923,8 +1922,7 @@ static char *lookup_variable(char *var, rewrite_ctx *ctx) case 5: if (!strcmp(var, "HTTPS")) { - int flag = rewrite_is_https && rewrite_is_https(r->connection); - return apr_pstrdup(r->pool, flag ? "on" : "off"); + return apr_pstrdup(r->pool, !strcasecmp("https", ap_http_scheme(r)) ? "on" : "off"); } break; @@ -4461,7 +4459,6 @@ static int post_config(apr_pool_t *p, } rewrite_ssl_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup); - rewrite_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https); return OK; } diff --git a/modules/metadata/mod_remoteip.c b/modules/metadata/mod_remoteip.c index 28e01df..e0a7051 100644 --- a/modules/metadata/mod_remoteip.c +++ b/modules/metadata/mod_remoteip.c @@ -20,6 +20,8 @@ #include "http_config.h" #include "http_connection.h" #include "http_protocol.h" +#include "http_request.h" +#include "http_vhost.h" #include "http_log.h" #include "apr_strings.h" #include "apr_lib.h" @@ -44,6 +46,13 @@ typedef struct { * from the proxy-via IP header value list) */ const char *proxies_header_name; + /** A header that may indicate user is using a + * HTTPS connection to the reverse-proxy, and + * the value that it must match for it to do so. + */ + const char *secure_header_name; + const char *secure_header_value; + unsigned short secure_port; /** A list of trusted proxies, ideally configured * with the most commonly encountered listed first */ @@ -85,6 +94,15 @@ static void *merge_remoteip_server_config(apr_pool_t *p, void *globalv, config->proxymatch_ip = server->proxymatch_ip ? server->proxymatch_ip : global->proxymatch_ip; + config->secure_header_name = server->secure_header_name + ? server->secure_header_name + : global->secure_header_name; + config->secure_header_value = server->secure_header_value + ? server->secure_header_value + : global->secure_header_value; + config->secure_port = server->secure_port + ? server->secure_port + : global->secure_port; return config; } @@ -106,6 +124,35 @@ static const char *proxies_header_name_set(cmd_parms *cmd, void *dummy, return NULL; } +static const char *secure_header_set(cmd_parms *cmd, void *dummy, + const char *name, const char *value) +{ + remoteip_config_t *config = ap_get_module_config(cmd->server->module_config, + &remoteip_module); + if (!name || !value) + return "SecureIndicatorHeader requires header name and valid value"; + + config->secure_header_name = name; + config->secure_header_value = value; + return NULL; +} + +static const char *secure_port_set(cmd_parms *cmd, void *dummy, const char *value) +{ + remoteip_config_t *config = ap_get_module_config(cmd->server->module_config, + &remoteip_module); + if (value) { + char *tail; + int intval; + intval = apr_strtoi64(value, &tail, 0); + if (errno == 0 && *tail == '\0' && intval > 0 && intval < 65536) { + config->secure_port = (unsigned short) intval; + return NULL; /* no error */ + } + } + return "SecureIndicatorSSLPort must be an integer between 0 and 65536"; +} + /* Would be quite nice if APR exported this */ /* apr:network_io/unix/sockaddr.c */ static int looks_like_ip(const char *ipstr) @@ -229,6 +276,7 @@ static int remoteip_modify_request(request_rec *r) char *proxy_ips = NULL; char *parse_remote; char *eos; + char *secure = NULL; unsigned char *addrbyte; /* If no RemoteIPInternalProxy, RemoteIPInternalProxyList, RemoteIPTrustedProxy @@ -394,6 +442,25 @@ static int remoteip_modify_request(request_rec *r) return OK; } + if (config->secure_header_name) { + secure = (char *) apr_table_get(r->headers_in, config->secure_header_name); + } + + if (secure) { + if (!strcmp(secure, config->secure_header_value)) { + apr_table_setn(r->subprocess_env, "HTTPS", "on"); + } + else { + secure = NULL; + } + /* Header is available. Unset even if no match. */ + apr_table_unset(r->headers_in, config->secure_header_name); + } + else { + secure = NULL; + } + apr_table_setn(r->notes, "remoteip-secure", secure ? "1" : "0"); + req->proxied_remote = remote; req->proxy_ips = proxy_ips; @@ -417,13 +484,76 @@ static int remoteip_modify_request(request_rec *r) ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, req->proxy_ips - ? "Using %s as client's IP by proxies %s" - : "Using %s as client's IP by internal proxies%s", + ? "Using %s as client's IP by %s proxies %s" + : "Using %s as client's IP by %s internal proxies %s", req->useragent_ip, + secure ? "HTTPS" : "HTTP", (req->proxy_ips ? req->proxy_ips : "")); return OK; } +static int remoteip_ssl_hook_Fixup(request_rec *r) +{ + request_rec *current_req = r; + /* for internal redirect via mod_rewrite and other handlers */ + int issecure = 0; + while (current_req) { + const char* secure = apr_table_get(current_req->notes, "remoteip-secure"); + if (secure) { + if (secure[0] == '1') + issecure = 1; + break; + } + current_req = current_req->prev; + } + + if (issecure) + apr_table_setn(r->subprocess_env, "HTTPS", "on"); + return OK; +} + +static const char* remoteip_read_scheme(const request_rec *r) +{ + request_rec *current_req = (request_rec *) r; + /* for internal redirect via mod_rewrite and other handlers */ + while (current_req) { + const char* secure = apr_table_get(current_req->notes, "remoteip-secure"); + if (secure) { + if (secure[0] == '1') + return "https"; + return NULL; + } + current_req = current_req->prev; + } + // fallback to other handlers + return NULL; +} + +static unsigned short remoteip_read_port(const request_rec *r) +{ + request_rec *current_req = (request_rec *) r; + while (current_req) { + const char* secure = apr_table_get(r->notes, "remoteip-secure"); + if (secure) { + if (secure[0] != '1') + return 0; + + remoteip_config_t *config = (remoteip_config_t *) + ap_get_module_config(r->server->module_config, &remoteip_module); + if (!config) { + /* will probably never happen */ + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + "remoteip-secure note is set, but configuration is missing"); + return 0; + } + return config->secure_port; + } + + current_req = current_req->prev; + } + return 0; +} + static const command_rec remoteip_cmds[] = { AP_INIT_TAKE1("RemoteIPHeader", header_name_set, NULL, RSRC_CONF, @@ -447,12 +577,21 @@ static const command_rec remoteip_cmds[] = RSRC_CONF | EXEC_ON_READ, "The filename to read the list of internal proxies, " "see the RemoteIPInternalProxy directive"), + AP_INIT_TAKE2("SecureIndicatorHeader", secure_header_set, NULL, RSRC_CONF, + "Specifies a request header and value that indicates a secure connection, " + "e.g. \"X-Forwarded-Proto https\" or \"X-Secure-Connection on\""), + AP_INIT_TAKE1("SecureIndicatorSSLPort", secure_port_set, NULL, RSRC_CONF, + "Port to be used for redirections if SecureIndicatorHeader is set, " + "Default is \"SecureInidcatorSSLPort 443\" "), { NULL } }; static void register_hooks(apr_pool_t *p) { - ap_hook_post_read_request(remoteip_modify_request, NULL, NULL, APR_HOOK_FIRST); + ap_hook_post_headers_parsed(remoteip_modify_request, NULL, NULL, APR_HOOK_FIRST); + ap_hook_http_scheme(remoteip_read_scheme, NULL, NULL, APR_HOOK_FIRST); + ap_hook_default_port(remoteip_read_port, NULL, NULL, APR_HOOK_FIRST); + ap_hook_fixups(remoteip_ssl_hook_Fixup, NULL,NULL, APR_HOOK_MIDDLE); } AP_DECLARE_MODULE(remoteip) = { diff --git a/server/protocol.c b/server/protocol.c index 63b358d..1dacd89 100644 --- a/server/protocol.c +++ b/server/protocol.c @@ -70,6 +70,7 @@ APR_HOOK_STRUCT( APR_HOOK_LINK(protocol_propose) APR_HOOK_LINK(protocol_switch) APR_HOOK_LINK(protocol_get) + APR_HOOK_LINK(post_headers_parsed) ) AP_DECLARE_DATA ap_filter_rec_t *ap_old_write_func = NULL; @@ -1352,6 +1353,16 @@ request_rec *ap_read_request(conn_rec *conn) apr_brigade_destroy(tmp_bb); + if (access_status = ap_run_post_headers_parsed(r)) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + "post_headers_parsed hooks returned failure"); + ap_die(access_status, r); + ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r); + ap_run_log_transaction(r); + r = NULL; + goto traceout; + } + /* update what we think the virtual host is based on the headers we've * now read. may update status. */ @@ -2303,3 +2314,5 @@ AP_IMPLEMENT_HOOK_RUN_FIRST(int,protocol_switch, (c, r, s, protocol), DECLINED) AP_IMPLEMENT_HOOK_RUN_FIRST(const char *,protocol_get, (const conn_rec *c), (c), NULL) +AP_IMPLEMENT_HOOK_RUN_ALL(int, post_headers_parsed, + (request_rec *r), (r), OK, DECLINED) diff --git a/server/vhost.c b/server/vhost.c index 1896595..dc41a4b 100644 --- a/server/vhost.c +++ b/server/vhost.c @@ -1001,7 +1001,9 @@ static void check_hostalias(request_rec *r) virthost_s = NULL; last_s = NULL; - port = r->connection->local_addr->port; + port = ap_default_port(r); + if (port == 0) + port = r->connection->local_addr->port; /* Recall that the name_chain is a list of server_addr_recs, some of * whose ports may not match. Also each server may appear more than @@ -1068,7 +1070,9 @@ static void check_serverpath(request_rec *r) name_chain *src; apr_port_t port; - port = r->connection->local_addr->port; + port = ap_default_port(r); + if (port == 0) + port = r->connection->local_addr->port; /* * This is in conjunction with the ServerPath code in http_core, so we