Add a new (D)TLS configuration

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 <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
bb10
Timur Pocheptsov 2018-02-19 13:46:21 +01:00
parent 061fbceb77
commit 30978dc1a5
7 changed files with 177 additions and 20 deletions

View File

@ -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.

View File

@ -91,6 +91,10 @@ namespace QSsl {
TlsV1_1OrLater,
TlsV1_2OrLater,
DtlsV1_0,
DtlsV1_2,
DtlsV1_2OrLater,
UnknownProtocol = -1
};

View File

@ -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) {

View File

@ -154,9 +154,15 @@ public:
void setBackendConfigurationOption(const QByteArray &name, const QVariant &value);
void setBackendConfiguration(const QMap<QByteArray, QVariant> &backendConfig = QMap<QByteArray, QVariant>());
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,

View File

@ -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

View File

@ -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<QSslCipher> supportedCiphers;
QVector<QSslEllipticCurve> supportedEllipticCurves;
QExplicitlySharedDataPointer<QSslConfigurationPrivate> config;
QExplicitlySharedDataPointer<QSslConfigurationPrivate> dtlsConfig;
};
Q_GLOBAL_STATIC(QSslSocketGlobalData, globalData)
@ -2125,6 +2131,26 @@ void QSslSocketPrivate::setDefaultSupportedCiphers(const QList<QSslCipher> &ciph
globalData()->supportedCiphers = ciphers;
}
/*!
\internal
*/
void q_setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers)
{
QMutexLocker locker(&globalData()->mutex);
globalData()->dtlsConfig.detach();
globalData()->dtlsConfig->ciphers = ciphers;
}
/*!
\internal
*/
QList<QSslCipher> q_getDefaultDtlsCiphers()
{
QSslSocketPrivate::ensureInitialized();
QMutexLocker locker(&globalData()->mutex);
return globalData()->dtlsConfig->ciphers;
}
/*!
\internal
*/
@ -2142,6 +2168,7 @@ void QSslSocketPrivate::setDefaultSupportedEllipticCurves(const QVector<QSslElli
{
const QMutexLocker locker(&globalData()->mutex);
globalData()->config.detach();
globalData()->dtlsConfig.detach();
globalData()->supportedEllipticCurves = curves;
}
@ -2164,6 +2191,8 @@ void QSslSocketPrivate::setDefaultCaCertificates(const QList<QSslCertificate> &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<QSslCertificate> &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<QSslConfigurationPrivate*>(configuration.d.constData());
}
/*!
\internal
*/

View File

@ -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<QSslCipher> &ciphers,
QList<QSslCipher> &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<QSslCipher> &ciphers);
long QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions)
{
long options;
@ -452,29 +478,27 @@ void QSslSocketPrivate::resetDefaultCiphers()
QList<QSslCipher> ciphers;
QList<QSslCipher> 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()