Advertisement
nateshoffner

Untitled

May 20th, 2013
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 18.29 KB | None | 0 0
  1. /* Licensed to the Apache Software Foundation (ASF) under one or more
  2.  * contributor license agreements.  See the NOTICE file distributed with
  3.  * this work for additional information regarding copyright ownership.
  4.  * The ASF licenses this file to You under the Apache License, Version 2.0
  5.  * (the "License"); you may not use this file except in compliance with
  6.  * the License.  You may obtain a copy of the License at
  7.  *
  8.  *     http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  *
  16.  * Derived from mod_remoteip.c.
  17.  * Default values for directives are hard-wired for CloudFlare defaults.
  18.  *
  19.  * Supported directives and defaults:
  20.  *
  21.  * CloudFlareIPHeader CF-Connecting-IP
  22.  * CloudFlareIPTrustedProxy 204.93.173.0/24
  23.  *
  24.  * Version 1.0.3
  25.  */
  26.  
  27. #include "ap_config.h"
  28. #include "ap_mmn.h"
  29. #include "httpd.h"
  30. #include "http_config.h"
  31. #include "http_connection.h"
  32. #include "http_protocol.h"
  33. #include "http_log.h"
  34. #include "apr_strings.h"
  35. #include "apr_lib.h"
  36. #define APR_WANT_BYTEFUNC
  37. #include "apr_want.h"
  38. #include "apr_network_io.h"
  39.  
  40. module AP_MODULE_DECLARE_DATA cloudflare_module;
  41.  
  42. #define CF_DEFAULT_IP_HEADER "CF-Connecting-IP"
  43. #define CF_DEFAULT_TRUSTED_PROXY {"204.93.240.0/24", "204.93.177.0/24", "199.27.128.0/21", "173.245.48.0/20", "103.22.200.0/22", "141.101.64.0/18","108.162.192.0/18","190.93.240.0/20"}
  44. #define CF_DEFAULT_TRUSTED_PROXY_COUNT 8
  45.  
  46. typedef struct {
  47.     /** A proxy IP mask to match */
  48.     apr_ipsubnet_t *ip;
  49.     /** Flagged if internal, otherwise an external trusted proxy */
  50.     void  *internal;
  51. } cloudflare_proxymatch_t;
  52.  
  53. typedef struct {
  54.     /** The header to retrieve a proxy-via ip list */
  55.     const char *header_name;
  56.     /** A header to record the proxied IP's
  57.      * (removed as the physical connection and
  58.      * from the proxy-via ip header value list)
  59.      */
  60.     const char *proxies_header_name;
  61.     /** A list of trusted proxies, ideally configured
  62.      *  with the most commonly encountered listed first
  63.      */
  64.    
  65.     int deny_all;
  66.     /** If this flag is set, only allow requests which originate from a CF Trusted Proxy IP.
  67.      * Return 403 otherwise.
  68.      */
  69.     apr_array_header_t *proxymatch_ip;
  70. } cloudflare_config_t;
  71.  
  72. typedef struct {
  73.     /** The previous proxy-via request header value */
  74.     const char *prior_remote;
  75.     /** The unmodified original ip and address */
  76.     const char *orig_ip;
  77.     apr_sockaddr_t *orig_addr;
  78.     /** The list of proxy ip's ignored as remote ip's */
  79.     const char *proxy_ips;
  80.     /** The remaining list of untrusted proxied remote ip's */
  81.     const char *proxied_remote;
  82.     /** The most recently modified ip and address record */
  83.     const char *proxied_ip;
  84.     apr_sockaddr_t proxied_addr;
  85. } cloudflare_conn_t;
  86.  
  87. static apr_status_t set_cf_default_proxies(apr_pool_t *p, cloudflare_config_t *config);
  88.  
  89. static void *create_cloudflare_server_config(apr_pool_t *p, server_rec *s)
  90. {
  91.     cloudflare_config_t *config = apr_pcalloc(p, sizeof *config);
  92.     /* config->header_name = NULL;
  93.      * config->proxies_header_name = NULL;
  94.      */
  95.     if (config == NULL) {
  96.         return NULL;
  97.     }
  98.     if (set_cf_default_proxies(p, config) != APR_SUCCESS) {
  99.         return NULL;
  100.     }
  101.     config->header_name = CF_DEFAULT_IP_HEADER;
  102.     return config;
  103. }
  104.  
  105. static void *merge_cloudflare_server_config(apr_pool_t *p, void *globalv,
  106.                                             void *serverv)
  107. {
  108.     cloudflare_config_t *global = (cloudflare_config_t *) globalv;
  109.     cloudflare_config_t *server = (cloudflare_config_t *) serverv;
  110.     cloudflare_config_t *config;
  111.  
  112.     config = (cloudflare_config_t *) apr_palloc(p, sizeof(*config));
  113.     config->header_name = server->header_name
  114.                         ? server->header_name
  115.                         : global->header_name;
  116.     config->proxies_header_name = server->proxies_header_name
  117.                                 ? server->proxies_header_name
  118.                                 : global->proxies_header_name;
  119.     config->proxymatch_ip = server->proxymatch_ip
  120.                           ? server->proxymatch_ip
  121.                           : global->proxymatch_ip;
  122.     return config;
  123. }
  124.  
  125. static const char *header_name_set(cmd_parms *cmd, void *dummy,
  126.                                    const char *arg)
  127. {
  128.     cloudflare_config_t *config = ap_get_module_config(cmd->server->module_config,
  129.                                                        &cloudflare_module);
  130.     config->header_name = apr_pstrdup(cmd->pool, arg);
  131.     return NULL;
  132. }
  133.  
  134. static const char *deny_all_set(cmd_parms *cmd, void *dummy)
  135. {
  136.     cloudflare_config_t *config = ap_get_module_config(cmd->server->module_config,
  137.                                                        &cloudflare_module);
  138.     config->deny_all = 1;
  139.     return NULL;
  140. }
  141.  
  142. /* Would be quite nice if APR exported this */
  143. /* apr:network_io/unix/sockaddr.c */
  144. static int looks_like_ip(const char *ipstr)
  145. {
  146.     if (ap_strchr_c(ipstr, ':')) {
  147.         /* definitely not a hostname; assume it is intended to be an IPv6 address */
  148.         return 1;
  149.     }
  150.  
  151.     /* simple IPv4 address string check */
  152.     while ((*ipstr == '.') || apr_isdigit(*ipstr))
  153.         ipstr++;
  154.     return (*ipstr == '\0');
  155. }
  156.  
  157. static apr_status_t set_cf_default_proxies(apr_pool_t *p, cloudflare_config_t *config)
  158. {
  159.      apr_status_t rv;
  160.      cloudflare_proxymatch_t *match;
  161.      int i;
  162.      char *proxies[] = CF_DEFAULT_TRUSTED_PROXY;
  163.  
  164.      for (i=0; i<CF_DEFAULT_TRUSTED_PROXY_COUNT; i++) {
  165.          char *ip = apr_pstrdup(p, proxies[i]);
  166.          char *s = ap_strchr(ip, '/');
  167.  
  168.          if (s) {
  169.              *s++ = '\0';
  170.          }
  171.          if (!config->proxymatch_ip) {
  172.              config->proxymatch_ip = apr_array_make(p, 1, sizeof(*match));
  173.          }
  174.          
  175.          match = (cloudflare_proxymatch_t *) apr_array_push(config->proxymatch_ip);
  176.          rv = apr_ipsubnet_create(&match->ip, ip, s, p);
  177.      }
  178.      return rv;
  179. }
  180.  
  181. static const char *proxies_set(cmd_parms *cmd, void *internal,
  182.                                const char *arg)
  183. {
  184.     cloudflare_config_t *config = ap_get_module_config(cmd->server->module_config,
  185.                                                        &cloudflare_module);
  186.     cloudflare_proxymatch_t *match;
  187.     apr_status_t rv;
  188.     char *ip = apr_pstrdup(cmd->temp_pool, arg);
  189.     char *s = ap_strchr(ip, '/');
  190.     if (s)
  191.         *s++ = '\0';
  192.  
  193.     if (!config->proxymatch_ip)
  194.         config->proxymatch_ip = apr_array_make(cmd->pool, 1, sizeof(*match));
  195.     match = (cloudflare_proxymatch_t *) apr_array_push(config->proxymatch_ip);
  196.     match->internal = internal;
  197.  
  198.     if (looks_like_ip(ip)) {
  199.         /* Note s may be null, that's fine (explicit host) */
  200.         rv = apr_ipsubnet_create(&match->ip, ip, s, cmd->pool);
  201.     }
  202.     else
  203.     {
  204.         apr_sockaddr_t *temp_sa;
  205.  
  206.         if (s) {
  207.             return apr_pstrcat(cmd->pool, "RemoteIP: Error parsing IP ", arg,
  208.                                " the subnet /", s, " is invalid for ",
  209.                                cmd->cmd->name, NULL);
  210.         }
  211.  
  212.         rv = apr_sockaddr_info_get(&temp_sa,  ip, APR_UNSPEC, 0,
  213.                                    APR_IPV4_ADDR_OK, cmd->temp_pool);
  214.         while (rv == APR_SUCCESS)
  215.         {
  216.             apr_sockaddr_ip_get(&ip, temp_sa);
  217.             rv = apr_ipsubnet_create(&match->ip, ip, NULL, cmd->pool);
  218.             if (!(temp_sa = temp_sa->next))
  219.                 break;
  220.             match = (cloudflare_proxymatch_t *)
  221.                     apr_array_push(config->proxymatch_ip);
  222.             match->internal = internal;
  223.         }
  224.     }
  225.  
  226.     if (rv != APR_SUCCESS) {
  227.         char msgbuf[128];
  228.         apr_strerror(rv, msgbuf, sizeof(msgbuf));
  229.         return apr_pstrcat(cmd->pool, "RemoteIP: Error parsing IP ", arg,
  230.                            " (", msgbuf, " error) for ", cmd->cmd->name, NULL);
  231.     }
  232.  
  233.     return NULL;
  234. }
  235.  
  236. static int cloudflare_modify_connection(request_rec *r)
  237. {
  238.     conn_rec *c = r->connection;
  239.     cloudflare_config_t *config = (cloudflare_config_t *)
  240.         ap_get_module_config(r->server->module_config, &cloudflare_module);
  241.  
  242.     cloudflare_conn_t *conn;
  243. #ifdef REMOTEIP_OPTIMIZED
  244.     apr_sockaddr_t temp_sa_buff;
  245.     apr_sockaddr_t *temp_sa = &temp_sa_buff;
  246. #else
  247.     apr_sockaddr_t *temp_sa;
  248. #endif
  249.     apr_status_t rv;
  250.     char *remote = (char *) apr_table_get(r->headers_in, config->header_name);
  251.     char *proxy_ips = NULL;
  252.     char *parse_remote;
  253.     char *eos;
  254.     unsigned char *addrbyte;
  255.     void *internal = NULL;
  256.  
  257.     apr_pool_userdata_get((void*)&conn, "mod_cloudflare-conn", c->pool);
  258.  
  259.     if (conn) {
  260.         if (remote && (strcmp(remote, conn->prior_remote) == 0)) {
  261.             /* TODO: Recycle r-> overrides from previous request
  262.              */
  263.             goto ditto_request_rec;
  264.         }
  265.         else {
  266.             /* TODO: Revert connection from previous request
  267.              */
  268.             c->remote_addr = conn->orig_addr;
  269.             c->remote_ip = (char *) conn->orig_ip;
  270.         }
  271.     }
  272.  
  273.     /* Deny requests that do not have a CloudFlareRemoteIPHeader set when
  274.      * DenyAllButCloudFlare is set. Do not modify the request otherwise and
  275.      * return early.
  276.      */
  277.     if (!remote) {
  278.         if (config->deny_all) {
  279.             return 403;
  280.         }
  281.  
  282.         return OK;
  283.     }
  284.  
  285.     remote = apr_pstrdup(r->pool, remote);
  286.  
  287. #ifdef REMOTEIP_OPTIMIZED
  288.     memcpy(temp_sa, c->remote_addr, sizeof(*temp_sa));
  289.     temp_sa->pool = r->pool;
  290. #else
  291.     temp_sa = c->remote_addr;
  292. #endif
  293.  
  294.     while (remote) {
  295.  
  296.         /* verify c->remote_addr is trusted if there is a trusted proxy list
  297.          */
  298.         if (config->proxymatch_ip) {
  299.             int i;
  300.             cloudflare_proxymatch_t *match;
  301.             match = (cloudflare_proxymatch_t *)config->proxymatch_ip->elts;
  302.             for (i = 0; i < config->proxymatch_ip->nelts; ++i) {
  303.                 if (apr_ipsubnet_test(match[i].ip, c->remote_addr)) {
  304.                     internal = match[i].internal;
  305.                     break;
  306.                 }
  307.             }
  308.             if (i && i >= config->proxymatch_ip->nelts) {
  309.                 if (config->deny_all) {
  310.                     return 403;
  311.                 } else {
  312.                     break;
  313.                 }
  314.             }
  315.         }
  316.  
  317.         if ((parse_remote = strrchr(remote, ',')) == NULL) {
  318.             parse_remote = remote;
  319.             remote = NULL;
  320.         }
  321.         else {
  322.             *(parse_remote++) = '\0';
  323.         }
  324.  
  325.         while (*parse_remote == ' ')
  326.             ++parse_remote;
  327.  
  328.         eos = parse_remote + strlen(parse_remote) - 1;
  329.         while (eos >= parse_remote && *eos == ' ')
  330.             *(eos--) = '\0';
  331.  
  332.         if (eos < parse_remote) {
  333.             if (remote)
  334.                 *(remote + strlen(remote)) = ',';
  335.             else
  336.                 remote = parse_remote;
  337.             break;
  338.         }
  339.  
  340. #ifdef REMOTEIP_OPTIMIZED
  341.         /* Decode remote_addr - sucks; apr_sockaddr_vars_set isn't 'public' */
  342.         if (inet_pton(AF_INET, parse_remote,
  343.                       &temp_sa->sa.sin.sin_addr) > 0) {
  344.             apr_sockaddr_vars_set(temp_sa, APR_INET, temp_sa.port);
  345.         }
  346. #if APR_HAVE_IPV6
  347.         else if (inet_pton(AF_INET6, parse_remote,
  348.                            &temp_sa->sa.sin6.sin6_addr) > 0) {
  349.             apr_sockaddr_vars_set(temp_sa, APR_INET6, temp_sa.port);
  350.         }
  351. #endif
  352.         else {
  353.             rv = apr_get_netos_error();
  354. #else /* !REMOTEIP_OPTIMIZED */
  355.         /* We map as IPv4 rather than IPv6 for equivilant host names
  356.          * or IPV4OVERIPV6
  357.          */
  358.         rv = apr_sockaddr_info_get(&temp_sa,  parse_remote,
  359.                                    APR_UNSPEC, temp_sa->port,
  360.                                    APR_IPV4_ADDR_OK, r->pool);
  361.         if (rv != APR_SUCCESS) {
  362. #endif
  363.             ap_log_rerror(APLOG_MARK, APLOG_DEBUG,  rv, r,
  364.                           "RemoteIP: Header %s value of %s cannot be parsed "
  365.                           "as a client IP",
  366.                           config->header_name, parse_remote);
  367.             if (remote)
  368.                 *(remote + strlen(remote)) = ',';
  369.             else
  370.                 remote = parse_remote;
  371.             break;
  372.         }
  373.  
  374.         addrbyte = (unsigned char *) &temp_sa->sa.sin.sin_addr;
  375.  
  376.         /* For intranet (Internal proxies) ignore all restrictions below */
  377.         if (!internal
  378.               && ((temp_sa->family == APR_INET
  379.                    /* For internet (non-Internal proxies) deny all
  380.                     * RFC3330 designated local/private subnets:
  381.                     * 10.0.0.0/8   169.254.0.0/16  192.168.0.0/16
  382.                     * 127.0.0.0/8  172.16.0.0/12
  383.                     */
  384.                       && (addrbyte[0] == 10
  385.                        || addrbyte[0] == 127
  386.                        || (addrbyte[0] == 169 && addrbyte[1] == 254)
  387.                        || (addrbyte[0] == 172 && (addrbyte[1] & 0xf0) == 16)
  388.                        || (addrbyte[0] == 192 && addrbyte[1] == 168)))
  389. #if APR_HAVE_IPV6
  390.                || (temp_sa->family == APR_INET6
  391.                    /* For internet (non-Internal proxies) we translated
  392.                     * IPv4-over-IPv6-mapped addresses as IPv4, above.
  393.                     * Accept only Global Unicast 2000::/3 defined by RFC4291
  394.                     */
  395.                       && ((temp_sa->sa.sin6.sin6_addr.s6_addr[0] & 0xe0) != 0x20))
  396. #endif
  397.         )) {
  398.             ap_log_rerror(APLOG_MARK, APLOG_DEBUG,  rv, r,
  399.                           "RemoteIP: Header %s value of %s appears to be "
  400.                           "a private IP or nonsensical.  Ignored",
  401.                           config->header_name, parse_remote);
  402.             if (remote)
  403.                 *(remote + strlen(remote)) = ',';
  404.             else
  405.                 remote = parse_remote;
  406.             break;
  407.         }
  408.  
  409.         if (!conn) {
  410.             conn = (cloudflare_conn_t *) apr_palloc(c->pool, sizeof(*conn));
  411.             apr_pool_userdata_set(conn, "mod_cloudflare-conn", NULL, c->pool);
  412.             conn->orig_addr = c->remote_addr;
  413.             conn->orig_ip = c->remote_ip;
  414.         }
  415.  
  416.         /* Set remote_ip string */
  417.         if (!internal) {
  418.             if (proxy_ips)
  419.                 proxy_ips = apr_pstrcat(r->pool, proxy_ips, ", ",
  420.                                         c->remote_ip, NULL);
  421.             else
  422.                 proxy_ips = c->remote_ip;
  423.         }
  424.  
  425.         c->remote_addr = temp_sa;
  426.         apr_sockaddr_ip_get(&c->remote_ip, c->remote_addr);
  427.     }
  428.  
  429.     /* Nothing happened? */
  430.     if (!conn || (c->remote_addr == conn->orig_addr))
  431.         return OK;
  432.  
  433.     /* Fixups here, remote becomes the new Via header value, etc
  434.      * In the heavy operations above we used request scope, to limit
  435.      * conn pool memory growth on keepalives, so here we must scope
  436.      * the final results to the connection pool lifetime.
  437.      * To limit memory growth, we keep recycling the same buffer
  438.      * for the final apr_sockaddr_t in the remoteip conn rec.
  439.      */
  440.     c->remote_ip = apr_pstrdup(c->pool, c->remote_ip);
  441.     conn->proxied_ip = c->remote_ip;
  442.     memcpy(&conn->proxied_addr, temp_sa, sizeof(*temp_sa));
  443.     conn->proxied_addr.pool = c->pool;
  444.     c->remote_addr = &conn->proxied_addr;
  445.  
  446.     if (remote)
  447.         remote = apr_pstrdup(c->pool, remote);
  448.     conn->proxied_remote = remote;
  449.     conn->prior_remote = apr_pstrdup(c->pool, apr_table_get(r->headers_in,
  450.                                                       config->header_name));
  451.     if (proxy_ips)
  452.         proxy_ips = apr_pstrdup(c->pool, proxy_ips);
  453.     conn->proxy_ips = proxy_ips;
  454.  
  455.     /* Unset remote_host string DNS lookups */
  456.     c->remote_host = NULL;
  457.     c->remote_logname = NULL;
  458.  
  459. ditto_request_rec:
  460.  
  461.     // Why do we unset the headers here?
  462.     //if (conn->proxied_remote) {
  463.     //    apr_table_setn(r->headers_in, config->header_name, conn->proxied_remote);
  464.     //} else {
  465.     //    apr_table_unset(r->headers_in, config->header_name);
  466.     // }
  467.  
  468.     if (conn->proxy_ips) {
  469.         apr_table_setn(r->notes, "cloudflare-proxy-ip-list", conn->proxy_ips);
  470.         if (config->proxies_header_name)
  471.             apr_table_setn(r->headers_in, config->proxies_header_name,
  472.                            conn->proxy_ips);
  473.     }
  474.  
  475.     ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, 0, r,
  476.                   conn->proxy_ips
  477.                       ? "Using %s as client's IP by proxies %s"
  478.                       : "Using %s as client's IP by internal proxies",
  479.                   conn->proxied_ip, conn->proxy_ips);
  480.     return OK;
  481. }
  482.  
  483. static const command_rec cloudflare_cmds[] =
  484. {
  485.     AP_INIT_TAKE1("CloudFlareRemoteIPHeader", header_name_set, NULL, RSRC_CONF,
  486.                   "Specifies a request header to trust as the client IP, "
  487.                   "Overrides the default of CF-Connecting-IP"),
  488.     AP_INIT_ITERATE("CloudFlareRemoteIPTrustedProxy", proxies_set, 0, RSRC_CONF,
  489.                     "Specifies one or more proxies which are trusted "
  490.                     "to present IP headers. Overrides the defaults."),
  491.     AP_INIT_NO_ARGS("DenyAllButCloudFlare", deny_all_set, NULL, RSRC_CONF,
  492.                     "Return a 403 status to all requests which do not originate from "
  493.                     "a CloudFlareRemoteIPTrustedProxy."),
  494.     { NULL }
  495. };
  496.  
  497. static void register_hooks(apr_pool_t *p)
  498. {
  499.     // We need to run very early so as to not trip up mod_security.
  500.     // Hence, this little trick, as mod_security runs at APR_HOOK_REALLY_FIRST.
  501.     ap_hook_post_read_request(cloudflare_modify_connection, NULL, NULL, APR_HOOK_REALLY_FIRST - 10);
  502. }
  503.  
  504. module AP_MODULE_DECLARE_DATA cloudflare_module = {
  505.     STANDARD20_MODULE_STUFF,
  506.     NULL,                            /* create per-directory config structure */
  507.     NULL,                            /* merge per-directory config structures */
  508.     create_cloudflare_server_config, /* create per-server config structure */
  509.     merge_cloudflare_server_config,  /* merge per-server config structures */
  510.     cloudflare_cmds,                 /* command apr_table_t */
  511.     register_hooks                   /* register hooks */
  512. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement