1 /*********************************************************************
3 * File : $Source: /cvsroot/ijbswa/current/ssl.c,v $
5 * Purpose : File with TLS/SSL extension. Contains methods for
6 * creating, using and closing TLS/SSL connections that do
7 * not depend on particular TLS/SSL library.
9 * Copyright : Written by and Copyright (c) 2017 Vaclav Svec. FIT CVUT.
10 * Copyright (C) 2018-2020 by Fabian Keil <fk@fabiankeil.de>
12 * This program is free software; you can redistribute it
13 * and/or modify it under the terms of the GNU General
14 * Public License as published by the Free Software
15 * Foundation; either version 2 of the License, or (at
16 * your option) any later version.
18 * This program is distributed in the hope that it will
19 * be useful, but WITHOUT ANY WARRANTY; without even the
20 * implied warranty of MERCHANTABILITY or FITNESS FOR A
21 * PARTICULAR PURPOSE. See the GNU General Public
22 * License for more details.
24 * The GNU General Public License should be included with
25 * this file. If not, you can view it at
26 * http://www.gnu.org/copyleft/gpl.html
27 * or write to the Free Software Foundation, Inc., 59
28 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30 *********************************************************************/
39 #include "ssl_common.h"
42 * Macros for ssl_common.c
44 #define CERT_SERIAL_NUM_LENGTH 4 /* Bytes of hash to be used for creating serial number of certificate. Min=2 and max=16 */
46 /*********************************************************************
48 * Function : client_use_ssl
50 * Description : Tests if client in current client state structure
51 * should use SSL connection or standard connection.
54 * 1 : csp = Current client state (buffers, headers, etc...)
56 * Returns : If client should use TLS/SSL connection, 1 is returned.
57 * Otherwise 0 is returned.
59 *********************************************************************/
60 extern int client_use_ssl(const struct client_state *csp)
62 return csp->http->client_ssl;
66 /*********************************************************************
68 * Function : server_use_ssl
70 * Description : Tests if server in current client state structure
71 * should use SSL connection or standard connection.
74 * 1 : csp = Current client state (buffers, headers, etc...)
76 * Returns : If server should use TLS/SSL connection, 1 is returned.
77 * Otherwise 0 is returned.
79 *********************************************************************/
80 extern int server_use_ssl(const struct client_state *csp)
82 return csp->http->server_ssl;
86 /*********************************************************************
88 * Function : ssl_send_data_delayed
90 * Description : Sends the contents of buf (for n bytes) to given SSL
91 * connection, optionally delaying the operation.
94 * 1 : ssl = SSL context to send data to
95 * 2 : buf = Pointer to data to be sent
96 * 3 : len = Length of data to be sent to the SSL context
97 * 4 : delay = Delay in milliseconds.
99 * Returns : 0 on success (entire buffer sent).
102 *********************************************************************/
103 extern int ssl_send_data_delayed(struct ssl_attr* ssl_attr,
104 const unsigned char *buf, size_t len,
111 if (ssl_send_data(ssl_attr, buf, len) < 0)
124 enum { MAX_WRITE_LENGTH = 10 };
126 if ((i + MAX_WRITE_LENGTH) > len)
128 write_length = len - i;
132 write_length = MAX_WRITE_LENGTH;
135 privoxy_millisleep(delay);
137 if (ssl_send_data(ssl_attr, buf + i, write_length) < 0)
149 /*********************************************************************
151 * Function : ssl_flush_socket
153 * Description : Send any pending "buffered" content with given
154 * SSL connection. Alternative to function flush_socket.
157 * 1 : ssl = SSL context to send buffer to
158 * 2 : iob = The I/O buffer to flush, usually csp->iob.
160 * Returns : On success, the number of bytes send are returned (zero
161 * indicates nothing was sent). On error, -1 is returned.
163 *********************************************************************/
164 extern long ssl_flush_socket(struct ssl_attr *ssl_attr, struct iob *iob)
166 /* Computing length of buffer part to send */
167 long len = iob->eod - iob->cur;
174 /* Sending data to given SSl context */
175 if (ssl_send_data(ssl_attr, (const unsigned char *)iob->cur, (size_t)len) < 0)
179 iob->eod = iob->cur = iob->buf;
184 /*********************************************************************
186 * Function : close_client_and_server_ssl_connections
188 * Description : Checks if client or server should use secured
189 * connection over SSL and if so, closes all of them.
192 * 1 : csp = Current client state (buffers, headers, etc...)
196 *********************************************************************/
197 extern void close_client_and_server_ssl_connections(struct client_state *csp)
199 if (client_use_ssl(csp) == 1)
201 close_client_ssl_connection(csp);
203 if (server_use_ssl(csp) == 1)
205 close_server_ssl_connection(csp);
210 /*********************************************************************
212 * Function : tunnel_established_successfully
214 * Description : Check if parent proxy server response contains
215 * information about successfully created connection with
216 * destination server. (HTTP/... 2xx ...)
219 * 1 : server_response = Buffer with parent proxy server response
220 * 2 : response_len = Length of server_response
222 * Returns : 1 => Connection created successfully
223 * 0 => Connection wasn't created successfully
225 *********************************************************************/
226 extern int tunnel_established_successfully(const char *server_response,
227 unsigned int response_len)
229 unsigned int pos = 0;
231 if (server_response == NULL)
236 /* Tests if "HTTP/" string is at the begin of received response */
237 if (strncmp(server_response, "HTTP/", 5) != 0)
242 for (pos = 0; pos < response_len; pos++)
244 if (server_response[pos] == ' ')
251 * response_len -3 because of buffer end, response structure and 200 code.
252 * There must be at least 3 chars after space.
253 * End of buffer: ... 2xx'\0'
256 if (pos >= (response_len - 3))
261 /* Test HTTP status code */
262 if (server_response[pos + 1] != '2')
271 /*********************************************************************
273 * Function : free_certificate_chain
275 * Description : Frees certificates linked list. This linked list is
276 * used to save information about certificates in
280 * 1 : csp = Current client state (buffers, headers, etc...)
284 *********************************************************************/
285 extern void free_certificate_chain(struct client_state *csp)
287 struct certs_chain *cert = csp->server_certs_chain.next;
289 /* Cleaning buffers */
290 memset(csp->server_certs_chain.info_buf, 0,
291 sizeof(csp->server_certs_chain.info_buf));
292 memset(csp->server_certs_chain.file_buf, 0,
293 sizeof(csp->server_certs_chain.file_buf));
294 csp->server_certs_chain.next = NULL;
296 /* Freeing memory in whole linked list */
299 struct certs_chain *cert_for_free = cert;
301 freez(cert_for_free);
306 /*********************************************************************
308 * Function : ssl_send_certificate_error
310 * Description : Sends info about invalid server certificate to client.
311 * Sent message is including all trusted chain certificates,
312 * that can be downloaded in web browser.
315 * 1 : csp = Current client state (buffers, headers, etc...)
319 *********************************************************************/
320 extern void ssl_send_certificate_error(struct client_state *csp)
322 struct ssl_attr *ssl_attr = &csp->ssl_client_attr;
323 size_t message_len = 0;
325 struct certs_chain *cert = NULL;
327 /* Header of message with certificate information */
328 const char message_begin[] =
329 "HTTP/1.1 200 OK\r\n"
330 "Content-Type: text/html\r\n"
331 "Connection: close\r\n\r\n"
333 "<html><head><title>Server certificate verification failed</title></head>\n"
334 "<body><h1>Server certificate verification failed</h1>\n"
335 "<p><a href=\"https://" CGI_SITE_2_HOST "/\">Privoxy</a> was unable "
336 "to securely connnect to the destination server.</p>"
338 const char message_end[] = "</body></html>\r\n\r\n";
339 char reason[INVALID_CERT_INFO_BUF_SIZE];
340 memset(reason, 0, sizeof(reason));
342 /* Get verification message from verification return code */
343 ssl_crt_verify_info(reason, sizeof(reason), csp);
346 * Computing total length of message with all certificates inside
348 message_len = strlen(message_begin) + strlen(message_end)
349 + strlen(reason) + strlen("</p>") + 1;
351 cert = &(csp->server_certs_chain);
352 while (cert->next != NULL)
354 size_t base64_len = 4 * ((strlen(cert->file_buf) + 2) / 3) + 1;
356 message_len += strlen(cert->info_buf) + strlen("<pre></pre>\n")
357 + base64_len + strlen("<a href=\"data:application"
358 "/x-x509-ca-cert;base64,\">Download certificate</a>");
363 * Joining all blocks in one long message
365 char message[message_len];
366 memset(message, 0, message_len);
368 strlcpy(message, message_begin, message_len);
369 strlcat(message, reason , message_len);
370 strlcat(message, "</p>" , message_len);
372 cert = &(csp->server_certs_chain);
373 while (cert->next != NULL)
376 size_t base64_len = 4 * ((strlen(cert->file_buf) + 2) / 3) + 1; /* +1 for terminating null*/
377 char base64_buf[base64_len];
378 memset(base64_buf, 0, base64_len);
380 /* Encoding certificate into base64 code */
381 ret = ssl_base64_encode((unsigned char*)base64_buf,
382 base64_len, &olen, (const unsigned char*)cert->file_buf,
383 strlen(cert->file_buf));
386 log_error(LOG_LEVEL_ERROR,
387 "Encoding to base64 failed, buffer is to small");
390 strlcat(message, "<pre>", message_len);
391 strlcat(message, cert->info_buf, message_len);
392 strlcat(message, "</pre>\n", message_len);
396 strlcat(message, "<a href=\"data:application/x-x509-ca-cert;base64,",
398 strlcat(message, base64_buf, message_len);
399 strlcat(message, "\">Download certificate</a>", message_len);
404 strlcat(message, message_end, message_len);
407 * Sending final message to client
409 ssl_send_data(ssl_attr, (const unsigned char *)message, strlen(message));
411 free_certificate_chain(csp);
415 /*********************************************************************
417 * Function : file_exists
419 * Description : Tests if file exists and is readable.
422 * 1 : path = Path to tested file.
424 * Returns : 1 => File exists and is readable.
425 * 0 => File doesn't exist or is not readable.
427 *********************************************************************/
428 extern int file_exists(const char *path)
431 if ((f = fopen(path, "r")) != NULL)
441 /*********************************************************************
443 * Function : make_certs_path
445 * Description : Creates path to file from three pieces. This function
446 * takes parameters and puts them in one new mallocated
447 * char * in correct order. Returned variable must be freed
448 * by caller. This function is mainly used for creating
449 * paths of certificates and keys files.
452 * 1 : conf_dir = Name/path of directory where is the file.
453 * '.' can be used for current directory.
454 * 2 : file_name = Name of file in conf_dir without suffix.
455 * 3 : suffix = Suffix of given file_name.
457 * Returns : path => Path was built up successfully
458 * NULL => Path can't be built up
460 *********************************************************************/
461 extern char *make_certs_path(const char *conf_dir, const char *file_name,
464 /* Test if all given parameters are valid */
465 if (conf_dir == NULL || *conf_dir == '\0' || file_name == NULL ||
466 *file_name == '\0' || suffix == NULL || *suffix == '\0')
468 log_error(LOG_LEVEL_ERROR,
469 "make_certs_path failed: bad input parameters");
474 size_t path_size = strlen(conf_dir)
475 + strlen(file_name) + strlen(suffix) + 2;
477 /* Setting delimiter and editing path length */
478 #if defined(_WIN32) || defined(__OS2__)
481 #else /* ifndef _WIN32 || __OS2__ */
483 #endif /* ifndef _WIN32 || __OS2__ */
486 * Building up path from many parts
489 if (*conf_dir != '/' && basedir && *basedir)
492 * Replacing conf_dir with basedir. This new variable contains
493 * absolute path to cwd.
495 path_size += strlen(basedir) + 2;
496 path = zalloc_or_die(path_size);
498 strlcpy(path, basedir, path_size);
499 strlcat(path, delim, path_size);
500 strlcat(path, conf_dir, path_size);
501 strlcat(path, delim, path_size);
502 strlcat(path, file_name, path_size);
503 strlcat(path, suffix, path_size);
506 #endif /* defined unix */
508 path = zalloc_or_die(path_size);
510 strlcpy(path, conf_dir, path_size);
511 strlcat(path, delim, path_size);
512 strlcat(path, file_name, path_size);
513 strlcat(path, suffix, path_size);
520 /*********************************************************************
522 * Function : get_certificate_serial
524 * Description : Computes serial number for new certificate from host
525 * name hash. This hash must be already saved in csp
529 * 1 : csp = Current client state (buffers, headers, etc...)
531 * Returns : Serial number for new certificate
533 *********************************************************************/
534 extern unsigned long get_certificate_serial(struct client_state *csp)
536 unsigned long exp = 1;
537 unsigned long serial = 0;
539 int i = CERT_SERIAL_NUM_LENGTH;
543 serial += exp * (unsigned)csp->http->hash_of_host[i];
550 /*********************************************************************
552 * Function : generate_certificate_valid_date
554 * Description : Turns a time_t into the format expected by mbedTLS.
557 * 1 : time_spec = The timestamp to convert
558 * 2 : buffer = The buffer to write the date to
559 * 3 : buffer_size = The size of the buffer
562 * Returns : 0 => The conversion worked
563 * 1 => The conversion failed
565 *********************************************************************/
566 static int generate_certificate_valid_date(time_t time_spec, char *buffer,
567 size_t buffer_size, const char *fmt)
569 struct tm valid_date;
573 timeptr = privoxy_gmtime_r(&time_spec, &valid_date);
579 ret = strftime(buffer, buffer_size, fmt, timeptr);
590 /*********************************************************************
592 * Function : get_certificate_valid_from_date
594 * Description : Generates a "valid from" date in the format
595 * expected by mbedTLS.
598 * 1 : buffer = The buffer to write the date to
599 * 2 : buffer_size = The size of the buffer
602 * Returns : 0 => The generation worked
603 * 1 => The generation failed
605 *********************************************************************/
606 extern int get_certificate_valid_from_date(char *buffer, size_t buffer_size, const char *fmt)
610 time_spec = time(NULL);
611 /* 1 month in the past */
612 time_spec -= 30 * 24 * 60 * 60;
614 return generate_certificate_valid_date(time_spec, buffer, buffer_size, fmt);
619 /*********************************************************************
621 * Function : get_certificate_valid_to_date
623 * Description : Generates a "valid to" date in the format
624 * expected by mbedTLS.
627 * 1 : buffer = The buffer to write the date to
628 * 2 : buffer_size = The size of the buffer
631 * Returns : 0 => The generation worked
632 * 1 => The generation failed
634 *********************************************************************/
635 extern int get_certificate_valid_to_date(char *buffer, size_t buffer_size, const char *fmt)
639 time_spec = time(NULL);
640 /* Three months in the future */
641 time_spec += 90 * 24 * 60 * 60;
643 return generate_certificate_valid_date(time_spec, buffer, buffer_size, fmt);
648 /*********************************************************************
650 * Function : host_is_ip_address
652 * Description : Checks whether or not a host is specified by
653 * IP address. Does not actually validate the
657 * 1 : host = The host name to check
662 *********************************************************************/
663 extern int host_is_ip_address(const char *host)
667 if (NULL != strstr(host, ":"))
669 /* Assume an IPv6 address. */
673 for (p = host; *p; p++)
675 if ((*p != '.') && !privoxy_isdigit(*p))
677 /* Not a dot or digit so it can't be an IPv4 address. */
683 * Host only consists of dots and digits so
684 * assume that is an IPv4 address.