diff options
Diffstat (limited to 'cpp/src/IceSSL/OpenSSLTransceiverI.cpp')
-rw-r--r-- | cpp/src/IceSSL/OpenSSLTransceiverI.cpp | 158 |
1 files changed, 133 insertions, 25 deletions
diff --git a/cpp/src/IceSSL/OpenSSLTransceiverI.cpp b/cpp/src/IceSSL/OpenSSLTransceiverI.cpp index 5acd71dc8f3..fa60bdaf5f7 100644 --- a/cpp/src/IceSSL/OpenSSLTransceiverI.cpp +++ b/cpp/src/IceSSL/OpenSSLTransceiverI.cpp @@ -65,6 +65,19 @@ Init init; } #endif +extern "C" +{ + +int +IceSSL_opensslVerifyCallback(int ok, X509_STORE_CTX* ctx) +{ + SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx())); + TransceiverI* p = reinterpret_cast<TransceiverI*>(SSL_get_ex_data(ssl, 0)); + return p->verifyCallback(ok, ctx); +} + +} + IceInternal::NativeInfoPtr IceSSL::TransceiverI::getNativeInfo() { @@ -102,6 +115,35 @@ IceSSL::TransceiverI::initialize(IceInternal::Buffer& readBuffer, IceInternal::B throw ex; } SSL_set_bio(_ssl, bio, bio); + + // + // Store a pointer to ourself for use in OpenSSL callbacks. + // + SSL_set_ex_data(_ssl, 0, this); + + // + // Determine whether a certificate is required from the peer. + // + { + int sslVerifyMode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + switch(_engine->getVerifyPeer()) + { + case 0: + sslVerifyMode = SSL_VERIFY_NONE; + break; + case 1: + sslVerifyMode = SSL_VERIFY_PEER; + break; + case 2: + sslVerifyMode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + break; + default: + { + assert(false); + } + } + SSL_set_verify(_ssl, sslVerifyMode, IceSSL_opensslVerifyCallback); + } } while(!SSL_is_init_finished(_ssl)) @@ -542,6 +584,50 @@ IceSSL::TransceiverI::setBufferSize(int rcvSize, int sndSize) _stream->setBufferSize(rcvSize, sndSize); } +int +IceSSL::TransceiverI::verifyCallback(int ok, X509_STORE_CTX* c) +{ + if(!ok && _engine->securityTraceLevel() >= 1) + { + X509* cert = X509_STORE_CTX_get_current_cert(c); + int err = X509_STORE_CTX_get_error(c); + char buf[256]; + + Trace out(_engine->getLogger(), _engine->securityTraceCategory()); + out << "certificate verification failure\n"; + + X509_NAME_oneline(X509_get_issuer_name(cert), buf, static_cast<int>(sizeof(buf))); + out << "issuer = " << buf << '\n'; + X509_NAME_oneline(X509_get_subject_name(cert), buf, static_cast<int>(sizeof(buf))); + out << "subject = " << buf << '\n'; + out << "depth = " << X509_STORE_CTX_get_error_depth(c) << '\n'; + out << "error = " << X509_verify_cert_error_string(err) << '\n'; + out << IceInternal::fdToString(SSL_get_fd(_ssl)); + } + + // + // Initialize the native connection info with the verified + // certificate chain. SSL_get_peer_cert_chain doesn't return the + // verified chain, it returns the chain sent by the peer. + // + try + { + _info = initNativeConnectionInfo(c); + } + catch(const Ice::Exception&) + { + // Ignore + } + + // + // Always return 1 to prevent SSL_connect/SSL_accept from + // returning SSL_ERROR_SSL for verification failures. This ensure + // that we can raise SecurityException for verification failures + // rather than a ProtocolException. + // + return 1; +} + IceSSL::TransceiverI::TransceiverI(const InstancePtr& instance, const IceInternal::StreamSocketPtr& stream, const string& hostOrAdapterName, bool incoming) : _instance(instance), @@ -561,6 +647,16 @@ IceSSL::TransceiverI::~TransceiverI() NativeConnectionInfoPtr IceSSL::TransceiverI::getNativeConnectionInfo() const { + if(!_info) + { + return initNativeConnectionInfo(0); + } + return _info; +} + +NativeConnectionInfoPtr +IceSSL::TransceiverI::initNativeConnectionInfo(X509_STORE_CTX* ctx) const +{ NativeConnectionInfoPtr info = new NativeConnectionInfo(); IceInternal::fdToAddressAndPort(_stream->fd(), info->localAddress, info->localPort, info->remoteAddress, info->remotePort); @@ -569,8 +665,15 @@ IceSSL::TransceiverI::getNativeConnectionInfo() const info->rcvSize = IceInternal::getRecvBufferSize(_stream->fd()); info->sndSize = IceInternal::getSendBufferSize(_stream->fd()); } + info->adapterName = _adapterName; + info->incoming = _incoming; - if(_ssl != 0) + STACK_OF(X509)* chain = 0; + if(ctx) + { + chain = X509_STORE_CTX_get1_chain(ctx); + } + if(chain == 0 && _ssl != 0) { // // On the client side, SSL_get_peer_cert_chain returns the entire chain of certs. @@ -581,34 +684,39 @@ IceSSL::TransceiverI::getNativeConnectionInfo() const // certificate in the chain. If they are not the same, it is added to the chain. // X509* cert = SSL_get_peer_certificate(_ssl); - STACK_OF(X509)* chain = SSL_get_peer_cert_chain(_ssl); - if(cert != 0 && (chain == 0 || sk_X509_num(chain) == 0 || cert != sk_X509_value(chain, 0))) - { - CertificatePtr certificate = new Certificate(cert); + chain = SSL_get_peer_cert_chain(_ssl); + if(cert != 0 && (chain == 0 || sk_X509_num(chain) == 0 || cert != sk_X509_value(chain, 0))) + { + CertificatePtr certificate = new Certificate(cert); info->nativeCerts.push_back(certificate); info->certs.push_back(certificate->encode()); - } - else - { - X509_free(cert); - } - - if(chain != 0) - { - for(int i = 0; i < sk_X509_num(chain); ++i) - { - // - // Duplicate the certificate since the stack comes straight from the SSL connection. - // - CertificatePtr certificate = new Certificate(X509_dup(sk_X509_value(chain, i))); - info->nativeCerts.push_back(certificate); - info->certs.push_back(certificate->encode()); - } - } - - info->cipher = SSL_get_cipher_name(_ssl); // Nothing needs to be free'd. + } + else + { + X509_free(cert); + } } + if(chain != 0) + { + for(int i = 0; i < sk_X509_num(chain); ++i) + { + // + // Duplicate the certificate since the stack comes straight from the SSL connection. + // + CertificatePtr certificate = new Certificate(X509_dup(sk_X509_value(chain, i))); + info->nativeCerts.push_back(certificate); + info->certs.push_back(certificate->encode()); + } + if(ctx) + { + sk_X509_pop_free(chain, X509_free); + } + } + if(_ssl != 0) + { + info->cipher = SSL_get_cipher_name(_ssl); // Nothing needs to be free'd. + } info->adapterName = _adapterName; info->incoming = _incoming; return info; |