]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Merged from trunk rev.14126
authorAmos Jeffries <squid3@treenet.co.nz>
Sun, 28 Jun 2015 15:02:53 +0000 (08:02 -0700)
committerAmos Jeffries <squid3@treenet.co.nz>
Sun, 28 Jun 2015 15:02:53 +0000 (08:02 -0700)
12 files changed:
1  2 
src/Makefile.am
src/SquidConfig.h
src/adaptation/icap/ServiceRep.cc
src/anyp/PortCfg.cc
src/anyp/PortCfg.h
src/cache_cf.cc
src/security/PeerOptions.cc
src/security/PeerOptions.h
src/ssl/support.cc
src/ssl/support.h
src/tests/stub_libsecurity.cc
src/tests/stub_libsslsquid.cc

diff --cc src/Makefile.am
index 4f2ee10fd1dfdd45bd58366836f30081eb261b4d,ecc95486a9fb86c4e04cef6bfd1adad60d4b7fe7..12dde6ec67c6a7b9f7cc1a00ee8ce2e4d0cced48
@@@ -611,7 -607,7 +607,6 @@@ squid_LDADD = 
        http/libsquid-http.la \
        parser/libsquid-parser.la \
        dns/libdns.la \
--      security/libsecurity.la \
        base/libbase.la \
        libsquid.la \
        ip/libip.la \
        ipc/libipc.la \
        mgr/libmgr.la \
        anyp/libanyp.la \
++      security/libsecurity.la \
        comm/libcomm.la \
        eui/libeui.la \
        icmp/libicmp.la icmp/libicmp-core.la \
@@@ -1254,9 -1247,7 +1248,8 @@@ tests_testACLMaxUserIP_SOURCES= 
        SquidList.h \
        SquidList.cc \
        mem_node.cc \
-       Packer.cc \
        Parsing.cc \
 +      tests/stub_libsecurity.cc \
        SquidMath.cc \
        StatCounters.cc \
        StatCounters.h \
index 340223396122d7eb28f010808ddfdab34751b6d5,f4c5a9777bf71ca735bb68a015e051e92fcf389c..22c9d3dd4e40c85255f103ed1a6ae526da286df7
  #include "DelayConfig.h"
  #include "helper/ChildConfig.h"
  #include "HttpHeaderTools.h"
- #include "icmp/IcmpConfig.h"
  #include "ip/Address.h"
  #include "Notes.h"
 +#include "security/forward.h"
  #include "YesNoNone.h"
  
  #if USE_OPENSSL
index 0c8053e9da599ba7bcc8ab17ef06508c073b6dfc,b7f5ea6acdb2175c801887c85593bed6cf077082..1c5b6088991e5a2b6442afbae5b92b51d095228e
@@@ -68,6 -79,14 +79,14 @@@ Adaptation::Icap::ServiceRep::finalize(
          }
      }
  
 -        sslContext = writeableCfg().secure.createContext(true);
+     if (cfg().protocol.caseCmp("icaps") == 0)
+         writeableCfg().secure.encryptTransport = true;
+     if (cfg().secure.encryptTransport) {
+         debugs(3, DBG_IMPORTANT, "Initializing service " << cfg().resource << " SSL context");
++        sslContext = writeableCfg().secure.createClientContext(true);
+     }
      theSessionFailures.configure(TheConfig.oldest_service_failure > 0 ?
                                   TheConfig.oldest_service_failure : -1);
  }
index a4facffdedcf6722e43db011ce4813bac10b7aa5,26a10e3f2f880f7220c149121b3ddb7ae176a8af..c3ea4e263fd71e9d9a9d0a04a8b5c950cbecb262
@@@ -44,9 -44,18 +44,10 @@@ AnyP::PortCfg::PortCfg() 
      disable_pmtu_discovery(0),
      listenConn()
  #if USE_OPENSSL
 -    ,cert(NULL),
 -    key(NULL),
 -    version(0),
 -    cipher(NULL),
 -    options(NULL),
 +    ,
      clientca(NULL),
 -    cafile(NULL),
 -    capath(NULL),
 -    crlfile(NULL),
      dhfile(NULL),
 -    sslflags(NULL),
+     tls_dh(NULL),
      sslContextSessionId(NULL),
      generateHostCertificates(false),
      dynamicCertMemCacheSize(std::numeric_limits<size_t>::max()),
      clientVerifyCrls(),
      clientCA(),
      dhParams(),
 -    contextMethod(),
 -    sslContextFlags(0),
 -    sslOptions(0)
+     eecdhCurve(NULL),
 +    contextMethod()
  #endif
  {
      memset(&tcp_keepalive, 0, sizeof(tcp_keepalive));
@@@ -76,9 -88,19 +78,11 @@@ AnyP::PortCfg::~PortCfg(
      safe_free(defaultsite);
  
  #if USE_OPENSSL
 -    safe_free(cert);
 -    safe_free(key);
 -    safe_free(cipher);
 -    safe_free(options);
      safe_free(clientca);
 -    safe_free(cafile);
 -    safe_free(capath);
 -    safe_free(crlfile);
      safe_free(dhfile);
 -    safe_free(sslflags);
+     safe_free(tls_dh);
      safe_free(sslContextSessionId);
+     safe_free(eecdhCurve);
  #endif
  }
  
@@@ -101,13 -123,31 +105,15 @@@ AnyP::PortCfg::clone() cons
      b->ftp_track_dirs = ftp_track_dirs;
      b->disable_pmtu_discovery = disable_pmtu_discovery;
      b->tcp_keepalive = tcp_keepalive;
 +    b->secure = secure;
  
  #if USE_OPENSSL
 -    if (cert)
 -        b->cert = xstrdup(cert);
 -    if (key)
 -        b->key = xstrdup(key);
 -    b->version = version;
 -    if (cipher)
 -        b->cipher = xstrdup(cipher);
 -    if (options)
 -        b->options = xstrdup(options);
      if (clientca)
          b->clientca = xstrdup(clientca);
 -    if (cafile)
 -        b->cafile = xstrdup(cafile);
 -    if (capath)
 -        b->capath = xstrdup(capath);
 -    if (crlfile)
 -        b->crlfile = xstrdup(crlfile);
      if (dhfile)
          b->dhfile = xstrdup(dhfile);
 -    if (sslflags)
 -        b->sslflags = xstrdup(sslflags);
+     if (tls_dh)
+         b->tls_dh = xstrdup(tls_dh);
      if (sslContextSessionId)
          b->sslContextSessionId = xstrdup(sslContextSessionId);
  
@@@ -155,13 -195,67 +161,31 @@@ AnyP::PortCfg::configureSslServerContex
          }
      }
  
-     contextMethod = Ssl::contextMethod(secure.sslVersion);
-     if (!contextMethod)
-         fatalf("Unable to compute context method to use");
 -    // backward compatibility hack for sslversion= configuration
 -    if (version > 2) {
 -        const char *add = NULL;
 -        switch (version) {
 -        case 3:
 -            add = "NO_TLSv1,NO_TLSv1_1,NO_TLSv1_2";
 -            break;
 -        case 4:
 -            add = "NO_SSLv3,NO_TLSv1_1,NO_TLSv1_2";
 -            break;
 -        case 5:
 -            add = "NO_SSLv3,NO_TLSv1,NO_TLSv1_2";
 -            break;
 -        case 6:
 -            add = "NO_SSLv3,NO_TLSv1,NO_TLSv1_1";
 -            break;
 -        default: // nothing
 -            break;
 -        }
 -        if (add) {
 -            SBuf tmpOpts;
 -            if (options) {
 -                tmpOpts.append(options, strlen(options));
 -                tmpOpts.append(",",1);
 -            }
 -            tmpOpts.append(add, strlen(add));
 -            xfree(options);
 -            options = xstrdup(tmpOpts.c_str());
 -        }
 -        version = 0; // prevent options being repeatedly appended
 -    }
 -
++    secure.updateTlsVersionLimits();
+ #if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+     contextMethod = TLS_server_method();
+ #else
+     contextMethod = SSLv23_server_method();
+ #endif
  
-     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);
 -
 -    sslOptions = Ssl::parse_options(options);
 -
      staticSslContext.reset(sslCreateServerContext(*this));
  
      if (!staticSslContext) {
index a2b3def9505bc45283777b293149dfc7cfca10a5,66670ebb432fe426fd6a6b14ce953537a5d7e682..7886f4748cc94a7ce3ac06a84b4bc0e7aa04767a
@@@ -69,12 -67,19 +69,13 @@@ public
       */
      Comm::ConnectionPointer listenConn;
  
 +    /// TLS configuration options for this listening port
 +    Security::PeerOptions secure;
 +
  #if USE_OPENSSL
 -    char *cert;
 -    char *key;
 -    int version;
 -    char *cipher;
 -    char *options;
      char *clientca;
 -    char *cafile;
 -    char *capath;
 -    char *crlfile;
      char *dhfile;
 -    char *sslflags;
+     char *tls_dh;
      char *sslContextSessionId; ///< "session id context" for staticSslContext
      bool generateHostCertificates; ///< dynamically make host cert for sslBump
      size_t dynamicCertMemCacheSize; ///< max size of generated certificates memory cache
      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
  #endif
  };
  
diff --cc src/cache_cf.cc
index 485fb5f1b0d2be5069e0030e81f2974c3c2e7c73,5c816bb5c3393f8894266603f86cfc97642f6590..444c7114676b7b65956c4a874ab00ec4b943ff0b
@@@ -3575,31 -3568,46 +3568,36 @@@ parse_port_option(AnyP::PortCfgPointer 
      } else if (strcmp(token, "ssl-bump") == 0) {
          s->flags.tunnelSslBumping = true;
      } else if (strncmp(token, "cert=", 5) == 0) {
 -        safe_free(s->cert);
 -        s->cert = xstrdup(token + 5);
 +        s->secure.parse(token);
      } else if (strncmp(token, "key=", 4) == 0) {
 -        safe_free(s->key);
 -        s->key = xstrdup(token + 4);
 +        s->secure.parse(token);
      } else if (strncmp(token, "version=", 8) == 0) {
 -        s->version = xatoi(token + 8);
 -        if (s->version < 1 || s->version > 6)
 -            self_destruct();
+         debugs(3, DBG_PARSE_NOTE(1), "UPGRADE WARNING: '" << token << "' is deprecated " <<
+                "in " << cfg_directive << ". Use 'options=' instead.");
-         if (s->secure.sslVersion < 1 || s->secure.sslVersion > 4)
-             self_destruct();
 +        s->secure.parse(token);
      } else if (strncmp(token, "options=", 8) == 0) {
 -        safe_free(s->options);
 -        s->options = xstrdup(token + 8);
 +        s->secure.parse(token);
      } else if (strncmp(token, "cipher=", 7) == 0) {
 -        safe_free(s->cipher);
 -        s->cipher = xstrdup(token + 7);
 +        s->secure.parse(token);
      } else if (strncmp(token, "clientca=", 9) == 0) {
          safe_free(s->clientca);
          s->clientca = xstrdup(token + 9);
      } else if (strncmp(token, "cafile=", 7) == 0) {
 -        safe_free(s->cafile);
 -        s->cafile = xstrdup(token + 7);
 +        s->secure.parse(token);
      } else if (strncmp(token, "capath=", 7) == 0) {
 -        safe_free(s->capath);
 -        s->capath = xstrdup(token + 7);
 +        s->secure.parse(token);
      } else if (strncmp(token, "crlfile=", 8) == 0) {
 -        safe_free(s->crlfile);
 -        s->crlfile = xstrdup(token + 8);
 +        s->secure.parse(token);
      } 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);
 +        s->secure.parse(token+3);
      } else if (strncmp(token, "sslcontext=", 11) == 0) {
          safe_free(s->sslContextSessionId);
          s->sslContextSessionId = xstrdup(token + 11);
@@@ -3794,36 -3800,37 +3792,36 @@@ dump_generic_port(StoreEntry * e, cons
  #if USE_OPENSSL
      if (s->flags.tunnelSslBumping)
          storeAppendPrintf(e, " ssl-bump");
 +#endif
  
 -    if (s->cert)
 -        storeAppendPrintf(e, " cert=%s", s->cert);
 +    if (!s->secure.certFile.isEmpty())
 +        storeAppendPrintf(e, " tls-cert=" SQUIDSBUFPH, SQUIDSBUFPRINT(s->secure.certFile));
  
 -    if (s->key)
 -        storeAppendPrintf(e, " key=%s", s->key);
 +    if (!s->secure.privateKeyFile.isEmpty() && s->secure.privateKeyFile != s->secure.certFile)
 +        storeAppendPrintf(e, " tls-key=" SQUIDSBUFPH, SQUIDSBUFPRINT(s->secure.privateKeyFile));
  
-     if (s->secure.sslVersion)
-         storeAppendPrintf(e, " tls-version=%d", s->secure.sslVersion);
 -    if (s->options)
 -        storeAppendPrintf(e, " options=%s", s->options);
 +    if (!s->secure.sslOptions.isEmpty())
 +        storeAppendPrintf(e, " tls-options=" SQUIDSBUFPH, SQUIDSBUFPRINT(s->secure.sslOptions));
  
 -    if (s->cipher)
 -        storeAppendPrintf(e, " cipher=%s", s->cipher);
 +    if (!s->secure.sslCipher.isEmpty())
 +        storeAppendPrintf(e, " tls-cipher=" SQUIDSBUFPH, SQUIDSBUFPRINT(s->secure.sslCipher));
  
 -    if (s->cafile)
 -        storeAppendPrintf(e, " cafile=%s", s->cafile);
 +    if (!s->secure.caFile.isEmpty())
 +        storeAppendPrintf(e, " tls-cafile=" SQUIDSBUFPH, SQUIDSBUFPRINT(s->secure.caFile));
  
 -    if (s->capath)
 -        storeAppendPrintf(e, " capath=%s", s->capath);
 +    if (!s->secure.caDir.isEmpty())
 +        storeAppendPrintf(e, " tls-capath=" SQUIDSBUFPH, SQUIDSBUFPRINT(s->secure.caDir));
  
 -    if (s->crlfile)
 -        storeAppendPrintf(e, " crlfile=%s", s->crlfile);
 +    if (!s->secure.crlFile.isEmpty())
 +        storeAppendPrintf(e, " tls-crlfile=" SQUIDSBUFPH, SQUIDSBUFPRINT(s->secure.crlFile));
  
 +#if USE_OPENSSL
      if (s->dhfile)
          storeAppendPrintf(e, " dhparams=%s", s->dhfile);
  
 -    if (s->sslflags)
 -        storeAppendPrintf(e, " sslflags=%s", s->sslflags);
 -
+     if (s->tls_dh)
+         storeAppendPrintf(e, " tls-dh=%s", s->tls_dh);
      if (s->sslContextSessionId)
          storeAppendPrintf(e, " sslcontext=%s", s->sslContextSessionId);
  
index 9aa8706fb0f931b88b4da286087e4bb35acdd0b4,577053755e395ac6256c3a6b39903797a0522d58..94e32741ca4484dc358778cd706753f386ef0f6e
@@@ -8,10 -8,9 +8,11 @@@
  
  #include "squid.h"
  #include "Debug.h"
 +#include "fatal.h"
  #include "globals.h"
+ #include "parser/Tokenizer.h"
  #include "Parsing.h"
 +#include "parser/Tokenizer.h"
  #include "security/PeerOptions.h"
  
  #if USE_OPENSSL
@@@ -81,269 -60,67 +85,324 @@@ Security::PeerOptions::parse(const cha
      }
  }
  
 -// XXX: make a GnuTLS variant
 -Security::ContextPointer
 -Security::PeerOptions::createContext(bool setOptions)
++void
++Security::PeerOptions::updateTlsVersionLimits()
+ {
 -    Security::ContextPointer t = NULL;
 -
+     if (!tlsMinVersion.isEmpty()) {
+         ::Parser::Tokenizer tok(tlsMinVersion);
+         int64_t v = 0;
+         if (tok.skip('1') && tok.skip('.') && tok.int64(v, 10, false, 1) && v <= 2) {
+             // only account for TLS here - SSL versions are handled by options= parameter
+             if (v > 0)
+                 sslOptions.append(",NO_TLSv1",9);
+             if (v > 1)
+                 sslOptions.append(",NO_TLSv1_1",11);
+             if (v > 2)
+                 sslOptions.append(",NO_TLSv1_2",11);
+         } else {
+             debugs(0, DBG_PARSE_NOTE(1), "WARNING: Unknown TLS minimum version: " << tlsMinVersion);
+         }
+     } else if (sslVersion > 2) {
+         // backward compatibility hack for sslversion= configuration
+         // only use if tls-min-version=N.N is not present
+         const char *add = NULL;
+         switch (sslVersion) {
+         case 3:
+             add = "NO_TLSv1,NO_TLSv1_1,NO_TLSv1_2";
+             break;
+         case 4:
+             add = "NO_SSLv3,NO_TLSv1_1,NO_TLSv1_2";
+             break;
+         case 5:
+             add = "NO_SSLv3,NO_TLSv1,NO_TLSv1_2";
+             break;
+         case 6:
+             add = "NO_SSLv3,NO_TLSv1,NO_TLSv1_1";
+             break;
+         default: // nothing
+             break;
+         }
+         if (add) {
+             if (!sslOptions.isEmpty())
+                 sslOptions.append(",",1);
+             sslOptions.append(add, strlen(add));
+         }
+         sslVersion = 0; // prevent sslOptions being repeatedly appended
+     }
++}
 +// XXX: make a GnuTLS variant
 +Security::ContextPointer
 +Security::PeerOptions::createClientContext(bool setOptions)
 +{
 +    Security::ContextPointer t = NULL;
 +
++    updateTlsVersionLimits();
  #if USE_OPENSSL
      // XXX: temporary performance regression. c_str() data copies and prevents this being a const method
-     t = sslCreateClientContext(certFile.c_str(), privateKeyFile.c_str(), sslVersion, sslCipher.c_str(),
-                                (setOptions ? parsedOptions : 0), parsedFlags, caFile.c_str(), caDir.c_str(), crlFile.c_str());
+     t = sslCreateClientContext(certFile.c_str(), privateKeyFile.c_str(), sslCipher.c_str(),
 -                               (setOptions ? sslOptions.c_str() : NULL), sslFlags.c_str(),
++                               (setOptions ? parsedOptions : 0), parsedFlags,
+                                caFile.c_str(), caDir.c_str(), crlFile.c_str());
  #endif
  
      return t;
  }
  
 +/// set of options we can parse and what they map to
 +static struct ssl_option {
 +    const char *name;
 +    long value;
 +
 +} ssl_options[] = {
 +
 +#if SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
 +    {
 +        "NETSCAPE_REUSE_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
 +    },
 +#endif
 +#if SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
 +    {
 +        "SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
 +    },
 +#endif
 +#if SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
 +    {
 +        "MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
 +    },
 +#endif
 +#if SSL_OP_SSLEAY_080_CLIENT_DH_BUG
 +    {
 +        "SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG
 +    },
 +#endif
 +#if SSL_OP_TLS_D5_BUG
 +    {
 +        "TLS_D5_BUG", SSL_OP_TLS_D5_BUG
 +    },
 +#endif
 +#if SSL_OP_TLS_BLOCK_PADDING_BUG
 +    {
 +        "TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG
 +    },
 +#endif
 +#if SSL_OP_TLS_ROLLBACK_BUG
 +    {
 +        "TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG
 +    },
 +#endif
 +#if SSL_OP_ALL
 +    {
 +        "ALL", (long)SSL_OP_ALL
 +    },
 +#endif
 +#if SSL_OP_SINGLE_DH_USE
 +    {
 +        "SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE
 +    },
 +#endif
 +#if SSL_OP_EPHEMERAL_RSA
 +    {
 +        "EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA
 +    },
 +#endif
 +#if SSL_OP_PKCS1_CHECK_1
 +    {
 +        "PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1
 +    },
 +#endif
 +#if SSL_OP_PKCS1_CHECK_2
 +    {
 +        "PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2
 +    },
 +#endif
 +#if SSL_OP_NETSCAPE_CA_DN_BUG
 +    {
 +        "NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG
 +    },
 +#endif
 +#if SSL_OP_NON_EXPORT_FIRST
 +    {
 +        "NON_EXPORT_FIRST", SSL_OP_NON_EXPORT_FIRST
 +    },
 +#endif
 +#if SSL_OP_CIPHER_SERVER_PREFERENCE
 +    {
 +        "CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE
 +    },
 +#endif
 +#if SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
 +    {
 +        "NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
 +    },
 +#endif
 +#if SSL_OP_NO_SSLv3
 +    {
 +        "NO_SSLv3", SSL_OP_NO_SSLv3
 +    },
 +#endif
 +#if SSL_OP_NO_TLSv1
 +    {
 +        "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
 +#if SSL_OP_NO_COMPRESSION
 +    {
 +        "No_Compression", SSL_OP_NO_COMPRESSION
 +    },
 +#endif
 +#if SSL_OP_NO_TICKET
 +    {
 +        "NO_TICKET", SSL_OP_NO_TICKET
 +    },
++#endif
++#if SSL_OP_SINGLE_ECDH_USE
++    {
++        "SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE
++    },
 +#endif
 +    {
 +        "", 0
 +    },
 +    {
 +        NULL, 0
 +    }
 +};
 +
 +long
 +Security::ParseOptions(const char *options)
 +{
 +    long op = 0;
 +    char *tmp;
 +    char *option;
 +
 +    if (options) {
 +
 +    tmp = xstrdup(options);
 +    option = strtok(tmp, ":,");
 +
 +    while (option) {
 +
 +        enum {
 +            MODE_ADD, MODE_REMOVE
 +        } mode;
 +
 +        switch (*option) {
 +
 +        case '!':
 +
 +        case '-':
 +            mode = MODE_REMOVE;
 +            ++option;
 +            break;
 +
 +        case '+':
 +            mode = MODE_ADD;
 +            ++option;
 +            break;
 +
 +        default:
 +            mode = MODE_ADD;
 +            break;
 +        }
 +
 +        struct ssl_option *opt = NULL;
 +        for (struct ssl_option *opttmp = ssl_options; opttmp->name; ++opttmp) {
 +            if (strcmp(opttmp->name, option) == 0) {
 +                opt = opttmp;
 +                break;
 +            }
 +        }
 +
 +        long value = 0;
 +        if (opt)
 +            value = opt->value;
 +        else if (strncmp(option, "0x", 2) == 0) {
 +            /* Special case.. hex specification */
 +            value = strtol(option + 2, NULL, 16);
 +        } else {
 +            fatalf("Unknown SSL option '%s'", option);
 +            value = 0;      /* Keep GCC happy */
 +        }
 +
 +        switch (mode) {
 +
 +        case MODE_ADD:
 +            op |= value;
 +            break;
 +
 +        case MODE_REMOVE:
 +            op &= ~value;
 +            break;
 +        }
 +
 +        option = strtok(NULL, ":,");
 +    }
 +
 +    safe_free(tmp);
 +    }
 +
 +#if SSL_OP_NO_SSLv2
 +    // compliance with RFC 6176: Prohibiting Secure Sockets Layer (SSL) Version 2.0
 +    op = op | SSL_OP_NO_SSLv2;
 +#endif
 +    return op;
 +}
 +
 +long
 +Security::ParseFlags(const SBuf &flags)
 +{
 +    if (flags.isEmpty())
 +        return 0;
 +
 +    static struct {
 +        SBuf label;
 +        long mask;
 +    } flagTokens[] = {
 +        { SBuf("NO_DEFAULT_CA"), SSL_FLAG_NO_DEFAULT_CA },
 +        { SBuf("DELAYED_AUTH"), SSL_FLAG_DELAYED_AUTH },
 +        { SBuf("DONT_VERIFY_PEER"), SSL_FLAG_DONT_VERIFY_PEER },
 +        { SBuf("DONT_VERIFY_DOMAIN"), SSL_FLAG_DONT_VERIFY_DOMAIN },
 +        { SBuf("NO_SESSION_REUSE"), SSL_FLAG_NO_SESSION_REUSE },
 +#if X509_V_FLAG_CRL_CHECK
 +        { SBuf("VERIFY_CRL"), SSL_FLAG_VERIFY_CRL },
 +        { SBuf("VERIFY_CRL_ALL"), SSL_FLAG_VERIFY_CRL_ALL },
 +#endif
 +        { SBuf(), 0 }
 +    };
 +
 +    ::Parser::Tokenizer tok(flags);
 +    static const CharacterSet delims("Flag-delimiter", ":,");
 +
 +    long fl = 0;
 +    do {
 +        long found = 0;
 +        for (size_t i = 0; flagTokens[i].mask; ++i) {
 +            if (tok.skip(flagTokens[i].label) == 0) {
 +                found = flagTokens[i].mask;
 +                break;
 +            }
 +        }
 +        if (!found)
 +            fatalf("Unknown SSL flag '" SQUIDSBUFPH "'", SQUIDSBUFPRINT(tok.remaining()));
 +        fl |= found;
 +    } while (tok.skipOne(delims));
 +
 +    return fl;
 +}
 +
  void
  parse_securePeerOptions(Security::PeerOptions *opt)
  {
index 15510c9088528991221cd3adefdd9713be85e69e,7defc9a862c03c7692e1b3dba4a75b3bc9e02fef..c05d02c4b93dd93ff2d0bcbb382a0e480dfd337b
@@@ -29,9 -28,9 +29,12 @@@ public
      /// reset the configuration details to default
      void clear() {*this = PeerOptions();}
  
 -    /// generate a security context from these configured options
 -    Security::ContextPointer createContext(bool setOptions);
 +    /// generate a security client-context from these configured options
 +    Security::ContextPointer createClientContext(bool setOptions);
 +
++    /// sync the context options with tls-min-version=N configuration
++    void updateTlsVersionLimits();
      SBuf certFile;       ///< path of file containing PEM format X509 certificate
      SBuf privateKeyFile; ///< path of file containing private key in PEM format
      SBuf sslOptions;     ///< library-specific options string
      SBuf crlFile;        ///< path of file containing Certificate Revoke List
  
      SBuf sslCipher;
 -    SBuf sslFlags;
 +    SBuf sslFlags;       ///< flags defining what TLS operations Squid performs
      SBuf sslDomain;
  
+     SBuf tlsMinVersion;  ///< version label for minimum TLS version to permit
      long parsedOptions; ///< parsed value of sslOptions
 +    long parsedFlags;   ///< parsed value of sslFlags
  
+ private:
      int sslVersion;
  
+ public:
      /// whether transport encryption (TLS/SSL) is to be used on connections to the peer
      bool encryptTransport;
  };
index 300dc8b14c803565b8f149e5da815435e90c2648,e2ea36d74c4b1532dae2571656a23aa7f157f28c..a7aaaa9479ae95550bf8ff15f1bf7e910ff30180
@@@ -542,8 -867,12 +592,12 @@@ static boo
  configureSslContext(SSL_CTX *sslContext, AnyP::PortCfg &port)
  {
      int ssl_error;
 -    SSL_CTX_set_options(sslContext, port.sslOptions);
 +    SSL_CTX_set_options(sslContext, port.secure.parsedOptions);
  
+ #if defined(SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS)
+     SSL_CTX_set_info_callback(sslContext, ssl_info_cb);
+ #endif
      if (port.sslContextSessionId)
          SSL_CTX_set_session_id_context(sslContext, (const unsigned char *)port.sslContextSessionId, strlen(port.sslContextSessionId));
  
      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;
 -    if ((cafile || port.capath) && !SSL_CTX_load_verify_locations(sslContext, cafile, port.capath)) {
 +    const char *cafile = port.secure.caFile.isEmpty() ? port.clientca : port.secure.caFile.c_str();
 +    if ((cafile || !port.secure.caDir.isEmpty()) && !SSL_CTX_load_verify_locations(sslContext, cafile, port.secure.caDir.c_str())) {
          ssl_error = ERR_get_error();
          debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate locations: " << ERR_error_string(ssl_error, NULL));
      }
  
      if (port.clientCA.get()) {
          ERR_clear_error();
-         SSL_CTX_set_client_CA_list(sslContext, port.clientCA.get());
+         if (STACK_OF(X509_NAME) *clientca = SSL_dup_CA_list(port.clientCA.get())) {
+             SSL_CTX_set_client_CA_list(sslContext, clientca);
+         } else {
+             ssl_error = ERR_get_error();
+             debugs(83, DBG_CRITICAL, "ERROR: Failed to dupe the client CA list: " << ERR_error_string(ssl_error, NULL));
+             return false;
+         }
  
 -        if (port.sslContextFlags & SSL_FLAG_DELAYED_AUTH) {
 +        if (port.secure.parsedFlags & 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 {
  SSL_CTX *
  sslCreateServerContext(AnyP::PortCfg &port)
  {
 -    int ssl_error;
 -    const char *keyfile, *certfile;
 -    certfile = port.cert;
 -    keyfile = port.key;
 -
      ssl_initialize();
 -
 -    if (!keyfile)
 -        keyfile = certfile;
 -
 -    if (!certfile)
 -        certfile = keyfile;
 -
      SSL_CTX *sslContext = SSL_CTX_new(port.contextMethod);
  
-     int ssl_error;
      if (sslContext == NULL) {
--        ssl_error = ERR_get_error();
++        int 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));
++        int ssl_error = ERR_get_error();
 +        debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire SSL certificate '" << port.secure.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));
++        int ssl_error = ERR_get_error();
 +        debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire SSL private key '" << port.secure.privateKeyFile << "': " << ERR_error_string(ssl_error, NULL));
          SSL_CTX_free(sslContext);
          return NULL;
      }
@@@ -716,141 -1072,48 +785,45 @@@ int Ssl::OpenSSLtoSquidSSLVersion(int s
          return 1;
  }
  
- #if OPENSSL_VERSION_NUMBER < 0x00909000L
- SSL_METHOD *
- #else
- const SSL_METHOD *
- #endif
- Ssl::method(int version)
+ #if defined(TLSEXT_TYPE_next_proto_neg)
+ //Dummy next_proto_neg callback
+ static int
+ ssl_next_proto_cb(SSL *s, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
  {
-     switch (version) {
-     case 2:
-         debugs(83, DBG_IMPORTANT, "SSLv2 is not available in this Proxy.");
-         return NULL;
-         break;
-     case 3:
-         debugs(83, 5, "Using SSLv3.");
-         return SSLv3_client_method();
-         break;
-     case 4:
-         debugs(83, 5, "Using TLSv1.");
-         return 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.");
-         return 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");
-         return TLSv1_2_client_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.");
-         return SSLv23_client_method();
-         break;
-     }
-     //Not reached
-     return NULL;
+     static const unsigned char supported_protos[] = {8, 'h','t','t', 'p', '/', '1', '.', '1'};
+     (void)SSL_select_next_proto(out, outlen, in, inlen, supported_protos, sizeof(supported_protos));
+     return SSL_TLSEXT_ERR_OK;
  }
- const SSL_METHOD *
- Ssl::serverMethod(int version)
- {
-     switch (version) {
-     case 2:
-         debugs(83, DBG_IMPORTANT, "SSLv2 is not available in this Proxy.");
-         return NULL;
-         break;
-     case 3:
-         debugs(83, 5, "Using SSLv3.");
-         return SSLv3_server_method();
-         break;
-     case 4:
-         debugs(83, 5, "Using TLSv1.");
-         return 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.");
-         return 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");
-         return 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.");
-         return SSLv23_server_method();
-         break;
-     }
-     //Not reached
-     return NULL;
- }
  
  SSL_CTX *
- sslCreateClientContext(const char *certfile, const char *keyfile, int version, const char *cipher, long options, long fl, const char *CAfile, const char *CApath, const char *CRLfile)
 -sslCreateClientContext(const char *certfile, const char *keyfile, const char *cipher, const char *options, const char *flags, const char *CAfile, const char *CApath, const char *CRLfile)
++sslCreateClientContext(const char *certfile, const char *keyfile, const char *cipher, long options, long fl, const char *CAfile, const char *CApath, const char *CRLfile)
  {
--    int ssl_error;
-     Ssl::ContextMethod method;
-     SSL_CTX * sslContext;
 -    long fl = Ssl::parse_flags(flags);
--
      ssl_initialize();
  
-     if (!(method = Ssl::method(version)))
-         return NULL;
-     sslContext = SSL_CTX_new(method);
+ #if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+     SSL_CTX *sslContext = SSL_CTX_new(TLS_client_method());
+ #else
+     SSL_CTX *sslContext = SSL_CTX_new(SSLv23_client_method());
+ #endif
  
      if (sslContext == NULL) {
--        ssl_error = ERR_get_error();
++        int ssl_error = ERR_get_error();
          fatalf("Failed to allocate SSL context: %s\n",
                 ERR_error_string(ssl_error, NULL));
      }
  
 -    SSL_CTX_set_options(sslContext, Ssl::parse_options(options));
 +    SSL_CTX_set_options(sslContext, options);
  
+ #if defined(SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS)
+     SSL_CTX_set_info_callback(sslContext, ssl_info_cb);
+ #endif
      if (*cipher) {
          debugs(83, 5, "Using chiper suite " << cipher << ".");
  
          if (!SSL_CTX_set_cipher_list(sslContext, cipher)) {
--            ssl_error = ERR_get_error();
++            int 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_IMPORTANT, "Using certificate in " << certfile);
  
          if (!SSL_CTX_use_certificate_chain_file(sslContext, certfile)) {
--            ssl_error = ERR_get_error();
++            int ssl_error = ERR_get_error();
              fatalf("Failed to acquire SSL certificate '%s': %s\n",
                     certfile, ERR_error_string(ssl_error, NULL));
          }
          ssl_ask_password(sslContext, keyfile);
  
          if (!SSL_CTX_use_PrivateKey_file(sslContext, keyfile, SSL_FILETYPE_PEM)) {
--            ssl_error = ERR_get_error();
++            int ssl_error = ERR_get_error();
              fatalf("Failed to acquire SSL private key '%s': %s\n",
                     keyfile, ERR_error_string(ssl_error, NULL));
          }
          debugs(83, 5, "Comparing private and public SSL keys.");
  
          if (!SSL_CTX_check_private_key(sslContext)) {
--            ssl_error = ERR_get_error();
++            int ssl_error = ERR_get_error();
              fatalf("SSL private key '%s' does not match public key '%s': %s\n",
                     certfile, keyfile, ERR_error_string(ssl_error, NULL));
          }
      debugs(83, 9, "Setting CA certificate locations.");
  
      if ((*CAfile || *CApath) && !SSL_CTX_load_verify_locations(sslContext, CAfile, CApath)) {
--        ssl_error = ERR_get_error();
++        int 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) &&
              !SSL_CTX_set_default_verify_paths(sslContext)) {
--        ssl_error = ERR_get_error();
++        int ssl_error = ERR_get_error();
          debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting default CA certificate location: " << ERR_error_string(ssl_error, NULL));
      }
  
index 64a9d76a1b2cce2a0b99282e4c71cfc1371cc6cc,6e9e3237bee450b3a9ef0bfa2491f99cc87d47fe..681e0da4dcf445f24e4cc466ed1e2ccf93b96cac
@@@ -92,7 -92,7 +92,7 @@@ typedef CbDataList<Ssl::CertError> Cert
  SSL_CTX *sslCreateServerContext(AnyP::PortCfg &port);
  
  /// \ingroup ServerProtocolSSLAPI
- SSL_CTX *sslCreateClientContext(const char *certfile, const char *keyfile, int version, const char *cipher, long options, long flags, const char *CAfile, const char *CApath, const char *CRLfile);
 -SSL_CTX *sslCreateClientContext(const char *certfile, const char *keyfile, const char *cipher, const char *options, const char *flags, const char *CAfile, const char *CApath, const char *CRLfile);
++SSL_CTX *sslCreateClientContext(const char *certfile, const char *keyfile, const char *cipher, long options, long flags, const char *CAfile, const char *CApath, const char *CRLfile);
  
  /// \ingroup ServerProtocolSSLAPI
  int ssl_read_method(int, char *, int);
index b274c3b4b35aa1df67dab69f8dae83a1a14df6fe,ec7903251ed75ddb3f6c91a2ab5091e3772ffb86..32cce654b4ed49e0e5b885eb0685f14184364942
@@@ -19,8 -19,6 +19,9 @@@ std::ostream &Security::operator <<(std
  #include "security/PeerOptions.h"
  Security::PeerOptions Security::ProxyOutgoingConfig;
  void Security::PeerOptions::parse(char const*) STUB
 -Security::ContextPointer Security::PeerOptions::createContext(bool) STUB_RETVAL(NULL)
 +Security::ContextPointer Security::PeerOptions::createClientContext(bool) STUB_RETVAL(NULL)
++void Security::PeerOptions::updateTlsVersionLimits() STUB
  void parse_securePeerOptions(Security::PeerOptions *) STUB
 +long Security::ParseOptions(const char *) STUB_RETVAL(0)
 +long Security::ParseFlags(const SBuf &) STUB_RETVAL(0)
  
index 2d131b772bee9ee10ab46be512cfbe2a2227b5bc,fe524951544d9541a3e859490e3102b6843175e4..82c55ed490f97a0a9ab8807b089b7df21a9f6c27
@@@ -57,7 -57,7 +57,7 @@@ bool CertError::operator == (const Cert
  bool CertError::operator != (const CertError &ce) const STUB_RETVAL(false)
  } // namespace Ssl
  SSL_CTX *sslCreateServerContext(AnyP::PortCfg &port) STUB_RETVAL(NULL)
- SSL_CTX *sslCreateClientContext(const char *certfile, const char *keyfile, int version, const char *cipher, long options, const char *flags, const char *CAfile, const char *CApath, const char *CRLfile) STUB_RETVAL(NULL)
 -SSL_CTX *sslCreateClientContext(const char *certfile, const char *keyfile, const char *cipher, const char *options, const char *flags, const char *CAfile, const char *CApath, const char *CRLfile) STUB_RETVAL(NULL)
++SSL_CTX *sslCreateClientContext(const char *certfile, const char *keyfile, const char *cipher, long options, const char *flags, const char *CAfile, const char *CApath, const char *CRLfile) STUB_RETVAL(NULL)
  int ssl_read_method(int, char *, int) STUB_RETVAL(0)
  int ssl_write_method(int, const char *, int) STUB_RETVAL(0)
  void ssl_shutdown_method(SSL *ssl) STUB
@@@ -73,9 -73,9 +73,8 @@@ namespace Ss
  //GETX509ATTRIBUTE GetX509Fingerprint;
  const char *BumpModeStr[] = {""};
  long parse_flags(const char *flags) STUB_RETVAL(0)
 -long parse_options(const char *options) STUB_RETVAL(0)
  STACK_OF(X509_CRL) *loadCrl(const char *CRLFile, long &flags) STUB_RETVAL(NULL)
  DH *readDHParams(const char *dhfile) STUB_RETVAL(NULL)
- ContextMethod contextMethod(int version) STUB_RETVAL(ContextMethod())
  bool generateUntrustedCert(X509_Pointer & untrustedCert, EVP_PKEY_Pointer & untrustedPkey, X509_Pointer const & cert, EVP_PKEY_Pointer const & pkey) STUB_RETVAL(false)
  SSL_CTX * generateSslContext(CertificateProperties const &properties, AnyP::PortCfg &port) STUB_RETVAL(NULL)
  bool verifySslCertificate(SSL_CTX * sslContext,  CertificateProperties const &properties) STUB_RETVAL(false)