From 30978dc1a50368e45c3764d7efc283c4e660a9b9 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Mon, 19 Feb 2018 13:46:21 +0100 Subject: Add a new (D)TLS configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Namespace QSsl: introduce DtlsV1_0/DtlsV1_2/DtlsV1_2OrLater enumerators into SslProtocol. Implement QSslConfiguration::defaultDtlsConfiguration. Make some functions shared - now not only QSslSocket needs them, but also DTLS-related code. This patch-set also enables protocol-specific set of ciphers (so for DTLS we are using the correct method - 'DTLS_method'). Change-Id: I828fc898674aa3c0a471e8e5b94575bb50538601 Reviewed-by: Edward Welbourne Reviewed-by: MÃ¥rten Nordheim --- src/network/ssl/qssl.cpp | 3 ++ src/network/ssl/qssl.h | 4 +++ src/network/ssl/qsslconfiguration.cpp | 56 ++++++++++++++++++++++++++++++ src/network/ssl/qsslconfiguration.h | 6 ++++ src/network/ssl/qsslconfiguration_p.h | 5 +++ src/network/ssl/qsslsocket.cpp | 61 ++++++++++++++++++++++++++++++++- src/network/ssl/qsslsocket_openssl.cpp | 62 +++++++++++++++++++++++----------- 7 files changed, 177 insertions(+), 20 deletions(-) diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp index 85d1a99c45..0102956097 100644 --- a/src/network/ssl/qssl.cpp +++ b/src/network/ssl/qssl.cpp @@ -125,6 +125,9 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl"); \value TlsV1_1OrLater TLSv1.1 and later versions. This option is not available when using the WinRT backend due to platform limitations. \value TlsV1_2 TLSv1.2. When using the WinRT backend this option will also enable TLSv1.0 and TLSv1.1. \value TlsV1_2OrLater TLSv1.2 and later versions. This option is not available when using the WinRT backend due to platform limitations. + \value DtlsV1_0 DTLSv1.0 + \value DtlsV1_2 DTLSv1.2 + \value DtlsV1_2OrLater DTLSv1.2 and later versions. \value UnknownProtocol The cipher's protocol cannot be determined. \value AnyProtocol The socket understands SSLv2, SSLv3, and TLSv1.0. This value is used by QSslSocket only. diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h index c2a468c97c..7d688e27fc 100644 --- a/src/network/ssl/qssl.h +++ b/src/network/ssl/qssl.h @@ -91,6 +91,10 @@ namespace QSsl { TlsV1_1OrLater, TlsV1_2OrLater, + DtlsV1_0, + DtlsV1_2, + DtlsV1_2OrLater, + UnknownProtocol = -1 }; diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp index 116a6693c4..8c9fa5d4f2 100644 --- a/src/network/ssl/qsslconfiguration.cpp +++ b/src/network/ssl/qsslconfiguration.cpp @@ -997,6 +997,27 @@ QSslConfiguration::NextProtocolNegotiationStatus QSslConfiguration::nextProtocol return d->nextProtocolNegotiationStatus; } +/*! + This function returns true if DTLS cookie verification was enabled on a + server-side socket. + + \sa setDtlsCookieVerificationEnabled() + */ +bool QSslConfiguration::dtlsCookieVerificationEnabled() const +{ + return d->dtlsCookieEnabled; +} + +/*! + This function enables DTLS cookie verification when \a enable is true. + + \sa dtlsCookieVerificationEnabled() + */ +void QSslConfiguration::setDtlsCookieVerificationEnabled(bool enable) +{ + d->dtlsCookieEnabled = enable; +} + /*! Returns the default SSL configuration to be used in new SSL connections. @@ -1030,6 +1051,41 @@ void QSslConfiguration::setDefaultConfiguration(const QSslConfiguration &configu QSslConfigurationPrivate::setDefaultConfiguration(configuration); } +/*! + Returns the default DTLS configuration to be used in new DTLS + connections. + + The default DTLS configuration consists of: + + \list + \li no local certificate and no private key + \li protocol DtlsV1_2OrLater + \li the system's default CA certificate list + \li the cipher list equal to the list of the SSL libraries' + supported TLS 1.2 ciphers that use 128 or more secret bits + for the cipher. + \endlist + + \sa setDefaultDtlsConfiguration() +*/ +QSslConfiguration QSslConfiguration::defaultDtlsConfiguration() +{ + return QSslConfigurationPrivate::defaultDtlsConfiguration(); +} + +/*! + Sets the default DTLS configuration to be used in new DTLS + connections to be \a configuration. Existing connections are not + affected by this call. + + \sa defaultDtlsConfiguration() +*/ +void QSslConfiguration::setDefaultDtlsConfiguration(const QSslConfiguration &configuration) +{ + QSslConfigurationPrivate::setDefaultDtlsConfiguration(configuration); +} + + /*! \internal */ bool QSslConfigurationPrivate::peerSessionWasShared(const QSslConfiguration &configuration) { diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h index a5561d9828..520504ff4c 100644 --- a/src/network/ssl/qsslconfiguration.h +++ b/src/network/ssl/qsslconfiguration.h @@ -154,9 +154,15 @@ public: void setBackendConfigurationOption(const QByteArray &name, const QVariant &value); void setBackendConfiguration(const QMap &backendConfig = QMap()); + bool dtlsCookieVerificationEnabled() const; + void setDtlsCookieVerificationEnabled(bool enable); + static QSslConfiguration defaultConfiguration(); static void setDefaultConfiguration(const QSslConfiguration &configuration); + static QSslConfiguration defaultDtlsConfiguration(); + static void setDefaultDtlsConfiguration(const QSslConfiguration &configuration); + enum NextProtocolNegotiationStatus { NextProtocolNegotiationNone, NextProtocolNegotiationNegotiated, diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h index 38a98239db..f44485d51a 100644 --- a/src/network/ssl/qsslconfiguration_p.h +++ b/src/network/ssl/qsslconfiguration_p.h @@ -137,10 +137,15 @@ public: QByteArray nextNegotiatedProtocol; QSslConfiguration::NextProtocolNegotiationStatus nextProtocolNegotiationStatus; + bool dtlsCookieEnabled = true; + // in qsslsocket.cpp: static QSslConfiguration defaultConfiguration(); static void setDefaultConfiguration(const QSslConfiguration &configuration); static void deepCopyDefaultConfiguration(QSslConfigurationPrivate *config); + + static QSslConfiguration defaultDtlsConfiguration(); + static void setDefaultDtlsConfiguration(const QSslConfiguration &configuration); }; // implemented here for inlining purposes diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 4273904c12..2a415ace44 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -336,12 +336,18 @@ QT_BEGIN_NAMESPACE class QSslSocketGlobalData { public: - QSslSocketGlobalData() : config(new QSslConfigurationPrivate) {} + QSslSocketGlobalData() + : config(new QSslConfigurationPrivate), + dtlsConfig(new QSslConfigurationPrivate) + { + dtlsConfig->protocol = QSsl::DtlsV1_2OrLater; + } QMutex mutex; QList supportedCiphers; QVector supportedEllipticCurves; QExplicitlySharedDataPointer config; + QExplicitlySharedDataPointer dtlsConfig; }; Q_GLOBAL_STATIC(QSslSocketGlobalData, globalData) @@ -2125,6 +2131,26 @@ void QSslSocketPrivate::setDefaultSupportedCiphers(const QList &ciph globalData()->supportedCiphers = ciphers; } +/*! + \internal +*/ +void q_setDefaultDtlsCiphers(const QList &ciphers) +{ + QMutexLocker locker(&globalData()->mutex); + globalData()->dtlsConfig.detach(); + globalData()->dtlsConfig->ciphers = ciphers; +} + +/*! + \internal +*/ +QList q_getDefaultDtlsCiphers() +{ + QSslSocketPrivate::ensureInitialized(); + QMutexLocker locker(&globalData()->mutex); + return globalData()->dtlsConfig->ciphers; +} + /*! \internal */ @@ -2142,6 +2168,7 @@ void QSslSocketPrivate::setDefaultSupportedEllipticCurves(const QVectormutex); globalData()->config.detach(); + globalData()->dtlsConfig.detach(); globalData()->supportedEllipticCurves = curves; } @@ -2164,6 +2191,8 @@ void QSslSocketPrivate::setDefaultCaCertificates(const QList &c QMutexLocker locker(&globalData()->mutex); globalData()->config.detach(); globalData()->config->caCertificates = certs; + globalData()->dtlsConfig.detach(); + globalData()->dtlsConfig->caCertificates = certs; // when the certificates are set explicitly, we do not want to // load the system certificates on demand s_loadRootCertsOnDemand = false; @@ -2183,6 +2212,8 @@ bool QSslSocketPrivate::addDefaultCaCertificates(const QString &path, QSsl::Enco QMutexLocker locker(&globalData()->mutex); globalData()->config.detach(); globalData()->config->caCertificates += certs; + globalData()->dtlsConfig.detach(); + globalData()->dtlsConfig->caCertificates += certs; return true; } @@ -2195,6 +2226,8 @@ void QSslSocketPrivate::addDefaultCaCertificate(const QSslCertificate &cert) QMutexLocker locker(&globalData()->mutex); globalData()->config.detach(); globalData()->config->caCertificates += cert; + globalData()->dtlsConfig.detach(); + globalData()->dtlsConfig->caCertificates += cert; } /*! @@ -2206,6 +2239,8 @@ void QSslSocketPrivate::addDefaultCaCertificates(const QList &c QMutexLocker locker(&globalData()->mutex); globalData()->config.detach(); globalData()->config->caCertificates += certs; + globalData()->dtlsConfig.detach(); + globalData()->dtlsConfig->caCertificates += certs; } /*! @@ -2260,6 +2295,30 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri ptr->backendConfig = global->backendConfig; } +/*! + \internal +*/ +QSslConfiguration QSslConfigurationPrivate::defaultDtlsConfiguration() +{ + QSslSocketPrivate::ensureInitialized(); + QMutexLocker locker(&globalData()->mutex); + + return QSslConfiguration(globalData()->dtlsConfig.data()); +} + +/*! + \internal +*/ +void QSslConfigurationPrivate::setDefaultDtlsConfiguration(const QSslConfiguration &configuration) +{ + QSslSocketPrivate::ensureInitialized(); + QMutexLocker locker(&globalData()->mutex); + if (globalData()->dtlsConfig == configuration.d) + return; // nothing to do + + globalData()->dtlsConfig = const_cast(configuration.d.constData()); +} + /*! \internal */ diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index e269a1f8ea..3858b4b21f 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -193,8 +193,7 @@ QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(const SSL_CIPHER return ciph; } -// static -inline QSslErrorEntry QSslErrorEntry::fromStoreContext(X509_STORE_CTX *ctx) +QSslErrorEntry QSslErrorEntry::fromStoreContext(X509_STORE_CTX *ctx) { return { q_X509_STORE_CTX_get_error(ctx), @@ -247,6 +246,33 @@ int q_X509Callback(int ok, X509_STORE_CTX *ctx) return 1; } +static void q_loadCiphersForConnection(SSL *connection, QList &ciphers, + QList &defaultCiphers) +{ + Q_ASSERT(connection); + + STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(connection); + for (int i = 0; i < q_sk_SSL_CIPHER_num(supportedCiphers); ++i) { + if (SSL_CIPHER *cipher = q_sk_SSL_CIPHER_value(supportedCiphers, i)) { + QSslCipher ciph = QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher); + if (!ciph.isNull()) { + // Unconditionally exclude ADH and AECDH ciphers since they offer no MITM protection + if (!ciph.name().toLower().startsWith(QLatin1String("adh")) && + !ciph.name().toLower().startsWith(QLatin1String("exp-adh")) && + !ciph.name().toLower().startsWith(QLatin1String("aecdh"))) { + ciphers << ciph; + + if (ciph.usedBits() >= 128) + defaultCiphers << ciph; + } + } + } + } +} + +// Defined in qsslsocket.cpp +void q_setDefaultDtlsCiphers(const QList &ciphers); + long QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions) { long options; @@ -452,29 +478,27 @@ void QSslSocketPrivate::resetDefaultCiphers() QList ciphers; QList defaultCiphers; - STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(mySsl); - for (int i = 0; i < q_sk_SSL_CIPHER_num(supportedCiphers); ++i) { - if (SSL_CIPHER *cipher = q_sk_SSL_CIPHER_value(supportedCiphers, i)) { - QSslCipher ciph = QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher); - if (!ciph.isNull()) { - // Unconditionally exclude ADH and AECDH ciphers since they offer no MITM protection - if (!ciph.name().toLower().startsWith(QLatin1String("adh")) && - !ciph.name().toLower().startsWith(QLatin1String("exp-adh")) && - !ciph.name().toLower().startsWith(QLatin1String("aecdh"))) { - ciphers << ciph; - - if (ciph.usedBits() >= 128) - defaultCiphers << ciph; - } - } - } - } + q_loadCiphersForConnection(mySsl, ciphers, defaultCiphers); q_SSL_CTX_free(myCtx); q_SSL_free(mySsl); setDefaultSupportedCiphers(ciphers); setDefaultCiphers(defaultCiphers); + + ciphers.clear(); + defaultCiphers.clear(); + + myCtx = q_SSL_CTX_new(q_DTLS_client_method()); + if (myCtx) { + mySsl = q_SSL_new(myCtx); + if (mySsl) { + q_loadCiphersForConnection(mySsl, ciphers, defaultCiphers); + q_setDefaultDtlsCiphers(defaultCiphers); + q_SSL_free(mySsl); + } + q_SSL_CTX_free(myCtx); + } } void QSslSocketPrivate::resetDefaultEllipticCurves() -- cgit v1.2.3