664 lines
19 KiB
C++
664 lines
19 KiB
C++
/*
|
|
* 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/io/async/SSLContext.h>
|
|
|
|
#include <folly/Format.h>
|
|
#include <folly/Memory.h>
|
|
#include <folly/Random.h>
|
|
#include <folly/SharedMutex.h>
|
|
#include <folly/SpinLock.h>
|
|
#include <folly/ssl/Init.h>
|
|
#include <folly/system/ThreadId.h>
|
|
|
|
// ---------------------------------------------------------------------
|
|
// SSLContext implementation
|
|
// ---------------------------------------------------------------------
|
|
namespace folly {
|
|
//
|
|
// 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());
|
|
}
|
|
|
|
int opt = 0;
|
|
switch (version) {
|
|
case TLSv1:
|
|
opt = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
|
|
break;
|
|
case SSLv3:
|
|
opt = SSL_OP_NO_SSLv2;
|
|
break;
|
|
case TLSv1_2:
|
|
opt = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 |
|
|
SSL_OP_NO_TLSv1_1;
|
|
break;
|
|
case SSLv2:
|
|
default:
|
|
// do nothing
|
|
break;
|
|
}
|
|
|
|
// Disable TLS 1.3 by default, for now, if this version of OpenSSL
|
|
// 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);
|
|
|
|
SSL_CTX_set_mode(ctx_, SSL_MODE_AUTO_RETRY);
|
|
|
|
checkPeerName_ = false;
|
|
|
|
SSL_CTX_set_options(ctx_, SSL_OP_NO_COMPRESSION);
|
|
|
|
sslAcceptRunner_ = std::make_unique<SSLAcceptRunner>();
|
|
|
|
#if FOLLY_OPENSSL_HAS_SNI
|
|
SSL_CTX_set_tlsext_servername_callback(ctx_, baseServerNameOpenSSLCallback);
|
|
SSL_CTX_set_tlsext_servername_arg(ctx_, this);
|
|
#endif
|
|
}
|
|
|
|
SSLContext::~SSLContext() {
|
|
if (ctx_ != nullptr) {
|
|
SSL_CTX_free(ctx_);
|
|
ctx_ = nullptr;
|
|
}
|
|
|
|
#if FOLLY_OPENSSL_HAS_ALPN
|
|
deleteNextProtocolsStrings();
|
|
#endif
|
|
}
|
|
|
|
void SSLContext::ciphers(const std::string& ciphers) {
|
|
setCiphersOrThrow(ciphers);
|
|
}
|
|
|
|
void SSLContext::setClientECCurvesList(
|
|
const std::vector<std::string>& ecCurves) {
|
|
if (ecCurves.empty()) {
|
|
return;
|
|
}
|
|
#if OPENSSL_VERSION_NUMBER >= 0x1000200fL
|
|
std::string ecCurvesList;
|
|
join(":", ecCurves, ecCurvesList);
|
|
int rc = SSL_CTX_set1_curves_list(ctx_, ecCurvesList.c_str());
|
|
if (rc == 0) {
|
|
throw std::runtime_error("SSL_CTX_set1_curves_list " + getErrors());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void SSLContext::setServerECCurve(const std::string& curveName) {
|
|
#if OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH)
|
|
EC_KEY* ecdh = nullptr;
|
|
int nid;
|
|
|
|
/*
|
|
* Elliptic-Curve Diffie-Hellman parameters are either "named curves"
|
|
* from RFC 4492 section 5.1.1, or explicitly described curves over
|
|
* binary fields. OpenSSL only supports the "named curves", which provide
|
|
* maximum interoperability.
|
|
*/
|
|
|
|
nid = OBJ_sn2nid(curveName.c_str());
|
|
if (nid == 0) {
|
|
LOG(FATAL) << "Unknown curve name:" << curveName.c_str();
|
|
}
|
|
ecdh = EC_KEY_new_by_curve_name(nid);
|
|
if (ecdh == nullptr) {
|
|
LOG(FATAL) << "Unable to create curve:" << curveName.c_str();
|
|
}
|
|
|
|
SSL_CTX_set_tmp_ecdh(ctx_, ecdh);
|
|
EC_KEY_free(ecdh);
|
|
#else
|
|
throw std::runtime_error("Elliptic curve encryption not allowed");
|
|
#endif
|
|
}
|
|
|
|
SSLContext::SSLContext(SSL_CTX* ctx) : ctx_(ctx) {
|
|
if (SSL_CTX_up_ref(ctx) == 0) {
|
|
throw std::runtime_error("Failed to increment SSL_CTX refcount");
|
|
}
|
|
}
|
|
|
|
void SSLContext::setX509VerifyParam(
|
|
const ssl::X509VerifyParam& x509VerifyParam) {
|
|
if (!x509VerifyParam) {
|
|
return;
|
|
}
|
|
if (SSL_CTX_set1_param(ctx_, x509VerifyParam.get()) != 1) {
|
|
throw std::runtime_error("SSL_CTX_set1_param " + getErrors());
|
|
}
|
|
}
|
|
|
|
void SSLContext::setCiphersOrThrow(const std::string& ciphers) {
|
|
int rc = SSL_CTX_set_cipher_list(ctx_, ciphers.c_str());
|
|
if (rc == 0) {
|
|
throw std::runtime_error("SSL_CTX_set_cipher_list: " + getErrors());
|
|
}
|
|
providedCiphersString_ = ciphers;
|
|
}
|
|
|
|
void SSLContext::setVerificationOption(
|
|
const SSLContext::SSLVerifyPeerEnum& verifyPeer) {
|
|
CHECK(verifyPeer != SSLVerifyPeerEnum::USE_CTX); // dont recurse
|
|
verifyPeer_ = verifyPeer;
|
|
}
|
|
|
|
int SSLContext::getVerificationMode(
|
|
const SSLContext::SSLVerifyPeerEnum& verifyPeer) {
|
|
CHECK(verifyPeer != SSLVerifyPeerEnum::USE_CTX);
|
|
int mode = SSL_VERIFY_NONE;
|
|
switch (verifyPeer) {
|
|
// case SSLVerifyPeerEnum::USE_CTX: // can't happen
|
|
// break;
|
|
|
|
case SSLVerifyPeerEnum::VERIFY:
|
|
mode = SSL_VERIFY_PEER;
|
|
break;
|
|
|
|
case SSLVerifyPeerEnum::VERIFY_REQ_CLIENT_CERT:
|
|
mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
|
|
break;
|
|
|
|
case SSLVerifyPeerEnum::NO_VERIFY:
|
|
mode = SSL_VERIFY_NONE;
|
|
break;
|
|
case SSLVerifyPeerEnum::USE_CTX:
|
|
default:
|
|
break;
|
|
}
|
|
return mode;
|
|
}
|
|
|
|
int SSLContext::getVerificationMode() {
|
|
return getVerificationMode(verifyPeer_);
|
|
}
|
|
|
|
void SSLContext::authenticate(
|
|
bool checkPeerCert,
|
|
bool checkPeerName,
|
|
const std::string& peerName) {
|
|
int mode;
|
|
if (checkPeerCert) {
|
|
mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
|
|
SSL_VERIFY_CLIENT_ONCE;
|
|
checkPeerName_ = checkPeerName;
|
|
peerFixedName_ = peerName;
|
|
} else {
|
|
mode = SSL_VERIFY_NONE;
|
|
checkPeerName_ = false; // can't check name without cert!
|
|
peerFixedName_.clear();
|
|
}
|
|
SSL_CTX_set_verify(ctx_, mode, nullptr);
|
|
}
|
|
|
|
void SSLContext::loadCertificate(const char* path, const char* format) {
|
|
if (path == nullptr || format == nullptr) {
|
|
throw std::invalid_argument(
|
|
"loadCertificateChain: either <path> or <format> is nullptr");
|
|
}
|
|
if (strcmp(format, "PEM") == 0) {
|
|
if (SSL_CTX_use_certificate_chain_file(ctx_, path) != 1) {
|
|
int errnoCopy = errno;
|
|
std::string reason("SSL_CTX_use_certificate_chain_file: ");
|
|
reason.append(path);
|
|
reason.append(": ");
|
|
reason.append(getErrors(errnoCopy));
|
|
throw std::runtime_error(reason);
|
|
}
|
|
} else {
|
|
throw std::runtime_error(
|
|
"Unsupported certificate format: " + std::string(format));
|
|
}
|
|
}
|
|
|
|
void SSLContext::loadCertificateFromBufferPEM(folly::StringPiece cert) {
|
|
if (cert.data() == nullptr) {
|
|
throw std::invalid_argument("loadCertificate: <cert> is nullptr");
|
|
}
|
|
|
|
ssl::BioUniquePtr bio(BIO_new(BIO_s_mem()));
|
|
if (bio == nullptr) {
|
|
throw std::runtime_error("BIO_new: " + getErrors());
|
|
}
|
|
|
|
int written = BIO_write(bio.get(), cert.data(), int(cert.size()));
|
|
if (written <= 0 || static_cast<unsigned>(written) != cert.size()) {
|
|
throw std::runtime_error("BIO_write: " + getErrors());
|
|
}
|
|
|
|
ssl::X509UniquePtr x509(
|
|
PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
|
|
if (x509 == nullptr) {
|
|
throw std::runtime_error("PEM_read_bio_X509: " + getErrors());
|
|
}
|
|
|
|
if (SSL_CTX_use_certificate(ctx_, x509.get()) == 0) {
|
|
throw std::runtime_error("SSL_CTX_use_certificate: " + getErrors());
|
|
}
|
|
|
|
// Any further X509 PEM blocks are treated as additional certificates in
|
|
// the certificate chain.
|
|
constexpr size_t kMaxCertChain = 64;
|
|
|
|
for (size_t i = 0; i < kMaxCertChain; i++) {
|
|
x509.reset(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
|
|
if (x509 == nullptr) {
|
|
ERR_clear_error();
|
|
return;
|
|
}
|
|
|
|
if (SSL_CTX_add1_chain_cert(ctx_, x509.get()) == 0) {
|
|
throw std::runtime_error("SSL_CTX_add0_chain_cert: " + getErrors());
|
|
}
|
|
}
|
|
|
|
throw std::runtime_error(
|
|
"loadCertificateFromBufferPEM(): Too many certificates in chain");
|
|
}
|
|
|
|
void SSLContext::loadPrivateKey(const char* path, const char* format) {
|
|
if (path == nullptr || format == nullptr) {
|
|
throw std::invalid_argument(
|
|
"loadPrivateKey: either <path> or <format> is nullptr");
|
|
}
|
|
if (strcmp(format, "PEM") == 0) {
|
|
if (SSL_CTX_use_PrivateKey_file(ctx_, path, SSL_FILETYPE_PEM) == 0) {
|
|
throw std::runtime_error("SSL_CTX_use_PrivateKey_file: " + getErrors());
|
|
}
|
|
} else {
|
|
throw std::runtime_error(
|
|
"Unsupported private key format: " + std::string(format));
|
|
}
|
|
}
|
|
|
|
void SSLContext::loadPrivateKeyFromBufferPEM(folly::StringPiece pkey) {
|
|
if (pkey.data() == nullptr) {
|
|
throw std::invalid_argument("loadPrivateKey: <pkey> is nullptr");
|
|
}
|
|
|
|
ssl::BioUniquePtr bio(BIO_new(BIO_s_mem()));
|
|
if (bio == nullptr) {
|
|
throw std::runtime_error("BIO_new: " + getErrors());
|
|
}
|
|
|
|
int written = BIO_write(bio.get(), pkey.data(), int(pkey.size()));
|
|
if (written <= 0 || static_cast<unsigned>(written) != pkey.size()) {
|
|
throw std::runtime_error("BIO_write: " + getErrors());
|
|
}
|
|
|
|
ssl::EvpPkeyUniquePtr key(
|
|
PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
|
|
if (key == nullptr) {
|
|
throw std::runtime_error("PEM_read_bio_PrivateKey: " + getErrors());
|
|
}
|
|
|
|
if (SSL_CTX_use_PrivateKey(ctx_, key.get()) == 0) {
|
|
throw std::runtime_error("SSL_CTX_use_PrivateKey: " + getErrors());
|
|
}
|
|
}
|
|
|
|
void SSLContext::loadCertKeyPairFromBufferPEM(
|
|
folly::StringPiece cert,
|
|
folly::StringPiece pkey) {
|
|
loadCertificateFromBufferPEM(cert);
|
|
loadPrivateKeyFromBufferPEM(pkey);
|
|
if (!isCertKeyPairValid()) {
|
|
throw std::runtime_error("SSL certificate and private key do not match");
|
|
}
|
|
}
|
|
|
|
void SSLContext::loadCertKeyPairFromFiles(
|
|
const char* certPath,
|
|
const char* keyPath,
|
|
const char* certFormat,
|
|
const char* keyFormat) {
|
|
loadCertificate(certPath, certFormat);
|
|
loadPrivateKey(keyPath, keyFormat);
|
|
if (!isCertKeyPairValid()) {
|
|
throw std::runtime_error("SSL certificate and private key do not match");
|
|
}
|
|
}
|
|
|
|
bool SSLContext::isCertKeyPairValid() const {
|
|
return SSL_CTX_check_private_key(ctx_) == 1;
|
|
}
|
|
|
|
void SSLContext::loadTrustedCertificates(const char* path) {
|
|
if (path == nullptr) {
|
|
throw std::invalid_argument("loadTrustedCertificates: <path> is nullptr");
|
|
}
|
|
if (SSL_CTX_load_verify_locations(ctx_, path, nullptr) == 0) {
|
|
throw std::runtime_error("SSL_CTX_load_verify_locations: " + getErrors());
|
|
}
|
|
ERR_clear_error();
|
|
}
|
|
|
|
void SSLContext::loadTrustedCertificates(X509_STORE* store) {
|
|
SSL_CTX_set_cert_store(ctx_, store);
|
|
}
|
|
|
|
void SSLContext::loadClientCAList(const char* path) {
|
|
auto clientCAs = SSL_load_client_CA_file(path);
|
|
if (clientCAs == nullptr) {
|
|
LOG(ERROR) << "Unable to load ca file: " << path << " " << getErrors();
|
|
return;
|
|
}
|
|
SSL_CTX_set_client_CA_list(ctx_, clientCAs);
|
|
}
|
|
|
|
void SSLContext::passwordCollector(
|
|
std::shared_ptr<PasswordCollector> collector) {
|
|
if (collector == nullptr) {
|
|
LOG(ERROR) << "passwordCollector: ignore invalid password collector";
|
|
return;
|
|
}
|
|
collector_ = collector;
|
|
SSL_CTX_set_default_passwd_cb(ctx_, passwordCallback);
|
|
SSL_CTX_set_default_passwd_cb_userdata(ctx_, this);
|
|
}
|
|
|
|
#if FOLLY_OPENSSL_HAS_SNI
|
|
|
|
void SSLContext::setServerNameCallback(const ServerNameCallback& cb) {
|
|
serverNameCb_ = cb;
|
|
}
|
|
|
|
void SSLContext::addClientHelloCallback(const ClientHelloCallback& cb) {
|
|
clientHelloCbs_.push_back(cb);
|
|
}
|
|
|
|
int SSLContext::baseServerNameOpenSSLCallback(SSL* ssl, int* al, void* data) {
|
|
auto context = (SSLContext*)data;
|
|
|
|
if (context == nullptr) {
|
|
return SSL_TLSEXT_ERR_NOACK;
|
|
}
|
|
|
|
for (auto& cb : context->clientHelloCbs_) {
|
|
// Generic callbacks to happen after we receive the Client Hello.
|
|
// For example, we use one to switch which cipher we use depending
|
|
// on the user's TLS version. Because the primary purpose of
|
|
// baseServerNameOpenSSLCallback is for SNI support, and these callbacks
|
|
// are side-uses, we ignore any possible failures other than just logging
|
|
// them.
|
|
cb(ssl);
|
|
}
|
|
|
|
if (!context->serverNameCb_) {
|
|
return SSL_TLSEXT_ERR_NOACK;
|
|
}
|
|
|
|
ServerNameCallbackResult ret = context->serverNameCb_(ssl);
|
|
switch (ret) {
|
|
case SERVER_NAME_FOUND:
|
|
return SSL_TLSEXT_ERR_OK;
|
|
case SERVER_NAME_NOT_FOUND:
|
|
return SSL_TLSEXT_ERR_NOACK;
|
|
case SERVER_NAME_NOT_FOUND_ALERT_FATAL:
|
|
*al = TLS1_AD_UNRECOGNIZED_NAME;
|
|
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
|
default:
|
|
CHECK(false);
|
|
}
|
|
|
|
return SSL_TLSEXT_ERR_NOACK;
|
|
}
|
|
#endif // FOLLY_OPENSSL_HAS_SNI
|
|
|
|
#if FOLLY_OPENSSL_HAS_ALPN
|
|
int SSLContext::alpnSelectCallback(
|
|
SSL* /* ssl */,
|
|
const unsigned char** out,
|
|
unsigned char* outlen,
|
|
const unsigned char* in,
|
|
unsigned int inlen,
|
|
void* data) {
|
|
auto context = (SSLContext*)data;
|
|
CHECK(context);
|
|
if (context->advertisedNextProtocols_.empty()) {
|
|
*out = nullptr;
|
|
*outlen = 0;
|
|
} else {
|
|
auto i = context->pickNextProtocols();
|
|
const auto& item = context->advertisedNextProtocols_[i];
|
|
if (SSL_select_next_proto(
|
|
(unsigned char**)out,
|
|
outlen,
|
|
item.protocols,
|
|
item.length,
|
|
in,
|
|
inlen) != OPENSSL_NPN_NEGOTIATED) {
|
|
return SSL_TLSEXT_ERR_NOACK;
|
|
}
|
|
}
|
|
return SSL_TLSEXT_ERR_OK;
|
|
}
|
|
|
|
bool SSLContext::setAdvertisedNextProtocols(
|
|
const std::list<std::string>& protocols) {
|
|
return setRandomizedAdvertisedNextProtocols({{1, protocols}});
|
|
}
|
|
|
|
bool SSLContext::setRandomizedAdvertisedNextProtocols(
|
|
const std::list<NextProtocolsItem>& items) {
|
|
unsetNextProtocols();
|
|
if (items.empty()) {
|
|
return false;
|
|
}
|
|
int total_weight = 0;
|
|
for (const auto& item : items) {
|
|
if (item.protocols.empty()) {
|
|
continue;
|
|
}
|
|
AdvertisedNextProtocolsItem advertised_item;
|
|
advertised_item.length = 0;
|
|
for (const auto& proto : item.protocols) {
|
|
++advertised_item.length;
|
|
auto protoLength = proto.length();
|
|
if (protoLength >= 256) {
|
|
deleteNextProtocolsStrings();
|
|
return false;
|
|
}
|
|
advertised_item.length += unsigned(protoLength);
|
|
}
|
|
advertised_item.protocols = new unsigned char[advertised_item.length];
|
|
if (!advertised_item.protocols) {
|
|
throw std::runtime_error("alloc failure");
|
|
}
|
|
unsigned char* dst = advertised_item.protocols;
|
|
for (auto& proto : item.protocols) {
|
|
auto protoLength = uint8_t(proto.length());
|
|
*dst++ = (unsigned char)protoLength;
|
|
memcpy(dst, proto.data(), protoLength);
|
|
dst += protoLength;
|
|
}
|
|
total_weight += item.weight;
|
|
advertisedNextProtocols_.push_back(advertised_item);
|
|
advertisedNextProtocolWeights_.push_back(item.weight);
|
|
}
|
|
if (total_weight == 0) {
|
|
deleteNextProtocolsStrings();
|
|
return false;
|
|
}
|
|
nextProtocolDistribution_ = std::discrete_distribution<>(
|
|
advertisedNextProtocolWeights_.begin(),
|
|
advertisedNextProtocolWeights_.end());
|
|
SSL_CTX_set_alpn_select_cb(ctx_, alpnSelectCallback, this);
|
|
// Client cannot really use randomized alpn
|
|
// Note that this function reverses the typical return value convention
|
|
// of openssl and returns 0 on success.
|
|
return SSL_CTX_set_alpn_protos(
|
|
ctx_,
|
|
advertisedNextProtocols_[0].protocols,
|
|
advertisedNextProtocols_[0].length) == 0;
|
|
}
|
|
|
|
void SSLContext::deleteNextProtocolsStrings() {
|
|
for (auto protocols : advertisedNextProtocols_) {
|
|
delete[] protocols.protocols;
|
|
}
|
|
advertisedNextProtocols_.clear();
|
|
advertisedNextProtocolWeights_.clear();
|
|
}
|
|
|
|
void SSLContext::unsetNextProtocols() {
|
|
deleteNextProtocolsStrings();
|
|
SSL_CTX_set_alpn_select_cb(ctx_, nullptr, nullptr);
|
|
SSL_CTX_set_alpn_protos(ctx_, nullptr, 0);
|
|
// clear the error stack here since openssl internals sometimes add a
|
|
// malloc failure when doing a memdup of NULL, 0..
|
|
ERR_clear_error();
|
|
}
|
|
|
|
size_t SSLContext::pickNextProtocols() {
|
|
CHECK(!advertisedNextProtocols_.empty()) << "Failed to pickNextProtocols";
|
|
auto rng = ThreadLocalPRNG();
|
|
return size_t(nextProtocolDistribution_(rng));
|
|
}
|
|
|
|
#endif // FOLLY_OPENSSL_HAS_ALPN
|
|
|
|
SSL* SSLContext::createSSL() const {
|
|
SSL* ssl = SSL_new(ctx_);
|
|
if (ssl == nullptr) {
|
|
throw std::runtime_error("SSL_new: " + getErrors());
|
|
}
|
|
return ssl;
|
|
}
|
|
|
|
void SSLContext::setSessionCacheContext(const std::string& context) {
|
|
SSL_CTX_set_session_id_context(
|
|
ctx_,
|
|
reinterpret_cast<const unsigned char*>(context.data()),
|
|
std::min<unsigned int>(
|
|
static_cast<unsigned int>(context.length()), SSL_MAX_SID_CTX_LENGTH));
|
|
}
|
|
|
|
/**
|
|
* Match a name with a pattern. The pattern may include wildcard. A single
|
|
* wildcard "*" can match up to one component in the domain name.
|
|
*
|
|
* @param host Host name, typically the name of the remote host
|
|
* @param pattern Name retrieved from certificate
|
|
* @param size Size of "pattern"
|
|
* @return True, if "host" matches "pattern". False otherwise.
|
|
*/
|
|
bool SSLContext::matchName(const char* host, const char* pattern, int size) {
|
|
bool match = false;
|
|
int i = 0, j = 0;
|
|
while (i < size && host[j] != '\0') {
|
|
if (toupper(pattern[i]) == toupper(host[j])) {
|
|
i++;
|
|
j++;
|
|
continue;
|
|
}
|
|
if (pattern[i] == '*') {
|
|
while (host[j] != '.' && host[j] != '\0') {
|
|
j++;
|
|
}
|
|
i++;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (i == size && host[j] == '\0') {
|
|
match = true;
|
|
}
|
|
return match;
|
|
}
|
|
|
|
int SSLContext::passwordCallback(char* password, int size, int, void* data) {
|
|
auto context = (SSLContext*)data;
|
|
if (context == nullptr || context->passwordCollector() == nullptr) {
|
|
return 0;
|
|
}
|
|
std::string userPassword;
|
|
// call user defined password collector to get password
|
|
context->passwordCollector()->getPassword(userPassword, size);
|
|
auto const length = std::min(userPassword.size(), size_t(size));
|
|
std::memcpy(password, userPassword.data(), length);
|
|
return int(length);
|
|
}
|
|
|
|
#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH)
|
|
void SSLContext::enableFalseStart() {
|
|
SSL_CTX_set_mode(ctx_, SSL_MODE_HANDSHAKE_CUTTHROUGH);
|
|
}
|
|
#endif
|
|
|
|
void SSLContext::initializeOpenSSL() {
|
|
folly::ssl::init();
|
|
}
|
|
|
|
void SSLContext::setOptions(long options) {
|
|
long newOpt = SSL_CTX_set_options(ctx_, options);
|
|
if ((newOpt & options) != options) {
|
|
throw std::runtime_error("SSL_CTX_set_options failed");
|
|
}
|
|
}
|
|
|
|
std::string SSLContext::getErrors(int errnoCopy) {
|
|
std::string errors;
|
|
unsigned long errorCode;
|
|
char message[256];
|
|
|
|
errors.reserve(512);
|
|
while ((errorCode = ERR_get_error()) != 0) {
|
|
if (!errors.empty()) {
|
|
errors += "; ";
|
|
}
|
|
const char* reason = ERR_reason_error_string(errorCode);
|
|
if (reason == nullptr) {
|
|
snprintf(message, sizeof(message) - 1, "SSL error # %08lX", errorCode);
|
|
reason = message;
|
|
}
|
|
errors += reason;
|
|
}
|
|
if (errors.empty()) {
|
|
errors = "error code: " + folly::to<std::string>(errnoCopy);
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
void SSLContext::enableTLS13() {
|
|
#if FOLLY_OPENSSL_IS_110
|
|
SSL_CTX_set_max_proto_version(ctx_, 0);
|
|
#endif
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, const PasswordCollector& collector) {
|
|
os << collector.describe();
|
|
return os;
|
|
}
|
|
|
|
} // namespace folly
|