diff options
-rw-r--r-- | src/network/configure.json | 26 | ||||
-rw-r--r-- | src/network/ssl/qsslconfiguration.cpp | 37 | ||||
-rw-r--r-- | src/network/ssl/qsslconfiguration.h | 3 | ||||
-rw-r--r-- | src/network/ssl/qsslconfiguration_p.h | 6 | ||||
-rw-r--r-- | src/network/ssl/qsslerror.cpp | 45 | ||||
-rw-r--r-- | src/network/ssl/qsslerror.h | 12 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket.cpp | 6 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl.cpp | 309 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl11_symbols_p.h | 4 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl_p.h | 12 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl_symbols.cpp | 48 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl_symbols_p.h | 39 |
12 files changed, 540 insertions, 7 deletions
diff --git a/src/network/configure.json b/src/network/configure.json index 327131ba11..368209cd3f 100644 --- a/src/network/configure.json +++ b/src/network/configure.json @@ -15,6 +15,7 @@ "openssl-linked": { "type": "void", "name": "openssl", "value": "linked" }, "openssl-runtime": { "type": "void", "name": "openssl", "value": "runtime" }, "dtls": "boolean", + "ocsp": "boolean", "sctp": "boolean", "securetransport": "boolean", "ssl": "boolean", @@ -163,6 +164,23 @@ ] }, "use": "openssl" + }, + "ocsp": { + "label": "OCSP stapling support in OpenSSL", + "type": "compile", + "test": { + "include": ["openssl/ssl.h", "openssl/ocsp.h"], + "tail": [ + "#if defined(OPENSSL_NO_OCSP) || defined(OPENSSL_NO_TLSEXT)", + "# error OpenSSL without OCSP stapling", + "#endif" + ], + "main": [ + "(void)SSL_get_tlsext_status_ocsp_resp(nullptr, nullptr);", + "(void)d2i_OCSP_RESPONSE(nullptr, nullptr, 0);" + ] + }, + "use": "openssl" } }, @@ -237,6 +255,13 @@ "condition": "features.openssl && tests.dtls", "output": [ "publicFeature" ] }, + "ocsp": { + "label": "OCSP-stapling", + "purpose": "Provides OCSP stapling support", + "section": "Networking", + "condition": "features.opensslv11 && tests.ocsp", + "output": [ "publicFeature" ] + }, "opensslv11": { "label": "OpenSSL 1.1", "condition": "features.openssl && tests.openssl11", @@ -370,6 +395,7 @@ For example: "openssl-linked", "opensslv11", "dtls", + "ocsp", "sctp", "system-proxies" ] diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp index 3f732b4646..4697a5f90f 100644 --- a/src/network/ssl/qsslconfiguration.cpp +++ b/src/network/ssl/qsslconfiguration.cpp @@ -228,7 +228,8 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const d->nextAllowedProtocols == other.d->nextAllowedProtocols && d->nextNegotiatedProtocol == other.d->nextNegotiatedProtocol && d->nextProtocolNegotiationStatus == other.d->nextProtocolNegotiationStatus && - d->dtlsCookieEnabled == other.d->dtlsCookieEnabled; + d->dtlsCookieEnabled == other.d->dtlsCookieEnabled && + d->ocspStaplingEnabled == other.d->ocspStaplingEnabled; } /*! @@ -272,7 +273,8 @@ bool QSslConfiguration::isNull() const d->preSharedKeyIdentityHint.isNull() && d->nextAllowedProtocols.isEmpty() && d->nextNegotiatedProtocol.isNull() && - d->nextProtocolNegotiationStatus == QSslConfiguration::NextProtocolNegotiationNone); + d->nextProtocolNegotiationStatus == QSslConfiguration::NextProtocolNegotiationNone && + d->ocspStaplingEnabled == false); } /*! @@ -1094,6 +1096,37 @@ void QSslConfiguration::setDefaultDtlsConfiguration(const QSslConfiguration &con #endif // dtls +/*! + \since 5.13 + If \a enabled is true, client QSslSocket will send a certificate status request + to its peer when initiating a handshake. During the handshake QSslSocket will + verify the server's response. This value must be set before the handshake + starts. + + \sa ocspStaplingEnabled() +*/ +void QSslConfiguration::setOcspStaplingEnabled(bool enabled) +{ +#if QT_CONFIG(ocsp) + d->ocspStaplingEnabled = enabled; +#else + if (enabled) + qCWarning(lcSsl, "Enabling OCSP-stapling requires the feature 'ocsp'"); +#endif // ocsp +} + +/*! + \since 5.13 + Returns true if OCSP stapling was enabled by setOCSPStaplingEnabled(), + otherwise false (which is the default value). + + \sa setOcspStaplingEnabled() +*/ +bool QSslConfiguration::ocspStaplingEnabled() const +{ + return d->ocspStaplingEnabled; +} + /*! \internal */ bool QSslConfigurationPrivate::peerSessionWasShared(const QSslConfiguration &configuration) { diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h index 454ac0cee3..8f53e25a53 100644 --- a/src/network/ssl/qsslconfiguration.h +++ b/src/network/ssl/qsslconfiguration.h @@ -170,6 +170,9 @@ public: static void setDefaultDtlsConfiguration(const QSslConfiguration &configuration); #endif // dtls + void setOcspStaplingEnabled(bool enable); + bool ocspStaplingEnabled() const; + enum NextProtocolNegotiationStatus { NextProtocolNegotiationNone, NextProtocolNegotiationNegotiated, diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h index 6c23165c6a..83126bb9a0 100644 --- a/src/network/ssl/qsslconfiguration_p.h +++ b/src/network/ssl/qsslconfiguration_p.h @@ -143,6 +143,12 @@ public: const bool dtlsCookieEnabled = false; #endif // dtls +#if QT_CONFIG(ocsp) + bool ocspStaplingEnabled = false; +#else + const bool ocspStaplingEnabled = false; +#endif + // in qsslsocket.cpp: static QSslConfiguration defaultConfiguration(); static void setDefaultConfiguration(const QSslConfiguration &configuration); diff --git a/src/network/ssl/qsslerror.cpp b/src/network/ssl/qsslerror.cpp index 3f79d1a037..74ff6987d2 100644 --- a/src/network/ssl/qsslerror.cpp +++ b/src/network/ssl/qsslerror.cpp @@ -86,6 +86,18 @@ \value UnspecifiedError \value NoSslSupport \value CertificateBlacklisted + \value OcspNoResponseFound + \value OcspMalformedRequest + \value OcspMalformedResponse + \value OcspInternalError + \value OcspTryLater + \value OcspSigRequred + \value OcspUnauthorized + \value OcspResponseCannotBeTrusted + \value OcspResponseCertIdUnknown + \value OcspResponseExpired + \value OcspStatusUnknown + \sa QSslError::errorString() */ @@ -292,6 +304,39 @@ QString QSslError::errorString() const case CertificateBlacklisted: errStr = QSslSocket::tr("The peer certificate is blacklisted"); break; + case OcspNoResponseFound: + errStr = QSslSocket::tr("No OCSP status response found"); + break; + case OcspMalformedRequest: + errStr = QSslSocket::tr("The OCSP status request had invalid syntax"); + break; + case OcspMalformedResponse: + errStr = QSslSocket::tr("OCSP response contains an unexpected number of SingleResponse structures"); + break; + case OcspInternalError: + errStr = QSslSocket::tr("OCSP responder reached an inconsistent internal state"); + break; + case OcspTryLater: + errStr = QSslSocket::tr("OCSP responder was unable to return a status for the requested certificate"); + break; + case OcspSigRequred: + errStr = QSslSocket::tr("The server requires the client to sign the OCSP request in order to construct a response"); + break; + case OcspUnauthorized: + errStr = QSslSocket::tr("The client is not authorized to request OCSP status from this server"); + break; + case OcspResponseCannotBeTrusted: + errStr = QSslSocket::tr("OCSP reponder's identity cannot be verified"); + break; + case OcspResponseCertIdUnknown: + errStr = QSslSocket::tr("The identity of a certificate in an OCSP response cannot be established"); + break; + case OcspResponseExpired: + errStr = QSslSocket::tr("The certificate status response has expired"); + break; + case OcspStatusUnknown: + errStr = QSslSocket::tr("The certificate's status is unknown"); + break; default: errStr = QSslSocket::tr("Unknown error"); break; diff --git a/src/network/ssl/qsslerror.h b/src/network/ssl/qsslerror.h index d7c959423d..513b8afd7f 100644 --- a/src/network/ssl/qsslerror.h +++ b/src/network/ssl/qsslerror.h @@ -80,6 +80,18 @@ public: HostNameMismatch, NoSslSupport, CertificateBlacklisted, + CertificateStatusUnknown, + OcspNoResponseFound, + OcspMalformedRequest, + OcspMalformedResponse, + OcspInternalError, + OcspTryLater, + OcspSigRequred, + OcspUnauthorized, + OcspResponseCannotBeTrusted, + OcspResponseCertIdUnknown, + OcspResponseExpired, + OcspStatusUnknown, UnspecifiedError = -1 }; diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 4a9d054c0d..270e81774d 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -952,6 +952,9 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration) d->configuration.nextAllowedProtocols = configuration.allowedNextProtocols(); d->configuration.nextNegotiatedProtocol = configuration.nextNegotiatedProtocol(); d->configuration.nextProtocolNegotiationStatus = configuration.nextProtocolNegotiationStatus(); +#if QT_CONFIG(ocsp) + d->configuration.ocspStaplingEnabled = configuration.ocspStaplingEnabled(); +#endif // if the CA certificates were set explicitly (either via // QSslConfiguration::setCaCertificates() or QSslSocket::setCaCertificates(), @@ -2324,6 +2327,9 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri #if QT_CONFIG(dtls) ptr->dtlsCookieEnabled = global->dtlsCookieEnabled; #endif +#if QT_CONFIG(ocsp) + ptr->ocspStaplingEnabled = global->ocspStaplingEnabled; +#endif } /*! diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 5a49b56c9d..56764ebc7f 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -83,6 +83,10 @@ #include <QtCore/qvarlengtharray.h> #include <QtCore/qscopedvaluerollback.h> +#if QT_CONFIG(ocsp) +#include <openssl/ocsp.h> +#endif + #include <string.h> QT_BEGIN_NAMESPACE @@ -198,8 +202,92 @@ QSslErrorEntry QSslErrorEntry::fromStoreContext(X509_STORE_CTX *ctx) }; } +#if QT_CONFIG(ocsp) + +QSslError qt_OCSP_response_status_to_QSslError(long code) +{ + switch (code) { + case OCSP_RESPONSE_STATUS_MALFORMEDREQUEST: + return QSslError::OcspMalformedRequest; + case OCSP_RESPONSE_STATUS_INTERNALERROR: + return QSslError::OcspInternalError; + case OCSP_RESPONSE_STATUS_TRYLATER: + return QSslError::OcspTryLater; + case OCSP_RESPONSE_STATUS_SIGREQUIRED: + return QSslError::OcspSigRequred; + case OCSP_RESPONSE_STATUS_UNAUTHORIZED: + return QSslError::OcspUnauthorized; + case OCSP_RESPONSE_STATUS_SUCCESSFUL: + default: + return {}; + } + Q_UNREACHABLE(); +} + +bool qt_OCSP_certificate_match(OCSP_SINGLERESP *singleResponse, X509 *peerCert, X509 *issuer) +{ + // OCSP_basic_verify does verify that the responder is legit, the response is + // correctly signed, CertID is correct. But it does not know which certificate + // we were presented with by our peer, so it does not check if it's a response + // for our peer's certificate. + Q_ASSERT(singleResponse && peerCert && issuer); + + const OCSP_CERTID *certId = q_OCSP_SINGLERESP_get0_id(singleResponse); // Does not increment refcount. + if (!certId) { + qCWarning(lcSsl, "A SingleResponse without CertID"); + return false; + } + + ASN1_OBJECT *md = nullptr; + ASN1_INTEGER *reportedSerialNumber = nullptr; + const int result = q_OCSP_id_get0_info(nullptr, &md, nullptr, &reportedSerialNumber, const_cast<OCSP_CERTID *>(certId)); + if (result != 1 || !md || !reportedSerialNumber) { + qCWarning(lcSsl, "Failed to extract a hash and serial number from CertID structure"); + return false; + } + + if (!q_X509_get_serialNumber(peerCert)) { + // Is this possible at all? But we have to check this, + // ASN1_INTEGER_cmp (called from OCSP_id_cmp) dereferences + // without any checks at all. + qCWarning(lcSsl, "No serial number in peer's ceritificate"); + return false; + } + + const int nid = q_OBJ_obj2nid(md); + if (nid == NID_undef) { + qCWarning(lcSsl, "Unknown hash algorithm in CertID"); + return false; + } + + const EVP_MD *digest = q_EVP_get_digestbynid(nid); // Does not increment refcount. + if (!digest) { + qCWarning(lcSsl) << "No digest for nid" << nid; + return false; + } + + OCSP_CERTID *recreatedId = q_OCSP_cert_to_id(digest, peerCert, issuer); + if (!recreatedId) { + qCWarning(lcSsl, "Failed to re-create CertID"); + return false; + } + const QSharedPointer<OCSP_CERTID> guard(recreatedId, q_OCSP_CERTID_free); + + if (q_OCSP_id_cmp(const_cast<OCSP_CERTID *>(certId), recreatedId)) { + qDebug(lcSsl, "Certificate ID mismatch"); + return false; + } + // Bingo! + return true; +} + +#endif // ocsp + // ### This list is shared between all threads, and protected by a -// mutex. Investigate using thread local storage instead. +// mutex. Investigate using thread local storage instead. Or better properly +// use OpenSSL's ability to attach application data to an SSL/SSL_CTX +// and extract it in a callback. See how it's done, for example, in PSK +// callback or in DTLS verification callback. struct QSslErrorList { QMutex mutex; @@ -406,6 +494,21 @@ bool QSslSocketBackendPrivate::initSslContext() } #endif +#if QT_CONFIG(ocsp) + if (configuration.ocspStaplingEnabled) { + if (mode == QSslSocket::SslServerMode) { + setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError, + QSslSocket::tr("Server-side QSslSocket does not support OCSP stapling")); + return false; + } + if (q_SSL_set_tlsext_status_type(ssl, TLSEXT_STATUSTYPE_ocsp) != 1) { + setErrorAndEmit(QAbstractSocket::SslInternalError, + QSslSocket::tr("Failed to enable OCSP stapling")); + return false; + } + } +#endif // ocsp + return true; } @@ -993,9 +1096,33 @@ bool QSslSocketBackendPrivate::startHandshake() } } - bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer - || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer - && mode == QSslSocket::SslClientMode); + const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer + || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer + && mode == QSslSocket::SslClientMode); + +#if QT_CONFIG(ocsp) + // For now it's always QSslSocket::SslClientMode - initSslContext() will bail out early, + // if it's enabled in QSslSocket::SslServerMode. This can change. + if (!configuration.peerCertificate.isNull() && configuration.ocspStaplingEnabled && doVerifyPeer) { + if (!checkOcspStatus()) { + if (ocspErrors.isEmpty()) { + { + const ScopedBool bg(inSetAndEmitError, true); + setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, ocspErrorDescription); + } + q->abort(); + return false; + } + + for (const QSslError &error : ocspErrors) { + errors << error; + emit q->peerVerifyError(error); + if (q->state() != QAbstractSocket::ConnectedState) + return false; + } + } + } +#endif // ocsp // Check the peer certificate itself. First try the subject's common name // (CN) as a wildcard, then try all alternate subject name DNS entries the @@ -1242,6 +1369,180 @@ void QSslSocketBackendPrivate::_q_caRootLoaded(QSslCertificate cert, QSslCertifi #endif +#if QT_CONFIG(ocsp) + +bool QSslSocketBackendPrivate::checkOcspStatus() +{ + Q_ASSERT(ssl); + Q_ASSERT(mode == QSslSocket::SslClientMode); // See initSslContext() for SslServerMode + Q_ASSERT(configuration.peerVerifyMode != QSslSocket::VerifyNone); + + ocspErrorDescription.clear(); + ocspErrors.clear(); + + const unsigned char *responseData = nullptr; + const long responseLength = q_SSL_get_tlsext_status_ocsp_resp(ssl, &responseData); + if (responseLength <= 0 || !responseData) { + ocspErrors.push_back(QSslError::OcspNoResponseFound); + return false; + } + + OCSP_RESPONSE *response = q_d2i_OCSP_RESPONSE(nullptr, &responseData, responseLength); + if (!response) { + // Treat this as a fatal SslHandshakeError. + ocspErrorDescription = QSslSocket::tr("Failed to decode OCSP response"); + return false; + } + const QSharedPointer<OCSP_RESPONSE> responseGuard(response, q_OCSP_RESPONSE_free); + + const int ocspStatus = q_OCSP_response_status(response); + if (ocspStatus != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + // It's not a definitive response, it's an error message (not signed by the responder). + ocspErrors.push_back(qt_OCSP_response_status_to_QSslError(ocspStatus)); + return false; + } + + OCSP_BASICRESP *basicResponse = q_OCSP_response_get1_basic(response); + if (!basicResponse) { + // SslHandshakeError. + ocspErrorDescription = QSslSocket::tr("Failed to extract basic OCSP response"); + return false; + } + const QSharedPointer<OCSP_BASICRESP> basicResponseGuard(basicResponse, q_OCSP_BASICRESP_free); + + SSL_CTX *ctx = q_SSL_get_SSL_CTX(ssl); // Does not increment refcount. + Q_ASSERT(ctx); + X509_STORE *store = q_SSL_CTX_get_cert_store(ctx); // Does not increment refcount. + if (!store) { + // SslHandshakeError. + ocspErrorDescription = QSslSocket::tr("No certificate verification store, cannot verify OCSP response"); + return false; + } + + STACK_OF(X509) *peerChain = q_SSL_get_peer_cert_chain(ssl); // Does not increment refcount. + X509 *peerX509 = q_SSL_get_peer_certificate(ssl); + Q_ASSERT(peerChain || peerX509); + const QSharedPointer<X509> peerX509Guard(peerX509, q_X509_free); + // OCSP_basic_verify with 0 as verificationFlags: + // + // 0) Tries to find the OCSP responder's certificate in either peerChain + // or basicResponse->certs. If not found, verification fails. + // 1) It checks the signature using the responder's public key. + // 2) Then it tries to validate the responder's cert (building a chain + // etc.) + // 3) It checks CertID in response. + // 4) Ensures the responder is authorized to sign the status respond. + // + // Here it's important to notice that it calls X509_cert_verify and + // as a result, possibly, our verification callback. Given this callback + // at the moment uses a global variable, we have to lock. This will change + // as soon as we fix our verification procedure. + // Also note, OpenSSL prior to 1.0.2b would only use bs->certs to + // verify the responder's chain (see their commit 4ba9a4265bd). + // Working this around - is too much fuss for ancient versions we + // are dropping quite soon anyway. + { + const unsigned long verificationFlags = 0; + const QMutexLocker locker(&_q_sslErrorList()->mutex); + // Before unlocking the mutex, startHandshake() stores errors (found in SSL_connect() + // or SSL_accept()) into the local variable, so it's safe to clear it here - as soon + // as we managed to lock, whoever had the lock before, already stored their own copy + // of errors. + _q_sslErrorList()->errors.clear(); + const int success = q_OCSP_basic_verify(basicResponse, peerChain, store, verificationFlags); + if (success <= 0 || _q_sslErrorList()->errors.size()) { + _q_sslErrorList()->errors.clear(); + ocspErrors.push_back(QSslError::OcspResponseCannotBeTrusted); + } + } + + if (q_OCSP_resp_count(basicResponse) != 1) { + ocspErrors.push_back(QSslError::OcspMalformedResponse); + return false; + } + + OCSP_SINGLERESP *singleResponse = q_OCSP_resp_get0(basicResponse, 0); + if (!singleResponse) { + ocspErrors.clear(); + // A fatal problem -> SslHandshakeError. + ocspErrorDescription = QSslSocket::tr("Failed to decode a SingleResponse from OCSP status response"); + return false; + } + + // Let's make sure the response is for the correct certificate - we + // can re-create this CertID using our peer's certificate and its + // issuer's public key. + + bool matchFound = false; + if (configuration.peerCertificate.isSelfSigned()) { + matchFound = qt_OCSP_certificate_match(singleResponse, peerX509, peerX509); + } else { + const STACK_OF(X509) *certs = q_SSL_get_peer_cert_chain(ssl); + if (!certs) // Oh, what a cataclysm! Last try: + certs = q_OCSP_resp_get0_certs(basicResponse); + if (certs) { + // It could be the first certificate in 'certs' is our peer's + // certificate. Since it was not captured by the 'self-signed' branch + // above, the CertID will not match and we'll just iterate on to the + // next certificate. So we start from 0, not 1. + for (int i = 0, e = q_sk_X509_num(certs); i < e; ++i) { + X509 *issuer = q_sk_X509_value(certs, i); + matchFound = qt_OCSP_certificate_match(singleResponse, peerX509, issuer); + if (matchFound) { + if (q_X509_check_issued(issuer, peerX509) == X509_V_OK) + break; + matchFound = false; + } + } + } + } + + if (!matchFound) + ocspErrors.push_back({QSslError::OcspResponseCertIdUnknown, configuration.peerCertificate}); + + // Check if the response is valid time-wise: + ASN1_GENERALIZEDTIME *revTime = nullptr; + ASN1_GENERALIZEDTIME *thisUpdate = nullptr; + ASN1_GENERALIZEDTIME *nextUpdate = nullptr; + int reason; + const int certStatus = q_OCSP_single_get0_status(singleResponse, &reason, &revTime, &thisUpdate, &nextUpdate); + if (!thisUpdate) { + // This is unexpected, treat as SslHandshakeError, OCSP_check_validity assumes this pointer + // to be != nullptr. + ocspErrors.clear(); + ocspErrorDescription = QSslSocket::tr("Failed to extract 'this update time' from the SingleResponse"); + return false; + } + + // OCSP_check_validity(this, next, nsec, maxsec) does this check: + // this <= now <= next. They allow some freedom to account + // for delays/time inaccuracy. + // this > now + nsec ? -> NOT_YET_VALID + // if maxsec >= 0: + // now - maxsec > this ? -> TOO_OLD + // now - nsec > next ? -> EXPIRED + // next < this ? -> NEXT_BEFORE_THIS + // OK. + if (!q_OCSP_check_validity(thisUpdate, nextUpdate, 60, -1)) + ocspErrors.push_back({QSslError::OcspResponseExpired, configuration.peerCertificate}); + + // And finally, the status: + switch (certStatus) { + case V_OCSP_CERTSTATUS_GOOD: + // This certificate was not found among the revoked ones. + break; + case V_OCSP_CERTSTATUS_REVOKED: + ocspErrors.push_back({QSslError::CertificateRevoked, configuration.peerCertificate}); + break; + case V_OCSP_CERTSTATUS_UNKNOWN: + ocspErrors.push_back({QSslError::OcspStatusUnknown, configuration.peerCertificate}); + } + + return !ocspErrors.size(); +} + +#endif // ocsp + void QSslSocketBackendPrivate::disconnectFromHost() { if (ssl) { diff --git a/src/network/ssl/qsslsocket_openssl11_symbols_p.h b/src/network/ssl/qsslsocket_openssl11_symbols_p.h index fae007e12d..b94b05b765 100644 --- a/src/network/ssl/qsslsocket_openssl11_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl11_symbols_p.h @@ -172,6 +172,10 @@ void q_BIO_set_init(BIO *a, int init); int q_BIO_get_shutdown(BIO *a); void q_BIO_set_shutdown(BIO *a, int shut); +#if QT_CONFIG(ocsp) +const OCSP_CERTID *q_OCSP_SINGLERESP_get0_id(const OCSP_SINGLERESP *x); +#endif // ocsp + #define q_SSL_CTX_set_min_proto_version(ctx, version) \ q_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, version, nullptr) diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h index c16b9d5f76..6396b44808 100644 --- a/src/network/ssl/qsslsocket_openssl_p.h +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -69,6 +69,9 @@ #include <QtNetwork/private/qtnetworkglobal_p.h> #include "qsslsocket_p.h" +#include <QtCore/qvector.h> +#include <QtCore/qstring.h> + #ifdef Q_OS_WIN #include <qt_windows.h> #if defined(OCSP_RESPONSE) @@ -152,6 +155,15 @@ public: void _q_caRootLoaded(QSslCertificate,QSslCertificate) override; #endif +#if QT_CONFIG(ocsp) + bool checkOcspStatus(); + + // This decription will go to setErrorAndEmit(SslHandshakeError, ocspErrorDescription) + QString ocspErrorDescription; + // These will go to sslErrors() + QVector<QSslError> ocspErrors; +#endif + Q_AUTOTEST_EXPORT static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions); static QSslCipher QSslCipher_from_SSL_CIPHER(const SSL_CIPHER *cipher); static QList<QSslCertificate> STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509); diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index 75ed3c16e0..3688e8ffd3 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -199,6 +199,28 @@ DEFINEFUNC2(int, BIO_meth_set_create, BIO_METHOD *biom, biom, DgramCreateCallbac DEFINEFUNC2(int, BIO_meth_set_destroy, BIO_METHOD *biom, biom, DgramDestroyCallback dtr, dtr, return 0, return) #endif // dtls +#if QT_CONFIG(ocsp) +DEFINEFUNC(const OCSP_CERTID *, OCSP_SINGLERESP_get0_id, const OCSP_SINGLERESP *x, x, return nullptr, return) +DEFINEFUNC3(OCSP_RESPONSE *, d2i_OCSP_RESPONSE, OCSP_RESPONSE **a, a, const unsigned char **in, in, long len, len, return nullptr, return) +DEFINEFUNC(void, OCSP_RESPONSE_free, OCSP_RESPONSE *rs, rs, return, DUMMYARG) +DEFINEFUNC(OCSP_BASICRESP *, OCSP_response_get1_basic, OCSP_RESPONSE *resp, resp, return nullptr, return) +DEFINEFUNC(void, OCSP_BASICRESP_free, OCSP_BASICRESP *bs, bs, return, DUMMYARG) +DEFINEFUNC(int, OCSP_response_status, OCSP_RESPONSE *resp, resp, return OCSP_RESPONSE_STATUS_INTERNALERROR, return) +DEFINEFUNC4(int, OCSP_basic_verify, OCSP_BASICRESP *bs, bs, STACK_OF(X509) *certs, certs, X509_STORE *st, st, unsigned long flags, flags, return -1, return) +DEFINEFUNC(int, OCSP_resp_count, OCSP_BASICRESP *bs, bs, return 0, return) +DEFINEFUNC2(OCSP_SINGLERESP *, OCSP_resp_get0, OCSP_BASICRESP *bs, bs, int idx, idx, return nullptr, return) +DEFINEFUNC5(int, OCSP_single_get0_status, OCSP_SINGLERESP *single, single, int *reason, reason, ASN1_GENERALIZEDTIME **revtime, revtime, + ASN1_GENERALIZEDTIME **thisupd, thisupd, ASN1_GENERALIZEDTIME **nextupd, nextupd, return -1, return) +DEFINEFUNC4(int, OCSP_check_validity, ASN1_GENERALIZEDTIME *thisupd, thisupd, ASN1_GENERALIZEDTIME *nextupd, nextupd, long nsec, nsec, long maxsec, maxsec, return 0, return) +DEFINEFUNC3(OCSP_CERTID *, OCSP_cert_to_id, const EVP_MD *dgst, dgst, X509 *subject, subject, X509 *issuer, issuer, return nullptr, return) +DEFINEFUNC(void, OCSP_CERTID_free, OCSP_CERTID *cid, cid, return, DUMMYARG) +DEFINEFUNC5(int, OCSP_id_get0_info, ASN1_OCTET_STRING **piNameHash, piNameHash, ASN1_OBJECT **pmd, pmd, + ASN1_OCTET_STRING **piKeyHash, piKeyHash, ASN1_INTEGER **pserial, pserial, OCSP_CERTID *cid, cid, + return 0, return) +DEFINEFUNC(const STACK_OF(X509) *, OCSP_resp_get0_certs, const OCSP_BASICRESP *bs, bs, return nullptr, return) +DEFINEFUNC2(int, OCSP_id_cmp, OCSP_CERTID *a, a, OCSP_CERTID *b, b, return -1, return) +#endif // ocsp + DEFINEFUNC2(void, BIO_set_data, BIO *a, a, void *ptr, ptr, return, DUMMYARG) DEFINEFUNC(void *, BIO_get_data, BIO *a, a, return nullptr, return) DEFINEFUNC2(void, BIO_set_init, BIO *a, a, int init, init, return, DUMMYARG) @@ -329,6 +351,7 @@ DEFINEFUNC(const char *, SSLeay_version, int a, a, return nullptr, return) #endif // QT_CONFIG(opensslv11) DEFINEFUNC(long, ASN1_INTEGER_get, ASN1_INTEGER *a, a, return 0, return) +DEFINEFUNC2(int, ASN1_INTEGER_cmp, const ASN1_INTEGER *a, a, const ASN1_INTEGER *b, b, return 1, return) DEFINEFUNC(int, ASN1_STRING_length, ASN1_STRING *a, a, return 0, return) DEFINEFUNC2(int, ASN1_STRING_to_UTF8, unsigned char **a, a, ASN1_STRING *b, b, return 0, return) DEFINEFUNC4(long, BIO_ctrl, BIO *a, a, int b, b, long c, c, void *d, d, return -1, return) @@ -357,6 +380,7 @@ DEFINEFUNC5(int, EVP_CipherInit, EVP_CIPHER_CTX *ctx, ctx, const EVP_CIPHER *typ DEFINEFUNC6(int, EVP_CipherInit_ex, EVP_CIPHER_CTX *ctx, ctx, const EVP_CIPHER *cipher, cipher, ENGINE *impl, impl, const unsigned char *key, key, const unsigned char *iv, iv, int enc, enc, return 0, return) DEFINEFUNC5(int, EVP_CipherUpdate, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, const unsigned char *in, in, int inl, inl, return 0, return) DEFINEFUNC3(int, EVP_CipherFinal, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, return 0, return) +DEFINEFUNC(const EVP_MD *, EVP_get_digestbyname, const char *name, name, return nullptr, return) #ifndef OPENSSL_NO_DES DEFINEFUNC(const EVP_CIPHER *, EVP_des_cbc, DUMMYARG, DUMMYARG, return nullptr, return) DEFINEFUNC(const EVP_CIPHER *, EVP_des_ede3_cbc, DUMMYARG, DUMMYARG, return nullptr, return) @@ -473,6 +497,7 @@ DEFINEFUNC(long, SSL_get_verify_result, const SSL *a, a, return -1, return) DEFINEFUNC(long, SSL_get_verify_result, SSL *a, a, return -1, return) #endif DEFINEFUNC(SSL *, SSL_new, SSL_CTX *a, a, return nullptr, return) +DEFINEFUNC(SSL_CTX *, SSL_get_SSL_CTX, SSL *a, a, return nullptr, return) DEFINEFUNC4(long, SSL_ctrl, SSL *a, a, int cmd, cmd, long larg, larg, void *parg, parg, return -1, return) DEFINEFUNC3(int, SSL_read, SSL *a, a, void *b, b, int c, c, return -1, return) DEFINEFUNC3(void, SSL_set_bio, SSL *a, a, BIO *b, b, BIO *c, c, return, DUMMYARG) @@ -1020,7 +1045,25 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(BIO_meth_set_create) RESOLVEFUNC(BIO_meth_set_destroy) #endif // dtls - +#if QT_CONFIG(ocsp) + RESOLVEFUNC(OCSP_SINGLERESP_get0_id) + RESOLVEFUNC(d2i_OCSP_RESPONSE) + RESOLVEFUNC(OCSP_RESPONSE_free) + RESOLVEFUNC(OCSP_response_status) + RESOLVEFUNC(OCSP_response_get1_basic) + RESOLVEFUNC(OCSP_BASICRESP_free) + RESOLVEFUNC(OCSP_basic_verify) + RESOLVEFUNC(OCSP_resp_count) + RESOLVEFUNC(OCSP_resp_get0) + RESOLVEFUNC(OCSP_single_get0_status) + RESOLVEFUNC(OCSP_check_validity) + RESOLVEFUNC(OCSP_cert_to_id) + RESOLVEFUNC(OCSP_id_get0_info) + RESOLVEFUNC(OCSP_resp_get0_certs); + RESOLVEFUNC(OCSP_CERTID_free) + RESOLVEFUNC(OCSP_cert_to_id) + RESOLVEFUNC(OCSP_id_cmp) +#endif // ocsp RESOLVEFUNC(BIO_set_data) RESOLVEFUNC(BIO_get_data) RESOLVEFUNC(BIO_set_init) @@ -1127,6 +1170,7 @@ bool q_resolveOpenSslSymbols() #endif // !opensslv11 RESOLVEFUNC(ASN1_INTEGER_get) + RESOLVEFUNC(ASN1_INTEGER_cmp) RESOLVEFUNC(ASN1_STRING_length) RESOLVEFUNC(ASN1_STRING_to_UTF8) RESOLVEFUNC(BIO_ctrl) @@ -1163,6 +1207,7 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(EVP_CipherInit_ex) RESOLVEFUNC(EVP_CipherUpdate) RESOLVEFUNC(EVP_CipherFinal) + RESOLVEFUNC(EVP_get_digestbyname) #ifndef OPENSSL_NO_DES RESOLVEFUNC(EVP_des_cbc) RESOLVEFUNC(EVP_des_ede3_cbc) @@ -1265,6 +1310,7 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(SSL_get_peer_certificate) RESOLVEFUNC(SSL_get_verify_result) RESOLVEFUNC(SSL_new) + RESOLVEFUNC(SSL_get_SSL_CTX) RESOLVEFUNC(SSL_ctrl) RESOLVEFUNC(SSL_read) RESOLVEFUNC(SSL_set_accept_state) diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index 7e759d3825..b5aac1c2ae 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -72,6 +72,10 @@ #include "qsslsocket_openssl_p.h" #include <QtCore/qglobal.h> +#if QT_CONFIG(ocsp) +#include <openssl/ocsp.h> +#endif + QT_BEGIN_NAMESPACE #define DUMMYARG @@ -224,6 +228,7 @@ QT_BEGIN_NAMESPACE bool q_resolveOpenSslSymbols(); long q_ASN1_INTEGER_get(ASN1_INTEGER *a); +int q_ASN1_INTEGER_cmp(const ASN1_INTEGER *x, const ASN1_INTEGER *y); int q_ASN1_STRING_length(ASN1_STRING *a); int q_ASN1_STRING_to_UTF8(unsigned char **a, ASN1_STRING *b); long q_BIO_ctrl(BIO *a, int b, long c, void *d); @@ -267,6 +272,8 @@ int q_EVP_CipherInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, const unsigned int q_EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ENGINE *impl, const unsigned char *key, const unsigned char *iv, int enc); int q_EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl); int q_EVP_CipherFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl); +const EVP_MD *q_EVP_get_digestbyname(const char *name); + #ifndef OPENSSL_NO_DES const EVP_CIPHER *q_EVP_des_cbc(); const EVP_CIPHER *q_EVP_des_ede3_cbc(); @@ -299,6 +306,7 @@ int q_OBJ_ln2nid(const char *s); int q_i2t_ASN1_OBJECT(char *buf, int buf_len, ASN1_OBJECT *obj); int q_OBJ_obj2txt(char *buf, int buf_len, ASN1_OBJECT *obj, int no_name); int q_OBJ_obj2nid(const ASN1_OBJECT *a); +#define q_EVP_get_digestbynid(a) q_EVP_get_digestbyname(q_OBJ_nid2sn(a)) #ifdef SSLEAY_MACROS // ### verify void *q_PEM_ASN1_read_bio(d2i_of_void *a, const char *b, BIO *c, void **d, pem_password_cb *e, @@ -385,6 +393,7 @@ STACK_OF(X509) *q_SSL_get_peer_cert_chain(SSL *a); X509 *q_SSL_get_peer_certificate(SSL *a); long q_SSL_get_verify_result(const SSL *a); SSL *q_SSL_new(SSL_CTX *a); +SSL_CTX *q_SSL_get_SSL_CTX(SSL *a); long q_SSL_ctrl(SSL *ssl,int cmd, long larg, void *parg); int q_SSL_read(SSL *a, void *b, int c); void q_SSL_set_bio(SSL *a, BIO *b, BIO *c); @@ -576,6 +585,36 @@ int q_BIO_set_ex_data(BIO *b, int idx, void *data); class QDateTime; QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime); +#ifndef OPENSSL_NO_TLSEXT + +#define q_SSL_set_tlsext_status_type(ssl, type) q_SSL_ctrl((ssl), SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE, (type), nullptr) + +#endif // OPENSSL_NO_TLSEXT + +#if QT_CONFIG(ocsp) + +OCSP_RESPONSE *q_d2i_OCSP_RESPONSE(OCSP_RESPONSE **a, const unsigned char **in, long len); +void q_OCSP_RESPONSE_free(OCSP_RESPONSE *rs); +int q_OCSP_response_status(OCSP_RESPONSE *resp); +OCSP_BASICRESP *q_OCSP_response_get1_basic(OCSP_RESPONSE *resp); +void q_OCSP_BASICRESP_free(OCSP_BASICRESP *bs); +int q_OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs, X509_STORE *st, unsigned long flags); +int q_OCSP_resp_count(OCSP_BASICRESP *bs); +OCSP_SINGLERESP *q_OCSP_resp_get0(OCSP_BASICRESP *bs, int idx); +int q_OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason, ASN1_GENERALIZEDTIME **revtime, + ASN1_GENERALIZEDTIME **thisupd, ASN1_GENERALIZEDTIME **nextupd); +int q_OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd, long nsec, long maxsec); +int q_OCSP_id_get0_info(ASN1_OCTET_STRING **piNameHash, ASN1_OBJECT **pmd, ASN1_OCTET_STRING **pikeyHash, + ASN1_INTEGER **pserial, OCSP_CERTID *cid); +const STACK_OF(X509) *q_OCSP_resp_get0_certs(const OCSP_BASICRESP *bs); +OCSP_CERTID *q_OCSP_cert_to_id(const EVP_MD *dgst, X509 *subject, X509 *issuer); +void q_OCSP_CERTID_free(OCSP_CERTID *cid); +int q_OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b); + +#define q_SSL_get_tlsext_status_ocsp_resp(ssl, arg) q_SSL_ctrl(ssl, SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP, 0, arg) + +#endif // ocsp + QT_END_NAMESPACE #endif |