You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by wr...@apache.org on 2009/03/20 05:43:25 UTC

svn commit: r756323 - /httpd/sandbox/mod_remoteip/mod_remoteip.c

Author: wrowe
Date: Fri Mar 20 04:43:24 2009
New Revision: 756323

URL: http://svn.apache.org/viewvc?rev=756323&view=rev
Log:
Round out features by adding Internal and Trusted whitelist comparisons,
filtering out private IP's for Trusted proxies (but permitting them for
Internal proxies).  Further optimize and clear up the optimizations.

Modified:
    httpd/sandbox/mod_remoteip/mod_remoteip.c

Modified: httpd/sandbox/mod_remoteip/mod_remoteip.c
URL: http://svn.apache.org/viewvc/httpd/sandbox/mod_remoteip/mod_remoteip.c?rev=756323&r1=756322&r2=756323&view=diff
==============================================================================
--- httpd/sandbox/mod_remoteip/mod_remoteip.c (original)
+++ httpd/sandbox/mod_remoteip/mod_remoteip.c Fri Mar 20 04:43:24 2009
@@ -29,13 +29,24 @@
 module AP_MODULE_DECLARE_DATA remoteip_module;
 
 typedef struct {
+    /** A proxy IP mask to match */
+    apr_ipsubnet_t *ip;
+    /** Flagged if internal, otherwise an external trusted proxy */
+    int internal;
+} remoteip_proxymatch_t;
+
+typedef struct {
     /** The header to retrieve a proxy-via ip list */
-    const char *remoteip_header;
+    const char *header_name;
     /** A header to record the proxied IP's 
      * (removed as the physical connection and 
      * from the proxy-via ip header value list) 
      */
-    const char *remoteip_proxies_header;
+    const char *proxies_header_name;
+    /** A list of trusted proxies, ideally configured
+     *  with the most commonly encountered listed first
+     */
+    apr_array_header_t *proxymatch_ip;
 } remoteip_config_t;
 
 typedef struct {
@@ -56,8 +67,8 @@
 static void *create_remoteip_server_config(apr_pool_t *p, server_rec *s)
 {
     remoteip_config_t *config = apr_pcalloc(p, sizeof *config);
-    /* config->remoteip_header = NULL;
-     * config->remoteip_proxies_header = NULL;
+    /* config->header_name = NULL;
+     * config->proxies_header_name = NULL;
      */
     return config;
 }
@@ -70,30 +81,103 @@
     remoteip_config_t *config;
 
     config = (remoteip_config_t *) apr_palloc(p, sizeof(*config));
-    config->remoteip_header = server->remoteip_header
-                            ? server->remoteip_header
-                            : global->remoteip_header;
-    config->remoteip_proxies_header = server->remoteip_proxies_header
-                                    ? server->remoteip_proxies_header
-                                    : global->remoteip_proxies_header;
+    config->header_name = server->header_name
+                        ? server->header_name
+                        : global->header_name;
+    config->proxies_header_name = server->proxies_header_name
+                                ? server->proxies_header_name
+                                : global->proxies_header_name;
+    config->proxymatch_ip = server->proxymatch_ip
+                          ? server->proxymatch_ip
+                          : global->proxymatch_ip;
     return config;
 }
 
-static const char *remoteip_header_set(cmd_parms *cmd, void *dummy,
-                                       const char *arg)
+static const char *header_name_set(cmd_parms *cmd, void *dummy,
+                                   const char *arg)
 {
     remoteip_config_t *config = ap_get_module_config(cmd->server->module_config,
                                                      &remoteip_module);
-    config->remoteip_header = apr_pstrdup(cmd->pool, arg);
+    config->header_name = apr_pstrdup(cmd->pool, arg);
     return NULL;
 }
 
-static const char *remoteip_proxies_header_set(cmd_parms *cmd, void *dummy,
-                                       const char *arg)
+static const char *proxies_header_name_set(cmd_parms *cmd, void *dummy,
+                                           const char *arg)
 {
     remoteip_config_t *config = ap_get_module_config(cmd->server->module_config,
                                                      &remoteip_module);
-    config->remoteip_proxies_header = apr_pstrdup(cmd->pool, arg);
+    config->proxies_header_name = apr_pstrdup(cmd->pool, arg);
+    return NULL;
+}
+
+static const char *proxies_set(cmd_parms *cmd, void *internal,
+                               const char *arg)
+{
+    remoteip_config_t *config = ap_get_module_config(cmd->server->module_config,
+                                                     &remoteip_module);
+    remoteip_proxymatch_t *match;
+    char *ip = apr_pstrdup(cmd->temp_pool, arg);
+    char *s = ap_strchr(ip, '/');
+    apr_status_t rv;
+
+    if (!config->proxymatch_ip)
+        config->proxymatch_ip = apr_array_make(cmd->pool, 1, sizeof(*match));
+    match = (remoteip_proxymatch_t *) apr_array_push(config->proxymatch_ip);
+
+    if (s) {
+        *s++ = '\0';
+        rv = apr_ipsubnet_create(&match->ip, ip, s, cmd->pool);
+    }
+    else {
+        rv = apr_ipsubnet_create(&match->ip, ip, NULL, cmd->pool);
+    }
+
+    if (rv != APR_SUCCESS) {
+        char msgbuf[128];
+        apr_strerror(rv, msgbuf, sizeof(msgbuf));
+        return apr_pstrcat(cmd->pool, "RemoteIP: Error parsing IP ", arg, 
+                           " (", msgbuf, " error) for ", cmd->cmd->name, NULL);
+    }
+
+    return NULL;
+}
+
+static const char *proxylist_read(cmd_parms *cmd, void *internal,
+                                  const char *filename)
+{
+    remoteip_config_t *config = ap_get_module_config(cmd->server->module_config,
+                                                     &remoteip_module);
+    char lbuf[MAX_STRING_LEN];
+    char *arg;
+    char *args;
+    const char *errmsg;
+    ap_configfile_t *cfp;
+    apr_status_t rv;
+
+    filename = ap_server_root_relative(cmd->temp_pool, filename);
+    rv = ap_pcfg_openfile(&cfp, cmd->temp_pool, filename);
+    if (rv != APR_SUCCESS) {
+        return apr_psprintf(cmd->pool, "%s: Could not open file %s: %s",
+                            cmd->cmd->name, filename, 
+                            apr_strerror(rv, lbuf, sizeof(lbuf)));
+    }
+
+    while (!(ap_cfg_getline(lbuf, MAX_STRING_LEN, cfp))) {
+        args = lbuf;
+        while (*(arg = ap_getword_conf(cmd->temp_pool, &args)) != '\0') {
+            if (*arg == '#' || *arg == '\0')
+                break;
+            errmsg = proxies_set(cmd, internal, arg);
+            if (errmsg) {
+                errmsg = apr_psprintf(cmd->pool, "%s at line %d of %s", 
+                                      errmsg, cfp->line_number, filename);
+                return errmsg;
+            }
+        }
+    }
+
+    ap_cfg_closefile(cfp);
     return NULL;
 }
 
@@ -110,10 +194,11 @@
     apr_sockaddr_t *temp_sa;
 #endif
     apr_status_t rv;
-    char *remote = (char *) apr_table_get(r->headers_in, config->remoteip_header);
+    char *remote = (char *) apr_table_get(r->headers_in, config->header_name);
     char *proxy_ips = NULL;
     char *parse_remote;
     char *eos;
+    int internal = 0;
 
     apr_pool_userdata_get(&conn, "mod_remoteip-conn", c->pool);
 
@@ -145,8 +230,21 @@
 
     while (remote) {
 
-        /* TODO: verify c->remote_ip is trusted
+        /* verify c->remote_addr is trusted if there is a trusted proxy list
          */
+        if (config->proxymatch_ip) {
+            int i;
+            remoteip_proxymatch_t *match;
+            match = (remoteip_proxymatch_t *)config->proxymatch_ip->elts;
+            for (i = 0; i < config->proxymatch_ip->nelts; ++i) {
+                if (apr_ipsubnet_test(match[i].ip, c->remote_addr)) {
+                    internal = match[i].internal;
+                    break;
+                }
+            }
+            if (i && i >= config->proxymatch_ip->nelts)
+                break;
+        }
 
         if ((parse_remote = strrchr(remote, ',')) == NULL) {
             parse_remote = remote;
@@ -182,9 +280,9 @@
                            &temp_sa->sa.sin6.sin6_addr) > 0) {
             apr_sockaddr_vars_set(temp_sa, APR_INET6, temp_sa.port);
         }
+#endif
         else {
             rv = apr_get_netos_error();
-#endif
 #else /* !REMOTEIP_OPTIMIZED */
         /* We map as IPv4 rather than IPv6 for equivilant host names
          * or IPV4OVERIPV6 
@@ -195,8 +293,45 @@
         if (rv != APR_SUCCESS) {
 #endif
             ap_log_rerror(APLOG_MARK, APLOG_DEBUG,  rv, r,
-                          "Header %s value of %s doesn't appear to be a client IP",
-                          config->remoteip_header, parse_remote);
+                          "RemoteIP: Header %s value of %s cannot be parsed "
+                          "as a client IP",
+                          config->header_name, parse_remote);
+            if (remote)
+                *(remote + strlen(remote)) = ',';
+            else
+                remote = parse_remote;
+            break;
+        }
+
+        if (!internal
+              && ((temp_sa->family == APR_INET
+                         /* denying 127. loopback, 10., 172.240..., 192.168. 
+                          * private subnets, and Class D and E special subnets
+                          */
+                      && (temp_sa->sa.sin.sin_addr.s_net == 127
+                       || temp_sa->sa.sin.sin_addr.s_net == 10
+                       || (temp_sa->sa.sin.sin_addr.s_net == 172
+                        && temp_sa->sa.sin.sin_addr.s_host & 0xf0 == 0xf0)
+                       || (temp_sa->sa.sin.sin_addr.s_net == 192
+                        && temp_sa->sa.sin.sin_addr.s_host == 168)
+                       || (temp_sa->sa.sin.sin_addr.s_net == 169
+                        && temp_sa->sa.sin.sin_addr.s_host == 254)
+                       || temp_sa->sa.sin.sin_addr.s_net & 0xe0 == 0xe0))
+#if APR_HAVE_IPV6
+               || (temp_sa->family == APR_INET6
+                         /* we translated IPv4-over-IPv6-mapped addresses 
+                          * as IPv4 above, all that is left to deny are 
+                          * multicast, special subnets, etc etc, where the
+                          * high nibble of the address is 0 or f
+                          */
+                      && (temp_sa->sa.sin6.sin6_addr.s6_addr[0] & 0xf0 == 0x00
+                       || temp_sa->sa.sin6.sin6_addr.s6_addr[0] & 0xf0 == 0xf0))
+#endif
+        )) {
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG,  rv, r,
+                          "RemoteIP: Header %s value of %s appears to be "
+                          "a private IP or nonsensical.  Ignored",
+                          config->header_name, parse_remote);
             if (remote)
                 *(remote + strlen(remote)) = ',';
             else
@@ -212,56 +347,92 @@
         }
 
         /* Set remote_ip string */
-        if (proxy_ips)
-            proxy_ips = apr_pstrcat(r->pool, proxy_ips, ", ", c->remote_ip, NULL);
-        else 
-            proxy_ips = c->remote_ip;
+        if (!internal) {
+            if (proxy_ips)
+                proxy_ips = apr_pstrcat(r->pool, proxy_ips, ", ", 
+                                        c->remote_ip, NULL);
+            else
+                proxy_ips = c->remote_ip;
+        }
 
         c->remote_addr = temp_sa;
         apr_sockaddr_ip_get(&c->remote_ip, c->remote_addr);
     }
 
-    /* Fixups here, remote becomes Via, etc */
-
-    if (!proxy_ips)
+    /* Nothing happened? */
+    if (!conn || (c->remote_addr == conn->orig_addr))
         return OK;
 
+    /* Fixups here, remote becomes the new Via header value, etc 
+     * In the heavy operations above we used request scope, to limit
+     * conn pool memory growth on keepalives, so here we must scope
+     * the final results to the connection pool lifetime.
+     * To limit memory growth, we keep recycling the same buffer
+     * for the final apr_sockaddr_t in the remoteip conn rec.
+     */
     c->remote_ip = apr_pstrdup(c->pool, c->remote_ip);
-
     conn->proxied_ip = c->remote_ip;
     memcpy(&conn->proxied_addr, &temp_sa, sizeof(temp_sa));
     conn->proxied_addr.pool = c->pool;
-    conn->proxied_remote = apr_pstrdup(c->pool, remote);
-    conn->prior_remote = apr_pstrdup(c->pool, apr_table_get(r->headers_in, config->remoteip_header));
-    conn->proxy_ips = apr_pstrdup(c->pool, proxy_ips);
-
     c->remote_addr = &conn->proxied_addr;
 
+    if (remote)
+        remote = apr_pstrdup(c->pool, remote);
+    conn->proxied_remote = remote;
+    conn->prior_remote = apr_pstrdup(c->pool, apr_table_get(r->headers_in, 
+                                                      config->header_name));
+    if (proxy_ips)
+        proxy_ips = apr_pstrdup(c->pool, proxy_ips);
+    conn->proxy_ips = proxy_ips;
+
     /* Unset remote_host string DNS lookups */
     c->remote_host = NULL;
     c->remote_logname = NULL;
 
 ditto_request_rec:
     if (conn->proxied_remote)
-        apr_table_setn(r->headers_in, config->remoteip_header, conn->proxied_remote);
+        apr_table_setn(r->headers_in, config->header_name,
+                       conn->proxied_remote);
     else
-        apr_table_unset(r->headers_in, config->remoteip_header);
-    apr_table_setn(r->notes, "remoteip-proxy-ip-list", conn->proxy_ips);
-    if (config->remoteip_proxies_header)
-        apr_table_setn(r->headers_in, config->remoteip_proxies_header, conn->proxy_ips);
+        apr_table_unset(r->headers_in, config->header_name);
+    if (conn->proxy_ips) {
+        apr_table_setn(r->notes, "remoteip-proxy-ip-list", conn->proxy_ips);
+        if (config->proxies_header_name)
+            apr_table_setn(r->headers_in, config->proxies_header_name,
+                           conn->proxy_ips);
+    }
 
     ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, 0, r,
-                  "Using %s as client's IP by proxies %s",
+                  conn->proxy_ips 
+                      ? "Using %s as client's IP by proxies %s"
+                      : "Using %s as client's IP by internal proxies",
                   conn->proxied_ip, conn->proxy_ips);
     return OK;
 }
 
 static const command_rec remoteip_cmds[] =
 {
-    AP_INIT_TAKE1("RemoteIPHeader", remoteip_header_set, NULL, RSRC_CONF,
-                  "Specifies a request header to trust as the client IP, e.g. X-Forwarded-For."),
-    AP_INIT_TAKE1("RemoteIPProxiesHeader", remoteip_proxies_header_set, NULL, RSRC_CONF,
-                  "Specifies a request header record proxy IP's; if not given then do not record."),
+    AP_INIT_TAKE1("RemoteIPHeader", header_name_set, NULL, RSRC_CONF,
+                  "Specifies a request header to trust as the client IP, "
+                  "e.g. X-Forwarded-For"),
+    AP_INIT_TAKE1("RemoteIPProxiesHeader", proxies_header_name_set,
+                  NULL, RSRC_CONF,
+                  "Specifies a request header to record proxy IP's, "
+                  "e.g. X-Forwarded-By; if not given then do not record"),
+    AP_INIT_ITERATE("RemoteIPTrustedProxy", proxies_set, 0, RSRC_CONF,
+                    "Specifies one or more proxies which are trusted "
+                    "to present IP headers"),
+    AP_INIT_ITERATE("RemoteIPInternalProxy", proxies_set, (void*)1, RSRC_CONF,
+                    "Specifies one or more internal (transparent) proxies "
+                    "which are trusted to present IP headers"),
+    AP_INIT_TAKE1("RemoteIPTrustedProxyList", proxylist_read, 0,
+                  RSRC_CONF | EXEC_ON_READ,
+                  "The filename to read the list of trusted proxies, "
+                  "see the RemoteIPTrustedProxy directive"),
+    AP_INIT_TAKE1("RemoteIPInternalProxyList", proxylist_read, (void*)1,
+                  RSRC_CONF | EXEC_ON_READ,
+                  "The filename to read the list of internal proxies, "
+                  "see the RemoteIPInternalProxy directive"),
     { NULL }
 };