]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Support Ephemeral Elliptic Curve Diffie-Hellman (EECDH) key exchange
authorPaulo Matias <matias@ufscar.br>
Tue, 22 Dec 2015 07:54:23 +0000 (20:54 +1300)
committerAmos Jeffries <squid3@treenet.co.nz>
Tue, 22 Dec 2015 07:54:23 +0000 (20:54 +1300)
Which allows for forward secrecy with better performance than traditional
ephemeral DH.

Also replaces http(s)_port dhparams= option with tls-dh= that takes a
curve name as well as filename for curve parameters.

src/anyp/PortCfg.cc
src/anyp/PortCfg.h
src/cache_cf.cc
src/cf.data.pre
src/ssl/support.cc

index d2299244ac90e4b490daba9100ba4ded846d5a9a..fdefc94afecc0ff9383e8d7231befdaf25a1024f 100644 (file)
@@ -53,6 +53,7 @@ AnyP::PortCfg::PortCfg() :
     capath(NULL),
     crlfile(NULL),
     dhfile(NULL),
+    tls_dh(NULL),
     sslflags(NULL),
     sslContextSessionId(NULL),
     generateHostCertificates(false),
@@ -66,6 +67,7 @@ AnyP::PortCfg::PortCfg() :
     clientVerifyCrls(),
     clientCA(),
     dhParams(),
+    eecdhCurve(NULL),
     contextMethod(),
     sslContextFlags(0),
     sslOptions(0)
@@ -94,8 +96,10 @@ AnyP::PortCfg::~PortCfg()
     safe_free(capath);
     safe_free(crlfile);
     safe_free(dhfile);
+    safe_free(tls_dh);
     safe_free(sslflags);
     safe_free(sslContextSessionId);
+    safe_free(eecdhCurve);
 #endif
 }
 
@@ -139,6 +143,8 @@ AnyP::PortCfg::clone() const
         b->crlfile = xstrdup(crlfile);
     if (dhfile)
         b->dhfile = xstrdup(dhfile);
+    if (tls_dh)
+        b->tls_dh = xstrdup(tls_dh);
     if (sslflags)
         b->sslflags = xstrdup(sslflags);
     if (sslContextSessionId)
@@ -192,8 +198,23 @@ AnyP::PortCfg::configureSslServerContext()
     if (!contextMethod)
         fatalf("Unable to compute context method to use");
 
-    if (dhfile)
-        dhParams.reset(Ssl::readDHParams(dhfile));
+    const char *dhParamsFile = dhfile; // backward compatibility for dhparams= configuration
+    safe_free(eecdhCurve); // clear any previous EECDH configuration
+    if (tls_dh && *tls_dh) {
+        eecdhCurve = xstrdup(tls_dh);
+        char *p = strchr(eecdhCurve, ':');
+        if (p) {  // tls-dh=eecdhCurve:dhParamsFile
+            *p = '\0';
+            dhParamsFile = p+1;
+        } else {  // tls-dh=dhParamsFile
+            dhParamsFile = tls_dh;
+            // a NULL eecdhCurve means "do not use EECDH"
+            safe_free(eecdhCurve);
+        }
+    }
+
+    if (dhParamsFile && *dhParamsFile)
+        dhParams.reset(Ssl::readDHParams(dhParamsFile));
 
     if (sslflags)
         sslContextFlags = Ssl::parse_flags(sslflags);
index f4f3d9e6f4ca71d12f86410f374ad03ab8297230..66670ebb432fe426fd6a6b14ce953537a5d7e682 100644 (file)
@@ -78,6 +78,7 @@ public:
     char *capath;
     char *crlfile;
     char *dhfile;
+    char *tls_dh;
     char *sslflags;
     char *sslContextSessionId; ///< "session id context" for staticSslContext
     bool generateHostCertificates; ///< dynamically make host cert for sslBump
@@ -93,6 +94,7 @@ public:
     Ssl::X509_CRL_STACK_Pointer clientVerifyCrls; ///< additional CRL lists to use when verifying the client certificate
     Ssl::X509_NAME_STACK_Pointer clientCA; ///< CA certificates to use when verifying client certificates
     Ssl::DH_Pointer dhParams; ///< DH parameters for temporary/ephemeral DH key exchanges
+    char *eecdhCurve; ///< Elliptic curve for ephemeral EC-based DH key exchanges
     Ssl::ContextMethod contextMethod; ///< The context method (SSL_METHOD) to use when creating certificates
     long sslContextFlags; ///< flags modifying the use of SSL
     long sslOptions; ///< SSL engine options
index 2e36f569233774c7d5e437ba361db8b7ebb8123b..5723efa3bd154229b5960a61fd4d54d6bdb2a973 100644 (file)
@@ -3754,8 +3754,13 @@ parse_port_option(AnyP::PortCfgPointer &s, char *token)
         safe_free(s->crlfile);
         s->crlfile = xstrdup(token + 8);
     } else if (strncmp(token, "dhparams=", 9) == 0) {
+        debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: '" << token << "' is deprecated " <<
+               "in " << cfg_directive << ". Use 'tls-dh=' instead.");
         safe_free(s->dhfile);
         s->dhfile = xstrdup(token + 9);
+    } else if (strncmp(token, "tls-dh=", 7) == 0) {
+        safe_free(s->tls_dh);
+        s->tls_dh = xstrdup(token + 7);
     } else if (strncmp(token, "sslflags=", 9) == 0) {
         safe_free(s->sslflags);
         s->sslflags = xstrdup(token + 9);
@@ -3979,6 +3984,9 @@ dump_generic_port(StoreEntry * e, const char *n, const AnyP::PortCfgPointer &s)
     if (s->dhfile)
         storeAppendPrintf(e, " dhparams=%s", s->dhfile);
 
+    if (s->tls_dh)
+        storeAppendPrintf(e, " tls-dh=%s", s->tls_dh);
+
     if (s->sslflags)
         storeAppendPrintf(e, " sslflags=%s", s->sslflags);
 
index a2e7a1eadc0012fc672c2042f65e9c2fb39ed317..f603278a337dd30d501a06e7215ad7dbf6a33888 100644 (file)
@@ -1824,6 +1824,12 @@ DOC_START
                            SINGLE_DH_USE Always create a new key when using
                                      temporary/ephemeral DH key exchanges
                            NO_TICKET Disables TLS tickets extension
+
+                           SINGLE_ECDH_USE
+                                     Enable ephemeral ECDH key exchange.
+                                     The adopted curve should be specified
+                                     using the tls-dh option.
+
                            ALL       Enable various bug workarounds
                                      suggested as "harmless" by OpenSSL
                                      Be warned that this reduces SSL/TLS
@@ -1845,11 +1851,15 @@ DOC_START
                        the client certificate, in addition to CRLs stored in
                        the capath. Implies VERIFY_CRL flag below.
 
-          dhparams=    File containing DH parameters for temporary/ephemeral
-                       DH key exchanges. See OpenSSL documentation for details
-                       on how to create this file.
-                       WARNING: EDH ciphers will be silently disabled if this
-                                option is not set.
+          tls-dh=[curve:]file
+                       File containing DH parameters for temporary/ephemeral DH key
+                       exchanges, optionally prefixed by a curve for ephemeral ECDH
+                       key exchanges.
+                       See OpenSSL documentation for details on how to create the
+                       DH parameter file. Supported curves for ECDH can be listed
+                       using the "openssl ecparam -list_curves" command.
+                       WARNING: EDH and EECDH ciphers will be silently disabled if
+                                this option is not set.
 
           sslflags=    Various flags modifying the use of SSL:
                            DELAYED_AUTH
@@ -1988,8 +1998,15 @@ DOC_START
                            NO_SSLv2  Disallow the use of SSLv2
                            NO_SSLv3  Disallow the use of SSLv3
                            NO_TLSv1  Disallow the use of TLSv1
+
                            SINGLE_DH_USE Always create a new key when using
                                      temporary/ephemeral DH key exchanges
+
+                           SINGLE_ECDH_USE
+                                     Enable ephemeral ECDH key exchange.
+                                     The adopted curve should be specified
+                                     using the tls-dh option.
+
                        See src/ssl_support.c or OpenSSL SSL_CTX_set_options
                        documentation for a complete list of options.
 
@@ -2007,8 +2024,10 @@ DOC_START
                        the client certificate, in addition to CRLs stored in
                        the capath. Implies VERIFY_CRL flag below.
 
-          dhparams=    File containing DH parameters for temporary/ephemeral
-                       DH key exchanges.
+          tls-dh=[curve:]file
+                       File containing DH parameters for temporary/ephemeral DH key
+                       exchanges, optionally prefixed by a curve for ephemeral ECDH
+                       key exchanges.
 
           sslflags=    Various flags modifying the use of SSL:
                            DELAYED_AUTH
index 76d99c219a7f58879606536ea44f9eba5cb4019d..de878b243448d2caf30abf979e66e29e31cc3881 100644 (file)
@@ -492,6 +492,11 @@ ssl_options[] = {
     {
         "NO_TICKET", SSL_OP_NO_TICKET
     },
+#endif
+#if SSL_OP_SINGLE_ECDH_USE
+    {
+        "SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE
+    },
 #endif
     {
         "", 0
@@ -852,6 +857,29 @@ ssl_info_cb(const SSL *ssl, int where, int ret)
 }
 #endif
 
+static bool
+configureSslEECDH(SSL_CTX *sslContext, const char *curve)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH)
+    int nid = OBJ_sn2nid(curve);
+    if (!nid) {
+        debugs(83, DBG_CRITICAL, "ERROR: Unknown EECDH curve '" << curve << "'");
+        return false;
+    }
+
+    EC_KEY *ecdh = EC_KEY_new_by_curve_name(nid);
+    if (ecdh == NULL)
+        return false;
+
+    const bool ok = SSL_CTX_set_tmp_ecdh(sslContext, ecdh) != 0;
+    EC_KEY_free(ecdh);
+    return ok;
+#else
+    debugs(83, DBG_CRITICAL, "ERROR: EECDH is not available in this build. Please link against OpenSSL>=0.9.8 and ensure OPENSSL_NO_ECDH is not set.");
+    return false;
+#endif
+}
+
 static bool
 configureSslContext(SSL_CTX *sslContext, AnyP::PortCfg &port)
 {
@@ -888,6 +916,16 @@ configureSslContext(SSL_CTX *sslContext, AnyP::PortCfg &port)
     debugs(83, 9, "Setting RSA key generation callback.");
     SSL_CTX_set_tmp_rsa_callback(sslContext, ssl_temp_rsa_cb);
 
+    if (port.eecdhCurve) {
+        debugs(83, 9, "Setting Ephemeral ECDH curve to " << port.eecdhCurve << ".");
+
+        if (!configureSslEECDH(sslContext, port.eecdhCurve)) {
+            ssl_error = ERR_get_error();
+            debugs(83, DBG_CRITICAL, "ERROR: Unable to configure Ephemeral ECDH: " << ERR_error_string(ssl_error, NULL));
+            return false;
+        }
+    }
+
     debugs(83, 9, "Setting CA certificate locations.");
 
     const char *cafile = port.cafile ? port.cafile : port.clientca;