/mordor/streams/ssl.cpp

http://github.com/mozy/mordor · C++ · 730 lines · 637 code · 55 blank · 38 comment · 99 complexity · b93480671002c7c1e56d2e0387833173 MD5 · raw file

  1. // Copyright (c) 2009 - Mozy, Inc.
  2. #include "ssl.h"
  3. #include <sstream>
  4. #include <openssl/err.h>
  5. #include <openssl/x509v3.h>
  6. #include "mordor/assert.h"
  7. #include "mordor/log.h"
  8. #include "mordor/util.h"
  9. #ifdef MSVC
  10. #pragma comment(lib, "libeay32")
  11. #pragma comment(lib, "ssleay32")
  12. #endif
  13. namespace Mordor {
  14. static Logger::ptr g_log = Log::lookup("mordor:streams:ssl");
  15. namespace {
  16. static struct SSLInitializer {
  17. SSLInitializer()
  18. {
  19. SSL_library_init();
  20. SSL_load_error_strings();
  21. }
  22. ~SSLInitializer()
  23. {
  24. ERR_free_strings();
  25. CRYPTO_cleanup_all_ex_data();
  26. EVP_cleanup();
  27. }
  28. } g_init;
  29. }
  30. static bool hasOpenSSLError()
  31. {
  32. unsigned long err = ERR_peek_error();
  33. if (err == SSL_ERROR_NONE)
  34. return false;
  35. switch (ERR_GET_REASON(err)) {
  36. case ERR_R_MALLOC_FAILURE:
  37. throw std::bad_alloc();
  38. case ERR_R_PASSED_NULL_PARAMETER:
  39. {
  40. char buf[120];
  41. ERR_error_string(err, buf);
  42. MORDOR_THROW_EXCEPTION(std::invalid_argument(buf));
  43. }
  44. default:
  45. return true;
  46. }
  47. }
  48. static std::string getOpenSSLErrorMessage()
  49. {
  50. std::ostringstream os;
  51. unsigned long err;
  52. char buf[120];
  53. while ( (err = ERR_get_error()) != SSL_ERROR_NONE) {
  54. if (!os.str().empty())
  55. os << "\n";
  56. os << ERR_error_string(err, buf);
  57. }
  58. return os.str();
  59. }
  60. OpenSSLException::OpenSSLException() :
  61. std::runtime_error(getOpenSSLErrorMessage())
  62. {
  63. }
  64. std::string
  65. CertificateVerificationException::constructMessage(long verifyResult)
  66. {
  67. return X509_verify_cert_error_string(verifyResult);
  68. }
  69. // Adapted from https://www.codeblog.org/viewsrc/openssl-fips-1.1.1/demos/x509/mkcert.c
  70. static void add_ext(X509 *cert, int nid, const char *value);
  71. static void mkcert(boost::shared_ptr<X509> &cert,
  72. boost::shared_ptr<EVP_PKEY> &pkey, int bits, int serial,
  73. int days)
  74. {
  75. RSA *rsa;
  76. X509_NAME *name=NULL;
  77. pkey.reset(EVP_PKEY_new(), &EVP_PKEY_free);
  78. if (!pkey)
  79. throw std::bad_alloc();
  80. cert.reset(X509_new(), &X509_free);
  81. if (!cert)
  82. throw std::bad_alloc();
  83. rsa = RSA_generate_key(bits,RSA_F4,NULL,NULL);
  84. MORDOR_VERIFY(EVP_PKEY_assign_RSA(pkey.get(),rsa));
  85. X509_set_version(cert.get(),2);
  86. ASN1_INTEGER_set(X509_get_serialNumber(cert.get()),serial);
  87. X509_gmtime_adj(X509_get_notBefore(cert.get()),0);
  88. X509_gmtime_adj(X509_get_notAfter(cert.get()),(long)60*60*24*days);
  89. X509_set_pubkey(cert.get(),pkey.get());
  90. name=X509_get_subject_name(cert.get());
  91. /* This function creates and adds the entry, working out the
  92. * correct string type and performing checks on its length.
  93. * Normally we'd check the return value for errors...
  94. */
  95. X509_NAME_add_entry_by_txt(name,"C",
  96. MBSTRING_ASC,
  97. (const unsigned char *)"United States",
  98. -1, -1, 0);
  99. X509_NAME_add_entry_by_txt(name,"CN",
  100. MBSTRING_ASC,
  101. (const unsigned char *)"Mordor Default Self-signed Certificate",
  102. -1, -1, 0);
  103. /* Its self signed so set the issuer name to be the same as the
  104. * subject.
  105. */
  106. X509_set_issuer_name(cert.get(),name);
  107. /* Add various extensions: standard extensions */
  108. add_ext(cert.get(), NID_basic_constraints, "critical,CA:TRUE");
  109. add_ext(cert.get(), NID_key_usage, "critical,keyCertSign,cRLSign");
  110. add_ext(cert.get(), NID_subject_key_identifier, "hash");
  111. /* Some Netscape specific extensions */
  112. add_ext(cert.get(), NID_netscape_cert_type, "sslCA");
  113. MORDOR_VERIFY(X509_sign(cert.get(),pkey.get(),EVP_md5()));
  114. }
  115. /* Add extension using V3 code: we can set the config file as NULL
  116. * because we wont reference any other sections.
  117. */
  118. void add_ext(X509 *cert, int nid, const char *value)
  119. {
  120. X509V3_CTX ctx;
  121. /* This sets the 'context' of the extensions. */
  122. /* No configuration database */
  123. X509V3_set_ctx_nodb(&ctx);
  124. /* Issuer and subject certs: both the target since it is self signed,
  125. * no request and no CRL
  126. */
  127. X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
  128. X509_EXTENSION *ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, (char*) value);
  129. MORDOR_VERIFY(ex);
  130. X509_add_ext(cert,ex,-1);
  131. X509_EXTENSION_free(ex);
  132. }
  133. SSLStream::SSLStream(Stream::ptr parent, bool client, bool own, SSL_CTX *ctx)
  134. : MutatingFilterStream(parent, own)
  135. {
  136. MORDOR_ASSERT(parent);
  137. clearSSLError();
  138. if (ctx)
  139. m_ctx.reset(ctx, &nop<SSL_CTX *>);
  140. else
  141. m_ctx.reset(SSL_CTX_new(client ? SSLv23_client_method() :
  142. SSLv23_server_method()), &SSL_CTX_free);
  143. if (!m_ctx) {
  144. MORDOR_ASSERT(hasOpenSSLError());
  145. MORDOR_THROW_EXCEPTION(OpenSSLException(getOpenSSLErrorMessage()))
  146. << boost::errinfo_api_function("SSL_CTX_new");
  147. }
  148. // Auto-generate self-signed server cert
  149. if (!ctx && !client) {
  150. boost::shared_ptr<X509> cert;
  151. boost::shared_ptr<EVP_PKEY> pkey;
  152. mkcert(cert, pkey, 1024, rand(), 365);
  153. SSL_CTX_use_certificate(m_ctx.get(), cert.get());
  154. SSL_CTX_use_PrivateKey(m_ctx.get(), pkey.get());
  155. }
  156. m_ssl.reset(SSL_new(m_ctx.get()), &SSL_free);
  157. if (!m_ssl) {
  158. MORDOR_ASSERT(hasOpenSSLError());
  159. MORDOR_THROW_EXCEPTION(OpenSSLException(getOpenSSLErrorMessage()))
  160. << boost::errinfo_api_function("SSL_CTX_new");
  161. }
  162. m_readBio = BIO_new(BIO_s_mem());
  163. m_writeBio = BIO_new(BIO_s_mem());
  164. if (!m_readBio || !m_writeBio) {
  165. if (m_readBio) BIO_free(m_readBio);
  166. if (m_writeBio) BIO_free(m_writeBio);
  167. MORDOR_ASSERT(hasOpenSSLError());
  168. MORDOR_THROW_EXCEPTION(OpenSSLException(getOpenSSLErrorMessage()))
  169. << boost::errinfo_api_function("BIO_new");
  170. }
  171. BIO_set_mem_eof_return(m_readBio, -1);
  172. SSL_set_bio(m_ssl.get(), m_readBio, m_writeBio);
  173. }
  174. void
  175. SSLStream::clearSSLError()
  176. {
  177. std::string msg;
  178. unsigned long err = SSL_ERROR_NONE;
  179. while ((err = ERR_get_error()) != SSL_ERROR_NONE) {
  180. switch (ERR_GET_REASON(err)) {
  181. case ERR_R_MALLOC_FAILURE:
  182. msg = "bad alloc";
  183. break;
  184. case ERR_R_PASSED_NULL_PARAMETER:
  185. msg = "invalid argument";
  186. break;
  187. default:
  188. {
  189. char buf[120];
  190. const char * errBuf = ERR_error_string(err, buf);
  191. if (errBuf != NULL) {
  192. msg = errBuf;
  193. }
  194. }
  195. }
  196. MORDOR_LOG_ERROR(g_log) << this
  197. << " ssl: " << m_ssl.get()
  198. << " ignoring error: " << err
  199. << " error msg: " << msg;
  200. };
  201. // Clear it again for insurance.
  202. ERR_clear_error();
  203. }
  204. void
  205. SSLStream::close(CloseType type)
  206. {
  207. MORDOR_ASSERT(type == BOTH);
  208. if (!(sslCallWithLock(boost::bind(SSL_get_shutdown, m_ssl.get()), NULL) & SSL_SENT_SHUTDOWN)) {
  209. unsigned long error = SSL_ERROR_NONE;
  210. const int result = sslCallWithLock(boost::bind(SSL_shutdown, m_ssl.get()), &error);
  211. if (result <= 0) {
  212. MORDOR_LOG_DEBUG(g_log) << this << " SSL_shutdown(" << m_ssl.get()
  213. << "): " << result << " (" << error << ")";
  214. switch (error) {
  215. case SSL_ERROR_NONE:
  216. case SSL_ERROR_ZERO_RETURN:
  217. break;
  218. case SSL_ERROR_WANT_READ:
  219. case SSL_ERROR_WANT_WRITE:
  220. case SSL_ERROR_WANT_CONNECT:
  221. case SSL_ERROR_WANT_ACCEPT:
  222. case SSL_ERROR_WANT_X509_LOOKUP:
  223. MORDOR_NOTREACHED();
  224. case SSL_ERROR_SYSCALL:
  225. if (hasOpenSSLError()) {
  226. std::string message = getOpenSSLErrorMessage();
  227. MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown("
  228. << m_ssl.get() << "): " << result << " (" << error
  229. << ", " << message << ")";
  230. MORDOR_THROW_EXCEPTION(OpenSSLException(message))
  231. << boost::errinfo_api_function("SSL_shutdown");
  232. }
  233. MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown("
  234. << m_ssl.get() << "): " << result << " (" << error
  235. << ")";
  236. if (result == 0)
  237. break;
  238. MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_shutdown");
  239. case SSL_ERROR_SSL:
  240. {
  241. MORDOR_ASSERT(hasOpenSSLError());
  242. std::string message = getOpenSSLErrorMessage();
  243. MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown("
  244. << m_ssl.get() << "): " << result << " (" << error
  245. << ", " << message << ")";
  246. MORDOR_THROW_EXCEPTION(OpenSSLException(message))
  247. << boost::errinfo_api_function("SSL_shutdown");
  248. }
  249. default:
  250. MORDOR_NOTREACHED();
  251. }
  252. }
  253. flush(false);
  254. }
  255. // follow apache to call SSL_shutdown blindly without error handling
  256. int count = 0;
  257. while (!(sslCallWithLock(boost::bind(SSL_get_shutdown, m_ssl.get()), NULL) & SSL_RECEIVED_SHUTDOWN)) {
  258. unsigned long error = SSL_ERROR_NONE;
  259. const int result = sslCallWithLock(boost::bind(SSL_shutdown, m_ssl.get()), &error);
  260. MORDOR_LOG_DEBUG(g_log) << this << " SSL_shutdown(" << m_ssl.get()
  261. << "): " << result << " (" << error << "), count=" << count;
  262. if (result > 0 || ++count == 3) {
  263. break;
  264. }
  265. }
  266. parent()->close();
  267. }
  268. size_t
  269. SSLStream::read(void *buffer, size_t length)
  270. {
  271. const int toRead = (int)std::min<size_t>(0x0fffffff, length);
  272. while (true) {
  273. unsigned long error = SSL_ERROR_NONE;
  274. const int result = sslCallWithLock(boost::bind(SSL_read, m_ssl.get(), buffer, toRead), &error);
  275. if (result > 0) {
  276. return result;
  277. }
  278. MORDOR_LOG_DEBUG(g_log) << this << " SSL_read(" << m_ssl.get() << ", "
  279. << toRead << "): " << result << " (" << error << ")";
  280. switch (error) {
  281. case SSL_ERROR_NONE:
  282. return result;
  283. case SSL_ERROR_ZERO_RETURN:
  284. // Received close_notify message
  285. MORDOR_ASSERT(result == 0);
  286. return 0;
  287. case SSL_ERROR_WANT_READ:
  288. wantRead();
  289. continue;
  290. case SSL_ERROR_WANT_WRITE:
  291. case SSL_ERROR_WANT_CONNECT:
  292. case SSL_ERROR_WANT_ACCEPT:
  293. case SSL_ERROR_WANT_X509_LOOKUP:
  294. MORDOR_NOTREACHED();
  295. case SSL_ERROR_SYSCALL:
  296. if (hasOpenSSLError()) {
  297. std::string message = getOpenSSLErrorMessage();
  298. MORDOR_LOG_ERROR(g_log) << this << " SSL_read("
  299. << m_ssl.get() << ", " << toRead << "): " << result
  300. << " (" << error << ", " << message << ")";
  301. MORDOR_THROW_EXCEPTION(OpenSSLException(message))
  302. << boost::errinfo_api_function("SSL_read");
  303. }
  304. MORDOR_LOG_WARNING(g_log) << this << " SSL_read("
  305. << m_ssl.get() << ", " << toRead << "): " << result
  306. << " (" << error << ")";
  307. if (result == 0) {
  308. return 0;
  309. }
  310. MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_read");
  311. case SSL_ERROR_SSL:
  312. {
  313. MORDOR_ASSERT(hasOpenSSLError());
  314. std::string message = getOpenSSLErrorMessage();
  315. MORDOR_LOG_ERROR(g_log) << this << " SSL_read("
  316. << m_ssl.get() << ", " << toRead << "): " << result
  317. << " (" << error << ", " << message << ")";
  318. MORDOR_THROW_EXCEPTION(OpenSSLException(message))
  319. << boost::errinfo_api_function("SSL_read");
  320. }
  321. default:
  322. MORDOR_NOTREACHED();
  323. }
  324. }
  325. }
  326. size_t
  327. SSLStream::write(const Buffer &buffer, size_t length)
  328. {
  329. // SSL_write will create at least two SSL records for each call -
  330. // one for data, and one tiny one for the checksum or IV or something.
  331. // Dealing with lots of extra records can take some serious CPU time
  332. // server-side, so we want to provide it with as much data as possible,
  333. // even if that means reallocating. That's why we use pass the flag to
  334. // coalesce small segments, instead of only doing the first available
  335. // segment
  336. return Stream::write(buffer, length, true);
  337. }
  338. size_t
  339. SSLStream::write(const void *buffer, size_t length)
  340. {
  341. flush(false);
  342. if (length == 0)
  343. return 0;
  344. const int toWrite = (int)std::min<size_t>(0x7fffffff, length);
  345. while (true) {
  346. unsigned long error = SSL_ERROR_NONE;
  347. const int result = sslCallWithLock(boost::bind(SSL_write, m_ssl.get(), buffer, toWrite), &error);
  348. if (result > 0) {
  349. return result;
  350. }
  351. MORDOR_LOG_DEBUG(g_log) << this << " SSL_write(" << m_ssl.get() << ", "
  352. << toWrite << "): " << result << " (" << error << ")";
  353. switch (error) {
  354. case SSL_ERROR_NONE:
  355. return result;
  356. case SSL_ERROR_ZERO_RETURN:
  357. // Received close_notify message
  358. MORDOR_ASSERT(result != 0);
  359. return result;
  360. case SSL_ERROR_WANT_READ:
  361. MORDOR_THROW_EXCEPTION(OpenSSLException("SSL_write generated SSL_ERROR_WANT_READ"));
  362. case SSL_ERROR_WANT_WRITE:
  363. case SSL_ERROR_WANT_CONNECT:
  364. case SSL_ERROR_WANT_ACCEPT:
  365. case SSL_ERROR_WANT_X509_LOOKUP:
  366. MORDOR_NOTREACHED();
  367. case SSL_ERROR_SYSCALL:
  368. if (hasOpenSSLError()) {
  369. std::string message = getOpenSSLErrorMessage();
  370. MORDOR_LOG_ERROR(g_log) << this << " SSL_write("
  371. << m_ssl.get() << ", " << toWrite << "): "
  372. << result << " (" << error << ", " << message
  373. << ")";
  374. MORDOR_THROW_EXCEPTION(OpenSSLException(message))
  375. << boost::errinfo_api_function("SSL_write");
  376. }
  377. MORDOR_LOG_ERROR(g_log) << this << " SSL_write("
  378. << m_ssl.get() << ", " << toWrite << "): " << result
  379. << " (" << error << ")";
  380. if (result == 0) {
  381. MORDOR_THROW_EXCEPTION(UnexpectedEofException());
  382. }
  383. MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_write");
  384. case SSL_ERROR_SSL:
  385. {
  386. MORDOR_ASSERT(hasOpenSSLError());
  387. std::string message = getOpenSSLErrorMessage();
  388. MORDOR_LOG_ERROR(g_log) << this << " SSL_write("
  389. << m_ssl.get() << ", " << toWrite << "): " << result
  390. << " (" << error << ", " << message << ")";
  391. MORDOR_THROW_EXCEPTION(OpenSSLException(message))
  392. << boost::errinfo_api_function("SSL_write");
  393. }
  394. default:
  395. MORDOR_NOTREACHED();
  396. }
  397. }
  398. }
  399. void
  400. SSLStream::flush(bool flushParent)
  401. {
  402. static const int WRITE_BUF_LENGTH = 4096;
  403. char writeBuf[WRITE_BUF_LENGTH];
  404. int toWrite = 0;
  405. do {
  406. toWrite = BIO_read(m_writeBio, (void *)writeBuf, WRITE_BUF_LENGTH);
  407. if (toWrite > 0) {
  408. m_writeBuffer.copyIn((const void *)writeBuf, toWrite);
  409. }
  410. } while (toWrite > 0);
  411. if (m_writeBuffer.readAvailable() == 0)
  412. return;
  413. while (m_writeBuffer.readAvailable()) {
  414. MORDOR_LOG_TRACE(g_log) << this << " parent()->write("
  415. << m_writeBuffer.readAvailable() << ")";
  416. size_t written = parent()->write(m_writeBuffer,
  417. m_writeBuffer.readAvailable());
  418. MORDOR_LOG_TRACE(g_log) << this << " parent()->write("
  419. << m_writeBuffer.readAvailable() << "): " << written;
  420. m_writeBuffer.consume(written);
  421. }
  422. if (flushParent)
  423. parent()->flush(flushParent);
  424. }
  425. void
  426. SSLStream::accept()
  427. {
  428. while (true) {
  429. unsigned long error = SSL_ERROR_NONE;
  430. const int result = sslCallWithLock(boost::bind(SSL_accept, m_ssl.get()), &error);
  431. if (result > 0) {
  432. flush(false);
  433. return;
  434. }
  435. MORDOR_LOG_DEBUG(g_log) << this << " SSL_accept(" << m_ssl.get()
  436. << "): " << result << " (" << error << ")";
  437. switch (error) {
  438. case SSL_ERROR_NONE:
  439. flush(false);
  440. return;
  441. case SSL_ERROR_ZERO_RETURN:
  442. // Received close_notify message
  443. return;
  444. case SSL_ERROR_WANT_READ:
  445. flush();
  446. wantRead();
  447. continue;
  448. case SSL_ERROR_WANT_WRITE:
  449. case SSL_ERROR_WANT_CONNECT:
  450. case SSL_ERROR_WANT_ACCEPT:
  451. case SSL_ERROR_WANT_X509_LOOKUP:
  452. MORDOR_NOTREACHED();
  453. case SSL_ERROR_SYSCALL:
  454. if (hasOpenSSLError()) {
  455. std::string message = getOpenSSLErrorMessage();
  456. MORDOR_LOG_ERROR(g_log) << this << " SSL_accept("
  457. << m_ssl.get() << "): " << result << " (" << error
  458. << ", " << message << ")";
  459. MORDOR_THROW_EXCEPTION(OpenSSLException(message))
  460. << boost::errinfo_api_function("SSL_accept");
  461. }
  462. MORDOR_LOG_ERROR(g_log) << this << " SSL_accept("
  463. << m_ssl.get() << "): " << result << " (" << error
  464. << ")";
  465. if (result == 0) {
  466. MORDOR_THROW_EXCEPTION(UnexpectedEofException());
  467. }
  468. MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_accept");
  469. case SSL_ERROR_SSL:
  470. {
  471. MORDOR_ASSERT(hasOpenSSLError());
  472. std::string message = getOpenSSLErrorMessage();
  473. MORDOR_LOG_ERROR(g_log) << this << " SSL_accept("
  474. << m_ssl.get() << "): " << result << " (" << error
  475. << ", " << message << ")";
  476. MORDOR_THROW_EXCEPTION(OpenSSLException(message))
  477. << boost::errinfo_api_function("SSL_accept");
  478. }
  479. default:
  480. MORDOR_NOTREACHED();
  481. }
  482. }
  483. }
  484. void
  485. SSLStream::connect()
  486. {
  487. while (true) {
  488. unsigned long error = SSL_ERROR_NONE;
  489. const int result = sslCallWithLock(boost::bind(SSL_connect, m_ssl.get()), &error);
  490. MORDOR_LOG_DEBUG(g_log) << this << " SSL_connect(" << m_ssl.get()
  491. << "): " << result << " (" << error << ")";
  492. if (result > 0) {
  493. flush(false);
  494. return;
  495. }
  496. switch (error) {
  497. case SSL_ERROR_NONE:
  498. flush(false);
  499. return;
  500. case SSL_ERROR_ZERO_RETURN:
  501. // Received close_notify message
  502. return;
  503. case SSL_ERROR_WANT_READ:
  504. flush();
  505. wantRead();
  506. continue;
  507. case SSL_ERROR_WANT_WRITE:
  508. case SSL_ERROR_WANT_CONNECT:
  509. case SSL_ERROR_WANT_ACCEPT:
  510. case SSL_ERROR_WANT_X509_LOOKUP:
  511. MORDOR_NOTREACHED();
  512. case SSL_ERROR_SYSCALL:
  513. if (hasOpenSSLError()) {
  514. std::string message = getOpenSSLErrorMessage();
  515. MORDOR_LOG_ERROR(g_log) << this << " SSL_connect("
  516. << m_ssl.get() << "): " << result << " (" << error
  517. << ", " << message << ")";
  518. MORDOR_THROW_EXCEPTION(OpenSSLException(message))
  519. << boost::errinfo_api_function("SSL_connect");
  520. }
  521. MORDOR_LOG_ERROR(g_log) << this << " SSL_connect("
  522. << m_ssl.get() << "): " << result << " (" << error
  523. << ")";
  524. if (result == 0) {
  525. MORDOR_THROW_EXCEPTION(UnexpectedEofException());
  526. }
  527. MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_connect");
  528. case SSL_ERROR_SSL:
  529. {
  530. MORDOR_ASSERT(hasOpenSSLError());
  531. std::string message = getOpenSSLErrorMessage();
  532. MORDOR_LOG_ERROR(g_log) << this << " SSL_connect("
  533. << m_ssl.get() << "): " << result << " (" << error
  534. << ", " << message << ")";
  535. MORDOR_THROW_EXCEPTION(OpenSSLException(message))
  536. << boost::errinfo_api_function("SSL_connect");
  537. }
  538. default:
  539. MORDOR_NOTREACHED();
  540. }
  541. }
  542. }
  543. void
  544. SSLStream::serverNameIndication(const std::string &hostname)
  545. {
  546. // Older versions of OpenSSL don't support this (I'm looking at you,
  547. // Leopard); just ignore it then
  548. #ifdef SSL_set_tlsext_host_name
  549. boost::mutex::scoped_lock lock(m_mutex);
  550. if (!SSL_set_tlsext_host_name(m_ssl.get(), hostname.c_str())) {
  551. if (!hasOpenSSLError()) return;
  552. std::string message = getOpenSSLErrorMessage();
  553. MORDOR_LOG_ERROR(g_log) << this << " SSL_set_tlsext_host_name("
  554. << m_ssl.get() << ", " << hostname.c_str() << "): " << message;
  555. MORDOR_THROW_EXCEPTION(OpenSSLException(message))
  556. << boost::errinfo_api_function("SSL_set_tlsext_host_name");
  557. }
  558. #endif
  559. }
  560. void
  561. SSLStream::verifyPeerCertificate()
  562. {
  563. const long verifyResult = sslCallWithLock(boost::bind(SSL_get_verify_result, m_ssl.get()), NULL);
  564. MORDOR_LOG_LEVEL(g_log, verifyResult ? Log::WARNING : Log::DEBUG) << this
  565. << " SSL_get_verify_result(" << m_ssl.get() << "): "
  566. << verifyResult;
  567. if (verifyResult != X509_V_OK)
  568. MORDOR_THROW_EXCEPTION(CertificateVerificationException(verifyResult));
  569. }
  570. void
  571. SSLStream::verifyPeerCertificate(const std::string &hostname)
  572. {
  573. if (!hostname.empty()) {
  574. boost::mutex::scoped_lock lock(m_mutex);
  575. std::string wildcardHostname = "*";
  576. size_t dot = hostname.find('.');
  577. if (dot != std::string::npos)
  578. wildcardHostname.append(hostname.substr(dot));
  579. boost::shared_ptr<X509> cert;
  580. cert.reset(SSL_get_peer_certificate(m_ssl.get()), &X509_free);
  581. if (!cert)
  582. MORDOR_THROW_EXCEPTION(CertificateVerificationException(
  583. X509_V_ERR_APPLICATION_VERIFICATION,
  584. "No Certificate Presented"));
  585. int critical = -1, altNameIndex = -1;
  586. GENERAL_NAMES *gens = (GENERAL_NAMES *)X509_get_ext_d2i(cert.get(),
  587. NID_subject_alt_name, &critical, &altNameIndex);
  588. if (gens) {
  589. do {
  590. try {
  591. bool success = false;
  592. for(int i = 0; i < sk_GENERAL_NAME_num(gens); i++)
  593. {
  594. GENERAL_NAME *gen = sk_GENERAL_NAME_value(gens, i);
  595. if(gen->type != GEN_DNS) continue;
  596. std::string altName((const char *)gen->d.dNSName->data,
  597. gen->d.dNSName->length);
  598. if (altName == wildcardHostname ||
  599. altName == hostname) {
  600. success = true;
  601. break;
  602. }
  603. }
  604. sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
  605. if (success)
  606. return;
  607. } catch (...) {
  608. sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
  609. throw;
  610. }
  611. gens = (GENERAL_NAMES *)X509_get_ext_d2i(cert.get(),
  612. NID_subject_alt_name, &critical, &altNameIndex);
  613. } while (gens);
  614. }
  615. X509_NAME *name = X509_get_subject_name(cert.get());
  616. if (!name)
  617. MORDOR_THROW_EXCEPTION(CertificateVerificationException(
  618. X509_V_ERR_APPLICATION_VERIFICATION,
  619. "No Subject Name"));
  620. int len = X509_NAME_get_text_by_NID(name, NID_commonName, NULL, 0);
  621. if (len == -1)
  622. MORDOR_THROW_EXCEPTION(CertificateVerificationException(
  623. X509_V_ERR_APPLICATION_VERIFICATION,
  624. "No Common Name"));
  625. std::string commonName;
  626. commonName.resize(len);
  627. X509_NAME_get_text_by_NID(name, NID_commonName, &commonName[0],
  628. len + 1);
  629. if (commonName == wildcardHostname || commonName == hostname)
  630. return;
  631. MORDOR_THROW_EXCEPTION(CertificateVerificationException(
  632. X509_V_ERR_APPLICATION_VERIFICATION,
  633. "No Matching Common Name"));
  634. }
  635. }
  636. void
  637. SSLStream::wantRead()
  638. {
  639. if (m_readBuffer.readAvailable() == 0) {
  640. MORDOR_LOG_TRACE(g_log) << this << " parent()->read(32768)";
  641. const size_t result = parent()->read(m_readBuffer, 32768);
  642. MORDOR_LOG_TRACE(g_log) << this << " parent()->read(32768): " << result;
  643. if (result == 0) {
  644. BIO_set_mem_eof_return(m_readBio, 0);
  645. return;
  646. }
  647. }
  648. MORDOR_ASSERT(m_readBuffer.readAvailable());
  649. const iovec iov = m_readBuffer.readBuffer(~0, false);
  650. MORDOR_ASSERT(iov.iov_len > 0);
  651. const int written = BIO_write(m_readBio, (char *)iov.iov_base, iov.iov_len);
  652. MORDOR_ASSERT(written > 0);
  653. if (written > 0) {
  654. m_readBuffer.consume(written);
  655. }
  656. MORDOR_LOG_DEBUG(g_log) << this << " wantRead(): " << written;
  657. }
  658. int
  659. SSLStream::sslCallWithLock(boost::function<int ()> dg, unsigned long *error)
  660. {
  661. boost::mutex::scoped_lock lock(m_mutex);
  662. // If error is NULL, it means that sslCallWithLock is not supposed to call SSL_get_error
  663. // after dg got called. If SSL_get_error is not supposed to be called, there is no need
  664. // to clear current thread's error queue.
  665. if (error == NULL) {
  666. return dg();
  667. }
  668. clearSSLError();
  669. const int result = dg();
  670. if (result <= 0) {
  671. *error = SSL_get_error(m_ssl.get(), result);
  672. }
  673. return result;
  674. }
  675. }