From 90515dfa50c676df37f440893de6e344fbfd2cdc Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sun, 30 Sep 2012 00:50:14 +0900 Subject: RPC over SSL/TLS transport To enable RPC over SSL/TLS, specify server certificate and private key using --rpc-certificate and --rpc-private-key options and enable --rpc-secure option. After the encryption is enabled, use https and wss scheme to access RPC server. --- src/SocketCore.cc | 148 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 90 insertions(+), 58 deletions(-) (limited to 'src/SocketCore.cc') diff --git a/src/SocketCore.cc b/src/SocketCore.cc index 969e15b3..658071ff 100644 --- a/src/SocketCore.cc +++ b/src/SocketCore.cc @@ -125,8 +125,6 @@ namespace { enum TlsState { // TLS object is not initialized. A2_TLS_NONE = 0, - // TLS object is initialized. Ready for handshake. - A2_TLS_INITIALIZED = 1, // TLS object is now handshaking. A2_TLS_HANDSHAKING = 2, // TLS object is now connected. @@ -140,11 +138,19 @@ std::vector > SocketCore::bindAddrs_; #ifdef ENABLE_SSL -SharedHandle SocketCore::tlsContext_; +SharedHandle SocketCore::clTlsContext_; +SharedHandle SocketCore::svTlsContext_; -void SocketCore::setTLSContext(const SharedHandle& tlsContext) +void SocketCore::setClientTLSContext +(const SharedHandle& tlsContext) { - tlsContext_ = tlsContext; + clTlsContext_ = tlsContext; +} + +void SocketCore::setServerTLSContext +(const SharedHandle& tlsContext) +{ + svTlsContext_ = tlsContext; } #endif // ENABLE_SSL @@ -818,12 +824,24 @@ void SocketCore::readData(char* data, size_t& len) len = ret; } -void SocketCore::prepareSecureConnection() +bool SocketCore::tlsAccept() { - if(!secure_) { + return tlsHandshake(svTlsContext_.get(), A2STR::NIL); +} + +bool SocketCore::tlsConnect(const std::string& hostname) +{ + return tlsHandshake(clTlsContext_.get(), hostname); +} + +bool SocketCore::tlsHandshake(TLSContext* tlsctx, const std::string& hostname) +{ + wantRead_ = false; + wantWrite_ = false; #ifdef HAVE_OPENSSL - // for SSL - ssl = SSL_new(tlsContext_->getSSLCtx()); + switch(secure_) { + case A2_TLS_NONE: + ssl = SSL_new(tlsctx->getSSLCtx()); if(!ssl) { throw DL_ABORT_EX (fmt(EX_SSL_INIT_FAILURE, ERR_error_string(ERR_get_error(), 0))); @@ -832,48 +850,25 @@ void SocketCore::prepareSecureConnection() throw DL_ABORT_EX (fmt(EX_SSL_INIT_FAILURE, ERR_error_string(ERR_get_error(), 0))); } -#endif // HAVE_OPENSSL -#ifdef HAVE_LIBGNUTLS - int r; - gnutls_init(&sslSession_, GNUTLS_CLIENT); - // It seems err is not error message, but the argument string - // which causes syntax error. - const char* err; - // Disables TLS1.1 here because there are servers that don't - // understand TLS1.1. - r = gnutls_priority_set_direct(sslSession_, "NORMAL:!VERS-TLS1.1", &err); - if(r != GNUTLS_E_SUCCESS) { - throw DL_ABORT_EX(fmt(EX_SSL_INIT_FAILURE, gnutls_strerror(r))); - } - // put the x509 credentials to the current session - gnutls_credentials_set(sslSession_, GNUTLS_CRD_CERTIFICATE, - tlsContext_->getCertCred()); - gnutls_transport_set_ptr(sslSession_, (gnutls_transport_ptr_t)sockfd_); -#endif // HAVE_LIBGNUTLS - secure_ = A2_TLS_INITIALIZED; - } -} - -bool SocketCore::initiateSecureConnection(const std::string& hostname) -{ - wantRead_ = false; - wantWrite_ = false; -#ifdef HAVE_OPENSSL - switch(secure_) { - case A2_TLS_INITIALIZED: - secure_ = A2_TLS_HANDSHAKING; + // Fall through #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if(!util::isNumericHost(hostname)) { + if(tlsctx->getSide() == TLS_CLIENT && !util::isNumericHost(hostname)) { // TLS extensions: SNI. There is not documentation about the // return code for this function (actually this is macro // wrapping SSL_ctrl at the time of this writing). SSL_set_tlsext_host_name(ssl, hostname.c_str()); } #endif // SSL_CTRL_SET_TLSEXT_HOSTNAME + secure_ = A2_TLS_HANDSHAKING; // Fall through case A2_TLS_HANDSHAKING: { ERR_clear_error(); - int e = SSL_connect(ssl); + int e; + if(tlsctx->getSide() == TLS_CLIENT) { + e = SSL_connect(ssl); + } else { + e = SSL_accept(ssl); + } if (e <= 0) { int ssl_error = SSL_get_error(ssl, e); @@ -893,9 +888,21 @@ bool SocketCore::initiateSecureConnection(const std::string& hostname) } break; - case SSL_ERROR_SYSCALL: - throw DL_ABORT_EX(EX_SSL_IO_ERROR); - + case SSL_ERROR_SYSCALL: { + int sslErr = ERR_get_error(); + if(sslErr == 0) { + if(e == 0) { + throw DL_ABORT_EX("Got EOF in SSL handshake"); + } else if(e == -1) { + throw DL_ABORT_EX(fmt("SSL I/O error: %s", strerror(errno))); + } else { + throw DL_ABORT_EX(EX_SSL_IO_ERROR); + } + } else { + throw DL_ABORT_EX(fmt("SSL I/O error: %s", + ERR_error_string(sslErr, 0))); + } + } case SSL_ERROR_SSL: throw DL_ABORT_EX(EX_SSL_PROTOCOL_ERROR); @@ -903,7 +910,8 @@ bool SocketCore::initiateSecureConnection(const std::string& hostname) throw DL_ABORT_EX(fmt(EX_SSL_UNKNOWN_ERROR, ssl_error)); } } - if(tlsContext_->peerVerificationEnabled()) { + if(tlsctx->getSide() == TLS_CLIENT && + tlsctx->peerVerificationEnabled()) { // verify peer X509* peerCert = SSL_get_peer_certificate(ssl); if(!peerCert) { @@ -984,20 +992,44 @@ bool SocketCore::initiateSecureConnection(const std::string& hostname) #endif // HAVE_OPENSSL #ifdef HAVE_LIBGNUTLS switch(secure_) { - case A2_TLS_INITIALIZED: - secure_ = A2_TLS_HANDSHAKING; - // Check hostname is not numeric and it includes ".". Setting - // "localhost" will produce TLS alert. - if(!util::isNumericHost(hostname) && - hostname.find(".") != std::string::npos) { - // TLS extensions: SNI - int ret = gnutls_server_name_set(sslSession_, GNUTLS_NAME_DNS, - hostname.c_str(), hostname.size()); - if(ret < 0) { - A2_LOG_WARN(fmt("Setting hostname in SNI extension failed. Cause: %s", - gnutls_strerror(ret))); + case A2_TLS_NONE: + int r; + gnutls_init(&sslSession_, + tlsctx->getSide() == TLS_CLIENT ? + GNUTLS_CLIENT : GNUTLS_SERVER); + // It seems err is not error message, but the argument string + // which causes syntax error. + const char* err; + // For client side, disables TLS1.1 here because there are servers + // that don't understand TLS1.1. TODO Is this still necessary? + r = gnutls_priority_set_direct(sslSession_, + tlsctx->getSide() == TLS_CLIENT ? + "NORMAL:-VERS-TLS1.1" : + "NORMAL", + &err); + if(r != GNUTLS_E_SUCCESS) { + throw DL_ABORT_EX(fmt(EX_SSL_INIT_FAILURE, gnutls_strerror(r))); + } + // put the x509 credentials to the current session + gnutls_credentials_set(sslSession_, GNUTLS_CRD_CERTIFICATE, + tlsctx->getCertCred()); + gnutls_transport_set_ptr(sslSession_, (gnutls_transport_ptr_t)sockfd_); + if(tlsctx->getSide() == TLS_CLIENT) { + // Check hostname is not numeric and it includes ".". Setting + // "localhost" will produce TLS alert. + if(!util::isNumericHost(hostname) && + hostname.find(".") != std::string::npos) { + // TLS extensions: SNI + int ret = gnutls_server_name_set(sslSession_, GNUTLS_NAME_DNS, + hostname.c_str(), hostname.size()); + if(ret < 0) { + A2_LOG_WARN(fmt + ("Setting hostname in SNI extension failed. Cause: %s", + gnutls_strerror(ret))); + } } } + secure_ = A2_TLS_HANDSHAKING; // Fall through case A2_TLS_HANDSHAKING: { int ret = gnutls_handshake(sslSession_); @@ -1008,7 +1040,7 @@ bool SocketCore::initiateSecureConnection(const std::string& hostname) throw DL_ABORT_EX(fmt(EX_SSL_INIT_FAILURE, gnutls_strerror(ret))); } - if(tlsContext_->peerVerificationEnabled()) { + if(tlsctx->getSide() == TLS_CLIENT && tlsctx->peerVerificationEnabled()) { // verify peer unsigned int status; ret = gnutls_certificate_verify_peers2(sslSession_, &status); -- cgit