QSslSocket (OpenSSL 1.1) - respect requested protocol version

Properly handle single protocol TLS configurations. Previously,
due to the use of generic (non version-specific) client/server method
they worked as ranges of protocols instead. This also fixes a couple
of previously broken tests.

Task-number: QTBUG-67584
Change-Id: Ied23113a4fab6b407a34c953e3bd33eab153bb67
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Timur Pocheptsov 2018-04-09 15:46:21 +02:00
parent 4b6542c9ff
commit e3cea2a7b9
4 changed files with 75 additions and 13 deletions

View File

@ -117,7 +117,7 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl");
Describes the protocol of the cipher.
\value SslV3 SSLv3. When using the WinRT backend this option will also enable TLSv1.0
\value SslV2 SSLv2
\value SslV2 SSLv2. Note, SSLv2 support was removed in OpenSSL 1.1.
\value TlsV1_0 TLSv1.0
\value TlsV1_0OrLater TLSv1.0 and later versions. This option is not available when using the WinRT backend due to platform limitations.
\value TlsV1 Obsolete, means the same as TlsV1_0
@ -126,8 +126,8 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl");
\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 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.
\value AnyProtocol The socket understands SSLv2, SSLv3, TLSv1.0 and all
supported later versions of TLS. This value is used by QSslSocket only.
\value TlsV1SslV3 On the client side, this will send
a TLS 1.0 Client Hello, enabling TLSv1_0 and SSLv3 connections.
On the server side, this will enable both SSLv3 and TLSv1_0 connections.

View File

@ -100,6 +100,68 @@ init_context:
return;
}
long minVersion = TLS_ANY_VERSION;
long maxVersion = TLS_ANY_VERSION;
switch (sslContext->sslConfiguration.protocol()) {
// The single-protocol versions first:
case QSsl::SslV3:
minVersion = SSL3_VERSION;
maxVersion = SSL3_VERSION;
break;
case QSsl::TlsV1_0:
minVersion = TLS1_VERSION;
maxVersion = TLS1_VERSION;
break;
case QSsl::TlsV1_1:
minVersion = TLS1_1_VERSION;
maxVersion = TLS1_1_VERSION;
break;
case QSsl::TlsV1_2:
minVersion = TLS1_2_VERSION;
maxVersion = TLS1_2_VERSION;
break;
// Ranges:
case QSsl::TlsV1SslV3:
case QSsl::AnyProtocol:
minVersion = SSL3_VERSION;
maxVersion = TLS_MAX_VERSION;
break;
case QSsl::SecureProtocols:
case QSsl::TlsV1_0OrLater:
minVersion = TLS1_VERSION;
maxVersion = TLS_MAX_VERSION;
break;
case QSsl::TlsV1_1OrLater:
minVersion = TLS1_1_VERSION;
maxVersion = TLS_MAX_VERSION;
break;
case QSsl::TlsV1_2OrLater:
minVersion = TLS1_2_VERSION;
maxVersion = TLS_MAX_VERSION;
break;
case QSsl::SslV2:
// This protocol is not supported by OpenSSL 1.1 and we handle
// it as an error (see the code above).
Q_UNREACHABLE();
break;
case QSsl::UnknownProtocol:
break;
}
if (minVersion != TLS_ANY_VERSION
&& !q_SSL_CTX_set_min_proto_version(sslContext->ctx, minVersion)) {
sslContext->errorStr = QSslSocket::tr("Error while setting the minimal protocol version");
sslContext->errorCode = QSslError::UnspecifiedError;
return;
}
if (maxVersion != TLS_ANY_VERSION
&& !q_SSL_CTX_set_max_proto_version(sslContext->ctx, maxVersion)) {
sslContext->errorStr = QSslSocket::tr("Error while setting the maximum protocol version");
sslContext->errorCode = QSslError::UnspecifiedError;
return;
}
// Enable bug workarounds.
long options = QSslSocketBackendPrivate::setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions);
q_SSL_CTX_set_options(sslContext->ctx, options);

View File

@ -129,4 +129,10 @@ const char *q_OpenSSL_version(int type);
unsigned long q_SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *session);
#define q_SSL_CTX_set_min_proto_version(ctx, version) \
q_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, version, nullptr)
#define q_SSL_CTX_set_max_proto_version(ctx, version) \
q_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, version, nullptr)
#endif

View File

@ -1188,13 +1188,7 @@ void tst_QSslSocket::protocolServerSide_data()
#if !defined(OPENSSL_NO_SSL2)
// OpenSSL 1.1 has removed SSL2 support. But there is no OPENSSL_NO_SSL2 macro ...
#define OPENSSL_NO_SSL2
#endif
// A client using our OpenSSL1.1 backend will negotiate up from TLS 1.0 or 1.1
// to TLS 1.2 if the server asks for it, where our older backend fails to compromise.
// So some tests that fail for the old pass with the new.
const bool willUseTLS12 = true;
#else
const bool willUseTLS12 = false;
#endif // OPENSSL_NO_SSL2
#endif // opensslv11
#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
@ -1290,7 +1284,7 @@ void tst_QSslSocket::protocolServerSide_data()
QTest::newRow("tls1.1orlater-ssl3") << QSsl::TlsV1_1OrLater << QSsl::SslV3 << false;
#endif
QTest::newRow("tls1.1orlater-tls1.0") << QSsl::TlsV1_1OrLater << QSsl::TlsV1_0 << willUseTLS12;
QTest::newRow("tls1.1orlater-tls1.0") << QSsl::TlsV1_1OrLater << QSsl::TlsV1_0 << false;
QTest::newRow("tls1.1orlater-tls1.1") << QSsl::TlsV1_1OrLater << QSsl::TlsV1_1 << true;
QTest::newRow("tls1.1orlater-tls1.2") << QSsl::TlsV1_1OrLater << QSsl::TlsV1_2 << true;
@ -1300,8 +1294,8 @@ void tst_QSslSocket::protocolServerSide_data()
#if !defined(OPENSSL_NO_SSL3)
QTest::newRow("tls1.2orlater-ssl3") << QSsl::TlsV1_2OrLater << QSsl::SslV3 << false;
#endif
QTest::newRow("tls1.2orlater-tls1.0") << QSsl::TlsV1_2OrLater << QSsl::TlsV1_0 << willUseTLS12;
QTest::newRow("tls1.2orlater-tls1.1") << QSsl::TlsV1_2OrLater << QSsl::TlsV1_1 << willUseTLS12;
QTest::newRow("tls1.2orlater-tls1.0") << QSsl::TlsV1_2OrLater << QSsl::TlsV1_0 << false;
QTest::newRow("tls1.2orlater-tls1.1") << QSsl::TlsV1_2OrLater << QSsl::TlsV1_1 << false;
QTest::newRow("tls1.2orlater-tls1.2") << QSsl::TlsV1_2OrLater << QSsl::TlsV1_2 << true;
QTest::newRow("any-tls1.0") << QSsl::AnyProtocol << QSsl::TlsV1_0 << true;