Allow to rewrite the request destination for https-intercepted requests
authorFabian Keil <fk@fabiankeil.de>
Tue, 22 Dec 2020 17:24:17 +0000 (18:24 +0100)
committerFabian Keil <fk@fabiankeil.de>
Sat, 2 Jan 2021 12:55:03 +0000 (13:55 +0100)
... behind the client's back.

The documentation already sort of claimed that it was supported
by not especially mentioning that it didn't work for https-inspected
requests.

Fixes SF bug #923 reported by withoutname.

jcc.c

diff --git a/jcc.c b/jcc.c
index 44e7561..e7ad4e2 100644 (file)
--- a/jcc.c
+++ b/jcc.c
@@ -968,8 +968,8 @@ static int crunch_response_triggered(struct client_state *csp, const struct crun
  *
  * Parameters  :
  *          1  :  csp = Current client state (buffers, headers, etc...)
  *
  * Parameters  :
  *          1  :  csp = Current client state (buffers, headers, etc...)
- *          2  :  fwd = The forwarding spec used for the request
- *                XXX: Should use http->fwd instead.
+ *          2  :  fwd = The forwarding spec used for the request.
+ *                      Can be NULL.
  *          3  :  request_line = The old request line which will be replaced.
  *
  * Returns     :  Nothing. Terminates in case of memory problems.
  *          3  :  request_line = The old request line which will be replaced.
  *
  * Returns     :  Nothing. Terminates in case of memory problems.
@@ -997,7 +997,7 @@ static void build_request_line(struct client_state *csp, const struct forward_sp
    *request_line = strdup(http->gpc);
    string_append(request_line, " ");
 
    *request_line = strdup(http->gpc);
    string_append(request_line, " ");
 
-   if (fwd->forward_host && fwd->type != FORWARD_WEBSERVER)
+   if (fwd != NULL && fwd->forward_host && fwd->type != FORWARD_WEBSERVER)
    {
       string_append(request_line, http->url);
    }
    {
       string_append(request_line, http->url);
    }
@@ -2528,6 +2528,95 @@ static jb_err receive_encrypted_request(struct client_state *csp)
 }
 
 
 }
 
 
+/*********************************************************************
+ *
+ * Function    :  change_encrypted_request_destination
+ *
+ * Description :  Parse a (rewritten) request line from an encrypted
+ *                request and regenerate the http request data.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  Forwards the parse_http_request() return code.
+ *                Terminates in case of memory problems.
+ *
+ *********************************************************************/
+static jb_err change_encrypted_request_destination(struct client_state *csp)
+{
+   jb_err err;
+   char *original_host = csp->http->host;
+
+   log_error(LOG_LEVEL_REDIRECTS, "Rewrite detected: %s",
+      csp->https_headers->first->str);
+   csp->http->host = NULL;
+   free_http_request(csp->http);
+   err = parse_http_request(csp->https_headers->first->str, csp->http);
+   if (JB_ERR_OK != err)
+   {
+      log_error(LOG_LEVEL_ERROR, "Couldn't parse rewritten request: %s.",
+         jb_err_to_string(err));
+      return err;
+   }
+
+   if (csp->http->host == NULL)
+   {
+      /*
+       * The rewritten request line did not specify a host
+       * which means we can use the original host specified
+       * by the client.
+       */
+      csp->http->host = original_host;
+      log_error(LOG_LEVEL_REDIRECTS, "Keeping the original host: %s",
+         csp->http->host);
+      /*
+       * If the rewritten request line didn't contain a host
+       * it also didn't contain a port so we can reuse the host
+       * and set the port to 443.
+       */
+      freez(csp->http->hostport);
+      csp->http->hostport = strdup_or_die(csp->http->host);
+      csp->http->port = 443;
+      /*
+       * While the request line didn't mention it,
+       * we're https-inspecting and want to speak TLS
+       * with the server.
+       */
+      csp->http->server_ssl = 1;
+      csp->http->ssl = 1;
+   }
+   else
+   {
+      /* The rewrite filter added a host so we can ditch the original */
+      freez(original_host);
+      csp->http->server_ssl = csp->http->ssl;
+   }
+
+   csp->http->client_ssl = 1;
+
+   freez(csp->https_headers->first->str);
+   build_request_line(csp, NULL, &csp->https_headers->first->str);
+
+   if (!server_use_ssl(csp))
+   {
+      log_error(LOG_LEVEL_REDIRECTS,
+         "Rewritten request line results in downgrade to http");
+      /*
+       * Replace the unencryptd headers received with the
+       * CONNECT request with the ones we received securely.
+       */
+      destroy_list(csp->headers);
+      csp->headers->first = csp->https_headers->first;
+      csp->headers->last  = csp->https_headers->last;
+      csp->https_headers->first = NULL;
+      csp->https_headers->last = NULL;
+   }
+
+   return JB_ERR_OK;
+
+}
+
+
 /*********************************************************************
  *
  * Function    :  process_encrypted_request
 /*********************************************************************
  *
  * Function    :  process_encrypted_request
@@ -2709,6 +2798,17 @@ static jb_err process_encrypted_request(struct client_state *csp)
       return JB_ERR_PARSE;
    }
 
       return JB_ERR_PARSE;
    }
 
+   if ((NULL == csp->https_headers->first->str)
+      || (strcmp(csp->http->cmd, csp->https_headers->first->str) &&
+         (JB_ERR_OK != change_encrypted_request_destination(csp))))
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Failed to get the request destination in the rewritten headers");
+      ssl_send_data_delayed(&(csp->ssl_client_attr),
+         (const unsigned char *)CHEADER, strlen(CHEADER), get_write_delay(csp));
+      return JB_ERR_PARSE;
+   }
+
    log_error(LOG_LEVEL_HEADER, "Encrypted request processed");
    log_applied_actions(csp->action);
    log_error(LOG_LEVEL_REQUEST, "https://%s%s", csp->http->hostport,
    log_error(LOG_LEVEL_HEADER, "Encrypted request processed");
    log_applied_actions(csp->action);
    log_error(LOG_LEVEL_REQUEST, "https://%s%s", csp->http->hostport,