#include "mbedtls/pem.h"
#include "mbedtls/base64.h"
#include "mbedtls/error.h"
+#include "mbedtls/oid.h"
+#include "mbedtls/asn1write.h"
#include "config.h"
#include "project.h"
#include "errlog.h"
#include "jcc.h"
#include "ssl.h"
+#include "encode.h"
/*
#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 set 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 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 */
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);
*
* Function : is_ssl_pending
*
- * Description : Tests if there are some waiting data on ssl connection
+ * Description : Tests if there are some waiting data on ssl connection.
+ * Only considers data that has actually been received
+ * locally and ignores data that is still on the fly
+ * or has not yet been sent by the remote end.
*
* Parameters :
* 1 : ssl = SSL context to test
}
+/*********************************************************************
+ *
+ * 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 = 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(mbedtls_ssl_context *ssl,
+ const unsigned char *buf, size_t len,
+ unsigned int delay)
+{
+ size_t i = 0;
+
+ if (delay == 0)
+ {
+ if (ssl_send_data(ssl, 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, buf + i, write_length) < 0)
+ {
+ return -1;
+ }
+ i += write_length;
+ }
+
+ return 0;
+
+}
+
+
/*********************************************************************
*
* Function : ssl_recv_data
* 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]));
+ privoxy_mutex_lock(&certificate_mutex);
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]));
+ privoxy_mutex_unlock(&certificate_mutex);
ret = -1;
goto exit;
}
- privoxy_mutex_unlock(&(certificates_mutexes[cert_mutex_id]));
+ privoxy_mutex_unlock(&certificate_mutex);
/*
* Seed the RNG
{
/*
* We can't use function mbedtls_net_free, because this function
- * inter alia close TCP connection on setted fd. Instead of this
+ * inter alia close TCP connection on set fd. Instead of this
* function, we change fd to -1, which is the same what does
* rest of mbedtls_net_free function.
*/
/* Log the reason without the trailing new line */
log_error(LOG_LEVEL_ERROR,
- "The X509 certificate verification failed: %N",
- strlen(reason)-1, reason);
+ "X509 certificate verification for %s failed: %N",
+ csp->http->hostport, strlen(reason)-1, reason);
ret = -1;
}
else
{
log_error(LOG_LEVEL_ERROR,
"mbedtls_ssl_handshake with server failed: %s", err_buf);
+ free_certificate_chain(csp);
ret = -1;
}
goto exit;
{
/*
* We can't use function mbedtls_net_free, because this function
- * inter alia close TCP connection on setted fd. Instead of this
+ * inter alia close TCP connection on set fd. Instead of this
* function, we change fd to -1, which is the same what does
* rest of mbedtls_net_free function.
*/
}
+/*********************************************************************
+ *
+ * Function : ssl_certificate_is_invalid
+ *
+ * Description : Checks whether or not a certificate is valid.
+ * Currently only checks that the certificate can be
+ * parsed and that the "valid to" date is in the future.
+ *
+ * Parameters :
+ * 1 : cert_file = The certificate to check
+ *
+ * Returns : 0 => The certificate is valid.
+ * 1 => The certificate is invalid
+ *
+ *********************************************************************/
+static int ssl_certificate_is_invalid(const char *cert_file)
+{
+ mbedtls_x509_crt cert;
+ int ret;
+
+ mbedtls_x509_crt_init(&cert);
+
+ ret = mbedtls_x509_crt_parse_file(&cert, cert_file);
+ if (ret != 0)
+ {
+ char err_buf[ERROR_BUF_SIZE];
+
+ mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+ log_error(LOG_LEVEL_ERROR,
+ "Loading certificate %s to check validity failed: %s",
+ cert_file, err_buf);
+ mbedtls_x509_crt_free(&cert);
+
+ return 1;
+ }
+ if (mbedtls_x509_time_is_past(&cert.valid_to))
+ {
+ mbedtls_x509_crt_free(&cert);
+
+ return 1;
+ }
+
+ mbedtls_x509_crt_free(&cert);
+
+ return 0;
+
+}
+
+
+/*********************************************************************
+ *
+ * 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
+ *
+ * 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)
+{
+#ifdef HAVE_GMTIME_R
+ struct tm valid_date;
+#endif
+ struct tm *timeptr;
+ size_t ret;
+
+#ifdef HAVE_GMTIME_R
+ timeptr = gmtime_r(&time_spec, &valid_date);
+#elif defined(MUTEX_LOCKS_AVAILABLE)
+ privoxy_mutex_lock(&gmtime_mutex);
+ timeptr = gmtime(&time_spec);
+#else
+#warning Using unlocked gmtime()
+ timeptr = gmtime(&time_spec);
+#endif
+ if (NULL == timeptr)
+ {
+#if !defined(HAVE_GMTIME_R) && defined(MUTEX_LOCKS_AVAILABLE)
+ privoxy_mutex_unlock(&gmtime_mutex);
+#endif
+ return 1;
+ }
+
+ ret = strftime(buffer, buffer_size, "%Y%m%d%H%M%S", timeptr);
+#if !defined(HAVE_GMTIME_R) && defined(MUTEX_LOCKS_AVAILABLE)
+ privoxy_mutex_unlock(&gmtime_mutex);
+#endif
+ if (ret != 14)
+ {
+ 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
+ *
+ * Returns : 0 => The generation worked
+ * 1 => The generation failed
+ *
+ *********************************************************************/
+static int get_certificate_valid_from_date(char *buffer, size_t buffer_size)
+{
+ 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);
+
+}
+
+
+/*********************************************************************
+ *
+ * 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
+ *
+ * Returns : 0 => The generation worked
+ * 1 => The generation failed
+ *
+ *********************************************************************/
+static int get_certificate_valid_to_date(char *buffer, size_t buffer_size)
+{
+ 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);
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : set_subject_alternative_name
+ *
+ * Description : Sets the Subject Alternative Name extension to a cert
+ *
+ * Parameters :
+ * 1 : cert = The certificate to modify
+ * 2 : hostname = The hostname to add
+ *
+ * Returns : <0 => Error while creating certificate.
+ * 0 => It worked
+ *
+ *********************************************************************/
+static int set_subject_alternative_name(mbedtls_x509write_cert *cert, const char *hostname)
+{
+ char err_buf[ERROR_BUF_SIZE];
+ int ret;
+ char *subject_alternative_name;
+ size_t subject_alternative_name_len;
+#define MBEDTLS_SUBJECT_ALTERNATIVE_NAME_MAX_LEN 255
+ unsigned char san_buf[MBEDTLS_SUBJECT_ALTERNATIVE_NAME_MAX_LEN + 1];
+ unsigned char *c;
+ int len;
+
+ subject_alternative_name_len = strlen(hostname) + 1;
+ subject_alternative_name = zalloc_or_die(subject_alternative_name_len);
+
+ strlcpy(subject_alternative_name, hostname, subject_alternative_name_len);
+
+ memset(san_buf, 0, sizeof(san_buf));
+
+ c = san_buf + sizeof(san_buf);
+ len = 0;
+
+ ret = mbedtls_asn1_write_raw_buffer(&c, san_buf,
+ (const unsigned char *)subject_alternative_name,
+ strlen(subject_alternative_name));
+ if (ret < 0)
+ {
+ mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+ log_error(LOG_LEVEL_ERROR,
+ "mbedtls_asn1_write_raw_buffer() failed: %s", err_buf);
+ goto exit;
+ }
+ len += ret;
+
+ ret = mbedtls_asn1_write_len(&c, san_buf, strlen(subject_alternative_name));
+ if (ret < 0)
+ {
+ mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+ log_error(LOG_LEVEL_ERROR,
+ "mbedtls_asn1_write_len() failed: %s", err_buf);
+ goto exit;
+ }
+ len += ret;
+
+ ret = mbedtls_asn1_write_tag(&c, san_buf, MBEDTLS_ASN1_CONTEXT_SPECIFIC | 2);
+ if (ret < 0)
+ {
+ mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+ log_error(LOG_LEVEL_ERROR,
+ "mbedtls_asn1_write_tag() failed: %s", err_buf);
+ goto exit;
+ }
+ len += ret;
+
+ ret = mbedtls_asn1_write_len(&c, san_buf, (size_t)len);
+ if (ret < 0)
+ {
+ mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+ log_error(LOG_LEVEL_ERROR,
+ "mbedtls_asn1_write_len() failed: %s", err_buf);
+ goto exit;
+ }
+ len += ret;
+
+ ret = mbedtls_asn1_write_tag(&c, san_buf,
+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+ if (ret < 0)
+ {
+ mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+ log_error(LOG_LEVEL_ERROR,
+ "mbedtls_asn1_write_tag() failed: %s", err_buf);
+ goto exit;
+ }
+ len += ret;
+
+ ret = mbedtls_x509write_crt_set_extension(cert,
+ MBEDTLS_OID_SUBJECT_ALT_NAME,
+ MBEDTLS_OID_SIZE(MBEDTLS_OID_SUBJECT_ALT_NAME),
+ 0, san_buf + sizeof(san_buf) - len, (size_t)len);
+ if (ret < 0)
+ {
+ mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+ log_error(LOG_LEVEL_ERROR,
+ "mbedtls_x509write_crt_set_extension() failed: %s", err_buf);
+ }
+
+exit:
+ freez(subject_alternative_name);
+
+ return ret;
+
+}
+
/*********************************************************************
*
* Function : generate_webpage_certificate
int ret = 0;
char err_buf[ERROR_BUF_SIZE];
cert_options cert_opt;
+ char cert_valid_from[15];
+ char cert_valid_to[15];
/* 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;
+
+ 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.output_file == NULL)
+ {
+ return -1;
+ }
+
+ cert_opt.subject_key = make_certs_path(csp->config->certificate_directory,
+ (const char *)csp->http->hash_of_host_hex, KEY_FILE_TYPE);
+ if (cert_opt.subject_key == NULL)
+ {
+ freez(cert_opt.output_file);
+ return -1;
+ }
+
+ if (file_exists(cert_opt.output_file) == 1)
+ {
+ /* The file exists, but is it valid? */
+ if (ssl_certificate_is_invalid(cert_opt.output_file))
+ {
+ log_error(LOG_LEVEL_CONNECT,
+ "Certificate %s is no longer valid. Removing it.",
+ cert_opt.output_file);
+ if (unlink(cert_opt.output_file))
+ {
+ log_error(LOG_LEVEL_ERROR, "Failed to unlink %s: %E",
+ cert_opt.output_file);
+
+ freez(cert_opt.output_file);
+ freez(cert_opt.subject_key);
+
+ return -1;
+ }
+ if (unlink(cert_opt.subject_key))
+ {
+ log_error(LOG_LEVEL_ERROR, "Failed to unlink %s: %E",
+ cert_opt.subject_key);
+
+ freez(cert_opt.output_file);
+ freez(cert_opt.subject_key);
+
+ return -1;
+ }
+ }
+ else
+ {
+ freez(cert_opt.output_file);
+ freez(cert_opt.subject_key);
+
+ return 0;
+ }
+ }
/*
* Create key for requested host
int subject_key_len = generate_key(csp, &key_buf);
if (subject_key_len < 0)
{
+ freez(cert_opt.output_file);
+ freez(cert_opt.subject_key);
log_error(LOG_LEVEL_ERROR, "Key generating failed");
return -1;
}
* 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;
+ unsigned long certificate_serial_time = (unsigned long)time(NULL);
+ int serial_num_size = snprintf(NULL, 0, "%lu%lu",
+ certificate_serial_time, 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);
+ ret = snprintf(serial_num_text, (size_t)serial_num_size, "%lu%lu",
+ certificate_serial_time, certificate_serial);
if (ret < 0 || ret >= serial_num_size)
{
log_error(LOG_LEVEL_ERROR,
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)
+ if (get_certificate_valid_from_date(cert_valid_from, sizeof(cert_valid_from))
+ || get_certificate_valid_to_date(cert_valid_to, sizeof(cert_valid_to)))
{
+ log_error(LOG_LEVEL_ERROR, "Generating one of the validity dates failed");
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.not_before = cert_valid_from;
+ cert_opt.not_after = 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
+ * Test if the private key was already created.
+ * XXX: Can this still happen?
*/
- if (file_exists(cert_opt.output_file) == 1 && subject_key_len == 0)
+ if (subject_key_len == 0)
{
+ log_error(LOG_LEVEL_ERROR, "Subject key was already created");
ret = 0;
goto exit;
}
}
#endif /* MBEDTLS_SHA1_C */
+ if (set_subject_alternative_name(&cert, csp->http->host))
+ {
+ /* Errors are already logged by set_subject_alternative_name() */
+ ret = -1;
+ goto exit;
+ }
+
/*
* Writing certificate into file
*/
}
-/*********************************************************************
- *
- * 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
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--)
{
int ret = 0;
struct certs_chain *cert = NULL;
- /* Header of message with certificate informations */
+ /* 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"
- "<html><body><h1>Invalid server certificate</h1><p>Reason: ";
+ "<html><body><h1>Server certificate verification failed</h1><p>Reason: ";
const char message_end[] = "</body></html>\r\n\r\n";
char reason[INVALID_CERT_INFO_BUF_SIZE];
memset(reason, 0, sizeof(reason));
{
size_t base64_len = 4 * ((strlen(cert->file_buf) + 2) / 3) + 1;
- message_len += strlen(cert->text_buf) + strlen("<pre></pre>\n")
+ 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;
}
strlcat(message, "<pre>", message_len);
- strlcat(message, cert->text_buf, message_len);
+ strlcat(message, cert->info_buf, message_len);
strlcat(message, "</pre>\n", message_len);
if (ret == 0)
*/
ssl_send_data(&(csp->mbedtls_client_attr.ssl),
(const unsigned char *)message, strlen(message));
- /*
- * Waiting before closing connection. Some browsers don'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.
+ * It's called once for each certificate in the server's
+ * certificate trusted chain and prepares information about
+ * the certificate. The information can be used to inform
+ * the user about invalid certificates.
*
* Parameters :
* 1 : csp_void = Current client state (buffers, headers, etc...)
*/
last->next = malloc_or_die(sizeof(struct certs_chain));
last->next->next = NULL;
- memset(last->next->text_buf, 0, sizeof(last->next->text_buf));
+ memset(last->next->info_buf, 0, sizeof(last->next->info_buf));
memset(last->next->file_buf, 0, sizeof(last->next->file_buf));
/*
crt->raw.p, crt->raw.len, (unsigned char *)last->file_buf,
sizeof(last->file_buf)-1, &olen)) != 0)
{
+ char err_buf[ERROR_BUF_SIZE];
+
+ mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+ log_error(LOG_LEVEL_ERROR, "mbedtls_pem_write_buffer() failed: %s",
+ err_buf);
+
return(ret);
}
/*
* Saving certificate information into buffer
*/
- mbedtls_x509_crt_info(last->text_buf, sizeof(last->text_buf) - 1,
- CERT_INFO_PREFIX, crt);
+ {
+ char buf[CERT_INFO_BUF_SIZE];
+ char *encoded_text;
+
+ mbedtls_x509_crt_info(buf, sizeof(buf), CERT_INFO_PREFIX, crt);
+ encoded_text = html_encode(buf);
+ strlcpy(last->info_buf, encoded_text, sizeof(last->info_buf));
+ freez(encoded_text);
+ }
return 0;
}
* Function : free_certificate_chain
*
* Description : Frees certificates linked list. This linked list is
- * used to save informations about certificates in
+ * used to save information about certificates in
* trusted chain.
*
* Parameters :
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.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 */
- if (cert != NULL)
+ while (cert != NULL)
{
- do
- {
- struct certs_chain *cert_for_free = cert;
- cert = cert->next;
- freez(cert_for_free);
- } while (cert != NULL);
+ struct certs_chain *cert_for_free = cert;
+ cert = cert->next;
+ freez(cert_for_free);
}
}
* Function : tunnel_established_successfully
*
* Description : Check if parent proxy server response contains
- * informations about successfully created connection with
+ * information about successfully created connection with
* destination server. (HTTP/... 2xx ...)
*
* Parameters :