]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/ssl/support.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / ssl / support.cc
index e68774aeb396e49032c0641892585243848b0be9..551b75698110532dd8e5b7cb5d6be7d659868e8c 100644 (file)
  */
 #if USE_SSL
 
-#include "fde.h"
 #include "acl/FilledChecklist.h"
+#include "fde.h"
+#include "globals.h"
+#include "protos.h"
 #include "ssl/ErrorDetail.h"
 #include "ssl/support.h"
 #include "ssl/gadgets.h"
 
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+const char *Ssl::BumpModeStr[] = {
+    "none",
+    "client-first",
+    "server-first",
+    NULL
+};
+
 /**
  \defgroup ServerProtocolSSLInternal Server-Side SSL Internals
  \ingroup ServerProtocolSSLAPI
@@ -67,8 +80,7 @@ ssl_ask_password_cb(char *buf, int size, int rwflag, void *userdata)
         len = strlen(buf);
 
     while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
-
-        len--;
+        --len;
 
     buf[len] = '\0';
 
@@ -119,12 +131,12 @@ ssl_temp_rsa_cb(SSL * ssl, int anInt, int keylen)
         break;
 
     default:
-        debugs(83, 1, "ssl_temp_rsa_cb: Unexpected key length " << keylen);
+        debugs(83, DBG_IMPORTANT, "ssl_temp_rsa_cb: Unexpected key length " << keylen);
         return NULL;
     }
 
     if (rsa == NULL) {
-        debugs(83, 1, "ssl_temp_rsa_cb: Failed to generate key " << keylen);
+        debugs(83, DBG_IMPORTANT, "ssl_temp_rsa_cb: Failed to generate key " << keylen);
         return NULL;
     }
 
@@ -132,7 +144,7 @@ ssl_temp_rsa_cb(SSL * ssl, int anInt, int keylen)
         if (do_debug(83, 5))
             PEM_write_RSAPrivateKey(debug_log, rsa, NULL, NULL, 0, NULL, NULL);
 
-        debugs(83, 1, "Generated ephemeral RSA key of length " << keylen);
+        debugs(83, DBG_IMPORTANT, "Generated ephemeral RSA key of length " << keylen);
     }
 
     return rsa;
@@ -171,7 +183,7 @@ int Ssl::matchX509CommonNames(X509 *peer_cert, void *check_data, int (*check_fun
 
     if (altnames) {
         int numalts = sk_GENERAL_NAME_num(altnames);
-        for (int i = 0; i < numalts; i++) {
+        for (int i = 0; i < numalts; ++i) {
             const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i);
             if (check->type != GEN_DNS) {
                 continue;
@@ -200,18 +212,26 @@ static int check_domain( void *check_data, ASN1_STRING *cn_data)
     return matchDomainName(server, cn[0] == '*' ? cn + 1 : cn);
 }
 
+bool Ssl::checkX509ServerValidity(X509 *cert, const char *server)
+{
+    return matchX509CommonNames(cert, (void *)server, check_domain);
+}
+
 /// \ingroup ServerProtocolSSLInternal
 static int
 ssl_verify_cb(int ok, X509_STORE_CTX * ctx)
 {
-    char buffer[256];
+    // preserve original ctx->error before SSL_ calls can overwrite it
+    Ssl::ssl_error_t error_no = ok ? SSL_ERROR_NONE : ctx->error;
+
+    char buffer[256] = "";
     SSL *ssl = (SSL *)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
     SSL_CTX *sslctx = SSL_get_SSL_CTX(ssl);
     const char *server = (const char *)SSL_get_ex_data(ssl, ssl_ex_index_server);
     void *dont_verify_domain = SSL_CTX_get_ex_data(sslctx, ssl_ctx_ex_index_dont_verify_domain);
     ACLChecklist *check = (ACLChecklist*)SSL_get_ex_data(ssl, ssl_ex_index_cert_error_check);
+    X509 *peeked_cert = (X509 *)SSL_get_ex_data(ssl, ssl_ex_index_ssl_peeked_cert);
     X509 *peer_cert = ctx->cert;
-    Ssl::ssl_error_t error_no = SSL_ERROR_NONE;
 
     X509_NAME_oneline(X509_get_subject_name(peer_cert), buffer,
                       sizeof(buffer));
@@ -220,70 +240,70 @@ ssl_verify_cb(int ok, X509_STORE_CTX * ctx)
         debugs(83, 5, "SSL Certificate signature OK: " << buffer);
 
         if (server) {
-            int found = Ssl::matchX509CommonNames(peer_cert, (void *)server, check_domain);
-
-            if (!found) {
+            if (!Ssl::checkX509ServerValidity(peer_cert, server)) {
                 debugs(83, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " << buffer << " does not match domainname " << server);
                 ok = 0;
                 error_no = SQUID_X509_V_ERR_DOMAIN_MISMATCH;
-
-                if (check)
-                    Filled(check)->ssl_error = SQUID_X509_V_ERR_DOMAIN_MISMATCH;
             }
         }
-    } else {
-        error_no = ctx->error;
-        switch (ctx->error) {
-
-        case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
-        case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
-            debugs(83, 5, "SSL Certficate error: CA not known: " << buffer);
-            break;
-
-        case X509_V_ERR_CERT_NOT_YET_VALID:
-            debugs(83, 5, "SSL Certficate not yet valid: " << buffer);
-            break;
-
-        case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
-            debugs(83, 5, "SSL Certificate has illegal \'not before\' field: " <<
-                   buffer);
-
-            break;
-
-        case X509_V_ERR_CERT_HAS_EXPIRED:
-            debugs(83, 5, "SSL Certificate expired: " << buffer);
-            break;
-
-        case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
-            debugs(83, 5, "SSL Certificate has invalid \'not after\' field: " << buffer);
-            break;
-
-        case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
-            debugs(83, 5, "SSL Certificate is self signed: " << buffer);
-            break;
+    }
 
-        default:
-            debugs(83, 1, "SSL unknown certificate error " << ctx->error << " in " << buffer);
-            break;
+    if (ok && peeked_cert) {
+        // Check whether the already peeked certificate matches the new one.
+        if (X509_cmp(peer_cert, peeked_cert) != 0) {
+            debugs(83, 2, "SQUID_X509_V_ERR_CERT_CHANGE: Certificate " << buffer << " does not match peeked certificate");
+            ok = 0;
+            error_no =  SQUID_X509_V_ERR_CERT_CHANGE;
         }
-
-        if (check)
-            Filled(check)->ssl_error = ctx->error;
     }
 
-    if (!ok && check) {
-        if (check->fastCheck()) {
-            debugs(83, 3, "bypassing SSL error " << ctx->error << " in " << buffer);
-            ok = 1;
-        } else {
-            debugs(83, 5, "confirming SSL error " << ctx->error);
+    if (!ok) {
+        Ssl::Errors *errs = static_cast<Ssl::Errors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors));
+        if (!errs) {
+            errs = new Ssl::Errors(error_no);
+            if (!SSL_set_ex_data(ssl, ssl_ex_index_ssl_errors,  (void *)errs)) {
+                debugs(83, 2, "Failed to set ssl error_no in ssl_verify_cb: Certificate " << buffer);
+                delete errs;
+                errs = NULL;
+            }
+        } else // remember another error number
+            errs->push_back_unique(error_no);
+
+        if (const char *err_descr = Ssl::GetErrorDescr(error_no))
+            debugs(83, 5, err_descr << ": " << buffer);
+        else
+            debugs(83, DBG_IMPORTANT, "SSL unknown certificate error " << error_no << " in " << buffer);
+
+        if (check) {
+            ACLFilledChecklist *filledCheck = Filled(check);
+            assert(!filledCheck->sslErrors);
+            filledCheck->sslErrors = new Ssl::Errors(error_no);
+            if (check->fastCheck() == ACCESS_ALLOWED) {
+                debugs(83, 3, "bypassing SSL error " << error_no << " in " << buffer);
+                ok = 1;
+            } else {
+                debugs(83, 5, "confirming SSL error " << error_no);
+            }
+            delete filledCheck->sslErrors;
+            filledCheck->sslErrors = NULL;
         }
     }
 
     if (!dont_verify_domain && server) {}
 
-    if (error_no != SSL_ERROR_NONE && !SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail) ) {
-        Ssl::ErrorDetail *errDetail = new Ssl::ErrorDetail(error_no, peer_cert);
+    if (!ok && !SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail) ) {
+
+        // Find the broken certificate. It may be intermediate.
+        X509 *broken_cert = peer_cert; // reasonable default if search fails
+        // Our SQUID_X509_V_ERR_DOMAIN_MISMATCH implies peer_cert is at fault.
+        if (error_no != SQUID_X509_V_ERR_DOMAIN_MISMATCH) {
+            if (X509 *last_used_cert = X509_STORE_CTX_get_current_cert(ctx))
+                broken_cert = last_used_cert;
+        }
+
+        Ssl::ErrorDetail *errDetail =
+            new Ssl::ErrorDetail(error_no, peer_cert, broken_cert);
+
         if (!SSL_set_ex_data(ssl, ssl_ex_index_ssl_error_detail,  errDetail)) {
             debugs(83, 2, "Failed to set Ssl::ErrorDetail in ssl_verify_cb: Certificate " << buffer);
             delete errDetail;
@@ -353,7 +373,7 @@ ssl_options[] = {
 #endif
 #if SSL_OP_ALL
     {
-        "ALL", SSL_OP_ALL
+        "ALL", (long)SSL_OP_ALL
     },
 #endif
 #if SSL_OP_SINGLE_DH_USE
@@ -410,6 +430,16 @@ ssl_options[] = {
     {
         "NO_TLSv1", SSL_OP_NO_TLSv1
     },
+#endif
+#if SSL_OP_NO_TLSv1_1
+    {
+        "NO_TLSv1_1", SSL_OP_NO_TLSv1_1
+    },
+#endif
+#if SSL_OP_NO_TLSv1_2
+    {
+        "NO_TLSv1_2", SSL_OP_NO_TLSv1_2
+    },
 #endif
     {
         "", 0
@@ -423,7 +453,7 @@ ssl_options[] = {
 static long
 ssl_parse_options(const char *options)
 {
-    long op = SSL_OP_ALL;
+    long op = 0;
     char *tmp;
     char *option;
 
@@ -448,12 +478,12 @@ ssl_parse_options(const char *options)
 
         case '-':
             mode = MODE_REMOVE;
-            option++;
+            ++option;
             break;
 
         case '+':
             mode = MODE_ADD;
-            option++;
+            ++option;
             break;
 
         default:
@@ -461,7 +491,7 @@ ssl_parse_options(const char *options)
             break;
         }
 
-        for (opttmp = ssl_options; opttmp->name; opttmp++) {
+        for (opttmp = ssl_options; opttmp->name; ++opttmp) {
             if (strcmp(opttmp->name, option) == 0) {
                 opt = opttmp;
                 break;
@@ -587,6 +617,23 @@ ssl_free_ErrorDetail(void *, void *ptr, CRYPTO_EX_DATA *,
     delete errDetail;
 }
 
+static void
+ssl_free_SslErrors(void *, void *ptr, CRYPTO_EX_DATA *,
+                   int, long, void *)
+{
+    Ssl::Errors *errs = static_cast <Ssl::Errors*>(ptr);
+    delete errs;
+}
+
+// "free" function for X509 certificates
+static void
+ssl_free_X509(void *, void *ptr, CRYPTO_EX_DATA *,
+              int, long, void *)
+{
+    X509  *cert = static_cast <X509 *>(ptr);
+    X509_free(cert);
+}
+
 /// \ingroup ServerProtocolSSLInternal
 static void
 ssl_initialize(void)
@@ -626,6 +673,8 @@ ssl_initialize(void)
     ssl_ctx_ex_index_dont_verify_domain = SSL_CTX_get_ex_new_index(0, (void *) "dont_verify_domain", NULL, NULL, NULL);
     ssl_ex_index_cert_error_check = SSL_get_ex_new_index(0, (void *) "cert_error_check", NULL, &ssl_dupAclChecklist, &ssl_freeAclChecklist);
     ssl_ex_index_ssl_error_detail = SSL_get_ex_new_index(0, (void *) "ssl_error_detail", NULL, NULL, &ssl_free_ErrorDetail);
+    ssl_ex_index_ssl_peeked_cert  = SSL_get_ex_new_index(0, (void *) "ssl_peeked_cert", NULL, NULL, &ssl_free_X509);
+    ssl_ex_index_ssl_errors =  SSL_get_ex_new_index(0, (void *) "ssl_errors", NULL, NULL, &ssl_free_SslErrors);
 }
 
 /// \ingroup ServerProtocolSSLInternal
@@ -646,7 +695,7 @@ ssl_load_crl(SSL_CTX *sslContext, const char *CRLfile)
         if (!X509_STORE_add_crl(st, crl))
             debugs(83, 2, "WARNING: Failed to add CRL from file '" << CRLfile << "'");
         else
-            count++;
+            ++count;
 
         X509_CRL_free(crl);
     }
@@ -678,11 +727,21 @@ sslCreateServerContext(const char *certfile, const char *keyfile, int version, c
     if (!CAfile)
         CAfile = clientCA;
 
+    if (!certfile) {
+        debugs(83, DBG_CRITICAL, "ERROR: No certificate file");
+        return NULL;
+    }
+
     switch (version) {
 
     case 2:
+#ifndef OPENSSL_NO_SSL2
         debugs(83, 5, "Using SSLv2.");
         method = SSLv2_server_method();
+#else
+        debugs(83, DBG_IMPORTANT, "SSLv2 is not available in this Proxy.");
+        return NULL;
+#endif
         break;
 
     case 3:
@@ -695,6 +754,26 @@ sslCreateServerContext(const char *certfile, const char *keyfile, int version, c
         method = TLSv1_server_method();
         break;
 
+    case 5:
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L  // NP: not sure exactly which sub-version yet.
+        debugs(83, 5, "Using TLSv1.1.");
+        method = TLSv1_1_server_method();
+#else
+        debugs(83, DBG_IMPORTANT, "TLSv1.1 is not available in this Proxy.");
+        return NULL;
+#endif
+        break;
+
+    case 6:
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L // NP: not sure exactly which sub-version yet.
+        debugs(83, 5, "Using TLSv1.2");
+        method = TLSv1_2_server_method();
+#else
+        debugs(83, DBG_IMPORTANT, "TLSv1.2 is not available in this Proxy.");
+        return NULL;
+#endif
+        break;
+
     case 1:
 
     default:
@@ -707,8 +786,8 @@ sslCreateServerContext(const char *certfile, const char *keyfile, int version, c
 
     if (sslContext == NULL) {
         ssl_error = ERR_get_error();
-        fatalf("Failed to allocate SSL context: %s\n",
-               ERR_error_string(ssl_error, NULL));
+        debugs(83, DBG_CRITICAL, "ERROR: Failed to allocate SSL context: " << ERR_error_string(ssl_error, NULL));
+        return NULL;
     }
 
     SSL_CTX_set_options(sslContext, ssl_parse_options(options));
@@ -732,36 +811,39 @@ sslCreateServerContext(const char *certfile, const char *keyfile, int version, c
 
         if (!SSL_CTX_set_cipher_list(sslContext, cipher)) {
             ssl_error = ERR_get_error();
-            fatalf("Failed to set SSL cipher suite '%s': %s\n",
-                   cipher, ERR_error_string(ssl_error, NULL));
+            debugs(83, DBG_CRITICAL, "ERROR: Failed to set SSL cipher suite '" << cipher << "': " << ERR_error_string(ssl_error, NULL));
+            SSL_CTX_free(sslContext);
+            return NULL;
         }
     }
 
-    debugs(83, 1, "Using certificate in " << certfile);
+    debugs(83, DBG_IMPORTANT, "Using certificate in " << certfile);
 
     if (!SSL_CTX_use_certificate_chain_file(sslContext, certfile)) {
         ssl_error = ERR_get_error();
-        debugs(83, 0, "Failed to acquire SSL certificate '" << certfile << "': " << ERR_error_string(ssl_error, NULL)  );
-        goto error;
+        debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire SSL certificate '" << certfile << "': " << ERR_error_string(ssl_error, NULL));
+        SSL_CTX_free(sslContext);
+        return NULL;
     }
 
-    debugs(83, 1, "Using private key in " << keyfile);
+    debugs(83, DBG_IMPORTANT, "Using private key in " << keyfile);
     ssl_ask_password(sslContext, keyfile);
 
     if (!SSL_CTX_use_PrivateKey_file(sslContext, keyfile, SSL_FILETYPE_PEM)) {
         ssl_error = ERR_get_error();
-        debugs(83, 0, "Failed to acquire SSL private key '" << keyfile << "': " << ERR_error_string(ssl_error, NULL)  );
-        goto error;
+        debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire SSL private key '" << keyfile << "': " << ERR_error_string(ssl_error, NULL));
+        SSL_CTX_free(sslContext);
+        return NULL;
     }
 
     debugs(83, 5, "Comparing private and public SSL keys.");
 
     if (!SSL_CTX_check_private_key(sslContext)) {
         ssl_error = ERR_get_error();
-        debugs(83, 0, "SSL private key '" <<
-               certfile << "' does not match public key '" <<
-               keyfile << "': " << ERR_error_string(ssl_error, NULL)  );
-        goto error;
+        debugs(83, DBG_CRITICAL, "ERROR: SSL private key '" << certfile << "' does not match public key '" <<
+               keyfile << "': " << ERR_error_string(ssl_error, NULL));
+        SSL_CTX_free(sslContext);
+        return NULL;
     }
 
     debugs(83, 9, "Setting RSA key generation callback.");
@@ -771,15 +853,13 @@ sslCreateServerContext(const char *certfile, const char *keyfile, int version, c
 
     if ((CAfile || CApath) && !SSL_CTX_load_verify_locations(sslContext, CAfile, CApath)) {
         ssl_error = ERR_get_error();
-        debugs(83, 1, "Error setting CA certificate locations: " << ERR_error_string(ssl_error, NULL)  );
-        debugs(83, 1, "continuing anyway..." );
+        debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate locations: " << ERR_error_string(ssl_error, NULL));
     }
 
     if (!(fl & SSL_FLAG_NO_DEFAULT_CA) &&
             !SSL_CTX_set_default_verify_paths(sslContext)) {
         ssl_error = ERR_get_error();
-        debugs(83, 1, "Error setting default CA certificate location: " << ERR_error_string(ssl_error, NULL)  );
-        debugs(83, 1, "continuing anyway..." );
+        debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting default CA certificate location: " << ERR_error_string(ssl_error, NULL));
     }
 
     if (clientCA) {
@@ -788,8 +868,9 @@ sslCreateServerContext(const char *certfile, const char *keyfile, int version, c
         cert_names = SSL_load_client_CA_file(clientCA);
 
         if (cert_names == NULL) {
-            debugs(83, 1, "Error loading the client CA certificates from '" << clientCA << "\': " << ERR_error_string(ERR_get_error(),NULL)  );
-            goto error;
+            debugs(83, DBG_IMPORTANT, "ERROR: loading the client CA certificates from '" << clientCA << "\': " << ERR_error_string(ERR_get_error(),NULL));
+            SSL_CTX_free(sslContext);
+            return NULL;
         }
 
         ERR_clear_error();
@@ -832,10 +913,10 @@ sslCreateServerContext(const char *certfile, const char *keyfile, int version, c
         }
 
         if (!dh)
-            debugs(83, 1, "WARNING: Failed to read DH parameters '" << dhfile << "'");
+            debugs(83, DBG_IMPORTANT, "WARNING: Failed to read DH parameters '" << dhfile << "'");
         else if (dh && DH_check(dh, &codes) == 0) {
             if (codes) {
-                debugs(83, 1, "WARNING: Failed to verify DH parameters '" << dhfile  << "' (" << std::hex << codes  << ")");
+                debugs(83, DBG_IMPORTANT, "WARNING: Failed to verify DH parameters '" << dhfile  << "' (" << std::hex << codes  << ")");
                 DH_free(dh);
                 dh = NULL;
             }
@@ -849,11 +930,6 @@ sslCreateServerContext(const char *certfile, const char *keyfile, int version, c
         SSL_CTX_set_ex_data(sslContext, ssl_ctx_ex_index_dont_verify_domain, (void *) -1);
 
     return sslContext;
-
-error:
-    SSL_CTX_free(sslContext);
-
-    return NULL;
 }
 
 SSL_CTX *
@@ -879,8 +955,13 @@ sslCreateClientContext(const char *certfile, const char *keyfile, int version, c
     switch (version) {
 
     case 2:
+#ifndef OPENSSL_NO_SSL2
         debugs(83, 5, "Using SSLv2.");
         method = SSLv2_client_method();
+#else
+        debugs(83, DBG_IMPORTANT, "SSLv2 is not available in this Proxy.");
+        return NULL;
+#endif
         break;
 
     case 3:
@@ -893,6 +974,26 @@ sslCreateClientContext(const char *certfile, const char *keyfile, int version, c
         method = TLSv1_client_method();
         break;
 
+    case 5:
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L  // NP: not sure exactly which sub-version yet.
+        debugs(83, 5, "Using TLSv1.1.");
+        method = TLSv1_1_client_method();
+#else
+        debugs(83, DBG_IMPORTANT, "TLSv1.1 is not available in this Proxy.");
+        return NULL;
+#endif
+        break;
+
+    case 6:
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L // NP: not sure exactly which sub-version yet.
+        debugs(83, 5, "Using TLSv1.2");
+        method = TLSv1_2_client_method();
+#else
+        debugs(83, DBG_IMPORTANT, "TLSv1.2 is not available in this Proxy.");
+        return NULL;
+#endif
+        break;
+
     case 1:
 
     default:
@@ -922,7 +1023,7 @@ sslCreateClientContext(const char *certfile, const char *keyfile, int version, c
     }
 
     if (certfile) {
-        debugs(83, 1, "Using certificate in " << certfile);
+        debugs(83, DBG_IMPORTANT, "Using certificate in " << certfile);
 
         if (!SSL_CTX_use_certificate_chain_file(sslContext, certfile)) {
             ssl_error = ERR_get_error();
@@ -930,7 +1031,7 @@ sslCreateClientContext(const char *certfile, const char *keyfile, int version, c
                    certfile, ERR_error_string(ssl_error, NULL));
         }
 
-        debugs(83, 1, "Using private key in " << keyfile);
+        debugs(83, DBG_IMPORTANT, "Using private key in " << keyfile);
         ssl_ask_password(sslContext, keyfile);
 
         if (!SSL_CTX_use_PrivateKey_file(sslContext, keyfile, SSL_FILETYPE_PEM)) {
@@ -952,7 +1053,7 @@ sslCreateClientContext(const char *certfile, const char *keyfile, int version, c
     SSL_CTX_set_tmp_rsa_callback(sslContext, ssl_temp_rsa_cb);
 
     if (fl & SSL_FLAG_DONT_VERIFY_PEER) {
-        debugs(83, 1, "NOTICE: Peer certificates are not verified for validity!");
+        debugs(83, 2, "NOTICE: Peer certificates are not verified for validity!");
         SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, NULL);
     } else {
         debugs(83, 9, "Setting certificate verification callback.");
@@ -963,8 +1064,7 @@ sslCreateClientContext(const char *certfile, const char *keyfile, int version, c
 
     if ((CAfile || CApath) && !SSL_CTX_load_verify_locations(sslContext, CAfile, CApath)) {
         ssl_error = ERR_get_error();
-        debugs(83, 1, "Error setting CA certificate locations: " << ERR_error_string(ssl_error, NULL));
-        debugs(83, 1, "continuing anyway..." );
+        debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate locations: " << ERR_error_string(ssl_error, NULL));
     }
 
     if (CRLfile) {
@@ -983,8 +1083,7 @@ sslCreateClientContext(const char *certfile, const char *keyfile, int version, c
     if (!(fl & SSL_FLAG_NO_DEFAULT_CA) &&
             !SSL_CTX_set_default_verify_paths(sslContext)) {
         ssl_error = ERR_get_error();
-        debugs(83, 1, "Error setting default CA certificate location: " << ERR_error_string(ssl_error, NULL)  );
-        debugs(83, 1, "continuing anyway...");
+        debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting default CA certificate location: " << ERR_error_string(ssl_error, NULL));
     }
 
     return sslContext;
@@ -1035,10 +1134,8 @@ ssl_write_method(int fd, const char *buf, int len)
 }
 
 void
-ssl_shutdown_method(int fd)
+ssl_shutdown_method(SSL *ssl)
 {
-    SSL *ssl = fd_table[fd].ssl;
-
     SSL_shutdown(ssl);
 }
 
@@ -1059,7 +1156,7 @@ ssl_get_attribute(X509_NAME * name, const char *attribute_name)
     nid = OBJ_txt2nid((char *) attribute_name);
 
     if (nid == 0) {
-        debugs(83, 1, "WARNING: Unknown SSL attribute name '" << attribute_name << "'");
+        debugs(83, DBG_IMPORTANT, "WARNING: Unknown SSL attribute name '" << attribute_name << "'");
         return NULL;
     }
 
@@ -1148,7 +1245,6 @@ sslGetUserCertificatePEM(SSL *ssl)
 
     PEM_write_bio_X509(mem, cert);
 
-
     len = BIO_get_mem_data(mem, &ptr);
 
     str = (char *)xmalloc(len + 1);
@@ -1186,7 +1282,7 @@ sslGetUserCertificateChainPEM(SSL *ssl)
 
     mem = BIO_new(BIO_s_mem());
 
-    for (i = 0; i < sk_X509_num(chain); i++) {
+    for (i = 0; i < sk_X509_num(chain); ++i) {
         X509 *cert = sk_X509_value(chain, i);
         PEM_write_bio_X509(mem, cert);
     }
@@ -1229,13 +1325,13 @@ SSL_CTX * Ssl::generateSslContextUsingPkeyAndCertFromMemory(const char * data)
     return createSSLContext(cert, pkey);
 }
 
-SSL_CTX * Ssl::generateSslContext(char const *host, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey)
+SSL_CTX * Ssl::generateSslContext(CertificateProperties const &properties)
 {
     Ssl::X509_Pointer cert;
     Ssl::EVP_PKEY_Pointer pkey;
-    if (!generateSslCertificateAndPrivateKey(host, signedX509, signedPkey, cert, pkey, NULL)) {
+    if (!generateSslCertificate(cert, pkey, properties))
         return NULL;
-    }
+
     if (!cert)
         return NULL;
 
@@ -1245,7 +1341,7 @@ SSL_CTX * Ssl::generateSslContext(char const *host, Ssl::X509_Pointer const & si
     return createSSLContext(cert, pkey);
 }
 
-bool Ssl::verifySslCertificateDate(SSL_CTX * sslContext)
+bool Ssl::verifySslCertificate(SSL_CTX * sslContext, CertificateProperties const &properties)
 {
     // Temporary ssl for getting X509 certificate from SSL_CTX.
     Ssl::SSL_Pointer ssl(SSL_new(sslContext));
@@ -1253,7 +1349,121 @@ bool Ssl::verifySslCertificateDate(SSL_CTX * sslContext)
     ASN1_TIME * time_notBefore = X509_get_notBefore(cert);
     ASN1_TIME * time_notAfter = X509_get_notAfter(cert);
     bool ret = (X509_cmp_current_time(time_notBefore) < 0 && X509_cmp_current_time(time_notAfter) > 0);
-    return ret;
+    if (!ret)
+        return false;
+
+    return certificateMatchesProperties(cert, properties);
+}
+
+bool
+Ssl::setClientSNI(SSL *ssl, const char *fqdn)
+{
+    //The SSL_CTRL_SET_TLSEXT_HOSTNAME is a openssl macro which indicates
+    // if the TLS servername extension (SNI) is enabled in openssl library.
+#if defined(SSL_CTRL_SET_TLSEXT_HOSTNAME)
+    if (!SSL_set_tlsext_host_name(ssl, fqdn)) {
+        const int ssl_error = ERR_get_error();
+        debugs(83, 3,  "WARNING: unable to set TLS servername extension (SNI): " <<
+               ERR_error_string(ssl_error, NULL) << "\n");
+        return false;
+    }
+    return true;
+#else
+    debugs(83, 7,  "no support for TLS servername extension (SNI)\n");
+    return false;
+#endif
+}
+
+void Ssl::addChainToSslContext(SSL_CTX *sslContext, STACK_OF(X509) *chain)
+{
+    if (!chain)
+        return;
+
+    for (int i = 0; i < sk_X509_num(chain); ++i) {
+        X509 *cert = sk_X509_value(chain, i);
+        if (SSL_CTX_add_extra_chain_cert(sslContext, cert)) {
+            // increase the certificate lock
+            CRYPTO_add(&(cert->references),1,CRYPTO_LOCK_X509);
+        } else {
+            const int ssl_error = ERR_get_error();
+            debugs(83, DBG_IMPORTANT, "WARNING: can not add certificate to SSL context chain: " << ERR_error_string(ssl_error, NULL));
+        }
+    }
+}
+
+/**
+ \ingroup ServerProtocolSSLInternal
+ * Read certificate from file.
+ * See also: static readSslX509Certificate function, gadgets.cc file
+ */
+static X509 * readSslX509CertificatesChain(char const * certFilename,  STACK_OF(X509)* chain)
+{
+    if (!certFilename)
+        return NULL;
+    Ssl::BIO_Pointer bio(BIO_new(BIO_s_file_internal()));
+    if (!bio)
+        return NULL;
+    if (!BIO_read_filename(bio.get(), certFilename))
+        return NULL;
+    X509 *certificate = PEM_read_bio_X509(bio.get(), NULL, NULL, NULL);
+
+    if (certificate && chain) {
+
+        if (X509_check_issued(certificate, certificate) == X509_V_OK)
+            debugs(83, 5, "Certificate is self-signed, will not be chained");
+        else {
+            if (sk_X509_push(chain, certificate))
+                CRYPTO_add(&(certificate->references), 1, CRYPTO_LOCK_X509);
+            else
+                debugs(83, DBG_IMPORTANT, "WARNING: unable to add signing certificate to cert chain");
+            // and add to the chain any certificate loaded from the file
+            while (X509 *ca = PEM_read_bio_X509(bio.get(), NULL, NULL, NULL)) {
+                if (!sk_X509_push(chain, ca))
+                    debugs(83, DBG_IMPORTANT, "WARNING: unable to add CA certificate to cert chain");
+            }
+        }
+    }
+
+    return certificate;
+}
+
+void Ssl::readCertChainAndPrivateKeyFromFiles(X509_Pointer & cert, EVP_PKEY_Pointer & pkey, X509_STACK_Pointer & chain, char const * certFilename, char const * keyFilename)
+{
+    if (keyFilename == NULL)
+        keyFilename = certFilename;
+    if (!chain)
+        chain.reset(sk_X509_new_null());
+    if (!chain)
+        debugs(83, DBG_IMPORTANT, "WARNING: unable to allocate memory for cert chain");
+    pkey.reset(readSslPrivateKey(keyFilename, ssl_ask_password_cb));
+    cert.reset(readSslX509CertificatesChain(certFilename, chain.get()));
+    if (!pkey || !cert || !X509_check_private_key(cert.get(), pkey.get())) {
+        pkey.reset(NULL);
+        cert.reset(NULL);
+    }
+}
+
+bool Ssl::generateUntrustedCert(X509_Pointer &untrustedCert, EVP_PKEY_Pointer &untrustedPkey, X509_Pointer const  &cert, EVP_PKEY_Pointer const & pkey)
+{
+    // Generate the self-signed certificate, using a hard-coded subject prefix
+    Ssl::CertificateProperties certProperties;
+    if (const char *cn = CommonHostName(cert.get())) {
+        certProperties.commonName = "Not trusted by \"";
+        certProperties.commonName += cn;
+        certProperties.commonName += "\"";
+    } else if (const char *org = getOrganization(cert.get())) {
+        certProperties.commonName =  "Not trusted by \"";
+        certProperties.commonName += org;
+        certProperties.commonName += "\"";
+    } else
+        certProperties.commonName =  "Not trusted";
+    certProperties.setCommonName = true;
+    // O, OU, and other CA subject fields will be mimicked
+    // Expiration date and other common properties will be mimicked
+    certProperties.signAlgorithm = Ssl::algSignSelf;
+    certProperties.signWithPkey.resetAndLock(pkey.get());
+    certProperties.mimicCert.resetAndLock(cert.get());
+    return Ssl::generateSslCertificate(untrustedCert, untrustedPkey, certProperties);
 }
 
 #endif /* USE_SSL */