/*********************************************************************
*
* 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 Privoxy was unable "
"to securely connect to the destination server. 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 */
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("Server certificate verification failed
\n"
"
", message_len); strlcat(message, cert->info_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 */ (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; } /********************************************************************* * * Function : enforce_sane_certificate_state * * Description : Makes sure the certificate state is sane. * * Parameters : * 1 : certificate = Path to the potentionally existing certifcate. * 2 : key = Path to the potentionally existing key. * * Returns : -1 => Error * 0 => Certificate state is sane * *********************************************************************/ extern int enforce_sane_certificate_state(const char *certificate, const char *key) { const int certificate_exists = file_exists(certificate); const int key_exists = file_exists(key); if (!certificate_exists && key_exists) { log_error(LOG_LEVEL_ERROR, "A website key already exists but there's no matching certificate. " "Removing %s before creating a new key and certificate.", key); if (unlink(key)) { log_error(LOG_LEVEL_ERROR, "Failed to unlink %s: %E", key); return -1; } } if (certificate_exists && !key_exists) { log_error(LOG_LEVEL_ERROR, "A certificate exists but there's no matching key. " "Removing %s before creating a new key and certificate.", certificate); if (unlink(certificate)) { log_error(LOG_LEVEL_ERROR, "Failed to unlink %s: %E", certificate); return -1; } } return 0; }