proxygen
SSLContextManager.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
17 
23 #include <wangle/ssl/SSLUtil.h>
26 
27 #include <folly/Conv.h>
28 #include <folly/ScopeGuard.h>
29 #include <folly/String.h>
31 #include <functional>
32 
33 
34 #include <string>
36 
37 #define OPENSSL_MISSING_FEATURE(name) \
38 do { \
39  throw std::runtime_error("missing " #name " support in openssl"); \
40 } while(0)
41 
42 
43 using folly::SSLContext;
44 using std::string;
45 using std::shared_ptr;
46 // Get OpenSSL portability APIs
47 using namespace folly::ssl;
48 
71 namespace wangle {
72 
73 namespace {
74 
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);
79  if (x509) {
80  X509_up_ref(x509);
81  }
82  SSL_free(ssl);
83  return x509;
84 }
85 
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;
90  int nid;
91 
92  /*
93  * Elliptic-Curve Diffie-Hellman parameters are either "named curves"
94  * from RFC 4492 section 5.1.1, or explicitly described curves over
95  * binary fields. OpenSSL only supports the "named curves", which provide
96  * maximum interoperability.
97  */
98 
99  nid = OBJ_sn2nid(curveName.c_str());
100  if (nid == 0) {
101  LOG(FATAL) << "Unknown curve name:" << curveName.c_str();
102  }
103  ecdh = EC_KEY_new_by_curve_name(nid);
104  if (ecdh == nullptr) {
105  LOG(FATAL) << "Unable to create curve:" << curveName.c_str();
106  }
107 
108  SSL_CTX_set_tmp_ecdh(ctx, ecdh);
109  EC_KEY_free(ecdh);
110 #endif
111 #endif
112 }
113 
114 // The following was auto-generated by
115 // openssl dhparam -C 2048 with OepnSSL 1.1.0e
116 DH *get_dh2048()
117 {
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
145  };
146  static unsigned char dhg_2048[] = {
147  0x02,
148  };
149  DH *dh = DH_new();
150  BIGNUM *dhp_bn, *dhg_bn;
151 
152  if (dh == nullptr)
153  return nullptr;
154  dhp_bn = BN_bin2bn(dhp_2048, sizeof (dhp_2048), nullptr);
155  dhg_bn = BN_bin2bn(dhg_2048, sizeof (dhg_2048), nullptr);
156  // Note: DH_set0_pqg is defined only in OpenSSL 1.1.0; for
157  // other versions, it is defined in the portability library
158  // at folly/portability/OpenSSL.h
159  if (dhp_bn == nullptr || dhg_bn == nullptr
160  || !DH_set0_pqg(dh, dhp_bn, nullptr, dhg_bn)) {
161  DH_free(dh);
162  BN_free(dhp_bn);
163  BN_free(dhg_bn);
164  return nullptr;
165  }
166  return dh;
167 }
168 
169 std::string flattenList(const std::list<std::string>& list) {
170  std::string s;
171  bool first = true;
172  for (auto& item : list) {
173  if (first) {
174  first = false;
175  } else {
176  s.append(", ");
177  }
178  s.append(item);
179  }
180  return s;
181 }
182 }
183 
184 SSLContextManager::~SSLContextManager() = default;
185 
186 SSLContextManager::SSLContextManager(
187  folly::EventBase* eventBase,
188  const std::string& vipName,
189  bool strict,
190  SSLStats* stats) :
191  vipName_(vipName),
192  stats_(stats),
193  eventBase_(eventBase),
194  strict_(strict) {
195 
196 }
197 
199  ctxs.swap(other.ctxs);
200  defaultCtx.swap(other.defaultCtx);
201  defaultCtxDomainName.swap(other.defaultCtxDomainName);
202  dnMap.swap(other.dnMap);
203 }
204 
206  ctxs.clear();
207  defaultCtx = nullptr;
208  defaultCtxDomainName.clear();
209  dnMap.clear();
210 }
211 
213  const std::vector<SSLContextConfig>& ctxConfigs,
214  const SSLCacheOptions& cacheOptions,
215  const TLSTicketKeySeeds* ticketSeeds,
216  const folly::SocketAddress& vipAddress,
217  const std::shared_ptr<SSLCacheProvider>& externalCache) {
218 
219  SslContexts contexts;
220  TLSTicketKeySeeds oldTicketSeeds;
221  // This assumes that all ctxs have the same ticket seeds. Which we assume in
222  // other places as well
223  if (!ticketSeeds) {
224  // find first non null ticket manager and update seeds from it
225  for (auto& ctx : contexts_.ctxs) {
226  auto ticketManager = ctx->getTicketManager();
227  if (ticketManager) {
228  ticketManager->getTLSTicketKeySeeds(
229  oldTicketSeeds.oldSeeds,
230  oldTicketSeeds.currentSeeds,
231  oldTicketSeeds.newSeeds);
232  break;
233  }
234  }
235  }
236 
237  for (const auto& ctxConfig : ctxConfigs) {
238  addSSLContextConfig(ctxConfig,
239  cacheOptions,
240  ticketSeeds ? ticketSeeds : &oldTicketSeeds,
241  vipAddress,
242  externalCache,
243  &contexts);
244  }
245  contexts_.swap(contexts);
246 }
247 
249  const SSLContextConfig& ctxConfig,
250  const SSLCacheOptions& cacheOptions,
251  const TLSTicketKeySeeds* ticketSeeds,
252  const folly::SocketAddress& vipAddress,
253  const std::shared_ptr<SSLCacheProvider>& externalCache,
254  SslContexts* contexts) {
255 
256  if (!contexts) {
257  contexts = &contexts_;
258  }
259 
260  unsigned numCerts = 0;
261  std::string commonName;
262  std::string lastCertPath;
263  std::unique_ptr<std::list<std::string>> subjectAltName;
264  auto sslCtx =
265  std::make_shared<ServerSSLContext>(ctxConfig.sslVersion);
266  for (const auto& cert : ctxConfig.certificates) {
267  try {
268  if (ctxConfig.keyOffloadParams.offloadType.empty()) {
269  // The private key lives in the same process
270  // This needs to be called before loadPrivateKey().
271  if (!cert.passwordPath.empty()) {
272  auto sslPassword = std::make_shared<folly::PasswordInFile>(
273  cert.passwordPath);
274  sslCtx->passwordCollector(std::move(sslPassword));
275  }
276  sslCtx->loadCertKeyPairFromFiles(
277  cert.certPath.c_str(),
278  cert.keyPath.c_str(),
279  "PEM",
280  "PEM");
281  } else {
282  loadCertKeyPairExternal(sslCtx, ctxConfig, cert.certPath);
283  }
284  } catch (const std::exception& ex) {
285  // The exception isn't very useful without the certificate path name,
286  // so throw a new exception that includes the path to the certificate.
287  string msg = folly::to<string>("error loading SSL certificate ",
288  cert.certPath, ": ",
289  folly::exceptionStr(ex));
290  LOG(ERROR) << msg;
291  throw std::runtime_error(msg);
292  }
293 
294  // Verify that the Common Name and (if present) Subject Alternative Names
295  // are the same for all the certs specified for the SSL context.
296  numCerts++;
297  X509* x509 = getX509(sslCtx->getSSLCtx());
298  if (!x509) {
299  throw std::runtime_error(
300  folly::to<std::string>(
301  "Certificate: ", cert.certPath, " is invalid"));
302  }
303  auto guard = folly::makeGuard([x509] { X509_free(x509); });
304  auto cn = SSLUtil::getCommonName(x509);
305  if (!cn) {
306  throw std::runtime_error(folly::to<string>("Cannot get CN for X509 ",
307  cert.certPath));
308  }
309  auto altName = SSLUtil::getSubjectAltName(x509);
310  VLOG(2) << "cert " << cert.certPath << " CN: " << *cn;
311  if (altName) {
312  altName->sort();
313  VLOG(2) << "cert " << cert.certPath << " SAN: " << flattenList(*altName);
314  } else {
315  VLOG(2) << "cert " << cert.certPath << " SAN: " << "{none}";
316  }
317  if (numCerts == 1) {
318  commonName = *cn;
319  subjectAltName = std::move(altName);
320  } else {
321  if (commonName != *cn) {
322  throw std::runtime_error(folly::to<string>("X509 ", cert.certPath,
323  " does not have same CN as ",
324  lastCertPath));
325  }
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 ",
330  lastCertPath));
331  }
332  } else {
333  if ((subjectAltName == nullptr) || (*altName != *subjectAltName)) {
334  throw std::runtime_error(folly::to<string>("X509 ", cert.certPath,
335  " does not have same SAN as ",
336  lastCertPath));
337  }
338  }
339  }
340  lastCertPath = cert.certPath;
341  }
342 
343  overrideConfiguration(sslCtx, ctxConfig);
344 
345  // Let the server pick the highest performing cipher from among the client's
346  // choices.
347  //
348  // Let's use a unique private key for all DH key exchanges.
349  //
350  // Because some old implementations choke on empty fragments, most SSL
351  // applications disable them (it's part of SSL_OP_ALL). This
352  // will improve performance and decrease write buffer fragmentation.
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);
357 
358  // Important that we do this *after* checking the TLS1.1 ciphers above,
359  // since we test their validity by actually setting them.
360  sslCtx->ciphers(ctxConfig.sslCiphers);
361 
362  // Use a fix DH param
363  DH* dh = get_dh2048();
364  SSL_CTX_set_tmp_dh(sslCtx->getSSLCtx(), dh);
365  DH_free(dh);
366 
367  const string& curve = ctxConfig.eccCurveName;
368  if (!curve.empty()) {
369  set_key_from_curve(sslCtx->getSSLCtx(), curve);
370  }
371 
372  if (!ctxConfig.clientCAFile.empty()) {
373  try {
374  sslCtx->loadTrustedCertificates(ctxConfig.clientCAFile.c_str());
375  sslCtx->loadClientCAList(ctxConfig.clientCAFile.c_str());
376 
377  // Only allow over-riding of verification callback if one
378  // isn't explicitly set on the context
379  if (clientCertVerifyCallback_ == nullptr) {
380  sslCtx->setVerificationOption(ctxConfig.clientVerification);
381  } else {
382  clientCertVerifyCallback_->attachSSLContext(sslCtx);
383  }
384 
385  } catch (const std::exception& ex) {
386  string msg = folly::to<string>("error loading client CA",
387  ctxConfig.clientCAFile, ": ",
388  folly::exceptionStr(ex));
389  LOG(ERROR) << msg;
390  throw std::runtime_error(msg);
391  }
392  }
393 
394  // we always want to setup the session id context
395  // to make session resumption work (tickets or session cache)
396  std::string sessionIdContext = commonName;
397  if (ctxConfig.sessionContext && !ctxConfig.sessionContext->empty()) {
398  sessionIdContext = *ctxConfig.sessionContext;
399  }
400  VLOG(2) << "For vip " << vipName_ << ", CN " << commonName
401  << ", setting sid_ctx " << sessionIdContext;
402  sslCtx->setSessionCacheContext(sessionIdContext);
403 
404  sslCtx->setupSessionCache(
405  ctxConfig,
406  cacheOptions,
407  externalCache,
408  sessionIdContext,
409  stats_);
410  sslCtx->setupTicketManager(ticketSeeds, ctxConfig, stats_);
411  VLOG(2) << "On VipID=" << vipAddress.describe() << " context=" << sslCtx;
412 
413  // finalize sslCtx setup by the individual features supported by openssl
414  ctxSetupByOpensslFeature(sslCtx, ctxConfig, *contexts);
415 
416  try {
417  insert(sslCtx,
418  ctxConfig.isDefault,
419  *contexts);
420  } catch (const std::exception& ex) {
421  string msg = folly::to<string>("Error adding certificate : ",
422  folly::exceptionStr(ex));
423  LOG(ERROR) << msg;
424  throw std::runtime_error(msg);
425  }
426 
427 }
428 
429 #ifdef PROXYGEN_HAVE_SERVERNAMECALLBACK
430 SSLContext::ServerNameCallbackResult
431 SSLContextManager::serverNameCallback(SSL* ssl) {
432  shared_ptr<SSLContext> ctx;
433 
434  const char* sn = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
435  bool reqHasServerName = true;
436  if (!sn) {
437  VLOG(6) << "Server Name (tlsext_hostname) is missing, using default";
440  }
441  reqHasServerName = false;
442 
443  sn = contexts_.defaultCtxDomainName.c_str();
444  }
445  size_t snLen = strlen(sn);
446  VLOG(6) << "Server Name (SNI TLS extension): '" << sn << "' ";
447 
448  // FIXME: This code breaks the abstraction. Suggestion?
450  CHECK(sslSocket);
451 
452  // Check if we think the client is outdated and require weak crypto.
453  CertCrypto certCryptoReq = CertCrypto::BEST_AVAILABLE;
454 
455  // TODO: use SSL_get_sigalgs (requires openssl 1.0.2).
456  auto clientInfo = sslSocket->getClientHelloInfo();
457  if (clientInfo) {
458  certCryptoReq = CertCrypto::SHA1_SIGNATURE;
459  for (const auto& sigAlgPair : clientInfo->clientHelloSigAlgs_) {
460  if (sigAlgPair.first ==
462  certCryptoReq = CertCrypto::BEST_AVAILABLE;
463  break;
464  }
465  }
466 
467  // Assume the client supports SHA2 if it sent SNI.
468  const auto& extensions = clientInfo->clientHelloExtensions_;
469  if (std::find(extensions.begin(), extensions.end(),
470  folly::ssl::TLSExtension::SERVER_NAME) != extensions.end()) {
471  certCryptoReq = CertCrypto::BEST_AVAILABLE;
472  }
473  }
474 
475  DNString dnstr(sn, snLen);
476  uint32_t count = 0;
477  do {
478  // First look for a context with the exact crypto needed. Weaker crypto will
479  // be in the map as best available if it is the best we have for that
480  // subject name.
481  SSLContextKey key(dnstr, certCryptoReq);
482  ctx = getSSLCtx(key);
483  if (ctx) {
484  sslSocket->switchServerSSLContext(ctx);
486  if (reqHasServerName) {
488  }
489  clientHelloTLSExtStats_->recordCertCrypto(certCryptoReq, certCryptoReq);
490  }
491  return SSLContext::SERVER_NAME_FOUND;
492  }
493 
494  // If we didn't find an exact match, look for a cert with upgraded crypto.
495  if (certCryptoReq != CertCrypto::BEST_AVAILABLE) {
496  SSLContextKey fallbackKey(dnstr, CertCrypto::BEST_AVAILABLE);
497  ctx = getSSLCtx(fallbackKey);
498  if (ctx) {
499  sslSocket->switchServerSSLContext(ctx);
501  if (reqHasServerName) {
503  }
505  certCryptoReq, CertCrypto::BEST_AVAILABLE);
506  }
507  return SSLContext::SERVER_NAME_FOUND;
508  }
509  }
510 
511  // Give the noMatchFn one chance to add the correct cert
512  }
513  while (count++ == 0 && noMatchFn_ && noMatchFn_(sn));
514 
515  VLOG(6) << folly::stringPrintf("Cannot find a SSL_CTX for \"%s\"", sn);
516 
517  if (clientHelloTLSExtStats_ && reqHasServerName) {
519  }
520  return SSLContext::SERVER_NAME_NOT_FOUND;
521 }
522 #endif
523 
524 // Consolidate all SSL_CTX setup which depends on openssl version/feature
525 void
527  shared_ptr<ServerSSLContext> sslCtx,
528  const SSLContextConfig& ctxConfig,
529  SslContexts& contexts) {
530  // Disable compression - profiling shows this to be very expensive in
531  // terms of CPU and memory consumption.
532  //
533 #ifdef SSL_OP_NO_COMPRESSION
534  sslCtx->setOptions(SSL_OP_NO_COMPRESSION);
535 #endif
536 
537  // Enable early release of SSL buffers to reduce the memory footprint
538 #ifdef SSL_MODE_RELEASE_BUFFERS
539  // Note: SSL_CTX_set_mode doesn't set, just ORs the arg with existing mode
540  SSL_CTX_set_mode(sslCtx->getSSLCtx(), SSL_MODE_RELEASE_BUFFERS);
541 
542 #endif
543 #ifdef SSL_MODE_EARLY_RELEASE_BBIO
544  // Note: SSL_CTX_set_mode doesn't set, just ORs the arg with existing mode
545  SSL_CTX_set_mode(sslCtx->getSSLCtx(), SSL_MODE_EARLY_RELEASE_BBIO);
546 #endif
547 
548  // This number should (probably) correspond to HTTPSession::kMaxReadSize
549  // For now, this number must also be large enough to accommodate our
550  // largest certificate, because some older clients (IE6/7) require the
551  // cert to be in a single fragment.
552 #ifdef SSL_CTRL_SET_MAX_SEND_FRAGMENT
553  SSL_CTX_set_max_send_fragment(sslCtx->getSSLCtx(), 8000);
554 #endif
555 
556  // NPN (Next Protocol Negotiation)
557  if (!ctxConfig.nextProtocols.empty()) {
558 #if FOLLY_OPENSSL_HAS_ALPN
559  sslCtx->setRandomizedAdvertisedNextProtocols(
560  ctxConfig.nextProtocols);
561 #else
563 #endif
564  }
565 
566  // SNI
567 #ifdef PROXYGEN_HAVE_SERVERNAMECALLBACK
568  noMatchFn_ = ctxConfig.sniNoMatchFn;
569  if (ctxConfig.isDefault) {
570  if (contexts.defaultCtx) {
571  throw std::runtime_error(">1 X509 is set as default");
572  }
573 
574  contexts.defaultCtx = sslCtx;
575  contexts.defaultCtx->setServerNameCallback(
576  std::bind(&SSLContextManager::serverNameCallback, this,
577  std::placeholders::_1));
578  }
579 #else
580  if (contexts.ctxs.size() > 1) {
582  }
583 #endif
584 }
585 
586 void
587 SSLContextManager::insert(shared_ptr<ServerSSLContext> sslCtx,
588  bool defaultFallback,
589  SslContexts& contexts) {
590  X509* x509 = getX509(sslCtx->getSSLCtx());
591  if (!x509) {
592  throw std::runtime_error("SSLCtx is invalid");
593  }
594  auto guard = folly::makeGuard([x509] { X509_free(x509); });
595  auto cn = SSLUtil::getCommonName(x509);
596  if (!cn) {
597  throw std::runtime_error("Cannot get CN");
598  }
599 
617  // Not sure if we ever get this kind of X509...
618  // If we do, assume '*' is always in the CN and ignore all subject alternative
619  // names.
620  if (cn->length() == 1 && (*cn)[0] == '*') {
621  if (!defaultFallback) {
622  throw std::runtime_error("STAR X509 is not the default");
623  }
624  contexts.ctxs.emplace_back(sslCtx);
625  return;
626  }
627 
628  CertCrypto certCrypto;
629  int sigAlg = X509_get_signature_nid(x509);
630  if (sigAlg == NID_sha1WithRSAEncryption ||
631  sigAlg == NID_ecdsa_with_SHA1) {
632  certCrypto = CertCrypto::SHA1_SIGNATURE;
633  VLOG(4) << "Adding SSLContext with SHA1 Signature";
634  } else {
635  certCrypto = CertCrypto::BEST_AVAILABLE;
636  VLOG(4) << "Adding SSLContext with best available crypto";
637  }
638 
639  // Insert by CN
640  insertSSLCtxByDomainName(cn->c_str(),
641  cn->length(),
642  sslCtx,
643  contexts,
644  certCrypto);
645 
646  // Insert by subject alternative name(s)
647  auto altNames = SSLUtil::getSubjectAltName(x509);
648  if (altNames) {
649  for (auto& name : *altNames) {
651  name.length(),
652  sslCtx,
653  contexts,
654  certCrypto);
655  }
656  }
657 
658  if (defaultFallback) {
659  contexts.defaultCtxDomainName = *cn;
660  }
661 
662  contexts.ctxs.emplace_back(sslCtx);
663 }
664 
665 void
667  size_t len,
668  shared_ptr<SSLContext> sslCtx,
669  SslContexts& contexts,
670  CertCrypto certCrypto) {
671  try {
672  insertSSLCtxByDomainNameImpl(dn, len, sslCtx, contexts, certCrypto);
673  } catch (const std::runtime_error& ex) {
674  if (strict_) {
675  throw ex;
676  } else {
677  LOG(ERROR) << ex.what() << " DN=" << dn;
678  }
679  }
680 }
681 void
683  size_t len,
684  shared_ptr<SSLContext> sslCtx,
685  SslContexts& contexts,
686  CertCrypto certCrypto)
687 {
688  VLOG(4) <<
689  folly::stringPrintf("Adding CN/Subject-alternative-name \"%s\" for "
690  "SNI search", dn);
691 
692  // Only support wildcard domains which are prefixed exactly by "*." .
693  // "*" appearing at other locations is not accepted.
694 
695  if (len > 2 && dn[0] == '*') {
696  if (dn[1] == '.') {
697  // skip the first '*'
698  dn++;
699  len--;
700  } else {
701  throw std::runtime_error(
702  "Invalid wildcard CN/subject-alternative-name \"" + std::string(dn) + "\" "
703  "(only allow character \".\" after \"*\"");
704  }
705  }
706 
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 '*')");
710  }
711 
712  if (strchr(dn, '*')) {
713  throw std::runtime_error("X509 has '*' in the the CN or subject alternative name "
714  "(after removing any preceding '*')");
715  }
716 
717  DNString dnstr(dn, len);
718  insertIntoDnMap(SSLContextKey(dnstr, certCrypto), sslCtx, true, contexts);
719  if (certCrypto != CertCrypto::BEST_AVAILABLE) {
720  // Note: there's no partial ordering here (you either get what you request,
721  // or you get best available).
722  VLOG(6) << "Attempting insert of weak crypto SSLContext as best available.";
725  sslCtx,
726  false,
727  contexts);
728  }
729 }
730 
732  shared_ptr<SSLContext> sslCtx,
733  bool overwrite,
734  SslContexts& contexts)
735 {
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.";
745  v->second = sslCtx;
746  } else {
747  VLOG(6) << "Leaving existing SSLContext in map.";
748  }
749 }
750 
752  contexts_.clear();
753 }
754 
755 shared_ptr<SSLContext>
757 {
758  auto ctx = getSSLCtxByExactDomain(key);
759  if (ctx) {
760  return ctx;
761  }
762  return getSSLCtxBySuffix(key);
763 }
764 
765 shared_ptr<SSLContext>
767 {
768  size_t dot;
769 
770  if ((dot = key.dnString.find_first_of(".")) != DNString::npos) {
771  SSLContextKey suffixKey(DNString(key.dnString, dot),
772  key.certCrypto);
773  const auto v = contexts_.dnMap.find(suffixKey);
774  if (v != contexts_.dnMap.end()) {
775  VLOG(6) << folly::stringPrintf("\"%s\" is a willcard match to \"%s\"",
776  key.dnString.c_str(),
777  suffixKey.dnString.c_str());
778  return v->second;
779  }
780  }
781 
782  VLOG(6) << folly::stringPrintf("\"%s\" is not a wildcard match",
783  key.dnString.c_str());
784  return shared_ptr<SSLContext>();
785 }
786 
787 shared_ptr<SSLContext>
789 {
790  const auto v = contexts_.dnMap.find(key);
791  if (v == contexts_.dnMap.end()) {
792  VLOG(6) << folly::stringPrintf("\"%s\" is not an exact match",
793  key.dnString.c_str());
794  return shared_ptr<SSLContext>();
795  } else {
796  VLOG(6) << folly::stringPrintf("\"%s\" is an exact match",
797  key.dnString.c_str());
798  return v->second;
799  }
800 }
801 
802 shared_ptr<SSLContext>
804  return contexts_.defaultCtx;
805 }
806 
807 void
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
813  for (auto& ctx : contexts_.ctxs) {
814  auto tmgr = ctx->getTicketManager();
815  if (tmgr) {
816  tmgr->setTLSTicketKeySeeds(oldSeeds, currentSeeds, newSeeds);
817  }
818  }
819 #endif
820 }
821 
822 } // namespace wangle
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)
Definition: OpenSSL.cpp:212
static std::unique_ptr< std::list< std::string > > getSubjectAltName(const X509 *cert)
Definition: SSLUtil.cpp:70
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
Definition: Base-inl.h:2567
std::string stringPrintf(const char *format,...)
Definition: String.cpp:223
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 > &currentSeeds, const std::vector< std::string > &newSeeds)
void insertIntoDnMap(SSLContextKey key, std::shared_ptr< folly::SSLContext > sslCtx, bool overwrite, SslContexts &contexts)
const char * name
Definition: http_parser.c:437
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)
Definition: Base.h:840
ssl::ClientHelloInfo * getClientHelloInfo() const
#define OPENSSL_MISSING_FEATURE(name)
static std::unique_ptr< std::string > getCommonName(const X509 *cert)
Definition: SSLUtil.cpp:54
virtual void recordAbsentHostname() noexcept=0
std::shared_ptr< folly::SSLContext > getSSLCtxBySuffix(const SSLContextKey &key) const
int * count
FOLLY_NODISCARD detail::ScopeGuardImplDecay< F, true > makeGuard(F &&f) noexcept(noexcept(detail::ScopeGuardImplDecay< F, true >(static_cast< F && >(f))))
Definition: ScopeGuard.h:184
const char * string
Definition: Conv.cpp:212
int bind(NetworkSocket s, const sockaddr *name, socklen_t namelen)
Definition: NetOps.cpp:76
static set< string > s
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_
constexpr detail::First first
Definition: Base-inl.h:2553
virtual void recordCertCrypto(CertCrypto requested, CertCrypto served) noexcept=0
constexpr auto dot