]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/ssl/support.cc
Merged from trunk
[thirdparty/squid.git] / src / ssl / support.cc
index 8d01d9a5aa6bbeb6d88f0f25916c8e409110f759..aaaa63a5c99f4ad21980943176340c96fb7cc6d3 100644 (file)
@@ -39,6 +39,7 @@
 #if USE_SSL
 
 #include "acl/FilledChecklist.h"
+#include "anyp/PortCfg.h"
 #include "fde.h"
 #include "globals.h"
 #include "ssl/ErrorDetail.h"
@@ -448,8 +449,8 @@ ssl_options[] = {
 };
 
 /// \ingroup ServerProtocolSSLInternal
-static long
-ssl_parse_options(const char *options)
+long
+Ssl::parse_options(const char *options)
 {
     long op = 0;
     char *tmp;
@@ -542,8 +543,8 @@ no_options:
 #define SSL_FLAG_VERIFY_CRL_ALL                (1<<6)
 
 /// \ingroup ServerProtocolSSLInternal
-static long
-ssl_parse_flags(const char *flags)
+long
+Ssl::parse_flags(const char *flags)
 {
     long fl = 0;
     char *tmp;
@@ -702,99 +703,71 @@ ssl_load_crl(SSL_CTX *sslContext, const char *CRLfile)
     return count;
 }
 
-SSL_CTX *
-sslCreateServerContext(const char *certfile, const char *keyfile, int version, const char *cipher, const char *options, const char *flags, const char *clientCA, const char *CAfile, const char *CApath, const char *CRLfile, const char *dhfile, const char *context)
+STACK_OF(X509_CRL) *
+Ssl::loadCrl(const char *CRLFile, long &flags)
 {
-    int ssl_error;
-#if OPENSSL_VERSION_NUMBER < 0x00909000L
-    SSL_METHOD *method;
-#else
-    const SSL_METHOD *method;
-#endif
-    SSL_CTX *sslContext;
-    long fl = ssl_parse_flags(flags);
-
-    ssl_initialize();
-
-    if (!keyfile)
-        keyfile = certfile;
-
-    if (!certfile)
-        certfile = keyfile;
-
-    if (!CAfile)
-        CAfile = clientCA;
-
-    if (!certfile) {
-        debugs(83, DBG_CRITICAL, "ERROR: No certificate file");
+    X509_CRL *crl;
+    BIO *in = BIO_new_file(CRLFile, "r");
+    if (!in) {
+        debugs(83, 2, "WARNING: Failed to open CRL file '" << CRLFile << "'");
         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.");
+    STACK_OF(X509_CRL) *CRLs = sk_X509_CRL_new_null();
+    if (!CRLs) {
+        debugs(83, 2, "WARNING: Failed to allocate X509_CRL stack  to load file '" << CRLFile << "'");
         return NULL;
-#endif
-        break;
-
-    case 3:
-        debugs(83, 5, "Using SSLv3.");
-        method = SSLv3_server_method();
-        break;
+    }
 
-    case 4:
-        debugs(83, 5, "Using TLSv1.");
-        method = TLSv1_server_method();
-        break;
+    int count = 0;
+    while ((crl = PEM_read_bio_X509_CRL(in,NULL,NULL,NULL))) {
+        if (!sk_X509_CRL_push(CRLs, crl))
+            debugs(83, 2, "WARNING: Failed to add CRL from file '" << CRLFile << "'");
+        else
+            ++count;
+    }
+    BIO_free(in);
 
-    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;
+    if (count)
+        flags |= SSL_FLAG_VERIFY_CRL;
 
-    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;
+    return CRLs;
+}
 
-    case 1:
+DH *
+Ssl::readDHParams(const char *dhfile)
+{
+    FILE *in = fopen(dhfile, "r");
+    DH *dh = NULL;
+    int codes;
 
-    default:
-        debugs(83, 5, "Using SSLv2/SSLv3.");
-        method = SSLv23_server_method();
-        break;
+    if (in) {
+        dh = PEM_read_DHparams(in, NULL, NULL, NULL);
+        fclose(in);
     }
 
-    sslContext = SSL_CTX_new(method);
-
-    if (sslContext == NULL) {
-        ssl_error = ERR_get_error();
-        debugs(83, DBG_CRITICAL, "ERROR: Failed to allocate SSL context: " << ERR_error_string(ssl_error, NULL));
-        return NULL;
+    if (!dh)
+        debugs(83, DBG_IMPORTANT, "WARNING: Failed to read DH parameters '" << dhfile << "'");
+    else if (dh && DH_check(dh, &codes) == 0) {
+        if (codes) {
+            debugs(83, DBG_IMPORTANT, "WARNING: Failed to verify DH parameters '" << dhfile  << "' (" << std::hex << codes  << ")");
+            DH_free(dh);
+            dh = NULL;
+        }
     }
+    return dh;
+}
 
-    SSL_CTX_set_options(sslContext, ssl_parse_options(options));
+static bool
+configureSslContext(SSL_CTX *sslContext, AnyP::PortCfg &port)
+{
+    int ssl_error;
+    SSL_CTX_set_options(sslContext, port.sslOptions);
 
-    if (context && *context) {
-        SSL_CTX_set_session_id_context(sslContext, (const unsigned char *)context, strlen(context));
-    }
+    if (port.sslContextSessionId)
+        SSL_CTX_set_session_id_context(sslContext, (const unsigned char *)port.sslContextSessionId, strlen(port.sslContextSessionId));
 
-    if (fl & SSL_FLAG_NO_SESSION_REUSE) {
+    if (port.sslContextFlags & SSL_FLAG_NO_SESSION_REUSE) {
         SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_OFF);
     }
 
@@ -804,77 +777,38 @@ sslCreateServerContext(const char *certfile, const char *keyfile, int version, c
         SSL_CTX_set_quiet_shutdown(sslContext, 1);
     }
 
-    if (cipher) {
-        debugs(83, 5, "Using chiper suite " << cipher << ".");
+    if (port.cipher) {
+        debugs(83, 5, "Using chiper suite " << port.cipher << ".");
 
-        if (!SSL_CTX_set_cipher_list(sslContext, cipher)) {
+        if (!SSL_CTX_set_cipher_list(sslContext, port.cipher)) {
             ssl_error = ERR_get_error();
-            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, DBG_CRITICAL, "ERROR: Failed to set SSL cipher suite '" << port.cipher << "': " << ERR_error_string(ssl_error, NULL));
+            return false;
         }
     }
 
-    debugs(83, DBG_IMPORTANT, "Using certificate in " << certfile);
-
-    if (!SSL_CTX_use_certificate_chain_file(sslContext, certfile)) {
-        ssl_error = ERR_get_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, 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, 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, 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.");
     SSL_CTX_set_tmp_rsa_callback(sslContext, ssl_temp_rsa_cb);
 
     debugs(83, 9, "Setting CA certificate locations.");
 
-    if ((CAfile || CApath) && !SSL_CTX_load_verify_locations(sslContext, CAfile, CApath)) {
+    const char *cafile = port.cafile ? port.cafile : port.clientca;
+    if ((cafile || port.capath) && !SSL_CTX_load_verify_locations(sslContext, cafile, port.capath)) {
         ssl_error = ERR_get_error();
         debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate locations: " << ERR_error_string(ssl_error, NULL));
     }
 
-    if (!(fl & SSL_FLAG_NO_DEFAULT_CA) &&
+    if (!(port.sslContextFlags & SSL_FLAG_NO_DEFAULT_CA) &&
             !SSL_CTX_set_default_verify_paths(sslContext)) {
         ssl_error = ERR_get_error();
         debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting default CA certificate location: " << ERR_error_string(ssl_error, NULL));
     }
 
-    if (clientCA) {
-        STACK_OF(X509_NAME) *cert_names;
-        debugs(83, 9, "Set client certifying authority list.");
-        cert_names = SSL_load_client_CA_file(clientCA);
-
-        if (cert_names == NULL) {
-            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;
-        }
-
+    if (port.clientCA.get()) {
         ERR_clear_error();
-        SSL_CTX_set_client_CA_list(sslContext, cert_names);
+        SSL_CTX_set_client_CA_list(sslContext, port.clientCA.get());
 
-        if (fl & SSL_FLAG_DELAYED_AUTH) {
+        if (port.sslContextFlags & SSL_FLAG_DELAYED_AUTH) {
             debugs(83, 9, "Not requesting client certificates until acl processing requires one");
             SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, NULL);
         } else {
@@ -882,17 +816,20 @@ sslCreateServerContext(const char *certfile, const char *keyfile, int version, c
             SSL_CTX_set_verify(sslContext, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, ssl_verify_cb);
         }
 
-        if (CRLfile) {
-            ssl_load_crl(sslContext, CRLfile);
-            fl |= SSL_FLAG_VERIFY_CRL;
+        if (port.clientVerifyCrls.get()) {
+            X509_STORE *st = SSL_CTX_get_cert_store(sslContext);
+            for (int i = 0; i < sk_X509_CRL_num(port.clientVerifyCrls.get()); ++i) {
+                X509_CRL *crl = sk_X509_CRL_value(port.clientVerifyCrls.get(), i);
+                if (!X509_STORE_add_crl(st, crl))
+                    debugs(83, 2, "WARNING: Failed to add CRL");
+            }
         }
 
 #if X509_V_FLAG_CRL_CHECK
-        if (fl & SSL_FLAG_VERIFY_CRL_ALL)
+        if (port.sslContextFlags & SSL_FLAG_VERIFY_CRL_ALL)
             X509_STORE_set_flags(SSL_CTX_get_cert_store(sslContext), X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
-        else if (fl & SSL_FLAG_VERIFY_CRL)
+        else if (port.sslContextFlags & SSL_FLAG_VERIFY_CRL)
             X509_STORE_set_flags(SSL_CTX_get_cert_store(sslContext), X509_V_FLAG_CRL_CHECK);
-
 #endif
 
     } else {
@@ -900,32 +837,93 @@ sslCreateServerContext(const char *certfile, const char *keyfile, int version, c
         SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, NULL);
     }
 
-    if (dhfile) {
-        FILE *in = fopen(dhfile, "r");
-        DH *dh = NULL;
-        int codes;
+    if (port.dhParams.get()) {
+        SSL_CTX_set_tmp_dh(sslContext, port.dhParams.get());
+    }
 
-        if (in) {
-            dh = PEM_read_DHparams(in, NULL, NULL, NULL);
-            fclose(in);
+    if (port.sslContextFlags & SSL_FLAG_DONT_VERIFY_DOMAIN)
+        SSL_CTX_set_ex_data(sslContext, ssl_ctx_ex_index_dont_verify_domain, (void *) -1);
+
+    return true;
+}
+
+SSL_CTX *
+sslCreateServerContext(AnyP::PortCfg &port)
+{
+    int ssl_error;
+    SSL_CTX *sslContext;
+    const char *keyfile, *certfile;
+    certfile = port.cert;
+    keyfile = port.key;
+
+    ssl_initialize();
+
+    if (!keyfile)
+        keyfile = certfile;
+
+    if (!certfile)
+        certfile = keyfile;
+
+    sslContext = SSL_CTX_new(port.contextMethod);
+
+    if (sslContext == NULL) {
+        ssl_error = ERR_get_error();
+        debugs(83, DBG_CRITICAL, "ERROR: Failed to allocate SSL context: " << ERR_error_string(ssl_error, NULL));
+        return NULL;
+    }
+
+    if (!SSL_CTX_use_certificate(sslContext, port.signingCert.get())) {
+        ssl_error = ERR_get_error();
+        debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire SSL certificate '" << certfile << "': " << ERR_error_string(ssl_error, NULL));
+        SSL_CTX_free(sslContext);
+        return NULL;
+    }
+
+    if (!SSL_CTX_use_PrivateKey(sslContext, port.signPkey.get())) {
+        ssl_error = ERR_get_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;
+    }
+
+    Ssl::addChainToSslContext(sslContext, port.certsToChain.get());
+
+    /* Alternate code;
+        debugs(83, DBG_IMPORTANT, "Using certificate in " << certfile);
+
+        if (!SSL_CTX_use_certificate_chain_file(sslContext, certfile)) {
+            ssl_error = ERR_get_error();
+            debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire SSL certificate '" << certfile << "': " << ERR_error_string(ssl_error, NULL));
+            SSL_CTX_free(sslContext);
+            return NULL;
         }
 
-        if (!dh)
-            debugs(83, DBG_IMPORTANT, "WARNING: Failed to read DH parameters '" << dhfile << "'");
-        else if (dh && DH_check(dh, &codes) == 0) {
-            if (codes) {
-                debugs(83, DBG_IMPORTANT, "WARNING: Failed to verify DH parameters '" << dhfile  << "' (" << std::hex << codes  << ")");
-                DH_free(dh);
-                dh = NULL;
-            }
+        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, DBG_CRITICAL, "ERROR: Failed to acquire SSL private key '" << keyfile << "': " << ERR_error_string(ssl_error, NULL));
+            SSL_CTX_free(sslContext);
+            return NULL;
         }
 
-        if (dh)
-            SSL_CTX_set_tmp_dh(sslContext, dh);
-    }
+        debugs(83, 5, "Comparing private and public SSL keys.");
 
-    if (fl & SSL_FLAG_DONT_VERIFY_DOMAIN)
-        SSL_CTX_set_ex_data(sslContext, ssl_ctx_ex_index_dont_verify_domain, (void *) -1);
+        if (!SSL_CTX_check_private_key(sslContext)) {
+            ssl_error = ERR_get_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;
+        }
+    */
+
+    if (!configureSslContext(sslContext, port)) {
+        debugs(83, DBG_CRITICAL, "ERROR: Configuring static SSL context");
+        SSL_CTX_free(sslContext);
+        return NULL;
+    }
 
     return sslContext;
 }
@@ -940,7 +938,7 @@ sslCreateClientContext(const char *certfile, const char *keyfile, int version, c
     const SSL_METHOD *method;
 #endif
     SSL_CTX *sslContext;
-    long fl = ssl_parse_flags(flags);
+    long fl = Ssl::parse_flags(flags);
 
     ssl_initialize();
 
@@ -1008,7 +1006,7 @@ sslCreateClientContext(const char *certfile, const char *keyfile, int version, c
                ERR_error_string(ssl_error, NULL));
     }
 
-    SSL_CTX_set_options(sslContext, ssl_parse_options(options));
+    SSL_CTX_set_options(sslContext, Ssl::parse_options(options));
 
     if (cipher) {
         debugs(83, 5, "Using chiper suite " << cipher << ".");
@@ -1296,21 +1294,84 @@ sslGetUserCertificateChainPEM(SSL *ssl)
     return str;
 }
 
+Ssl::ContextMethod
+Ssl::contextMethod(int version)
+{
+    Ssl::ContextMethod method;
+
+    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:
+        debugs(83, 5, "Using SSLv3.");
+        method = SSLv3_server_method();
+        break;
+
+    case 4:
+        debugs(83, 5, "Using TLSv1.");
+        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:
+        debugs(83, 5, "Using SSLv2/SSLv3.");
+        method = SSLv23_server_method();
+        break;
+    }
+    return method;
+}
+
 /// \ingroup ServerProtocolSSLInternal
 /// Create SSL context and apply ssl certificate and private key to it.
-static SSL_CTX * createSSLContext(Ssl::X509_Pointer & x509, Ssl::EVP_PKEY_Pointer & pkey)
+static SSL_CTX *
+createSSLContext(Ssl::X509_Pointer & x509, Ssl::EVP_PKEY_Pointer & pkey, AnyP::PortCfg &port)
 {
-    Ssl::SSL_CTX_Pointer sslContext(SSL_CTX_new(SSLv23_server_method()));
+    Ssl::SSL_CTX_Pointer sslContext(SSL_CTX_new(port.contextMethod));
 
     if (!SSL_CTX_use_certificate(sslContext.get(), x509.get()))
         return NULL;
 
     if (!SSL_CTX_use_PrivateKey(sslContext.get(), pkey.get()))
         return NULL;
+
+    if (!configureSslContext(sslContext.get(), port))
+        return NULL;
+
     return sslContext.release();
 }
 
-SSL_CTX * Ssl::generateSslContextUsingPkeyAndCertFromMemory(const char * data)
+SSL_CTX *
+Ssl::generateSslContextUsingPkeyAndCertFromMemory(const char * data, AnyP::PortCfg &port)
 {
     Ssl::X509_Pointer cert;
     Ssl::EVP_PKEY_Pointer pkey;
@@ -1320,10 +1381,11 @@ SSL_CTX * Ssl::generateSslContextUsingPkeyAndCertFromMemory(const char * data)
     if (!cert || !pkey)
         return NULL;
 
-    return createSSLContext(cert, pkey);
+    return createSSLContext(cert, pkey, port);
 }
 
-SSL_CTX * Ssl::generateSslContext(CertificateProperties const &properties)
+SSL_CTX *
+Ssl::generateSslContext(CertificateProperties const &properties, AnyP::PortCfg &port)
 {
     Ssl::X509_Pointer cert;
     Ssl::EVP_PKEY_Pointer pkey;
@@ -1336,7 +1398,7 @@ SSL_CTX * Ssl::generateSslContext(CertificateProperties const &properties)
     if (!pkey)
         return NULL;
 
-    return createSSLContext(cert, pkey);
+    return createSSLContext(cert, pkey, port);
 }
 
 bool Ssl::verifySslCertificate(SSL_CTX * sslContext, CertificateProperties const &properties)
@@ -1429,6 +1491,12 @@ void Ssl::readCertChainAndPrivateKeyFromFiles(X509_Pointer & cert, EVP_PKEY_Poin
 {
     if (keyFilename == NULL)
         keyFilename = certFilename;
+
+    if (certFilename == NULL)
+        certFilename = keyFilename;
+
+    debugs(83, DBG_IMPORTANT, "Using certificate in " << certFilename);
+
     if (!chain)
         chain.reset(sk_X509_new_null());
     if (!chain)