]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
TLS: GnuTLS implementation for listening ports and client connections (#81)
authorAmos Jeffries <yadij@users.noreply.github.com>
Thu, 1 Feb 2018 09:51:54 +0000 (22:51 +1300)
committerGitHub <noreply@github.com>
Thu, 1 Feb 2018 09:51:54 +0000 (22:51 +1300)
Move the http_port cert= and key= options logic to libsecurity and add GnuTLS implementation for PEM file loading. Also adds some extra debugging to clarify listening port initialization problems with the PEM files.

Enable most of the http(s)_port listening socket logic to always build except where OpenSSL-specific dependency still exists. It may seem reasonable to leave it optionally excluded for minimal builds, however a minimal proxy that does not support HTTPS in any way is increasingly useless in the modern web so preference is given to building the generic TLS related code. This also simplifies the required testing to detect code portability issues.

GnuTLS implementation is added for https_port configured with static cert=/key= parameters and the resulting TLS handshake behaviour. Squid built with GnuTLS can now act as useful parent proxies behind a SSL-Bump'ing frontend or for other clients which require a TLS explicit proxy.

Also fixes the definitions for the CertPointer and PrivateKeyPointer.

18 files changed:
configure.ac
src/cache_cf.cc
src/cf.data.pre
src/client_side.cc
src/client_side.h
src/security/KeyData.cc [new file with mode: 0644]
src/security/KeyData.h
src/security/Makefile.am
src/security/PeerOptions.h
src/security/ServerOptions.cc
src/security/ServerOptions.h
src/security/forward.h
src/servers/Http1Server.cc
src/ssl/bio.h
src/ssl/support.cc
src/ssl/support.h
src/tests/stub_libsecurity.cc
src/tests/stub_libsslsquid.cc

index bef2087da9442de45baa1b554a4d4d3697c9d037..b45f33c763c73fba9aa649234a069ff75bce9e37 100644 (file)
@@ -1253,7 +1253,7 @@ if test "x$with_gnutls" != "xno"; then
     ## by testing for a 3.1.5+ function which we use
     AC_CHECK_LIB(gnutls,gnutls_certificate_verify_peers3,[LIBGNUTLS_LIBS="-lgnutls"])
   ])
-  AC_CHECK_HEADERS(gnutls/gnutls.h gnutls/x509.h)
+  AC_CHECK_HEADERS(gnutls/gnutls.h gnutls/x509.h gnutls/abstract.h)
 
   SQUID_STATE_ROLLBACK(squid_gnutls_state) #de-pollute LIBS
 
index 319b81c8a6891334afa718e6a225bd4fad9ce072..5e17f04441d9133353c1d910dff412d24491c454 100644 (file)
@@ -915,8 +915,8 @@ configDoConfigure(void)
     for (AnyP::PortCfgPointer s = HttpPortList; s != NULL; s = s->next) {
         if (!s->secure.encryptTransport)
             continue;
-        debugs(3, DBG_IMPORTANT, "Initializing " << AnyP::UriScheme(s->transport.protocol) << "_port " << s->s << " TLS context");
-        s->secure.createSigningContexts(*s);
+        debugs(3, DBG_IMPORTANT, "Initializing " << AnyP::UriScheme(s->transport.protocol) << "_port " << s->s << " TLS contexts");
+        s->secure.initServerContexts(*s);
     }
 
     // prevent infinite fetch loops in the request parser
index fdecc02e7b886881bb66f987192bbe0c99f6e67a..4f8a21837763de465f13f8eba227e40188333895 100644 (file)
@@ -2223,12 +2223,33 @@ DOC_START
 
        TLS / SSL Options:
 
-          cert=        Path to SSL certificate (PEM format).
+          tls-cert=    Path to file containing an X.509 certificate (PEM format)
+                       to be used in the TLS handshake ServerHello.
 
-          key=         Path to SSL private key file (PEM format)
-                       if not specified, the certificate file is
-                       assumed to be a combined certificate and
-                       key file.
+                       If this certificate is constrained by KeyUsage TLS
+                       feature it must allow HTTP server usage, along with
+                       any additional restrictions imposed by your choice
+                       of options= settings.
+
+                       When OpenSSL is used this file may also contain a
+                       chain of intermediate CA certificates to send in the
+                       TLS handshake.
+
+                       When GnuTLS is used this option (and any paired
+                       tls-key= option) may be repeated to load multiple
+                       certificates for different domains.
+
+                       Also, when generate-host-certificates=on is configured
+                       the first tls-cert= option must be a CA certificate
+                       capable of signing the automatically generated
+                       certificates.
+
+          tls-key=     Path to a file containing private key file (PEM format)
+                       for the previous tls-cert= option.
+
+                       If tls-key= is not specified tls-cert= is assumed to
+                       reference a PEM file containing both the certificate
+                       and private key.
 
           cipher=      Colon separated list of supported ciphers.
                        NOTE: some ciphers such as EDH ciphers depend on
@@ -2372,18 +2393,19 @@ TYPE: PortCfg
 DEFAULT: none
 LOC: HttpPortList
 DOC_START
-       Usage:  [ip:]port [mode] cert=certificate.pem [options]
+       Usage:  [ip:]port [mode] tls-cert=certificate.pem [options]
 
        The socket address where Squid will listen for client requests made
        over TLS or SSL connections. Commonly referred to as HTTPS.
 
        This is most useful for situations where you are running squid in
-       accelerator mode and you want to do the TLS work at the accelerator level.
+       accelerator mode and you want to do the TLS work at the accelerator
+       level.
 
        You may specify multiple socket addresses on multiple lines,
        each with their own certificate and/or options.
 
-       The TLS cert= option is mandatory on HTTPS ports.
+       The tls-cert= option is mandatory on HTTPS ports.
 
        See http_port for a list of modes and options.
 DOC_END
@@ -2794,12 +2816,14 @@ DOC_START
        disable         Do not support https:// URLs.
        
        cert=/path/to/client/certificate
-                       A client TLS certificate to use when connecting.
+                       A client X.509 certificate to use when connecting.
        
        key=/path/to/client/private_key
-                       The private TLS key corresponding to the cert= above.
-                       If key= is not specified cert= is assumed to reference
-                       a PEM file containing both the certificate and the key.
+                       The private key corresponding to the cert= above.
+
+                       If key= is not specified cert= is assumed to
+                       reference a PEM file containing both the certificate
+                       and private key.
        
        cipher=...      The list of valid TLS ciphers to use.
 
@@ -3568,14 +3592,15 @@ DOC_START
        tls             Encrypt connections to this peer with TLS.
        
        sslcert=/path/to/ssl/certificate
-                       A client SSL certificate to use when connecting to
+                       A client X.509 certificate to use when connecting to
                        this peer.
        
        sslkey=/path/to/ssl/key
-                       The private SSL key corresponding to sslcert above.
-                       If 'sslkey' is not specified 'sslcert' is assumed to
-                       reference a combined file containing both the
-                       certificate and the key.
+                       The private key corresponding to sslcert above.
+
+                       If sslkey= is not specified sslcert= is assumed to
+                       reference a PEM file containing both the certificate
+                       and private key.
        
        sslcipher=...   The list of valid SSL ciphers to use when connecting
                        to this peer.
@@ -8974,14 +8999,16 @@ DOC_START
        These options are used for Secure ICAP (icaps://....) services only.
 
        tls-cert=/path/to/ssl/certificate
-                       A client SSL certificate to use when connecting to
-                       this icap server.
+                       A client X.509 certificate to use when connecting to
+                       this ICAP server.
 
        tls-key=/path/to/ssl/key
-                       The private TLS/SSL key corresponding to sslcert above.
-                       If 'tls-key' is not specified 'tls-cert' is assumed to
-                       reference a combined PEM format file containing both the
-                       certificate and the key.
+                       The private key corresponding to the previous
+                       tls-cert= option.
+
+                       If tls-key= is not specified tls-cert= is assumed to
+                       reference a PEM file containing both the certificate
+                       and private key.
 
        tls-cipher=...  The list of valid TLS/SSL ciphers to use when connecting
                        to this icap server.
index dc77cf992636e7dd511f7170d0f08b2c8a114bfc..5a1c799a952035b57a6dd595e4005a3a2de85c8d 100644 (file)
@@ -171,9 +171,6 @@ private:
 static void clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub);
 
 static IOACB httpAccept;
-#if USE_OPENSSL
-static IOACB httpsAccept;
-#endif
 static CTCB clientLifetimeTimeout;
 #if USE_IDENT
 static IDCB clientIdentDone;
@@ -2531,7 +2528,6 @@ httpAccept(const CommAcceptCbParams &params)
     AsyncJob::Start(srv); // usually async-calls readSomeData()
 }
 
-#if USE_OPENSSL
 /// Create TLS connection structure and update fd_table
 static bool
 httpsCreate(const Comm::ConnectionPointer &conn, const Security::ContextPointer &ctx)
@@ -2541,61 +2537,87 @@ httpsCreate(const Comm::ConnectionPointer &conn, const Security::ContextPointer
         return true;
     }
 
+    debugs(33, DBG_IMPORTANT, "ERROR: could not create TLS server context for " << conn);
     conn->close();
     return false;
 }
 
 /**
  *
- * \retval 1 on success
- * \retval 0 when needs more data
- * \retval -1 on error
+ * \retval true on success
+ * \retval false when needs more data
+ * \retval false when an error occurred (and closes the TCP connection)
  */
-static int
-Squid_SSL_accept(ConnStateData *conn, PF *callback)
+static bool
+tlsAttemptHandshake(ConnStateData *conn, PF *callback)
 {
+    // TODO: maybe throw instead of just closing the TCP connection.
+    // see https://github.com/squid-cache/squid/pull/81#discussion_r153053278
     int fd = conn->clientConnection->fd;
-    auto ssl = fd_table[fd].ssl.get();
-    int ret;
+    auto session = fd_table[fd].ssl.get();
 
     errno = 0;
-    if ((ret = SSL_accept(ssl)) <= 0) {
-        const int xerrno = errno;
-        const int ssl_error = SSL_get_error(ssl, ret);
 
-        switch (ssl_error) {
+#if USE_OPENSSL
+    const auto ret = SSL_accept(session);
+    if (ret > 0)
+        return true;
 
-        case SSL_ERROR_WANT_READ:
-            Comm::SetSelect(fd, COMM_SELECT_READ, callback, (callback != NULL ? conn : NULL), 0);
-            return 0;
+    const int xerrno = errno;
+    const auto ssl_error = SSL_get_error(session, ret);
 
-        case SSL_ERROR_WANT_WRITE:
-            Comm::SetSelect(fd, COMM_SELECT_WRITE, callback, (callback != NULL ? conn : NULL), 0);
-            return 0;
+    switch (ssl_error) {
 
-        case SSL_ERROR_SYSCALL:
-            if (ret == 0) {
-                debugs(83, 2, "Error negotiating SSL connection on FD " << fd << ": Aborted by client: " << ssl_error);
-            } else {
-                debugs(83, (xerrno == ECONNRESET) ? 1 : 2, "Error negotiating SSL connection on FD " << fd << ": " <<
-                       (xerrno == 0 ? Security::ErrorString(ssl_error) : xstrerr(xerrno)));
-            }
-            return -1;
+    case SSL_ERROR_WANT_READ:
+        Comm::SetSelect(fd, COMM_SELECT_READ, callback, (callback ? conn : nullptr), 0);
+        return false;
 
-        case SSL_ERROR_ZERO_RETURN:
-            debugs(83, DBG_IMPORTANT, "Error negotiating SSL connection on FD " << fd << ": Closed by client");
-            return -1;
+    case SSL_ERROR_WANT_WRITE:
+        Comm::SetSelect(fd, COMM_SELECT_WRITE, callback, (callback ? conn : nullptr), 0);
+        return false;
 
-        default:
-            debugs(83, DBG_IMPORTANT, "Error negotiating SSL connection on FD " <<
-                   fd << ": " << Security::ErrorString(ERR_get_error()) <<
-                   " (" << ssl_error << "/" << ret << ")");
-            return -1;
+    case SSL_ERROR_SYSCALL:
+        if (ret == 0) {
+            debugs(83, 2, "Error negotiating SSL connection on FD " << fd << ": Aborted by client: " << ssl_error);
+        } else {
+            debugs(83, (xerrno == ECONNRESET) ? 1 : 2, "Error negotiating SSL connection on FD " << fd << ": " <<
+                   (xerrno == 0 ? Security::ErrorString(ssl_error) : xstrerr(xerrno)));
         }
+        break;
+
+    case SSL_ERROR_ZERO_RETURN:
+        debugs(83, DBG_IMPORTANT, "Error negotiating SSL connection on FD " << fd << ": Closed by client");
+        break;
+
+    default:
+        debugs(83, DBG_IMPORTANT, "Error negotiating SSL connection on FD " <<
+               fd << ": " << Security::ErrorString(ssl_error) <<
+               " (" << ssl_error << "/" << ret << ")");
+    }
+
+#elif USE_GNUTLS
+
+    const auto x = gnutls_handshake(session);
+    if (x == GNUTLS_E_SUCCESS)
+        return true;
+
+    if (gnutls_error_is_fatal(x)) {
+        debugs(83, 2, "Error negotiating TLS on " << conn->clientConnection << ": Aborted by client: " << Security::ErrorString(x));
 
-        /* NOTREACHED */
+    } else if (x == GNUTLS_E_INTERRUPTED || x == GNUTLS_E_AGAIN) {
+        const auto ioAction = (gnutls_record_get_direction(session)==0 ? COMM_SELECT_READ : COMM_SELECT_WRITE);
+        Comm::SetSelect(fd, ioAction, callback, (callback ? conn : nullptr), 0);
+        return false;
     }
-    return 1;
+
+#else
+    // Performing TLS handshake should never be reachable without a TLS/SSL library.
+    (void)session; // avoid compiler and static analysis complaints
+    fatal("FATAL: HTTPS not supported by this Squid.");
+#endif
+
+    conn->clientConnection->close();
+    return false;
 }
 
 /** negotiate an SSL connection */
@@ -2603,14 +2625,13 @@ static void
 clientNegotiateSSL(int fd, void *data)
 {
     ConnStateData *conn = (ConnStateData *)data;
-    int ret;
-    if ((ret = Squid_SSL_accept(conn, clientNegotiateSSL)) <= 0) {
-        if (ret < 0) // An error
-            conn->clientConnection->close();
+
+    if (!tlsAttemptHandshake(conn, clientNegotiateSSL))
         return;
-    }
 
     Security::SessionPointer session(fd_table[fd].ssl);
+
+#if USE_OPENSSL
     if (Security::SessionIsResumed(session)) {
         debugs(83, 2, "Session " << SSL_get_session(session.get()) <<
                " reused on FD " << fd << " (" << fd_table[fd].ipaddr <<
@@ -2655,10 +2676,14 @@ clientNegotiateSSL(int fd, void *data)
                " on FD " << fd << " (" << fd_table[fd].ipaddr << ":" <<
                fd_table[fd].remote_port << ")");
     }
+#else
+    debugs(83, 2, "TLS session reuse not yet implemented.");
+#endif
 
     // Connection established. Retrieve TLS connection parameters for logging.
     conn->clientConnection->tlsNegotiations()->retrieveNegotiatedInfo(session);
 
+#if USE_OPENSSL
     X509 *client_cert = SSL_get_peer_certificate(session.get());
 
     if (client_cert) {
@@ -2670,8 +2695,11 @@ clientNegotiateSSL(int fd, void *data)
 
         X509_free(client_cert);
     } else {
-        debugs(83, 5, "FD " << fd << " has no certificate.");
+        debugs(83, 5, "FD " << fd << " has no client certificate.");
     }
+#else
+    debugs(83, 2, "Client certificate requesting not yet implemented.");
+#endif
 
     conn->readSomeData();
 }
@@ -2697,6 +2725,7 @@ httpsEstablish(ConnStateData *connState, const Security::ContextPointer &ctx)
     Comm::SetSelect(details->fd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
 }
 
+#if USE_OPENSSL
 /**
  * A callback function to use with the ACLFilledChecklist callback.
  */
@@ -2725,6 +2754,7 @@ httpsSslBumpAccessCheckDone(allow_t answer, void *data)
     if (!connState->fakeAConnectRequest("ssl-bump", connState->inBuf))
         connState->clientConnection->close();
 }
+#endif
 
 /** handle a new HTTPS connection */
 static void
@@ -2758,6 +2788,7 @@ void
 ConnStateData::postHttpsAccept()
 {
     if (port->flags.tunnelSslBumping) {
+#if USE_OPENSSL
         debugs(33, 5, "accept transparent connection: " << clientConnection);
 
         if (!Config.accessList.ssl_bump) {
@@ -2789,12 +2820,16 @@ ConnStateData::postHttpsAccept()
         acl_checklist->al->request = request;
         HTTPMSGLOCK(acl_checklist->al->request);
         acl_checklist->nonBlockingCheck(httpsSslBumpAccessCheckDone, this);
+#else
+        fatal("FATAL: SSL-Bump requires --with-openssl");
+#endif
         return;
     } else {
         httpsEstablish(this, port->secure.staticContext);
     }
 }
 
+#if USE_OPENSSL
 void
 ConnStateData::sslCrtdHandleReplyWrapper(void *data, const Helper::Reply &reply)
 {
@@ -2907,15 +2942,15 @@ void ConnStateData::buildSslCertGenerationParams(Ssl::CertificateProperties &cer
     assert(certProperties.signAlgorithm != Ssl::algSignEnd);
 
     if (certProperties.signAlgorithm == Ssl::algSignUntrusted) {
-        assert(port->secure.untrustedSigningCert);
-        certProperties.signWithX509.resetAndLock(port->secure.untrustedSigningCert.get());
-        certProperties.signWithPkey.resetAndLock(port->secure.untrustedSignPkey.get());
+        assert(port->secure.untrustedSigningCa.cert);
+        certProperties.signWithX509.resetAndLock(port->secure.untrustedSigningCa.cert.get());
+        certProperties.signWithPkey.resetAndLock(port->secure.untrustedSigningCa.pkey.get());
     } else {
-        assert(port->secure.signingCert.get());
-        certProperties.signWithX509.resetAndLock(port->secure.signingCert.get());
+        assert(port->secure.signingCa.cert.get());
+        certProperties.signWithX509.resetAndLock(port->secure.signingCa.cert.get());
 
-        if (port->secure.signPkey)
-            certProperties.signWithPkey.resetAndLock(port->secure.signPkey.get());
+        if (port->secure.signingCa.pkey)
+            certProperties.signWithPkey.resetAndLock(port->secure.signingCa.pkey.get());
     }
     signAlgorithm = certProperties.signAlgorithm;
 
@@ -3254,7 +3289,7 @@ ConnStateData::startPeekAndSplice()
     }
 
     // will call httpsPeeked() with certificate and connection, eventually
-    Security::ContextPointer unConfiguredCTX(Ssl::createSSLContext(port->secure.signingCert, port->secure.signPkey, port->secure));
+    Security::ContextPointer unConfiguredCTX(Ssl::createSSLContext(port->secure.signingCa.cert, port->secure.signingCa.pkey, port->secure));
     fd_table[clientConnection->fd].dynamicTlsContext = unConfiguredCTX;
 
     if (!httpsCreate(clientConnection, unConfiguredCTX))
@@ -3269,12 +3304,11 @@ ConnStateData::startPeekAndSplice()
     bio->hold(true);
 
     // Here squid should have all of the client hello message so the
-    // Squid_SSL_accept should return 0;
+    // tlsAttemptHandshake() should return false;
     // This block exist only to force openSSL parse client hello and detect
     // ERR_SECURE_ACCEPT_FAIL error, which should be checked and splice if required.
-    int ret = 0;
-    if ((ret = Squid_SSL_accept(this, NULL)) < 0) {
-        debugs(83, 2, "SSL_accept failed.");
+    if (!tlsAttemptHandshake(this, nullptr)) {
+        debugs(83, 2, "TLS handshake failed.");
         HttpRequest::Pointer request(http ? http->request : nullptr);
         if (!clientTunnelOnError(this, context, request, HttpRequestMethod(), ERR_SECURE_ACCEPT_FAIL))
             clientConnection->close();
@@ -3497,12 +3531,12 @@ clientHttpConnectionsOpen(void)
                 Ssl::TheGlobalContextStorage.addLocalStorage(s->s, s->secure.dynamicCertMemCacheSize);
             }
         }
+#endif
 
         if (s->secure.encryptTransport && !s->secure.staticContext) {
             debugs(1, DBG_CRITICAL, "ERROR: Ignoring " << scheme << "_port " << s->s << " due to TLS context initialization failure.");
             continue;
         }
-#endif
 
         // Fill out a Comm::Connection which IPC will open as a listener for us
         //  then pass back when active so we can start a TcpAcceptor subscription.
@@ -3522,7 +3556,6 @@ clientHttpConnectionsOpen(void)
                                             ListeningStartedDialer(&clientListenerConnectionOpened, s, Ipc::fdnHttpSocket, sub));
             Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->listenConn, Ipc::fdnHttpSocket, listenCall);
 
-#if USE_OPENSSL
         } else if (s->transport.protocol == AnyP::PROTO_HTTPS) {
             // setup the subscriptions such that new connections accepted by listenConn are handled by HTTPS
             RefCount<AcceptCall> subCall = commCbCall(5, 5, "httpsAccept", CommAcceptCbPtrFun(httpsAccept, CommAcceptCbParams(NULL)));
@@ -3532,7 +3565,6 @@ clientHttpConnectionsOpen(void)
                                             ListeningStartedDialer(&clientListenerConnectionOpened,
                                                     s, Ipc::fdnHttpsSocket, sub));
             Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->listenConn, Ipc::fdnHttpsSocket, listenCall);
-#endif
         }
 
         HttpSockets[NHttpSockets] = -1; // set in clientListenerConnectionOpened
index e36a0a3d12de0698132508bdf32523547a779ff1..d8a2ebc8b9f3621a262c90a807ee4ff683eb0922 100644 (file)
@@ -219,10 +219,10 @@ public:
     /// The caller assumes responsibility for connection closure detection.
     void stopPinnedConnectionMonitoring();
 
-#if USE_OPENSSL
     /// the second part of old httpsAccept, waiting for future HttpsServer home
     void postHttpsAccept();
 
+#if USE_OPENSSL
     /// Initializes and starts a peek-and-splice negotiation with the SSL client
     void startPeekAndSplice();
 
diff --git a/src/security/KeyData.cc b/src/security/KeyData.cc
new file mode 100644 (file)
index 0000000..e763ebe
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#include "squid.h"
+#include "anyp/PortCfg.h"
+#include "fatal.h"
+#include "security/KeyData.h"
+#include "SquidConfig.h"
+#include "ssl/bio.h"
+
+/**
+ * Read certificate from file.
+ * See also: Ssl::ReadX509Certificate function, gadgets.cc file
+ */
+bool
+Security::KeyData::loadX509CertFromFile()
+{
+    debugs(83, DBG_IMPORTANT, "Using certificate in " << certFile);
+
+#if USE_OPENSSL
+    const char *certFilename = certFile.c_str();
+    Ssl::BIO_Pointer bio(BIO_new(BIO_s_file()));
+    if (!bio || !BIO_read_filename(bio.get(), certFilename)) {
+        const auto x = ERR_get_error();
+        debugs(83, DBG_IMPORTANT, "ERROR: unable to load certificate file '" << certFile << "': " << ErrorString(x));
+        return false;
+    }
+
+    if (X509 *certificate = PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)) {
+        if (X509_check_issued(certificate, certificate) == X509_V_OK)
+            debugs(83, 5, "Certificate is self-signed, will not be chained");
+        else
+            cert.resetWithoutLocking(certificate);
+    }
+
+#elif USE_GNUTLS
+    const char *certFilename = certFile.c_str();
+    gnutls_datum_t data;
+    Security::ErrorCode x = gnutls_load_file(certFilename, &data);
+    if (x != GNUTLS_E_SUCCESS) {
+        debugs(83, DBG_IMPORTANT, "ERROR: unable to load certificate file '" << certFile << "': " << ErrorString(x));
+        return false;
+    }
+
+    gnutls_pcert_st pcrt;
+    x = gnutls_pcert_import_x509_raw(&pcrt, &data, GNUTLS_X509_FMT_PEM, 0);
+    if (x != GNUTLS_E_SUCCESS) {
+        debugs(83, DBG_IMPORTANT, "ERROR: unable to import certificate from '" << certFile << "': " << ErrorString(x));
+        return false;
+    }
+    gnutls_free(data.data);
+
+    gnutls_x509_crt_t certificate;
+    x = gnutls_pcert_export_x509(&pcrt, &certificate);
+    if (x != GNUTLS_E_SUCCESS) {
+        debugs(83, DBG_IMPORTANT, "ERROR: unable to X.509 convert certificate from '" << certFile << "': " << ErrorString(x));
+        return false;
+    }
+
+    if (certificate) {
+        cert = Security::CertPointer(certificate, [](gnutls_x509_crt_t p) {
+                   debugs(83, 5, "gnutls_x509_crt_deinit cert=" << (void*)p);
+                   gnutls_x509_crt_deinit(p);
+               });
+    } else {
+        cert.reset(); // paranoid: ensure cert is unset
+    }
+
+#else
+    // do nothing.
+#endif
+
+    if (!cert) {
+        debugs(83, DBG_IMPORTANT, "ERROR: unable to load certificate from '" << certFile << "'");
+    }
+
+    return bool(cert);
+}
+
+/**
+ * Read certificate from file.
+ * See also: Ssl::ReadX509Certificate function, gadgets.cc file
+ */
+void
+Security::KeyData::loadX509ChainFromFile()
+{
+#if USE_OPENSSL
+    // XXX: This BIO loads the public cert as first chain cert,
+    //      so the code appending chains sends it twice in handshakes.
+    const char *certFilename = certFile.c_str();
+    Ssl::BIO_Pointer bio(BIO_new(BIO_s_file()));
+    if (!bio || !BIO_read_filename(bio.get(), certFilename)) {
+        const auto x = ERR_get_error();
+        debugs(83, DBG_IMPORTANT, "ERROR: unable to load chain file '" << certFile << "': " << ErrorString(x));
+        return;
+    }
+
+    if (X509_check_issued(cert.get(), cert.get()) == X509_V_OK)
+        debugs(83, 5, "Certificate is self-signed, will not be chained");
+    else {
+        // and add to the chain any other certificate exist in the file
+        while (X509 *ca = PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)) {
+            // XXX: self-signed check should be applied to all certs loaded.
+            // XXX: missing checks that the chained certs are actually part of a chain for validating cert.
+            chain.emplace_front(Security::CertPointer(ca));
+        }
+    }
+
+#elif USE_GNUTLS
+    // XXX: implement chain loading
+    debugs(83, 2, "Loading certificate chain from PEM files not implemented in this Squid.");
+
+#else
+    // nothing to do.
+#endif
+}
+
+/**
+ * Read X.509 private key from file.
+ */
+bool
+Security::KeyData::loadX509PrivateKeyFromFile()
+{
+    debugs(83, DBG_IMPORTANT, "Using key in " << privateKeyFile);
+
+#if USE_OPENSSL
+    const char *keyFilename = privateKeyFile.c_str();
+    // XXX: Ssl::AskPasswordCb needs SSL_CTX_set_default_passwd_cb_userdata()
+    // so this may not fully work iff Config.Program.ssl_password is set.
+    pem_password_cb *cb = ::Config.Program.ssl_password ? &Ssl::AskPasswordCb : nullptr;
+    Ssl::ReadPrivateKeyFromFile(keyFilename, pkey, cb);
+
+    if (pkey && !X509_check_private_key(cert.get(), pkey.get())) {
+        debugs(83, DBG_IMPORTANT, "WARNING: '" << privateKeyFile << "' X509_check_private_key() failed");
+        pkey.reset();
+    }
+
+#elif USE_GNUTLS
+    const char *keyFilename = privateKeyFile.c_str();
+    gnutls_datum_t data;
+    if (gnutls_load_file(keyFilename, &data) == GNUTLS_E_SUCCESS) {
+        gnutls_privkey_t key;
+        (void)gnutls_privkey_init(&key);
+        Security::ErrorCode x = gnutls_privkey_import_x509_raw(key, &data, GNUTLS_X509_FMT_PEM, nullptr, 0);
+        if (x == GNUTLS_E_SUCCESS) {
+            gnutls_x509_privkey_t xkey;
+            gnutls_privkey_export_x509(key, &xkey);
+            gnutls_privkey_deinit(key);
+            pkey = Security::PrivateKeyPointer(xkey, [](gnutls_x509_privkey_t p) {
+                       debugs(83, 5, "gnutls_x509_privkey_deinit pkey=" << (void*)p);
+                       gnutls_x509_privkey_deinit(p);
+                   });
+        }
+    }
+    gnutls_free(data.data);
+
+#else
+    // nothing to do.
+#endif
+
+    return bool(pkey);
+}
+
+void
+Security::KeyData::loadFromFiles(const AnyP::PortCfg &port, const char *portType)
+{
+    char buf[128];
+    if (!loadX509CertFromFile()) {
+        debugs(83, DBG_IMPORTANT, "WARNING: '" << portType << "_port " << port.s.toUrl(buf, sizeof(buf)) << "' missing certificate in '" << certFile << "'");
+        return;
+    }
+
+    // certificate chain in the PEM file is optional
+    loadX509ChainFromFile();
+
+    // pkey is mandatory, not having it makes cert and chain pointless.
+    if (!loadX509PrivateKeyFromFile()) {
+        debugs(83, DBG_IMPORTANT, "WARNING: '" << portType << "_port " << port.s.toUrl(buf, sizeof(buf)) << "' missing private key in '" << privateKeyFile << "'");
+        cert.reset();
+        chain.clear();
+    }
+}
index 6707d30ce6be532f645a970d461e0500800fd476..2a13bb613f893f8eb647b17b7b32ebf890bfbf1b 100644 (file)
@@ -9,6 +9,7 @@
 #ifndef SQUID_SRC_SECURITY_KEYDATA_H
 #define SQUID_SRC_SECURITY_KEYDATA_H
 
+#include "anyp/forward.h"
 #include "sbuf/SBuf.h"
 #include "security/forward.h"
 
@@ -18,9 +19,25 @@ namespace Security
 /// TLS certificate and private key details from squid.conf
 class KeyData
 {
+public:
+    /// load the contents of certFile and privateKeyFile into memory cert, pkey and chain
+    void loadFromFiles(const AnyP::PortCfg &, const char *portType);
+
 public:
     SBuf certFile;       ///< path of file containing PEM format X.509 certificate
     SBuf privateKeyFile; ///< path of file containing private key in PEM format
+
+    /// public X.509 certificate from certFile
+    Security::CertPointer cert;
+    /// private key from privateKeyFile
+    Security::PrivateKeyPointer pkey;
+    /// any certificates which must be chained from cert
+    Security::CertList chain;
+
+private:
+    bool loadX509CertFromFile();
+    void loadX509ChainFromFile();
+    bool loadX509PrivateKeyFromFile();
 };
 
 } // namespace Security
index c41207757ef7eec053232d7796e338ca26ca0791..b56aa13e74faa7604643852254a44d9c83463cb0 100644 (file)
@@ -22,6 +22,7 @@ libsecurity_la_SOURCES= \
        Handshake.cc \
        Handshake.h \
        forward.h \
+       KeyData.cc \
        KeyData.h \
        LockingPointer.h \
        NegotiationHistory.cc \
index 57b2a45c8372685b98e46f9bf018251020547ac0..891d0fab96a284125d47040f33a5e69060e06694 100644 (file)
@@ -66,6 +66,7 @@ private:
     void parseOptions(); ///< parsed value of sslOptions
     long parseFlags();
     void loadCrlFile();
+    void loadKeysFile();
 
 public:
     SBuf sslOptions;     ///< library-specific options string
index c22a39f49b86486d1c798c79bac2d18ad01d2048..46e5625851983ad2984fb686dc74b711dce9821d 100644 (file)
@@ -44,11 +44,8 @@ Security::ServerOptions::operator =(const Security::ServerOptions &old) {
 
         staticContextSessionId = old.staticContextSessionId;
         generateHostCertificates = old.generateHostCertificates;
-        signingCert = old.signingCert;
-        signPkey = old.signPkey;
-        certsToChain = old.certsToChain;
-        untrustedSigningCert = old.untrustedSigningCert;
-        untrustedSignPkey = old.untrustedSignPkey;
+        signingCa = old.signingCa;
+        untrustedSigningCa = old.untrustedSigningCa;
         dynamicCertMemCacheSize = old.dynamicCertMemCacheSize;
     }
     return *this;
@@ -189,6 +186,23 @@ Security::ServerOptions::createBlankContext() const
     return ctx;
 }
 
+void
+Security::ServerOptions::initServerContexts(AnyP::PortCfg &port)
+{
+    const char *portType = AnyP::ProtocolType_str[port.transport.protocol];
+    for (auto &keyData : certs) {
+        keyData.loadFromFiles(port, portType);
+    }
+
+    if (generateHostCertificates) {
+        createSigningContexts(port);
+
+    } else if (!createStaticServerContext(port)) {
+        char buf[128];
+        fatalf("%s_port %s initialization error", portType, port.s.toUrl(buf, sizeof(buf)));
+    }
+}
+
 bool
 Security::ServerOptions::createStaticServerContext(AnyP::PortCfg &port)
 {
@@ -196,10 +210,61 @@ Security::ServerOptions::createStaticServerContext(AnyP::PortCfg &port)
 
     Security::ContextPointer t(createBlankContext());
     if (t) {
+
 #if USE_OPENSSL
-        if (!Ssl::InitServerContext(t, port))
+        if (certs.size() > 1) {
+            // NOTE: calling SSL_CTX_use_certificate() repeatedly _replaces_ the previous cert details.
+            //       so we cannot use it and support multiple server certificates with OpenSSL.
+            debugs(83, DBG_CRITICAL, "ERROR: OpenSSL does not support multiple server certificates. Ignoring addional cert= parameters.");
+        }
+
+        const auto &keys = certs.front();
+
+        if (!SSL_CTX_use_certificate(t.get(), keys.cert.get())) {
+            const auto x = ERR_get_error();
+            debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire TLS certificate '" << keys.certFile << "': " << Security::ErrorString(x));
             return false;
+        }
+
+        if (!SSL_CTX_use_PrivateKey(t.get(), keys.pkey.get())) {
+            const auto x = ERR_get_error();
+            debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire TLS private key '" << keys.privateKeyFile << "': " << Security::ErrorString(x));
+            return false;
+        }
+
+        for (auto cert : keys.chain) {
+            if (SSL_CTX_add_extra_chain_cert(t.get(), cert.get())) {
+                // increase the certificate lock
+                X509_up_ref(cert.get());
+            } else {
+                const auto error = ERR_get_error();
+                debugs(83, DBG_IMPORTANT, "WARNING: can not add certificate to SSL context chain: " << Security::ErrorString(error));
+            }
+        }
+
+#elif USE_GNUTLS
+        for (auto &keys : certs) {
+            gnutls_x509_crt_t crt = keys.cert.get();
+            gnutls_x509_privkey_t xkey = keys.pkey.get();
+            const auto x = gnutls_certificate_set_x509_key(t.get(), &crt, 1, xkey);
+            if (x != GNUTLS_E_SUCCESS) {
+                SBuf whichFile = keys.certFile;
+                if (keys.certFile != keys.privateKeyFile) {
+                    whichFile.appendf(" and ");
+                    whichFile.append(keys.privateKeyFile);
+                }
+                debugs(83, DBG_CRITICAL, "ERROR: Failed to initialize server context with keys from " << whichFile << ": " << Security::ErrorString(x));
+                return false;
+            }
+                // XXX: add cert chain to the context
+        }
 #endif
+
+        if (!updateContextConfig(t)) {
+            debugs(83, DBG_CRITICAL, "ERROR: Configuring static TLS context");
+            return false;
+        }
+
         if (!loadClientCaFile())
             return false;
     }
@@ -209,39 +274,40 @@ Security::ServerOptions::createStaticServerContext(AnyP::PortCfg &port)
 }
 
 void
-Security::ServerOptions::createSigningContexts(AnyP::PortCfg &port)
+Security::ServerOptions::createSigningContexts(const AnyP::PortCfg &port)
 {
-    const char *portType = AnyP::ProtocolType_str[port.transport.protocol];
-    if (!certs.empty()) {
-#if USE_OPENSSL
-        Security::KeyData &keys = certs.front();
-        Ssl::readCertChainAndPrivateKeyFromFiles(signingCert, signPkey, certsToChain, keys.certFile.c_str(), keys.privateKeyFile.c_str());
-#else
-        char buf[128];
-        fatalf("Directive '%s_port %s' requires --with-openssl.", portType, port.s.toUrl(buf, sizeof(buf)));
-#endif
-    }
+    // For signing we do not have a pre-initialized context object. Instead
+    // contexts are generated as needed. This method initializes the cert
+    // and key pointers used to sign those contexts later.
 
-    if (!signingCert) {
+    signingCa = certs.front();
+
+    const char *portType = AnyP::ProtocolType_str[port.transport.protocol];
+    if (!signingCa.cert) {
         char buf[128];
-        fatalf("No valid signing SSL certificate configured for %s_port %s", portType, port.s.toUrl(buf, sizeof(buf)));
+        // XXX: we never actually checked that the cert is capable of signing!
+        fatalf("No valid signing certificate configured for %s_port %s", portType, port.s.toUrl(buf, sizeof(buf)));
     }
 
-    if (!signPkey)
-        debugs(3, DBG_IMPORTANT, "No SSL private key configured for  " << portType << "_port " << port.s);
+    if (!signingCa.pkey)
+        debugs(3, DBG_IMPORTANT, "No TLS private key configured for  " << portType << "_port " << port.s);
 
 #if USE_OPENSSL
-    Ssl::generateUntrustedCert(untrustedSigningCert, untrustedSignPkey, signingCert, signPkey);
+    Ssl::generateUntrustedCert(untrustedSigningCa.cert, untrustedSigningCa.pkey, signingCa.cert, signingCa.pkey);
+#elif USE_GNUTLS
+    // TODO: implement for GnuTLS. Just a warning for now since generate is implicitly on for all crypto builds.
+    signingCa.cert.reset();
+    signingCa.pkey.reset();
+    debugs(83, DBG_CRITICAL, "WARNING: Dynamic TLS certificate generation requires --with-openssl.");
+    return;
+#else
+    debugs(83, DBG_CRITICAL, "ERROR: Dynamic TLS certificate generation requires --with-openssl.");
+    return;
 #endif
 
-    if (!untrustedSigningCert) {
+    if (!untrustedSigningCa.cert) {
         char buf[128];
-        fatalf("Unable to generate signing SSL certificate for untrusted sites for %s_port %s", portType, port.s.toUrl(buf, sizeof(buf)));
-    }
-
-    if (!createStaticServerContext(port)) {
-        char buf[128];
-        fatalf("%s_port %s initialization error", portType, port.s.toUrl(buf, sizeof(buf)));
+        fatalf("Unable to generate signing certificate for untrusted sites for %s_port %s", portType, port.s.toUrl(buf, sizeof(buf)));
     }
 }
 
index 941bf671b6f18291c2a046d835dcd7779f0a46a3..fbfc587443476451a5c8d81dc30fce9f04b910a5 100644 (file)
@@ -41,14 +41,8 @@ public:
     virtual Security::ContextPointer createBlankContext() const;
     virtual void dumpCfg(Packable *, const char *pfx) const;
 
-    /// generate a security server-context from these configured options
-    /// the resulting context is stored in staticContext
-    /// \returns true if a context could be created
-    bool createStaticServerContext(AnyP::PortCfg &);
-
-    /// initialize contexts for signing dynamic TLS certificates (if needed)
-    /// the resulting context is stored in signingCert, signPKey, untrustedSigningCert, untrustedSignPKey
-    void createSigningContexts(AnyP::PortCfg &);
+    /// initialize all server contexts as-needed
+    void initServerContexts(AnyP::PortCfg &);
 
     /// update the given TLS security context using squid.conf settings
     bool updateContextConfig(Security::ContextPointer &);
@@ -70,13 +64,21 @@ public:
     Security::ContextPointer staticContext;
     SBuf staticContextSessionId; ///< "session id context" for staticContext
 
+#if USE_OPENSSL
+    bool generateHostCertificates = true; ///< dynamically make host cert
+#elif USE_GNUTLS
+    // TODO: GnuTLS does implement TLS server connections so the cert
+    // generate vs static choice can be reached in the code now.
+    // But this feature is not fully working implemented so must not
+    // be enabled by default for production installations.
+    bool generateHostCertificates = false; ///< dynamically make host cert
+#else
+    // same as OpenSSL so config errors show up easily
     bool generateHostCertificates = true; ///< dynamically make host cert
+#endif
 
-    Security::CertPointer signingCert; ///< x509 certificate for signing generated certificates
-    Security::PrivateKeyPointer signPkey; ///< private key for signing generated certificates
-    Security::CertList certsToChain; ///<  x509 certificates to send with the generated cert
-    Security::CertPointer untrustedSigningCert; ///< x509 certificate for signing untrusted generated certificates
-    Security::PrivateKeyPointer untrustedSignPkey; ///< private key for signing untrusted generated certificates
+    Security::KeyData signingCa; ///< x509 certificate and key for signing generated certificates
+    Security::KeyData untrustedSigningCa; ///< x509 certificate and key for signing untrusted generated certificates
 
     /// max size of generated certificates memory cache (4 MB default)
     size_t dynamicCertMemCacheSize = 4*1024*1024;
@@ -85,6 +87,15 @@ private:
     bool loadClientCaFile();
     void loadDhParams();
 
+    /// generate a security server-context from these configured options
+    /// the resulting context is stored in staticContext
+    /// \returns true if a context could be created
+    bool createStaticServerContext(AnyP::PortCfg &);
+
+    /// initialize contexts for signing dynamic TLS certificates (if needed)
+    /// the resulting keys are stored in signingCa and untrustedSigningCa
+    void createSigningContexts(const AnyP::PortCfg &);
+
 private:
     SBuf clientCaFile;  ///< name of file to load client CAs from
 #if USE_OPENSSL
index 275b6d8db0a2399874961c9da8410a6c550ad8b0..7bf3f0e194284aa222092fbcfce9020557a76c9b 100644 (file)
@@ -13,8 +13,8 @@
 #include "security/Context.h"
 #include "security/Session.h"
 
-#if USE_GNUTLS && HAVE_GNUTLS_X509_H
-#include <gnutls/x509.h>
+#if USE_GNUTLS && HAVE_GNUTLS_ABSTRACT_H
+#include <gnutls/abstract.h>
 #endif
 #include <list>
 #if USE_OPENSSL && HAVE_OPENSSL_ERR_H
@@ -86,10 +86,9 @@ typedef CbDataList<Security::CertError> CertErrors;
 CtoCpp1(X509_free, X509 *)
 typedef Security::LockingPointer<X509, X509_free_cpp, HardFun<int, X509 *, X509_up_ref> > CertPointer;
 #elif USE_GNUTLS
-CtoCpp1(gnutls_x509_crt_deinit, gnutls_x509_crt_t)
-typedef Security::LockingPointer<struct gnutls_x509_crt_int, gnutls_x509_crt_deinit> CertPointer;
+typedef std::shared_ptr<struct gnutls_x509_crt_int> CertPointer;
 #else
-typedef void * CertPointer;
+typedef std::shared_ptr<void> CertPointer;
 #endif
 
 #if USE_OPENSSL
@@ -167,9 +166,10 @@ class PeerOptions;
 #if USE_OPENSSL
 CtoCpp1(EVP_PKEY_free, EVP_PKEY *)
 typedef Security::LockingPointer<EVP_PKEY, EVP_PKEY_free_cpp, HardFun<int, EVP_PKEY *, EVP_PKEY_up_ref> > PrivateKeyPointer;
+#elif USE_GNUTLS
+typedef std::shared_ptr<struct gnutls_x509_privkey_int> PrivateKeyPointer;
 #else
-// XXX: incompatible with the other PrivateKeyPointer declaration (lacks self-initialization)
-typedef void *PrivateKeyPointer;
+typedef std::shared_ptr<void> PrivateKeyPointer;
 #endif
 
 class ServerOptions;
index c53cd1c1ee197dfe71b9469d97d929a2f6795b7a..aff5a272c2d3c89555861b76d74d2502b451fc81 100644 (file)
@@ -42,14 +42,12 @@ Http::One::Server::start()
 {
     ConnStateData::start();
 
-#if USE_OPENSSL
     // XXX: Until we create an HttpsServer class, use this hack to allow old
     // client_side.cc code to manipulate ConnStateData object directly
     if (isHttpsServer) {
         postHttpsAccept();
         return;
     }
-#endif
 
     typedef CommCbMemFunT<Server, CommTimeoutCbParams> TimeoutDialer;
     AsyncCall::Pointer timeoutCall =  JobCallback(33, 5,
index 92f68a540a528c20ccf498668f84454a6b41a41b..fb311b0d993ef52c7698cce2381b866a10e99080 100644 (file)
@@ -13,7 +13,9 @@
 
 #include "FadingCounter.h"
 #include "fd.h"
+#include "MemBuf.h"
 #include "security/Handshake.h"
+#include "ssl/support.h"
 
 #include <iosfwd>
 #include <list>
index 566d54907b33070fd76568cdf989097acb3c78e4..8c9647d061ef7f90322e7c7a893a8d32211ce128 100644 (file)
@@ -59,15 +59,14 @@ std::vector<const char *> Ssl::BumpModeStr = {
  \ingroup ServerProtocolSSLAPI
  */
 
-/// \ingroup ServerProtocolSSLInternal
-static int
-ssl_ask_password_cb(char *buf, int size, int rwflag, void *userdata)
+int
+Ssl::AskPasswordCb(char *buf, int size, int rwflag, void *userdata)
 {
     FILE *in;
     int len = 0;
     char cmdline[1024];
 
-    snprintf(cmdline, sizeof(cmdline), "\"%s\" \"%s\"", Config.Program.ssl_password, (const char *)userdata);
+    snprintf(cmdline, sizeof(cmdline), "\"%s\" \"%s\"", ::Config.Program.ssl_password, (const char *)userdata);
     in = popen(cmdline, "r");
 
     if (fgets(buf, size, in))
@@ -89,7 +88,7 @@ static void
 ssl_ask_password(SSL_CTX * context, const char * prompt)
 {
     if (Config.Program.ssl_password) {
-        SSL_CTX_set_default_passwd_cb(context, ssl_ask_password_cb);
+        SSL_CTX_set_default_passwd_cb(context, Ssl::AskPasswordCb);
         SSL_CTX_set_default_passwd_cb_userdata(context, (void *)prompt);
     }
 }
@@ -512,27 +511,6 @@ Ssl::InitServerContext(Security::ContextPointer &ctx, AnyP::PortCfg &port)
     if (!ctx)
         return false;
 
-    if (!SSL_CTX_use_certificate(ctx.get(), port.secure.signingCert.get())) {
-        const int ssl_error = ERR_get_error();
-        const auto &keys = port.secure.certs.front();
-        debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire TLS certificate '" << keys.certFile << "': " << Security::ErrorString(ssl_error));
-        return false;
-    }
-
-    if (!SSL_CTX_use_PrivateKey(ctx.get(), port.secure.signPkey.get())) {
-        const int ssl_error = ERR_get_error();
-        const auto &keys = port.secure.certs.front();
-        debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire TLS private key '" << keys.privateKeyFile << "': " << Security::ErrorString(ssl_error));
-        return false;
-    }
-
-    Ssl::addChainToSslContext(ctx, port.secure.certsToChain);
-
-    if (!port.secure.updateContextConfig(ctx)) {
-        debugs(83, DBG_CRITICAL, "ERROR: Configuring static SSL context");
-        return false;
-    }
-
     return true;
 }
 
@@ -837,7 +815,7 @@ Ssl::chainCertificatesToSSLContext(Security::ContextPointer &ctx, Security::Serv
 {
     assert(ctx);
     // Add signing certificate to the certificates chain
-    X509 *signingCert = options.signingCert.get();
+    X509 *signingCert = options.signingCa.cert.get();
     if (SSL_CTX_add_extra_chain_cert(ctx.get(), signingCert)) {
         // increase the certificate lock
         X509_up_ref(signingCert);
@@ -845,7 +823,16 @@ Ssl::chainCertificatesToSSLContext(Security::ContextPointer &ctx, Security::Serv
         const int ssl_error = ERR_get_error();
         debugs(33, DBG_IMPORTANT, "WARNING: can not add signing certificate to SSL context chain: " << Security::ErrorString(ssl_error));
     }
-    Ssl::addChainToSslContext(ctx, options.certsToChain);
+
+    for (auto cert : options.signingCa.chain) {
+        if (SSL_CTX_add_extra_chain_cert(ctx.get(), cert.get())) {
+            // increase the certificate lock
+            X509_up_ref(cert.get());
+        } else {
+            const auto error = ERR_get_error();
+            debugs(83, DBG_IMPORTANT, "WARNING: can not add certificate to SSL dynamic context chain: " << Security::ErrorString(error));
+        }
+    }
 }
 
 void
@@ -943,23 +930,6 @@ Ssl::setClientSNI(SSL *ssl, const char *fqdn)
 #endif
 }
 
-void
-Ssl::addChainToSslContext(Security::ContextPointer &ctx, Security::CertList &chain)
-{
-    if (chain.empty())
-        return;
-
-    for (auto cert : chain) {
-        if (SSL_CTX_add_extra_chain_cert(ctx.get(), cert.get())) {
-            // increase the certificate lock
-            X509_up_ref(cert.get());
-        } else {
-            const int ssl_error = ERR_get_error();
-            debugs(83, DBG_IMPORTANT, "WARNING: can not add certificate to SSL context chain: " << Security::ErrorString(ssl_error));
-        }
-    }
-}
-
 static const char *
 hasAuthorityInfoAccessCaIssuers(X509 *cert)
 {
@@ -1236,65 +1206,6 @@ Ssl::unloadSquidUntrusted()
     }
 }
 
-/**
- \ingroup ServerProtocolSSLInternal
- * Read certificate from file.
- * See also: static readSslX509Certificate function, gadgets.cc file
- */
-static X509 * readSslX509CertificatesChain(char const * certFilename, Security::CertList &chain)
-{
-    if (!certFilename)
-        return NULL;
-    Ssl::BIO_Pointer bio(BIO_new(BIO_s_file()));
-    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) {
-
-        if (X509_check_issued(certificate, certificate) == X509_V_OK)
-            debugs(83, 5, "Certificate is self-signed, will not be chained");
-        else {
-            // and add to the chain any other certificate exist in the file
-            while (X509 *ca = PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr))
-                chain.emplace_front(Security::CertPointer(ca));
-        }
-    }
-
-    return certificate;
-}
-
-void
-Ssl::readCertChainAndPrivateKeyFromFiles(Security::CertPointer & cert, Security::PrivateKeyPointer & pkey, Security::CertList &chain, char const * certFilename, char const * keyFilename)
-{
-    if (keyFilename == NULL)
-        keyFilename = certFilename;
-
-    if (certFilename == NULL)
-        certFilename = keyFilename;
-
-    debugs(83, DBG_IMPORTANT, "Using certificate in " << certFilename);
-
-    // XXX: ssl_ask_password_cb needs SSL_CTX_set_default_passwd_cb_userdata()
-    // so this may not fully work iff Config.Program.ssl_password is set.
-    pem_password_cb *cb = ::Config.Program.ssl_password ? &ssl_ask_password_cb : NULL;
-    Ssl::ReadPrivateKeyFromFile(keyFilename, pkey, cb);
-    cert.resetWithoutLocking(readSslX509CertificatesChain(certFilename, chain));
-    if (!cert) {
-        debugs(83, DBG_IMPORTANT, "WARNING: missing cert in '" << certFilename << "'");
-    } else if (!pkey) {
-        debugs(83, DBG_IMPORTANT, "WARNING: missing private key in '" << keyFilename << "'");
-    } else if (!X509_check_private_key(cert.get(), pkey.get())) {
-        debugs(83, DBG_IMPORTANT, "WARNING: X509_check_private_key() failed to verify signing cert");
-    } else
-        return; // everything is okay
-
-    pkey.reset();
-    cert.reset();
-}
-
 bool Ssl::generateUntrustedCert(Security::CertPointer &untrustedCert, Security::PrivateKeyPointer &untrustedPkey, Security::CertPointer const  &cert, Security::PrivateKeyPointer const & pkey)
 {
     // Generate the self-signed certificate, using a hard-coded subject prefix
index 33075d7ba2e27262765ded3a0765f621978ca905..38c080bfb9bc283cd0408d09b7c8f3ac7dd9ac19 100644 (file)
@@ -65,6 +65,11 @@ class MemMap;
 
 namespace Ssl
 {
+
+/// callback for receiving password to access password secured PEM files
+/// XXX: Requires SSL_CTX_set_default_passwd_cb_userdata()!
+int AskPasswordCb(char *buf, int size, int rwflag, void *userdata);
+
 /// initialize the SSL library global state.
 /// call before generating any SSL context
 void Initialize();
@@ -264,12 +269,6 @@ bool configureSSL(SSL *ssl, CertificateProperties const &properties, AnyP::PortC
  */
 bool configureSSLUsingPkeyAndCertFromMemory(SSL *ssl, const char *data, AnyP::PortCfg &port);
 
-/**
-  \ingroup ServerProtocolSSLAPI
-  * Adds the certificates in certList to the certificate chain of the SSL context
- */
-void addChainToSslContext(Security::ContextPointer &, Security::CertList &);
-
 /**
   \ingroup ServerProtocolSSLAPI
   * Configures sslContext to use squid untrusted certificates internal list
@@ -277,15 +276,6 @@ void addChainToSslContext(Security::ContextPointer &, Security::CertList &);
  */
 void useSquidUntrusted(SSL_CTX *sslContext);
 
-/**
- \ingroup ServerProtocolSSLAPI
- *  Read certificate, private key and any certificates which must be chained from files.
- * See also: Ssl::readCertAndPrivateKeyFromFiles function,  defined in gadgets.h
- * \param certFilename name of file with certificate and certificates which must be chainned.
- * \param keyFilename name of file with private key.
- */
-void readCertChainAndPrivateKeyFromFiles(Security::CertPointer & cert, Security::PrivateKeyPointer & pkey, Security::CertList &chain, char const * certFilename, char const * keyFilename);
-
 /**
    \ingroup ServerProtocolSSLAPI
    * Iterates over the X509 common and alternate names and to see if  matches with given data
index f4db58dee5d85d65a6e02eabcd55f29ee0f012b5..33caad1a20a82d5fb13ded51e743f4aee5fb3594 100644 (file)
@@ -31,6 +31,12 @@ std::ostream &Security::operator <<(std::ostream &os, const Security::EncryptorA
 Security::HandshakeParser::HandshakeParser() STUB
 bool Security::HandshakeParser::parseHello(const SBuf &) STUB_RETVAL(false)
 
+#include "security/KeyData.h"
+namespace Security
+{
+void KeyData::loadFromFiles(const AnyP::PortCfg &, const char *) STUB
+}
+
 #include "security/NegotiationHistory.h"
 Security::NegotiationHistory::NegotiationHistory() STUB
 void Security::NegotiationHistory::retrieveNegotiatedInfo(const Security::SessionPointer &) STUB
@@ -91,8 +97,9 @@ Security::ServerOptions &Security::ServerOptions::operator=(Security::ServerOpti
 void Security::ServerOptions::parse(const char *) STUB
 void Security::ServerOptions::dumpCfg(Packable *, const char *) const STUB
 Security::ContextPointer Security::ServerOptions::createBlankContext() const STUB_RETVAL(Security::ContextPointer())
+void Security::ServerOptions::initServerContexts(AnyP::PortCfg&) STUB
 bool Security::ServerOptions::createStaticServerContext(AnyP::PortCfg &) STUB_RETVAL(false)
-void Security::ServerOptions::createSigningContexts(AnyP::PortCfg &) STUB
+void Security::ServerOptions::createSigningContexts(const AnyP::PortCfg &) STUB
 bool Security::ServerOptions::updateContextConfig(Security::ContextPointer &) STUB_RETVAL(false)
 void Security::ServerOptions::updateContextEecdh(Security::ContextPointer &) STUB
 void Security::ServerOptions::updateContextClientCa(Security::ContextPointer &) STUB
index 64601d7c429263cf7ae82d517d5eb17b2aad034c..fb47a924c744e466919a2544bb34773239e9e635 100644 (file)
@@ -50,6 +50,7 @@ const String & Ssl::ErrorDetail::toString() const STUB_RETSTATREF(String)
 #include "ssl/support.h"
 namespace Ssl
 {
+int AskPasswordCb(char *, int, int, void *) STUB_RETVAL(0)
 bool InitServerContext(Security::ContextPointer &, AnyP::PortCfg &) STUB_RETVAL(false)
 bool InitClientContext(Security::ContextPointer &, Security::PeerOptions &, const char *) STUB_RETVAL(false)
 void SetupVerifyCallback(Security::ContextPointer &) STUB
@@ -70,8 +71,6 @@ bool generateUntrustedCert(Security::CertPointer &, Security::PrivateKeyPointer
 Security::ContextPointer GenerateSslContext(CertificateProperties const &, Security::ServerOptions &, bool) STUB_RETVAL(Security::ContextPointer())
 bool verifySslCertificate(Security::ContextPointer &, CertificateProperties const &) STUB_RETVAL(false)
 Security::ContextPointer GenerateSslContextUsingPkeyAndCertFromMemory(const char *, Security::ServerOptions &, bool) STUB_RETVAL(Security::ContextPointer())
-void addChainToSslContext(Security::ContextPointer &, STACK_OF(X509) *) STUB
-void readCertChainAndPrivateKeyFromFiles(Security::CertPointer &, Security::PrivateKeyPointer &, Security::CertList &, char const *, char const *) STUB
 int matchX509CommonNames(X509 *peer_cert, void *check_data, int (*check_func)(void *check_data,  ASN1_STRING *cn_data)) STUB_RETVAL(0)
 bool checkX509ServerValidity(X509 *cert, const char *server) STUB_RETVAL(false)
 int asn1timeToString(ASN1_TIME *tm, char *buf, int len) STUB_RETVAL(0)