Get *=default*ed default constructors
[folly.git] / folly / wangle / ssl / SSLContextManager.cpp
1 /*
2  *  Copyright (c) 2015, Facebook, Inc.
3  *  All rights reserved.
4  *
5  *  This source code is licensed under the BSD-style license found in the
6  *  LICENSE file in the root directory of this source tree. An additional grant
7  *  of patent rights can be found in the PATENTS file in the same directory.
8  *
9  */
10 #include <folly/wangle/ssl/SSLContextManager.h>
11
12 #include <folly/wangle/ssl/ClientHelloExtStats.h>
13 #include <folly/wangle/ssl/DHParam.h>
14 #include <folly/wangle/ssl/PasswordInFile.h>
15 #include <folly/wangle/ssl/SSLCacheOptions.h>
16 #include <folly/wangle/ssl/SSLSessionCacheManager.h>
17 #include <folly/wangle/ssl/SSLUtil.h>
18 #include <folly/wangle/ssl/TLSTicketKeyManager.h>
19 #include <folly/wangle/ssl/TLSTicketKeySeeds.h>
20
21 #include <folly/Conv.h>
22 #include <folly/ScopeGuard.h>
23 #include <folly/String.h>
24 #include <functional>
25 #include <openssl/asn1.h>
26 #include <openssl/ssl.h>
27 #include <string>
28 #include <folly/io/async/EventBase.h>
29
30 #define OPENSSL_MISSING_FEATURE(name) \
31 do { \
32   throw std::runtime_error("missing " #name " support in openssl");  \
33 } while(0)
34
35
36 using std::string;
37 using std::shared_ptr;
38
39 /**
40  * SSLContextManager helps to create and manage all SSL_CTX,
41  * SSLSessionCacheManager and TLSTicketManager for a listening
42  * VIP:PORT. (Note, in SNI, a listening VIP:PORT can have >1 SSL_CTX(s)).
43  *
44  * Other responsibilities:
45  * 1. It also handles the SSL_CTX selection after getting the tlsext_hostname
46  *    in the client hello message.
47  *
48  * Usage:
49  * 1. Each listening VIP:PORT serving SSL should have one SSLContextManager.
50  *    It maps to Acceptor in the wangle vocabulary.
51  *
52  * 2. Create a SSLContextConfig object (e.g. by parsing the JSON config).
53  *
54  * 3. Call SSLContextManager::addSSLContextConfig() which will
55  *    then create and configure the SSL_CTX
56  *
57  * Note: Each Acceptor, with SSL support, should have one SSLContextManager to
58  * manage all SSL_CTX for the VIP:PORT.
59  */
60
61 namespace folly {
62
63 namespace {
64
65 X509* getX509(SSL_CTX* ctx) {
66   SSL* ssl = SSL_new(ctx);
67   SSL_set_connect_state(ssl);
68   X509* x509 = SSL_get_certificate(ssl);
69   CRYPTO_add(&x509->references, 1, CRYPTO_LOCK_X509);
70   SSL_free(ssl);
71   return x509;
72 }
73
74 void set_key_from_curve(SSL_CTX* ctx, const std::string& curveName) {
75 #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
76 #ifndef OPENSSL_NO_ECDH
77   EC_KEY* ecdh = nullptr;
78   int nid;
79
80   /*
81    * Elliptic-Curve Diffie-Hellman parameters are either "named curves"
82    * from RFC 4492 section 5.1.1, or explicitly described curves over
83    * binary fields. OpenSSL only supports the "named curves", which provide
84    * maximum interoperability.
85    */
86
87   nid = OBJ_sn2nid(curveName.c_str());
88   if (nid == 0) {
89     LOG(FATAL) << "Unknown curve name:" << curveName.c_str();
90     return;
91   }
92   ecdh = EC_KEY_new_by_curve_name(nid);
93   if (ecdh == nullptr) {
94     LOG(FATAL) << "Unable to create curve:" << curveName.c_str();
95     return;
96   }
97
98   SSL_CTX_set_tmp_ecdh(ctx, ecdh);
99   EC_KEY_free(ecdh);
100 #endif
101 #endif
102 }
103
104 // Helper to create TLSTicketKeyManger and aware of the needed openssl
105 // version/feature.
106 std::unique_ptr<TLSTicketKeyManager> createTicketManagerHelper(
107   std::shared_ptr<folly::SSLContext> ctx,
108   const TLSTicketKeySeeds* ticketSeeds,
109   const SSLContextConfig& ctxConfig,
110   SSLStats* stats) {
111
112   std::unique_ptr<TLSTicketKeyManager> ticketManager;
113 #ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
114   if (ticketSeeds && ctxConfig.sessionTicketEnabled) {
115     ticketManager = folly::make_unique<TLSTicketKeyManager>(ctx.get(), stats);
116     ticketManager->setTLSTicketKeySeeds(
117       ticketSeeds->oldSeeds,
118       ticketSeeds->currentSeeds,
119       ticketSeeds->newSeeds);
120   } else {
121     ctx->setOptions(SSL_OP_NO_TICKET);
122   }
123 #else
124   if (ticketSeeds && ctxConfig.sessionTicketEnabled) {
125     OPENSSL_MISSING_FEATURE(TLSTicket);
126   }
127 #endif
128   return ticketManager;
129 }
130
131 std::string flattenList(const std::list<std::string>& list) {
132   std::string s;
133   bool first = true;
134   for (auto& item : list) {
135     if (first) {
136       first = false;
137     } else {
138       s.append(", ");
139     }
140     s.append(item);
141   }
142   return s;
143 }
144
145 }
146
147 SSLContextManager::~SSLContextManager() = default;
148
149 SSLContextManager::SSLContextManager(
150   EventBase* eventBase,
151   const std::string& vipName,
152   bool strict,
153   SSLStats* stats) :
154     stats_(stats),
155     eventBase_(eventBase),
156     strict_(strict) {
157 }
158
159 void SSLContextManager::addSSLContextConfig(
160   const SSLContextConfig& ctxConfig,
161   const SSLCacheOptions& cacheOptions,
162   const TLSTicketKeySeeds* ticketSeeds,
163   const folly::SocketAddress& vipAddress,
164   const std::shared_ptr<SSLCacheProvider>& externalCache) {
165
166   unsigned numCerts = 0;
167   std::string commonName;
168   std::string lastCertPath;
169   std::unique_ptr<std::list<std::string>> subjectAltName;
170   auto sslCtx = std::make_shared<SSLContext>(ctxConfig.sslVersion);
171   for (const auto& cert : ctxConfig.certificates) {
172     try {
173       sslCtx->loadCertificate(cert.certPath.c_str());
174     } catch (const std::exception& ex) {
175       // The exception isn't very useful without the certificate path name,
176       // so throw a new exception that includes the path to the certificate.
177       string msg = folly::to<string>("error loading SSL certificate ",
178                                      cert.certPath, ": ",
179                                      folly::exceptionStr(ex));
180       LOG(ERROR) << msg;
181       throw std::runtime_error(msg);
182     }
183
184     // Verify that the Common Name and (if present) Subject Alternative Names
185     // are the same for all the certs specified for the SSL context.
186     numCerts++;
187     X509* x509 = getX509(sslCtx->getSSLCtx());
188     auto guard = folly::makeGuard([x509] { X509_free(x509); });
189     auto cn = SSLUtil::getCommonName(x509);
190     if (!cn) {
191       throw std::runtime_error(folly::to<string>("Cannot get CN for X509 ",
192                                                  cert.certPath));
193     }
194     auto altName = SSLUtil::getSubjectAltName(x509);
195     VLOG(2) << "cert " << cert.certPath << " CN: " << *cn;
196     if (altName) {
197       altName->sort();
198       VLOG(2) << "cert " << cert.certPath << " SAN: " << flattenList(*altName);
199     } else {
200       VLOG(2) << "cert " << cert.certPath << " SAN: " << "{none}";
201     }
202     if (numCerts == 1) {
203       commonName = *cn;
204       subjectAltName = std::move(altName);
205     } else {
206       if (commonName != *cn) {
207         throw std::runtime_error(folly::to<string>("X509 ", cert.certPath,
208                                           " does not have same CN as ",
209                                           lastCertPath));
210       }
211       if (altName == nullptr) {
212         if (subjectAltName != nullptr) {
213           throw std::runtime_error(folly::to<string>("X509 ", cert.certPath,
214                                             " does not have same SAN as ",
215                                             lastCertPath));
216         }
217       } else {
218         if ((subjectAltName == nullptr) || (*altName != *subjectAltName)) {
219           throw std::runtime_error(folly::to<string>("X509 ", cert.certPath,
220                                             " does not have same SAN as ",
221                                             lastCertPath));
222         }
223       }
224     }
225     lastCertPath = cert.certPath;
226
227     // TODO t4438250 - Add ECDSA support to the crypto_ssl offload server
228     //                 so we can avoid storing the ECDSA private key in the
229     //                 address space of the Internet-facing process.  For
230     //                 now, if cert name includes "-EC" to denote elliptic
231     //                 curve, we load its private key even if the server as
232     //                 a whole has been configured for async crypto.
233     if (ctxConfig.isLocalPrivateKey ||
234         (cert.certPath.find("-EC") != std::string::npos)) {
235       // The private key lives in the same process
236
237       // This needs to be called before loadPrivateKey().
238       if (!cert.passwordPath.empty()) {
239         auto sslPassword = std::make_shared<PasswordInFile>(cert.passwordPath);
240         sslCtx->passwordCollector(sslPassword);
241       }
242
243       try {
244         sslCtx->loadPrivateKey(cert.keyPath.c_str());
245       } catch (const std::exception& ex) {
246         // Throw an error that includes the key path, so the user can tell
247         // which key had a problem.
248         string msg = folly::to<string>("error loading private SSL key ",
249                                        cert.keyPath, ": ",
250                                        folly::exceptionStr(ex));
251         LOG(ERROR) << msg;
252         throw std::runtime_error(msg);
253       }
254     }
255   }
256   if (!ctxConfig.isLocalPrivateKey) {
257     enableAsyncCrypto(sslCtx);
258   }
259
260   // Let the server pick the highest performing cipher from among the client's
261   // choices.
262   //
263   // Let's use a unique private key for all DH key exchanges.
264   //
265   // Because some old implementations choke on empty fragments, most SSL
266   // applications disable them (it's part of SSL_OP_ALL).  This
267   // will improve performance and decrease write buffer fragmentation.
268   sslCtx->setOptions(SSL_OP_CIPHER_SERVER_PREFERENCE |
269     SSL_OP_SINGLE_DH_USE |
270     SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
271
272   // Configure SSL ciphers list
273   if (!ctxConfig.tls11Ciphers.empty()) {
274     // FIXME: create a dummy SSL_CTX for cipher testing purpose? It can
275     //        remove the ordering dependency
276
277     // Test to see if the specified TLS1.1 ciphers are valid.  Note that
278     // these will be overwritten by the ciphers() call below.
279     sslCtx->setCiphersOrThrow(ctxConfig.tls11Ciphers);
280   }
281
282   // Important that we do this *after* checking the TLS1.1 ciphers above,
283   // since we test their validity by actually setting them.
284   sslCtx->ciphers(ctxConfig.sslCiphers);
285
286   // Use a fix DH param
287   DH* dh = get_dh2048();
288   SSL_CTX_set_tmp_dh(sslCtx->getSSLCtx(), dh);
289   DH_free(dh);
290
291   const string& curve = ctxConfig.eccCurveName;
292   if (!curve.empty()) {
293     set_key_from_curve(sslCtx->getSSLCtx(), curve);
294   }
295
296   if (!ctxConfig.clientCAFile.empty()) {
297     try {
298       sslCtx->setVerificationOption(SSLContext::VERIFY_REQ_CLIENT_CERT);
299       sslCtx->loadTrustedCertificates(ctxConfig.clientCAFile.c_str());
300       sslCtx->loadClientCAList(ctxConfig.clientCAFile.c_str());
301     } catch (const std::exception& ex) {
302       string msg = folly::to<string>("error loading client CA",
303                                      ctxConfig.clientCAFile, ": ",
304                                      folly::exceptionStr(ex));
305       LOG(ERROR) << msg;
306       throw std::runtime_error(msg);
307     }
308   }
309
310   // - start - SSL session cache config
311   // the internal cache never does what we want (per-thread-per-vip).
312   // Disable it.  SSLSessionCacheManager will set it appropriately.
313   SSL_CTX_set_session_cache_mode(sslCtx->getSSLCtx(), SSL_SESS_CACHE_OFF);
314   SSL_CTX_set_timeout(sslCtx->getSSLCtx(),
315                       cacheOptions.sslCacheTimeout.count());
316   std::unique_ptr<SSLSessionCacheManager> sessionCacheManager;
317   if (ctxConfig.sessionCacheEnabled &&
318       cacheOptions.maxSSLCacheSize > 0 &&
319       cacheOptions.sslCacheFlushSize > 0) {
320     sessionCacheManager =
321       folly::make_unique<SSLSessionCacheManager>(
322         cacheOptions.maxSSLCacheSize,
323         cacheOptions.sslCacheFlushSize,
324         sslCtx.get(),
325         vipAddress,
326         commonName,
327         eventBase_,
328         stats_,
329         externalCache);
330   }
331   // - end - SSL session cache config
332
333   std::unique_ptr<TLSTicketKeyManager> ticketManager =
334     createTicketManagerHelper(sslCtx, ticketSeeds, ctxConfig, stats_);
335
336   // finalize sslCtx setup by the individual features supported by openssl
337   ctxSetupByOpensslFeature(sslCtx, ctxConfig);
338
339   try {
340     insert(sslCtx,
341            std::move(sessionCacheManager),
342            std::move(ticketManager),
343            ctxConfig.isDefault);
344   } catch (const std::exception& ex) {
345     string msg = folly::to<string>("Error adding certificate : ",
346                                    folly::exceptionStr(ex));
347     LOG(ERROR) << msg;
348     throw std::runtime_error(msg);
349   }
350
351 }
352
353 #ifdef PROXYGEN_HAVE_SERVERNAMECALLBACK
354 SSLContext::ServerNameCallbackResult
355 SSLContextManager::serverNameCallback(SSL* ssl) {
356   shared_ptr<SSLContext> ctx;
357
358   const char* sn = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
359   if (!sn) {
360     VLOG(6) << "Server Name (tlsext_hostname) is missing";
361     if (clientHelloTLSExtStats_) {
362       clientHelloTLSExtStats_->recordAbsentHostname();
363     }
364     return SSLContext::SERVER_NAME_NOT_FOUND;
365   }
366   size_t snLen = strlen(sn);
367   VLOG(6) << "Server Name (SNI TLS extension): '" << sn << "' ";
368
369   // FIXME: This code breaks the abstraction. Suggestion?
370   AsyncSSLSocket* sslSocket = AsyncSSLSocket::getFromSSL(ssl);
371   CHECK(sslSocket);
372
373   DNString dnstr(sn, snLen);
374
375   uint32_t count = 0;
376   do {
377     // Try exact match first
378     ctx = getSSLCtx(dnstr);
379     if (ctx) {
380       sslSocket->switchServerSSLContext(ctx);
381       if (clientHelloTLSExtStats_) {
382         clientHelloTLSExtStats_->recordMatch();
383       }
384       return SSLContext::SERVER_NAME_FOUND;
385     }
386
387     ctx = getSSLCtxBySuffix(dnstr);
388     if (ctx) {
389       sslSocket->switchServerSSLContext(ctx);
390       if (clientHelloTLSExtStats_) {
391         clientHelloTLSExtStats_->recordMatch();
392       }
393       return SSLContext::SERVER_NAME_FOUND;
394     }
395
396     // Give the noMatchFn one chance to add the correct cert
397   }
398   while (count++ == 0 && noMatchFn_ && noMatchFn_(sn));
399
400   VLOG(6) << folly::stringPrintf("Cannot find a SSL_CTX for \"%s\"", sn);
401
402   if (clientHelloTLSExtStats_) {
403     clientHelloTLSExtStats_->recordNotMatch();
404   }
405   return SSLContext::SERVER_NAME_NOT_FOUND;
406 }
407 #endif
408
409 // Consolidate all SSL_CTX setup which depends on openssl version/feature
410 void
411 SSLContextManager::ctxSetupByOpensslFeature(
412   shared_ptr<folly::SSLContext> sslCtx,
413   const SSLContextConfig& ctxConfig) {
414   // Disable compression - profiling shows this to be very expensive in
415   // terms of CPU and memory consumption.
416   //
417 #ifdef SSL_OP_NO_COMPRESSION
418   sslCtx->setOptions(SSL_OP_NO_COMPRESSION);
419 #endif
420
421   // Enable early release of SSL buffers to reduce the memory footprint
422 #ifdef SSL_MODE_RELEASE_BUFFERS
423  sslCtx->getSSLCtx()->mode |= SSL_MODE_RELEASE_BUFFERS;
424 #endif
425 #ifdef SSL_MODE_EARLY_RELEASE_BBIO
426   sslCtx->getSSLCtx()->mode |=  SSL_MODE_EARLY_RELEASE_BBIO;
427 #endif
428
429   // This number should (probably) correspond to HTTPSession::kMaxReadSize
430   // For now, this number must also be large enough to accommodate our
431   // largest certificate, because some older clients (IE6/7) require the
432   // cert to be in a single fragment.
433 #ifdef SSL_CTRL_SET_MAX_SEND_FRAGMENT
434   SSL_CTX_set_max_send_fragment(sslCtx->getSSLCtx(), 8000);
435 #endif
436
437   // Specify cipher(s) to be used for TLS1.1 client
438   if (!ctxConfig.tls11Ciphers.empty()) {
439 #ifdef PROXYGEN_HAVE_SERVERNAMECALLBACK
440     // Specified TLS1.1 ciphers are valid
441     sslCtx->addClientHelloCallback(
442       std::bind(
443         &SSLContext::switchCiphersIfTLS11,
444         sslCtx.get(),
445         std::placeholders::_1,
446         ctxConfig.tls11Ciphers
447       )
448     );
449 #else
450     OPENSSL_MISSING_FEATURE(SNI);
451 #endif
452   }
453
454   // NPN (Next Protocol Negotiation)
455   if (!ctxConfig.nextProtocols.empty()) {
456 #ifdef OPENSSL_NPN_NEGOTIATED
457     sslCtx->setRandomizedAdvertisedNextProtocols(ctxConfig.nextProtocols);
458 #else
459     OPENSSL_MISSING_FEATURE(NPN);
460 #endif
461   }
462
463   // SNI
464 #ifdef PROXYGEN_HAVE_SERVERNAMECALLBACK
465   noMatchFn_ = ctxConfig.sniNoMatchFn;
466   if (ctxConfig.isDefault) {
467     if (defaultCtx_) {
468       throw std::runtime_error(">1 X509 is set as default");
469     }
470
471     defaultCtx_ = sslCtx;
472     defaultCtx_->setServerNameCallback(
473       std::bind(&SSLContextManager::serverNameCallback, this,
474                 std::placeholders::_1));
475   }
476 #else
477   if (ctxs_.size() > 1) {
478     OPENSSL_MISSING_FEATURE(SNI);
479   }
480 #endif
481 }
482
483 void
484 SSLContextManager::insert(shared_ptr<SSLContext> sslCtx,
485                           std::unique_ptr<SSLSessionCacheManager> smanager,
486                           std::unique_ptr<TLSTicketKeyManager> tmanager,
487                           bool defaultFallback) {
488   X509* x509 = getX509(sslCtx->getSSLCtx());
489   auto guard = folly::makeGuard([x509] { X509_free(x509); });
490   auto cn = SSLUtil::getCommonName(x509);
491   if (!cn) {
492     throw std::runtime_error("Cannot get CN");
493   }
494
495   /**
496    * Some notes from RFC 2818. Only for future quick references in case of bugs
497    *
498    * RFC 2818 section 3.1:
499    * "......
500    * If a subjectAltName extension of type dNSName is present, that MUST
501    * be used as the identity. Otherwise, the (most specific) Common Name
502    * field in the Subject field of the certificate MUST be used. Although
503    * the use of the Common Name is existing practice, it is deprecated and
504    * Certification Authorities are encouraged to use the dNSName instead.
505    * ......
506    * In some cases, the URI is specified as an IP address rather than a
507    * hostname. In this case, the iPAddress subjectAltName must be present
508    * in the certificate and must exactly match the IP in the URI.
509    * ......"
510    */
511
512   // Not sure if we ever get this kind of X509...
513   // If we do, assume '*' is always in the CN and ignore all subject alternative
514   // names.
515   if (cn->length() == 1 && (*cn)[0] == '*') {
516     if (!defaultFallback) {
517       throw std::runtime_error("STAR X509 is not the default");
518     }
519     ctxs_.emplace_back(sslCtx);
520     sessionCacheManagers_.emplace_back(std::move(smanager));
521     ticketManagers_.emplace_back(std::move(tmanager));
522     return;
523   }
524
525   // Insert by CN
526   insertSSLCtxByDomainName(cn->c_str(), cn->length(), sslCtx);
527
528   // Insert by subject alternative name(s)
529   auto altNames = SSLUtil::getSubjectAltName(x509);
530   if (altNames) {
531     for (auto& name : *altNames) {
532       insertSSLCtxByDomainName(name.c_str(), name.length(), sslCtx);
533     }
534   }
535
536   ctxs_.emplace_back(sslCtx);
537   sessionCacheManagers_.emplace_back(std::move(smanager));
538   ticketManagers_.emplace_back(std::move(tmanager));
539 }
540
541 void
542 SSLContextManager::insertSSLCtxByDomainName(const char* dn, size_t len,
543                                             shared_ptr<SSLContext> sslCtx) {
544   try {
545     insertSSLCtxByDomainNameImpl(dn, len, sslCtx);
546   } catch (const std::runtime_error& ex) {
547     if (strict_) {
548       throw ex;
549     } else {
550       LOG(ERROR) << ex.what() << " DN=" << dn;
551     }
552   }
553 }
554 void
555 SSLContextManager::insertSSLCtxByDomainNameImpl(const char* dn, size_t len,
556                                                 shared_ptr<SSLContext> sslCtx)
557 {
558   VLOG(4) <<
559     folly::stringPrintf("Adding CN/Subject-alternative-name \"%s\" for "
560                         "SNI search", dn);
561
562   // Only support wildcard domains which are prefixed exactly by "*." .
563   // "*" appearing at other locations is not accepted.
564
565   if (len > 2 && dn[0] == '*') {
566     if (dn[1] == '.') {
567       // skip the first '*'
568       dn++;
569       len--;
570     } else {
571       throw std::runtime_error(
572         "Invalid wildcard CN/subject-alternative-name \"" + std::string(dn) + "\" "
573         "(only allow character \".\" after \"*\"");
574     }
575   }
576
577   if (len == 1 && *dn == '.') {
578     throw std::runtime_error("X509 has only '.' in the CN or subject alternative name "
579                     "(after removing any preceding '*')");
580   }
581
582   if (strchr(dn, '*')) {
583     throw std::runtime_error("X509 has '*' in the the CN or subject alternative name "
584                     "(after removing any preceding '*')");
585   }
586
587   DNString dnstr(dn, len);
588   const auto v = dnMap_.find(dnstr);
589   if (v == dnMap_.end()) {
590     dnMap_.emplace(dnstr, sslCtx);
591   } else if (v->second == sslCtx) {
592     VLOG(6)<< "Duplicate CN or subject alternative name found in the same X509."
593       "  Ignore the later name.";
594   } else {
595     throw std::runtime_error("Duplicate CN or subject alternative name found: \"" +
596                              std::string(dnstr.c_str()) + "\"");
597   }
598 }
599
600 shared_ptr<SSLContext>
601 SSLContextManager::getSSLCtxBySuffix(const DNString& dnstr) const
602 {
603   size_t dot;
604
605   if ((dot = dnstr.find_first_of(".")) != DNString::npos) {
606     DNString suffixDNStr(dnstr, dot);
607     const auto v = dnMap_.find(suffixDNStr);
608     if (v != dnMap_.end()) {
609       VLOG(6) << folly::stringPrintf("\"%s\" is a willcard match to \"%s\"",
610                                      dnstr.c_str(), suffixDNStr.c_str());
611       return v->second;
612     }
613   }
614
615   VLOG(6) << folly::stringPrintf("\"%s\" is not a wildcard match",
616                                  dnstr.c_str());
617   return shared_ptr<SSLContext>();
618 }
619
620 shared_ptr<SSLContext>
621 SSLContextManager::getSSLCtx(const DNString& dnstr) const
622 {
623   const auto v = dnMap_.find(dnstr);
624   if (v == dnMap_.end()) {
625     VLOG(6) << folly::stringPrintf("\"%s\" is not an exact match",
626                                    dnstr.c_str());
627     return shared_ptr<SSLContext>();
628   } else {
629     VLOG(6) << folly::stringPrintf("\"%s\" is an exact match", dnstr.c_str());
630     return v->second;
631   }
632 }
633
634 shared_ptr<SSLContext>
635 SSLContextManager::getDefaultSSLCtx() const {
636   return defaultCtx_;
637 }
638
639 void
640 SSLContextManager::reloadTLSTicketKeys(
641   const std::vector<std::string>& oldSeeds,
642   const std::vector<std::string>& currentSeeds,
643   const std::vector<std::string>& newSeeds) {
644 #ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
645   for (auto& tmgr: ticketManagers_) {
646     tmgr->setTLSTicketKeySeeds(oldSeeds, currentSeeds, newSeeds);
647   }
648 #endif
649 }
650
651 } // namespace