2022-05-10 10:06:48 +00:00
|
|
|
// Copyright (C) 2021 The Qt Company Ltd.
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
#include "qsslsocket_openssl_symbols_p.h"
|
2021-02-02 13:55:00 +00:00
|
|
|
#include "qx509_openssl_p.h"
|
2021-03-04 18:20:18 +00:00
|
|
|
#include "qtls_openssl_p.h"
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2018-03-22 15:45:04 +00:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
#include "qwindowscarootfetcher_p.h"
|
|
|
|
#endif
|
|
|
|
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
#include <QtNetwork/private/qsslpresharedkeyauthenticator_p.h>
|
|
|
|
#include <QtNetwork/private/qsslcertificate_p.h>
|
|
|
|
#include <QtNetwork/private/qocspresponse_p.h>
|
|
|
|
#include <QtNetwork/private/qsslsocket_p.h>
|
|
|
|
|
|
|
|
#include <QtNetwork/qsslpresharedkeyauthenticator.h>
|
|
|
|
|
2018-05-08 10:35:46 +00:00
|
|
|
#include <QtCore/qscopedvaluerollback.h>
|
2020-07-17 13:47:33 +00:00
|
|
|
#include <QtCore/qscopeguard.h>
|
2018-10-25 08:44:16 +00:00
|
|
|
|
2018-11-13 14:25:25 +00:00
|
|
|
#include <algorithm>
|
2021-03-04 18:20:18 +00:00
|
|
|
#include <cstring>
|
2014-10-15 13:13:47 +00:00
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
2022-04-12 13:03:54 +00:00
|
|
|
using namespace Qt::StringLiterals;
|
|
|
|
|
2021-01-26 16:20:29 +00:00
|
|
|
namespace {
|
2021-01-18 13:29:10 +00:00
|
|
|
|
2020-06-25 08:44:57 +00:00
|
|
|
QSsl::AlertLevel tlsAlertLevel(int value)
|
2019-10-30 10:49:07 +00:00
|
|
|
{
|
2020-06-25 08:44:57 +00:00
|
|
|
using QSsl::AlertLevel;
|
|
|
|
|
2019-10-30 10:49:07 +00:00
|
|
|
if (const char *typeString = q_SSL_alert_type_string(value)) {
|
|
|
|
// Documented to return 'W' for warning, 'F' for fatal,
|
|
|
|
// 'U' for unknown.
|
|
|
|
switch (typeString[0]) {
|
|
|
|
case 'W':
|
2020-06-25 08:44:57 +00:00
|
|
|
return AlertLevel::Warning;
|
2019-10-30 10:49:07 +00:00
|
|
|
case 'F':
|
2020-06-25 08:44:57 +00:00
|
|
|
return AlertLevel::Fatal;
|
2019-10-30 10:49:07 +00:00
|
|
|
default:;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 08:44:57 +00:00
|
|
|
return AlertLevel::Unknown;
|
2019-10-30 10:49:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QString tlsAlertDescription(int value)
|
|
|
|
{
|
2022-04-12 13:12:22 +00:00
|
|
|
QString description = QLatin1StringView(q_SSL_alert_desc_string_long(value));
|
2019-10-30 10:49:07 +00:00
|
|
|
if (!description.size())
|
2022-04-12 13:03:54 +00:00
|
|
|
description = "no description provided"_L1;
|
2019-10-30 10:49:07 +00:00
|
|
|
return description;
|
|
|
|
}
|
|
|
|
|
2020-06-25 08:44:57 +00:00
|
|
|
QSsl::AlertType tlsAlertType(int value)
|
2019-10-30 10:49:07 +00:00
|
|
|
{
|
|
|
|
// In case for some reason openssl gives us a value,
|
|
|
|
// which is not in our enum actually, we leave it to
|
|
|
|
// an application to handle (supposedly they have
|
|
|
|
// if or switch-statements).
|
2020-06-25 08:44:57 +00:00
|
|
|
return QSsl::AlertType(value & 0xff);
|
2019-10-30 10:49:07 +00:00
|
|
|
}
|
|
|
|
|
2020-05-14 14:40:08 +00:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
2020-06-22 10:25:41 +00:00
|
|
|
QSslCertificate findCertificateToFetch(const QList<QSslError> &tlsErrors, bool checkAIA)
|
2020-05-14 14:40:08 +00:00
|
|
|
{
|
|
|
|
QSslCertificate certToFetch;
|
|
|
|
|
|
|
|
for (const auto &tlsError : tlsErrors) {
|
|
|
|
switch (tlsError.error()) {
|
|
|
|
case QSslError::UnableToGetLocalIssuerCertificate: // site presented intermediate cert, but root is unknown
|
|
|
|
case QSslError::SelfSignedCertificateInChain: // site presented a complete chain, but root is unknown
|
|
|
|
certToFetch = tlsError.certificate();
|
|
|
|
break;
|
|
|
|
case QSslError::SelfSignedCertificate:
|
|
|
|
case QSslError::CertificateBlacklisted:
|
|
|
|
//With these errors, we know it will be untrusted so save time by not asking windows
|
|
|
|
return QSslCertificate{};
|
|
|
|
default:
|
|
|
|
#ifdef QSSLSOCKET_DEBUG
|
2021-03-04 18:20:18 +00:00
|
|
|
qCDebug(lcTlsBackend) << tlsError.errorString();
|
2020-05-14 14:40:08 +00:00
|
|
|
#endif
|
|
|
|
//TODO - this part is strange.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (checkAIA) {
|
|
|
|
const auto extensions = certToFetch.extensions();
|
|
|
|
for (const auto &ext : extensions) {
|
|
|
|
if (ext.oid() == QStringLiteral("1.3.6.1.5.5.7.1.1")) // See RFC 4325
|
|
|
|
return certToFetch;
|
|
|
|
}
|
|
|
|
//The only reason we check this extensions is because an application set trusted
|
|
|
|
//CA certificates explicitly, thus technically disabling CA fetch. So, if it's
|
|
|
|
//the case and an intermediate certificate is missing, and no extensions is
|
|
|
|
//present on the leaf certificate - we fail the handshake immediately.
|
|
|
|
return QSslCertificate{};
|
|
|
|
}
|
|
|
|
|
|
|
|
return certToFetch;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // Q_OS_WIN
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
} // unnamed namespace
|
2019-10-30 10:49:07 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
namespace QTlsPrivate {
|
2019-10-30 10:49:07 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
extern "C" {
|
|
|
|
|
|
|
|
int q_X509Callback(int ok, X509_STORE_CTX *ctx)
|
2019-10-30 10:49:07 +00:00
|
|
|
{
|
2021-03-04 18:20:18 +00:00
|
|
|
if (!ok) {
|
|
|
|
// Store the error and at which depth the error was detected.
|
2019-10-30 10:49:07 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
using ErrorListPtr = QList<QSslErrorEntry> *;
|
|
|
|
ErrorListPtr errors = nullptr;
|
2019-10-30 10:49:07 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// Error list is attached to either 'SSL' or 'X509_STORE'.
|
|
|
|
if (X509_STORE *store = q_X509_STORE_CTX_get0_store(ctx)) // We try store first:
|
|
|
|
errors = ErrorListPtr(q_X509_STORE_get_ex_data(store, 0));
|
2019-10-30 10:49:07 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (!errors) {
|
|
|
|
// Not found on store? Try SSL and its external data then. According to the OpenSSL's
|
|
|
|
// documentation:
|
|
|
|
//
|
|
|
|
// "Whenever a X509_STORE_CTX object is created for the verification of the
|
|
|
|
// peer's certificate during a handshake, a pointer to the SSL object is
|
|
|
|
// stored into the X509_STORE_CTX object to identify the connection affected.
|
|
|
|
// To retrieve this pointer the X509_STORE_CTX_get_ex_data() function can be
|
|
|
|
// used with the correct index."
|
|
|
|
const auto offset = QTlsBackendOpenSSL::s_indexForSSLExtraData
|
2021-11-29 15:31:21 +00:00
|
|
|
+ TlsCryptographOpenSSL::errorOffsetInExData;
|
|
|
|
if (SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(
|
|
|
|
ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx()))) {
|
|
|
|
|
|
|
|
// We may be in a renegotiation, check if we are inside a call to SSL_read:
|
|
|
|
const auto tlsOffset = QTlsBackendOpenSSL::s_indexForSSLExtraData
|
|
|
|
+ TlsCryptographOpenSSL::socketOffsetInExData;
|
|
|
|
auto tls = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, tlsOffset));
|
|
|
|
Q_ASSERT(tls);
|
|
|
|
if (tls->isInSslRead()) {
|
|
|
|
// We are in a renegotiation, make a note of this for later.
|
|
|
|
// We'll check that the certificate is the same as the one we got during
|
|
|
|
// the initial handshake
|
|
|
|
tls->setRenegotiated(true);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
errors = ErrorListPtr(q_SSL_get_ex_data(ssl, offset));
|
2021-11-29 15:31:21 +00:00
|
|
|
}
|
2021-03-04 18:20:18 +00:00
|
|
|
}
|
2019-10-30 10:49:07 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (!errors) {
|
|
|
|
qCWarning(lcTlsBackend, "Neither X509_STORE, nor SSL contains error list, handshake failure");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
errors->append(X509CertificateOpenSSL::errorEntryFromStoreContext(ctx));
|
|
|
|
}
|
|
|
|
// Always return OK to allow verification to continue. We handle the
|
|
|
|
// errors gracefully after collecting all errors, after verification has
|
|
|
|
// completed.
|
|
|
|
return 1;
|
2019-10-30 10:49:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int q_X509CallbackDirect(int ok, X509_STORE_CTX *ctx)
|
|
|
|
{
|
|
|
|
// Passed to SSL_CTX_set_verify()
|
|
|
|
// https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_verify.html
|
|
|
|
// Returns 0 to abort verification, 1 to continue.
|
|
|
|
|
|
|
|
// This is a new, experimental verification callback, reporting
|
|
|
|
// errors immediately and returning 0 or 1 depending on an application
|
|
|
|
// either ignoring or not ignoring verification errors as they come.
|
|
|
|
if (!ctx) {
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
qCWarning(lcTlsBackend, "Invalid store context (nullptr)");
|
2019-10-30 10:49:07 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ok) {
|
|
|
|
// "Whenever a X509_STORE_CTX object is created for the verification of the
|
|
|
|
// peer's certificate during a handshake, a pointer to the SSL object is
|
|
|
|
// stored into the X509_STORE_CTX object to identify the connection affected.
|
|
|
|
// To retrieve this pointer the X509_STORE_CTX_get_ex_data() function can be
|
|
|
|
// used with the correct index."
|
|
|
|
SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx()));
|
|
|
|
if (!ssl) {
|
2021-03-04 18:20:18 +00:00
|
|
|
qCWarning(lcTlsBackend, "No external data (SSL) found in X509 store object");
|
2019-10-30 10:49:07 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
const auto offset = QTlsBackendOpenSSL::s_indexForSSLExtraData
|
|
|
|
+ TlsCryptographOpenSSL::socketOffsetInExData;
|
|
|
|
auto crypto = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, offset));
|
|
|
|
if (!crypto) {
|
|
|
|
qCWarning(lcTlsBackend, "No external data (TlsCryptographOpenSSL) found in SSL object");
|
2019-10-30 10:49:07 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
return crypto->emitErrorFromCallback(ctx);
|
2019-10-30 10:49:07 +00:00
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-09-27 11:04:54 +00:00
|
|
|
#ifndef OPENSSL_NO_PSK
|
2021-03-04 18:20:18 +00:00
|
|
|
static unsigned q_ssl_psk_client_callback(SSL *ssl, const char *hint, char *identity, unsigned max_identity_len,
|
|
|
|
unsigned char *psk, unsigned max_psk_len)
|
2014-10-15 13:13:47 +00:00
|
|
|
{
|
2021-03-04 18:20:18 +00:00
|
|
|
auto *tls = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData));
|
|
|
|
return tls->pskClientTlsCallback(hint, identity, max_identity_len, psk, max_psk_len);
|
2014-10-15 13:13:47 +00:00
|
|
|
}
|
2016-03-23 11:27:11 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
static unsigned int q_ssl_psk_server_callback(SSL *ssl, const char *identity, unsigned char *psk,
|
|
|
|
unsigned int max_psk_len)
|
2016-03-23 11:27:11 +00:00
|
|
|
{
|
2021-03-04 18:20:18 +00:00
|
|
|
auto *tls = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData));
|
|
|
|
Q_ASSERT(tls);
|
|
|
|
return tls->pskServerTlsCallback(identity, psk, max_psk_len);
|
2016-03-23 11:27:11 +00:00
|
|
|
}
|
2018-12-13 14:39:26 +00:00
|
|
|
|
|
|
|
#ifdef TLS1_3_VERSION
|
2021-03-04 18:20:18 +00:00
|
|
|
static unsigned q_ssl_psk_restore_client(SSL *ssl, const char *hint, char *identity, unsigned max_identity_len,
|
|
|
|
unsigned char *psk, unsigned max_psk_len)
|
2018-12-13 14:39:26 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(hint);
|
|
|
|
Q_UNUSED(identity);
|
|
|
|
Q_UNUSED(max_identity_len);
|
|
|
|
Q_UNUSED(psk);
|
|
|
|
Q_UNUSED(max_psk_len);
|
|
|
|
|
|
|
|
#ifdef QT_DEBUG
|
2021-03-04 18:20:18 +00:00
|
|
|
auto tls = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData));
|
|
|
|
Q_ASSERT(tls);
|
|
|
|
Q_ASSERT(tls->d);
|
|
|
|
Q_ASSERT(tls->d->tlsMode() == QSslSocket::SslClientMode);
|
2018-12-13 14:39:26 +00:00
|
|
|
#endif
|
2021-08-31 13:18:55 +00:00
|
|
|
unsigned retVal = 0;
|
|
|
|
|
|
|
|
// Let developers opt-in to having the normal PSK callback get called for TLS 1.3
|
|
|
|
// PSK (which works differently in a few ways, and is called at the start of every connection).
|
|
|
|
// When they do opt-in we just call the old callback from here.
|
|
|
|
if (qEnvironmentVariableIsSet("QT_USE_TLS_1_3_PSK"))
|
|
|
|
retVal = q_ssl_psk_client_callback(ssl, hint, identity, max_identity_len, psk, max_psk_len);
|
|
|
|
|
2018-12-13 14:39:26 +00:00
|
|
|
q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback);
|
|
|
|
|
2021-08-31 13:18:55 +00:00
|
|
|
return retVal;
|
2018-12-13 14:39:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int q_ssl_psk_use_session_callback(SSL *ssl, const EVP_MD *md, const unsigned char **id,
|
|
|
|
size_t *idlen, SSL_SESSION **sess)
|
|
|
|
{
|
|
|
|
Q_UNUSED(md);
|
|
|
|
Q_UNUSED(id);
|
|
|
|
Q_UNUSED(idlen);
|
|
|
|
Q_UNUSED(sess);
|
|
|
|
|
|
|
|
#ifdef QT_DEBUG
|
2021-03-04 18:20:18 +00:00
|
|
|
auto *tls = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData));
|
|
|
|
Q_ASSERT(tls);
|
|
|
|
Q_ASSERT(tls->d);
|
|
|
|
Q_ASSERT(tls->d->tlsMode() == QSslSocket::SslClientMode);
|
2018-12-13 14:39:26 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// Temporarily rebind the psk because it will be called next. The function will restore it.
|
|
|
|
q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_restore_client);
|
|
|
|
|
|
|
|
return 1; // need to return 1 or else "the connection setup fails."
|
|
|
|
}
|
2020-01-27 13:11:08 +00:00
|
|
|
|
|
|
|
int q_ssl_sess_set_new_cb(SSL *ssl, SSL_SESSION *session)
|
|
|
|
{
|
|
|
|
if (!ssl) {
|
2021-03-04 18:20:18 +00:00
|
|
|
qCWarning(lcTlsBackend, "Invalid SSL (nullptr)");
|
2020-01-27 13:11:08 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!session) {
|
2021-03-04 18:20:18 +00:00
|
|
|
qCWarning(lcTlsBackend, "Invalid SSL_SESSION (nullptr)");
|
2020-01-27 13:11:08 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
auto *tls = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData));
|
|
|
|
Q_ASSERT(tls);
|
|
|
|
return tls->handleNewSessionTicket(ssl);
|
2020-01-27 13:11:08 +00:00
|
|
|
}
|
2018-12-13 14:39:26 +00:00
|
|
|
#endif // TLS1_3_VERSION
|
|
|
|
|
2019-09-27 11:04:54 +00:00
|
|
|
#endif // !OPENSSL_NO_PSK
|
2018-11-13 14:25:25 +00:00
|
|
|
|
|
|
|
#if QT_CONFIG(ocsp)
|
|
|
|
|
|
|
|
int qt_OCSP_status_server_callback(SSL *ssl, void *ocspRequest)
|
|
|
|
{
|
2020-06-27 12:18:09 +00:00
|
|
|
Q_UNUSED(ocspRequest);
|
2018-11-13 14:25:25 +00:00
|
|
|
if (!ssl)
|
|
|
|
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
auto crypto = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData));
|
|
|
|
if (!crypto)
|
2018-11-13 14:25:25 +00:00
|
|
|
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
Q_ASSERT(crypto->d);
|
|
|
|
Q_ASSERT(crypto->d->tlsMode() == QSslSocket::SslServerMode);
|
|
|
|
const QByteArray &response = crypto->ocspResponseDer;
|
2018-11-13 14:25:25 +00:00
|
|
|
Q_ASSERT(response.size());
|
|
|
|
|
|
|
|
unsigned char *derCopy = static_cast<unsigned char *>(q_OPENSSL_malloc(size_t(response.size())));
|
|
|
|
if (!derCopy)
|
|
|
|
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
|
|
|
|
|
|
|
std::copy(response.data(), response.data() + response.size(), derCopy);
|
2021-10-11 16:44:48 +00:00
|
|
|
// We don't check the return value: internally OpenSSL simply assigns the
|
2018-11-13 14:25:25 +00:00
|
|
|
// pointer (it assumes it now owns this memory btw!) and the length.
|
|
|
|
q_SSL_set_tlsext_status_ocsp_resp(ssl, derCopy, response.size());
|
|
|
|
|
|
|
|
return SSL_TLSEXT_ERR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // ocsp
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
void qt_AlertInfoCallback(const SSL *connection, int from, int value)
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2021-03-04 18:20:18 +00:00
|
|
|
// Passed to SSL_set_info_callback()
|
|
|
|
// https://www.openssl.org/docs/man1.1.1/man3/SSL_set_info_callback.html
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (!connection) {
|
|
|
|
#ifdef QSSLSOCKET_DEBUG
|
|
|
|
qCWarning(lcTlsBackend, "Invalid 'connection' parameter (nullptr)");
|
|
|
|
#endif // QSSLSOCKET_DEBUG
|
|
|
|
return;
|
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
const auto offset = QTlsBackendOpenSSL::s_indexForSSLExtraData
|
|
|
|
+ TlsCryptographOpenSSL::socketOffsetInExData;
|
|
|
|
auto crypto = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(connection, offset));
|
|
|
|
if (!crypto) {
|
|
|
|
// SSL_set_ex_data can fail:
|
|
|
|
#ifdef QSSLSOCKET_DEBUG
|
|
|
|
qCWarning(lcTlsBackend, "No external data (socket backend) found for parameter 'connection'");
|
|
|
|
#endif // QSSLSOCKET_DEBUG
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(from & SSL_CB_ALERT)) {
|
|
|
|
// We only want to know about alerts (at least for now).
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (from & SSL_CB_WRITE)
|
|
|
|
crypto->alertMessageSent(value);
|
|
|
|
else
|
|
|
|
crypto->alertMessageReceived(value);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
} // extern "C"
|
|
|
|
|
2018-10-25 08:44:16 +00:00
|
|
|
#if QT_CONFIG(ocsp)
|
2021-03-04 18:20:18 +00:00
|
|
|
namespace {
|
2018-10-25 08:44:16 +00:00
|
|
|
|
2020-07-13 10:43:44 +00:00
|
|
|
QSslError::SslError qt_OCSP_response_status_to_SslError(long code)
|
2018-10-25 08:44:16 +00:00
|
|
|
{
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2019-02-11 15:07:04 +00:00
|
|
|
QOcspRevocationReason qt_OCSP_revocation_reason(int reason)
|
2019-01-16 13:43:09 +00:00
|
|
|
{
|
|
|
|
switch (reason) {
|
|
|
|
case OCSP_REVOKED_STATUS_NOSTATUS:
|
2019-02-11 15:07:04 +00:00
|
|
|
return QOcspRevocationReason::None;
|
2019-01-16 13:43:09 +00:00
|
|
|
case OCSP_REVOKED_STATUS_UNSPECIFIED:
|
2019-02-11 15:07:04 +00:00
|
|
|
return QOcspRevocationReason::Unspecified;
|
2019-01-16 13:43:09 +00:00
|
|
|
case OCSP_REVOKED_STATUS_KEYCOMPROMISE:
|
2019-02-11 15:07:04 +00:00
|
|
|
return QOcspRevocationReason::KeyCompromise;
|
2019-01-16 13:43:09 +00:00
|
|
|
case OCSP_REVOKED_STATUS_CACOMPROMISE:
|
2019-02-11 15:07:04 +00:00
|
|
|
return QOcspRevocationReason::CACompromise;
|
2019-01-16 13:43:09 +00:00
|
|
|
case OCSP_REVOKED_STATUS_AFFILIATIONCHANGED:
|
2019-02-11 15:07:04 +00:00
|
|
|
return QOcspRevocationReason::AffiliationChanged;
|
2019-01-16 13:43:09 +00:00
|
|
|
case OCSP_REVOKED_STATUS_SUPERSEDED:
|
2019-02-11 15:07:04 +00:00
|
|
|
return QOcspRevocationReason::Superseded;
|
2019-01-16 13:43:09 +00:00
|
|
|
case OCSP_REVOKED_STATUS_CESSATIONOFOPERATION:
|
2019-02-11 15:07:04 +00:00
|
|
|
return QOcspRevocationReason::CessationOfOperation;
|
2019-01-16 13:43:09 +00:00
|
|
|
case OCSP_REVOKED_STATUS_CERTIFICATEHOLD:
|
2019-02-11 15:07:04 +00:00
|
|
|
return QOcspRevocationReason::CertificateHold;
|
2019-01-16 13:43:09 +00:00
|
|
|
case OCSP_REVOKED_STATUS_REMOVEFROMCRL:
|
2019-02-11 15:07:04 +00:00
|
|
|
return QOcspRevocationReason::RemoveFromCRL;
|
2019-01-16 13:43:09 +00:00
|
|
|
default:
|
2019-02-11 15:07:04 +00:00
|
|
|
return QOcspRevocationReason::None;
|
2019-01-16 13:43:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
}
|
|
|
|
|
2018-10-25 08:44:16 +00:00
|
|
|
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) {
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
qCWarning(lcTlsBackend, "A SingleResponse without CertID");
|
2018-10-25 08:44:16 +00:00
|
|
|
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) {
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
qCWarning(lcTlsBackend, "Failed to extract a hash and serial number from CertID structure");
|
2018-10-25 08:44:16 +00:00
|
|
|
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.
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
qCWarning(lcTlsBackend, "No serial number in peer's ceritificate");
|
2018-10-25 08:44:16 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const int nid = q_OBJ_obj2nid(md);
|
|
|
|
if (nid == NID_undef) {
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
qCWarning(lcTlsBackend, "Unknown hash algorithm in CertID");
|
2018-10-25 08:44:16 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const EVP_MD *digest = q_EVP_get_digestbynid(nid); // Does not increment refcount.
|
|
|
|
if (!digest) {
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
qCWarning(lcTlsBackend) << "No digest for nid" << nid;
|
2018-10-25 08:44:16 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
OCSP_CERTID *recreatedId = q_OCSP_cert_to_id(digest, peerCert, issuer);
|
|
|
|
if (!recreatedId) {
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
qCWarning(lcTlsBackend, "Failed to re-create CertID");
|
2018-10-25 08:44:16 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const QSharedPointer<OCSP_CERTID> guard(recreatedId, q_OCSP_CERTID_free);
|
|
|
|
|
|
|
|
if (q_OCSP_id_cmp(const_cast<OCSP_CERTID *>(certId), recreatedId)) {
|
2021-11-30 13:30:53 +00:00
|
|
|
qCDebug(lcTlsBackend, "Certificate ID mismatch");
|
2018-10-25 08:44:16 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Bingo!
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
} // unnamed namespace
|
2018-10-25 08:44:16 +00:00
|
|
|
#endif // ocsp
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
TlsCryptographOpenSSL::~TlsCryptographOpenSSL()
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2021-03-04 18:20:18 +00:00
|
|
|
destroySslContext();
|
|
|
|
}
|
TLS socket: make verification callback lock-free (OpenSSL)
When our QSslSocketBackendPrivate (OpenSSL backend) was developed,
the ancient versions of OpenSSL did not have an API needed to pass
an application-specific data into verification callback. Thus the
developers resorted to the use of global variables (a list with errors)
and locks. Some of our auto-tests use QNAM and in-process server.
Whenever the client (essentially qhttpthreadeddelegate) and the server
live in different threads, any use of 'https' is dead-lock prone,
which recent events demonstrated and which were previously observed
but not understood properly (rare occasions, not always easy to
reproduce). Now we fix this for good by removing locking.
There are two places (in 5.12) where these locks are needed:
1. Before calling SSL_connect/SSL_accept (handshake) - here
we reuse the same trick we do in PSK callback ('SSL' has
an external data set, and it's 'this', meaning an object
of type QSslSocketBackendPrivate).
2. The static member function 'verify', here we do not have
'SSL', but we have our temporary 'X509_STORE', to which
we can directly attach an external data - a pointer to
a vector to collect verification errors.
Note, this change assumes that OpenSSL Qt is build/linked
against is at least of version 1.0.1 - we set external data
on SSL unconditionally (no version checks).
Fixes: QTBUG-76157
Change-Id: I05c98e77dfd5fb0c2c260fb6c463732facf53ffc
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2019-06-14 09:34:08 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
void TlsCryptographOpenSSL::init(QSslSocket *qObj, QSslSocketPrivate *dObj)
|
|
|
|
{
|
|
|
|
Q_ASSERT(qObj);
|
|
|
|
Q_ASSERT(dObj);
|
|
|
|
q = qObj;
|
|
|
|
d = dObj;
|
TLS socket: make verification callback lock-free (OpenSSL)
When our QSslSocketBackendPrivate (OpenSSL backend) was developed,
the ancient versions of OpenSSL did not have an API needed to pass
an application-specific data into verification callback. Thus the
developers resorted to the use of global variables (a list with errors)
and locks. Some of our auto-tests use QNAM and in-process server.
Whenever the client (essentially qhttpthreadeddelegate) and the server
live in different threads, any use of 'https' is dead-lock prone,
which recent events demonstrated and which were previously observed
but not understood properly (rare occasions, not always easy to
reproduce). Now we fix this for good by removing locking.
There are two places (in 5.12) where these locks are needed:
1. Before calling SSL_connect/SSL_accept (handshake) - here
we reuse the same trick we do in PSK callback ('SSL' has
an external data set, and it's 'this', meaning an object
of type QSslSocketBackendPrivate).
2. The static member function 'verify', here we do not have
'SSL', but we have our temporary 'X509_STORE', to which
we can directly attach an external data - a pointer to
a vector to collect verification errors.
Note, this change assumes that OpenSSL Qt is build/linked
against is at least of version 1.0.1 - we set external data
on SSL unconditionally (no version checks).
Fixes: QTBUG-76157
Change-Id: I05c98e77dfd5fb0c2c260fb6c463732facf53ffc
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2019-06-14 09:34:08 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
ocspResponses.clear();
|
|
|
|
ocspResponseDer.clear();
|
TLS socket: make verification callback lock-free (OpenSSL)
When our QSslSocketBackendPrivate (OpenSSL backend) was developed,
the ancient versions of OpenSSL did not have an API needed to pass
an application-specific data into verification callback. Thus the
developers resorted to the use of global variables (a list with errors)
and locks. Some of our auto-tests use QNAM and in-process server.
Whenever the client (essentially qhttpthreadeddelegate) and the server
live in different threads, any use of 'https' is dead-lock prone,
which recent events demonstrated and which were previously observed
but not understood properly (rare occasions, not always easy to
reproduce). Now we fix this for good by removing locking.
There are two places (in 5.12) where these locks are needed:
1. Before calling SSL_connect/SSL_accept (handshake) - here
we reuse the same trick we do in PSK callback ('SSL' has
an external data set, and it's 'this', meaning an object
of type QSslSocketBackendPrivate).
2. The static member function 'verify', here we do not have
'SSL', but we have our temporary 'X509_STORE', to which
we can directly attach an external data - a pointer to
a vector to collect verification errors.
Note, this change assumes that OpenSSL Qt is build/linked
against is at least of version 1.0.1 - we set external data
on SSL unconditionally (no version checks).
Fixes: QTBUG-76157
Change-Id: I05c98e77dfd5fb0c2c260fb6c463732facf53ffc
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2019-06-14 09:34:08 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
systemOrSslErrorDetected = false;
|
|
|
|
handshakeInterrupted = false;
|
TLS socket: make verification callback lock-free (OpenSSL)
When our QSslSocketBackendPrivate (OpenSSL backend) was developed,
the ancient versions of OpenSSL did not have an API needed to pass
an application-specific data into verification callback. Thus the
developers resorted to the use of global variables (a list with errors)
and locks. Some of our auto-tests use QNAM and in-process server.
Whenever the client (essentially qhttpthreadeddelegate) and the server
live in different threads, any use of 'https' is dead-lock prone,
which recent events demonstrated and which were previously observed
but not understood properly (rare occasions, not always easy to
reproduce). Now we fix this for good by removing locking.
There are two places (in 5.12) where these locks are needed:
1. Before calling SSL_connect/SSL_accept (handshake) - here
we reuse the same trick we do in PSK callback ('SSL' has
an external data set, and it's 'this', meaning an object
of type QSslSocketBackendPrivate).
2. The static member function 'verify', here we do not have
'SSL', but we have our temporary 'X509_STORE', to which
we can directly attach an external data - a pointer to
a vector to collect verification errors.
Note, this change assumes that OpenSSL Qt is build/linked
against is at least of version 1.0.1 - we set external data
on SSL unconditionally (no version checks).
Fixes: QTBUG-76157
Change-Id: I05c98e77dfd5fb0c2c260fb6c463732facf53ffc
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2019-06-14 09:34:08 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
fetchAuthorityInformation = false;
|
|
|
|
caToFetch = QSslCertificate{};
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2021-07-12 09:41:43 +00:00
|
|
|
void TlsCryptographOpenSSL::checkSettingSslContext(std::shared_ptr<QSslContext> tlsContext)
|
2018-02-19 12:46:21 +00:00
|
|
|
{
|
2021-07-12 09:28:19 +00:00
|
|
|
if (!sslContextPointer)
|
|
|
|
sslContextPointer = std::move(tlsContext);
|
2021-03-04 18:20:18 +00:00
|
|
|
}
|
2018-02-19 12:46:21 +00:00
|
|
|
|
2021-07-12 09:41:43 +00:00
|
|
|
std::shared_ptr<QSslContext> TlsCryptographOpenSSL::sslContext() const
|
2021-03-04 18:20:18 +00:00
|
|
|
{
|
|
|
|
return sslContextPointer;
|
2018-02-19 12:46:21 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
QList<QSslError> TlsCryptographOpenSSL::tlsErrors() const
|
|
|
|
{
|
|
|
|
return sslErrors;
|
|
|
|
}
|
2018-02-19 12:46:21 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
void TlsCryptographOpenSSL::startClientEncryption()
|
2011-11-15 21:58:05 +00:00
|
|
|
{
|
2021-03-04 18:20:18 +00:00
|
|
|
if (!initSslContext()) {
|
|
|
|
Q_ASSERT(d);
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
setErrorAndEmit(d, QAbstractSocket::SslInternalError,
|
|
|
|
QSslSocket::tr("Unable to init SSL Context: %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()));
|
2021-03-04 18:20:18 +00:00
|
|
|
return;
|
2019-11-13 09:37:36 +00:00
|
|
|
}
|
2011-11-15 21:58:05 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// Start connecting. This will place outgoing data in the BIO, so we
|
|
|
|
// follow up with calling transmit().
|
|
|
|
startHandshake();
|
|
|
|
transmit();
|
|
|
|
}
|
2011-11-15 21:58:05 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
void TlsCryptographOpenSSL::startServerEncryption()
|
|
|
|
{
|
|
|
|
if (!initSslContext()) {
|
|
|
|
Q_ASSERT(d);
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
setErrorAndEmit(d, QAbstractSocket::SslInternalError,
|
|
|
|
QSslSocket::tr("Unable to init SSL Context: %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()));
|
2021-03-04 18:20:18 +00:00
|
|
|
return;
|
|
|
|
}
|
2015-04-18 10:34:26 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// Start connecting. This will place outgoing data in the BIO, so we
|
|
|
|
// follow up with calling transmit().
|
|
|
|
startHandshake();
|
|
|
|
transmit();
|
2011-11-15 21:58:05 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
bool TlsCryptographOpenSSL::startHandshake()
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2021-03-04 18:20:18 +00:00
|
|
|
// Check if the connection has been established. Get all errors from the
|
|
|
|
// verification stage.
|
|
|
|
Q_ASSERT(q);
|
|
|
|
Q_ASSERT(d);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
using ScopedBool = QScopedValueRollback<bool>;
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (inSetAndEmitError)
|
2011-04-27 10:05:43 +00:00
|
|
|
return false;
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
const auto mode = d->tlsMode();
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
pendingFatalAlert = false;
|
|
|
|
errorsReportedFromCallback = false;
|
|
|
|
QList<QSslErrorEntry> lastErrors;
|
|
|
|
q_SSL_set_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData + errorOffsetInExData, &lastErrors);
|
|
|
|
|
|
|
|
// SSL_set_ex_data can fail, but see the callback's code - we handle this there.
|
|
|
|
q_SSL_set_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData + socketOffsetInExData, this);
|
|
|
|
q_SSL_set_info_callback(ssl, qt_AlertInfoCallback);
|
|
|
|
|
|
|
|
int result = (mode == QSslSocket::SslClientMode) ? q_SSL_connect(ssl) : q_SSL_accept(ssl);
|
|
|
|
q_SSL_set_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData + errorOffsetInExData, nullptr);
|
|
|
|
// Note, unlike errors as external data on SSL object, we do not unset
|
|
|
|
// a callback/ex-data if alert notifications are enabled: an alert can
|
|
|
|
// arrive after the handshake, for example, this happens when the server
|
|
|
|
// does not find a ClientCert or does not like it.
|
|
|
|
|
|
|
|
if (!lastErrors.isEmpty() || errorsReportedFromCallback)
|
|
|
|
storePeerCertificates();
|
|
|
|
|
2021-03-12 11:12:59 +00:00
|
|
|
// storePeerCertificate() if called above - would update the
|
|
|
|
// configuration with peer's certificates.
|
|
|
|
auto configuration = q->sslConfiguration();
|
2021-03-04 18:20:18 +00:00
|
|
|
if (!errorsReportedFromCallback) {
|
2021-03-12 11:12:59 +00:00
|
|
|
const auto &peerCertificateChain = configuration.peerCertificateChain();
|
2021-03-04 18:20:18 +00:00
|
|
|
for (const auto ¤tError : qAsConst(lastErrors)) {
|
|
|
|
emit q->peerVerifyError(QTlsPrivate::X509CertificateOpenSSL::openSSLErrorToQSslError(currentError.code,
|
2021-03-12 11:12:59 +00:00
|
|
|
peerCertificateChain.value(currentError.depth)));
|
2021-03-04 18:20:18 +00:00
|
|
|
if (q->state() != QAbstractSocket::ConnectedState)
|
|
|
|
break;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
errorList << lastErrors;
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// Connection aborted during handshake phase.
|
|
|
|
if (q->state() != QAbstractSocket::ConnectedState)
|
2011-04-27 10:05:43 +00:00
|
|
|
return false;
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// Check if we're encrypted or not.
|
|
|
|
if (result <= 0) {
|
|
|
|
switch (q_SSL_get_error(ssl, result)) {
|
|
|
|
case SSL_ERROR_WANT_READ:
|
|
|
|
case SSL_ERROR_WANT_WRITE:
|
|
|
|
// The handshake is not yet complete.
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
QString errorString = QTlsBackendOpenSSL::msgErrorsDuringHandshake();
|
|
|
|
#ifdef QSSLSOCKET_DEBUG
|
|
|
|
qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::startHandshake: error!" << errorString;
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
const ScopedBool bg(inSetAndEmitError, true);
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
setErrorAndEmit(d, QAbstractSocket::SslHandshakeFailedError, errorString);
|
2021-03-04 18:20:18 +00:00
|
|
|
if (pendingFatalAlert) {
|
|
|
|
trySendFatalAlert();
|
|
|
|
pendingFatalAlert = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
q->abort();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// store peer certificate chain
|
|
|
|
storePeerCertificates();
|
2014-10-15 13:13:47 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// Start translating errors.
|
|
|
|
QList<QSslError> errors;
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-12 11:12:59 +00:00
|
|
|
// Note, the storePeerCerificates() probably updated the configuration at this point.
|
|
|
|
configuration = q->sslConfiguration();
|
|
|
|
// Check the whole chain for blacklisting (including root, as we check for subjectInfo and issuer)
|
|
|
|
const auto &peerCertificateChain = configuration.peerCertificateChain();
|
|
|
|
for (const QSslCertificate &cert : peerCertificateChain) {
|
2021-03-04 18:20:18 +00:00
|
|
|
if (QSslCertificatePrivate::isBlacklisted(cert)) {
|
|
|
|
QSslError error(QSslError::CertificateBlacklisted, cert);
|
|
|
|
errors << error;
|
|
|
|
emit q->peerVerifyError(error);
|
|
|
|
if (q->state() != QAbstractSocket::ConnectedState)
|
|
|
|
return false;
|
|
|
|
}
|
2018-12-13 14:39:26 +00:00
|
|
|
}
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-12 11:12:59 +00:00
|
|
|
const bool doVerifyPeer = configuration.peerVerifyMode() == QSslSocket::VerifyPeer
|
|
|
|
|| (configuration.peerVerifyMode() == QSslSocket::AutoVerifyPeer
|
2021-03-04 18:20:18 +00:00
|
|
|
&& mode == QSslSocket::SslClientMode);
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2018-10-25 08:44:16 +00:00
|
|
|
#if QT_CONFIG(ocsp)
|
2021-03-04 18:20:18 +00:00
|
|
|
// For now it's always QSslSocket::SslClientMode - initSslContext() will bail out early,
|
|
|
|
// if it's enabled in QSslSocket::SslServerMode. This can change.
|
2021-03-12 11:12:59 +00:00
|
|
|
if (!configuration.peerCertificate().isNull() && configuration.ocspStaplingEnabled() && doVerifyPeer) {
|
2021-03-04 18:20:18 +00:00
|
|
|
if (!checkOcspStatus()) {
|
|
|
|
if (ocspErrors.isEmpty()) {
|
|
|
|
{
|
|
|
|
const ScopedBool bg(inSetAndEmitError, true);
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
setErrorAndEmit(d, QAbstractSocket::SslHandshakeFailedError, ocspErrorDescription);
|
2021-03-04 18:20:18 +00:00
|
|
|
}
|
|
|
|
q->abort();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const QSslError &error : ocspErrors) {
|
|
|
|
errors << error;
|
|
|
|
emit q->peerVerifyError(error);
|
|
|
|
if (q->state() != QAbstractSocket::ConnectedState)
|
|
|
|
return false;
|
|
|
|
}
|
2018-10-25 08:44:16 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-04 18:20:18 +00:00
|
|
|
#endif // ocsp
|
2018-11-13 14:25:25 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// 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
|
|
|
|
// same way.
|
2021-03-12 11:12:59 +00:00
|
|
|
if (!configuration.peerCertificate().isNull()) {
|
2021-03-04 18:20:18 +00:00
|
|
|
// but only if we're a client connecting to a server
|
|
|
|
// if we're the server, don't check CN
|
|
|
|
const auto verificationPeerName = d->verificationName();
|
|
|
|
if (mode == QSslSocket::SslClientMode) {
|
|
|
|
QString peerName = (verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName);
|
|
|
|
|
2021-03-12 11:12:59 +00:00
|
|
|
if (!isMatchingHostname(configuration.peerCertificate(), peerName)) {
|
2021-03-04 18:20:18 +00:00
|
|
|
// No matches in common names or alternate names.
|
2021-03-12 11:12:59 +00:00
|
|
|
QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate());
|
2021-03-04 18:20:18 +00:00
|
|
|
errors << error;
|
|
|
|
emit q->peerVerifyError(error);
|
|
|
|
if (q->state() != QAbstractSocket::ConnectedState)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// No peer certificate presented. Report as error if the socket
|
|
|
|
// expected one.
|
|
|
|
if (doVerifyPeer) {
|
|
|
|
QSslError error(QSslError::NoPeerCertificate);
|
|
|
|
errors << error;
|
|
|
|
emit q->peerVerifyError(error);
|
|
|
|
if (q->state() != QAbstractSocket::ConnectedState)
|
|
|
|
return false;
|
|
|
|
}
|
2018-11-13 14:25:25 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// Translate errors from the error list into QSslErrors.
|
|
|
|
errors.reserve(errors.size() + errorList.size());
|
|
|
|
for (const auto &error : qAsConst(errorList))
|
2021-03-12 11:12:59 +00:00
|
|
|
errors << X509CertificateOpenSSL::openSSLErrorToQSslError(error.code, peerCertificateChain.value(error.depth));
|
2021-03-04 18:20:18 +00:00
|
|
|
|
|
|
|
if (!errors.isEmpty()) {
|
|
|
|
sslErrors = errors;
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
const bool fetchEnabled = QSslSocketPrivate::rootCertOnDemandLoadingSupported()
|
|
|
|
&& d->isRootsOnDemandAllowed();
|
|
|
|
// !fetchEnabled is a special case scenario, when we potentially have a missing
|
|
|
|
// intermediate certificate and a recoverable chain, but on demand cert loading
|
|
|
|
// was disabled by setCaCertificates call. For this scenario we check if "Authority
|
|
|
|
// Information Access" is present - wincrypt can deal with such certificates.
|
|
|
|
QSslCertificate certToFetch;
|
|
|
|
if (doVerifyPeer && !d->verifyErrorsHaveBeenIgnored())
|
|
|
|
certToFetch = findCertificateToFetch(sslErrors, !fetchEnabled);
|
|
|
|
|
|
|
|
//Skip this if not using system CAs, or if the SSL errors are configured in advance to be ignorable
|
|
|
|
if (!certToFetch.isNull()) {
|
|
|
|
fetchAuthorityInformation = !fetchEnabled;
|
|
|
|
//Windows desktop versions starting from vista ship with minimal set of roots and download on demand
|
|
|
|
//from the windows update server CA roots that are trusted by MS. It also can fetch a missing intermediate
|
|
|
|
//in case "Authority Information Access" extension is present.
|
|
|
|
//
|
|
|
|
//However, this is only transparent if using WinINET - we have to trigger it
|
|
|
|
//ourselves.
|
|
|
|
fetchCaRootForCert(certToFetch);
|
2018-11-13 14:25:25 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-03-04 18:20:18 +00:00
|
|
|
#endif // Q_OS_WIN
|
|
|
|
if (!checkSslErrors())
|
|
|
|
return false;
|
|
|
|
// A slot, attached to sslErrors signal can call
|
|
|
|
// abort/close/disconnetFromHost/etc; no need to
|
|
|
|
// continue handshake then.
|
|
|
|
if (q->state() != QAbstractSocket::ConnectedState)
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
sslErrors.clear();
|
2018-11-13 14:25:25 +00:00
|
|
|
}
|
2018-10-25 08:44:16 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
continueHandshake();
|
2011-04-27 10:05:43 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
void TlsCryptographOpenSSL::enableHandshakeContinuation()
|
2012-01-30 15:52:27 +00:00
|
|
|
{
|
2021-03-04 18:20:18 +00:00
|
|
|
handshakeInterrupted = false;
|
2012-01-30 15:52:27 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
void TlsCryptographOpenSSL::cancelCAFetch()
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2021-03-04 18:20:18 +00:00
|
|
|
fetchAuthorityInformation = false;
|
|
|
|
caToFetch = QSslCertificate{};
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
void TlsCryptographOpenSSL::continueHandshake()
|
|
|
|
{
|
|
|
|
Q_ASSERT(q);
|
|
|
|
Q_ASSERT(d);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
auto *plainSocket = d->plainTcpSocket();
|
|
|
|
Q_ASSERT(plainSocket);
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
const auto mode = d->tlsMode();
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// if we have a max read buffer size, reset the plain socket's to match
|
|
|
|
if (const auto maxSize = d->maxReadBufferSize())
|
|
|
|
plainSocket->setReadBufferSize(maxSize);
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (q_SSL_session_reused(ssl))
|
2021-03-12 11:12:59 +00:00
|
|
|
QTlsBackend::setPeerSessionShared(d, true);
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
#ifdef QT_DECRYPT_SSL_TRAFFIC
|
|
|
|
if (q_SSL_get_session(ssl)) {
|
|
|
|
size_t master_key_len = q_SSL_SESSION_get_master_key(q_SSL_get_session(ssl), nullptr, 0);
|
|
|
|
size_t client_random_len = q_SSL_get_client_random(ssl, nullptr, 0);
|
|
|
|
QByteArray masterKey(int(master_key_len), Qt::Uninitialized); // Will not overflow
|
|
|
|
QByteArray clientRandom(int(client_random_len), Qt::Uninitialized); // Will not overflow
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
q_SSL_SESSION_get_master_key(q_SSL_get_session(ssl),
|
|
|
|
reinterpret_cast<unsigned char*>(masterKey.data()),
|
|
|
|
masterKey.size());
|
|
|
|
q_SSL_get_client_random(ssl, reinterpret_cast<unsigned char *>(clientRandom.data()),
|
|
|
|
clientRandom.size());
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
QByteArray debugLineClientRandom("CLIENT_RANDOM ");
|
|
|
|
debugLineClientRandom.append(clientRandom.toHex().toUpper());
|
|
|
|
debugLineClientRandom.append(" ");
|
|
|
|
debugLineClientRandom.append(masterKey.toHex().toUpper());
|
|
|
|
debugLineClientRandom.append("\n");
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2022-04-12 13:03:54 +00:00
|
|
|
QString sslKeyFile = QDir::tempPath() + "/qt-ssl-keys"_L1;
|
2021-03-04 18:20:18 +00:00
|
|
|
QFile file(sslKeyFile);
|
|
|
|
if (!file.open(QIODevice::Append))
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
qCWarning(lcTlsBackend) << "could not open file" << sslKeyFile << "for appending";
|
2021-03-04 18:20:18 +00:00
|
|
|
if (!file.write(debugLineClientRandom))
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
qCWarning(lcTlsBackend) << "could not write to file" << sslKeyFile;
|
2021-03-04 18:20:18 +00:00
|
|
|
file.close();
|
|
|
|
} else {
|
|
|
|
qCWarning(lcTlsBackend, "could not decrypt SSL traffic");
|
|
|
|
}
|
|
|
|
#endif // QT_DECRYPT_SSL_TRAFFIC
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-12 11:12:59 +00:00
|
|
|
const auto &configuration = q->sslConfiguration();
|
2021-03-04 18:20:18 +00:00
|
|
|
// Cache this SSL session inside the QSslContext
|
2021-03-12 11:12:59 +00:00
|
|
|
if (!(configuration.testSslOption(QSsl::SslOptionDisableSessionSharing))) {
|
2021-03-04 18:20:18 +00:00
|
|
|
if (!sslContextPointer->cacheSession(ssl)) {
|
2021-07-12 09:28:19 +00:00
|
|
|
sslContextPointer.reset(); // we could not cache the session
|
2021-03-04 18:20:18 +00:00
|
|
|
} else {
|
|
|
|
// Cache the session for permanent usage as well
|
2021-03-12 11:12:59 +00:00
|
|
|
if (!(configuration.testSslOption(QSsl::SslOptionDisableSessionPersistence))) {
|
2021-03-04 18:20:18 +00:00
|
|
|
if (!sslContextPointer->sessionASN1().isEmpty())
|
2021-03-12 11:12:59 +00:00
|
|
|
QTlsBackend::setSessionAsn1(d, sslContextPointer->sessionASN1());
|
|
|
|
QTlsBackend::setSessionLifetimeHint(d, sslContextPointer->sessionTicketLifeTimeHint());
|
2021-03-04 18:20:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
#if !defined(OPENSSL_NO_NEXTPROTONEG)
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-12 11:12:59 +00:00
|
|
|
QTlsBackend::setAlpnStatus(d, sslContextPointer->npnContext().status);
|
2021-03-04 18:20:18 +00:00
|
|
|
if (sslContextPointer->npnContext().status == QSslConfiguration::NextProtocolNegotiationUnsupported) {
|
|
|
|
// we could not agree -> be conservative and use HTTP/1.1
|
2021-03-12 11:12:59 +00:00
|
|
|
// T.P.: I have to admit, this is a really strange notion of 'conservative',
|
|
|
|
// given the protocol-neutral nature of ALPN/NPN.
|
|
|
|
QTlsBackend::setNegotiatedProtocol(d, QByteArrayLiteral("http/1.1"));
|
2021-03-04 18:20:18 +00:00
|
|
|
} else {
|
|
|
|
const unsigned char *proto = nullptr;
|
|
|
|
unsigned int proto_len = 0;
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
q_SSL_get0_alpn_selected(ssl, &proto, &proto_len);
|
|
|
|
if (proto_len && mode == QSslSocket::SslClientMode) {
|
|
|
|
// Client does not have a callback that sets it ...
|
2021-03-12 11:12:59 +00:00
|
|
|
QTlsBackend::setAlpnStatus(d, QSslConfiguration::NextProtocolNegotiationNegotiated);
|
2021-03-04 18:20:18 +00:00
|
|
|
}
|
2014-03-10 16:39:46 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (!proto_len) { // Test if NPN was more lucky ...
|
|
|
|
q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len);
|
|
|
|
}
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (proto_len)
|
2021-03-12 11:12:59 +00:00
|
|
|
QTlsBackend::setNegotiatedProtocol(d, QByteArray(reinterpret_cast<const char *>(proto), proto_len));
|
2021-03-04 18:20:18 +00:00
|
|
|
else
|
2021-03-12 11:12:59 +00:00
|
|
|
QTlsBackend::setNegotiatedProtocol(d,{});
|
2021-03-04 18:20:18 +00:00
|
|
|
}
|
|
|
|
#endif // !defined(OPENSSL_NO_NEXTPROTONEG)
|
2014-03-10 16:39:46 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (mode == QSslSocket::SslClientMode) {
|
|
|
|
EVP_PKEY *key;
|
|
|
|
if (q_SSL_get_server_tmp_key(ssl, &key))
|
2021-03-12 11:12:59 +00:00
|
|
|
QTlsBackend::setEphemeralKey(d, QSslKey(key, QSsl::PublicKey));
|
2021-03-04 18:20:18 +00:00
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
d->setEncrypted(true);
|
|
|
|
emit q->encrypted();
|
|
|
|
if (d->isAutoStartingHandshake() && d->isPendingClose()) {
|
|
|
|
d->setPendingClose(false);
|
|
|
|
q->disconnectFromHost();
|
2018-02-19 12:46:21 +00:00
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
void TlsCryptographOpenSSL::transmit()
|
2014-09-03 09:12:12 +00:00
|
|
|
{
|
2021-03-04 18:20:18 +00:00
|
|
|
Q_ASSERT(q);
|
|
|
|
Q_ASSERT(d);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2018-05-08 10:35:46 +00:00
|
|
|
using ScopedBool = QScopedValueRollback<bool>;
|
|
|
|
|
|
|
|
if (inSetAndEmitError)
|
|
|
|
return;
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
// If we don't have any SSL context, don't bother transmitting.
|
|
|
|
if (!ssl)
|
|
|
|
return;
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
auto &writeBuffer = d->tlsWriteBuffer();
|
|
|
|
auto &buffer = d->tlsBuffer();
|
|
|
|
auto *plainSocket = d->plainTcpSocket();
|
|
|
|
Q_ASSERT(plainSocket);
|
|
|
|
bool &emittedBytesWritten = d->tlsEmittedBytesWritten();
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
bool transmitting;
|
|
|
|
do {
|
|
|
|
transmitting = false;
|
|
|
|
|
|
|
|
// If the connection is secure, we can transfer data from the write
|
|
|
|
// buffer (in plain text) to the write BIO through SSL_write.
|
2021-03-04 18:20:18 +00:00
|
|
|
if (q->isEncrypted() && !writeBuffer.isEmpty()) {
|
2011-04-27 10:05:43 +00:00
|
|
|
qint64 totalBytesWritten = 0;
|
|
|
|
int nextDataBlockSize;
|
|
|
|
while ((nextDataBlockSize = writeBuffer.nextDataBlockSize()) > 0) {
|
|
|
|
int writtenBytes = q_SSL_write(ssl, writeBuffer.readPointer(), nextDataBlockSize);
|
|
|
|
if (writtenBytes <= 0) {
|
2013-07-25 17:22:01 +00:00
|
|
|
int error = q_SSL_get_error(ssl, writtenBytes);
|
|
|
|
//write can result in a want_write_error - not an error - continue transmitting
|
|
|
|
if (error == SSL_ERROR_WANT_WRITE) {
|
|
|
|
transmitting = true;
|
|
|
|
break;
|
|
|
|
} else if (error == SSL_ERROR_WANT_READ) {
|
|
|
|
//write can result in a want_read error, possibly due to renegotiation - not an error - stop transmitting
|
|
|
|
transmitting = false;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
// ### Better error handling.
|
2018-05-08 10:35:46 +00:00
|
|
|
const ScopedBool bg(inSetAndEmitError, true);
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
setErrorAndEmit(d, QAbstractSocket::SslInternalError,
|
|
|
|
QSslSocket::tr("Unable to write data: %1").arg(
|
|
|
|
QTlsBackendOpenSSL::getErrorsFromOpenSsl()));
|
2013-07-25 17:22:01 +00:00
|
|
|
return;
|
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
#ifdef QSSLSOCKET_DEBUG
|
2021-03-04 18:20:18 +00:00
|
|
|
qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: encrypted" << writtenBytes << "bytes";
|
2011-04-27 10:05:43 +00:00
|
|
|
#endif
|
|
|
|
writeBuffer.free(writtenBytes);
|
|
|
|
totalBytesWritten += writtenBytes;
|
|
|
|
|
|
|
|
if (writtenBytes < nextDataBlockSize) {
|
|
|
|
// break out of the writing loop and try again after we had read
|
|
|
|
transmitting = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (totalBytesWritten > 0) {
|
|
|
|
// Don't emit bytesWritten() recursively.
|
|
|
|
if (!emittedBytesWritten) {
|
|
|
|
emittedBytesWritten = true;
|
|
|
|
emit q->bytesWritten(totalBytesWritten);
|
|
|
|
emittedBytesWritten = false;
|
|
|
|
}
|
2015-09-14 12:53:13 +00:00
|
|
|
emit q->channelBytesWritten(0, totalBytesWritten);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if we've got any data to be written to the socket.
|
|
|
|
QVarLengthArray<char, 4096> data;
|
|
|
|
int pendingBytes;
|
2018-09-27 12:20:46 +00:00
|
|
|
while (plainSocket->isValid() && (pendingBytes = q_BIO_pending(writeBio)) > 0
|
|
|
|
&& plainSocket->openMode() != QIODevice::NotOpen) {
|
2011-04-27 10:05:43 +00:00
|
|
|
// Read encrypted data from the write BIO into a buffer.
|
|
|
|
data.resize(pendingBytes);
|
|
|
|
int encryptedBytesRead = q_BIO_read(writeBio, data.data(), pendingBytes);
|
|
|
|
|
|
|
|
// Write encrypted data from the buffer to the socket.
|
2011-12-19 15:10:45 +00:00
|
|
|
qint64 actualWritten = plainSocket->write(data.constData(), encryptedBytesRead);
|
2011-04-27 10:05:43 +00:00
|
|
|
#ifdef QSSLSOCKET_DEBUG
|
2021-03-04 18:20:18 +00:00
|
|
|
qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: wrote" << encryptedBytesRead
|
|
|
|
<< "encrypted bytes to the socket" << actualWritten << "actual.";
|
2011-04-27 10:05:43 +00:00
|
|
|
#endif
|
2011-12-19 15:10:45 +00:00
|
|
|
if (actualWritten < 0) {
|
|
|
|
//plain socket write fails if it was in the pending close state.
|
2018-05-08 10:35:46 +00:00
|
|
|
const ScopedBool bg(inSetAndEmitError, true);
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
setErrorAndEmit(d, plainSocket->error(), plainSocket->errorString());
|
2011-12-19 15:10:45 +00:00
|
|
|
return;
|
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
transmitting = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if we've got any data to be read from the socket.
|
2021-03-04 18:20:18 +00:00
|
|
|
if (!q->isEncrypted() || !d->maxReadBufferSize() || buffer.size() < d->maxReadBufferSize())
|
2011-04-27 10:05:43 +00:00
|
|
|
while ((pendingBytes = plainSocket->bytesAvailable()) > 0) {
|
|
|
|
// Read encrypted data from the socket into a buffer.
|
|
|
|
data.resize(pendingBytes);
|
|
|
|
// just peek() here because q_BIO_write could write less data than expected
|
|
|
|
int encryptedBytesRead = plainSocket->peek(data.data(), pendingBytes);
|
2012-05-30 15:03:57 +00:00
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
#ifdef QSSLSOCKET_DEBUG
|
2021-03-04 18:20:18 +00:00
|
|
|
qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: read" << encryptedBytesRead << "encrypted bytes from the socket";
|
2011-04-27 10:05:43 +00:00
|
|
|
#endif
|
|
|
|
// Write encrypted data from the buffer into the read BIO.
|
|
|
|
int writtenToBio = q_BIO_write(readBio, data.constData(), encryptedBytesRead);
|
|
|
|
|
2017-05-08 17:07:37 +00:00
|
|
|
// Throw away the results.
|
2011-04-27 10:05:43 +00:00
|
|
|
if (writtenToBio > 0) {
|
2017-05-08 17:07:37 +00:00
|
|
|
plainSocket->skip(writtenToBio);
|
2011-04-27 10:05:43 +00:00
|
|
|
} else {
|
|
|
|
// ### Better error handling.
|
2018-05-08 10:35:46 +00:00
|
|
|
const ScopedBool bg(inSetAndEmitError, true);
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
setErrorAndEmit(d, QAbstractSocket::SslInternalError,
|
|
|
|
QSslSocket::tr("Unable to decrypt data: %1")
|
|
|
|
.arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()));
|
2011-04-27 10:05:43 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
transmitting = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the connection isn't secured yet, this is the time to retry the
|
|
|
|
// connect / accept.
|
2021-03-04 18:20:18 +00:00
|
|
|
if (!q->isEncrypted()) {
|
2011-04-27 10:05:43 +00:00
|
|
|
#ifdef QSSLSOCKET_DEBUG
|
2021-03-04 18:20:18 +00:00
|
|
|
qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: testing encryption";
|
2011-04-27 10:05:43 +00:00
|
|
|
#endif
|
|
|
|
if (startHandshake()) {
|
|
|
|
#ifdef QSSLSOCKET_DEBUG
|
2021-03-04 18:20:18 +00:00
|
|
|
qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: encryption established";
|
2011-04-27 10:05:43 +00:00
|
|
|
#endif
|
2021-03-04 18:20:18 +00:00
|
|
|
d->setEncrypted(true);
|
2011-04-27 10:05:43 +00:00
|
|
|
transmitting = true;
|
|
|
|
} else if (plainSocket->state() != QAbstractSocket::ConnectedState) {
|
|
|
|
#ifdef QSSLSOCKET_DEBUG
|
2021-03-04 18:20:18 +00:00
|
|
|
qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: connection lost";
|
2011-04-27 10:05:43 +00:00
|
|
|
#endif
|
|
|
|
break;
|
2021-03-04 18:20:18 +00:00
|
|
|
} else if (d->isPaused()) {
|
2012-01-20 12:55:15 +00:00
|
|
|
// just wait until the user continues
|
|
|
|
return;
|
2011-04-27 10:05:43 +00:00
|
|
|
} else {
|
|
|
|
#ifdef QSSLSOCKET_DEBUG
|
2021-03-04 18:20:18 +00:00
|
|
|
qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: encryption not done yet";
|
2011-04-27 10:05:43 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the request is small and the remote host closes the transmission
|
|
|
|
// after sending, there's a chance that startHandshake() will already
|
|
|
|
// have triggered a shutdown.
|
|
|
|
if (!ssl)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// We always read everything from the SSL decryption buffers, even if
|
|
|
|
// we have a readBufferMaxSize. There's no point in leaving data there
|
|
|
|
// just so that readBuffer.size() == readBufferMaxSize.
|
|
|
|
int readBytes = 0;
|
2017-07-26 10:39:11 +00:00
|
|
|
const int bytesToRead = 4096;
|
2011-04-27 10:05:43 +00:00
|
|
|
do {
|
2021-03-04 18:20:18 +00:00
|
|
|
if (q->readChannelCount() == 0) {
|
2018-09-27 12:20:46 +00:00
|
|
|
// The read buffer is deallocated, don't try resize or write to it.
|
|
|
|
break;
|
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
// Don't use SSL_pending(). It's very unreliable.
|
2021-11-29 15:31:21 +00:00
|
|
|
inSslRead = true;
|
2017-07-26 10:39:11 +00:00
|
|
|
readBytes = q_SSL_read(ssl, buffer.reserve(bytesToRead), bytesToRead);
|
2021-11-29 15:31:21 +00:00
|
|
|
inSslRead = false;
|
|
|
|
if (renegotiated) {
|
|
|
|
renegotiated = false;
|
|
|
|
X509 *x509 = q_SSL_get_peer_certificate(ssl);
|
|
|
|
const auto peerCertificate =
|
|
|
|
QTlsPrivate::X509CertificateOpenSSL::certificateFromX509(x509);
|
|
|
|
// Fail the renegotiate if the certificate has changed, else: continue.
|
|
|
|
if (peerCertificate != q->peerCertificate()) {
|
|
|
|
const ScopedBool bg(inSetAndEmitError, true);
|
|
|
|
setErrorAndEmit(
|
|
|
|
d, QAbstractSocket::RemoteHostClosedError,
|
|
|
|
QSslSocket::tr(
|
|
|
|
"TLS certificate unexpectedly changed during renegotiation!"));
|
|
|
|
q->abort();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2017-07-26 10:39:11 +00:00
|
|
|
if (readBytes > 0) {
|
2011-04-27 10:05:43 +00:00
|
|
|
#ifdef QSSLSOCKET_DEBUG
|
2021-03-04 18:20:18 +00:00
|
|
|
qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: decrypted" << readBytes << "bytes";
|
2011-04-27 10:05:43 +00:00
|
|
|
#endif
|
2017-07-26 10:39:11 +00:00
|
|
|
buffer.chop(bytesToRead - readBytes);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (bool *readyReadEmittedPointer = d->readyReadPointer())
|
2011-04-27 10:05:43 +00:00
|
|
|
*readyReadEmittedPointer = true;
|
|
|
|
emit q->readyRead();
|
2015-09-14 12:53:13 +00:00
|
|
|
emit q->channelReadyRead(0);
|
2011-04-27 10:05:43 +00:00
|
|
|
transmitting = true;
|
|
|
|
continue;
|
|
|
|
}
|
2017-07-26 10:39:11 +00:00
|
|
|
buffer.chop(bytesToRead);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Error.
|
|
|
|
switch (q_SSL_get_error(ssl, readBytes)) {
|
|
|
|
case SSL_ERROR_WANT_READ:
|
|
|
|
case SSL_ERROR_WANT_WRITE:
|
|
|
|
// Out of data.
|
|
|
|
break;
|
|
|
|
case SSL_ERROR_ZERO_RETURN:
|
|
|
|
// The remote host closed the connection.
|
|
|
|
#ifdef QSSLSOCKET_DEBUG
|
2021-03-04 18:20:18 +00:00
|
|
|
qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: remote disconnect";
|
2011-04-27 10:05:43 +00:00
|
|
|
#endif
|
2013-04-04 09:30:43 +00:00
|
|
|
shutdown = true; // the other side shut down, make sure we do not send shutdown ourselves
|
2018-05-08 10:35:46 +00:00
|
|
|
{
|
|
|
|
const ScopedBool bg(inSetAndEmitError, true);
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
setErrorAndEmit(d, QAbstractSocket::RemoteHostClosedError,
|
|
|
|
QSslSocket::tr("The TLS/SSL connection has been closed"));
|
2018-05-08 10:35:46 +00:00
|
|
|
}
|
2013-04-04 09:30:43 +00:00
|
|
|
return;
|
2011-04-27 10:05:43 +00:00
|
|
|
case SSL_ERROR_SYSCALL: // some IO error
|
|
|
|
case SSL_ERROR_SSL: // error in the SSL library
|
|
|
|
// we do not know exactly what the error is, nor whether we can recover from it,
|
|
|
|
// so just return to prevent an endless loop in the outer "while" statement
|
2020-04-13 18:31:34 +00:00
|
|
|
systemOrSslErrorDetected = true;
|
2018-05-08 10:35:46 +00:00
|
|
|
{
|
|
|
|
const ScopedBool bg(inSetAndEmitError, true);
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
setErrorAndEmit(d, QAbstractSocket::SslInternalError,
|
|
|
|
QSslSocket::tr("Error while reading: %1")
|
|
|
|
.arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()));
|
2018-05-08 10:35:46 +00:00
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
return;
|
|
|
|
default:
|
|
|
|
// SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: can only happen with a
|
|
|
|
// BIO_s_connect() or BIO_s_accept(), which we do not call.
|
|
|
|
// SSL_ERROR_WANT_X509_LOOKUP: can only happen with a
|
|
|
|
// SSL_CTX_set_client_cert_cb(), which we do not call.
|
|
|
|
// So this default case should never be triggered.
|
2018-05-08 10:35:46 +00:00
|
|
|
{
|
|
|
|
const ScopedBool bg(inSetAndEmitError, true);
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
setErrorAndEmit(d, QAbstractSocket::SslInternalError,
|
|
|
|
QSslSocket::tr("Error while reading: %1")
|
|
|
|
.arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()));
|
2018-05-08 10:35:46 +00:00
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (ssl && readBytes > 0);
|
2012-12-21 13:02:38 +00:00
|
|
|
} while (ssl && transmitting);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
void TlsCryptographOpenSSL::disconnectFromHost()
|
2018-06-27 09:38:53 +00:00
|
|
|
{
|
2021-03-04 18:20:18 +00:00
|
|
|
if (ssl) {
|
|
|
|
if (!shutdown && !q_SSL_in_init(ssl) && !systemOrSslErrorDetected) {
|
|
|
|
if (q_SSL_shutdown(ssl) != 1) {
|
|
|
|
// Some error may be queued, clear it.
|
|
|
|
QTlsBackendOpenSSL::clearErrorQueue();
|
|
|
|
}
|
|
|
|
shutdown = true;
|
|
|
|
transmit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Q_ASSERT(d);
|
|
|
|
auto *plainSocket = d->plainTcpSocket();
|
|
|
|
Q_ASSERT(plainSocket);
|
|
|
|
plainSocket->disconnectFromHost();
|
2018-06-27 09:38:53 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
void TlsCryptographOpenSSL::disconnected()
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2021-03-04 18:20:18 +00:00
|
|
|
Q_ASSERT(d);
|
|
|
|
auto *plainSocket = d->plainTcpSocket();
|
|
|
|
Q_ASSERT(plainSocket);
|
2021-06-24 11:59:09 +00:00
|
|
|
d->setEncrypted(false);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (plainSocket->bytesAvailable() <= 0) {
|
|
|
|
destroySslContext();
|
|
|
|
} else {
|
|
|
|
// Move all bytes into the plain buffer.
|
|
|
|
const qint64 tmpReadBufferMaxSize = d->maxReadBufferSize();
|
|
|
|
// Reset temporarily, so the plain socket buffer is completely drained:
|
|
|
|
d->setMaxReadBufferSize(0);
|
|
|
|
transmit();
|
|
|
|
d->setMaxReadBufferSize(tmpReadBufferMaxSize);
|
|
|
|
}
|
|
|
|
//if there is still buffered data in the plain socket, don't destroy the ssl context yet.
|
|
|
|
//it will be destroyed when the socket is deleted.
|
|
|
|
}
|
2018-05-08 10:35:46 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
QSslCipher TlsCryptographOpenSSL::sessionCipher() const
|
|
|
|
{
|
|
|
|
if (!ssl)
|
|
|
|
return {};
|
2018-05-08 10:35:46 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
const SSL_CIPHER *sessionCipher = q_SSL_get_current_cipher(ssl);
|
|
|
|
return sessionCipher ? QTlsBackendOpenSSL::qt_OpenSSL_cipher_to_QSslCipher(sessionCipher) : QSslCipher{};
|
|
|
|
}
|
2018-05-08 10:35:46 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
QSsl::SslProtocol TlsCryptographOpenSSL::sessionProtocol() const
|
|
|
|
{
|
|
|
|
if (!ssl)
|
|
|
|
return QSsl::UnknownProtocol;
|
2019-10-30 10:49:07 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
const int ver = q_SSL_version(ssl);
|
|
|
|
switch (ver) {
|
2021-06-18 10:46:33 +00:00
|
|
|
QT_WARNING_PUSH
|
|
|
|
QT_WARNING_DISABLE_DEPRECATED
|
2021-03-04 18:20:18 +00:00
|
|
|
case 0x301:
|
|
|
|
return QSsl::TlsV1_0;
|
|
|
|
case 0x302:
|
|
|
|
return QSsl::TlsV1_1;
|
2021-06-18 10:46:33 +00:00
|
|
|
QT_WARNING_POP
|
2021-03-04 18:20:18 +00:00
|
|
|
case 0x303:
|
|
|
|
return QSsl::TlsV1_2;
|
|
|
|
case 0x304:
|
|
|
|
return QSsl::TlsV1_3;
|
|
|
|
}
|
2019-10-30 10:49:07 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
return QSsl::UnknownProtocol;
|
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
QList<QOcspResponse> TlsCryptographOpenSSL::ocsps() const
|
|
|
|
{
|
|
|
|
return ocspResponses;
|
|
|
|
}
|
2019-10-30 10:49:07 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
bool TlsCryptographOpenSSL::checkSslErrors()
|
|
|
|
{
|
|
|
|
Q_ASSERT(q);
|
|
|
|
Q_ASSERT(d);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (sslErrors.isEmpty())
|
|
|
|
return true;
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
emit q->sslErrors(sslErrors);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-12 11:12:59 +00:00
|
|
|
const auto vfyMode = q->peerVerifyMode();
|
2021-03-04 18:20:18 +00:00
|
|
|
const auto mode = d->tlsMode();
|
|
|
|
|
2021-03-12 11:12:59 +00:00
|
|
|
bool doVerifyPeer = vfyMode == QSslSocket::VerifyPeer || (vfyMode == QSslSocket::AutoVerifyPeer
|
|
|
|
&& mode == QSslSocket::SslClientMode);
|
2021-03-04 18:20:18 +00:00
|
|
|
bool doEmitSslError = !d->verifyErrorsHaveBeenIgnored();
|
|
|
|
// check whether we need to emit an SSL handshake error
|
|
|
|
if (doVerifyPeer && doEmitSslError) {
|
|
|
|
if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) {
|
|
|
|
QSslSocketPrivate::pauseSocketNotifiers(q);
|
|
|
|
d->setPaused(true);
|
|
|
|
} else {
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
setErrorAndEmit(d, QAbstractSocket::SslHandshakeFailedError, sslErrors.constFirst().errorString());
|
2021-03-04 18:20:18 +00:00
|
|
|
auto *plainSocket = d->plainTcpSocket();
|
|
|
|
Q_ASSERT(plainSocket);
|
|
|
|
plainSocket->disconnectFromHost();
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2021-03-04 18:20:18 +00:00
|
|
|
return true;
|
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
int TlsCryptographOpenSSL::handleNewSessionTicket(SSL *connection)
|
|
|
|
{
|
|
|
|
// If we return 1, this means we own the session, but we don't.
|
|
|
|
// 0 would tell OpenSSL to deref (but they still have it in the
|
|
|
|
// internal cache).
|
|
|
|
Q_ASSERT(connection);
|
2021-03-12 11:12:59 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
Q_ASSERT(q);
|
2021-03-12 11:12:59 +00:00
|
|
|
Q_ASSERT(d);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (q->sslConfiguration().testSslOption(QSsl::SslOptionDisableSessionPersistence)) {
|
|
|
|
// We silently ignore, do nothing, remove from cache.
|
|
|
|
return 0;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
SSL_SESSION *currentSession = q_SSL_get_session(connection);
|
|
|
|
if (!currentSession) {
|
|
|
|
qCWarning(lcTlsBackend,
|
|
|
|
"New session ticket callback, the session is invalid (nullptr)");
|
|
|
|
return 0;
|
|
|
|
}
|
2018-10-25 08:44:16 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (q_SSL_version(connection) < 0x304) {
|
|
|
|
// We only rely on this mechanics with TLS >= 1.3
|
|
|
|
return 0;
|
2018-10-25 08:44:16 +00:00
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
#ifdef TLS1_3_VERSION
|
|
|
|
if (!q_SSL_SESSION_is_resumable(currentSession)) {
|
|
|
|
qCDebug(lcTlsBackend, "New session ticket, but the session is non-resumable");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif // TLS1_3_VERSION
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
const int sessionSize = q_i2d_SSL_SESSION(currentSession, nullptr);
|
|
|
|
if (sessionSize <= 0) {
|
|
|
|
qCWarning(lcTlsBackend, "could not store persistent version of SSL session");
|
|
|
|
return 0;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// We have somewhat perverse naming, it's not a ticket, it's a session.
|
|
|
|
QByteArray sessionTicket(sessionSize, 0);
|
|
|
|
auto data = reinterpret_cast<unsigned char *>(sessionTicket.data());
|
|
|
|
if (!q_i2d_SSL_SESSION(currentSession, &data)) {
|
|
|
|
qCWarning(lcTlsBackend, "could not store persistent version of SSL session");
|
|
|
|
return 0;
|
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-12 11:12:59 +00:00
|
|
|
QTlsBackend::setSessionAsn1(d, sessionTicket);
|
|
|
|
QTlsBackend::setSessionLifetimeHint(d, q_SSL_SESSION_get_ticket_lifetime_hint(currentSession));
|
2012-03-23 11:01:42 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
emit q->newSessionTicketReceived();
|
|
|
|
return 0;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
void TlsCryptographOpenSSL::alertMessageSent(int value)
|
2015-02-03 14:32:53 +00:00
|
|
|
{
|
2021-03-04 18:20:18 +00:00
|
|
|
Q_ASSERT(q);
|
|
|
|
Q_ASSERT(d);
|
|
|
|
|
|
|
|
const auto level = tlsAlertLevel(value);
|
|
|
|
if (level == QSsl::AlertLevel::Fatal && !q->isEncrypted()) {
|
|
|
|
// Note, this logic is handshake-time only:
|
|
|
|
pendingFatalAlert = true;
|
2015-02-03 14:32:53 +00:00
|
|
|
}
|
2021-03-04 18:20:18 +00:00
|
|
|
|
|
|
|
emit q->alertSent(level, tlsAlertType(value), tlsAlertDescription(value));
|
|
|
|
|
2015-02-03 14:32:53 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
void TlsCryptographOpenSSL::alertMessageReceived(int value)
|
2020-01-27 13:11:08 +00:00
|
|
|
{
|
2021-03-04 18:20:18 +00:00
|
|
|
Q_ASSERT(q);
|
2020-01-27 13:11:08 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
emit q->alertReceived(tlsAlertLevel(value), tlsAlertType(value), tlsAlertDescription(value));
|
|
|
|
}
|
2020-01-27 13:11:08 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
int TlsCryptographOpenSSL::emitErrorFromCallback(X509_STORE_CTX *ctx)
|
|
|
|
{
|
|
|
|
// Returns 0 to abort verification, 1 to continue despite error (as
|
|
|
|
// OpenSSL expects from the verification callback).
|
|
|
|
Q_ASSERT(q);
|
|
|
|
Q_ASSERT(ctx);
|
2020-01-27 13:11:08 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
using ScopedBool = QScopedValueRollback<bool>;
|
|
|
|
// While we are not setting, we are emitting and in general -
|
|
|
|
// we want to prevent accidental recursive startHandshake()
|
|
|
|
// calls:
|
|
|
|
const ScopedBool bg(inSetAndEmitError, true);
|
2020-01-27 13:11:08 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
X509 *x509 = q_X509_STORE_CTX_get_current_cert(ctx);
|
|
|
|
if (!x509) {
|
|
|
|
qCWarning(lcTlsBackend, "Could not obtain the certificate (that failed to verify)");
|
2020-01-27 13:11:08 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
const QSslCertificate certificate = QTlsPrivate::X509CertificateOpenSSL::certificateFromX509(x509);
|
|
|
|
const auto errorAndDepth = QTlsPrivate::X509CertificateOpenSSL::errorEntryFromStoreContext(ctx);
|
|
|
|
const QSslError tlsError = QTlsPrivate::X509CertificateOpenSSL::openSSLErrorToQSslError(errorAndDepth.code, certificate);
|
2020-01-27 13:11:08 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
errorsReportedFromCallback = true;
|
|
|
|
handshakeInterrupted = true;
|
|
|
|
emit q->handshakeInterruptedOnError(tlsError);
|
2020-01-27 13:11:08 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// Conveniently so, we also can access 'lastErrors' external data set
|
|
|
|
// in startHandshake, we store it for the case an application later
|
|
|
|
// wants to check errors (ignored or not):
|
|
|
|
const auto offset = QTlsBackendOpenSSL::s_indexForSSLExtraData
|
|
|
|
+ TlsCryptographOpenSSL::errorOffsetInExData;
|
|
|
|
if (auto errorList = static_cast<QList<QSslErrorEntry> *>(q_SSL_get_ex_data(ssl, offset)))
|
|
|
|
errorList->append(errorAndDepth);
|
2020-01-27 13:11:08 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// An application is expected to ignore this error (by calling ignoreSslErrors)
|
|
|
|
// in its directly connected slot:
|
|
|
|
return !handshakeInterrupted;
|
2020-01-27 13:11:08 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
void TlsCryptographOpenSSL::trySendFatalAlert()
|
2012-03-23 11:01:42 +00:00
|
|
|
{
|
2021-03-04 18:20:18 +00:00
|
|
|
Q_ASSERT(pendingFatalAlert);
|
|
|
|
Q_ASSERT(d);
|
2012-03-23 11:01:42 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
auto *plainSocket = d->plainTcpSocket();
|
2012-03-23 11:01:42 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
pendingFatalAlert = false;
|
|
|
|
QVarLengthArray<char, 4096> data;
|
|
|
|
int pendingBytes = 0;
|
|
|
|
while (plainSocket->isValid() && (pendingBytes = q_BIO_pending(writeBio)) > 0
|
|
|
|
&& plainSocket->openMode() != QIODevice::NotOpen) {
|
|
|
|
// Read encrypted data from the write BIO into a buffer.
|
|
|
|
data.resize(pendingBytes);
|
|
|
|
const int bioReadBytes = q_BIO_read(writeBio, data.data(), pendingBytes);
|
|
|
|
|
|
|
|
// Write encrypted data from the buffer to the socket.
|
|
|
|
qint64 actualWritten = plainSocket->write(data.constData(), bioReadBytes);
|
|
|
|
if (actualWritten < 0)
|
|
|
|
return;
|
|
|
|
plainSocket->flush();
|
2012-03-23 11:01:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
bool TlsCryptographOpenSSL::initSslContext()
|
2014-10-15 13:13:47 +00:00
|
|
|
{
|
2021-03-04 18:20:18 +00:00
|
|
|
Q_ASSERT(q);
|
|
|
|
Q_ASSERT(d);
|
2014-10-15 13:13:47 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// If no external context was set (e.g. by QHttpNetworkConnection) we will
|
2021-03-12 11:12:59 +00:00
|
|
|
// create a new one.
|
2021-03-04 18:20:18 +00:00
|
|
|
const auto mode = d->tlsMode();
|
2021-03-12 11:12:59 +00:00
|
|
|
const auto configuration = q->sslConfiguration();
|
|
|
|
if (!sslContextPointer)
|
|
|
|
sslContextPointer = QSslContext::sharedFromConfiguration(mode, configuration, d->isRootsOnDemandAllowed());
|
2014-10-15 13:13:47 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (sslContextPointer->error() != QSslError::NoError) {
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
setErrorAndEmit(d, QAbstractSocket::SslInvalidUserDataError, sslContextPointer->errorString());
|
2021-07-12 09:28:19 +00:00
|
|
|
sslContextPointer.reset();
|
2021-03-04 18:20:18 +00:00
|
|
|
return false;
|
|
|
|
}
|
2014-10-15 13:13:47 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// Create and initialize SSL session
|
|
|
|
if (!(ssl = sslContextPointer->createSsl())) {
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
setErrorAndEmit(d, QAbstractSocket::SslInternalError,
|
|
|
|
QSslSocket::tr("Error creating SSL session, %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()));
|
2021-03-04 18:20:18 +00:00
|
|
|
return false;
|
|
|
|
}
|
2016-03-23 11:27:11 +00:00
|
|
|
|
2021-03-12 11:12:59 +00:00
|
|
|
if (configuration.protocol() != QSsl::UnknownProtocol && mode == QSslSocket::SslClientMode) {
|
2021-03-04 18:20:18 +00:00
|
|
|
const auto verificationPeerName = d->verificationName();
|
|
|
|
// Set server hostname on TLS extension. RFC4366 section 3.1 requires it in ACE format.
|
|
|
|
QString tlsHostName = verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName;
|
|
|
|
if (tlsHostName.isEmpty())
|
|
|
|
tlsHostName = d->tlsHostName();
|
|
|
|
QByteArray ace = QUrl::toAce(tlsHostName);
|
|
|
|
// only send the SNI header if the URL is valid and not an IP
|
|
|
|
if (!ace.isEmpty()
|
|
|
|
&& !QHostAddress().setAddress(tlsHostName)
|
2021-03-12 11:12:59 +00:00
|
|
|
&& !(configuration.testSslOption(QSsl::SslOptionDisableServerNameIndication))) {
|
2021-03-04 18:20:18 +00:00
|
|
|
// We don't send the trailing dot from the host header if present see
|
|
|
|
// https://tools.ietf.org/html/rfc6066#section-3
|
|
|
|
if (ace.endsWith('.'))
|
|
|
|
ace.chop(1);
|
|
|
|
if (!q_SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, ace.data()))
|
|
|
|
qCWarning(lcTlsBackend, "could not set SSL_CTRL_SET_TLSEXT_HOSTNAME, Server Name Indication disabled");
|
|
|
|
}
|
|
|
|
}
|
2016-03-23 11:27:11 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// Clear the session.
|
|
|
|
errorList.clear();
|
2016-03-23 11:27:11 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// Initialize memory BIOs for encryption and decryption.
|
|
|
|
readBio = q_BIO_new(q_BIO_s_mem());
|
|
|
|
writeBio = q_BIO_new(q_BIO_s_mem());
|
|
|
|
if (!readBio || !writeBio) {
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
setErrorAndEmit(d, QAbstractSocket::SslInternalError,
|
|
|
|
QSslSocket::tr("Error creating SSL session: %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()));
|
2021-03-04 18:20:18 +00:00
|
|
|
if (readBio)
|
|
|
|
q_BIO_free(readBio);
|
|
|
|
if (writeBio)
|
|
|
|
q_BIO_free(writeBio);
|
|
|
|
return false;
|
|
|
|
}
|
2016-03-23 11:27:11 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// Assign the bios.
|
|
|
|
q_SSL_set_bio(ssl, readBio, writeBio);
|
2016-03-23 11:27:11 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (mode == QSslSocket::SslClientMode)
|
|
|
|
q_SSL_set_connect_state(ssl);
|
|
|
|
else
|
|
|
|
q_SSL_set_accept_state(ssl);
|
2014-10-15 13:13:47 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
q_SSL_set_ex_data(ssl, QTlsBackendOpenSSL::s_indexForSSLExtraData, this);
|
2012-03-23 11:01:42 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
#ifndef OPENSSL_NO_PSK
|
|
|
|
// Set the client callback for PSK
|
|
|
|
if (mode == QSslSocket::SslClientMode)
|
|
|
|
q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback);
|
|
|
|
else if (mode == QSslSocket::SslServerMode)
|
|
|
|
q_SSL_set_psk_server_callback(ssl, &q_ssl_psk_server_callback);
|
2020-05-14 14:40:08 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10101006L
|
|
|
|
// Set the client callback for TLSv1.3 PSK
|
|
|
|
if (mode == QSslSocket::SslClientMode
|
|
|
|
&& QSslSocket::sslLibraryBuildVersionNumber() >= 0x10101006L) {
|
|
|
|
q_SSL_set_psk_use_session_callback(ssl, &q_ssl_psk_use_session_callback);
|
|
|
|
}
|
|
|
|
#endif // openssl version >= 0x10101006L
|
2020-06-03 10:30:41 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
#endif // OPENSSL_NO_PSK
|
2012-03-23 11:01:42 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
#if QT_CONFIG(ocsp)
|
2021-03-12 11:12:59 +00:00
|
|
|
if (configuration.ocspStaplingEnabled()) {
|
2021-03-04 18:20:18 +00:00
|
|
|
if (mode == QSslSocket::SslServerMode) {
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
setErrorAndEmit(d, QAbstractSocket::SslInvalidUserDataError,
|
|
|
|
QSslSocket::tr("Server-side QSslSocket does not support OCSP stapling"));
|
2021-03-04 18:20:18 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (q_SSL_set_tlsext_status_type(ssl, TLSEXT_STATUSTYPE_ocsp) != 1) {
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
setErrorAndEmit(d, QAbstractSocket::SslInternalError,
|
|
|
|
QSslSocket::tr("Failed to enable OCSP stapling"));
|
2021-03-04 18:20:18 +00:00
|
|
|
return false;
|
|
|
|
}
|
2020-06-03 10:30:41 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
ocspResponseDer.clear();
|
2021-03-12 11:12:59 +00:00
|
|
|
const auto backendConfig = configuration.backendConfiguration();
|
|
|
|
auto responsePos = backendConfig.find("Qt-OCSP-response");
|
|
|
|
if (responsePos != backendConfig.end()) {
|
2021-03-04 18:20:18 +00:00
|
|
|
// This is our private, undocumented 'API' we use for the auto-testing of
|
|
|
|
// OCSP-stapling. It must be a der-encoded OCSP response, presumably set
|
|
|
|
// by tst_QOcsp.
|
|
|
|
const QVariant data(responsePos.value());
|
|
|
|
if (data.canConvert<QByteArray>())
|
|
|
|
ocspResponseDer = data.toByteArray();
|
2020-05-14 14:40:08 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (ocspResponseDer.size()) {
|
|
|
|
if (mode != QSslSocket::SslServerMode) {
|
Move plugin code from QtNetwork to qtbase/plugins
All TLS (and non-TLS) backends that QSsl classes rely
on are now in plugins/tls (as openssl, securetransport,
schannel and certonly plugins).
For now, I have to disable some tests that were using OpenSSL
calls - this to be refactored/re-thought. These include:
qsslsocket auto-test (test-case where we work with private keys),
qsslkey auto-test (similar to qsslsocket - test-case working with
keys using OpenSSL calls).
qasn1element moved to plugins too, so its auto-test have to
be re-thought.
Since now we can have more than one working TLS-backend on a given
platform, the presence of OpenSSL also means I force this backend
as active before running tests, to make sure features implemented
only in OpenSSL-backend are tested.
OCSP auto test is disabled for now, since it heavily relies on
OpenSSL symbols (to be refactored).
[ChangeLog][QtNetwork][QSslSocket] QSslSocket by default prefers 'openssl' backend
if it is available.
[ChangeLog][QtNetwork][QSslSocket] TLS-backends are not mutually exclusive anymore,
depending on a platform, more than one TLS backend can be built. E.g., configuring
Qt with -openssl does not prevent SecureTransport or Schannel plugin from being
built.
Fixes: QTBUG-91928
Change-Id: I4c05e32f10179066bee3a518bdfdd6c4b15320c3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
2021-03-25 11:41:08 +00:00
|
|
|
setErrorAndEmit(d, QAbstractSocket::SslInvalidUserDataError,
|
|
|
|
QSslSocket::tr("Client-side sockets do not send OCSP responses"));
|
2021-03-04 18:20:18 +00:00
|
|
|
return false;
|
2012-03-23 11:01:42 +00:00
|
|
|
}
|
2021-03-04 18:20:18 +00:00
|
|
|
}
|
|
|
|
#endif // ocsp
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TlsCryptographOpenSSL::destroySslContext()
|
|
|
|
{
|
|
|
|
if (ssl) {
|
|
|
|
if (!q_SSL_in_init(ssl) && !systemOrSslErrorDetected) {
|
|
|
|
// We do not send a shutdown alert here. Just mark the session as
|
|
|
|
// resumable for qhttpnetworkconnection's "optimization", otherwise
|
|
|
|
// OpenSSL won't start a session resumption.
|
|
|
|
if (q_SSL_shutdown(ssl) != 1) {
|
|
|
|
// Some error may be queued, clear it.
|
|
|
|
const auto errors = QTlsBackendOpenSSL::getErrorsFromOpenSsl();
|
|
|
|
Q_UNUSED(errors);
|
2012-03-23 11:01:42 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-04 18:20:18 +00:00
|
|
|
q_SSL_free(ssl);
|
|
|
|
ssl = nullptr;
|
2012-03-23 11:01:42 +00:00
|
|
|
}
|
2021-07-12 09:28:19 +00:00
|
|
|
sslContextPointer.reset();
|
2021-03-04 18:20:18 +00:00
|
|
|
}
|
2020-05-14 14:40:08 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
void TlsCryptographOpenSSL::storePeerCertificates()
|
|
|
|
{
|
|
|
|
Q_ASSERT(d);
|
2021-03-12 11:12:59 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// Store the peer certificate and chain. For clients, the peer certificate
|
|
|
|
// chain includes the peer certificate; for servers, it doesn't. Both the
|
|
|
|
// peer certificate and the chain may be empty if the peer didn't present
|
|
|
|
// any certificate.
|
|
|
|
X509 *x509 = q_SSL_get_peer_certificate(ssl);
|
2021-03-12 11:12:59 +00:00
|
|
|
|
|
|
|
const auto peerCertificate = QTlsPrivate::X509CertificateOpenSSL::certificateFromX509(x509);
|
|
|
|
QTlsBackend::storePeerCertificate(d, peerCertificate);
|
2021-03-04 18:20:18 +00:00
|
|
|
q_X509_free(x509);
|
2021-03-12 11:12:59 +00:00
|
|
|
auto peerCertificateChain = q->peerCertificateChain();
|
|
|
|
if (peerCertificateChain.isEmpty()) {
|
|
|
|
peerCertificateChain = QTlsPrivate::X509CertificateOpenSSL::stackOfX509ToQSslCertificates(q_SSL_get_peer_cert_chain(ssl));
|
|
|
|
if (!peerCertificate.isNull() && d->tlsMode() == QSslSocket::SslServerMode)
|
|
|
|
peerCertificateChain.prepend(peerCertificate);
|
|
|
|
QTlsBackend::storePeerCertificateChain(d, peerCertificateChain);
|
2016-03-01 20:10:04 +00:00
|
|
|
}
|
2012-03-23 11:01:42 +00:00
|
|
|
}
|
|
|
|
|
2018-10-25 08:44:16 +00:00
|
|
|
#if QT_CONFIG(ocsp)
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
bool TlsCryptographOpenSSL::checkOcspStatus()
|
2018-10-25 08:44:16 +00:00
|
|
|
{
|
|
|
|
Q_ASSERT(ssl);
|
2021-03-04 18:20:18 +00:00
|
|
|
Q_ASSERT(d);
|
|
|
|
|
2021-03-12 11:12:59 +00:00
|
|
|
const auto &configuration = q->sslConfiguration();
|
2021-03-04 18:20:18 +00:00
|
|
|
Q_ASSERT(d->tlsMode() == QSslSocket::SslClientMode); // See initSslContext() for SslServerMode
|
2021-03-12 11:12:59 +00:00
|
|
|
Q_ASSERT(configuration.peerVerifyMode() != QSslSocket::VerifyNone);
|
2018-10-25 08:44:16 +00:00
|
|
|
|
2020-07-17 13:47:33 +00:00
|
|
|
const auto clearErrorQueue = qScopeGuard([] {
|
2021-03-04 18:20:18 +00:00
|
|
|
QTlsBackendOpenSSL::logAndClearErrorQueue();
|
2020-07-17 13:47:33 +00:00
|
|
|
});
|
|
|
|
|
2019-01-25 14:11:34 +00:00
|
|
|
ocspResponses.clear();
|
2018-10-25 08:44:16 +00:00
|
|
|
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) {
|
2020-07-13 10:43:44 +00:00
|
|
|
ocspErrors.push_back(QSslError(QSslError::OcspNoResponseFound));
|
2018-10-25 08:44:16 +00:00
|
|
|
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).
|
2020-07-13 10:43:44 +00:00
|
|
|
ocspErrors.push_back(QSslError(qt_OCSP_response_status_to_SslError(ocspStatus)));
|
2018-10-25 08:44:16 +00:00
|
|
|
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.
|
|
|
|
//
|
2019-06-25 09:17:32 +00:00
|
|
|
// Note, OpenSSL prior to 1.0.2b would only use bs->certs to
|
2018-10-25 08:44:16 +00:00
|
|
|
// 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.
|
2019-06-25 09:17:32 +00:00
|
|
|
const unsigned long verificationFlags = 0;
|
|
|
|
const int success = q_OCSP_basic_verify(basicResponse, peerChain, store, verificationFlags);
|
|
|
|
if (success <= 0)
|
2020-07-13 10:43:44 +00:00
|
|
|
ocspErrors.push_back(QSslError(QSslError::OcspResponseCannotBeTrusted));
|
2018-10-25 08:44:16 +00:00
|
|
|
|
|
|
|
if (q_OCSP_resp_count(basicResponse) != 1) {
|
2020-07-13 10:43:44 +00:00
|
|
|
ocspErrors.push_back(QSslError(QSslError::OcspMalformedResponse));
|
2018-10-25 08:44:16 +00:00
|
|
|
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.
|
2019-01-25 14:11:34 +00:00
|
|
|
ocspResponses.push_back(QOcspResponse());
|
|
|
|
QOcspResponsePrivate *dResponse = ocspResponses.back().d.data();
|
2021-03-12 11:12:59 +00:00
|
|
|
dResponse->subjectCert = configuration.peerCertificate();
|
2018-10-25 08:44:16 +00:00
|
|
|
bool matchFound = false;
|
2021-03-12 11:12:59 +00:00
|
|
|
if (dResponse->subjectCert.isSelfSigned()) {
|
|
|
|
dResponse->signerCert = configuration.peerCertificate();
|
2018-10-25 08:44:16 +00:00
|
|
|
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
|
2021-03-04 18:20:18 +00:00
|
|
|
// 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) {
|
|
|
|
dResponse->signerCert = QTlsPrivate::X509CertificateOpenSSL::certificateFromX509(issuer);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
matchFound = false;
|
|
|
|
}
|
2020-04-13 18:31:34 +00:00
|
|
|
}
|
2013-04-04 09:30:43 +00:00
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (!matchFound) {
|
|
|
|
dResponse->signerCert.clear();
|
2021-03-12 11:12:59 +00:00
|
|
|
ocspErrors.push_back({QSslError::OcspResponseCertIdUnknown, configuration.peerCertificate()});
|
2015-06-19 13:35:34 +00:00
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// 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();
|
|
|
|
ocspResponses.clear();
|
|
|
|
ocspErrorDescription = QSslSocket::tr("Failed to extract 'this update time' from the SingleResponse");
|
|
|
|
return false;
|
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// 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))
|
2021-03-12 11:12:59 +00:00
|
|
|
ocspErrors.push_back({QSslError::OcspResponseExpired, configuration.peerCertificate()});
|
2014-03-10 13:00:08 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// And finally, the status:
|
|
|
|
switch (certStatus) {
|
|
|
|
case V_OCSP_CERTSTATUS_GOOD:
|
|
|
|
// This certificate was not found among the revoked ones.
|
|
|
|
dResponse->certificateStatus = QOcspCertificateStatus::Good;
|
|
|
|
break;
|
|
|
|
case V_OCSP_CERTSTATUS_REVOKED:
|
|
|
|
dResponse->certificateStatus = QOcspCertificateStatus::Revoked;
|
|
|
|
dResponse->revocationReason = qt_OCSP_revocation_reason(reason);
|
2021-03-12 11:12:59 +00:00
|
|
|
ocspErrors.push_back({QSslError::CertificateRevoked, configuration.peerCertificate()});
|
2021-03-04 18:20:18 +00:00
|
|
|
break;
|
|
|
|
case V_OCSP_CERTSTATUS_UNKNOWN:
|
|
|
|
dResponse->certificateStatus = QOcspCertificateStatus::Unknown;
|
2021-03-12 11:12:59 +00:00
|
|
|
ocspErrors.push_back({QSslError::OcspStatusUnknown, configuration.peerCertificate()});
|
2014-03-10 13:00:08 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
return !ocspErrors.size();
|
2014-03-10 13:00:08 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
#endif // QT_CONFIG(ocsp)
|
2019-09-27 11:04:54 +00:00
|
|
|
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
unsigned TlsCryptographOpenSSL::pskClientTlsCallback(const char *hint, char *identity,
|
|
|
|
unsigned max_identity_len,
|
|
|
|
unsigned char *psk, unsigned max_psk_len)
|
|
|
|
{
|
|
|
|
Q_ASSERT(q);
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
QSslPreSharedKeyAuthenticator authenticator;
|
|
|
|
// Fill in some read-only fields (for the user)
|
|
|
|
const int hintLength = hint ? int(std::strlen(hint)) : 0;
|
|
|
|
QTlsBackend::setupClientPskAuth(&authenticator, hint, hintLength, max_identity_len, max_psk_len);
|
|
|
|
// Let the client provide the remaining bits...
|
|
|
|
emit q->preSharedKeyAuthenticationRequired(&authenticator);
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// No PSK set? Return now to make the handshake fail
|
|
|
|
if (authenticator.preSharedKey().isEmpty())
|
|
|
|
return 0;
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// Copy data back into OpenSSL
|
|
|
|
const int identityLength = qMin(authenticator.identity().length(), authenticator.maximumIdentityLength());
|
|
|
|
std::memcpy(identity, authenticator.identity().constData(), identityLength);
|
|
|
|
identity[identityLength] = 0;
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
const int pskLength = qMin(authenticator.preSharedKey().length(), authenticator.maximumPreSharedKeyLength());
|
|
|
|
std::memcpy(psk, authenticator.preSharedKey().constData(), pskLength);
|
|
|
|
return pskLength;
|
|
|
|
}
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
unsigned TlsCryptographOpenSSL::pskServerTlsCallback(const char *identity, unsigned char *psk,
|
|
|
|
unsigned max_psk_len)
|
|
|
|
{
|
|
|
|
Q_ASSERT(q);
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
QSslPreSharedKeyAuthenticator authenticator;
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// Fill in some read-only fields (for the user)
|
2021-03-12 11:12:59 +00:00
|
|
|
QTlsBackend::setupServerPskAuth(&authenticator, identity, q->sslConfiguration().preSharedKeyIdentityHint(),
|
2021-03-04 18:20:18 +00:00
|
|
|
max_psk_len);
|
|
|
|
emit q->preSharedKeyAuthenticationRequired(&authenticator);
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// No PSK set? Return now to make the handshake fail
|
|
|
|
if (authenticator.preSharedKey().isEmpty())
|
|
|
|
return 0;
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
// Copy data back into OpenSSL
|
|
|
|
const int pskLength = qMin(authenticator.preSharedKey().length(), authenticator.maximumPreSharedKeyLength());
|
|
|
|
std::memcpy(psk, authenticator.preSharedKey().constData(), pskLength);
|
|
|
|
return pskLength;
|
2019-09-27 11:04:54 +00:00
|
|
|
}
|
|
|
|
|
2021-11-29 15:31:21 +00:00
|
|
|
bool TlsCryptographOpenSSL::isInSslRead() const
|
|
|
|
{
|
|
|
|
return inSslRead;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TlsCryptographOpenSSL::setRenegotiated(bool renegotiated)
|
|
|
|
{
|
|
|
|
this->renegotiated = renegotiated;
|
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
|
|
|
void TlsCryptographOpenSSL::fetchCaRootForCert(const QSslCertificate &cert)
|
2019-09-27 11:04:54 +00:00
|
|
|
{
|
2021-03-04 18:20:18 +00:00
|
|
|
Q_ASSERT(d);
|
|
|
|
Q_ASSERT(q);
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
//The root certificate is downloaded from windows update, which blocks for 15 seconds in the worst case
|
|
|
|
//so the request is done in a worker thread.
|
|
|
|
QList<QSslCertificate> customRoots;
|
|
|
|
if (fetchAuthorityInformation)
|
2021-03-12 11:12:59 +00:00
|
|
|
customRoots = q->sslConfiguration().caCertificates();
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
//Remember we are fetching and what we are fetching:
|
|
|
|
caToFetch = cert;
|
2020-03-19 20:18:19 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
QWindowsCaRootFetcher *fetcher = new QWindowsCaRootFetcher(cert, d->tlsMode(), customRoots,
|
|
|
|
q->peerVerifyName());
|
|
|
|
connect(fetcher, &QWindowsCaRootFetcher::finished, this, &TlsCryptographOpenSSL::caRootLoaded,
|
|
|
|
Qt::QueuedConnection);
|
|
|
|
QMetaObject::invokeMethod(fetcher, "start", Qt::QueuedConnection);
|
|
|
|
QSslSocketPrivate::pauseSocketNotifiers(q);
|
|
|
|
d->setPaused(true);
|
|
|
|
}
|
2020-03-19 20:18:19 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
void TlsCryptographOpenSSL::caRootLoaded(QSslCertificate cert, QSslCertificate trustedRoot)
|
|
|
|
{
|
|
|
|
if (caToFetch != cert) {
|
|
|
|
//Ooops, something from the previous connection attempt, ignore!
|
|
|
|
return;
|
|
|
|
}
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
Q_ASSERT(d);
|
|
|
|
Q_ASSERT(q);
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
//Done, fetched already:
|
|
|
|
caToFetch = QSslCertificate{};
|
2019-09-27 11:04:54 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (fetchAuthorityInformation) {
|
2021-03-12 11:12:59 +00:00
|
|
|
if (!q->sslConfiguration().caCertificates().contains(trustedRoot))
|
2021-03-04 18:20:18 +00:00
|
|
|
trustedRoot = QSslCertificate{};
|
|
|
|
fetchAuthorityInformation = false;
|
2019-09-27 11:04:54 +00:00
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
if (!trustedRoot.isNull() && !trustedRoot.isBlacklisted()) {
|
|
|
|
if (QSslSocketPrivate::rootCertOnDemandLoadingSupported()) {
|
|
|
|
//Add the new root cert to default cert list for use by future sockets
|
|
|
|
auto defaultConfig = QSslConfiguration::defaultConfiguration();
|
|
|
|
defaultConfig.addCaCertificate(trustedRoot);
|
|
|
|
QSslConfiguration::setDefaultConfiguration(defaultConfig);
|
|
|
|
}
|
|
|
|
//Add the new root cert to this socket for future connections
|
2021-03-12 11:12:59 +00:00
|
|
|
QTlsBackend::addTustedRoot(d, trustedRoot);
|
2021-03-04 18:20:18 +00:00
|
|
|
//Remove the broken chain ssl errors (as chain is verified by windows)
|
|
|
|
for (int i=sslErrors.count() - 1; i >= 0; --i) {
|
|
|
|
if (sslErrors.at(i).certificate() == cert) {
|
|
|
|
switch (sslErrors.at(i).error()) {
|
|
|
|
case QSslError::UnableToGetLocalIssuerCertificate:
|
|
|
|
case QSslError::CertificateUntrusted:
|
|
|
|
case QSslError::UnableToVerifyFirstCertificate:
|
|
|
|
case QSslError::SelfSignedCertificateInChain:
|
|
|
|
// error can be ignored if OS says the chain is trusted
|
|
|
|
sslErrors.removeAt(i);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// error cannot be ignored
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-09-27 11:04:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
auto *plainSocket = d->plainTcpSocket();
|
|
|
|
Q_ASSERT(plainSocket);
|
|
|
|
// Continue with remaining errors
|
|
|
|
if (plainSocket)
|
|
|
|
plainSocket->resume();
|
|
|
|
d->setPaused(false);
|
|
|
|
if (checkSslErrors() && ssl) {
|
|
|
|
bool willClose = (d->isAutoStartingHandshake() && d->isPendingClose());
|
|
|
|
continueHandshake();
|
|
|
|
if (!willClose)
|
|
|
|
transmit();
|
|
|
|
}
|
2020-05-14 14:40:08 +00:00
|
|
|
}
|
2011-06-18 14:53:53 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
#endif // Q_OS_WIN
|
2011-06-18 14:53:53 +00:00
|
|
|
|
2021-03-04 18:20:18 +00:00
|
|
|
} // namespace QTlsPrivate
|
2014-05-10 21:49:37 +00:00
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
QT_END_NAMESPACE
|