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