From: Anirudh Ramachandran Date: Mon, 21 Mar 2016 23:25:46 +0000 (-0700) Subject: Add support for probabilistically choosing server ciphers X-Git-Tag: 2016.07.26~421 X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=commitdiff_plain;h=0eb9fa392447fbe054508439dbd8bad7ca3c80bc Add support for probabilistically choosing server ciphers Summary:Since SSLContextManager sets SSL_OP_CIPHER_SERVER_PREFERENCE on the SSL_CTX when it creates contexts, we may be unable to accommodate any clients who prefer a different ciphersuite. Having differently weighted cipher preference lists allows SSLContext to set a list with a different most-preferred cipher for some fraction of new handshakes. Note: resumption will work with the previously negotiated ciphersuite even if the server doesn't explicitly prefer/support it anymore, provided the cipher is supported in OpenSSL. Reviewed By: knekritz Differential Revision: D3050496 fb-gh-sync-id: 1c3b77ce3af87f939f8b8c6fe72b6a64eeaeeeb4 shipit-source-id: 1c3b77ce3af87f939f8b8c6fe72b6a64eeaeeeb4 --- diff --git a/folly/io/async/SSLContext.cpp b/folly/io/async/SSLContext.cpp index 5232b681..eb9920a7 100644 --- a/folly/io/async/SSLContext.cpp +++ b/folly/io/async/SSLContext.cpp @@ -86,9 +86,7 @@ SSLContext::SSLContext(SSLVersion version) { SSL_CTX_set_tlsext_servername_arg(ctx_, this); #endif -#ifdef OPENSSL_NPN_NEGOTIATED - Random::seed(nextProtocolPicker_); -#endif + Random::seed(randomGenerator_); } SSLContext::~SSLContext() { @@ -338,20 +336,44 @@ int SSLContext::baseServerNameOpenSSLCallback(SSL* ssl, int* al, void* data) { void SSLContext::switchCiphersIfTLS11( SSL* ssl, - const std::string& tls11CipherString) { - - CHECK(!tls11CipherString.empty()) << "Shouldn't call if empty alt ciphers"; + const std::string& tls11CipherString, + const std::vector>& tls11AltCipherlist) { + CHECK(!(tls11CipherString.empty() && tls11AltCipherlist.empty())) + << "Shouldn't call if empty ciphers / alt ciphers"; if (TLS1_get_client_version(ssl) <= TLS1_VERSION) { // We only do this for TLS v 1.1 and later return; } + const std::string* ciphers = &tls11CipherString; + if (!tls11AltCipherlist.empty()) { + if (!cipherListPicker_) { + std::vector weights; + std::for_each( + tls11AltCipherlist.begin(), + tls11AltCipherlist.end(), + [&](const std::pair& e) { + weights.push_back(e.second); + }); + cipherListPicker_.reset( + new std::discrete_distribution(weights.begin(), weights.end())); + } + auto index = (*cipherListPicker_)(randomGenerator_); + if ((size_t)index >= tls11AltCipherlist.size()) { + LOG(ERROR) << "Trying to pick alt TLS11 cipher index " << index + << ", but tls11AltCipherlist is of length " + << tls11AltCipherlist.size(); + } else { + ciphers = &tls11AltCipherlist[index].first; + } + } + // Prefer AES for TLS versions 1.1 and later since these are not // vulnerable to BEAST attacks on AES. Note that we're setting the // cipher list on the SSL object, not the SSL_CTX object, so it will // only last for this request. - int rc = SSL_set_cipher_list(ssl, tls11CipherString.c_str()); + int rc = SSL_set_cipher_list(ssl, ciphers->c_str()); if ((rc == 0) || ERR_peek_error() != 0) { // This shouldn't happen since we checked for this when proxygen // started up. @@ -477,7 +499,7 @@ void SSLContext::unsetNextProtocols() { size_t SSLContext::pickNextProtocols() { CHECK(!advertisedNextProtocols_.empty()) << "Failed to pickNextProtocols"; - return nextProtocolDistribution_(nextProtocolPicker_); + return nextProtocolDistribution_(randomGenerator_); } int SSLContext::advertisedNextProtocolCallback(SSL* ssl, diff --git a/folly/io/async/SSLContext.h b/folly/io/async/SSLContext.h index 65b572cf..7b1df4ad 100644 --- a/folly/io/async/SSLContext.h +++ b/folly/io/async/SSLContext.h @@ -431,11 +431,14 @@ class SSLContext { /** * We want to vary which cipher we'll use based on the client's TLS version. + * + * XXX: The refernces to tls11CipherString and tls11AltCipherlist are reused + * for * each >= TLS 1.1 handshake, so we expect these fields to not change. */ void switchCiphersIfTLS11( - SSL* ssl, - const std::string& tls11CipherString - ); + SSL* ssl, + const std::string& tls11CipherString, + const std::vector>& tls11AltCipherlist); bool checkPeerName() { return checkPeerName_; } std::string peerFixedName() { return peerFixedName_; } @@ -491,6 +494,11 @@ class SSLContext { static bool initialized_; + // Used in randomized next-proto pick / randomized cipherlist + Random::DefaultGenerator randomGenerator_; + // To provide control over choice of server ciphersuites + std::unique_ptr> cipherListPicker_; + #ifdef OPENSSL_NPN_NEGOTIATED struct AdvertisedNextProtocolsItem { @@ -504,7 +512,6 @@ class SSLContext { std::vector advertisedNextProtocols_; std::vector advertisedNextProtocolWeights_; std::discrete_distribution nextProtocolDistribution_; - Random::DefaultGenerator nextProtocolPicker_; static int sNextProtocolsExDataIndex_;