diff --git a/NetSSL_OpenSSL/include/Poco/Net/Context.h b/NetSSL_OpenSSL/include/Poco/Net/Context.h index 9832b1a79..3e3131d2f 100644 --- a/NetSSL_OpenSSL/include/Poco/Net/Context.h +++ b/NetSSL_OpenSSL/include/Poco/Net/Context.h @@ -159,6 +159,10 @@ public: /// Specifies whether the builtin CA certificates from OpenSSL are used. /// Defaults to false. + bool ocspStaplingVerification; + /// Specifies whether Client should verify OCSP Response + /// Defaults to false. + std::string cipherList; /// Specifies the supported ciphers in OpenSSL notation. /// Defaults to "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH". @@ -402,6 +406,10 @@ public: /// preferences. When called, the SSL/TLS server will choose following its own /// preferences. + bool ocspStaplingResponseVerificationEnabled() const; + /// Returns true if automatic OCSP response + /// reception and verification is enabled for client connections + void setInvalidCertificateHandler(InvalidCertificateHandlerPtr pInvalidCertificageHandler); /// Sets a Context-specific InvalidCertificateHandler. /// @@ -430,6 +438,7 @@ private: VerificationMode _mode; SSL_CTX* _pSSLContext; bool _extendedCertificateVerification; + bool _ocspStaplingResponseVerification; InvalidCertificateHandlerPtr _pInvalidCertificateHandler; }; @@ -472,6 +481,12 @@ inline bool Context::extendedCertificateVerificationEnabled() const } +inline bool Context::ocspStaplingResponseVerificationEnabled() const +{ + return _ocspStaplingResponseVerification; +} + + inline Context::InvalidCertificateHandlerPtr Context::getInvalidCertificateHandler() const { return _pInvalidCertificateHandler; diff --git a/NetSSL_OpenSSL/include/Poco/Net/SSLManager.h b/NetSSL_OpenSSL/include/Poco/Net/SSLManager.h index fa2052b74..e834a5820 100644 --- a/NetSSL_OpenSSL/include/Poco/Net/SSLManager.h +++ b/NetSSL_OpenSSL/include/Poco/Net/SSLManager.h @@ -272,6 +272,11 @@ protected: /// The request is delegated to the PrivatekeyPassword event. This method returns the /// length of the password. + static int verifyOCSPResponseCallback(SSL* pSSL, void* arg); + /// The return value of this method defines how errors in + /// verification are handled. Return 0 to terminate the handshake, + /// or 1 to continue despite the error. + static Poco::Util::AbstractConfiguration& appConfig(); /// Returns the application configuration. /// diff --git a/NetSSL_OpenSSL/src/Context.cpp b/NetSSL_OpenSSL/src/Context.cpp index 1ecdca5be..f5d15e334 100644 --- a/NetSSL_OpenSSL/src/Context.cpp +++ b/NetSSL_OpenSSL/src/Context.cpp @@ -34,6 +34,7 @@ Context::Params::Params(): verificationMode(VERIFY_RELAXED), verificationDepth(9), loadDefaultCAs(false), + ocspStaplingVerification(false), cipherList("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"), dhUse2048Bits(false) { @@ -44,7 +45,8 @@ Context::Context(Usage usage, const Params& params): _usage(usage), _mode(params.verificationMode), _pSSLContext(0), - _extendedCertificateVerification(true) + _extendedCertificateVerification(true), + _ocspStaplingResponseVerification(false) { init(params); } @@ -62,7 +64,8 @@ Context::Context( _usage(usage), _mode(verificationMode), _pSSLContext(0), - _extendedCertificateVerification(true) + _extendedCertificateVerification(true), + _ocspStaplingResponseVerification(false) { Params params; params.privateKeyFile = privateKeyFile; @@ -86,7 +89,8 @@ Context::Context( _usage(usage), _mode(verificationMode), _pSSLContext(0), - _extendedCertificateVerification(true) + _extendedCertificateVerification(true), + _ocspStaplingResponseVerification(false) { Params params; params.caLocation = caLocation; @@ -178,6 +182,17 @@ void Context::init(const Params& params) SSL_CTX_set_session_cache_mode(_pSSLContext, SSL_SESS_CACHE_OFF); SSL_CTX_set_ex_data(_pSSLContext, SSLManager::instance().contextIndex(), this); + if (!isForServerUse() && params.ocspStaplingVerification) + { +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + _ocspStaplingResponseVerification = true; + SSL_CTX_set_tlsext_status_cb(_pSSLContext, &SSLManager::verifyOCSPResponseCallback); + SSL_CTX_set_tlsext_status_arg(_pSSLContext, this); +#else + throw SSLContextException("OCSP Stapling is not supported by this OpenSSL version"); +#endif + } + initDH(params.dhUse2048Bits, params.dhParamsFile); initECDH(params.ecdhCurve); } diff --git a/NetSSL_OpenSSL/src/SSLManager.cpp b/NetSSL_OpenSSL/src/SSLManager.cpp index efceb4dc0..f9a043f2a 100644 --- a/NetSSL_OpenSSL/src/SSLManager.cpp +++ b/NetSSL_OpenSSL/src/SSLManager.cpp @@ -29,7 +29,10 @@ #include "Poco/StringTokenizer.h" #include "Poco/Util/Application.h" #include "Poco/Util/OptionException.h" - +#if OPENSSL_VERSION_NUMBER >= 0x10001000L +#include +#include +#endif namespace Poco { @@ -271,6 +274,145 @@ int SSLManager::privateKeyPassphraseCallback(char* pBuf, int size, int flag, voi } +int SSLManager::verifyOCSPResponseCallback(SSL* pSSL, void* arg) +{ +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + const long OCSP_VALIDITY_LEEWAY = 5*60; + + Poco::Net::Context* pContext = static_cast(arg); + + // Fetch the OSCP verify flag + bool ocspVerifyFlag = pContext->ocspStaplingResponseVerificationEnabled(); + + const unsigned char* pResp; + int len = SSL_get_tlsext_status_ocsp_resp(pSSL, &pResp); + if (!pResp) + { + // OCSP response not received + return ocspVerifyFlag ? 0 : 1; + } + + OCSP_RESPONSE* pOcspResp = d2i_OCSP_RESPONSE(NULL, &pResp, len); + if (!pOcspResp) return 0; + + if (OCSP_response_status(pOcspResp) != OCSP_RESPONSE_STATUS_SUCCESSFUL) + { + OCSP_RESPONSE_free(pOcspResp); + return 0; + } + + OCSP_BASICRESP* pBasicResp = OCSP_response_get1_basic(pOcspResp); + if (!pBasicResp) + { + OCSP_RESPONSE_free(pOcspResp); + return 0; + } + + X509* pPeerCert = SSL_get_peer_certificate(pSSL); + if (!pPeerCert) + { + OCSP_BASICRESP_free(pBasicResp); + OCSP_RESPONSE_free(pOcspResp); + return 0; + } + + X509* pPeerIssuerCert = NULL; + STACK_OF(X509)* pCertChain = SSL_get_peer_cert_chain(pSSL); + unsigned certChainLen = sk_X509_num(pCertChain); + for (int i= 0; i < certChainLen ; i++) + { + if (!pPeerIssuerCert) + { + X509* pIssuerCert = sk_X509_value(pCertChain, i); + if (X509_check_issued(pIssuerCert, pPeerCert) == X509_V_OK) + { + pPeerIssuerCert = pIssuerCert; + break; + } + } + } + if (!pPeerIssuerCert) + { + X509_free(pPeerCert); + OCSP_BASICRESP_free(pBasicResp); + OCSP_RESPONSE_free(pOcspResp); + return 0; + } + + STACK_OF(X509)* pCerts = sk_X509_new_null(); + if (pCerts) + { + X509* pCert = X509_dup(pPeerIssuerCert); + if (pCert && !sk_X509_push(pCerts, pCert)) + { + X509_free(pCert); + sk_X509_free(pCerts); + pCerts = NULL; + } + } + + X509_STORE* pStore = SSL_CTX_get_cert_store(SSL_get_SSL_CTX(pSSL)); + + int verifyStatus = OCSP_basic_verify(pBasicResp, pCerts, pStore, OCSP_TRUSTOTHER); + + sk_X509_pop_free(pCerts, X509_free); + + if (verifyStatus <= 0) + { + X509_free(pPeerCert); + OCSP_BASICRESP_free(pBasicResp); + OCSP_RESPONSE_free(pOcspResp); + return 0; + } + + OCSP_CERTID* pCertId = OCSP_cert_to_id(NULL, pPeerCert, pPeerIssuerCert); + if (!pCertId) + { + X509_free(pPeerCert); + OCSP_BASICRESP_free(pBasicResp); + OCSP_RESPONSE_free(pOcspResp); + return 0; + } + + X509_free(pPeerCert); + + ASN1_GENERALIZEDTIME* pRevTime; + ASN1_GENERALIZEDTIME* pThisUpdate; + ASN1_GENERALIZEDTIME* pNextUpdate; + int certStatus; + int reason; + if (!OCSP_resp_find_status(pBasicResp, pCertId, &certStatus, &reason, &pRevTime, &pThisUpdate, &pNextUpdate)) + { + OCSP_CERTID_free(pCertId); + OCSP_BASICRESP_free(pBasicResp); + OCSP_RESPONSE_free(pOcspResp); + return 0; + } + + OCSP_CERTID_free(pCertId); + + if (certStatus != V_OCSP_CERTSTATUS_GOOD) + { + OCSP_BASICRESP_free(pBasicResp); + OCSP_RESPONSE_free(pOcspResp); + return 0; + } + + if (!OCSP_check_validity(pThisUpdate, pNextUpdate, OCSP_VALIDITY_LEEWAY, -1)) + { + OCSP_BASICRESP_free(pBasicResp); + OCSP_RESPONSE_free(pOcspResp); + return 0; + } + + OCSP_BASICRESP_free(pBasicResp); + OCSP_RESPONSE_free(pOcspResp); +#endif + + return 1; +} + + void SSLManager::initDefaultContext(bool server) { if (server && _ptrDefaultServerContext) return; diff --git a/NetSSL_OpenSSL/src/SecureSocketImpl.cpp b/NetSSL_OpenSSL/src/SecureSocketImpl.cpp index d079842cf..0b5c4c1e2 100644 --- a/NetSSL_OpenSSL/src/SecureSocketImpl.cpp +++ b/NetSSL_OpenSSL/src/SecureSocketImpl.cpp @@ -164,6 +164,13 @@ void SecureSocketImpl::connectSSL(bool performHandshake) } #endif +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + if(_pContext->ocspStaplingResponseVerificationEnabled()) + { + SSL_set_tlsext_status_type(_pSSL, TLSEXT_STATUSTYPE_ocsp); + } +#endif + if (_pSession) { SSL_set_session(_pSSL, _pSession->sslSession());