size_t SSLStream::read(void *buffer, size_t length) { const int toRead = (int)std::min<size_t>(0x0fffffff, length); while (true) { unsigned long error = SSL_ERROR_NONE; const int result = sslCallWithLock(std::bind(SSL_read, m_ssl.get(), buffer, toRead), &error); if (result > 0) { return result; } MORDOR_LOG_DEBUG(g_log) << this << " SSL_read(" << m_ssl.get() << ", " << toRead << "): " << result << " (" << error << ")"; switch (error) { case SSL_ERROR_NONE: return result; case SSL_ERROR_ZERO_RETURN: // Received close_notify message MORDOR_ASSERT(result == 0); return 0; case SSL_ERROR_WANT_READ: wantRead(); continue; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: MORDOR_NOTREACHED(); case SSL_ERROR_SYSCALL: if (hasOpenSSLError()) { std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_read(" << m_ssl.get() << ", " << toRead << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_read"); ; } MORDOR_LOG_WARNING(g_log) << this << " SSL_read(" << m_ssl.get() << ", " << toRead << "): " << result << " (" << error << ")"; if (result == 0) { return 0; } MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_read"); case SSL_ERROR_SSL: { MORDOR_ASSERT(hasOpenSSLError()); std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_read(" << m_ssl.get() << ", " << toRead << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_read"); ; } default: MORDOR_NOTREACHED(); } } }
void parse_pem(const std::string& crl_txt) { BIO *bio = BIO_new_mem_buf(const_cast<char *>(crl_txt.c_str()), crl_txt.length()); if (!bio) throw OpenSSLException(); X509_CRL *crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL); BIO_free(bio); if (!crl) throw OpenSSLException("CRL::parse_pem"); erase(); crl_ = crl; }
void parse_pem(const std::string& cert_txt, const std::string& title) { BIO *bio = BIO_new_mem_buf(const_cast<char *>(cert_txt.c_str()), cert_txt.length()); if (!bio) throw OpenSSLException(); ::X509 *cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); BIO_free(bio); if (!cert) throw OpenSSLException(std::string("X509::parse_pem: error in ") + title + std::string(":")); erase(); x509_ = cert; }
SSLStream::SSLStream(Stream::ptr parent, bool client, bool own, SSL_CTX *ctx) : MutatingFilterStream(parent, own) { MORDOR_ASSERT(parent); clearSSLError(); if (ctx) m_ctx.reset(ctx, &nop<SSL_CTX *>); else m_ctx.reset(SSL_CTX_new(client ? SSLv23_client_method() : SSLv23_server_method()), &SSL_CTX_free); if (!m_ctx) { MORDOR_ASSERT(hasOpenSSLError()); MORDOR_THROW_EXCEPTION(OpenSSLException(getOpenSSLErrorMessage())) // << boost::errinfo_api_function("SSL_CTX_new"); ; } // Auto-generate self-signed server cert if (!ctx && !client) { std::shared_ptr<X509> cert; std::shared_ptr<EVP_PKEY> pkey; mkcert(cert, pkey, 1024, rand(), 365); SSL_CTX_use_certificate(m_ctx.get(), cert.get()); SSL_CTX_use_PrivateKey(m_ctx.get(), pkey.get()); } m_ssl.reset(SSL_new(m_ctx.get()), &SSL_free); if (!m_ssl) { MORDOR_ASSERT(hasOpenSSLError()); MORDOR_THROW_EXCEPTION(OpenSSLException(getOpenSSLErrorMessage())) // << boost::errinfo_api_function("SSL_CTX_new"); ; } m_readBio = BIO_new(BIO_s_mem()); m_writeBio = BIO_new(BIO_s_mem()); if (!m_readBio || !m_writeBio) { if (m_readBio) BIO_free(m_readBio); if (m_writeBio) BIO_free(m_writeBio); MORDOR_ASSERT(hasOpenSSLError()); MORDOR_THROW_EXCEPTION(OpenSSLException(getOpenSSLErrorMessage())) // << boost::errinfo_api_function("BIO_new"); ; } BIO_set_mem_eof_return(m_readBio, -1); SSL_set_bio(m_ssl.get(), m_readBio, m_writeBio); }
RSAKeyImpl::RSAKeyImpl(const X509Certificate& cert): KeyPairImpl("rsa", KT_RSA_IMPL), _pRSA(0) { const X509* pCert = cert.certificate(); EVP_PKEY* pKey = X509_get_pubkey(const_cast<X509*>(pCert)); if (pKey) { _pRSA = EVP_PKEY_get1_RSA(pKey); EVP_PKEY_free(pKey); } else throw OpenSSLException("RSAKeyImpl(const X509Certificate&)"); }
void SSLStream::serverNameIndication(const std::string &hostname) { // Older versions of OpenSSL don't support this (I'm looking at you, // Leopard); just ignore it then #ifdef SSL_set_tlsext_host_name std::lock_guard<std::mutex> lock(m_mutex); if (!SSL_set_tlsext_host_name(m_ssl.get(), hostname.c_str())) { if (!hasOpenSSLError()) return; std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_set_tlsext_host_name(" << m_ssl.get() << ", " << hostname.c_str() << "): " << message; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_set_tlsext_host_name"); ; } #endif }
std::string render_pem() const { if (crl_) { BIO *bio = BIO_new(BIO_s_mem()); const int ret = PEM_write_bio_X509_CRL(bio, crl_); if (ret == 0) { BIO_free(bio); throw OpenSSLException("CRL::render_pem"); } { char *temp; const int buf_len = BIO_get_mem_data(bio, &temp); std::string ret = std::string(temp, buf_len); BIO_free(bio); return ret; } } else return ""; }
RSAKeyImpl::RSAKeyImpl(const EVPPKey& key): KeyPairImpl("rsa", KT_RSA_IMPL), _pRSA(EVP_PKEY_get1_RSA(const_cast<EVP_PKEY*>((const EVP_PKEY*)key))) { if (!_pRSA) throw OpenSSLException(); }
void SSLStream::connect() { while (true) { unsigned long error = SSL_ERROR_NONE; const int result = sslCallWithLock(std::bind(SSL_connect, m_ssl.get()), &error); MORDOR_LOG_DEBUG(g_log) << this << " SSL_connect(" << m_ssl.get() << "): " << result << " (" << error << ")"; if (result > 0) { flush(false); return; } switch (error) { case SSL_ERROR_NONE: flush(false); return; case SSL_ERROR_ZERO_RETURN: // Received close_notify message return; case SSL_ERROR_WANT_READ: flush(); wantRead(); continue; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: MORDOR_NOTREACHED(); case SSL_ERROR_SYSCALL: if (hasOpenSSLError()) { std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_connect(" << m_ssl.get() << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_connect"); ; } MORDOR_LOG_ERROR(g_log) << this << " SSL_connect(" << m_ssl.get() << "): " << result << " (" << error << ")"; if (result == 0) { MORDOR_THROW_EXCEPTION(UnexpectedEofException()); } MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_connect"); case SSL_ERROR_SSL: { MORDOR_ASSERT(hasOpenSSLError()); std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_connect(" << m_ssl.get() << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_connect"); ; } default: MORDOR_NOTREACHED(); } } }
void SSLStream::close(CloseType type) { MORDOR_ASSERT(type == BOTH); if (!(sslCallWithLock(std::bind(SSL_get_shutdown, m_ssl.get()), NULL) & SSL_SENT_SHUTDOWN)) { unsigned long error = SSL_ERROR_NONE; const int result = sslCallWithLock(std::bind(SSL_shutdown, m_ssl.get()), &error); if (result <= 0) { MORDOR_LOG_DEBUG(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ")"; switch (error) { case SSL_ERROR_NONE: case SSL_ERROR_ZERO_RETURN: break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: MORDOR_NOTREACHED(); case SSL_ERROR_SYSCALL: if (hasOpenSSLError()) { std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_shutdown"); ; } MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ")"; if (result == 0) break; MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_shutdown"); case SSL_ERROR_SSL: { MORDOR_ASSERT(hasOpenSSLError()); std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_shutdown"); ; } default: MORDOR_NOTREACHED(); } } flush(false); } while (!(sslCallWithLock(std::bind(SSL_get_shutdown, m_ssl.get()), NULL) & SSL_RECEIVED_SHUTDOWN)) { unsigned long error = SSL_ERROR_NONE; const int result = sslCallWithLock(std::bind(SSL_shutdown, m_ssl.get()), &error); MORDOR_LOG_DEBUG(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ")"; if (result > 0) { break; } switch (error) { case SSL_ERROR_NONE: case SSL_ERROR_ZERO_RETURN: break; case SSL_ERROR_WANT_READ: flush(); wantRead(); continue; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: MORDOR_NOTREACHED(); case SSL_ERROR_SYSCALL: if (hasOpenSSLError()) { std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_shutdown"); ; } MORDOR_LOG_WARNING(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ")"; if (result == 0) { break; } MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_shutdown"); case SSL_ERROR_SSL: { MORDOR_ASSERT(hasOpenSSLError()); std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_shutdown"); ; } default: MORDOR_NOTREACHED(); } break; } parent()->close(); }