]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
mod_proxy, mod_proxy_http: Support remote https proxies
authorJim Jagielski <jim@apache.org>
Mon, 15 Feb 2010 19:54:41 +0000 (19:54 +0000)
committerJim Jagielski <jim@apache.org>
Mon, 15 Feb 2010 19:54:41 +0000 (19:54 +0000)
by using HTTP CONNECT.
PR 19188.  [Philippe Dutrueux <lilas evidian.com>, Rainer Jung]
Backport of r909323, r910079, r910081, r910124

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x@910322 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
STATUS
docs/manual/mod/mod_proxy.xml
docs/manual/mod/mod_proxy_connect.xml
docs/manual/mod/mod_proxy_http.xml
include/ap_mmn.h
modules/proxy/mod_proxy.h
modules/proxy/proxy_util.c

diff --git a/CHANGES b/CHANGES
index e02ba7dece326553483ae66663ed95c31c223933..10b118b792c9c65dd23509b33869e66f591aed34 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -9,6 +9,10 @@ Changes with Apache 2.2.15
      access control is still vulnerable, unless using OpenSSL >= 0.9.8l.
      [Joe Orton, Ruediger Pluem, Hartmut Keil <Hartmut.Keil adnovum.ch>]
 
+  *) mod_proxy, mod_proxy_http: Support remote https proxies
+     by using HTTP CONNECT.
+     PR 19188.  [Philippe Dutrueux <lilas evidian.com>, Rainer Jung]
+
   *) worker: Don't report server has reached MaxClients until it has.
      Add message when server gets within MinSpareThreads of MaxClients.
      PR 46996.  [Dan Poirier]
diff --git a/STATUS b/STATUS
index 9603f84995eb92e648a907afcde7b93d148617d7..a6b1afab38b055e0b5e1670085b0890cf2f28adc 100644 (file)
--- a/STATUS
+++ b/STATUS
@@ -87,17 +87,6 @@ RELEASE SHOWSTOPPERS:
 PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
   [ start all new proposals below, under PATCHES PROPOSED. ]
 
-  * mod_proxy: Allow https to a remote forward proxy by issuing an HTTP
-    CONNECT request. This adds a CONNECT client (sending a connect request).
-    It is not the same as mod_proxy_connect, which is a CONNECT server
-    (responding to connect requests).
-    PR: https://issues.apache.org/bugzilla/show_bug.cgi?id=19188
-    Trunk patches: http://svn.apache.org/viewvc?rev=909323&view=rev
-                   http://svn.apache.org/viewvc?rev=910079&view=rev
-                   http://svn.apache.org/viewvc?rev=910081&view=rev
-                   http://svn.apache.org/viewvc?rev=910124&view=rev
-    2.2.x patch: http://people.apache.org/~rjung/patches/bz19188-proxyremote-https-v2.patch
-    +1: rjung, minfrin, jim
 
 
 PATCHES PROPOSED TO BACKPORT FROM TRUNK:
index 2cbbc1e34aadc6744e396273f1f726c563140eed..f683c0fc07e66286bce2cfa6ac865b0450abdc93 100644 (file)
@@ -475,8 +475,9 @@ request</description>
     </example>
 
     <p><var>scheme</var> is effectively the protocol that should be used to
-    communicate with the remote server; only <code>http</code> is supported by
-    this module.</p>
+    communicate with the remote server; only <code>http</code> and <code>https</code>
+    are supported by this module. When using <code>https</code>, the requests
+    are forwarded through the remote proxy using the HTTP CONNECT method.</p>
 
     <example><title>Example</title>
       ProxyRemote http://goodguys.example.com/ http://mirrorguys.example.com:8000<br />
index ed8f07d8454d78c9c371d2a77443dfe18ebb329a..7a3aee6eeefef24bab15203a37a1de3796b47faf 100644 (file)
     requests, <module>mod_proxy</module> and
     <module>mod_proxy_connect</module> have to be present in the server.</p>
 
+    <p>CONNECT is also used, when the server needs to send an HTTPS request
+    through a forward proxy. In this case the server acts as a CONNECT client.
+    This functionality is part of <module>mod_proxy</module> and
+    <module>mod_proxy_connect</module> is not needed in this case.</p>
+
     <note type="warning"><title>Warning</title>
       <p>Do not enable proxying until you have <a
       href="mod_proxy.html#access">secured your server</a>. Open proxy
index cbd77651eaf9af90c199b94f745ab4726a8df366..92e16251601acfbbfe8954bac601ab3d044ed47c 100644 (file)
@@ -32,7 +32,7 @@
 <summary>
     <p>This module <em>requires</em> the service of <module
     >mod_proxy</module>. It provides the features used for
-    proxying HTTP requests. <module>mod_proxy_http</module>
+    proxying HTTP and HTTPS requests. <module>mod_proxy_http</module>
     supports HTTP/0.9, HTTP/1.0 and HTTP/1.1. It does <em>not</em>
     provide any caching abilities. If you want to set up a caching
     proxy, you might want to use the additional service of the
index c58660db48e8877ec6c3e3b9f149a6c570e92733..eef610ab671b0505e7e73b973b312b6a8aef282c 100644 (file)
  * 20051115.22 (2.2.12) Add ap_escape_html2 API, with additional option
  * 20051115.23 (2.2.12) Add ap_open_piped_log_ex API, with cmdtype option,
  *                      and conditional cmdtype member of piped_log struct
+ * 20051115.24 (2.2.15) Add forward member to proxy_conn_rec
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503232UL /* "AP22" */
 #ifndef MODULE_MAGIC_NUMBER_MAJOR
 #define MODULE_MAGIC_NUMBER_MAJOR 20051115
 #endif
-#define MODULE_MAGIC_NUMBER_MINOR 23                    /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 24                    /* 0...n */
 
 /**
  * Determine if the server's current MODULE_MAGIC_NUMBER is at least a
index 04b710660aa94f63c198153e9ec76354cb91a964..358248f1353632201ba61a69f49beea7de29b5fc 100644 (file)
@@ -247,6 +247,7 @@ typedef struct {
                              * which the backend currently answers. */
     int          need_flush;/* Flag to decide whether we need to flush the
                              * filter chain or not */
+    void         *forward;  /* opaque forward proxy data */
 } proxy_conn_rec;
 
 typedef struct {
index b835832d059690dc3dd52dfd47d6da6c4e502952..a70a8758b076a57b526a73910ea47f4ac114f733 100644 (file)
 #define apr_socket_create apr_socket_create_ex
 #endif
 
+/*
+ * Opaque structure containing target server info when
+ * using a forward proxy.
+ * Up to now only used in combination with HTTP CONNECT.
+ */
+typedef struct {
+    int          use_http_connect; /* Use SSL Tunneling via HTTP CONNECT */
+    const char   *target_host;     /* Target hostname */
+    apr_port_t   target_port;      /* Target port */
+    const char   *proxy_auth;      /* Proxy authorization */
+} forward_info;
+
 /* Global balancer counter */
 int PROXY_DECLARE_DATA proxy_lb_workers = 0;
 static int lb_workers_limit = 0;
@@ -2085,6 +2097,34 @@ ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,
         if (proxyname) {
             conn->hostname = apr_pstrdup(conn->pool, proxyname);
             conn->port = proxyport;
+            /*
+             * If we have a forward proxy and the protocol is HTTPS,
+             * then we need to prepend a HTTP CONNECT request before
+             * sending our actual HTTPS requests.
+             * Save our real backend data for using it later during HTTP CONNECT.
+             */
+            if (conn->is_ssl) {
+                const char *proxy_auth;
+
+                forward_info *forward = apr_pcalloc(conn->pool, sizeof(forward_info));
+                conn->forward = forward;
+                forward->use_http_connect = 1;
+                forward->target_host = apr_pstrdup(conn->pool, uri->hostname);
+                forward->target_port = uri->port;
+                /* Do we want to pass Proxy-Authorization along?
+                 * If we haven't used it, then YES
+                 * If we have used it then MAYBE: RFC2616 says we MAY propagate it.
+                 * So let's make it configurable by env.
+                 * The logic here is the same used in mod_proxy_http.
+                 */
+                proxy_auth = apr_table_get(r->headers_in, "Proxy-Authorization");
+                if (proxy_auth != NULL &&
+                    proxy_auth[0] != '\0' &&
+                    r->user == NULL && /* we haven't yet authenticated */
+                    apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) {
+                    forward->proxy_auth = apr_pstrdup(conn->pool, proxy_auth);
+                }
+            }
         }
         else {
             conn->hostname = apr_pstrdup(conn->pool, uri->hostname);
@@ -2224,6 +2264,102 @@ static int is_socket_connected(apr_socket_t *sock)
 }
 #endif /* USE_ALTERNATE_IS_CONNECTED */
 
+
+/*
+ * Send a HTTP CONNECT request to a forward proxy.
+ * The proxy is given by "backend", the target server
+ * is contained in the "forward" member of "backend".
+ */
+static apr_status_t send_http_connect(proxy_conn_rec *backend,
+                                      server_rec *s)
+{
+    int status;
+    apr_size_t nbytes;
+    apr_size_t left;
+    int complete = 0;
+    char buffer[HUGE_STRING_LEN];
+    char drain_buffer[HUGE_STRING_LEN];
+    forward_info *forward = (forward_info *)backend->forward;
+    int len = 0;
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                 "proxy: CONNECT: sending the CONNECT request for %s:%d "
+                 "to the remote proxy %pI (%s)",
+                 forward->target_host, forward->target_port,
+                 backend->addr, backend->hostname);
+    /* Create the CONNECT request */
+    nbytes = apr_snprintf(buffer, sizeof(buffer),
+                          "CONNECT %s:%d HTTP/1.0" CRLF,
+                          forward->target_host, forward->target_port);
+    /* Add proxy authorization from the initial request if necessary */
+    if (forward->proxy_auth != NULL) {
+        nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes,
+                               "Proxy-Authorization: %s" CRLF,
+                               forward->proxy_auth);
+    }
+    /* Set a reasonable agent and send everything */
+    nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes,
+                           "Proxy-agent: %s" CRLF CRLF,
+                           ap_get_server_banner());
+    apr_socket_send(backend->sock, buffer, &nbytes);
+
+    /* Receive the whole CONNECT response */
+    left = sizeof(buffer) - 1;
+    /* Read until we find the end of the headers or run out of buffer */
+    do {
+        nbytes = left;
+        status = apr_socket_recv(backend->sock, buffer + len, &nbytes);
+        len += nbytes;
+        left -= nbytes;
+        buffer[len] = '\0';
+        if (strstr(buffer + len - nbytes, "\r\n\r\n") != NULL) {
+            complete = 1;
+            break;
+        }
+    } while (status == APR_SUCCESS && left > 0);
+    /* Drain what's left */
+    if (!complete) {
+        nbytes = sizeof(drain_buffer) - 1;
+        while (status == APR_SUCCESS && nbytes) {
+            status = apr_socket_recv(backend->sock, drain_buffer, &nbytes);
+            buffer[nbytes] = '\0';
+            nbytes = sizeof(drain_buffer) - 1;
+            if (strstr(drain_buffer, "\r\n\r\n") != NULL) {
+                complete = 1;
+                break;
+            }
+        }
+    }
+
+    /* Check for HTTP_OK response status */
+    if (status == APR_SUCCESS) {
+        int major, minor;
+        /* Only scan for three character status code */
+        char code_str[4];
+
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                     "send_http_connect: response from the forward proxy: %s",
+                     buffer);
+
+        /* Extract the returned code */
+        if (sscanf(buffer, "HTTP/%u.%u %3s", &major, &minor, code_str) == 3) {
+            status = atoi(code_str);
+            if (status == HTTP_OK) {
+                status = APR_SUCCESS;
+            }
+            else {
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+                             "send_http_connect: the forward proxy returned code is '%s'",
+                             code_str);
+            status = APR_INCOMPLETE;
+            }
+        }
+    }
+
+    return(status);
+}
+
+
 PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
                                             proxy_conn_rec *conn,
                                             proxy_worker *worker,
@@ -2336,7 +2472,33 @@ PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
              apr_socket_timeout_set(newsock, s->timeout);
         }
 
-        conn->sock   = newsock;
+        conn->sock = newsock;
+
+        if (conn->forward) {
+            forward_info *forward = (forward_info *)conn->forward;
+            /*
+             * For HTTP CONNECT we need to prepend CONNECT request before
+             * sending our actual HTTPS requests.
+             */
+            if (forward->use_http_connect) {
+                rv = send_http_connect(conn, s);
+                /* If an error occurred, loop round and try again */
+                if (rv != APR_SUCCESS) {
+                    conn->sock = NULL;
+                    apr_socket_close(newsock);
+                    loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
+                    ap_log_error(APLOG_MARK, loglevel, rv, s,
+                                 "proxy: %s: attempt to connect to %s:%d "
+                                 "via http CONNECT through %pI (%s) failed",
+                                 proxy_function,
+                                 forward->target_host, forward->target_port,
+                                 backend_addr, worker->hostname);
+                    backend_addr = backend_addr->next;
+                    continue;
+                }
+            }
+        }
+
         connected    = 1;
     }
     /*
@@ -2516,4 +2678,3 @@ ap_proxy_buckets_lifetime_transform(request_rec *r, apr_bucket_brigade *from,
     }
     return rv;
 }
-