X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=jcc.c;h=132618b8b950eba3f351278cc5533154bd20a381;hp=3bbacd15fba45e41a37357f544d0471ea5a36480;hb=704917e90c0566ef48bf1c4ebaa1407e5cbd0cf7;hpb=84ee09b204158e3b3e4062f5d4bb3faccf7a4e0c diff --git a/jcc.c b/jcc.c index 3bbacd15..132618b8 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,3 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.468 2017/08/12 09:33:25 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -6,7 +5,7 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.468 2017/08/12 09:33:25 fabiankeil Exp $" * Purpose : Main file. Contains main() method, main loop, and * the main connection-handling function. * - * Copyright : Written by and Copyright (C) 2001-2017 the + * Copyright : Written by and Copyright (C) 2001-2019 the * Privoxy team. http://www.privoxy.org/ * * Based on the Internet Junkbuster originally written @@ -113,6 +112,9 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.468 2017/08/12 09:33:25 fabiankeil Exp $" #include "project.h" #include "list.h" #include "jcc.h" +#ifdef FEATURE_HTTPS_FILTERING +#include "ssl.h" +#endif #include "filters.h" #include "loaders.h" #include "parsers.h" @@ -128,9 +130,6 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.468 2017/08/12 09:33:25 fabiankeil Exp $" #include "client-tags.h" #endif -const char jcc_h_rcs[] = JCC_H_VERSION; -const char project_h_rcs[] = PROJECT_H_VERSION; - int daemon_mode = 1; struct client_states clients[1]; struct file_list files[1]; @@ -144,14 +143,14 @@ int urls_rejected = 0; /* total nr of urls rejected */ int g_terminate = 0; #endif -#if !defined(_WIN32) && !defined(__OS2__) && !defined(AMIGA) +#if !defined(_WIN32) && !defined(__OS2__) static void sig_handler(int the_signal); #endif static int client_protocol_is_unsupported(const struct client_state *csp, char *req); static jb_err get_request_destination_elsewhere(struct client_state *csp, struct list *headers); static jb_err get_server_headers(struct client_state *csp); static const char *crunch_reason(const struct http_response *rsp); -static void send_crunch_response(const struct client_state *csp, struct http_response *rsp); +static void send_crunch_response(struct client_state *csp, struct http_response *rsp); static char *get_request_line(struct client_state *csp); static jb_err receive_client_request(struct client_state *csp); static jb_err parse_client_request(struct client_state *csp); @@ -167,12 +166,7 @@ static jb_socket bind_port_helper(const char *haddr, int hport, int backlog); static void bind_ports_helper(struct configuration_spec *config, jb_socket sockets[]); static void close_ports_helper(jb_socket sockets[]); static void listen_loop(void); - -#ifdef AMIGA -void serve(struct client_state *csp); -#else /* ifndef AMIGA */ static void serve(struct client_state *csp); -#endif /* def AMIGA */ #ifdef __BEOS__ static int32 server_thread(void *data); @@ -199,6 +193,13 @@ privoxy_mutex_t log_mutex; privoxy_mutex_t log_init_mutex; privoxy_mutex_t connection_reuse_mutex; +#ifdef LIMIT_MUTEX_NUMBER + privoxy_mutex_t certificates_mutexes[32]; +#else + privoxy_mutex_t certificates_mutexes[65536]; +#endif /* LIMIT_MUTEX_NUMBER */ + privoxy_mutex_t rng_mutex; + #ifdef FEATURE_EXTERNAL_FILTERS privoxy_mutex_t external_filter_mutex; #endif @@ -343,7 +344,7 @@ static const struct cruncher crunchers_light[] = { * * here? */ -#if !defined(_WIN32) && !defined(__OS2__) && !defined(AMIGA) +#if !defined(_WIN32) && !defined(__OS2__) /********************************************************************* * * Function : sig_handler @@ -394,6 +395,42 @@ static void sig_handler(int the_signal) #endif +/********************************************************************* + * + * Function : get_write_delay + * + * Description : Parse the delay-response parameter. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : Number of milliseconds to delay writes. + * + *********************************************************************/ +static unsigned int get_write_delay(const struct client_state *csp) +{ + unsigned int delay; + char *endptr; + char *newval; + + if ((csp->action->flags & ACTION_DELAY_RESPONSE) == 0) + { + return 0; + } + newval = csp->action->string[ACTION_STRING_DELAY_RESPONSE]; + + delay = (unsigned)strtol(newval, &endptr, 0); + if (*endptr != '\0') + { + log_error(LOG_LEVEL_FATAL, + "Invalid delay-response{} parameter: '%s'", newval); + } + + return delay; + +} + + /********************************************************************* * * Function : client_protocol_is_unsupported @@ -446,7 +483,8 @@ static int client_protocol_is_unsupported(const struct client_state *csp, char * log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 400 0", csp->ip_addr_str, req); freez(req); - write_socket(csp->cfd, response, strlen(response)); + write_socket_delayed(csp->cfd, response, strlen(response), + get_write_delay(csp)); return TRUE; } @@ -478,8 +516,10 @@ static int client_has_unsupported_expectations(const struct client_state *csp) csp->ip_addr_str); log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 417 0", csp->ip_addr_str, csp->http->cmd); - write_socket(csp->cfd, UNSUPPORTED_CLIENT_EXPECTATION_ERROR_RESPONSE, - strlen(UNSUPPORTED_CLIENT_EXPECTATION_ERROR_RESPONSE)); + write_socket_delayed(csp->cfd, + UNSUPPORTED_CLIENT_EXPECTATION_ERROR_RESPONSE, + strlen(UNSUPPORTED_CLIENT_EXPECTATION_ERROR_RESPONSE), + get_write_delay(csp)); return TRUE; } @@ -529,7 +569,8 @@ static jb_err get_request_destination_elsewhere(struct client_state *csp, struct log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 400 0", csp->ip_addr_str, csp->http->cmd); - write_socket(csp->cfd, CHEADER, strlen(CHEADER)); + write_socket_delayed(csp->cfd, CHEADER, strlen(CHEADER), + get_write_delay(csp)); destroy_list(headers); return JB_ERR_PARSE; @@ -557,7 +598,8 @@ static jb_err get_request_destination_elsewhere(struct client_state *csp, struct csp->ip_addr_str, csp->http->cmd, req); freez(req); - write_socket(csp->cfd, MISSING_DESTINATION_RESPONSE, strlen(MISSING_DESTINATION_RESPONSE)); + write_socket_delayed(csp->cfd, MISSING_DESTINATION_RESPONSE, + strlen(MISSING_DESTINATION_RESPONSE), get_write_delay(csp)); destroy_list(headers); return JB_ERR_PARSE; @@ -762,12 +804,12 @@ static void log_applied_actions(const struct current_action_spec *actions) * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) - * 1 : rsp = Fully prepared response. Will be freed on exit. + * 2 : rsp = Fully prepared response. Will be freed on exit. * * Returns : Nothing. * *********************************************************************/ -static void send_crunch_response(const struct client_state *csp, struct http_response *rsp) +static void send_crunch_response(struct client_state *csp, struct http_response *rsp) { const struct http_request *http = csp->http; char status_code[4]; @@ -802,13 +844,33 @@ static void send_crunch_response(const struct client_state *csp, struct http_res csp->ip_addr_str, http->ocmd, status_code, rsp->content_length); /* Write the answer to the client */ - if (write_socket(csp->cfd, rsp->head, rsp->head_length) - || write_socket(csp->cfd, rsp->body, rsp->content_length)) +#ifdef FEATURE_HTTPS_FILTERING + if (client_use_ssl(csp)) { - /* There is nothing we can do about it. */ - log_error(LOG_LEVEL_ERROR, - "Couldn't deliver the error message through client socket %d: %E", - csp->cfd); + if ((ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *)rsp->head, rsp->head_length) < 0) + || (ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *)rsp->body, rsp->content_length) < 0)) + { + /* There is nothing we can do about it. */ + log_error(LOG_LEVEL_CONNECT, "Couldn't deliver the error message " + "for %s through client socket %d using TLS/SSL", + http->url, csp->cfd); + } + } + else +#endif + { + if (write_socket_delayed(csp->cfd, rsp->head, rsp->head_length, + get_write_delay(csp)) + || write_socket_delayed(csp->cfd, rsp->body, rsp->content_length, + get_write_delay(csp))) + { + /* There is nothing we can do about it. */ + log_error(LOG_LEVEL_CONNECT, + "Couldn't deliver the error message for %s through client socket %d: %E", + http->url, csp->cfd); + } } /* Clean up and return */ @@ -909,8 +971,6 @@ static void build_request_line(struct client_state *csp, const struct forward_sp { struct http_request *http = csp->http; - assert(http->ssl == 0); - /* * Downgrade http version from 1.1 to 1.0 * if +downgrade action applies. @@ -1069,7 +1129,7 @@ static void wait_for_alive_connections(void) * 1 : sfd = Open socket to remember. * 2 : http = The destination for the connection. * 3 : fwd = The forwarder settings used. - * 3 : server_connection = storage. + * 4 : server_connection = storage. * * Returns : void * @@ -1304,8 +1364,9 @@ static char *get_request_line(struct client_state *csp) log_error(LOG_LEVEL_CONNECT, "No request line on socket %d received in time. Timeout: %d.", csp->cfd, csp->config->socket_timeout); - write_socket(csp->cfd, CLIENT_CONNECTION_TIMEOUT_RESPONSE, - strlen(CLIENT_CONNECTION_TIMEOUT_RESPONSE)); + write_socket_delayed(csp->cfd, CLIENT_CONNECTION_TIMEOUT_RESPONSE, + strlen(CLIENT_CONNECTION_TIMEOUT_RESPONSE), + get_write_delay(csp)); } else { @@ -1467,8 +1528,8 @@ static jb_err receive_chunked_client_request_body(struct client_state *csp) } if (status != CHUNK_STATUS_BODY_COMPLETE) { - write_socket(csp->cfd, CLIENT_BODY_PARSE_ERROR_RESPONSE, - strlen(CLIENT_BODY_PARSE_ERROR_RESPONSE)); + write_socket_delayed(csp->cfd, CLIENT_BODY_PARSE_ERROR_RESPONSE, + strlen(CLIENT_BODY_PARSE_ERROR_RESPONSE), get_write_delay(csp)); log_error(LOG_LEVEL_CLF, "%s - - [%T] \"Failed reading chunked client body\" 400 0", csp->ip_addr_str); return JB_ERR_PARSE; @@ -1676,7 +1737,8 @@ static jb_err receive_client_request(struct client_state *csp) freez(req); if (JB_ERR_OK != err) { - write_socket(csp->cfd, CHEADER, strlen(CHEADER)); + write_socket_delayed(csp->cfd, CHEADER, strlen(CHEADER), + get_write_delay(csp)); /* XXX: Use correct size */ log_error(LOG_LEVEL_CLF, "%s - - [%T] \"Invalid request\" 400 0", csp->ip_addr_str); log_error(LOG_LEVEL_ERROR, @@ -1866,7 +1928,7 @@ static jb_err parse_client_request(struct client_state *csp) csp->ip_addr_str); log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 400 0", csp->ip_addr_str, csp->http->cmd); - write_socket(csp->cfd, CHEADER, strlen(CHEADER)); + write_socket_delayed(csp->cfd, CHEADER, strlen(CHEADER), get_write_delay(csp)); return JB_ERR_PARSE; } csp->flags |= CSP_FLAG_CLIENT_HEADER_PARSING_DONE; @@ -1879,7 +1941,8 @@ static jb_err parse_client_request(struct client_state *csp) /* * A header filter broke the request line - bail out. */ - write_socket(csp->cfd, MESSED_UP_REQUEST_RESPONSE, strlen(MESSED_UP_REQUEST_RESPONSE)); + write_socket_delayed(csp->cfd, MESSED_UP_REQUEST_RESPONSE, + strlen(MESSED_UP_REQUEST_RESPONSE), get_write_delay(csp)); /* XXX: Use correct size */ log_error(LOG_LEVEL_CLF, "%s - - [%T] \"Invalid request generated\" 500 0", csp->ip_addr_str); @@ -1910,7 +1973,7 @@ static jb_err parse_client_request(struct client_state *csp) * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) * - * Returns : 0 on success, anything else is na error. + * Returns : 0 on success, anything else is an error. * *********************************************************************/ static int send_http_request(struct client_state *csp) @@ -1939,7 +2002,7 @@ static int send_http_request(struct client_state *csp) csp->http->hostport); } else if (((csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING) == 0) - && (flush_socket(csp->server_connection.sfd, csp->client_iob) < 0)) + && (flush_iob(csp->server_connection.sfd, csp->client_iob, 0) < 0)) { write_failure = 1; log_error(LOG_LEVEL_CONNECT, "Failed sending request body to: %s: %E", @@ -1951,6 +2014,300 @@ static int send_http_request(struct client_state *csp) } +#ifdef FEATURE_HTTPS_FILTERING +/********************************************************************* + * + * Function : send_https_request + * + * Description : Sends the HTTP headers from the client request + * and all the body data that has already been received. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : 0 on success, anything else is an error. + * + *********************************************************************/ +static int send_https_request(struct client_state *csp) +{ + char *hdr; + int ret; + long flushed = 0; + + hdr = list_to_text(csp->https_headers); + if (hdr == NULL) + { + /* FIXME Should handle error properly */ + log_error(LOG_LEVEL_FATAL, "Out of memory parsing client header"); + } + list_remove_all(csp->https_headers); + + /* + * Write the client's (modified) header to the server + * (along with anything else that may be in the buffer) + */ + ret = ssl_send_data(&(csp->mbedtls_server_attr.ssl), + (const unsigned char *)hdr, strlen(hdr)); + freez(hdr); + + if (ret < 0) + { + log_error(LOG_LEVEL_CONNECT, + "Failed sending encrypted request headers to: %s: %E", + csp->http->hostport); + mark_server_socket_tainted(csp); + close_client_and_server_ssl_connections(csp); + return 1; + } + + if (((csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING) == 0) + && ((flushed = ssl_flush_socket(&(csp->mbedtls_server_attr.ssl), + csp->client_iob)) < 0)) + { + log_error(LOG_LEVEL_CONNECT, "Failed sending request body to: %s: %E", + csp->http->hostport); + return 1; + } + if (flushed != 0) + { + if (csp->expected_client_content_length != 0) + { + if (csp->expected_client_content_length < flushed) + { + log_error(LOG_LEVEL_ERROR, + "Flushed %d bytes of request body while only expecting %llu", + flushed, csp->expected_client_content_length); + csp->expected_client_content_length = 0; + } + else + { + log_error(LOG_LEVEL_CONNECT, + "Flushed %d bytes of request body while expecting %llu", + flushed, csp->expected_client_content_length); + csp->expected_client_content_length -= (unsigned)flushed; + } + } + else + { + log_error(LOG_LEVEL_CONNECT, + "Flushed %d bytes of request body", flushed); + } + } + + log_error(LOG_LEVEL_CONNECT, "Encrypted request sent"); + + return 0; + +} + + +/********************************************************************* + * + * Function : receive_encrypted_request + * + * Description : Receives an encrypted request. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : JB_ERR_OK on success, + * JB_ERR_PARSE or JB_ERR_MEMORY otherwise + * + *********************************************************************/ +static jb_err receive_encrypted_request(struct client_state *csp) +{ + char buf[BUFFER_SIZE]; + int len; + char *p; + + do + { + log_error(LOG_LEVEL_HEADER, "Reading encrypted headers"); + if (!data_is_available(csp->cfd, (int)csp->config->keep_alive_timeout)) + { + log_error(LOG_LEVEL_CONNECT, + "Socket %d timed out while waiting for client headers", csp->cfd); + return JB_ERR_PARSE; + } + len = ssl_recv_data(&(csp->mbedtls_client_attr.ssl), + (unsigned char *)buf, sizeof(buf)); + if (len == -1) + { + return JB_ERR_PARSE; + } + if (add_to_iob(csp->client_iob, csp->config->buffer_limit, buf, len)) + { + return JB_ERR_MEMORY; + } + p = strstr(csp->client_iob->cur, "\r\n\r\n"); + } while (p == NULL); + + log_error(LOG_LEVEL_HEADER, "Encrypted headers received completely"); + + return JB_ERR_OK; +} + + +/********************************************************************* + * + * Function : process_encrypted_request + * + * Description : Receives and parses an encrypted request. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : JB_ERR_OK on success, + * JB_ERR_PARSE or JB_ERR_MEMORY otherwise + * + *********************************************************************/ +static jb_err process_encrypted_request(struct client_state *csp) +{ + char *p; + char *request_line; + jb_err err; + /* Temporary copy of the client's headers before they get enlisted in csp->https_headers */ + struct list header_list; + struct list *headers = &header_list; + + err = receive_encrypted_request(csp); + if (err != JB_ERR_OK) + { + /* XXX: Also used for JB_ERR_MEMORY */ + ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *)CHEADER, strlen(CHEADER)); + return err; + } + + /* We don't need get_request_line() because the whole HTTP head is buffered. */ + request_line = get_header(csp->client_iob); + if (request_line == NULL) + { + ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *)CHEADER, strlen(CHEADER)); + return JB_ERR_PARSE; + } + assert(*request_line != '\0'); + + if (client_protocol_is_unsupported(csp, request_line)) + { + ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *)CHEADER, strlen(CHEADER)); + return JB_ERR_PARSE; + } + +#ifdef FEATURE_FORCE_LOAD + if (force_required(csp, request_line)) + { + csp->flags |= CSP_FLAG_FORCED; + } +#endif /* def FEATURE_FORCE_LOAD */ + + free_http_request(csp->http); + + err = parse_http_request(request_line, csp->http); + /* XXX: Restore ssl setting. This is ugly */ + csp->http->client_ssl = 1; + csp->http->server_ssl = 1; + + freez(request_line); + if (JB_ERR_OK != err) + { + ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *)CHEADER, strlen(CHEADER)); + /* XXX: Use correct size */ + log_error(LOG_LEVEL_CLF, "%s - - [%T] \"Invalid request\" 400 0", csp->ip_addr_str); + log_error(LOG_LEVEL_ERROR, + "Couldn't parse request line received from %s: %s", + csp->ip_addr_str, jb_err_to_string(err)); + + free_http_request(csp->http); + return JB_ERR_PARSE; + } + + /* Parse the rest of the client's headers. */ + init_list(headers); + for (;;) + { + p = get_header(csp->client_iob); + + if (p == NULL) + { + /* There are no additional headers to read. */ + break; + } + enlist(headers, p); + freez(p); + } + + if (JB_ERR_OK != get_destination_from_https_headers(headers, csp->http)) + { + /* + * Our attempts to get the request destination + * elsewhere failed. + */ + ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *)CHEADER, strlen(CHEADER)); + return JB_ERR_PARSE; + } + +#ifndef FEATURE_EXTENDED_HOST_PATTERNS + /* Split the domain we just got for pattern matching */ + init_domain_components(csp->http); +#endif + + /* + * Determine the actions for this URL + */ +#ifdef FEATURE_TOGGLE + if (!(csp->flags & CSP_FLAG_TOGGLED_ON)) + { + /* Most compatible set of actions (i.e. none) */ + init_current_action(csp->action); + } + else +#endif /* ndef FEATURE_TOGGLE */ + { + get_url_actions(csp, csp->http); + } + + enlist(csp->https_headers, csp->http->cmd); + + /* Append the previously read headers */ + err = list_append_list_unique(csp->https_headers, headers); + destroy_list(headers); + if (JB_ERR_OK != err) + { + /* XXX: Send error message */ + return err; + } + + /* XXX: Work around crash */ + csp->error_message = NULL; + + /* XXX: Why do this here? */ + csp->http->ssl = 1; + + err = sed_https(csp); + if (JB_ERR_OK != err) + { + ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *)CHEADER, strlen(CHEADER)); + log_error(LOG_LEVEL_ERROR, "Failed to parse client request from %s.", + csp->ip_addr_str); + log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 400 0", + csp->ip_addr_str, csp->http->cmd); + return JB_ERR_PARSE; + } + + log_error(LOG_LEVEL_HEADER, "Encrypted request processed"); + + return err; + +} +#endif + + /********************************************************************* * * Function : handle_established_connection @@ -1966,7 +2323,6 @@ static int send_http_request(struct client_state *csp) *********************************************************************/ static void handle_established_connection(struct client_state *csp) { - char *receive_buffer; char *hdr; char *p; int n; @@ -1983,16 +2339,35 @@ static void handle_established_connection(struct client_state *csp) struct http_request *http; long len = 0; /* for buffer sizes (and negative error codes) */ int buffer_and_filter_content = 0; + unsigned int write_delay; +#ifdef FEATURE_HTTPS_FILTERING + int ret = 0; + int use_ssl_tunnel = 0; + csp->dont_verify_certificate = 0; + + /* + * Preset flags informing if SSL connections with server or client + * are opened or closed + */ + csp->ssl_with_server_is_opened = 0; + csp->ssl_with_client_is_opened = 0; + + if (csp->http->ssl && !(csp->action->flags & ACTION_ENABLE_HTTPS_FILTER)) + { + /* Pass encrypted content without filtering. */ + use_ssl_tunnel = 1; + } +#endif /* Skeleton for HTTP response, if we should intercept the request */ struct http_response *rsp; #ifdef FEATURE_CONNECTION_KEEP_ALIVE int watch_client_socket; #endif - const size_t receive_buffer_size = csp->config->receive_buffer_size; - receive_buffer = zalloc(receive_buffer_size + 1); - if (receive_buffer == NULL) + csp->receive_buffer_size = csp->config->receive_buffer_size; + csp->receive_buffer = zalloc(csp->receive_buffer_size + 1); + if (csp->receive_buffer == NULL) { log_error(LOG_LEVEL_ERROR, "Out of memory. Failed to allocate the receive buffer."); @@ -2017,6 +2392,7 @@ static void handle_established_connection(struct client_state *csp) #ifdef FEATURE_CONNECTION_KEEP_ALIVE watch_client_socket = 0 == (csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING); #endif + write_delay = get_write_delay(csp); for (;;) { @@ -2080,68 +2456,127 @@ static void handle_established_connection(struct client_state *csp) } len = 0; /* - * XXX: should not jump around, - * chat() is complicated enough already. + * XXX: Should not jump around, handle_established_connection() + * is complicated enough already. */ goto reading_done; } #endif /* FEATURE_CONNECTION_KEEP_ALIVE */ -#ifdef HAVE_POLL - poll_fds[0].fd = csp->cfd; -#ifdef FEATURE_CONNECTION_KEEP_ALIVE - if (!watch_client_socket) +#ifdef FEATURE_HTTPS_FILTERING + /* + * Test if some data from client or destination server are pending + * on TLS/SSL. We must work with them preferably. TLS/SSL data can + * be pending because of maximal fragment size. + */ + int read_ssl_server = 0; + int read_ssl_client = 0; + + if (client_use_ssl(csp)) { - /* - * Ignore incoming data, but still watch out - * for disconnects etc. These flags are always - * implied anyway but explicitly setting them - * doesn't hurt. - */ - poll_fds[0].events = POLLERR|POLLHUP; + read_ssl_client = is_ssl_pending(&(csp->mbedtls_client_attr.ssl)) != 0; } - else -#endif + + if (server_use_ssl(csp)) { - poll_fds[0].events = POLLIN; + read_ssl_server = is_ssl_pending(&(csp->mbedtls_server_attr.ssl)) != 0; } - poll_fds[1].fd = csp->server_connection.sfd; - poll_fds[1].events = POLLIN; - n = poll(poll_fds, 2, csp->config->socket_timeout * 1000); + + if (!read_ssl_server && !read_ssl_client) +#endif + { +#ifdef HAVE_POLL + poll_fds[0].fd = csp->cfd; +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + if (!watch_client_socket) + { + /* + * Ignore incoming data, but still watch out + * for disconnects etc. These flags are always + * implied anyway but explicitly setting them + * doesn't hurt. + */ + poll_fds[0].events = POLLERR|POLLHUP; + } + else +#endif + { + poll_fds[0].events = POLLIN; + } + poll_fds[1].fd = csp->server_connection.sfd; + poll_fds[1].events = POLLIN; + n = poll(poll_fds, 2, csp->config->socket_timeout * 1000); #else - timeout.tv_sec = csp->config->socket_timeout; - timeout.tv_usec = 0; - n = select((int)maxfd+1, &rfds, NULL, NULL, &timeout); + timeout.tv_sec = csp->config->socket_timeout; + timeout.tv_usec = 0; + n = select((int)maxfd + 1, &rfds, NULL, NULL, &timeout); #endif /* def HAVE_POLL */ - if (n == 0) - { - log_error(LOG_LEVEL_CONNECT, "Socket timeout %d reached: %s", - csp->config->socket_timeout, http->url); - if ((byte_count == 0) && (http->ssl == 0)) + /*server or client not responding in timeout */ + if (n == 0) { - send_crunch_response(csp, error_response(csp, "connection-timeout")); + log_error(LOG_LEVEL_CONNECT, "Socket timeout %d reached: %s", + csp->config->socket_timeout, http->url); + if ((byte_count == 0) && (http->ssl == 0)) + { + send_crunch_response(csp, error_response(csp, "connection-timeout")); + } + mark_server_socket_tainted(csp); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif + return; + } + else if (n < 0) + { +#ifdef HAVE_POLL + log_error(LOG_LEVEL_ERROR, "poll() failed!: %E"); +#else + log_error(LOG_LEVEL_ERROR, "select() failed!: %E"); +#endif + mark_server_socket_tainted(csp); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif + return; } - mark_server_socket_tainted(csp); - freez(receive_buffer); - return; } - else if (n < 0) +#ifdef FEATURE_HTTPS_FILTERING + else { + /* set FD if some data are pending on TLS/SSL connections */ +#ifndef HAVE_POLL + FD_ZERO(&rfds); +#endif + if (read_ssl_client) + { #ifdef HAVE_POLL - log_error(LOG_LEVEL_ERROR, "poll() failed!: %E"); + poll_fds[0].fd = csp->cfd; + poll_fds[0].events = POLLIN; #else - log_error(LOG_LEVEL_ERROR, "select() failed!: %E"); + FD_SET(csp->cfd, &rfds); #endif - mark_server_socket_tainted(csp); - freez(receive_buffer); - return; - } + n++; + } + if (read_ssl_server) + { +#ifdef HAVE_POLL + poll_fds[1].fd = csp->server_connection.sfd; + poll_fds[1].events = POLLIN; +#else + FD_SET(csp->server_connection.sfd, &rfds); +#endif + n++; + } + } +#endif /* * This is the body of the browser's request, * just read and write it. * + * Receives data from browser and sends it to server + * * XXX: Make sure the client doesn't use pipelining * behind Privoxy's back. */ @@ -2161,7 +2596,7 @@ static void handle_established_connection(struct client_state *csp) if (FD_ISSET(csp->cfd, &rfds)) #endif /* def HAVE_POLL*/ { - int max_bytes_to_read = (int)receive_buffer_size; + int max_bytes_to_read = (int)csp->receive_buffer_size; #ifdef FEATURE_CONNECTION_KEEP_ALIVE if ((csp->flags & CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ)) @@ -2176,7 +2611,7 @@ static void handle_established_connection(struct client_state *csp) */ watch_client_socket = 0; log_error(LOG_LEVEL_CONNECT, - "Stopping to watch the client socket %d. " + "Stop watching client socket %d. " "There's already another request waiting.", csp->cfd); continue; @@ -2195,7 +2630,7 @@ static void handle_established_connection(struct client_state *csp) } if (csp->expected_client_content_length != 0) { - if (csp->expected_client_content_length < receive_buffer_size) + if (csp->expected_client_content_length < csp->receive_buffer_size) { max_bytes_to_read = (int)csp->expected_client_content_length; } @@ -2203,42 +2638,79 @@ static void handle_established_connection(struct client_state *csp) "Waiting for up to %d bytes from the client.", max_bytes_to_read); } - assert(max_bytes_to_read <= receive_buffer_size); + assert(max_bytes_to_read <= csp->receive_buffer_size); #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - len = read_socket(csp->cfd, receive_buffer, max_bytes_to_read); - - if (len <= 0) +#ifdef FEATURE_HTTPS_FILTERING + /* + * Reading data from standard or secured connection (HTTP/HTTPS) + */ + if (client_use_ssl(csp)) { - /* XXX: not sure if this is necessary. */ - mark_server_socket_tainted(csp); - break; /* "game over, man" */ + /* + * Receiving HTTP request from client over TLS/SSL and sending + * it to server over TLS/SSL. + */ + len = ssl_recv_data(&(csp->mbedtls_client_attr.ssl), + (unsigned char *)csp->receive_buffer, (size_t)max_bytes_to_read); + + if (len <= 0) + { + mark_server_socket_tainted(csp); + break; + } + + ret = ssl_send_data(&(csp->mbedtls_server_attr.ssl), + (const unsigned char *)csp->receive_buffer, (size_t)len); + + if (ret < 0) + { + log_error(LOG_LEVEL_ERROR, + "Send request over TLS/SSL to: %s failed", http->host); + mark_server_socket_tainted(csp); + close_client_and_server_ssl_connections(csp); + return; + } } + else +#endif /* def FEATURE_HTTPS_FILTERING */ + { + len = read_socket(csp->cfd, csp->receive_buffer, max_bytes_to_read); + + if (len <= 0) + { + /* XXX: not sure if this is necessary. */ + mark_server_socket_tainted(csp); + break; /* "game over, man" */ + } #ifdef FEATURE_CONNECTION_KEEP_ALIVE - if (csp->expected_client_content_length != 0) - { - assert(len <= max_bytes_to_read); - csp->expected_client_content_length -= (unsigned)len; - log_error(LOG_LEVEL_CONNECT, - "Expected client content length set to %llu " - "after reading %d bytes.", - csp->expected_client_content_length, len); - if (csp->expected_client_content_length == 0) + if (csp->expected_client_content_length != 0) { + assert(len <= max_bytes_to_read); + csp->expected_client_content_length -= (unsigned)len; log_error(LOG_LEVEL_CONNECT, - "Done reading from the client."); - csp->flags |= CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; + "Expected client content length set to %llu " + "after reading %d bytes.", + csp->expected_client_content_length, len); + if (csp->expected_client_content_length == 0) + { + log_error(LOG_LEVEL_CONNECT, + "Done reading from the client."); + csp->flags |= CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ; + } } - } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - if (write_socket(csp->server_connection.sfd, receive_buffer, (size_t)len)) - { - log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); - mark_server_socket_tainted(csp); - freez(receive_buffer); - return; + if (write_socket(csp->server_connection.sfd, csp->receive_buffer, (size_t)len)) + { + log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); + mark_server_socket_tainted(csp); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif + return; + } } continue; } @@ -2271,19 +2743,39 @@ static void handle_established_connection(struct client_state *csp) log_error(LOG_LEVEL_CONNECT, "The server still wants to talk, but the client hung up on us."); mark_server_socket_tainted(csp); - freez(receive_buffer); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif return; #endif /* def _WIN32 */ } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - len = read_socket(csp->server_connection.sfd, receive_buffer, (int)receive_buffer_size); +#ifdef FEATURE_HTTPS_FILTERING + /* + * Reading data from standard or secured connection (HTTP/HTTPS) + */ + if (server_use_ssl(csp)) + { + len = ssl_recv_data(&(csp->mbedtls_server_attr.ssl), + (unsigned char *)csp->receive_buffer, csp->receive_buffer_size); + } + else +#endif + { + len = read_socket(csp->server_connection.sfd, csp->receive_buffer, + (int)csp->receive_buffer_size); + } if (len < 0) { log_error(LOG_LEVEL_ERROR, "read from: %s failed: %E", http->host); - if (http->ssl && (csp->fwd == NULL)) + if ((http->ssl && (csp->fwd == NULL)) +#ifdef FEATURE_HTTPS_FILTERING + && use_ssl_tunnel +#endif + ) { /* * Just hang up. We already confirmed the client's CONNECT @@ -2292,7 +2784,6 @@ static void handle_established_connection(struct client_state *csp) */ log_error(LOG_LEVEL_ERROR, "CONNECT already confirmed. Unable to tell the client about the problem."); - freez(receive_buffer); return; } else if (byte_count) @@ -2307,7 +2798,9 @@ static void handle_established_connection(struct client_state *csp) log_error(LOG_LEVEL_ERROR, "Already forwarded the original headers. " "Unable to tell the client about the problem."); mark_server_socket_tainted(csp); - freez(receive_buffer); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif return; } /* @@ -2320,7 +2813,7 @@ static void handle_established_connection(struct client_state *csp) #ifdef FEATURE_CONNECTION_KEEP_ALIVE if (csp->flags & CSP_FLAG_CHUNKED) { - if ((len >= 5) && !memcmp(receive_buffer+len-5, "0\r\n\r\n", 5)) + if ((len >= 5) && !memcmp(csp->receive_buffer+len-5, "0\r\n\r\n", 5)) { /* XXX: this is a temporary hack */ log_error(LOG_LEVEL_CONNECT, @@ -2337,19 +2830,19 @@ static void handle_established_connection(struct client_state *csp) * This is guaranteed by allocating with zalloc_or_die() * and never (intentionally) writing to the last byte. * - * receive_buffer_size is the size of the part of the + * csp->receive_buffer_size is the size of the part of the * buffer we intentionally write to, but we actually - * allocated receive_buffer_size+1 bytes so the assertion + * allocated csp->receive_buffer_size+1 bytes so the assertion * stays within the allocated range. */ - assert(receive_buffer[receive_buffer_size] == '\0'); + assert(csp->receive_buffer[csp->receive_buffer_size] == '\0'); /* * Add a trailing zero to let be able to use string operations. * XXX: do we still need this with filter_popups gone? */ - assert(len <= receive_buffer_size); - receive_buffer[len] = '\0'; + assert(len <= csp->receive_buffer_size); + csp->receive_buffer[len] = '\0'; /* * Normally, this would indicate that we've read @@ -2373,7 +2866,11 @@ static void handle_established_connection(struct client_state *csp) if (len == 0) { - if (server_body || http->ssl) + if (server_body || (http->ssl +#ifdef FEATURE_HTTPS_FILTERING + && use_ssl_tunnel +#endif + )) { /* * If we have been buffering up the document, @@ -2420,16 +2917,43 @@ static void handle_established_connection(struct client_state *csp) log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header"); } - if (write_socket(csp->cfd, hdr, strlen(hdr)) - || write_socket(csp->cfd, - ((p != NULL) ? p : csp->iob->cur), (size_t)csp->content_length)) +#ifdef FEATURE_HTTPS_FILTERING + /* + * Sending data with standard or secured connection (HTTP/HTTPS) + */ + if (client_use_ssl(csp)) { - log_error(LOG_LEVEL_ERROR, "write modified content to client failed: %E"); - freez(hdr); - freez(p); - mark_server_socket_tainted(csp); - freez(receive_buffer); - return; + if ((ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *)hdr, strlen(hdr)) < 0) + || (ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *) ((p != NULL) ? p : csp->iob->cur), + csp->content_length) < 0)) + { + log_error(LOG_LEVEL_ERROR, "write modified content to " + "client over TLS/SSL failed"); + freez(hdr); + freez(p); + mark_server_socket_tainted(csp); + close_client_and_server_ssl_connections(csp); + return; + } + } + else +#endif /* def FEATURE_HTTPS_FILTERING */ + { + if (write_socket_delayed(csp->cfd, hdr, strlen(hdr), write_delay) + || write_socket_delayed(csp->cfd, ((p != NULL) ? p : csp->iob->cur), + (size_t)csp->content_length, write_delay)) + { + log_error(LOG_LEVEL_ERROR, "write modified content to client failed: %E"); + freez(hdr); + freez(p); + mark_server_socket_tainted(csp); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif + return; + } } freez(hdr); @@ -2443,8 +2967,8 @@ static void handle_established_connection(struct client_state *csp) * This is NOT the body, so * Let's pretend the server just sent us a blank line. */ - snprintf(receive_buffer, receive_buffer_size, "\r\n"); - len = (int)strlen(receive_buffer); + snprintf(csp->receive_buffer, csp->receive_buffer_size, "\r\n"); + len = (int)strlen(csp->receive_buffer); /* * Now, let the normal header parsing algorithm below do its @@ -2455,11 +2979,15 @@ static void handle_established_connection(struct client_state *csp) } /* - * If this is an SSL connection or we're in the body - * of the server document, just write it to the client, - * unless we need to buffer the body for later content-filtering + * If we're in the body of the server document, just write it to + * the client, unless we need to buffer the body for later + * content-filtering. */ - if (server_body || http->ssl) + if (server_body || (http->ssl +#ifdef FEATURE_HTTPS_FILTERING + && use_ssl_tunnel +#endif + )) { if (buffer_and_filter_content) { @@ -2468,7 +2996,7 @@ static void handle_established_connection(struct client_state *csp) * has been reached, switch to non-filtering mode, i.e. make & write the * header, flush the iob and buf, and get out of the way. */ - if (add_to_iob(csp->iob, csp->config->buffer_limit, receive_buffer, len)) + if (add_to_iob(csp->iob, csp->config->buffer_limit, csp->receive_buffer, len)) { size_t hdrlen; long flushed; @@ -2487,21 +3015,51 @@ static void handle_established_connection(struct client_state *csp) rsp = cgi_error_memory(); send_crunch_response(csp, rsp); mark_server_socket_tainted(csp); - freez(receive_buffer); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif return; } hdrlen = strlen(hdr); - if (write_socket(csp->cfd, hdr, hdrlen) - || ((flushed = flush_socket(csp->cfd, csp->iob)) < 0) - || (write_socket(csp->cfd, receive_buffer, (size_t)len))) +#ifdef FEATURE_HTTPS_FILTERING + /* + * Sending data with standard or secured connection (HTTP/HTTPS) + */ + if (client_use_ssl(csp)) { - log_error(LOG_LEVEL_CONNECT, - "Flush header and buffers to client failed: %E"); - freez(hdr); - mark_server_socket_tainted(csp); - freez(receive_buffer); - return; + if ((ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *)hdr, hdrlen) < 0) + || ((flushed = ssl_flush_socket(&(csp->mbedtls_client_attr.ssl), + csp->iob)) < 0) + || (ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *)csp->receive_buffer, (size_t)len) < 0)) + { + log_error(LOG_LEVEL_CONNECT, + "Flush header and buffers to client failed"); + freez(hdr); + mark_server_socket_tainted(csp); + close_client_and_server_ssl_connections(csp); + return; + } + } + else +#endif /* def FEATURE_HTTPS_FILTERING */ + { + if (write_socket_delayed(csp->cfd, hdr, hdrlen, write_delay) + || ((flushed = flush_iob(csp->cfd, csp->iob, write_delay)) < 0) + || write_socket_delayed(csp->cfd, csp->receive_buffer, (size_t)len, + write_delay)) + { + log_error(LOG_LEVEL_CONNECT, + "Flush header and buffers to client failed: %E"); + freez(hdr); + mark_server_socket_tainted(csp); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif + return; + } } /* @@ -2514,15 +3072,39 @@ static void handle_established_connection(struct client_state *csp) buffer_and_filter_content = 0; server_body = 1; } - } - else - { - if (write_socket(csp->cfd, receive_buffer, (size_t)len)) + } + else + { +#ifdef FEATURE_HTTPS_FILTERING + /* + * Sending data with standard or secured connection (HTTP/HTTPS) + */ + if (client_use_ssl(csp)) + { + ret = ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *)csp->receive_buffer, (size_t)len); + if (ret < 0) + { + log_error(LOG_LEVEL_ERROR, + "Sending data to client failed"); + mark_server_socket_tainted(csp); + close_client_and_server_ssl_connections(csp); + return; + } + } + else +#endif /* def FEATURE_HTTPS_FILTERING */ { - log_error(LOG_LEVEL_ERROR, "write to client failed: %E"); - mark_server_socket_tainted(csp); - freez(receive_buffer); - return; + if (write_socket_delayed(csp->cfd, csp->receive_buffer, + (size_t)len, write_delay)) + { + log_error(LOG_LEVEL_ERROR, "write to client failed: %E"); + mark_server_socket_tainted(csp); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif + return; + } } } byte_count += (unsigned long long)len; @@ -2535,13 +3117,15 @@ static void handle_established_connection(struct client_state *csp) * Buffer up the data we just read. If that fails, there's * little we can do but send our static out-of-memory page. */ - if (add_to_iob(csp->iob, csp->config->buffer_limit, receive_buffer, len)) + if (add_to_iob(csp->iob, csp->config->buffer_limit, csp->receive_buffer, len)) { log_error(LOG_LEVEL_ERROR, "Out of memory while looking for end of server headers."); rsp = cgi_error_memory(); send_crunch_response(csp, rsp); mark_server_socket_tainted(csp); - freez(receive_buffer); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif return; } @@ -2559,10 +3143,27 @@ static void handle_established_connection(struct client_state *csp) "Applying the MS IIS5 hack didn't help."); log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd); - write_socket(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE, - strlen(INVALID_SERVER_HEADERS_RESPONSE)); +#ifdef FEATURE_HTTPS_FILTERING + /* + * Sending data with standard or secured connection (HTTP/HTTPS) + */ + if (client_use_ssl(csp)) + { + ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *)INVALID_SERVER_HEADERS_RESPONSE, + strlen(INVALID_SERVER_HEADERS_RESPONSE)); + } + else +#endif /* def FEATURE_HTTPS_FILTERING */ + { + write_socket_delayed(csp->cfd, + INVALID_SERVER_HEADERS_RESPONSE, + strlen(INVALID_SERVER_HEADERS_RESPONSE), write_delay); + } mark_server_socket_tainted(csp); - freez(receive_buffer); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif return; } else @@ -2608,12 +3209,18 @@ static void handle_established_connection(struct client_state *csp) } free_http_request(http); mark_server_socket_tainted(csp); - freez(receive_buffer); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif return; } + if (!csp->headers->first->str) + { + log_error(LOG_LEVEL_ERROR, "header search: csp->headers->first->str == NULL, assert will be called"); + } assert(csp->headers->first->str); - assert(!http->ssl); + if (strncmpic(csp->headers->first->str, "HTTP", 4) && strncmpic(csp->headers->first->str, "ICY", 3)) { @@ -2630,10 +3237,27 @@ static void handle_established_connection(struct client_state *csp) csp->headers->first->str); log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd); - write_socket(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE, - strlen(INVALID_SERVER_HEADERS_RESPONSE)); +#ifdef FEATURE_HTTPS_FILTERING + /* + * Sending data with standard or secured connection (HTTP/HTTPS) + */ + if (client_use_ssl(csp)) + { + ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *)INVALID_SERVER_HEADERS_RESPONSE, + strlen(INVALID_SERVER_HEADERS_RESPONSE)); + } + else +#endif /* def FEATURE_HTTPS_FILTERING */ + { + write_socket_delayed(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE, + strlen(INVALID_SERVER_HEADERS_RESPONSE), write_delay); + } free_http_request(http); mark_server_socket_tainted(csp); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif return; } @@ -2645,11 +3269,27 @@ static void handle_established_connection(struct client_state *csp) { log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd); - write_socket(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE, - strlen(INVALID_SERVER_HEADERS_RESPONSE)); +#ifdef FEATURE_HTTPS_FILTERING + /* + * Sending data with standard or secured connection (HTTP/HTTPS) + */ + if (client_use_ssl(csp)) + { + ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *)INVALID_SERVER_HEADERS_RESPONSE, + strlen(INVALID_SERVER_HEADERS_RESPONSE)); + } + else +#endif + { + write_socket_delayed(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE, + strlen(INVALID_SERVER_HEADERS_RESPONSE), write_delay); + } free_http_request(http); mark_server_socket_tainted(csp); - freez(receive_buffer); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif return; } hdr = list_to_text(csp->headers); @@ -2684,41 +3324,66 @@ static void handle_established_connection(struct client_state *csp) */ freez(hdr); mark_server_socket_tainted(csp); - freez(receive_buffer); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif return; } + /* Buffer and pcrs filter this if appropriate. */ + buffer_and_filter_content = content_requires_filtering(csp); - if (!http->ssl) /* We talk plaintext */ - { - buffer_and_filter_content = content_requires_filtering(csp); - } - /* - * Only write if we're not buffering for content modification - */ if (!buffer_and_filter_content) { /* * Write the server's (modified) header to * the client (along with anything else that - * may be in the buffer) + * may be in the buffer). Use standard or secured + * connection. */ - - if (write_socket(csp->cfd, hdr, strlen(hdr)) - || ((len = flush_socket(csp->cfd, csp->iob)) < 0)) +#ifdef FEATURE_HTTPS_FILTERING + if (client_use_ssl(csp)) { - log_error(LOG_LEVEL_CONNECT, "write header to client failed: %E"); + if ((ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *)hdr, strlen(hdr)) < 0) + || (len = ssl_flush_socket(&(csp->mbedtls_client_attr.ssl), + csp->iob) < 0)) + { + log_error(LOG_LEVEL_CONNECT, "Write header to client failed"); - /* - * The write failed, so don't bother mentioning it - * to the client... it probably can't hear us anyway. - */ - freez(hdr); - mark_server_socket_tainted(csp); - freez(receive_buffer); - return; + /* + * The write failed, so don't bother mentioning it + * to the client... it probably can't hear us anyway. + */ + freez(hdr); + mark_server_socket_tainted(csp); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif + return; + } } - } + else +#endif /* def FEATURE_HTTPS_FILTERING */ + { + if (write_socket_delayed(csp->cfd, hdr, strlen(hdr), write_delay) + || ((len = flush_iob(csp->cfd, csp->iob, write_delay)) < 0)) + { + log_error(LOG_LEVEL_ERROR, + "write header to client failed"); + /* + * The write failed, so don't bother mentioning it + * to the client... it probably can't hear us anyway. + */ + freez(hdr); + mark_server_socket_tainted(csp); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif + return; + } + } + } /* we're finished with the server's header */ @@ -2737,21 +3402,40 @@ static void handle_established_connection(struct client_state *csp) "Applying the MS IIS5 hack didn't help."); log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd); - write_socket(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE, - strlen(INVALID_SERVER_HEADERS_RESPONSE)); +#ifdef FEATURE_HTTPS_FILTERING + /* + * Sending data with standard or secured connection (HTTP/HTTPS) + */ + if (client_use_ssl(csp)) + { + ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *)INVALID_SERVER_HEADERS_RESPONSE, + strlen(INVALID_SERVER_HEADERS_RESPONSE)); + } + else +#endif /* def FEATURE_HTTPS_FILTERING */ + { + write_socket_delayed(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE, + strlen(INVALID_SERVER_HEADERS_RESPONSE), write_delay); + } mark_server_socket_tainted(csp); - freez(receive_buffer); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif return; } } continue; } mark_server_socket_tainted(csp); - freez(receive_buffer); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif return; /* huh? we should never get here */ } - freez(receive_buffer); - +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif if (csp->content_length == 0) { /* @@ -2809,6 +3493,9 @@ static void chat(struct client_state *csp) struct http_request *http; /* Skeleton for HTTP response, if we should intercept the request */ struct http_response *rsp; +#ifdef FEATURE_HTTPS_FILTERING + int use_ssl_tunnel = 0; +#endif http = csp->http; @@ -2830,43 +3517,79 @@ static void chat(struct client_state *csp) return; } +#ifdef FEATURE_HTTPS_FILTERING + /* + * Setting flags to use old solution with SSL tunnel and to disable + * certificates verification. + */ + if (csp->http->ssl && !(csp->action->flags & ACTION_ENABLE_HTTPS_FILTER)) + { + use_ssl_tunnel = 1; + } + + if (http->ssl && csp->action->flags & ACTION_IGNORE_CERTIFICATE_ERRORS) + { + csp->dont_verify_certificate = 1; + } +#endif + /* * build the http request to send to the server * we have to do one of the following: * - * create = use the original HTTP request to create a new - * HTTP request that has either the path component - * without the http://domainspec (w/path) or the - * full orininal URL (w/url) - * Note that the path and/or the HTTP version may - * have been altered by now. + * create = use the original HTTP request to create a new + * HTTP request that has either the path component + * without the http://domainspec (w/path) or the + * full orininal URL (w/url) + * Note that the path and/or the HTTP version may + * have been altered by now. * - * connect = Open a socket to the host:port of the server - * and short-circuit server and client socket. + * SSL proxy = Open a socket to the host:port of the server + * and create TLS/SSL connection with server and + * with client. Then behave like mediator between + * client and server over TLS/SSL. * - * pass = Pass the request unchanged if forwarding a CONNECT - * request to a parent proxy. Note that we'll be sending - * the CFAIL message ourselves if connecting to the parent - * fails, but we won't send a CSUCCEED message if it works, - * since that would result in a double message (ours and the - * parent's). After sending the request to the parent, we simply - * tunnel. + * SSL proxy = Pass the request unchanged if forwarding a CONNECT + * with request to a parent proxy. Note that we'll be sending + * forwarding the CFAIL message ourselves if connecting to the parent + * fails, but we won't send a CSUCCEED message if it works, + * since that would result in a double message (ours and the + * parent's). After sending the request to the parent, we + * must parse answer and send it to client. If connection + * with server is established, we do TLS/SSL proxy. Otherwise + * we send parent response to client and close connections. * * here's the matrix: * SSL * 0 1 * +--------+--------+ * | | | - * 0 | create | connect| - * | w/path | | + * 0 | create | SSL | + * | w/path | proxy | * Forwarding +--------+--------+ - * | | | - * 1 | create | pass | - * | w/url | | + * | | SSL | + * 1 | create | proxy | + * | w/url |+forward| * +--------+--------+ * */ +#ifdef FEATURE_HTTPS_FILTERING + /* + * Presetting SSL client and server flags + */ + if (http->ssl && !use_ssl_tunnel) + { + http->client_ssl = 1; + http->server_ssl = 1; + } + else + { + http->client_ssl = 0; + http->server_ssl = 0; + } +#endif + if (http->ssl && connect_port_is_forbidden(csp)) { const char *acceptable_connect_ports = @@ -2877,13 +3600,15 @@ static void chat(struct client_state *csp) csp->ip_addr_str, acceptable_connect_ports, csp->http->hostport); csp->action->flags |= ACTION_BLOCK; http->ssl = 0; +#ifdef FEATURE_HTTPS_FILTERING + http->client_ssl = 0; + http->server_ssl = 0; +#endif } - if (http->ssl == 0) - { - freez(csp->headers->first->str); - build_request_line(csp, fwd, &csp->headers->first->str); - } + + freez(csp->headers->first->str); + build_request_line(csp, fwd, &csp->headers->first->str); /* * We have a request. Check if one of the crunchers wants it. @@ -2942,7 +3667,63 @@ static void chat(struct client_state *csp) mark_connection_closed(&csp->server_connection); } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ +#ifdef FEATURE_HTTPS_FILTERING + if (http->ssl && !use_ssl_tunnel) + { + int ret; + /* + * Creating an SSL proxy. If forwarding is disabled, we must send + * CSUCCEED mesage to client. Then TLS/SSL connection with client + * is created. + */ + + if (fwd->forward_host == NULL) + { + /* + * We're lying to the client as the connection hasn't actually + * been established yet. We don't establish the connection until + * we have seen and parsed the encrypted client headers. + */ + if (write_socket_delayed(csp->cfd, CSUCCEED, + strlen(CSUCCEED), get_write_delay(csp)) != 0) + { + log_error(LOG_LEVEL_ERROR, "Sending SUCCEED to client failed"); + return; + } + } + ret = create_client_ssl_connection(csp); + if (ret != 0) + { + log_error(LOG_LEVEL_ERROR, + "Can't open secure connection with client"); + close_client_ssl_connection(csp); /* XXX: Is this needed? */ + return; + } + if (JB_ERR_OK != process_encrypted_request(csp)) + { + log_error(LOG_LEVEL_ERROR, "Failed to parse encrypted request."); + close_client_ssl_connection(csp); + return; + } + /* + * We have an encrypted request. Check if one of the crunchers now + * wants it (for example because the previously invisible path was + * required to match). + */ + if (crunch_response_triggered(csp, crunchers_all)) + { + /* + * Yes. The client got the crunch response and we're done here. + */ + close_client_ssl_connection(csp); + return; + } + } +#endif + /* + * Connecting to destination server + */ csp->server_connection.sfd = forwarded_connect(fwd, http, csp); if (csp->server_connection.sfd == JB_INVALID_SOCKET) @@ -2984,6 +3765,145 @@ static void chat(struct client_state *csp) return; } + +#ifdef FEATURE_HTTPS_FILTERING + /* + * Creating TLS/SSL connections with destination server or parent + * proxy. If forwarding is enabled, we must send client request to + * parent proxy and receive, parse and resend parent proxy answer. + */ + if (http->ssl && !use_ssl_tunnel) + { + if (fwd->forward_host != NULL) + { + char server_response[BUFFER_SIZE]; + int ret = 0; + int len = 0; + char *hdr = list_to_text(csp->headers); + memset(server_response, 0, sizeof(server_response)); + + if (hdr == NULL) + { + log_error(LOG_LEVEL_FATAL, + "Out of memory parsing client header"); + } + list_remove_all(csp->headers); + + /* + * Sending client's CONNECT request to the parent proxy + */ + ret = write_socket(csp->server_connection.sfd, hdr, strlen(hdr)); + + freez(hdr); + + if (ret != 0) + { + log_error(LOG_LEVEL_CONNECT, + "Sending request headers to: %s failed", http->hostport); + mark_server_socket_tainted(csp); + close_client_ssl_connection(csp); + return; + } + + /* Waiting for parent proxy server response */ + len = read_socket(csp->server_connection.sfd, server_response, + sizeof(server_response)-1); + + if (len <= 0) + { + log_error(LOG_LEVEL_ERROR, "No response from parent proxy " + "server on socket %d.", csp->server_connection.sfd); + + rsp = error_response(csp, "no-server-data"); + if (rsp) + { + send_crunch_response(csp, rsp); + } + mark_server_socket_tainted(csp); + close_client_ssl_connection(csp); + return; + } + + /* + * Test if connection with destination server was established + * successfully by parent proxy. Then we can send response to + * the client and continue or stop. + */ + if (!tunnel_established_successfully(server_response, (unsigned int)len)) + { + log_error(LOG_LEVEL_ERROR, "Forwarder hasn't established " + "connection with destination server."); + + write_socket(csp->cfd, server_response, (size_t)len); + mark_server_socket_tainted(csp); + close_client_ssl_connection(csp); + return; + } + + /* + * Parent proxy has established connection with destination server. + * Now we must create TLS/SSL connection with parent proxy. + */ + ret = create_server_ssl_connection(csp); + + /* + * If TLS/SSL connection wasn't created and invalid certificate + * wasn't detected, we can interrupt this fuction. Otherwise, we + * must inform the client about invalid server certificate. + */ + if (ret != 0 + && (csp->server_cert_verification_result == SSL_CERT_NOT_VERIFIED + || csp->server_cert_verification_result == SSL_CERT_VALID)) + { + rsp = error_response(csp, "connect-failed"); + if (rsp) + { + send_crunch_response(csp, rsp); + } + return; + } + + /* + * TLS/SSL connection with parent proxy is established, we can + * inform client about success. + */ + ret = write_socket(csp->cfd, server_response, (size_t)len); + if (ret != 0) + { + log_error(LOG_LEVEL_ERROR, + "Sending parent proxy response to client failed"); + mark_server_socket_tainted(csp); + close_client_ssl_connection(csp); + return; + } + }/* -END- if (fwd->forward_host != NULL) */ + else + { + /* + * Parent proxy is not used, we can just create TLS/SSL connection + * with destination server + */ + int ret = create_server_ssl_connection(csp); + /* + * If TLS/SSL connection wasn't created and invalid certificate + * wasn't detected, we can interrupt this function. Otherwise, we + * must inform client about invalid server certificate. + */ + if (ret != 0 + && (csp->server_cert_verification_result == SSL_CERT_NOT_VERIFIED + || csp->server_cert_verification_result == SSL_CERT_VALID)) + { + rsp = error_response(csp, "connect-failed"); + if (rsp) + { + send_crunch_response(csp, rsp); + } + return; + } + } + }/* -END- if (http->ssl) */ +#endif /* def FEATURE_HTTPS_FILTERING */ + #ifdef FEATURE_CONNECTION_KEEP_ALIVE save_connection_destination(csp->server_connection.sfd, http, fwd, &csp->server_connection); @@ -2999,7 +3919,11 @@ static void chat(struct client_state *csp) /* Client headers have been sent optimistically */ assert(csp->headers->last == NULL); } - else if (fwd->forward_host || (http->ssl == 0)) + else if (http->ssl == 0 || (fwd->forward_host +#ifdef FEATURE_HTTPS_FILTERING + && use_ssl_tunnel +#endif + )) { if (send_http_request(csp)) { @@ -3014,17 +3938,51 @@ static void chat(struct client_state *csp) else { /* - * We're running an SSL tunnel and we're not forwarding, - * so just ditch the client headers, send the "connect succeeded" - * message to the client, flush the rest, and get out of the way. + * Using old solution with SSL tunnel or new solution with SSL proxy */ list_remove_all(csp->headers); - if (write_socket(csp->cfd, CSUCCEED, strlen(CSUCCEED))) +#ifdef FEATURE_HTTPS_FILTERING + if (use_ssl_tunnel) +#endif { - return; + /* + * We're running an SSL tunnel and we're not forwarding, + * so just ditch the client headers, send the "connect succeeded" + * message to the client, flush the rest, and get out of the way. + */ + if (write_socket_delayed(csp->cfd, CSUCCEED, + strlen(CSUCCEED), get_write_delay(csp))) + { + return; + } + } +#ifdef FEATURE_HTTPS_FILTERING + else + { + /* + * If server certificate is invalid, we must inform client and then + * close connection with client. + */ + if (csp->server_cert_verification_result != SSL_CERT_VALID) + { + ssl_send_certificate_error(csp); + close_client_and_server_ssl_connections(csp); + return; + } + if (send_https_request(csp)) + { + rsp = error_response(csp, "connect-failed"); + if (rsp) + { + send_crunch_response(csp, rsp); /* XXX: use ssl*/ + } + close_client_and_server_ssl_connections(csp); + return; + } } +#endif /* def FEATURE_HTTPS_FILTERING */ clear_iob(csp->client_iob); - } + }/* -END- else ... if (http->ssl == 1) */ log_error(LOG_LEVEL_CONNECT, "to %s successful", http->hostport); @@ -3032,6 +3990,7 @@ static void chat(struct client_state *csp) csp->server_connection.request_sent = time(NULL); handle_established_connection(csp); + freez(csp->receive_buffer); } @@ -3090,6 +4049,7 @@ extern int fuzz_server_response(struct client_state *csp, char *fuzz_input_file) cgi_init_error_messages(); handle_established_connection(csp); + freez(csp->receive_buffer); return 0; } @@ -3188,11 +4148,7 @@ static void prepare_csp_for_next_request(struct client_state *csp) * Returns : N/A * *********************************************************************/ -#ifdef AMIGA -void serve(struct client_state *csp) -#else /* ifndef AMIGA */ static void serve(struct client_state *csp) -#endif /* def AMIGA */ { int config_file_change_detected = 0; /* Only used for debugging */ #ifdef FEATURE_CONNECTION_KEEP_ALIVE @@ -3562,6 +4518,19 @@ static void initialize_mutexes(void) /* * Prepare global mutex semaphores */ + +#ifdef LIMIT_MUTEX_NUMBER + int i = 0; + for (i = 0; i < 32; i++) +#else + int i = 0; + for (i = 0; i < 65536; i++) +#endif /* LIMIT_MUTEX_NUMBER */ + { + privoxy_mutex_init(&(certificates_mutexes[i])); + } + privoxy_mutex_init(&rng_mutex); + privoxy_mutex_init(&log_mutex); privoxy_mutex_init(&log_init_mutex); privoxy_mutex_init(&connection_reuse_mutex); @@ -3837,9 +4806,7 @@ int main(int argc, char **argv) clients->next = NULL; /* XXX: factor out initialising after the next stable release. */ -#ifdef AMIGA - InitAmiga(); -#elif defined(_WIN32) +#ifdef _WIN32 InitWin32(); #endif @@ -3862,7 +4829,7 @@ int main(int argc, char **argv) * are handled when and where they occur without relying * on a signal. */ -#if !defined(_WIN32) && !defined(__OS2__) && !defined(AMIGA) +#if !defined(_WIN32) && !defined(__OS2__) { int idx; const int catched_signals[] = { SIGTERM, SIGINT, SIGHUP }; @@ -4003,8 +4970,10 @@ int main(int argc, char **argv) * As soon as we have written the PID file, we can switch * to the user and group ID indicated by the --user option */ - write_pid_file(); - + if (pidfile != NULL) + { + write_pid_file(pidfile); + } if (NULL != pw) { if (setgid((NULL != grp) ? grp->gr_gid : pw->pw_gid)) @@ -4322,12 +5291,12 @@ static void listen_loop(void) for (;;) #endif { -#if !defined(FEATURE_PTHREAD) && !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) +#if !defined(FEATURE_PTHREAD) && !defined(_WIN32) && !defined(__BEOS__) && !defined(__OS2__) while (waitpid(-1, NULL, WNOHANG) > 0) { /* zombie children */ } -#endif /* !defined(FEATURE_PTHREAD) && !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) */ +#endif /* !defined(FEATURE_PTHREAD) && !defined(_WIN32) && !defined(__BEOS__) */ /* * Free data that was used by died threads @@ -4364,13 +5333,6 @@ static void listen_loop(void) if (!accept_connection(csp, bfds)) { log_error(LOG_LEVEL_CONNECT, "accept failed: %E"); - -#ifdef AMIGA - if (!childs) - { - exit(1); - } -#endif freez(csp_list); continue; } @@ -4433,8 +5395,8 @@ static void listen_loop(void) log_error(LOG_LEVEL_CONNECT, "Rejecting connection from %s. Maximum number of connections reached.", csp->ip_addr_str); - write_socket(csp->cfd, TOO_MANY_CONNECTIONS_RESPONSE, - strlen(TOO_MANY_CONNECTIONS_RESPONSE)); + write_socket_delayed(csp->cfd, TOO_MANY_CONNECTIONS_RESPONSE, + strlen(TOO_MANY_CONNECTIONS_RESPONSE), get_write_delay(csp)); close_socket(csp->cfd); freez(csp->ip_addr_str); freez(csp->listen_addr_str); @@ -4499,34 +5461,6 @@ static void listen_loop(void) } #endif -#if defined(AMIGA) && !defined(SELECTED_ONE_OPTION) -#define SELECTED_ONE_OPTION - csp->cfd = ReleaseSocket(csp->cfd, -1); - -#ifdef __amigaos4__ - child_id = (int)CreateNewProcTags(NP_Entry, (ULONG)server_thread, - NP_Output, Output(), - NP_CloseOutput, FALSE, - NP_Name, (ULONG)"privoxy child", - NP_Child, TRUE, - TAG_DONE); -#else - child_id = (int)CreateNewProcTags(NP_Entry, (ULONG)server_thread, - NP_Output, Output(), - NP_CloseOutput, FALSE, - NP_Name, (ULONG)"privoxy child", - NP_StackSize, 200*1024, - TAG_DONE); -#endif - if (0 != child_id) - { - childs++; - ((struct Task *)child_id)->tc_UserData = csp; - Signal((struct Task *)child_id, SIGF_SINGLE); - Wait(SIGF_SINGLE); - } -#endif - #if !defined(SELECTED_ONE_OPTION) child_id = fork(); @@ -4614,8 +5548,8 @@ static void listen_loop(void) log_error(LOG_LEVEL_ERROR, "Unable to take any additional connections: %E. Active threads: %d", active_threads); - write_socket(csp->cfd, TOO_MANY_CONNECTIONS_RESPONSE, - strlen(TOO_MANY_CONNECTIONS_RESPONSE)); + write_socket_delayed(csp->cfd, TOO_MANY_CONNECTIONS_RESPONSE, + strlen(TOO_MANY_CONNECTIONS_RESPONSE), get_write_delay(csp)); close_socket(csp->cfd); csp->flags &= ~CSP_FLAG_ACTIVE; } @@ -4632,7 +5566,15 @@ static void listen_loop(void) /* NOTREACHED unless FEATURE_GRACEFUL_TERMINATION is defined */ +#ifdef FEATURE_HTTPS_FILTERING /* Clean up. Aim: free all memory (no leaks) */ + if (rng_seeded == 1) + { + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + } +#endif + #ifdef FEATURE_GRACEFUL_TERMINATION log_error(LOG_LEVEL_ERROR, "Graceful termination requested");