]> granicus.if.org Git - apache/commitdiff
Add StrictHostCheck
authorEric Covener <covener@apache.org>
Tue, 14 Aug 2018 21:47:22 +0000 (21:47 +0000)
committerEric Covener <covener@apache.org>
Tue, 14 Aug 2018 21:47:22 +0000 (21:47 +0000)
.. to allow ucnonfigured hostnames to be rejected.

The checks happen during NVH mapping and checks that the
mapped VH itself has the host as a name or alias.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1838055 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
docs/manual/mod/core.xml
include/http_core.h
include/http_vhost.h
server/core.c
server/protocol.c
server/vhost.c

diff --git a/CHANGES b/CHANGES
index 75d41c2da0c8e4efe1b3771a6fef09e08fb07c8d..0dfe56d11c09347819323bca3a3b966e80bd30f6 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,9 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.1
 
+  *) core: Add StrictHostCheck to allow ucnonfigured hostnames to be
+     rejected. [Eric Covener]
+
   *) mod_status: Cumulate CPU time of exited child processes in the
      "cu" and "cs" values. Add CPU time of the parent process to the
      "c" and "s" values.
index a315816330644446e1208da3437731e0ee4db45e..090719f8eb00877fe0b8d74bbec600d4f5d8f5f7 100644 (file)
@@ -5240,6 +5240,40 @@ as if 'QualifyRedirectURL ON' was configured.</compatibility>
 </usage>
 </directivesynopsis>
 
+<directivesynopsis>
+<name>StrictHostCheck</name>
+<description>Controls whether the server requires the requested hostname be
+             listed enumerated in the virtual host handling the request
+             </description>
+<syntax>StrictHostCheck ON|OFF</syntax>
+<default>StrictHostCheck OFF</default>
+<contextlist><context>server config</context><context>virtual host</context>
+</contextlist>
+<compatibility>Added in 2.5.1</compatibility>
 
+<usage>
+    <p>By default, the server will respond to requests for any hostname,
+    including requests addressed to unexpected or unconfigured hostnames. 
+    While this is convenient, it is sometimes desirable to limit what hostnames
+    a backend application handles since it will often generate self-referential
+    responses.</p>
+
+    <p>By setting <directive>StrictHostCheck</directive> to <em>ON</em>,
+    the server will return an HTTP 400 error if the requested hostname
+    hasn't been explicitly listed by either <directive module="core"
+    >ServerName</directive> or <directive module="core"
+    >ServerAlias</directive> in the virtual host that best matches the
+    details of the incoming connection.</p>
+
+   <p>This directive also allows matching of the requested hostname to hostnames
+   specified within the opening <directive module="core">VirtualHost</directive>
+   tag, which is a relatively obscure configuration mechanism that acts like
+   additional <directive module="core">ServerAlias</directive> entries.</p>
+
+   <p>This directive has no affect in non-default virtual hosts. The value
+   inherited from the global server configuration, or the default virtualhost 
+   for the ip:port the underlying connection, determine the effective value.</p>
+</usage>
+</directivesynopsis>
 
 </modulesynopsis>
index 3b43b38f3e79215dceec9f5063394bacd551cabd..fe3836fb344325b458958d76fe24eba6d4a98b1f 100644 (file)
@@ -770,6 +770,7 @@ typedef struct {
  
     apr_size_t   flush_max_threshold;
     apr_int32_t  flush_max_pipelined;
+    unsigned int strict_host_check;
 } core_server_config;
 
 /* for AddOutputFiltersByType in core.c */
index 3941c2b22c13f172b89eded13ca82a45c8cead87..91903cd58a9440e6a1c473e078f358d02c72ed88 100644 (file)
@@ -99,6 +99,19 @@ AP_DECLARE(void) ap_update_vhost_given_ip(conn_rec *conn);
  */
 AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r);
 
+/**
+ * Updates r->server with the best name-based virtual host match, within
+ * the chain of matching virtual hosts selected by ap_update_vhost_given_ip.
+ * @param r The current request
+ * @param require_match 1 to return an HTTP error if the requested hostname is
+ * not explicitly matched to a VirtualHost. 
+ * @return return HTTP_OK unless require_match was specified and the requested
+ * hostname did not match any ServerName, ServerAlias, or VirtualHost 
+ * address-spec.
+ */
+AP_DECLARE(int) ap_update_vhost_from_headers_ex(request_rec *r, int require_match);
+
+
 /**
  * Match the host in the header with the hostname of the server for this
  * request.
index ffaa647f4ba3c59f1a695af96513029ae15a69b6..d3f2d256b9e52f2a12b45c79549fef5b180b50ec 100644 (file)
@@ -525,6 +525,7 @@ static void *create_core_server_config(apr_pool_t *a, server_rec *s)
     conf->protocols = apr_array_make(a, 5, sizeof(const char *));
     conf->protocols_honor_order = -1;
     conf->async_filter = 0;
+    conf->strict_host_check= AP_CORE_CONFIG_UNSET; 
 
     return (void *)conf;
 }
@@ -620,6 +621,12 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv)
                                   ? virt->flush_max_pipelined
                                   : base->flush_max_pipelined;
 
+    conf->strict_host_check = (virt->strict_host_check != AP_CORE_CONFIG_UNSET)
+                              ? virt->strict_host_check 
+                              : base->strict_host_check;
+
+    AP_CORE_MERGE_FLAG(strict_host_check, conf, base, virt);
+
     return conf;
 }
 
@@ -1962,7 +1969,12 @@ static const char *set_qualify_redirect_url(cmd_parms *cmd, void *d_, int flag)
 
     return NULL;
 }
-
+static const char *set_core_server_flag(cmd_parms *cmd, void *s_, int flag)
+{
+    core_server_config *conf =
+        ap_get_core_module_config(cmd->server->module_config);
+    return ap_set_flag_slot(cmd, conf, flag);
+}
 static const char *set_override_list(cmd_parms *cmd, void *d_, int argc, char *const argv[])
 {
     core_dir_config *d = d_;
@@ -4816,7 +4828,10 @@ AP_INIT_TAKE2("CGIVar", set_cgi_var, NULL, OR_FILEINFO,
 AP_INIT_FLAG("QualifyRedirectURL", set_qualify_redirect_url, NULL, OR_FILEINFO,
              "Controls whether the REDIRECT_URL environment variable is fully "
              "qualified"),
-
+AP_INIT_FLAG("StrictHostCheck", set_core_server_flag, 
+             (void *)APR_OFFSETOF(core_server_config, strict_host_check),  
+             RSRC_CONF,
+             "Controls whether a hostname match is required"),
 AP_INIT_TAKE1("ForceType", ap_set_string_slot_lower,
        (void *)APR_OFFSETOF(core_dir_config, mime_type), OR_FILEINFO,
      "a mime type that overrides other configured type"),
@@ -5891,4 +5906,3 @@ AP_DECLARE_MODULE(core) = {
     core_cmds,                    /* command apr_table_t */
     register_hooks                /* register hooks */
 };
-
index f98707472de1e48e07bc46780220fd1048e26e5b..d57f64f65a0b2fdca04d1f34a6e9f95e615f1669 100644 (file)
@@ -1348,10 +1348,11 @@ request_rec *ap_read_request(conn_rec *conn)
     apr_bucket_brigade *tmp_bb;
     apr_socket_t *csd;
     apr_interval_time_t cur_timeout;
-
+    core_server_config *conf = NULL;
 
     request_rec *r = ap_create_request(conn);
 
+    conf = ap_get_core_module_config(r->server->module_config);
     tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
 
     ap_run_pre_read_request(r, conn);
@@ -1455,7 +1456,23 @@ request_rec *ap_read_request(conn_rec *conn)
     /* update what we think the virtual host is based on the headers we've
      * now read. may update status.
      */
-    ap_update_vhost_from_headers(r);
+    
+    access_status = ap_update_vhost_from_headers_ex(r, conf->strict_host_check == AP_CORE_CONFIG_ON);
+    if (conf->strict_host_check == AP_CORE_CONFIG_ON && access_status != HTTP_OK) { 
+         if (r->server == ap_server_conf) { 
+             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO()
+                           "Requested hostname '%s' did not match any ServerName/ServerAlias "
+                           "in the global server configuration ", r->hostname);
+         } else { 
+             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO()
+                           "Requested hostname '%s' did not match any ServerName/ServerAlias "
+                           "in the matching virtual host (default vhost for "
+                           "current connection is  %s:%u)", 
+                           r->hostname, r->server->defn_name, r->server->defn_line_number);
+         }
+         r->status = access_status;
+    }
+
     access_status = r->status;
 
     /* Toggle to the Host:-based vhost's timeout mode to fetch the
index 45f521269ebbf0661f12ebd138b77a837e78f71d..98b34cadb6e36692d8d73a030ae3654101285fb3 100644 (file)
@@ -35,6 +35,7 @@
 #include "http_vhost.h"
 #include "http_protocol.h"
 #include "http_core.h"
+#include "http_main.h"
 
 #if APR_HAVE_ARPA_INET_H
 #include <arpa/inet.h>
@@ -976,7 +977,13 @@ AP_DECLARE(int) ap_matches_request_vhost(request_rec *r, const char *host,
 }
 
 
-static void check_hostalias(request_rec *r)
+/*
+ * Updates r->server from ServerName/ServerAlias. Per the interaction
+ * of ip and name-based vhosts, it only looks in the best match from the
+ * connection-level ip-based matching.
+ * Returns HTTP_BAD_REQUEST if there was no match.
+ */
+static int update_server_from_aliases(request_rec *r)
 {
     /*
      * Even if the request has a Host: header containing a port we ignore
@@ -1053,11 +1060,18 @@ static void check_hostalias(request_rec *r)
         goto found;
     }
 
-    return;
+    if (r->server == ap_server_conf) { 
+        if (matches_aliases(ap_server_conf, host)) {
+            s = ap_server_conf;
+            goto found;
+        }
+    }
+    return HTTP_BAD_REQUEST;
 
 found:
     /* s is the first matching server, we're done */
     r->server = s;
+    return HTTP_OK;
 }
 
 
@@ -1074,7 +1088,7 @@ static void check_serverpath(request_rec *r)
      * This is in conjunction with the ServerPath code in http_core, so we
      * get the right host attached to a non- Host-sending request.
      *
-     * See the comment in check_hostalias about how each vhost can be
+     * See the comment in update_server_from_aliases about how each vhost can be
      * listed multiple times.
      */
 
@@ -1137,11 +1151,17 @@ static APR_INLINE const char *construct_host_header(request_rec *r,
 }
 
 AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r)
+{
+    ap_update_vhost_from_headers_ex(r, 0);
+}
+
+AP_DECLARE(int) ap_update_vhost_from_headers_ex(request_rec *r, int require_match)
 {
     core_server_config *conf = ap_get_core_module_config(r->server->module_config);
     const char *host_header = apr_table_get(r->headers_in, "Host");
     int is_v6literal = 0;
     int have_hostname_from_url = 0;
+    int rc = HTTP_OK;
 
     if (r->hostname) {
         /*
@@ -1154,8 +1174,8 @@ AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r)
     else if (host_header != NULL) {
         is_v6literal = fix_hostname(r, host_header, conf->http_conformance);
     }
-    if (r->status != HTTP_OK)
-        return;
+    if (!require_match && r->status != HTTP_OK)
+        return HTTP_OK;
 
     if (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE) {
         /*
@@ -1176,10 +1196,16 @@ AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r)
     /* check if we tucked away a name_chain */
     if (r->connection->vhost_lookup_data) {
         if (r->hostname)
-            check_hostalias(r);
+            rc = update_server_from_aliases(r);
         else
             check_serverpath(r);
     }
+    else if (require_match) { 
+        /* check the base server config */
+        rc = update_server_from_aliases(r);
+    }
+    
+    return rc;
 }
 
 /**