From: Fabian Keil Date: Thu, 24 Jan 2019 12:24:14 +0000 (+0100) Subject: Add TLS/SSL interception support X-Git-Tag: v_3_0_29~585 X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=commitdiff_plain;h=2111876638f912fa7be56a3df315efbbfde91f38 Add TLS/SSL interception support To enable it, install mbedtls, configure with --with-mbedtls, set the TLS directives in the config file and enable the +enable-https-filtering action. Based on a patch by Václav Švec. Integration and clean-ups sponsored by Robert Klemme. --- diff --git a/GNUmakefile.in b/GNUmakefile.in index 9c496f3b..80833561 100644 --- a/GNUmakefile.in +++ b/GNUmakefile.in @@ -231,6 +231,10 @@ W32_HDRS = @WIN_ONLY@w32log.h w32taskbar.h win32.h w32res.h w32svrapi.h W32_LIB = @WIN_ONLY@-lwsock32 -lcomctl32 W32_INIS = @WIN_ONLY@config.txt trust.txt +SSL_SRC = @FEATURE_HTTPS_FILTERING_ONLY@ssl.c +SSL_OBJS = @FEATURE_HTTPS_FILTERING_ONLY@$(SSL_SRC:.c=.o) +SSL_HDRS = @FEATURE_HTTPS_FILTERING_ONLY@$(SSL_SRC:.c=.h) project.h + PCRS_SRC = @STATIC_PCRS_ONLY@pcrs.c PCRS_OBJS = @STATIC_PCRS_ONLY@$(PCRS_SRC:.c=.@OBJEXT@) PCRS_HDRS = @STATIC_PCRS_ONLY@$(PCRS_SRC:.c=.h) @@ -255,9 +259,9 @@ SOCKET_LIB = @SOCKET_LIB@ # PThreads library, if needed. PTHREAD_LIB = @PTHREAD_ONLY@@PTHREAD_LIB@ -SRCS = $(C_SRC) $(CLIENT_TAG_SRC) $(FUZZ_SRC) $(W32_SRC) $(PCRS_SRC) $(PCRE_SRC) $(REGEX_SRC) -OBJS = $(C_OBJS) $(CLIENT_TAG_OBJS) $(FUZZ_OBJS) $(W32_OBJS) $(PCRS_OBJS) $(PCRE_OBJS) $(REGEX_OBJS) -HDRS = $(C_HDRS) $(W32_HDRS) $(PCRS_HDRS) $(PCRE_OBJS) $(REGEX_HDRS) +SRCS = $(C_SRC) $(CLIENT_TAG_SRC) $(FUZZ_SRC) $(W32_SRC) $(PCRS_SRC) $(PCRE_SRC) $(REGEX_SRC) $(SSL_SRC) +OBJS = $(C_OBJS) $(CLIENT_TAG_OBJS) $(FUZZ_OBJS) $(W32_OBJS) $(PCRS_OBJS) $(PCRE_OBJS) $(REGEX_OBJS) $(SSL_OBJS) +HDRS = $(C_HDRS) $(W32_HDRS) $(PCRS_HDRS) $(PCRE_OBJS) $(REGEX_HDRS) $(SSL_HDRS) LIBS = @LIBS@ $(W32_LIB) $(SOCKET_LIB) $(PTHREAD_LIB) @@ -276,6 +280,7 @@ SPECIAL_CFLAGS = @SPECIAL_CFLAGS@ OTHER_CFLAGS = CFLAGS = @CFLAGS@ @CPPFLAGS@ $(OTHER_CFLAGS) $(SPECIAL_CFLAGS) -Wall \ + -Imbedtls/include \ @STATIC_PCRE_ONLY@ -Ipcre LDFLAGS = @LDFLAGS@ $(DEBUG_CFLAGS) $(SPECIAL_CFLAGS) diff --git a/actionlist.h b/actionlist.h index 01d4d371..eca607b5 100644 --- a/actionlist.h +++ b/actionlist.h @@ -73,6 +73,9 @@ DEFINE_ACTION_STRING ("delay-response", ACTION_DELAY_RESPONSE, DEFINE_CGI_PARAM_NO_RADIO("delay-response", ACTION_DELAY_RESPONSE, ACTION_STRING_DELAY_RESPONSE, "100") DEFINE_CGI_PARAM_RADIO ("deanimate-gifs", ACTION_DEANIMATE, ACTION_STRING_DEANIMATE, "last", 1) DEFINE_ACTION_BOOL ("downgrade-http-version", ACTION_DOWNGRADE) +#ifdef FEATURE_HTTPS_FILTERING +DEFINE_ACTION_BOOL ("enable-https-filtering", ACTION_ENABLE_HTTPS_FILTER) +#endif #ifdef FEATURE_EXTERNAL_FILTERS DEFINE_ACTION_MULTI ("external-filter", ACTION_MULTI_EXTERNAL_FILTER) #endif @@ -107,6 +110,9 @@ DEFINE_CGI_PARAM_RADIO ("hide-referrer", ACTION_HIDE_REFERER, DEFINE_CGI_PARAM_CUSTOM ("hide-referrer", ACTION_HIDE_REFERER, ACTION_STRING_REFERER, "http://www.privoxy.org/") DEFINE_ACTION_STRING ("hide-user-agent", ACTION_HIDE_USER_AGENT, ACTION_STRING_USER_AGENT) DEFINE_CGI_PARAM_NO_RADIO("hide-user-agent", ACTION_HIDE_USER_AGENT, ACTION_STRING_USER_AGENT, "Privoxy " VERSION) +#ifdef FEATURE_HTTPS_FILTERING +DEFINE_ACTION_BOOL ("ignore-certificate-errors", ACTION_IGNORE_CERTIFICATE_ERRORS) +#endif DEFINE_ACTION_STRING ("limit-connect", ACTION_LIMIT_CONNECT, ACTION_STRING_LIMIT_CONNECT) DEFINE_CGI_PARAM_NO_RADIO("limit-connect", ACTION_LIMIT_CONNECT, ACTION_STRING_LIMIT_CONNECT, "443") DEFINE_ACTION_STRING ("limit-cookie-lifetime", ACTION_LIMIT_COOKIE_LIFETIME, ACTION_STRING_LIMIT_COOKIE_LIFETIME) diff --git a/configure.in b/configure.in index e1a8e9dd..87395359 100644 --- a/configure.in +++ b/configure.in @@ -1131,6 +1131,36 @@ dnl fi AC_SUBST(STATIC_PCRE_ONLY) AC_SUBST(STATIC_PCRS_ONLY) +dnl ======================================================= +dnl check for mbedTLS which is required for https filtering +dnl ======================================================= +FEATURE_HTTPS_FILTERING_ONLY=# +OPT_MBEDTLS=no +AC_ARG_WITH(mbedtls,dnl +AC_HELP_STRING([--with-mbedtls],[enable mbedTLS detection for https filtering.]) +AC_HELP_STRING([--without-mbedtls], [disable mbedTLS detection]), + OPT_MBEDTLS=$withval) + +if test X"$OPT_MBEDTLS" != Xno; then + + AC_CHECK_LIB(mbedtls, mbedtls_ssl_init, + [ + AC_DEFINE(FEATURE_HTTPS_FILTERING, 1, [if mbedTLS is enabled]) + AC_SUBST(FEATURE_HTTPS_FILTERING, [1]) + FEATURE_HTTPS_FILTERING="yes" + ], [], -lmbedx509 -lmbedcrypto) + + if test "x$FEATURE_HTTPS_FILTERING" = "xyes"; then + AC_MSG_NOTICE([Detected mbedTLS. Enabling https filtering.]) + + LIBS="-lmbedtls -lmbedx509 -lmbedcrypto $LIBS" + + FEATURE_HTTPS_FILTERING_ONLY= + fi +fi +AC_SUBST(FEATURE_HTTPS_FILTERING_ONLY) + + dnl ================================================================= dnl Final cleanup and output dnl ================================================================= diff --git a/doc/source/p-config.sgml b/doc/source/p-config.sgml index 0ca50062..9bd21ebc 100644 --- a/doc/source/p-config.sgml +++ b/doc/source/p-config.sgml @@ -107,7 +107,8 @@ Copyright (C) 2001-2019 Privoxy Developers https://www.privoxy.org/ 4. ACCESS CONTROL AND SECURITY # 5. FORWARDING # 6. MISCELLANEOUS # - 7. WINDOWS GUI OPTIONS # + 7. TLS # + 8. WINDOWS GUI OPTIONS # # ################################################################## @@ -3895,6 +3896,368 @@ forward-socks4, forward-socks4a, forward-socks5 and forward-socks5t + + +TLS/SSL + + + +ca-directory + + + Specifies: + + + Directory with the CA key, the CA certificate and the trusted CAs file. + + + + + Type of value: + + + Text + + + + + Default value: + + Empty string + + + + Effect if unset: + + + Default value is used. + + + + + Notes: + + + This directive specifies the directory where the + CA key, the CA certificate and the trusted CAs file + are located. + + + + + Examples: + + + ca-directory /usr/local/etc/privoxy/CA + + + + +@@#ca-directory /usr/local/etc/privoxy/CA]]> + + + + + + +ca-cert-file + + + Specifies: + + + The CA certificate file in ".crt" format. + + + + + Type of value: + + + Text + + + + + Default value: + + cacert.crt + + + + Effect if unset: + + + Default value is used. + + + + + Notes: + + + This directive specifies the name of the CA certificate file + in ".crt" format. + + + It can be generated with: openssl req -new -x509 -extensions v3_ca -keyout cakey.pem -out cacert.crt -days 3650 + + + + + Examples: + + + ca-cert-file root.crt + + + + +@@#ca-cert-file cacert.crt]]> + + + + + + +ca-key-file + + + Specifies: + + + The CA key file in ".pem" format. + + + + + Type of value: + + + Text + + + + + Default value: + + cacert.pem + + + + Effect if unset: + + + Default value is used. + + + + + Notes: + + + This directive specifies the name of the CA key file + in ".pem" format. See the ca-cert-file + for a command to generate it. + + + + + Examples: + + + ca-key-file cakey.pem + + + + +@@#ca-key-file root.pem]]> + + + + + + +ca-password + + + Specifies: + + + The password for the CA keyfile. + + + + + Type of value: + + + Text + + + + + Default value: + + Empty string + + + + Effect if unset: + + + Default value is used. + + + + + Notes: + + + This directive specifies the password for the CA keyfile + that is used when Privoxy generates certificates for intercepted + requests. + + + Note that the password is shown on the CGI page so don't + reuse an important one. + + + + + Examples: + + + ca-password blafasel + + + + +@@#ca-password swordfish]]> + + + + + + +certificate-directory + + + Specifies: + + + Directory to safe generated keys and certificates. + + + + + Type of value: + + + Text + + + + + Default value: + + ./certs + + + + Effect if unset: + + + Default value is used. + + + + + Notes: + + + This directive specifies the directory where generated + TLS/SSL keys and certificates are saved. + + + + + Examples: + + + certificate-directory /usr/local/var/privoxy/certs + + + + +@@#certificate-directory /usr/local/var/privoxy/certs]]> + + + + + + +trusted-cas-file + + + Specifies: + + + The trusted CAs file in ".pem" format. + + + + + Type of value: + + + File name relative to ca-directory + + + + + Default value: + + trustedCAs.pem + + + + Effect if unset: + + + Default value is used. + + + + + Notes: + + + This directive specifies the trusted CAs file that is used when validating + certificates for intercepted TLS/SSL request. + + + An example file can be downloaded from + https://curl.haxx.se/ca/cacert.pem. + + + + + Examples: + + + trusted-cas-file trusted_cas_file.pem + + + + +@@#trusted-cas-file trustedCAs.pem]]> + + + + + + diff --git a/doc/source/user-manual.sgml b/doc/source/user-manual.sgml index 9483f736..7f1307e0 100644 --- a/doc/source/user-manual.sgml +++ b/doc/source/user-manual.sgml @@ -3801,6 +3801,72 @@ problem-host.example.com + + + +enable-https-filtering + + + + Typical use: + + Filter encrypted requests and responses + + + + + Effect: + + + Encrypted requests are decrypted, filtered and forwarded encrypted. + + + + + + Type: + + + Boolean. + + + + + Parameter: + + + N/A + + + + + + Notes: + + + This action allows &my-app; to filter encrypted requests and responses. + For this to work &my-app; has to generate a certificate and send it + to the client which has to accept it. + + + Before this works the directives in the + TLS section of the config + file have to be configured. + + + + + + Example usage (section): + + {+enable-https-filtering} +www.example.com + + + + + + external-filter @@ -5146,6 +5212,76 @@ new action + + +ignore-certificate-errors + + + + Typical use: + + Filter encrypted requests and responses without verifying the certificate + + + + + Effect: + + + Encrypted requests are forwarded to sites without verifying the certificate. + + + + + + Type: + + + Boolean. + + + + + Parameter: + + + N/A + + + + + + Notes: + + + When the + +enable-https-filtering + action is used &my-app; by default verifies that the remote site uses a valid + certificate. + + + If the certificate is invalid the connection is aborted. + + + This action disabled the certificate check allowing requests to sites + with invalid certificates. + + + + + + Example usage: + + + {+ignore-certificate-errors} + www.example.org + + + + + + + limit-connect diff --git a/jcc.c b/jcc.c index 34327f28..c30e75d1 100644 --- a/jcc.c +++ b/jcc.c @@ -112,6 +112,9 @@ #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" @@ -190,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 @@ -941,8 +951,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. @@ -2018,6 +2026,24 @@ static void handle_established_connection(struct client_state *csp) 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; @@ -2116,66 +2142,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); - 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); - 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. */ @@ -2240,38 +2327,76 @@ static void handle_established_connection(struct client_state *csp) assert(max_bytes_to_read <= csp->receive_buffer_size); #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ - len = read_socket(csp->cfd, csp->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, csp->receive_buffer, (size_t)len)) - { - log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host); - mark_server_socket_tainted(csp); - 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; } @@ -2304,18 +2429,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); +#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, csp->receive_buffer, (int)csp->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 @@ -2338,6 +2484,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); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif return; } /* @@ -2403,7 +2552,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, @@ -2450,16 +2603,43 @@ static void handle_established_connection(struct client_state *csp) log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header"); } - 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)) +#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); - 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); @@ -2485,11 +2665,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) { @@ -2517,20 +2701,51 @@ static void handle_established_connection(struct client_state *csp) rsp = cgi_error_memory(); send_crunch_response(csp, rsp); mark_server_socket_tainted(csp); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif return; } hdrlen = strlen(hdr); - 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)) +#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); - 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; + } } /* @@ -2546,12 +2761,36 @@ static void handle_established_connection(struct client_state *csp) } else { - if (write_socket_delayed(csp->cfd, csp->receive_buffer, - (size_t)len, write_delay)) +#ifdef FEATURE_HTTPS_FILTERING + /* + * Sending data with standard or secured connection (HTTP/HTTPS) + */ + if (client_use_ssl(csp)) { - log_error(LOG_LEVEL_ERROR, "write to client failed: %E"); - mark_server_socket_tainted(csp); - return; + 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 */ + { + 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; @@ -2570,6 +2809,9 @@ static void handle_established_connection(struct client_state *csp) rsp = cgi_error_memory(); send_crunch_response(csp, rsp); mark_server_socket_tainted(csp); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif return; } @@ -2587,10 +2829,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_delayed(csp->cfd, - INVALID_SERVER_HEADERS_RESPONSE, - strlen(INVALID_SERVER_HEADERS_RESPONSE), write_delay); +#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); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif return; } else @@ -2636,11 +2895,18 @@ static void handle_established_connection(struct client_state *csp) } free_http_request(http); mark_server_socket_tainted(csp); +#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)) { @@ -2657,10 +2923,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_delayed(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE, - strlen(INVALID_SERVER_HEADERS_RESPONSE), write_delay); +#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; } @@ -2672,10 +2955,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_delayed(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE, - strlen(INVALID_SERVER_HEADERS_RESPONSE), write_delay); +#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); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif return; } hdr = list_to_text(csp->headers); @@ -2710,39 +3010,66 @@ static void handle_established_connection(struct client_state *csp) */ freez(hdr); mark_server_socket_tainted(csp); +#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_delayed(csp->cfd, hdr, strlen(hdr), write_delay) - || ((len = flush_iob(csp->cfd, csp->iob, write_delay)) < 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); - 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 */ @@ -2761,18 +3088,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_delayed(csp->cfd, INVALID_SERVER_HEADERS_RESPONSE, - strlen(INVALID_SERVER_HEADERS_RESPONSE), write_delay); +#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); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif return; } } continue; } mark_server_socket_tainted(csp); +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif return; /* huh? we should never get here */ } - +#ifdef FEATURE_HTTPS_FILTERING + close_client_and_server_ssl_connections(csp); +#endif if (csp->content_length == 0) { /* @@ -2830,6 +3179,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; @@ -2851,43 +3203,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 = @@ -2898,13 +3286,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. @@ -2964,6 +3354,9 @@ static void chat(struct client_state *csp) } #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ + /* + * Connecting to destination server + */ csp->server_connection.sfd = forwarded_connect(fwd, http, csp); if (csp->server_connection.sfd == JB_INVALID_SOCKET) @@ -3005,6 +3398,141 @@ 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); + 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); + 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); + 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); + 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); @@ -3020,7 +3548,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)) { @@ -3035,18 +3567,68 @@ 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_delayed(csp->cfd, CSUCCEED, - strlen(CSUCCEED), get_write_delay(csp))) +#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 + { + 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) + { + if (write_socket_delayed(csp->cfd, CSUCCEED, + strlen(CSUCCEED), get_write_delay(csp)) != 0) + { + log_error(LOG_LEVEL_ERROR, "Sending SUCCEED to client failed"); + close_client_and_server_ssl_connections(csp); + return; + } + } + + ret = create_client_ssl_connection(csp); + if (ret != 0) + { + log_error(LOG_LEVEL_ERROR, + "Can't open secure connection with client"); + close_client_and_server_ssl_connections(csp); + return; + } + + /* + * 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; + } } +#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); @@ -3582,6 +4164,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); @@ -4617,7 +5212,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"); diff --git a/jcc.h b/jcc.h index 866c2a7b..2df07553 100644 --- a/jcc.h +++ b/jcc.h @@ -102,6 +102,13 @@ extern privoxy_mutex_t resolver_mutex; extern privoxy_mutex_t rand_mutex; #endif /* ndef HAVE_RANDOM */ +#ifdef LIMIT_MUTEX_NUMBER + extern privoxy_mutex_t certificates_mutexes[32]; +#else + extern privoxy_mutex_t certificates_mutexes[65536]; +#endif /* LIMIT_MUTEX_NUMBER */ +extern privoxy_mutex_t rng_mutex; + #endif /* FEATURE_PTHREAD */ /* Functions */ diff --git a/loadcfg.c b/loadcfg.c index 256dccfc..73fa6e32 100644 --- a/loadcfg.c +++ b/loadcfg.c @@ -135,6 +135,11 @@ static struct file_list *current_configfile = NULL; #define hash_admin_address 4112573064U /* "admin-address" */ #define hash_allow_cgi_request_crunching 258915987U /* "allow-cgi-request-crunching" */ #define hash_buffer_limit 1881726070U /* "buffer-limit */ +#define hash_ca_cert_file 1622923720U /* "ca-cert-file" */ +#define hash_ca_directory 1623615670U /* "ca-directory" */ +#define hash_ca_key_file 1184187891U /* "ca-key-file" */ +#define hash_ca_password 1184543320U /* "ca-password" */ +#define hash_certificate_directory 1367994217U /* "certificate-directory" */ #define hash_client_header_order 2701453514U /* "client-header-order" */ #define hash_client_specific_tag 3353703383U /* "client-specific-tag" */ #define hash_client_tag_lifetime 647957580U /* "client-tag-lifetime" */ @@ -181,6 +186,7 @@ static struct file_list *current_configfile = NULL; #define hash_trust_info_url 430331967U /* "trust-info-url" */ #define hash_trust_x_forwarded_for 2971537414U /* "trust-x-forwarded-for" */ #define hash_trusted_cgi_referrer 4270883427U /* "trusted-cgi-referrer" */ +#define hash_trusted_cas_file 2679803024U /* "trusted-cas-files" */ #define hash_trustfile 56494766U /* "trustfile" */ #define hash_usermanual 1416668518U /* "user-manual" */ #define hash_activity_animation 1817904738U /* "activity-animation" */ @@ -272,6 +278,15 @@ static void unload_configfile (void * data) freez(config->usermanual); freez(config->trusted_cgi_referrer); +#ifdef FEATURE_HTTPS_FILTERING + freez(config->ca_password); + freez(config->ca_directory); + freez(config->ca_cert_file); + freez(config->ca_key_file); + freez(config->certificate_directory); + freez(config->trusted_cas_file); +#endif + #ifdef FEATURE_TRUST freez(config->trustfile); list_remove_all(config->trust_info); @@ -568,7 +583,14 @@ struct configuration_spec * load_config(void) struct file_list *fs; unsigned long linenum = 0; int i; - char *logfile = NULL; + char *logfile = NULL; +#ifdef FEATURE_HTTPS_FILTERING + char *ca_cert_file = NULL; + char *ca_key_file = NULL; + char *ca_directory = NULL; + char *trusted_cas_file = NULL; + char *certificate_directory = NULL; +#endif if (!check_file_changed(current_configfile, configfile, &fs)) { @@ -613,6 +635,15 @@ struct configuration_spec * load_config(void) config->usermanual = strdup_or_die(USER_MANUAL_URL); config->proxy_args = strdup_or_die(""); config->forwarded_connect_retries = 0; +#ifdef FEATURE_HTTPS_FILTERING + config->ca_password = strdup(""); + ca_cert_file = strdup("cacert.crt"); + ca_key_file = strdup("cakey.pem"); + ca_directory = strdup("./CA"); + trusted_cas_file = strdup("trustedCAs.pem"); + certificate_directory = strdup("./certs"); +#endif + #ifdef FEATURE_CLIENT_TAGS config->client_tag_lifetime = 60; #endif @@ -1702,6 +1733,84 @@ struct configuration_spec * load_config(void) config->usermanual = strdup_or_die(arg); break; +#ifdef FEATURE_HTTPS_FILTERING +/* ************************************************************************* + * ca private key file password + * *************************************************************************/ + case hash_ca_password: + freez(config->ca_password); + config->ca_password = strdup(arg); + break; + +/* ************************************************************************* + * ca-directory directory + * *************************************************************************/ + case hash_ca_directory: + ca_directory = make_path(NULL, arg); + + if (NULL == ca_directory) + { + log_error(LOG_LEVEL_FATAL, "Out of memory while creating ca dir path"); + } + + break; + +/* ************************************************************************* + * ca cert file ca-cert-file + * In ca dir by default + * *************************************************************************/ + case hash_ca_cert_file: + ca_cert_file = make_path(config->ca_directory, arg); + + if (NULL == ca_cert_file) + { + log_error(LOG_LEVEL_FATAL, "Out of memory while creating ca certificate file path"); + } + + break; + +/* ************************************************************************* + * ca key file ca-key-file + * In ca dir by default + * *************************************************************************/ + case hash_ca_key_file: + ca_key_file = make_path(config->ca_directory, arg); + + if (NULL == ca_key_file) + { + log_error(LOG_LEVEL_FATAL, "Out of memory while creating ca key file path"); + } + + break; + +/* ************************************************************************* + * certificate-directory directory + * *************************************************************************/ + case hash_certificate_directory: + certificate_directory = make_path(NULL, arg); + + if (NULL == certificate_directory) + { + log_error(LOG_LEVEL_FATAL, + "Out of memory while creating certificate directory path"); + } + + break; + +/* ************************************************************************* + * trusted CAs file name trusted-cas-file + * *************************************************************************/ + case hash_trusted_cas_file: + trusted_cas_file = make_path(config->ca_directory, arg); + + if (NULL == trusted_cas_file) + { + log_error(LOG_LEVEL_FATAL, "Out of memory while creating trusted CAs file path"); + } + + break; +#endif + /* ************************************************************************* * Win32 Console options: * *************************************************************************/ @@ -1877,6 +1986,30 @@ struct configuration_spec * load_config(void) } } +#ifdef FEATURE_HTTPS_FILTERING + /* + * Setting SSL parameters from loaded values into structures + */ + freez(config->ca_directory); + config->ca_directory = make_path(NULL, ca_directory); + freez(ca_directory); + + freez(config->ca_cert_file); + config->ca_cert_file = make_path(config->ca_directory, ca_cert_file); + freez(ca_cert_file); + + freez(config->ca_key_file); + config->ca_key_file = make_path(config->ca_directory, ca_key_file); + freez(ca_key_file); + + freez(config->trusted_cas_file); + config->trusted_cas_file = make_path(config->ca_directory, trusted_cas_file); + freez(trusted_cas_file); + + freez(config->certificate_directory); + config->certificate_directory = make_path(NULL, certificate_directory); + freez(certificate_directory); +#endif #ifdef FEATURE_CONNECTION_KEEP_ALIVE if (config->default_server_timeout > config->keep_alive_timeout) { diff --git a/parsers.c b/parsers.c index 699ae7c5..f56345fe 100644 --- a/parsers.c +++ b/parsers.c @@ -4429,8 +4429,6 @@ jb_err get_destination_from_headers(const struct list *headers, struct http_requ char *p; char *host; - assert(!http->ssl); - host = get_header_value(headers, "Host:"); if (NULL == host) diff --git a/project.h b/project.h index eed9f870..75aaa65e 100644 --- a/project.h +++ b/project.h @@ -44,6 +44,34 @@ /* Needed for pcre choice */ #include "config.h" +#ifdef FEATURE_HTTPS_FILTERING +#ifdef FEATURE_PTHREAD +# include + typedef pthread_mutex_t privoxy_mutex_t; +#else +# ifdef _WIN32 +# include +# endif + typedef CRITICAL_SECTION privoxy_mutex_t; +#endif + +#include "mbedtls/net_sockets.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" + +#if defined(MBEDTLS_SSL_CACHE_C) +#include "mbedtls/ssl_cache.h" +#endif + +/* +* Macros for SSL structures +*/ +#define CERT_INFO_BUF_SIZE 4096 +#define CERT_FILE_BUF_SIZE 16384 +#define ISSUER_NAME_BUF_SIZE 2048 +#define HASH_OF_HOST_BUF_SIZE 16 +#endif + /* Need for struct sockaddr_storage */ #ifdef HAVE_RFC2553 # ifndef _WIN32 @@ -259,6 +287,23 @@ struct map struct map_entry *last; }; +#ifdef FEATURE_HTTPS_FILTERING +/* + * Struct of attributes necessary for TLS/SSL connection + */ +typedef struct { + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + mbedtls_net_context socket_fd; + mbedtls_x509_crt server_cert; + mbedtls_x509_crt ca_cert; + mbedtls_pk_context prim_key; + + #if defined(MBEDTLS_SSL_CACHE_C) + mbedtls_ssl_cache_context cache; + #endif +} mbedtls_connection_attr; +#endif /** * A HTTP request. This includes the method (GET, POST) and @@ -291,8 +336,55 @@ struct http_request char **dvec; /**< List of pointers to the strings in dbuffer. */ int dcount; /**< How many parts to this domain? (length of dvec) */ #endif /* ndef FEATURE_EXTENDED_HOST_PATTERNS */ + +#ifdef FEATURE_HTTPS_FILTERING + int client_ssl; /**< Flag if we should comunicate with slient over ssl */ + int server_ssl; /**< Flag if we should comunicate with server over ssl */ + unsigned char hash_of_host_hex[(HASH_OF_HOST_BUF_SIZE * 2) + 1]; /**< chars for hash in hex string and one for '\0' */ + unsigned char hash_of_host[HASH_OF_HOST_BUF_SIZE+1]; /**< chars for bytes of hash and one for '\0' */ +#endif }; + +#ifdef FEATURE_HTTPS_FILTERING +/* + * Properties of cert for generating + */ +typedef struct{ + char *issuer_crt; /* filename of the issuer certificate */ + char *subject_key; /* filename of the subject key file */ + char *issuer_key; /* filename of the issuer key file */ + const char *subject_pwd; /* password for the subject key file */ + const char *issuer_pwd; /* password for the issuer key file */ + char *output_file; /* where to store the constructed key file */ + const char *subject_name; /* subject name for certificate */ + char issuer_name[ISSUER_NAME_BUF_SIZE]; /* issuer name for certificate */ + const char *not_before; /* validity period not before */ + const char *not_after; /* validity period not after */ + const char *serial; /* serial number string */ + int is_ca; /* is a CA certificate */ + int max_pathlen; /* maximum CA path length */ +} cert_options; + +/* + * Properties of key for generating + */ +typedef struct{ + mbedtls_pk_type_t type; /* type of key to generate */ + int rsa_keysize; /* length of key in bits */ + char *key_file_path; /* filename of the key file */ +} key_options; + +/* + * Struct for linked list containing certificates + */ +typedef struct certs_chain { + char text_buf[CERT_INFO_BUF_SIZE]; /* text info about properties of certificate */ + char file_buf[CERT_FILE_BUF_SIZE]; /* buffer for whole certificate - format to save in file */ + struct certs_chain *next; /* next certificate in chain of trust */ +}certs_chain_t; +#endif + /** * Reasons for generating a http_response instead of delivering * the requested resource. Mostly ordered the way they are checked @@ -503,7 +595,10 @@ struct iob #define ACTION_LIMIT_COOKIE_LIFETIME 0x08000000UL /** Action bitmap: Delay writes */ #define ACTION_DELAY_RESPONSE 0x10000000UL - +/** Action bitmap: Turn https filtering on */ +#define ACTION_ENABLE_HTTPS_FILTER 0x20000000UL +/** Action bitmap: Turn certificates verification off */ +#define ACTION_IGNORE_CERTIFICATE_ERRORS 0x40000000UL /** Action string index: How to deanimate GIFs */ #define ACTION_STRING_DEANIMATE 0 @@ -948,6 +1043,11 @@ struct client_state /* XXX: should be renamed to server_iob */ struct iob iob[1]; +#ifdef FEATURE_HTTPS_FILTERING + mbedtls_connection_attr mbedtls_server_attr; /* attributes for connection to server */ + mbedtls_connection_attr mbedtls_client_attr; /* attributes for connection to client */ +#endif + /** An I/O buffer used for buffering data read from the client */ struct iob client_iob[1]; @@ -1010,6 +1110,28 @@ struct client_state * or NULL. Currently only used for socks errors. */ char *error_message; + +#ifdef FEATURE_HTTPS_FILTERING + /* Result of server certificate verification */ + uint32_t server_cert_verification_result; + + /* Flag for certificate validity checking */ + int dont_verify_certificate; + + /* + * Flags if SSL connection with server or client is opened. + * Thanks to this flags, we can call function to close both connections + * and we don't have to care about more details. + */ + int ssl_with_server_is_opened; + int ssl_with_client_is_opened; + + /* + * Server certificate chain of trust including strings with certificates + * informations and string with whole certificate file + */ + struct certs_chain server_certs_chain; +#endif }; /** @@ -1433,6 +1555,26 @@ struct configuration_spec /** Nonzero if we need to bind() to the new port. */ int need_bind; + +#ifdef FEATURE_HTTPS_FILTERING + /** Password for proxy ca file **/ + char * ca_password; + + /** Directory with files of ca **/ + char *ca_directory; + + /** Filename of ca certificate **/ + char * ca_cert_file; + + /** Filename of ca key **/ + char * ca_key_file; + + /** Directory for saving certificates and keys for each webpage **/ + char *certificate_directory; + + /** Filename of trusted CAs certificates **/ + char * trusted_cas_file; +#endif }; /** Calculates the number of elements in an array, using sizeof. */ diff --git a/ssl.c b/ssl.c new file mode 100644 index 00000000..591e9a50 --- /dev/null +++ b/ssl.c @@ -0,0 +1,2069 @@ +/********************************************************************* +* +* File : $Source: /cvsroot/ijbswa/current/ssl.c,v $ +* +* Purpose : File with TLS/SSL extension. Contains methods for +* creating, using and closing TLS/SSL connections. +* +* Copyright : Written by and Copyright (c) 2017 Vaclav Svec. FIT CVUT. +* Copyright (C) 2018-2019 by Fabian Keil +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will +* be useful, but WITHOUT ANY WARRANTY; without even the +* implied warranty of MERCHANTABILITY or FITNESS FOR A +* PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* The GNU General Public License should be included with +* this file. If not, you can view it at +* http://www.gnu.org/copyleft/gpl.html +* or write to the Free Software Foundation, Inc., 59 +* Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*********************************************************************/ + +#include +#include + +#if !defined(MBEDTLS_CONFIG_FILE) +# include "mbedtls/config.h" +#else +# include MBEDTLS_CONFIG_FILE +#endif + +#include "mbedtls/md5.h" +#include "mbedtls/pem.h" +#include "mbedtls/base64.h" +#include "mbedtls/error.h" + +#include "project.h" +#include "miscutil.h" +#include "errlog.h" +#include "jcc.h" +#include "config.h" +#include "ssl.h" + + +/* + * Macros for searching begin and end of certificates. + * Necessary to convert structure mbedtls_x509_crt to crt file. + */ +#define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n" +#define PEM_END_CRT "-----END CERTIFICATE-----\n" + +/* + * Macros for ssl.c + */ +#define ERROR_BUF_SIZE 1024 /* Size of buffer for error messages */ +#define CERTIFICATE_BUF_SIZE 16384 /* Size of buffer to save certificate. Value 4096 is mbedtls library buffer size for certificate in DER form */ +#define PRIVATE_KEY_BUF_SIZE 16000 /* Size of buffer to save private key. Value 16000 is taken from mbed TLS library examples. */ +#define RSA_KEY_PUBLIC_EXPONENT 65537 /* Public exponent for RSA private key generating */ +#define RSA_KEYSIZE 2048 /* Size of generated RSA keys */ +#define GENERATED_CERT_VALID_FROM "20100101000000" /* Date and time, which will be set in generated certificates as parameter valid from */ +#define GENERATED_CERT_VALID_TO "20401231235959" /* Date and time, which will be setted in generated certificates as parameter valid to */ +#define CERT_SIGNATURE_ALGORITHM MBEDTLS_MD_SHA256 /* The MD algorithm to use for the signature */ +#define CERT_SERIAL_NUM_LENGTH 4 /* Bytes of hash to be used for creating serial number of certificate. Min=2 and max=16 */ +#define LIMIT_MUTEX_NUMBER /* If this macro is defined, mutexes count for generating private keys is changed from 65536 to 32 */ +#define INVALID_CERT_INFO_BUF_SIZE 2048 /* Size of buffer for message with information about reason of certificate invalidity. Data after the end of buffer will not be saved */ +#define CERT_PARAM_COMMON_NAME "CN=" +#define CERT_PARAM_ORGANIZATION ",O=" +#define CERT_PARAM_ORG_UNIT ",OU=" +#define CERT_PARAM_COUNTRY ",C=CZ" +#define KEY_FILE_TYPE ".pem" +#define CERT_FILE_TYPE ".crt" +#define CERT_SUBJECT_PASSWORD "" +#define CERT_INFO_PREFIX "" + + +extern int generate_webpage_certificate(struct client_state * csp); +static char * make_certs_path(const char * conf_dir, const char * file_name, const char * suffix); +static int file_exists(const char * path); +static int host_to_hash(struct client_state *csp); +static int ssl_verify_callback(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags); +static void free_certificate_chain(struct client_state *csp); +static unsigned int get_certificate_mutex_id(struct client_state *csp); +static unsigned long get_certificate_serial(struct client_state *csp); +static void free_client_ssl_structures(struct client_state *csp); +static void free_server_ssl_structures(struct client_state *csp); +static int seed_rng(struct client_state *csp); + +/********************************************************************* + * + * Function : client_use_ssl + * + * Description : Tests if client in current client state structure + * should use SSL connection or standard connection. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : If client should use TLS/SSL connection, 1 is returned. + * Otherwise 0 is returned. + * + *********************************************************************/ +extern int client_use_ssl(const struct client_state *csp) +{ + return csp->http->client_ssl; +} + + +/********************************************************************* + * + * Function : server_use_ssl + * + * Description : Tests if server in current client state structure + * should use SSL connection or standard connection. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : If server should use TLS/SSL connection, 1 is returned. + * Otherwise 0 is returned. + * + *********************************************************************/ +extern int server_use_ssl(const struct client_state *csp) +{ + return csp->http->server_ssl; +} + + +/********************************************************************* +* +* Function : is_ssl_pending +* +* Description : Tests if there are some waitting data on ssl connection +* +* Parameters : +* 1 : ssl = SSL context to test +* +* Returns : 0 => No data are pending +* >0 => Pending data length +* +*********************************************************************/ +extern size_t is_ssl_pending(mbedtls_ssl_context *ssl) +{ + if (ssl == NULL) + { + return 0; + } + + return mbedtls_ssl_get_bytes_avail(ssl); +} + + +/********************************************************************* + * + * Function : ssl_send_data + * + * Description : Sends the content of buf (for n bytes) to given SSL + * connection context. + * + * Parameters : + * 1 : ssl = SSL context to send data to + * 2 : buf = Pointer to data to be sent + * 3 : len = Length of data to be sent to the SSL context + * + * Returns : Length of sent data or negative value on error. + * + *********************************************************************/ +extern int ssl_send_data(mbedtls_ssl_context *ssl, const unsigned char *buf, size_t len) +{ + int ret = 0; + size_t max_fragment_size = 0; /* Maximal length of data in one SSL fragment*/ + int send_len = 0; /* length of one data part to send */ + int pos = 0; /* Position of unsent part in buffer */ + + if (len == 0) + { + return 0; + } + + /* Getting maximal length of data sent in one fragment */ + max_fragment_size = mbedtls_ssl_get_max_frag_len(ssl); + + /* + * Whole buffer must be sent in many fragments, because each fragment + * has its maximal length. + */ + while (pos < len) + { + /* Compute length of data, that can be send in next fragment */ + if ((pos + (int)max_fragment_size) > len) + { + send_len = (int)len - pos; + } + else + { + send_len = (int)max_fragment_size; + } + + /* + * Sending one part of the buffer + */ + while ((ret = mbedtls_ssl_write(ssl, + (const unsigned char *)(buf + pos), + (size_t)send_len)) < 0) + { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && + ret != MBEDTLS_ERR_SSL_WANT_WRITE) + { + char err_buf[ERROR_BUF_SIZE]; + + memset(err_buf, 0, sizeof(err_buf)); + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, + "Sending data over TLS/SSL failed: %s", err_buf); + return -1; + } + } + /* Adding count of sent bytes to position in buffer */ + pos = pos + send_len; + } + + return (int)len; +} + + +/********************************************************************* + * + * Function : ssl_recv_data + * + * Description : Receives data from given SSL context and puts + * it into buffer. + * + * Parameters : + * 1 : ssl = SSL context to receive data from + * 2 : buf = Pointer to buffer where data will be written + * 3 : max_length = Maximum number of bytes to read + * + * Returns : Number of bytes read, 0 for EOF, or negative + * value on error. + * + *********************************************************************/ +extern int ssl_recv_data(mbedtls_ssl_context *ssl, unsigned char *buf, size_t max_length) +{ + int ret = 0; + memset(buf, 0, max_length); + + /* + * Receiving data from SSL context into buffer + */ + do + { + ret = mbedtls_ssl_read(ssl, buf, max_length); + } while (ret == MBEDTLS_ERR_SSL_WANT_READ + || ret == MBEDTLS_ERR_SSL_WANT_WRITE); + + if (ret < 0) + { + char err_buf[ERROR_BUF_SIZE]; + + memset(err_buf, 0, sizeof(err_buf)); + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, + "Receiving data over TLS/SSL failed: %s", err_buf); + } + + return ret; +} + + +/********************************************************************* + * + * Function : ssl_flush_socket + * + * Description : Send any pending "buffered" content with given + * SSL connection. Alternative to function flush_socket. + * + * Parameters : + * 1 : ssl = SSL context to send buffer to + * 2 : iob = The I/O buffer to flush, usually csp->iob. + * + * Returns : On success, the number of bytes send are returned (zero + * indicates nothing was sent). On error, -1 is returned. + * + *********************************************************************/ +extern long ssl_flush_socket(mbedtls_ssl_context *ssl, struct iob *iob) +{ + /* Computing length of buffer part to send */ + long len = iob->eod - iob->cur; + + if (len <= 0) + { + return(0); + } + + /* Sending data to given SSl context */ + if (ssl_send_data(ssl, (const unsigned char *)iob->cur, (size_t)len) < 0) + { + return -1; + } + iob->eod = iob->cur = iob->buf; + return(len); +} + + +/********************************************************************* + * + * Function : ssl_debug_callback + * + * Description : Debug callback function for mbedtls library. + * Prints info into log file. + * + * Parameters : + * 1 : ctx = File to save log in + * 2 : level = Debug level + * 3 : file = File calling debug message + * 4 : line = Line calling debug message + * 5 : str = Debug message + * + * Returns : N/A + * + *********************************************************************/ +static void ssl_debug_callback(void *ctx, int level, const char *file, int line, const char *str) +{ + /* + ((void)level); + fprintf((FILE *)ctx, "%s:%04d: %s", file, line, str); + fflush((FILE *)ctx); + log_error(LOG_LEVEL_INFO, "SSL debug message: %s:%04d: %s", file, line, str); + */ +} + + +/********************************************************************* + * + * Function : create_client_ssl_connection + * + * Description : Creates TLS/SSL secured connection with client + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : 0 on success, negative value if connection wasn't created + * successfully. + * + *********************************************************************/ +extern int create_client_ssl_connection(struct client_state *csp) +{ + /* Paths to certificates file and key file */ + char *key_file = NULL; + char *ca_file = NULL; + char *cert_file = NULL; + int ret = 0; + char err_buf[ERROR_BUF_SIZE]; + + memset(err_buf, 0, sizeof(err_buf)); + + /* + * Initializing mbedtls structures for TLS/SSL connection + */ + mbedtls_net_init(&(csp->mbedtls_client_attr.socket_fd)); + mbedtls_ssl_init(&(csp->mbedtls_client_attr.ssl)); + mbedtls_ssl_config_init(&(csp->mbedtls_client_attr.conf)); + mbedtls_x509_crt_init(&(csp->mbedtls_client_attr.server_cert)); + mbedtls_pk_init(&(csp->mbedtls_client_attr.prim_key)); +#if defined(MBEDTLS_SSL_CACHE_C) + mbedtls_ssl_cache_init(&(csp->mbedtls_client_attr.cache)); +#endif + + /* + * Preparing hash of host for creating certificates + */ + ret = host_to_hash(csp); + if (ret != 0) + { + log_error(LOG_LEVEL_ERROR, "Generating hash of host failed: %d", ret); + ret = -1; + goto exit; + } + + /* + * Preparing paths to certificates files and key file + */ + ca_file = csp->config->ca_cert_file; + cert_file = make_certs_path(csp->config->certificate_directory, + (const char *)csp->http->hash_of_host_hex, CERT_FILE_TYPE); + key_file = make_certs_path(csp->config->certificate_directory, + (const char *)csp->http->hash_of_host_hex, KEY_FILE_TYPE); + + if (cert_file == NULL || key_file == NULL) + { + ret = -1; + goto exit; + } + + /* + * Generating certificate for requested host. Mutex to prevent + * certificate and key inconsistence must be locked. + */ + unsigned int cert_mutex_id = get_certificate_mutex_id(csp); + privoxy_mutex_lock(&(certificates_mutexes[cert_mutex_id])); + + ret = generate_webpage_certificate(csp); + if (ret < 0) + { + log_error(LOG_LEVEL_ERROR, + "Generate_webpage_certificate failed: %d", ret); + privoxy_mutex_unlock(&(certificates_mutexes[cert_mutex_id])); + ret = -1; + goto exit; + } + privoxy_mutex_unlock(&(certificates_mutexes[cert_mutex_id])); + + /* + * Seed the RNG + */ + ret = seed_rng(csp); + if (ret != 0) + { + ret = -1; + goto exit; + } + + /* + * Loading CA file, webpage certificate and key files + */ + ret = mbedtls_x509_crt_parse_file(&(csp->mbedtls_client_attr.server_cert), + cert_file); + if (ret != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, + "Loading webpage certificate %s failed: %s", cert_file, err_buf); + ret = -1; + goto exit; + } + + ret = mbedtls_x509_crt_parse_file(&(csp->mbedtls_client_attr.server_cert), + ca_file); + if (ret != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, + "Loading CA certificate %s failed: %s", ca_file, err_buf); + ret = -1; + goto exit; + } + + ret = mbedtls_pk_parse_keyfile(&(csp->mbedtls_client_attr.prim_key), + key_file, NULL); + if (ret != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, + "Loading and parsing webpage certificate private key %s failed: %s", + key_file, err_buf); + ret = -1; + goto exit; + } + + /* + * Setting SSL parameters + */ + ret = mbedtls_ssl_config_defaults(&(csp->mbedtls_client_attr.conf), + MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + if (ret != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, + "mbedtls_ssl_config_defaults failed: %s", err_buf); + ret = -1; + goto exit; + } + + mbedtls_ssl_conf_rng(&(csp->mbedtls_client_attr.conf), + mbedtls_ctr_drbg_random, &ctr_drbg); + mbedtls_ssl_conf_dbg(&(csp->mbedtls_client_attr.conf), + ssl_debug_callback, stdout); + +#if defined(MBEDTLS_SSL_CACHE_C) + mbedtls_ssl_conf_session_cache(&(csp->mbedtls_client_attr.conf), + &(csp->mbedtls_client_attr.cache), mbedtls_ssl_cache_get, + mbedtls_ssl_cache_set); +#endif + + /* + * Setting certificates + */ + ret = mbedtls_ssl_conf_own_cert(&(csp->mbedtls_client_attr.conf), + &(csp->mbedtls_client_attr.server_cert), + &(csp->mbedtls_client_attr.prim_key)); + if (ret != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, + "mbedtls_ssl_conf_own_cert failed: %s", err_buf); + ret = -1; + goto exit; + } + + ret = mbedtls_ssl_setup(&(csp->mbedtls_client_attr.ssl), + &(csp->mbedtls_client_attr.conf)); + if (ret != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, "mbedtls_ssl_setup failed: %s", err_buf); + ret = -1; + goto exit; + } + + mbedtls_ssl_set_bio(&(csp->mbedtls_client_attr.ssl), + &(csp->mbedtls_client_attr.socket_fd), mbedtls_net_send, + mbedtls_net_recv, NULL); + mbedtls_ssl_session_reset(&(csp->mbedtls_client_attr.ssl)); + + /* + * Setting socket fd in mbedtls_net_context structure. This structure + * can't be set by mbedtls functions, because we already have created + * a TCP connection when this function is called. + */ + csp->mbedtls_client_attr.socket_fd.fd = csp->cfd; + + /* + * Handshake with client + */ + log_error(LOG_LEVEL_CONNECT, + "Performing the TLS/SSL handshake with client. Hash of host: %s", + csp->http->hash_of_host_hex); + while ((ret = mbedtls_ssl_handshake(&(csp->mbedtls_client_attr.ssl))) != 0) + { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && + ret != MBEDTLS_ERR_SSL_WANT_WRITE) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, + "medtls_ssl_handshake with client failed: %s", err_buf); + ret = -1; + goto exit; + } + } + + log_error(LOG_LEVEL_CONNECT, "Client successfully connected over TLS/SSL"); + csp->ssl_with_client_is_opened = 1; + +exit: + /* + * Freeing allocated paths to files + */ + freez(cert_file); + freez(key_file); + + /* Freeing structures if connection wasn't created successfully */ + if (ret < 0) + { + free_client_ssl_structures(csp); + } + return ret; +} + + +/********************************************************************* + * + * Function : close_client_ssl_connection + * + * Description : Closes TLS/SSL connection with client. This function + * checks if this connection is already created. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : N/A + * + *********************************************************************/ +static void close_client_ssl_connection(struct client_state *csp) +{ + int ret = 0; + + if (csp->ssl_with_client_is_opened == 0) + { + return; + } + + /* + * Notifying the peer that the connection is being closed. + */ + do { + ret = mbedtls_ssl_close_notify(&(csp->mbedtls_client_attr.ssl)); + } while (ret == MBEDTLS_ERR_SSL_WANT_WRITE); + + free_client_ssl_structures(csp); + csp->ssl_with_client_is_opened = 0; +} + + +/********************************************************************* + * + * Function : free_client_ssl_structures + * + * Description : Frees structures used for SSL communication with + * client. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : N/A + * + *********************************************************************/ +static void free_client_ssl_structures(struct client_state *csp) +{ + /* + * We can't use function mbedtls_net_free, because this function + * inter alia close TCP connection on setted fd. Instead of this + * function, we change fd to -1, which is the same what does + * rest of mbedtls_net_free function. + */ + csp->mbedtls_client_attr.socket_fd.fd = -1; + + /* Freeing mbedtls structures */ + mbedtls_x509_crt_free(&(csp->mbedtls_client_attr.server_cert)); + mbedtls_pk_free(&(csp->mbedtls_client_attr.prim_key)); + mbedtls_ssl_free(&(csp->mbedtls_client_attr.ssl)); + mbedtls_ssl_config_free(&(csp->mbedtls_client_attr.conf)); +#if defined(MBEDTLS_SSL_CACHE_C) + mbedtls_ssl_cache_free(&(csp->mbedtls_client_attr.cache)); +#endif +} + + +/********************************************************************* + * + * Function : create_server_ssl_connection + * + * Description : Creates TLS/SSL secured connection with server. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : 0 on success, negative value if connection wasn't created + * successfully. + * + *********************************************************************/ +extern int create_server_ssl_connection(struct client_state *csp) +{ + int ret = 0; + char err_buf[ERROR_BUF_SIZE]; + char *trusted_cas_file = NULL; + int auth_mode = MBEDTLS_SSL_VERIFY_REQUIRED; + + memset(err_buf, 0, sizeof(err_buf)); + + csp->server_cert_verification_result = SSL_CERT_NOT_VERIFIED; + csp->server_certs_chain.next = NULL; + + /* Setting path to file with trusted CAs */ + trusted_cas_file = csp->config->trusted_cas_file; + + /* + * Initializing mbedtls structures for TLS/SSL connection + */ + mbedtls_net_init(&(csp->mbedtls_server_attr.socket_fd)); + mbedtls_ssl_init(&(csp->mbedtls_server_attr.ssl)); + mbedtls_ssl_config_init(&(csp->mbedtls_server_attr.conf)); + mbedtls_x509_crt_init( &(csp->mbedtls_server_attr.ca_cert)); + + /* + * Setting socket fd in mbedtls_net_context structure. This structure + * can't be set by mbedtls functions, because we already have created + * TCP connection when calling this function. + */ + csp->mbedtls_server_attr.socket_fd.fd = csp->server_connection.sfd; + + /* + * Seed the RNG + */ + ret = seed_rng(csp); + if (ret != 0) + { + ret = -1; + goto exit; + } + + /* + * Loading file with trusted CAs + */ + ret = mbedtls_x509_crt_parse_file(&(csp->mbedtls_server_attr.ca_cert), + trusted_cas_file); + if (ret < 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, "Loading trusted CAs file %s failed: %s", + trusted_cas_file, err_buf); + ret = -1; + goto exit; + } + + /* + * Set TLS/SSL options + */ + ret = mbedtls_ssl_config_defaults(&(csp->mbedtls_server_attr.conf), + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + if (ret != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, "mbedtls_ssl_config_defaults failed: %s", + err_buf); + ret = -1; + goto exit; + } + + /* + * Setting how strict should certificate verification be and other + * parameters for certificate verification + */ + if (csp->dont_verify_certificate) + { + auth_mode = MBEDTLS_SSL_VERIFY_NONE; + } + + mbedtls_ssl_conf_authmode(&(csp->mbedtls_server_attr.conf), auth_mode); + mbedtls_ssl_conf_ca_chain(&(csp->mbedtls_server_attr.conf), + &(csp->mbedtls_server_attr.ca_cert), NULL); + + /* Setting callback function for certificates verification */ + mbedtls_ssl_conf_verify(&(csp->mbedtls_server_attr.conf), + ssl_verify_callback, (void *)csp); + + mbedtls_ssl_conf_rng(&(csp->mbedtls_server_attr.conf), + mbedtls_ctr_drbg_random, &ctr_drbg); + mbedtls_ssl_conf_dbg(&(csp->mbedtls_server_attr.conf), + ssl_debug_callback, stdout); + + ret = mbedtls_ssl_setup(&(csp->mbedtls_server_attr.ssl), + &(csp->mbedtls_server_attr.conf)); + if (ret != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, "mbedtls_ssl_setup failed: %s", err_buf); + ret = -1; + goto exit; + } + + /* + * Set the hostname to check against the received server certificate + */ + ret = mbedtls_ssl_set_hostname(&(csp->mbedtls_server_attr.ssl), + csp->http->host); + if (ret != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, "mbedtls_ssl_set_hostname failed: %s", + err_buf); + ret = -1; + goto exit; + } + + mbedtls_ssl_set_bio(&(csp->mbedtls_server_attr.ssl), + &(csp->mbedtls_server_attr.socket_fd), mbedtls_net_send, + mbedtls_net_recv, NULL); + + /* + * Handshake with server + */ + log_error(LOG_LEVEL_CONNECT, + "Performing the TLS/SSL handshake with server"); + + while ((ret = mbedtls_ssl_handshake(&(csp->mbedtls_server_attr.ssl))) != 0) + { + if (ret != MBEDTLS_ERR_SSL_WANT_READ + && ret != MBEDTLS_ERR_SSL_WANT_WRITE) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + + if (ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) + { + log_error(LOG_LEVEL_ERROR, + "Server certificate verification failed: %s", err_buf); + csp->server_cert_verification_result = + mbedtls_ssl_get_verify_result(&(csp->mbedtls_server_attr.ssl)); + + ret = -1; + } + else + { + log_error(LOG_LEVEL_ERROR, + "mbedtls_ssl_handshake with server failed: %s", err_buf); + ret = -1; + } + goto exit; + } + } + + log_error(LOG_LEVEL_CONNECT, "Server successfully connected over TLS/SSL"); + + /* + * Server certificate chain is valid, so we can clean + * chain, because we will not send it to client. + */ + free_certificate_chain(csp); + + csp->ssl_with_server_is_opened = 1; + csp->server_cert_verification_result = + mbedtls_ssl_get_verify_result(&(csp->mbedtls_server_attr.ssl)); + +exit: + /* Freeing structures if connection wasn't created successfully */ + if (ret < 0) + { + free_server_ssl_structures(csp); + } + + return ret; +} + + +/********************************************************************* + * + * Function : close_server_ssl_connection + * + * Description : Closes TLS/SSL connection with server. This function + * checks if this connection is already opened. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : N/A + * + *********************************************************************/ +static void close_server_ssl_connection(struct client_state *csp) +{ + int ret = 0; + + if (csp->ssl_with_server_is_opened == 0) + { + return; + } + + /* + * Notifying the peer that the connection is being closed. + */ + do { + ret = mbedtls_ssl_close_notify(&(csp->mbedtls_server_attr.ssl)); + } while (ret == MBEDTLS_ERR_SSL_WANT_WRITE); + + free_server_ssl_structures(csp); + csp->ssl_with_server_is_opened = 0; +} + + +/********************************************************************* + * + * Function : free_server_ssl_structures + * + * Description : Frees structures used for SSL communication with server + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : N/A + * + *********************************************************************/ +static void free_server_ssl_structures(struct client_state *csp) +{ + /* + * We can't use function mbedtls_net_free, because this function + * inter alia close TCP connection on setted fd. Instead of this + * function, we change fd to -1, which is the same what does + * rest of mbedtls_net_free function. + */ + csp->mbedtls_client_attr.socket_fd.fd = -1; + + mbedtls_x509_crt_free(&(csp->mbedtls_server_attr.ca_cert)); + mbedtls_ssl_free(&(csp->mbedtls_server_attr.ssl)); + mbedtls_ssl_config_free(&(csp->mbedtls_server_attr.conf)); +} + + +/********************************************************************* + * Function : close_client_and_server_ssl_connections + * + * Description : Checks if client or server should use secured + * connection over SSL and if so, closes all of them. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : N/A + * + *********************************************************************/ +extern void close_client_and_server_ssl_connections(struct client_state *csp) +{ + if (client_use_ssl(csp) == 1) + { + close_client_ssl_connection(csp); + } + if (server_use_ssl(csp) == 1) + { + close_server_ssl_connection(csp); + } +} + +/*====================== Certificates ======================*/ + +/********************************************************************* + * Function : write_certificate + * + * Description : Writes certificate into file. + * + * Parameters : + * 1 : crt = certificate to write into file + * 2 : output_file = path to save certificate file + * 3 : f_rng = mbedtls_ctr_drbg_random + * 4 : p_rng = mbedtls_ctr_drbg_context + * + * Returns : Length of written certificate on success or negative value + * on error + * + *********************************************************************/ +static int write_certificate(mbedtls_x509write_cert *crt, const char *output_file, + int(*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + FILE *f = NULL; + size_t len = 0; + unsigned char cert_buf[CERTIFICATE_BUF_SIZE + 1]; /* Buffer for certificate in PEM format + terminating NULL */ + int ret = 0; + char err_buf[ERROR_BUF_SIZE]; + + memset(err_buf, 0, sizeof(err_buf)); + memset(cert_buf, 0, sizeof(cert_buf)); + + /* + * Writing certificate into PEM string. If buffer is too small, fuction + * returns specific error and no buffer overflow can happen. + */ + if ((ret = mbedtls_x509write_crt_pem(crt, cert_buf, + sizeof(cert_buf) - 1, f_rng, p_rng)) != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, + "Writing certificate into buffer failed: %s", err_buf); + return -1; + } + + len = strlen((char *)cert_buf); + + /* + * Saving certificate into file + */ + if ((f = fopen(output_file, "w")) == NULL) + { + log_error(LOG_LEVEL_ERROR, "Opening file %s to save certificate failed", + output_file); + return -1; + } + + if (fwrite(cert_buf, 1, len, f) != len) + { + log_error(LOG_LEVEL_ERROR, + "Writing certificate into file %s failed", output_file); + fclose(f); + return -1; + } + + fclose(f); + + return (int)len; +} + + +/********************************************************************* + * Function : write_private_key + * + * Description : Writes private key into file and copies saved + * content into given pointer to string. If function + * returns 0 for success, this copy must be freed by + * caller. + * + * Parameters : + * 1 : key = key to write into file + * 2 : ret_buf = pointer to string with created key file content + * 3 : key_file_path = path where to save key file + * + * Returns : Length of written private key on success or negative value + * on error + * + *********************************************************************/ +static int write_private_key(mbedtls_pk_context *key, unsigned char **ret_buf, + const char *key_file_path) +{ + size_t len = 0; /* Length of created key */ + FILE *f = NULL; /* File to save certificate */ + int ret = 0; + char err_buf[ERROR_BUF_SIZE]; + + memset(err_buf, 0, sizeof(err_buf)); + + /* Initializing buffer for key file content */ + *ret_buf = (unsigned char *)malloc(PRIVATE_KEY_BUF_SIZE + 1); + if (*ret_buf == NULL) + { + log_error(LOG_LEVEL_ERROR, + "Creating buffer for private key failed: malloc fail"); + ret = -1; + goto exit; + } + memset(*ret_buf, 0, PRIVATE_KEY_BUF_SIZE + 1); + + /* + * Writing private key into PEM string + */ + if ((ret = mbedtls_pk_write_key_pem(key, *ret_buf, PRIVATE_KEY_BUF_SIZE)) != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, + "Writing private key into PEM string failed: %s", err_buf); + ret = -1; + goto exit; + } + len = strlen((char *)*ret_buf); + + /* + * Saving key into file + */ + if ((f = fopen(key_file_path, "wb")) == NULL) + { + log_error(LOG_LEVEL_ERROR, + "Opening file %s to save private key failed: %E", + key_file_path); + ret = -1; + goto exit; + } + + if (fwrite(*ret_buf, 1, len, f) != len) + { + fclose(f); + log_error(LOG_LEVEL_ERROR, + "Writing private key into file %s failed", + key_file_path); + ret = -1; + goto exit; + } + + fclose(f); + +exit: + if (ret < 0) + { + freez(*ret_buf); + *ret_buf = NULL; + return ret; + } + return (int)len; +} + + +/********************************************************************* + * Function : generate_key + * + * Description : Tests if private key for host saved in csp already + * exists. If this file doesn't exists, a new key is + * generated and saved in a file. The generated key is also + * copied into given parameter key_buf, which must be then + * freed by caller. If file with key exists, key_buf + * contain NULL and no private key is generated. + * + * Parameters : + * 1 : key_buf = buffer to save new generated key + * 2 : csp = Current client state (buffers, headers, etc...) + * + * Returns : -1 => Error while generating private key + * 0 => Key already exists + * >0 => Length of generated private key + * + *********************************************************************/ +static int generate_key(unsigned char **key_buf, struct client_state *csp) +{ + mbedtls_pk_context key; + key_options key_opt; + int ret = 0; + char err_buf[ERROR_BUF_SIZE]; + + key_opt.key_file_path = NULL; + memset(err_buf, 0, sizeof(err_buf)); + + /* + * Initializing structures for key generating + */ + mbedtls_pk_init(&key); + + /* + * Preparing path for key file and other properties for generating key + */ + key_opt.type = MBEDTLS_PK_RSA; + key_opt.rsa_keysize = RSA_KEYSIZE; + + key_opt.key_file_path = make_certs_path(csp->config->certificate_directory, + (char *)csp->http->hash_of_host_hex, KEY_FILE_TYPE); + if (key_opt.key_file_path == NULL) + { + ret = -1; + goto exit; + } + + /* + * Test if key already exists. If so, we don't have to create it again. + */ + if (file_exists(key_opt.key_file_path) == 1) + { + ret = 0; + goto exit; + } + + /* + * Seed the RNG + */ + ret = seed_rng(csp); + if (ret != 0) + { + ret = -1; + goto exit; + } + + /* + * Setting attributes of private key and generating it + */ + if ((ret = mbedtls_pk_setup(&key, + mbedtls_pk_info_from_type(key_opt.type))) != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, "mbedtls_pk_setup failed: %s", err_buf); + ret = -1; + goto exit; + } + + ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(key), mbedtls_ctr_drbg_random, + &ctr_drbg, (unsigned)key_opt.rsa_keysize, RSA_KEY_PUBLIC_EXPONENT); + if (ret != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, "Key generating failed: %s", err_buf); + ret = -1; + goto exit; + } + + /* + * Exporting private key into file + */ + if ((ret = write_private_key(&key, key_buf, key_opt.key_file_path)) < 0) + { + log_error(LOG_LEVEL_ERROR, + "Writing private key into file %s failed", key_opt.key_file_path); + ret = -1; + goto exit; + } + +exit: + /* + * Freeing used variables + */ + freez(key_opt.key_file_path); + + mbedtls_pk_free(&key); + + return ret; +} + + +/********************************************************************* + * + * Function : generate_webpage_certificate + * + * Description : Creates certificate file in presetted directory. + * If certificate already exists, no other certificate + * will be created. Subject of certificate is named + * by csp->http->host from parameter. This function also + * triggers generating of private key for new certificate. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : -1 => Error while creating certificate. + * 0 => Certificate alreaday exist. + * >0 => Length of created certificate. + * + *********************************************************************/ +extern int generate_webpage_certificate(struct client_state * csp) +{ + mbedtls_x509_crt issuer_cert; + mbedtls_pk_context loaded_issuer_key, loaded_subject_key; + mbedtls_pk_context *issuer_key = &loaded_issuer_key; + mbedtls_pk_context *subject_key = &loaded_subject_key; + mbedtls_x509write_cert cert; + mbedtls_mpi serial; + + unsigned char *key_buf = NULL; /* Buffer for created key */ + + int ret = 0; + char err_buf[ERROR_BUF_SIZE]; + cert_options cert_opt; + + memset(err_buf, 0, sizeof(err_buf)); + + /* Paths to keys and certificates needed to create certificate */ + cert_opt.issuer_key = NULL; + cert_opt.subject_key = NULL; + cert_opt.issuer_crt = NULL; + cert_opt.output_file = NULL; + + /* + * Create key for requested host + */ + int subject_key_len = generate_key(&key_buf, csp); + if (subject_key_len < 0) + { + log_error(LOG_LEVEL_ERROR, "Key generating failed"); + return -1; + } + + /* + * Initializing structures for certificate generating + */ + mbedtls_x509write_crt_init(&cert); + mbedtls_x509write_crt_set_md_alg( &cert, CERT_SIGNATURE_ALGORITHM); + mbedtls_pk_init(&loaded_issuer_key); + mbedtls_pk_init(&loaded_subject_key); + mbedtls_mpi_init(&serial); + mbedtls_x509_crt_init(&issuer_cert); + + /* + * Presetting parameters for certificate. We must compute total length + * of parameters. + */ + size_t cert_params_len = strlen(CERT_PARAM_COMMON_NAME) + + strlen(CERT_PARAM_ORGANIZATION) + strlen(CERT_PARAM_COUNTRY) + + strlen(CERT_PARAM_ORG_UNIT) + + 3 * strlen(csp->http->host) + 1; + char cert_params[cert_params_len]; + memset(cert_params, 0, cert_params_len); + + /* + * Converting unsigned long serial number to char * serial number. + * We must compute length of serial number in string + terminating null. + */ + unsigned long certificate_serial = get_certificate_serial(csp); + int serial_num_size = snprintf(NULL, 0, "%lu", certificate_serial) + 1; + if (serial_num_size <= 0) + { + serial_num_size = 1; + } + + char serial_num_text[serial_num_size]; /* Buffer for serial number */ + ret = snprintf(serial_num_text, (size_t)serial_num_size, "%lu", certificate_serial); + if (ret < 0 || ret >= serial_num_size) + { + log_error(LOG_LEVEL_ERROR, + "Converting certificate serial number into string failed"); + ret = -1; + goto exit; + } + + /* + * Preparing parameters for certificate + */ + strlcpy(cert_params, CERT_PARAM_COMMON_NAME, cert_params_len); + strlcat(cert_params, csp->http->host, cert_params_len); + strlcat(cert_params, CERT_PARAM_ORGANIZATION, cert_params_len); + strlcat(cert_params, csp->http->host, cert_params_len); + strlcat(cert_params, CERT_PARAM_ORG_UNIT, cert_params_len); + strlcat(cert_params, csp->http->host, cert_params_len); + strlcat(cert_params, CERT_PARAM_COUNTRY, cert_params_len); + + cert_opt.issuer_crt = csp->config->ca_cert_file; + cert_opt.issuer_key = csp->config->ca_key_file; + cert_opt.subject_key = make_certs_path(csp->config->certificate_directory, + (const char *)csp->http->hash_of_host_hex, KEY_FILE_TYPE); + cert_opt.output_file = make_certs_path(csp->config->certificate_directory, + (const char *)csp->http->hash_of_host_hex, CERT_FILE_TYPE); + + if (cert_opt.subject_key == NULL || cert_opt.output_file == NULL) + { + ret = -1; + goto exit; + } + + cert_opt.subject_pwd = CERT_SUBJECT_PASSWORD; + cert_opt.issuer_pwd = csp->config->ca_password; + cert_opt.subject_name = cert_params; + cert_opt.not_before = GENERATED_CERT_VALID_FROM; + cert_opt.not_after = GENERATED_CERT_VALID_TO; + cert_opt.serial = serial_num_text; + cert_opt.is_ca = 0; + cert_opt.max_pathlen = -1; + + /* + * Test if certificate exists and private key was already created + */ + if (file_exists(cert_opt.output_file) == 1 && subject_key_len == 0) + { + ret = 0; + goto exit; + } + + /* + * Seed the PRNG + */ + ret = seed_rng(csp); + if (ret != 0) + { + ret = -1; + goto exit; + } + + /* + * Parse serial to MPI + */ + ret = mbedtls_mpi_read_string(&serial, 10, cert_opt.serial); + if (ret != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, + "mbedtls_mpi_read_string failed: %s", err_buf); + ret = -1; + goto exit; + } + + /* + * Loading certificates + */ + ret = mbedtls_x509_crt_parse_file(&issuer_cert, cert_opt.issuer_crt); + if (ret != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, "Loading issuer certificate %s failed: %s", + cert_opt.issuer_crt, err_buf); + ret = -1; + goto exit; + } + + ret = mbedtls_x509_dn_gets(cert_opt.issuer_name, + sizeof(cert_opt.issuer_name), &issuer_cert.subject); + if (ret < 0) + { + mbedtls_strerror( ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, "mbedtls_x509_dn_gets failed: %s", err_buf); + ret = -1; + goto exit; + } + + /* + * Loading keys from file or from buffer + */ + if (key_buf != NULL && subject_key_len > 0) + { + /* Key was created in this function and is stored in buffer */ + ret = mbedtls_pk_parse_key(&loaded_subject_key, key_buf, + (size_t)(subject_key_len + 1), (unsigned const char *) + cert_opt.subject_pwd, strlen(cert_opt.subject_pwd)); + } + else + { + /* Key wasn't created in this function, because it already existed */ + ret = mbedtls_pk_parse_keyfile(&loaded_subject_key, + cert_opt.subject_key, cert_opt.subject_pwd); + } + + if (ret != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, "Parsing subject key %s failed: %s", + cert_opt.subject_key, err_buf); + ret = -1; + goto exit; + } + + ret = mbedtls_pk_parse_keyfile(&loaded_issuer_key, cert_opt.issuer_key, + cert_opt.issuer_pwd); + if (ret != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, + "Parsing issuer key %s failed: %s", cert_opt.issuer_key, err_buf); + ret = -1; + goto exit; + } + + /* + * Check if key and issuer certificate match + */ + if (!mbedtls_pk_can_do(&issuer_cert.pk, MBEDTLS_PK_RSA) || + mbedtls_mpi_cmp_mpi(&mbedtls_pk_rsa(issuer_cert.pk)->N, + &mbedtls_pk_rsa(*issuer_key)->N) != 0 || + mbedtls_mpi_cmp_mpi( &mbedtls_pk_rsa(issuer_cert.pk)->E, + &mbedtls_pk_rsa(*issuer_key )->E) != 0) + { + log_error(LOG_LEVEL_ERROR, + "Issuer key doesn't match issuer certificate"); + ret = -1; + goto exit; + } + + mbedtls_x509write_crt_set_subject_key(&cert, subject_key); + mbedtls_x509write_crt_set_issuer_key(&cert, issuer_key); + + /* + * Setting parameters of signed certificate + */ + ret = mbedtls_x509write_crt_set_subject_name(&cert, cert_opt.subject_name); + if (ret != 0) + { + mbedtls_strerror( ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, + "Setting subject name in signed certificate failed: %s", err_buf); + ret = -1; + goto exit; + } + + ret = mbedtls_x509write_crt_set_issuer_name(&cert, cert_opt.issuer_name); + if (ret != 0) + { + mbedtls_strerror( ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, + "Setting issuer name in signed certificate failed: %s", err_buf); + ret = -1; + goto exit; + } + + ret = mbedtls_x509write_crt_set_serial(&cert, &serial); + if (ret != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, + "Setting serial number in signed certificate failed: %s", err_buf); + ret = -1; + goto exit; + } + + ret = mbedtls_x509write_crt_set_validity(&cert, cert_opt.not_before, + cert_opt.not_after); + if (ret != 0) + { + mbedtls_strerror( ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, + "Setting validity in signed certificate failed: %s", err_buf); + ret = -1; + goto exit; + } + + /* + * Setting the basicConstraints extension for certificate + */ + ret = mbedtls_x509write_crt_set_basic_constraints(&cert, cert_opt.is_ca, + cert_opt.max_pathlen); + if (ret != 0) + { + mbedtls_strerror( ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, "Setting the basicConstraints extension " + "in signed certificate failed: %s", err_buf); + ret = -1; + goto exit; + } + +#if defined(MBEDTLS_SHA1_C) + /* Setting the subjectKeyIdentifier extension for certificate */ + ret = mbedtls_x509write_crt_set_subject_key_identifier(&cert); + if (ret != 0) + { + mbedtls_strerror( ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, "mbedtls_x509write_crt_set_subject_key_" + "identifier failed: %s", err_buf); + ret = -1; + goto exit; + } + + /* Setting the authorityKeyIdentifier extension for certificate */ + ret = mbedtls_x509write_crt_set_authority_key_identifier(&cert); + if (ret != 0) + { + mbedtls_strerror( ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, "mbedtls_x509write_crt_set_authority_key_" + "identifier failed: %s", err_buf); + ret = -1; + goto exit; + } +#endif /* MBEDTLS_SHA1_C */ + + /* + * Writing certificate into file + */ + ret = write_certificate(&cert, cert_opt.output_file, + mbedtls_ctr_drbg_random, &ctr_drbg); + if (ret < 0) + { + log_error(LOG_LEVEL_ERROR, "Writing certificate into file failed"); + goto exit; + } + +exit: + /* + * Freeing used structures + */ + mbedtls_x509write_crt_free(&cert); + mbedtls_pk_free(&loaded_subject_key); + mbedtls_pk_free(&loaded_issuer_key); + mbedtls_mpi_free(&serial); + mbedtls_x509_crt_free(&issuer_cert); + + freez(cert_opt.subject_key); + freez(cert_opt.output_file); + freez(key_buf); + + return ret; +} + + +/********************************************************************* + * + * Function : make_certs_path + * + * Description : Creates path to file from three pieces. This fuction + * takes parameters and puts them in one new mallocated + * char * in correct order. Returned variable must be freed + * by caller. This function is mainly used for creating + * paths of certificates and keys files. + * + * Parameters : + * 1 : conf_dir = Name/path of directory where is the file. + * '.' can be used for current directory. + * 2 : file_name = Name of file in conf_dir without suffix. + * 3 : suffix = Suffix of given file_name. + * + * Returns : path => Path was built up successfully + * NULL => Path can't be built up + * + *********************************************************************/ +static char *make_certs_path(const char *conf_dir, const char *file_name, + const char *suffix) +{ + /* Test if all given parameters are valid */ + if (conf_dir == NULL || *conf_dir == '\0' || file_name == NULL || + *file_name == '\0' || suffix == NULL || *suffix == '\0') + { + log_error(LOG_LEVEL_ERROR, + "make_certs_path failed: bad input parameters"); + return NULL; + } + + char *path = NULL; + size_t path_size = strlen(conf_dir) + + strlen(file_name) + strlen(suffix) + 2; + + /* Setting delimiter and editing path length */ +#if defined(_WIN32) || defined(__OS2__) + char delim[] = "\\"; + path_size += 1; +#else /* ifndef _WIN32 || __OS2__ */ + char delim[] = "/"; +#endif /* ifndef _WIN32 || __OS2__ */ + + /* + * Building up path from many parts + */ +#if defined(unix) + if (*conf_dir != '/' && basedir && *basedir) + { + /* + * Replacing conf_dir with basedir. This new variable contains + * absolute path to cwd. + */ + path_size += strlen(basedir) + 2; + path = (char *)malloc(path_size); + if (path == NULL) + { + log_error(LOG_LEVEL_ERROR, "make_certs_path failed: malloc fail"); + return NULL; + } + memset(path, 0, path_size); + + strlcpy(path, basedir, path_size); + strlcat(path, delim, path_size); + strlcat(path, conf_dir, path_size); + strlcat(path, delim, path_size); + strlcat(path, file_name, path_size); + strlcat(path, suffix, path_size); + } + else +#endif /* defined unix */ + { + path = (char *)malloc(path_size); + if (path == NULL) + { + log_error(LOG_LEVEL_ERROR, "make_certs_path failed: malloc fail"); + return NULL; + } + memset(path, 0, path_size); + + strlcpy(path, conf_dir, path_size); + strlcat(path, delim, path_size); + strlcat(path, file_name, path_size); + strlcat(path, suffix, path_size); + } + + return path; +} + + +/********************************************************************* + * + * Function : get_certificate_mutex_id + * + * Description : Computes mutex id from host name hash. This hash must + * be already saved in csp structure + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : Mutex id for given host name + * + *********************************************************************/ +static unsigned int get_certificate_mutex_id(struct client_state *csp) { +#ifdef LIMIT_MUTEX_NUMBER + return (unsigned int)(csp->http->hash_of_host[0] % 32); +#else + return (unsigned int)(csp->http->hash_of_host[1] + + 256 * (int)csp->http->hash_of_host[0]); +#endif /* LIMIT_MUTEX_NUMBER */ +} + + +/********************************************************************* + * + * Function : get_certificate_serial + * + * Description : Computes serial number for new certificate from host + * name hash. This hash must be already saved in csp + * structure. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : Serial number for new certificate + * + *********************************************************************/ +static unsigned long get_certificate_serial(struct client_state *csp) { + unsigned long exp = 1; + unsigned long serial = 0; + + int i = CERT_SERIAL_NUM_LENGTH; + /* Length of hash is 16 bytes, we must avoid to read next chars */ + if (i > 16) + { + i = 16; + } + if (i < 2) + { + i = 2; + } + + for (; i >= 0; i--) + { + serial += exp * (unsigned)csp->http->hash_of_host[i]; + exp *= 256; + } + return serial; +} + + +/********************************************************************* + * + * Function : ssl_send_certificate_error + * + * Description : Sends info about invalid server certificate to client. + * Sent message is including all trusted chain certificates, + * that can be downloaded in web browser. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : N/A + * + *********************************************************************/ +extern void ssl_send_certificate_error(struct client_state *csp) +{ + size_t message_len = 0; + int ret = 0; + struct certs_chain *cert = NULL; + + /* Header of message with certificate informations */ + const char message_begin[] = + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html\r\n" + "Connection: close\r\n\r\n" + "

Invalid server certificate

Reason: "; + const char message_end[] = "\r\n\r\n"; + char reason[INVALID_CERT_INFO_BUF_SIZE]; + memset(reason, 0, sizeof(reason)); + + /* Get verification message from verification return code */ + mbedtls_x509_crt_verify_info(reason, sizeof(reason), " ", + csp->server_cert_verification_result); + + /* + * Computing total length of message with all certificates inside + */ + message_len = strlen(message_begin) + strlen(message_end) + + strlen(reason) + strlen("

") + 1; + + cert = &(csp->server_certs_chain); + while (cert->next != NULL) + { + size_t base64_len = 4 * ((strlen(cert->file_buf) + 2) / 3) + 1; + + message_len += strlen(cert->text_buf) + strlen("
\n")
+                     +  base64_len + strlen("Download certificate");
+      cert = cert->next;
+   }
+
+   /*
+    * Joining all blocks in one long message
+    */
+   char message[message_len];
+   memset(message, 0, message_len);
+
+   strlcpy(message, message_begin, message_len);
+   strlcat(message, reason       , message_len);
+   strlcat(message, "

" , message_len); + + cert = &(csp->server_certs_chain); + while (cert->next != NULL) + { + size_t olen = 0; + size_t base64_len = 4 * ((strlen(cert->file_buf) + 2) / 3) + 1; /* +1 for terminating null*/ + char base64_buf[base64_len]; + memset(base64_buf, 0, base64_len); + + /* Encoding certificate into base64 code */ + ret = mbedtls_base64_encode((unsigned char*)base64_buf, + base64_len, &olen, (const unsigned char*)cert->file_buf, + strlen(cert->file_buf)); + if (ret != 0) + { + log_error(LOG_LEVEL_ERROR, + "Encoding to base64 failed, buffer is to small"); + } + + strlcat(message, "
",        message_len);
+      strlcat(message, cert->text_buf, message_len);
+      strlcat(message, "
\n", message_len); + + if (ret == 0) + { + strlcat(message, "Download certificate", message_len); + } + + cert = cert->next; + } + strlcat(message, message_end, message_len); + + /* + * Sending final message to client + */ + ssl_send_data(&(csp->mbedtls_client_attr.ssl), + (const unsigned char *)message, strlen(message)); + /* + * Waiting before closing connection. Some browsers doesn't show received + * message if there isn't this delay. + */ + sleep(1); + + free_certificate_chain(csp); +} + + +/********************************************************************* + * + * Function : ssl_verify_callback + * + * Description : This is a callback function for certificate verification. + * It's called for all certificates in server certificate + * trusted chain and it's preparing information about this + * certificates. Prepared informations can be used to inform + * user about invalid certificates. + * + * Parameters : + * 1 : csp_void = Current client state (buffers, headers, etc...) + * 2 : crt = certificate from trusted chain + * 3 : depth = depth in trusted chain + * 4 : flags = certificate flags + * + * Returns : 0 on success and negative value on error + * + *********************************************************************/ +static int ssl_verify_callback(void *csp_void, mbedtls_x509_crt *crt, + int depth, uint32_t *flags) +{ + struct client_state *csp = (struct client_state *)csp_void; + struct certs_chain *last = &(csp->server_certs_chain); + size_t olen = 0; + int ret = 0; + + /* + * Searching for last item in certificates linked list + */ + while (last->next != NULL) + { + last = last->next; + } + + /* + * Preparing next item in linked list for next certificate + * If malloc fails, we are continuing without this certificate + */ + last->next = (struct certs_chain *)malloc(sizeof(struct certs_chain)); + if (last->next != NULL) + { + last->next->next = NULL; + memset(last->next->text_buf, 0, sizeof(last->next->text_buf)); + memset(last->next->file_buf, 0, sizeof(last->next->file_buf)); + + /* + * Saving certificate file into buffer + */ + if ((ret = mbedtls_pem_write_buffer(PEM_BEGIN_CRT, PEM_END_CRT, + crt->raw.p, crt->raw.len, (unsigned char *)last->file_buf, + sizeof(last->file_buf)-1, &olen)) != 0) + { + return(ret); + } + + /* + * Saving certificate information into buffer + */ + mbedtls_x509_crt_info(last->text_buf, sizeof(last->text_buf) - 1, + CERT_INFO_PREFIX, crt); + } + else + { + log_error(LOG_LEVEL_ERROR, + "Malloc memory for server certificate informations failed"); + return -1; + } + + return 0; +} + + +/********************************************************************* + * + * Function : free_certificate_chain + * + * Description : Frees certificates linked list. This linked list is + * used to save informations about certificates in + * trusted chain. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : N/A + * + *********************************************************************/ +static void free_certificate_chain(struct client_state *csp) +{ + struct certs_chain *cert = csp->server_certs_chain.next; + + /* Cleaning buffers */ + memset(csp->server_certs_chain.text_buf, 0, + sizeof(csp->server_certs_chain.text_buf)); + memset(csp->server_certs_chain.text_buf, 0, + sizeof(csp->server_certs_chain.file_buf)); + csp->server_certs_chain.next = NULL; + + /* Freeing memory in whole linked list */ + if (cert != NULL) + { + do + { + struct certs_chain *cert_for_free = cert; + cert = cert->next; + freez(cert_for_free); + } while (cert != NULL); + } +} + + +/********************************************************************* +* +* Function : file_exists +* +* Description : Tests if file exists and is readable. +* +* Parameters : +* 1 : path = Path to tested file. +* +* Returns : 1 => File exists and is readable. +* 0 => File doesn't exist or is not readable. +* +*********************************************************************/ +static int file_exists(const char *path) +{ + FILE *f; + if ((f = fopen(path, "r")) != NULL) + { + fclose(f); + return 1; + } + + return 0; +} + + +/********************************************************************* +* +* Function : host_to_hash +* +* Description : Creates MD5 hash from host name. Host name is loaded +* from structure csp and saved again into it. +* +* Parameters : +* 1 : csp = Current client state (buffers, headers, etc...) +* +* Returns : 1 => Error while creating hash +* 0 => Hash created successfully +* +*********************************************************************/ +static int host_to_hash(struct client_state * csp) +{ + int ret = 0; + +#if !defined(MBEDTLS_MD5_C) + log_error(LOG_LEVEL_ERROR, "MBEDTLS_MD5_C is not defined. Can't create" + "MD5 hash for certificate and key name."); + return -1; +#else + memset(csp->http->hash_of_host, 0, sizeof(csp->http->hash_of_host)); + mbedtls_md5((unsigned char *)csp->http->host, strlen(csp->http->host), + csp->http->hash_of_host); + + /* Converting hash into string with hex */ + size_t i = 0; + for (; i < 16; i++) + { + if ((ret = sprintf((char *)csp->http->hash_of_host_hex + 2 * i, "%02x", + csp->http->hash_of_host[i])) < 0) + { + log_error(LOG_LEVEL_ERROR, "Sprintf return value: %d", ret); + return -1; + } + } + + return 0; +#endif /* MBEDTLS_MD5_C */ +} + + +/********************************************************************* + * + * Function : tunnel_established_successfully + * + * Description : Check if parent proxy server response contains + * informations about successfully created connection with + * destination server. (HTTP/... 2xx ...) + * + * Parameters : + * 1 : server_response = Buffer with parent proxy server response + * 2 : response_len = Length of server_response + * + * Returns : 1 => Connection created successfully + * 0 => Connection wasn't created successfully + * + *********************************************************************/ +extern int tunnel_established_successfully(const char *server_response, + unsigned int response_len) +{ + unsigned int pos = 0; + + if (server_response == NULL) + { + return 0; + } + + /* Tests if "HTTP/" string is at the begin of received response */ + if (strncmp(server_response, "HTTP/", 5) != 0) + { + return 0; + } + + for (pos = 0; pos < response_len; pos++) + { + if (server_response[pos] == ' ') + { + break; + } + } + + /* + * response_len -3 because of buffer end, response structure and 200 code. + * There must be at least 3 chars after space. + * End of buffer: ... 2xx'\0' + * pos = | + */ + if (pos >= (response_len - 3)) + { + return 0; + } + + /* Test HTTP status code */ + if (server_response[pos + 1] != '2') + { + return 0; + } + + return 1; +} + + +/********************************************************************* + * + * Function : seed_rng + * + * Description : Seeding the RNG for all SSL uses + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : -1 => RNG wasn't seed successfully + * 0 => RNG is seeded successfully + * + *********************************************************************/ +static int seed_rng(struct client_state *csp) +{ + int ret = 0; + char err_buf[ERROR_BUF_SIZE]; + + memset(err_buf, 0, sizeof(err_buf)); + + if (rng_seeded == 0) + { + privoxy_mutex_lock(&rng_mutex); + if (rng_seeded == 0) + { + mbedtls_ctr_drbg_init(&ctr_drbg); + mbedtls_entropy_init(&entropy); + ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, + &entropy, NULL, 0); + if (ret != 0) + { + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + log_error(LOG_LEVEL_ERROR, + "mbedtls_ctr_drbg_seed failed: %s", err_buf); + privoxy_mutex_unlock(&rng_mutex); + return -1; + } + rng_seeded = 1; + } + privoxy_mutex_unlock(&rng_mutex); + } + return 0; +} diff --git a/ssl.h b/ssl.h new file mode 100644 index 00000000..b552bfe2 --- /dev/null +++ b/ssl.h @@ -0,0 +1,66 @@ +#ifndef SSL_H_INCLUDED +#define SSL_H_INCLUDED +/********************************************************************* +* +* File : $Source: /cvsroot/ijbswa/current/ssl.h,v $ +* +* Purpose : File with TLS/SSL extension. Contains methods for +* creating, using and closing TLS/SSL connections. +* +* Copyright : Written by and Copyright (c) 2017 Vaclav Svec. FIT CVUT. +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will +* be useful, but WITHOUT ANY WARRANTY; without even the +* implied warranty of MERCHANTABILITY or FITNESS FOR A +* PARTICULAR PURPOSE. See the GNU General Public +* License for more details. +* +* The GNU General Public License should be included with +* this file. If not, you can view it at +* http://www.gnu.org/copyleft/gpl.html +* or write to the Free Software Foundation, Inc., 59 +* Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*********************************************************************/ + +#include "project.h" + +/* + * Values for flag determining certificate validity. + * These values are compatible with return value of function + * mbedtls_ssl_get_verify_result. There is not value for invalid + * certificate, this value is setted by function + * mbedtls_ssl_get_verify_result. + */ +#define SSL_CERT_VALID 0 +#define SSL_CERT_NOT_VERIFIED 0xFFFFFFFF + +/* Variables for one common RNG for all SSL use */ +static mbedtls_ctr_drbg_context ctr_drbg; +static mbedtls_entropy_context entropy; +static int rng_seeded; + +/* Boolean functions to get informations about TLS/SSL connections */ +extern int client_use_ssl(const struct client_state *csp); +extern int server_use_ssl(const struct client_state *csp); +extern size_t is_ssl_pending(mbedtls_ssl_context *ssl); +extern int tunnel_established_successfully(const char * response, unsigned int response_len); + +/* Functions for sending and receiving data over TLS/SSL connections */ +extern int ssl_send_data(mbedtls_ssl_context * ssl, const unsigned char * buf, size_t len); +extern int ssl_recv_data(mbedtls_ssl_context * ssl, unsigned char * buf, size_t maxLen); +extern long ssl_flush_socket(mbedtls_ssl_context * ssl, struct iob *iob); +extern void ssl_send_certificate_error(struct client_state *csp); + +/* Functions for opening and closing TLS/SSL connections */ +extern int create_client_ssl_connection(struct client_state *csp); +extern int create_server_ssl_connection(struct client_state *csp); +extern void close_client_and_server_ssl_connections(struct client_state *csp); + +#endif /* ndef SSL_H_INCLUDED */ diff --git a/user.action b/user.action index 7e8eb8ea..2277c127 100644 --- a/user.action +++ b/user.action @@ -193,3 +193,12 @@ www.example.com/nasty-ads/sponsor.gif # #{+block{Facebook "like" and similar tracking URLs.}} #www.facebook.com/(extern|plugins)/(login_status|like(box)?|activity|fan)\.php + +######### Examples for SSL actions ######### +# Following section enables TLS/SSL filtering for all sites defined by pattern and requested by HTTPS. +# {+enable-https-filtering} +# privoxy.org + +# Following section turns off certificate verification for all sites defined by pattern. +# {+ignore-certificate-errors} +# privoxy.org