Commit 0eb9fa39 authored by Anirudh Ramachandran's avatar Anirudh Ramachandran Committed by Facebook Github Bot 4

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
parent 948ec49b
...@@ -86,9 +86,7 @@ SSLContext::SSLContext(SSLVersion version) { ...@@ -86,9 +86,7 @@ SSLContext::SSLContext(SSLVersion version) {
SSL_CTX_set_tlsext_servername_arg(ctx_, this); SSL_CTX_set_tlsext_servername_arg(ctx_, this);
#endif #endif
#ifdef OPENSSL_NPN_NEGOTIATED Random::seed(randomGenerator_);
Random::seed(nextProtocolPicker_);
#endif
} }
SSLContext::~SSLContext() { SSLContext::~SSLContext() {
...@@ -338,20 +336,44 @@ int SSLContext::baseServerNameOpenSSLCallback(SSL* ssl, int* al, void* data) { ...@@ -338,20 +336,44 @@ int SSLContext::baseServerNameOpenSSLCallback(SSL* ssl, int* al, void* data) {
void SSLContext::switchCiphersIfTLS11( void SSLContext::switchCiphersIfTLS11(
SSL* ssl, SSL* ssl,
const std::string& tls11CipherString) { const std::string& tls11CipherString,
const std::vector<std::pair<std::string, int>>& tls11AltCipherlist) {
CHECK(!tls11CipherString.empty()) << "Shouldn't call if empty alt ciphers"; CHECK(!(tls11CipherString.empty() && tls11AltCipherlist.empty()))
<< "Shouldn't call if empty ciphers / alt ciphers";
if (TLS1_get_client_version(ssl) <= TLS1_VERSION) { if (TLS1_get_client_version(ssl) <= TLS1_VERSION) {
// We only do this for TLS v 1.1 and later // We only do this for TLS v 1.1 and later
return; return;
} }
const std::string* ciphers = &tls11CipherString;
if (!tls11AltCipherlist.empty()) {
if (!cipherListPicker_) {
std::vector<int> weights;
std::for_each(
tls11AltCipherlist.begin(),
tls11AltCipherlist.end(),
[&](const std::pair<std::string, int>& e) {
weights.push_back(e.second);
});
cipherListPicker_.reset(
new std::discrete_distribution<int>(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 // 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 // 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 // cipher list on the SSL object, not the SSL_CTX object, so it will
// only last for this request. // 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) { if ((rc == 0) || ERR_peek_error() != 0) {
// This shouldn't happen since we checked for this when proxygen // This shouldn't happen since we checked for this when proxygen
// started up. // started up.
...@@ -477,7 +499,7 @@ void SSLContext::unsetNextProtocols() { ...@@ -477,7 +499,7 @@ void SSLContext::unsetNextProtocols() {
size_t SSLContext::pickNextProtocols() { size_t SSLContext::pickNextProtocols() {
CHECK(!advertisedNextProtocols_.empty()) << "Failed to pickNextProtocols"; CHECK(!advertisedNextProtocols_.empty()) << "Failed to pickNextProtocols";
return nextProtocolDistribution_(nextProtocolPicker_); return nextProtocolDistribution_(randomGenerator_);
} }
int SSLContext::advertisedNextProtocolCallback(SSL* ssl, int SSLContext::advertisedNextProtocolCallback(SSL* ssl,
......
...@@ -431,11 +431,14 @@ class SSLContext { ...@@ -431,11 +431,14 @@ class SSLContext {
/** /**
* We want to vary which cipher we'll use based on the client's TLS version. * 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( void switchCiphersIfTLS11(
SSL* ssl, SSL* ssl,
const std::string& tls11CipherString const std::string& tls11CipherString,
); const std::vector<std::pair<std::string, int>>& tls11AltCipherlist);
bool checkPeerName() { return checkPeerName_; } bool checkPeerName() { return checkPeerName_; }
std::string peerFixedName() { return peerFixedName_; } std::string peerFixedName() { return peerFixedName_; }
...@@ -491,6 +494,11 @@ class SSLContext { ...@@ -491,6 +494,11 @@ class SSLContext {
static bool initialized_; 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<std::discrete_distribution<int>> cipherListPicker_;
#ifdef OPENSSL_NPN_NEGOTIATED #ifdef OPENSSL_NPN_NEGOTIATED
struct AdvertisedNextProtocolsItem { struct AdvertisedNextProtocolsItem {
...@@ -504,7 +512,6 @@ class SSLContext { ...@@ -504,7 +512,6 @@ class SSLContext {
std::vector<AdvertisedNextProtocolsItem> advertisedNextProtocols_; std::vector<AdvertisedNextProtocolsItem> advertisedNextProtocols_;
std::vector<int> advertisedNextProtocolWeights_; std::vector<int> advertisedNextProtocolWeights_;
std::discrete_distribution<int> nextProtocolDistribution_; std::discrete_distribution<int> nextProtocolDistribution_;
Random::DefaultGenerator nextProtocolPicker_;
static int sNextProtocolsExDataIndex_; static int sNextProtocolsExDataIndex_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment