123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700 |
- /*********************************************************************
- *
- * File : $Source: /cvsroot/ijbswa/current/ssl_common.c,v $
- *
- * Purpose : File with TLS/SSL extension. Contains methods for
- * creating, using and closing TLS/SSL connections that do
- * not depend on particular TLS/SSL library.
- *
- * Copyright : Written by and Copyright (c) 2017 Vaclav Svec. FIT CVUT.
- * Copyright (C) 2018-2020 by Fabian Keil <fk@fabiankeil.de>
- *
- * 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 <string.h>
- #include <ctype.h>
- #include "config.h"
- #include "project.h"
- #include "miscutil.h"
- #include "errlog.h"
- #include "ssl.h"
- #include "ssl_common.h"
- /*
- * Macros for ssl_common.c
- */
- #define CERT_SERIAL_NUM_LENGTH 4 /* Bytes of hash to be used for creating serial number of certificate. Min=2 and max=16 */
- /*********************************************************************
- *
- * 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 : ssl_send_data_delayed
- *
- * Description : Sends the contents of buf (for n bytes) to given SSL
- * connection, optionally delaying the operation.
- *
- * Parameters :
- * 1 : ssl_attr = 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
- * 4 : delay = Delay in milliseconds.
- *
- * Returns : 0 on success (entire buffer sent).
- * nonzero on error.
- *
- *********************************************************************/
- extern int ssl_send_data_delayed(struct ssl_attr* ssl_attr,
- const unsigned char *buf, size_t len,
- unsigned int delay)
- {
- size_t i = 0;
- if (delay == 0)
- {
- if (ssl_send_data(ssl_attr, buf, len) < 0)
- {
- return -1;
- }
- else
- {
- return 0;
- }
- }
- while (i < len)
- {
- size_t write_length;
- enum { MAX_WRITE_LENGTH = 10 };
- if ((i + MAX_WRITE_LENGTH) > len)
- {
- write_length = len - i;
- }
- else
- {
- write_length = MAX_WRITE_LENGTH;
- }
- privoxy_millisleep(delay);
- if (ssl_send_data(ssl_attr, buf + i, write_length) < 0)
- {
- return -1;
- }
- i += write_length;
- }
- return 0;
- }
- /*********************************************************************
- *
- * Function : ssl_flush_socket
- *
- * Description : Send any pending "buffered" content with given
- * SSL connection. Alternative to function flush_socket.
- *
- * Parameters :
- * 1 : ssl_attr = 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(struct ssl_attr *ssl_attr, 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_attr, (const unsigned char *)iob->cur, (size_t)len) < 0)
- {
- return -1;
- }
- iob->eod = iob->cur = iob->buf;
- return(len);
- }
- /*********************************************************************
- *
- * 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);
- }
- }
- /*********************************************************************
- *
- * Function : tunnel_established_successfully
- *
- * Description : Check if parent proxy server response contains
- * information 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 : free_certificate_chain
- *
- * Description : Frees certificates linked list. This linked list is
- * used to save information about certificates in
- * trusted chain.
- *
- * Parameters :
- * 1 : csp = Current client state (buffers, headers, etc...)
- *
- * Returns : N/A
- *
- *********************************************************************/
- extern void free_certificate_chain(struct client_state *csp)
- {
- struct certs_chain *cert = csp->server_certs_chain.next;
- /* Cleaning buffers */
- memset(csp->server_certs_chain.info_buf, 0,
- sizeof(csp->server_certs_chain.info_buf));
- memset(csp->server_certs_chain.file_buf, 0,
- sizeof(csp->server_certs_chain.file_buf));
- csp->server_certs_chain.next = NULL;
- /* Freeing memory in whole linked list */
- while (cert != NULL)
- {
- struct certs_chain *cert_for_free = cert;
- cert = cert->next;
- freez(cert_for_free);
- }
- }
- /*********************************************************************
- *
- * 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)
- {
- struct ssl_attr *ssl_attr = &csp->ssl_client_attr;
- size_t message_len = 0;
- int ret = 0;
- struct certs_chain *cert = NULL;
- const size_t head_length = 63;
- /* Header of message with certificate information */
- const char message_begin[] =
- "HTTP/1.1 200 OK\r\n"
- "Content-Type: text/html\r\n"
- "Connection: close\r\n\r\n"
- "<!DOCTYPE html>\n"
- "<html><head><title>Server certificate verification failed</title></head>\n"
- "<body><h1>Server certificate verification failed</h1>\n"
- "<p><a href=\"https://" CGI_SITE_2_HOST "/\">Privoxy</a> was unable "
- "to securely connect to the destination server.</p>"
- "<p>Reason: ";
- const char message_end[] = "</body></html>\r\n\r\n";
- char reason[INVALID_CERT_INFO_BUF_SIZE];
- memset(reason, 0, sizeof(reason));
- /* Get verification message from verification return code */
- ssl_crt_verify_info(reason, sizeof(reason), csp);
- /*
- * Computing total length of message with all certificates inside
- */
- message_len = strlen(message_begin) + strlen(message_end)
- + strlen(reason) + strlen("</p>") + 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->info_buf) + strlen("<pre></pre>\n")
- + base64_len + strlen("<a href=\"data:application"
- "/x-x509-ca-cert;base64,\">Download certificate</a>");
- 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, "</p>" , 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 = ssl_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, "<pre>", message_len);
- strlcat(message, cert->info_buf, message_len);
- strlcat(message, "</pre>\n", message_len);
- if (ret == 0)
- {
- strlcat(message, "<a href=\"data:application/x-x509-ca-cert;base64,",
- message_len);
- strlcat(message, base64_buf, message_len);
- strlcat(message, "\">Download certificate</a>", message_len);
- }
- cert = cert->next;
- }
- strlcat(message, message_end, message_len);
- /*
- * Sending final message to client
- */
- (void)ssl_send_data(ssl_attr, (const unsigned char *)message, strlen(message));
- free_certificate_chain(csp);
- log_error(LOG_LEVEL_CRUNCH, "Certificate error: %s: https://%s%s",
- reason, csp->http->hostport, csp->http->path);
- log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s https://%s%s %s\" 200 %lu",
- csp->ip_addr_str, csp->http->gpc, csp->http->hostport, csp->http->path,
- csp->http->version, message_len-head_length);
- #ifdef FEATURE_CONNECTION_KEEP_ALIVE
- csp->flags &= ~CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
- csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED;
- #endif
- }
- /*********************************************************************
- *
- * 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.
- *
- *********************************************************************/
- extern int file_exists(const char *path)
- {
- FILE *f;
- if ((f = fopen(path, "r")) != NULL)
- {
- fclose(f);
- return 1;
- }
- return 0;
- }
- /*********************************************************************
- *
- * Function : make_certs_path
- *
- * Description : Creates path to file from three pieces. This function
- * 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
- *
- *********************************************************************/
- extern 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)
- char delim[] = "\\";
- path_size += 1;
- #else /* ifndef _WIN32 */
- char delim[] = "/";
- #endif /* ifndef _WIN32 */
- /*
- * 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 = zalloc_or_die(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 = zalloc_or_die(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_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
- *
- *********************************************************************/
- extern unsigned long get_certificate_serial(struct client_state *csp)
- {
- unsigned long exp = 1;
- unsigned long serial = 0;
- int i = CERT_SERIAL_NUM_LENGTH;
- for (; i >= 0; i--)
- {
- serial += exp * (unsigned)csp->http->hash_of_host[i];
- exp *= 256;
- }
- return serial;
- }
- /*********************************************************************
- *
- * Function : generate_certificate_valid_date
- *
- * Description : Turns a time_t into the format expected by mbedTLS.
- *
- * Parameters :
- * 1 : time_spec = The timestamp to convert
- * 2 : buffer = The buffer to write the date to
- * 3 : buffer_size = The size of the buffer
- * 4 : fmt = format
- *
- * Returns : 0 => The conversion worked
- * 1 => The conversion failed
- *
- *********************************************************************/
- static int generate_certificate_valid_date(time_t time_spec, char *buffer,
- size_t buffer_size, const char *fmt)
- {
- struct tm valid_date;
- struct tm *timeptr;
- size_t ret;
- timeptr = privoxy_gmtime_r(&time_spec, &valid_date);
- if (NULL == timeptr)
- {
- return 1;
- }
- ret = strftime(buffer, buffer_size, fmt, timeptr);
- if (ret <= 0)
- {
- return 1;
- }
- return 0;
- }
- /*********************************************************************
- *
- * Function : get_certificate_valid_from_date
- *
- * Description : Generates a "valid from" date in the format
- * expected by mbedTLS.
- *
- * Parameters :
- * 1 : buffer = The buffer to write the date to
- * 2 : buffer_size = The size of the buffer
- * 3 : fmt = format
- *
- * Returns : 0 => The generation worked
- * 1 => The generation failed
- *
- *********************************************************************/
- extern int get_certificate_valid_from_date(char *buffer, size_t buffer_size, const char *fmt)
- {
- time_t time_spec;
- time_spec = time(NULL);
- /* 1 month in the past */
- time_spec -= 30 * 24 * 60 * 60;
- return generate_certificate_valid_date(time_spec, buffer, buffer_size, fmt);
- }
- /*********************************************************************
- *
- * Function : get_certificate_valid_to_date
- *
- * Description : Generates a "valid to" date in the format
- * expected by mbedTLS.
- *
- * Parameters :
- * 1 : buffer = The buffer to write the date to
- * 2 : buffer_size = The size of the buffer
- * 3 : fmt = format
- *
- * Returns : 0 => The generation worked
- * 1 => The generation failed
- *
- *********************************************************************/
- extern int get_certificate_valid_to_date(char *buffer, size_t buffer_size, const char *fmt)
- {
- time_t time_spec;
- time_spec = time(NULL);
- /* Three months in the future */
- time_spec += 90 * 24 * 60 * 60;
- return generate_certificate_valid_date(time_spec, buffer, buffer_size, fmt);
- }
- /*********************************************************************
- *
- * Function : host_is_ip_address
- *
- * Description : Checks whether or not a host is specified by
- * IP address. Does not actually validate the
- * address.
- *
- * Parameters :
- * 1 : host = The host name to check
- *
- * Returns : 1 => Yes
- * 0 => No
- *
- *********************************************************************/
- extern int host_is_ip_address(const char *host)
- {
- const char *p;
- if (NULL != strstr(host, ":"))
- {
- /* Assume an IPv6 address. */
- return 1;
- }
- for (p = host; *p; p++)
- {
- if ((*p != '.') && !privoxy_isdigit(*p))
- {
- /* Not a dot or digit so it can't be an IPv4 address. */
- return 0;
- }
- }
- /*
- * Host only consists of dots and digits so
- * assume that is an IPv4 address.
- */
- return 1;
- }
|