37 #define OPENSSL_MISSING_FEATURE(name) \ 39 throw std::runtime_error("missing " #name " support in openssl"); \ 45 using std::shared_ptr;
75 X509* getX509(SSL_CTX* ctx) {
76 SSL* ssl = SSL_new(ctx);
77 SSL_set_connect_state(ssl);
78 X509* x509 = SSL_get_certificate(ssl);
86 void set_key_from_curve(SSL_CTX* ctx,
const std::string& curveName) {
87 #if OPENSSL_VERSION_NUMBER >= 0x0090800fL 88 #ifndef OPENSSL_NO_ECDH 89 EC_KEY* ecdh =
nullptr;
99 nid = OBJ_sn2nid(curveName.c_str());
101 LOG(FATAL) <<
"Unknown curve name:" << curveName.c_str();
103 ecdh = EC_KEY_new_by_curve_name(nid);
104 if (ecdh ==
nullptr) {
105 LOG(FATAL) <<
"Unable to create curve:" << curveName.c_str();
108 SSL_CTX_set_tmp_ecdh(ctx, ecdh);
118 static unsigned char dhp_2048[] = {
119 0xA2, 0x8B, 0xFC, 0x05, 0x95, 0x2D, 0xC8, 0xB5, 0x41, 0x0E,
120 0x01, 0xA9, 0xDE, 0xF6, 0x4B, 0x6C, 0x36, 0x31, 0xAD, 0x07,
121 0x0B, 0x8D, 0xCE, 0x0D, 0x71, 0x2A, 0xB8, 0x27, 0xD0, 0xC9,
122 0x91, 0xB1, 0x13, 0x24, 0xCB, 0x35, 0x60, 0xA0, 0x83, 0xB1,
123 0xE1, 0xEF, 0xA0, 0x9D, 0x9F, 0xA9, 0xAB, 0x56, 0x78, 0xBA,
124 0xA6, 0xB4, 0xA5, 0xEC, 0x86, 0x80, 0xB4, 0x5A, 0xC5, 0x9E,
125 0x30, 0x1E, 0xCC, 0xF8, 0x2D, 0x55, 0xF9, 0x0E, 0x74, 0x8F,
126 0x72, 0x46, 0xF5, 0xFC, 0xD4, 0x5B, 0xBC, 0xC3, 0xBC, 0x89,
127 0xCE, 0xB8, 0xD7, 0x1E, 0xC8, 0xD1, 0x46, 0xB7, 0xF3, 0xD3,
128 0x1C, 0x3A, 0x62, 0xB4, 0x1E, 0x42, 0xEA, 0x79, 0x1C, 0x07,
129 0x05, 0x46, 0x1A, 0x0F, 0x35, 0x79, 0xCB, 0xF8, 0xD1, 0x44,
130 0xEE, 0x86, 0x7C, 0x34, 0xA8, 0x7D, 0x92, 0x67, 0x48, 0x2D,
131 0x6E, 0xC2, 0x44, 0xA4, 0x93, 0x85, 0xF5, 0x2B, 0x79, 0x72,
132 0x79, 0xB5, 0xF4, 0xB0, 0xC6, 0xE1, 0xF0, 0x9F, 0x00, 0x59,
133 0x37, 0x09, 0xE8, 0x2C, 0xDB, 0xA7, 0x9B, 0x89, 0xEE, 0x49,
134 0x55, 0x53, 0x48, 0xB4, 0x02, 0xC2, 0xFA, 0x7A, 0xBB, 0x28,
135 0xFC, 0x0D, 0x06, 0xCB, 0xA5, 0xE2, 0x04, 0xFF, 0xDE, 0x5D,
136 0x99, 0xE9, 0x55, 0xA0, 0xBA, 0x60, 0x1E, 0x5E, 0x47, 0x46,
137 0x6C, 0x2A, 0x30, 0x8E, 0xBE, 0x71, 0x56, 0x85, 0x2E, 0x53,
138 0xF9, 0x33, 0x5B, 0xC8, 0x8C, 0xC1, 0x80, 0xAF, 0xC3, 0x0B,
139 0x89, 0xF5, 0x5A, 0x23, 0x97, 0xED, 0xB7, 0x8F, 0x2B, 0x0B,
140 0x70, 0x73, 0x44, 0xD2, 0xE8, 0xEC, 0xF2, 0xDD, 0x80, 0x32,
141 0x53, 0x9A, 0x17, 0xD6, 0xC7, 0x71, 0x7F, 0xA5, 0xD6, 0x45,
142 0x06, 0x36, 0xCE, 0x7B, 0x5D, 0x77, 0xA7, 0x39, 0x5F, 0xC7,
143 0x2A, 0xEA, 0x77, 0xE2, 0x8F, 0xFA, 0x8A, 0x81, 0x4C, 0x3D,
144 0x41, 0x48, 0xA4, 0x7F, 0x33, 0x7B
146 static unsigned char dhg_2048[] = {
150 BIGNUM *dhp_bn, *dhg_bn;
154 dhp_bn = BN_bin2bn(dhp_2048,
sizeof (dhp_2048),
nullptr);
155 dhg_bn = BN_bin2bn(dhg_2048,
sizeof (dhg_2048),
nullptr);
159 if (dhp_bn ==
nullptr || dhg_bn ==
nullptr 172 for (
auto& item : list) {
184 SSLContextManager::~SSLContextManager() =
default;
186 SSLContextManager::SSLContextManager(
193 eventBase_(eventBase),
199 ctxs.swap(other.ctxs);
200 defaultCtx.swap(other.defaultCtx);
201 defaultCtxDomainName.swap(other.defaultCtxDomainName);
202 dnMap.swap(other.dnMap);
207 defaultCtx =
nullptr;
208 defaultCtxDomainName.clear();
213 const std::vector<SSLContextConfig>& ctxConfigs,
217 const std::shared_ptr<SSLCacheProvider>& externalCache) {
226 auto ticketManager = ctx->getTicketManager();
228 ticketManager->getTLSTicketKeySeeds(
237 for (
const auto& ctxConfig : ctxConfigs) {
240 ticketSeeds ? ticketSeeds : &oldTicketSeeds,
253 const std::shared_ptr<SSLCacheProvider>& externalCache,
260 unsigned numCerts = 0;
263 std::unique_ptr<std::list<std::string>> subjectAltName;
265 std::make_shared<ServerSSLContext>(ctxConfig.
sslVersion);
271 if (!cert.passwordPath.empty()) {
272 auto sslPassword = std::make_shared<folly::PasswordInFile>(
274 sslCtx->passwordCollector(
std::move(sslPassword));
276 sslCtx->loadCertKeyPairFromFiles(
277 cert.certPath.c_str(),
278 cert.keyPath.c_str(),
284 }
catch (
const std::exception& ex) {
287 string msg = folly::to<string>(
"error loading SSL certificate ",
291 throw std::runtime_error(msg);
297 X509* x509 = getX509(sslCtx->getSSLCtx());
299 throw std::runtime_error(
300 folly::to<std::string>(
301 "Certificate: ", cert.certPath,
" is invalid"));
306 throw std::runtime_error(folly::to<string>(
"Cannot get CN for X509 ",
310 VLOG(2) <<
"cert " << cert.certPath <<
" CN: " << *cn;
313 VLOG(2) <<
"cert " << cert.certPath <<
" SAN: " << flattenList(*altName);
315 VLOG(2) <<
"cert " << cert.certPath <<
" SAN: " <<
"{none}";
321 if (commonName != *cn) {
322 throw std::runtime_error(folly::to<string>(
"X509 ", cert.certPath,
323 " does not have same CN as ",
326 if (altName ==
nullptr) {
327 if (subjectAltName !=
nullptr) {
328 throw std::runtime_error(folly::to<string>(
"X509 ", cert.certPath,
329 " does not have same SAN as ",
333 if ((subjectAltName ==
nullptr) || (*altName != *subjectAltName)) {
334 throw std::runtime_error(folly::to<string>(
"X509 ", cert.certPath,
335 " does not have same SAN as ",
340 lastCertPath = cert.certPath;
353 sslCtx->setOptions(SSL_OP_CIPHER_SERVER_PREFERENCE |
354 SSL_OP_SINGLE_DH_USE |
355 SSL_OP_SINGLE_ECDH_USE |
356 SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
363 DH* dh = get_dh2048();
364 SSL_CTX_set_tmp_dh(sslCtx->getSSLCtx(), dh);
368 if (!curve.empty()) {
369 set_key_from_curve(sslCtx->getSSLCtx(), curve);
374 sslCtx->loadTrustedCertificates(ctxConfig.
clientCAFile.c_str());
375 sslCtx->loadClientCAList(ctxConfig.
clientCAFile.c_str());
385 }
catch (
const std::exception& ex) {
386 string msg = folly::to<string>(
"error loading client CA",
390 throw std::runtime_error(msg);
400 VLOG(2) <<
"For vip " <<
vipName_ <<
", CN " << commonName
401 <<
", setting sid_ctx " << sessionIdContext;
402 sslCtx->setSessionCacheContext(sessionIdContext);
404 sslCtx->setupSessionCache(
410 sslCtx->setupTicketManager(ticketSeeds, ctxConfig,
stats_);
411 VLOG(2) <<
"On VipID=" << vipAddress.
describe() <<
" context=" << sslCtx;
420 }
catch (
const std::exception& ex) {
421 string msg = folly::to<string>(
"Error adding certificate : ",
424 throw std::runtime_error(msg);
429 #ifdef PROXYGEN_HAVE_SERVERNAMECALLBACK 430 SSLContext::ServerNameCallbackResult
431 SSLContextManager::serverNameCallback(SSL* ssl) {
432 shared_ptr<SSLContext> ctx;
434 const char* sn = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
435 bool reqHasServerName =
true;
437 VLOG(6) <<
"Server Name (tlsext_hostname) is missing, using default";
441 reqHasServerName =
false;
445 size_t snLen = strlen(sn);
446 VLOG(6) <<
"Server Name (SNI TLS extension): '" << sn <<
"' ";
459 for (
const auto& sigAlgPair : clientInfo->clientHelloSigAlgs_) {
460 if (sigAlgPair.first ==
468 const auto& extensions = clientInfo->clientHelloExtensions_;
469 if (std::find(extensions.begin(), extensions.end(),
484 sslSocket->switchServerSSLContext(ctx);
486 if (reqHasServerName) {
491 return SSLContext::SERVER_NAME_FOUND;
499 sslSocket->switchServerSSLContext(ctx);
501 if (reqHasServerName) {
507 return SSLContext::SERVER_NAME_FOUND;
520 return SSLContext::SERVER_NAME_NOT_FOUND;
527 shared_ptr<ServerSSLContext> sslCtx,
533 #ifdef SSL_OP_NO_COMPRESSION 534 sslCtx->setOptions(SSL_OP_NO_COMPRESSION);
538 #ifdef SSL_MODE_RELEASE_BUFFERS 540 SSL_CTX_set_mode(sslCtx->getSSLCtx(), SSL_MODE_RELEASE_BUFFERS);
543 #ifdef SSL_MODE_EARLY_RELEASE_BBIO 545 SSL_CTX_set_mode(sslCtx->getSSLCtx(), SSL_MODE_EARLY_RELEASE_BBIO);
552 #ifdef SSL_CTRL_SET_MAX_SEND_FRAGMENT 553 SSL_CTX_set_max_send_fragment(sslCtx->getSSLCtx(), 8000);
558 #if FOLLY_OPENSSL_HAS_ALPN 559 sslCtx->setRandomizedAdvertisedNextProtocols(
567 #ifdef PROXYGEN_HAVE_SERVERNAMECALLBACK 571 throw std::runtime_error(
">1 X509 is set as default");
576 std::bind(&SSLContextManager::serverNameCallback,
this,
577 std::placeholders::_1));
580 if (contexts.
ctxs.size() > 1) {
588 bool defaultFallback,
590 X509* x509 = getX509(sslCtx->getSSLCtx());
592 throw std::runtime_error(
"SSLCtx is invalid");
597 throw std::runtime_error(
"Cannot get CN");
620 if (cn->length() == 1 && (*cn)[0] ==
'*') {
621 if (!defaultFallback) {
622 throw std::runtime_error(
"STAR X509 is not the default");
624 contexts.
ctxs.emplace_back(sslCtx);
629 int sigAlg = X509_get_signature_nid(x509);
630 if (sigAlg == NID_sha1WithRSAEncryption ||
631 sigAlg == NID_ecdsa_with_SHA1) {
633 VLOG(4) <<
"Adding SSLContext with SHA1 Signature";
636 VLOG(4) <<
"Adding SSLContext with best available crypto";
649 for (
auto&
name : *altNames) {
658 if (defaultFallback) {
659 contexts.defaultCtxDomainName = *cn;
662 contexts.ctxs.emplace_back(sslCtx);
668 shared_ptr<SSLContext> sslCtx,
673 }
catch (
const std::runtime_error& ex) {
677 LOG(ERROR) << ex.what() <<
" DN=" << dn;
684 shared_ptr<SSLContext> sslCtx,
695 if (len > 2 && dn[0] ==
'*') {
701 throw std::runtime_error(
702 "Invalid wildcard CN/subject-alternative-name \"" +
std::string(dn) +
"\" " 703 "(only allow character \".\" after \"*\"");
707 if (len == 1 && *dn ==
'.') {
708 throw std::runtime_error(
"X509 has only '.' in the CN or subject alternative name " 709 "(after removing any preceding '*')");
712 if (strchr(dn,
'*')) {
713 throw std::runtime_error(
"X509 has '*' in the the CN or subject alternative name " 714 "(after removing any preceding '*')");
722 VLOG(6) <<
"Attempting insert of weak crypto SSLContext as best available.";
732 shared_ptr<SSLContext> sslCtx,
736 const auto v = contexts.
dnMap.find(key);
737 if (
v == contexts.
dnMap.end()) {
738 VLOG(6) <<
"Inserting SSLContext into map.";
739 contexts.
dnMap.emplace(key, sslCtx);
740 }
else if (
v->second == sslCtx) {
741 VLOG(6)<<
"Duplicate CN or subject alternative name found in the same X509." 742 " Ignore the later name.";
743 }
else if (overwrite) {
744 VLOG(6) <<
"Overwriting SSLContext.";
747 VLOG(6) <<
"Leaving existing SSLContext in map.";
755 shared_ptr<SSLContext>
765 shared_ptr<SSLContext>
770 if ((dot = key.
dnString.find_first_of(
".")) != DNString::npos) {
784 return shared_ptr<SSLContext>();
787 shared_ptr<SSLContext>
794 return shared_ptr<SSLContext>();
802 shared_ptr<SSLContext>
809 const std::vector<std::string>& oldSeeds,
810 const std::vector<std::string>& currentSeeds,
811 const std::vector<std::string>& newSeeds) {
812 #ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB 814 auto tmgr = ctx->getTicketManager();
816 tmgr->setTLSTicketKeySeeds(oldSeeds, currentSeeds, newSeeds);
virtual void loadCertKeyPairExternal(const std::shared_ptr< folly::SSLContext > &, const SSLContextConfig &, const std::string &)
SSLContextConfig::SNINoMatchFn noMatchFn_
std::vector< std::string > newSeeds
void insertSSLCtxByDomainNameImpl(const char *dn, size_t len, std::shared_ptr< folly::SSLContext > sslCtx, SslContexts &contexts, CertCrypto certCrypto)
std::vector< std::string > currentSeeds
std::basic_string< char, dn_char_traits > DNString
int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
std::set< std::string > offloadType
static std::unique_ptr< std::list< std::string > > getSubjectAltName(const X509 *cert)
std::vector< std::shared_ptr< ServerSSLContext > > ctxs
std::shared_ptr< ServerSSLContext > defaultCtx
void addSSLContextConfig(const SSLContextConfig &ctxConfig, const SSLCacheOptions &cacheOptions, const TLSTicketKeySeeds *ticketSeeds, const folly::SocketAddress &vipAddress, const std::shared_ptr< SSLCacheProvider > &externalCache, SslContexts *contexts=nullptr)
virtual void overrideConfiguration(const std::shared_ptr< folly::SSLContext > &, const SSLContextConfig &)
fbstring exceptionStr(const std::exception &e)
constexpr detail::Map< Move > move
std::string stringPrintf(const char *format,...)
folly::Optional< std::string > sessionContext
void swap(SslContexts &other) noexcept
std::string describe() const
requires E e noexcept(noexcept(s.error(std::move(e))))
void insert(std::shared_ptr< ServerSSLContext > sslCtx, bool defaultFallback, SslContexts &contexts)
void reloadTLSTicketKeys(const std::vector< std::string > &oldSeeds, const std::vector< std::string > ¤tSeeds, const std::vector< std::string > &newSeeds)
std::string defaultCtxDomainName
void insertIntoDnMap(SSLContextKey key, std::shared_ptr< folly::SSLContext > sslCtx, bool overwrite, SslContexts &contexts)
folly::SSLContext::SSLVerifyPeerEnum clientVerification
void insertSSLCtxByDomainName(const char *dn, size_t len, std::shared_ptr< folly::SSLContext > sslCtx, SslContexts &contexts, CertCrypto certCrypto=CertCrypto::BEST_AVAILABLE)
virtual void recordMatch() noexcept=0
std::vector< CertificateInfo > certificates
void resetSSLContextConfigs(const std::vector< SSLContextConfig > &ctxConfig, const SSLCacheOptions &cacheOptions, const TLSTicketKeySeeds *ticketSeeds, const folly::SocketAddress &vipAddress, const std::shared_ptr< SSLCacheProvider > &externalCache)
std::shared_ptr< folly::SSLContext > getDefaultSSLCtx() const
virtual void recordNotMatch() noexcept=0
std::shared_ptr< folly::SSLContext > getSSLCtxByExactDomain(const SSLContextKey &key) const
std::unordered_map< SSLContextKey, std::shared_ptr< folly::SSLContext >, SSLContextKeyHash > dnMap
ClientHelloExtStats * clientHelloTLSExtStats_
Encoder::MutableCompressedList list
GuardImpl guard(ErrorHandler &&handler)
ssl::ClientHelloInfo * getClientHelloInfo() const
#define OPENSSL_MISSING_FEATURE(name)
static std::unique_ptr< std::string > getCommonName(const X509 *cert)
virtual void recordAbsentHostname() noexcept=0
std::shared_ptr< folly::SSLContext > getSSLCtxBySuffix(const SSLContextKey &key) const
FOLLY_NODISCARD detail::ScopeGuardImplDecay< F, true > makeGuard(F &&f) noexcept(noexcept(detail::ScopeGuardImplDecay< F, true >(static_cast< F && >(f))))
int bind(NetworkSocket s, const sockaddr *name, socklen_t namelen)
std::shared_ptr< folly::SSLContext > getSSLCtx(const SSLContextKey &key) const
std::vector< std::string > oldSeeds
std::list< folly::SSLContext::NextProtocolsItem > nextProtocols
static AsyncSSLSocket * getFromSSL(const SSL *ssl)
void ctxSetupByOpensslFeature(std::shared_ptr< ServerSSLContext > sslCtx, const SSLContextConfig &ctxConfig, SslContexts &contexts)
KeyOffloadParams keyOffloadParams
folly::SSLContext::SSLVersion sslVersion
std::unique_ptr< ClientCertVerifyCallback > clientCertVerifyCallback_
SNINoMatchFn sniNoMatchFn
constexpr detail::First first
virtual void recordCertCrypto(CertCrypto requested, CertCrypto served) noexcept=0