Commit 2fa292de authored by Sotirios Delimanolis's avatar Sotirios Delimanolis Committed by Facebook GitHub Bot

Set SSLContext minimum version through SSL_CTX_set_min_proto_version for OpenSSL >= 1.1.0

Summary:
Attempt to set minimum version of an OpenSSL context through `SSL_CTX_set_min_proto_version` instead of disabling them explicitly through options, as recommended here: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_min_proto_version.html

The test checks that the `SSLContext`'s `SSL_CTX` can keep a lower version regardless of the OpenSSL's configured minimum.

Reviewed By: yfeldblum

Differential Revision: D22338879

fbshipit-source-id: 356328c2ffba2a6a9d4243300a11fc823bc345d7
parent bff1bb83
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// SSLContext implementation // SSLContext implementation
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
namespace folly {
namespace { namespace {
int getExDataIndex() { int getExDataIndex() {
...@@ -36,51 +38,84 @@ int getExDataIndex() { ...@@ -36,51 +38,84 @@ int getExDataIndex() {
return index; return index;
} }
} // namespace /**
* Configure the given SSL context to use the given version.
namespace folly { */
void configureProtocolVersion(SSL_CTX* ctx, SSLContext::SSLVersion version) {
// #if FOLLY_OPENSSL_PREREQ(1, 1, 0)
// For OpenSSL portability API // Disable TLS 1.3 by default, for now, if this version of OpenSSL
// supports it. There are some semantic differences (e.g. assumptions
// SSLContext implementation // on getSession() returning a resumable session, SSL_CTX_set_ciphersuites,
SSLContext::SSLContext(SSLVersion version) { // etc.)
folly::ssl::init(); //
SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION);
ctx_ = SSL_CTX_new(SSLv23_method()); /*
if (ctx_ == nullptr) { * From the OpenSSL docs https://fburl.com/ii9k29qw:
throw std::runtime_error("SSL_CTX_new: " + getErrors()); * Setting the minimum or maximum version to 0, will enable protocol versions
* down to the lowest version, or up to the highest version supported by the
* library, respectively.
*
* We can use that as the default/fallback.
*/
int minVersion = 0;
switch (version) {
case SSLContext::SSLVersion::TLSv1:
minVersion = TLS1_VERSION;
break;
case SSLContext::SSLVersion::SSLv3:
minVersion = SSL3_VERSION;
break;
case SSLContext::SSLVersion::TLSv1_2:
minVersion = TLS1_2_VERSION;
break;
case SSLContext::SSLVersion::SSLv2:
default:
// do nothing
break;
} }
int setMinProtoResult = SSL_CTX_set_min_proto_version(ctx, minVersion);
DCHECK(setMinProtoResult == 1)
<< sformat("unsupported min TLS protocol version: 0x{:04x}", minVersion);
#else
int opt = 0; int opt = 0;
switch (version) { switch (version) {
case TLSv1: case SSLContext::SSLVersion::TLSv1:
opt = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; opt = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
break; break;
case SSLv3: case SSLContext::SSLVersion::SSLv3:
opt = SSL_OP_NO_SSLv2; opt = SSL_OP_NO_SSLv2;
break; break;
case TLSv1_2: case SSLContext::SSLVersion::TLSv1_2:
opt = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | opt = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 |
SSL_OP_NO_TLSv1_1; SSL_OP_NO_TLSv1_1;
break; break;
case SSLv2: case SSLContext::SSLVersion::SSLv2:
default: default:
// do nothing // do nothing
break; break;
} }
int newOpt = SSL_CTX_set_options(ctx, opt);
DCHECK((newOpt & opt) == opt);
#endif // FOLLY_OPENSSL_PREREQ(1, 1, 0)
}
// Disable TLS 1.3 by default, for now, if this version of OpenSSL } // namespace
// supports it. There are some semantic differences (e.g. assumptions
// on getSession() returning a resumable session, SSL_CTX_set_ciphersuites,
// etc.)
//
#if FOLLY_OPENSSL_IS_110
SSL_CTX_set_max_proto_version(ctx_, TLS1_2_VERSION);
#endif
int newOpt = SSL_CTX_set_options(ctx_, opt); //
DCHECK((newOpt & opt) == opt); // For OpenSSL portability API
// SSLContext implementation
SSLContext::SSLContext(SSLVersion version) {
folly::ssl::init();
ctx_ = SSL_CTX_new(SSLv23_method());
if (ctx_ == nullptr) {
throw std::runtime_error("SSL_CTX_new: " + getErrors());
}
// configure the TLS version used
configureProtocolVersion(ctx_, version);
SSL_CTX_set_mode(ctx_, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(ctx_, SSL_MODE_AUTO_RETRY);
......
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/FileUtil.h>
#include <folly/io/async/SSLContext.h>
#include <gtest/gtest.h>
#include "common/files/FileUtil.h"
using namespace facebook::files;
/*
* This test is meant to verify that SSLContext correctly sets its minimum
* protocol version and is not blocked by OpenSSL's default config.
*/
namespace folly {
// need OpenSSL version 1.1.1 for the SSL_CTX_get_min_proto_version function
// should reduce this prereq's scope if new tests are added that don't need it
#if FOLLY_OPENSSL_PREREQ(1, 1, 1)
/*
* The default OpenSSL config file contents for version OpenSSL 1.1.1c FIPS 28
* May 2019. This should set the MinProtocol to TLS 1.2 and SECLEVEL to 2.
*/
const std::string kOpenSSLConf = folly::stripLeftMargin(R"(
CipherString = @SECLEVEL=2:kEECDH:kRSA:kEDH:kPSK:kDHEPSK:kECDHEPSK:-aDSS:-3DES:!DES:!RC4:!RC2:!IDEA:-SEED:!eNULL:!aNULL:!MD5:-SHA384:-CAMELLIA:-ARIA:-AESCCM8
Ciphersuites = TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_SHA256
MinProtocol = TLSv1.2
)");
class SSLContextRegressionTest : public testing::Test {
public:
void SetUp() override {
TemporaryFile confFile(nullptr, "", true);
FileUtil::writeStringToFile(StringPiece(kOpenSSLConf), confFile.filename());
// simulate the system environment by loading a config file that should
// represent the CentOS 8 configuration
int result = CONF_modules_load_file(confFile.filename(), nullptr, 0);
ASSERT_EQ(result, 1) << "Failed to load OpenSSL conf from temporary file.";
}
};
// Tests that specifying a TLS version actually sets the underlying SSL
// context's minimum to that same version (and not the default in the config).
TEST_F(SSLContextRegressionTest, IsNotAffectedBySystemEnvironment) {
auto ctx = std::make_shared<SSLContext>(SSLContext::SSLVersion::TLSv1);
ASSERT_EQ(SSL_CTX_get_min_proto_version(ctx->getSSLCtx()), TLS1_VERSION);
}
#endif
} // namespace folly
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