SSLContext.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. /*
  2. * Copyright 2014-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. */
  16. #include <folly/io/async/SSLContext.h>
  17. #include <folly/Format.h>
  18. #include <folly/Memory.h>
  19. #include <folly/Random.h>
  20. #include <folly/SharedMutex.h>
  21. #include <folly/SpinLock.h>
  22. #include <folly/ssl/Init.h>
  23. #include <folly/system/ThreadId.h>
  24. // ---------------------------------------------------------------------
  25. // SSLContext implementation
  26. // ---------------------------------------------------------------------
  27. namespace folly {
  28. //
  29. // For OpenSSL portability API
  30. using namespace folly::ssl;
  31. // SSLContext implementation
  32. SSLContext::SSLContext(SSLVersion version) {
  33. folly::ssl::init();
  34. ctx_ = SSL_CTX_new(SSLv23_method());
  35. if (ctx_ == nullptr) {
  36. throw std::runtime_error("SSL_CTX_new: " + getErrors());
  37. }
  38. int opt = 0;
  39. switch (version) {
  40. case TLSv1:
  41. opt = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
  42. break;
  43. case SSLv3:
  44. opt = SSL_OP_NO_SSLv2;
  45. break;
  46. case TLSv1_2:
  47. opt = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 |
  48. SSL_OP_NO_TLSv1_1;
  49. break;
  50. default:
  51. // do nothing
  52. break;
  53. }
  54. int newOpt = SSL_CTX_set_options(ctx_, opt);
  55. DCHECK((newOpt & opt) == opt);
  56. SSL_CTX_set_mode(ctx_, SSL_MODE_AUTO_RETRY);
  57. checkPeerName_ = false;
  58. SSL_CTX_set_options(ctx_, SSL_OP_NO_COMPRESSION);
  59. sslAcceptRunner_ = std::make_unique<SSLAcceptRunner>();
  60. #if FOLLY_OPENSSL_HAS_SNI
  61. SSL_CTX_set_tlsext_servername_callback(ctx_, baseServerNameOpenSSLCallback);
  62. SSL_CTX_set_tlsext_servername_arg(ctx_, this);
  63. #endif
  64. }
  65. SSLContext::~SSLContext() {
  66. if (ctx_ != nullptr) {
  67. SSL_CTX_free(ctx_);
  68. ctx_ = nullptr;
  69. }
  70. #if FOLLY_OPENSSL_HAS_ALPN
  71. deleteNextProtocolsStrings();
  72. #endif
  73. }
  74. void SSLContext::ciphers(const std::string& ciphers) {
  75. setCiphersOrThrow(ciphers);
  76. }
  77. void SSLContext::setClientECCurvesList(
  78. const std::vector<std::string>& ecCurves) {
  79. if (ecCurves.size() == 0) {
  80. return;
  81. }
  82. #if OPENSSL_VERSION_NUMBER >= 0x1000200fL
  83. std::string ecCurvesList;
  84. join(":", ecCurves, ecCurvesList);
  85. int rc = SSL_CTX_set1_curves_list(ctx_, ecCurvesList.c_str());
  86. if (rc == 0) {
  87. throw std::runtime_error("SSL_CTX_set1_curves_list " + getErrors());
  88. }
  89. #endif
  90. }
  91. void SSLContext::setServerECCurve(const std::string& curveName) {
  92. #if OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH)
  93. EC_KEY* ecdh = nullptr;
  94. int nid;
  95. /*
  96. * Elliptic-Curve Diffie-Hellman parameters are either "named curves"
  97. * from RFC 4492 section 5.1.1, or explicitly described curves over
  98. * binary fields. OpenSSL only supports the "named curves", which provide
  99. * maximum interoperability.
  100. */
  101. nid = OBJ_sn2nid(curveName.c_str());
  102. if (nid == 0) {
  103. LOG(FATAL) << "Unknown curve name:" << curveName.c_str();
  104. }
  105. ecdh = EC_KEY_new_by_curve_name(nid);
  106. if (ecdh == nullptr) {
  107. LOG(FATAL) << "Unable to create curve:" << curveName.c_str();
  108. }
  109. SSL_CTX_set_tmp_ecdh(ctx_, ecdh);
  110. EC_KEY_free(ecdh);
  111. #else
  112. throw std::runtime_error("Elliptic curve encryption not allowed");
  113. #endif
  114. }
  115. void SSLContext::setX509VerifyParam(
  116. const ssl::X509VerifyParam& x509VerifyParam) {
  117. if (!x509VerifyParam) {
  118. return;
  119. }
  120. if (SSL_CTX_set1_param(ctx_, x509VerifyParam.get()) != 1) {
  121. throw std::runtime_error("SSL_CTX_set1_param " + getErrors());
  122. }
  123. }
  124. void SSLContext::setCiphersOrThrow(const std::string& ciphers) {
  125. int rc = SSL_CTX_set_cipher_list(ctx_, ciphers.c_str());
  126. if (rc == 0) {
  127. throw std::runtime_error("SSL_CTX_set_cipher_list: " + getErrors());
  128. }
  129. providedCiphersString_ = ciphers;
  130. }
  131. void SSLContext::setVerificationOption(
  132. const SSLContext::SSLVerifyPeerEnum& verifyPeer) {
  133. CHECK(verifyPeer != SSLVerifyPeerEnum::USE_CTX); // dont recurse
  134. verifyPeer_ = verifyPeer;
  135. }
  136. int SSLContext::getVerificationMode(
  137. const SSLContext::SSLVerifyPeerEnum& verifyPeer) {
  138. CHECK(verifyPeer != SSLVerifyPeerEnum::USE_CTX);
  139. int mode = SSL_VERIFY_NONE;
  140. switch (verifyPeer) {
  141. // case SSLVerifyPeerEnum::USE_CTX: // can't happen
  142. // break;
  143. case SSLVerifyPeerEnum::VERIFY:
  144. mode = SSL_VERIFY_PEER;
  145. break;
  146. case SSLVerifyPeerEnum::VERIFY_REQ_CLIENT_CERT:
  147. mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
  148. break;
  149. case SSLVerifyPeerEnum::NO_VERIFY:
  150. mode = SSL_VERIFY_NONE;
  151. break;
  152. default:
  153. break;
  154. }
  155. return mode;
  156. }
  157. int SSLContext::getVerificationMode() {
  158. return getVerificationMode(verifyPeer_);
  159. }
  160. void SSLContext::authenticate(
  161. bool checkPeerCert,
  162. bool checkPeerName,
  163. const std::string& peerName) {
  164. int mode;
  165. if (checkPeerCert) {
  166. mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
  167. SSL_VERIFY_CLIENT_ONCE;
  168. checkPeerName_ = checkPeerName;
  169. peerFixedName_ = peerName;
  170. } else {
  171. mode = SSL_VERIFY_NONE;
  172. checkPeerName_ = false; // can't check name without cert!
  173. peerFixedName_.clear();
  174. }
  175. SSL_CTX_set_verify(ctx_, mode, nullptr);
  176. }
  177. void SSLContext::loadCertificate(const char* path, const char* format) {
  178. if (path == nullptr || format == nullptr) {
  179. throw std::invalid_argument(
  180. "loadCertificateChain: either <path> or <format> is nullptr");
  181. }
  182. if (strcmp(format, "PEM") == 0) {
  183. if (SSL_CTX_use_certificate_chain_file(ctx_, path) != 1) {
  184. int errnoCopy = errno;
  185. std::string reason("SSL_CTX_use_certificate_chain_file: ");
  186. reason.append(path);
  187. reason.append(": ");
  188. reason.append(getErrors(errnoCopy));
  189. throw std::runtime_error(reason);
  190. }
  191. } else {
  192. throw std::runtime_error(
  193. "Unsupported certificate format: " + std::string(format));
  194. }
  195. }
  196. void SSLContext::loadCertificateFromBufferPEM(folly::StringPiece cert) {
  197. if (cert.data() == nullptr) {
  198. throw std::invalid_argument("loadCertificate: <cert> is nullptr");
  199. }
  200. ssl::BioUniquePtr bio(BIO_new(BIO_s_mem()));
  201. if (bio == nullptr) {
  202. throw std::runtime_error("BIO_new: " + getErrors());
  203. }
  204. int written = BIO_write(bio.get(), cert.data(), int(cert.size()));
  205. if (written <= 0 || static_cast<unsigned>(written) != cert.size()) {
  206. throw std::runtime_error("BIO_write: " + getErrors());
  207. }
  208. ssl::X509UniquePtr x509(
  209. PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
  210. if (x509 == nullptr) {
  211. throw std::runtime_error("PEM_read_bio_X509: " + getErrors());
  212. }
  213. if (SSL_CTX_use_certificate(ctx_, x509.get()) == 0) {
  214. throw std::runtime_error("SSL_CTX_use_certificate: " + getErrors());
  215. }
  216. }
  217. void SSLContext::loadPrivateKey(const char* path, const char* format) {
  218. if (path == nullptr || format == nullptr) {
  219. throw std::invalid_argument(
  220. "loadPrivateKey: either <path> or <format> is nullptr");
  221. }
  222. if (strcmp(format, "PEM") == 0) {
  223. if (SSL_CTX_use_PrivateKey_file(ctx_, path, SSL_FILETYPE_PEM) == 0) {
  224. throw std::runtime_error("SSL_CTX_use_PrivateKey_file: " + getErrors());
  225. }
  226. } else {
  227. throw std::runtime_error(
  228. "Unsupported private key format: " + std::string(format));
  229. }
  230. }
  231. void SSLContext::loadPrivateKeyFromBufferPEM(folly::StringPiece pkey) {
  232. if (pkey.data() == nullptr) {
  233. throw std::invalid_argument("loadPrivateKey: <pkey> is nullptr");
  234. }
  235. ssl::BioUniquePtr bio(BIO_new(BIO_s_mem()));
  236. if (bio == nullptr) {
  237. throw std::runtime_error("BIO_new: " + getErrors());
  238. }
  239. int written = BIO_write(bio.get(), pkey.data(), int(pkey.size()));
  240. if (written <= 0 || static_cast<unsigned>(written) != pkey.size()) {
  241. throw std::runtime_error("BIO_write: " + getErrors());
  242. }
  243. ssl::EvpPkeyUniquePtr key(
  244. PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
  245. if (key == nullptr) {
  246. throw std::runtime_error("PEM_read_bio_PrivateKey: " + getErrors());
  247. }
  248. if (SSL_CTX_use_PrivateKey(ctx_, key.get()) == 0) {
  249. throw std::runtime_error("SSL_CTX_use_PrivateKey: " + getErrors());
  250. }
  251. }
  252. void SSLContext::loadCertKeyPairFromBufferPEM(
  253. folly::StringPiece cert,
  254. folly::StringPiece pkey) {
  255. loadCertificateFromBufferPEM(cert);
  256. loadPrivateKeyFromBufferPEM(pkey);
  257. if (!isCertKeyPairValid()) {
  258. throw std::runtime_error("SSL certificate and private key do not match");
  259. }
  260. }
  261. void SSLContext::loadCertKeyPairFromFiles(
  262. const char* certPath,
  263. const char* keyPath,
  264. const char* certFormat,
  265. const char* keyFormat) {
  266. loadCertificate(certPath, certFormat);
  267. loadPrivateKey(keyPath, keyFormat);
  268. if (!isCertKeyPairValid()) {
  269. throw std::runtime_error("SSL certificate and private key do not match");
  270. }
  271. }
  272. bool SSLContext::isCertKeyPairValid() const {
  273. return SSL_CTX_check_private_key(ctx_) == 1;
  274. }
  275. void SSLContext::loadTrustedCertificates(const char* path) {
  276. if (path == nullptr) {
  277. throw std::invalid_argument("loadTrustedCertificates: <path> is nullptr");
  278. }
  279. if (SSL_CTX_load_verify_locations(ctx_, path, nullptr) == 0) {
  280. throw std::runtime_error("SSL_CTX_load_verify_locations: " + getErrors());
  281. }
  282. ERR_clear_error();
  283. }
  284. void SSLContext::loadTrustedCertificates(X509_STORE* store) {
  285. SSL_CTX_set_cert_store(ctx_, store);
  286. }
  287. void SSLContext::loadClientCAList(const char* path) {
  288. auto clientCAs = SSL_load_client_CA_file(path);
  289. if (clientCAs == nullptr) {
  290. LOG(ERROR) << "Unable to load ca file: " << path << " " << getErrors();
  291. return;
  292. }
  293. SSL_CTX_set_client_CA_list(ctx_, clientCAs);
  294. }
  295. void SSLContext::passwordCollector(
  296. std::shared_ptr<PasswordCollector> collector) {
  297. if (collector == nullptr) {
  298. LOG(ERROR) << "passwordCollector: ignore invalid password collector";
  299. return;
  300. }
  301. collector_ = collector;
  302. SSL_CTX_set_default_passwd_cb(ctx_, passwordCallback);
  303. SSL_CTX_set_default_passwd_cb_userdata(ctx_, this);
  304. }
  305. #if FOLLY_OPENSSL_HAS_SNI
  306. void SSLContext::setServerNameCallback(const ServerNameCallback& cb) {
  307. serverNameCb_ = cb;
  308. }
  309. void SSLContext::addClientHelloCallback(const ClientHelloCallback& cb) {
  310. clientHelloCbs_.push_back(cb);
  311. }
  312. int SSLContext::baseServerNameOpenSSLCallback(SSL* ssl, int* al, void* data) {
  313. SSLContext* context = (SSLContext*)data;
  314. if (context == nullptr) {
  315. return SSL_TLSEXT_ERR_NOACK;
  316. }
  317. for (auto& cb : context->clientHelloCbs_) {
  318. // Generic callbacks to happen after we receive the Client Hello.
  319. // For example, we use one to switch which cipher we use depending
  320. // on the user's TLS version. Because the primary purpose of
  321. // baseServerNameOpenSSLCallback is for SNI support, and these callbacks
  322. // are side-uses, we ignore any possible failures other than just logging
  323. // them.
  324. cb(ssl);
  325. }
  326. if (!context->serverNameCb_) {
  327. return SSL_TLSEXT_ERR_NOACK;
  328. }
  329. ServerNameCallbackResult ret = context->serverNameCb_(ssl);
  330. switch (ret) {
  331. case SERVER_NAME_FOUND:
  332. return SSL_TLSEXT_ERR_OK;
  333. case SERVER_NAME_NOT_FOUND:
  334. return SSL_TLSEXT_ERR_NOACK;
  335. case SERVER_NAME_NOT_FOUND_ALERT_FATAL:
  336. *al = TLS1_AD_UNRECOGNIZED_NAME;
  337. return SSL_TLSEXT_ERR_ALERT_FATAL;
  338. default:
  339. CHECK(false);
  340. }
  341. return SSL_TLSEXT_ERR_NOACK;
  342. }
  343. #endif // FOLLY_OPENSSL_HAS_SNI
  344. #if FOLLY_OPENSSL_HAS_ALPN
  345. int SSLContext::alpnSelectCallback(
  346. SSL* /* ssl */,
  347. const unsigned char** out,
  348. unsigned char* outlen,
  349. const unsigned char* in,
  350. unsigned int inlen,
  351. void* data) {
  352. SSLContext* context = (SSLContext*)data;
  353. CHECK(context);
  354. if (context->advertisedNextProtocols_.empty()) {
  355. *out = nullptr;
  356. *outlen = 0;
  357. } else {
  358. auto i = context->pickNextProtocols();
  359. const auto& item = context->advertisedNextProtocols_[i];
  360. if (SSL_select_next_proto(
  361. (unsigned char**)out,
  362. outlen,
  363. item.protocols,
  364. item.length,
  365. in,
  366. inlen) != OPENSSL_NPN_NEGOTIATED) {
  367. return SSL_TLSEXT_ERR_NOACK;
  368. }
  369. }
  370. return SSL_TLSEXT_ERR_OK;
  371. }
  372. bool SSLContext::setAdvertisedNextProtocols(
  373. const std::list<std::string>& protocols) {
  374. return setRandomizedAdvertisedNextProtocols({{1, protocols}});
  375. }
  376. bool SSLContext::setRandomizedAdvertisedNextProtocols(
  377. const std::list<NextProtocolsItem>& items) {
  378. unsetNextProtocols();
  379. if (items.size() == 0) {
  380. return false;
  381. }
  382. int total_weight = 0;
  383. for (const auto& item : items) {
  384. if (item.protocols.size() == 0) {
  385. continue;
  386. }
  387. AdvertisedNextProtocolsItem advertised_item;
  388. advertised_item.length = 0;
  389. for (const auto& proto : item.protocols) {
  390. ++advertised_item.length;
  391. auto protoLength = proto.length();
  392. if (protoLength >= 256) {
  393. deleteNextProtocolsStrings();
  394. return false;
  395. }
  396. advertised_item.length += unsigned(protoLength);
  397. }
  398. advertised_item.protocols = new unsigned char[advertised_item.length];
  399. if (!advertised_item.protocols) {
  400. throw std::runtime_error("alloc failure");
  401. }
  402. unsigned char* dst = advertised_item.protocols;
  403. for (auto& proto : item.protocols) {
  404. uint8_t protoLength = uint8_t(proto.length());
  405. *dst++ = (unsigned char)protoLength;
  406. memcpy(dst, proto.data(), protoLength);
  407. dst += protoLength;
  408. }
  409. total_weight += item.weight;
  410. advertisedNextProtocols_.push_back(advertised_item);
  411. advertisedNextProtocolWeights_.push_back(item.weight);
  412. }
  413. if (total_weight == 0) {
  414. deleteNextProtocolsStrings();
  415. return false;
  416. }
  417. nextProtocolDistribution_ = std::discrete_distribution<>(
  418. advertisedNextProtocolWeights_.begin(),
  419. advertisedNextProtocolWeights_.end());
  420. SSL_CTX_set_alpn_select_cb(ctx_, alpnSelectCallback, this);
  421. // Client cannot really use randomized alpn
  422. // Note that this function reverses the typical return value convention
  423. // of openssl and returns 0 on success.
  424. if (SSL_CTX_set_alpn_protos(
  425. ctx_,
  426. advertisedNextProtocols_[0].protocols,
  427. advertisedNextProtocols_[0].length) != 0) {
  428. return false;
  429. }
  430. return true;
  431. }
  432. void SSLContext::deleteNextProtocolsStrings() {
  433. for (auto protocols : advertisedNextProtocols_) {
  434. delete[] protocols.protocols;
  435. }
  436. advertisedNextProtocols_.clear();
  437. advertisedNextProtocolWeights_.clear();
  438. }
  439. void SSLContext::unsetNextProtocols() {
  440. deleteNextProtocolsStrings();
  441. SSL_CTX_set_alpn_select_cb(ctx_, nullptr, nullptr);
  442. SSL_CTX_set_alpn_protos(ctx_, nullptr, 0);
  443. // clear the error stack here since openssl internals sometimes add a
  444. // malloc failure when doing a memdup of NULL, 0..
  445. ERR_clear_error();
  446. }
  447. size_t SSLContext::pickNextProtocols() {
  448. CHECK(!advertisedNextProtocols_.empty()) << "Failed to pickNextProtocols";
  449. auto rng = ThreadLocalPRNG();
  450. return size_t(nextProtocolDistribution_(rng));
  451. }
  452. #endif // FOLLY_OPENSSL_HAS_ALPN
  453. SSL* SSLContext::createSSL() const {
  454. SSL* ssl = SSL_new(ctx_);
  455. if (ssl == nullptr) {
  456. throw std::runtime_error("SSL_new: " + getErrors());
  457. }
  458. return ssl;
  459. }
  460. void SSLContext::setSessionCacheContext(const std::string& context) {
  461. SSL_CTX_set_session_id_context(
  462. ctx_,
  463. reinterpret_cast<const unsigned char*>(context.data()),
  464. std::min<unsigned int>(
  465. static_cast<unsigned int>(context.length()), SSL_MAX_SID_CTX_LENGTH));
  466. }
  467. /**
  468. * Match a name with a pattern. The pattern may include wildcard. A single
  469. * wildcard "*" can match up to one component in the domain name.
  470. *
  471. * @param host Host name, typically the name of the remote host
  472. * @param pattern Name retrieved from certificate
  473. * @param size Size of "pattern"
  474. * @return True, if "host" matches "pattern". False otherwise.
  475. */
  476. bool SSLContext::matchName(const char* host, const char* pattern, int size) {
  477. bool match = false;
  478. int i = 0, j = 0;
  479. while (i < size && host[j] != '\0') {
  480. if (toupper(pattern[i]) == toupper(host[j])) {
  481. i++;
  482. j++;
  483. continue;
  484. }
  485. if (pattern[i] == '*') {
  486. while (host[j] != '.' && host[j] != '\0') {
  487. j++;
  488. }
  489. i++;
  490. continue;
  491. }
  492. break;
  493. }
  494. if (i == size && host[j] == '\0') {
  495. match = true;
  496. }
  497. return match;
  498. }
  499. int SSLContext::passwordCallback(char* password, int size, int, void* data) {
  500. SSLContext* context = (SSLContext*)data;
  501. if (context == nullptr || context->passwordCollector() == nullptr) {
  502. return 0;
  503. }
  504. std::string userPassword;
  505. // call user defined password collector to get password
  506. context->passwordCollector()->getPassword(userPassword, size);
  507. auto const length = std::min(userPassword.size(), size_t(size));
  508. std::memcpy(password, userPassword.data(), length);
  509. return int(length);
  510. }
  511. #if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH)
  512. void SSLContext::enableFalseStart() {
  513. SSL_CTX_set_mode(ctx_, SSL_MODE_HANDSHAKE_CUTTHROUGH);
  514. }
  515. #endif
  516. void SSLContext::initializeOpenSSL() {
  517. folly::ssl::init();
  518. }
  519. void SSLContext::setOptions(long options) {
  520. long newOpt = SSL_CTX_set_options(ctx_, options);
  521. if ((newOpt & options) != options) {
  522. throw std::runtime_error("SSL_CTX_set_options failed");
  523. }
  524. }
  525. std::string SSLContext::getErrors(int errnoCopy) {
  526. std::string errors;
  527. unsigned long errorCode;
  528. char message[256];
  529. errors.reserve(512);
  530. while ((errorCode = ERR_get_error()) != 0) {
  531. if (!errors.empty()) {
  532. errors += "; ";
  533. }
  534. const char* reason = ERR_reason_error_string(errorCode);
  535. if (reason == nullptr) {
  536. snprintf(message, sizeof(message) - 1, "SSL error # %08lX", errorCode);
  537. reason = message;
  538. }
  539. errors += reason;
  540. }
  541. if (errors.empty()) {
  542. errors = "error code: " + folly::to<std::string>(errnoCopy);
  543. }
  544. return errors;
  545. }
  546. std::ostream& operator<<(std::ostream& os, const PasswordCollector& collector) {
  547. os << collector.describe();
  548. return os;
  549. }
  550. } // namespace folly