Add the ability to enable various SSL bug workarounds.

There are lots of buggy SSL servers around and to connect to them you
need to disable various features. This commit adds the ability to
disable the SSL ticket extension, the ability to disable the insertion
of empty fragments, and the ability to disable compression.

Task-number: QTBUG-21906

Change-Id: I3e1d0347a46e9030b889bbf15b2aad19b8513b73
Merge-request: 68
Reviewed-by: Peter Hartmann <peter.hartmann@nokia.com>
This commit is contained in:
Richard Moore 2011-10-19 11:40:57 +02:00 committed by Qt by Nokia
parent 5b6894de87
commit 78d02e93ac
9 changed files with 206 additions and 9 deletions

View File

@ -120,4 +120,34 @@ QT_BEGIN_NAMESPACE
the correct setting for your protocol.
*/
/*!
\enum QSsl::SslOption
Describes the options that can be used to control the details of
SSL behaviour. These options are generally used to turn features off
to work around buggy servers.
\value SslOptionDisableEmptyFragments Disables the insertion of empty
fragments into the data when using block ciphers. When enabled, this
prevents some attacks (such as the BEAST attack), however it is
incompatible with some servers.
\value SslOptionDisableTickets Disables the SSL session ticket
extension. This can cause slower connection setup, however some servers
are not compatible with the extension.
\value SslOptionDisableCompression Disables the SSL compression
extension. When enabled, this allows the data being passed over SSL to
be compressed, however some servers are not compatible with this
extension.
\value SslOptionDisableServerNameIndication Disables the SSL server
name indication extension. When enabled, this tells the server the virtual
host being accessed allowing it to respond with the correct certificate.
By default, SslOptionDisableEmptyFragments is turned on since this causes
problems with a large number of servers, but the other options are disabled.
Note: Availability of above options depends on the version of the SSL
backend in use.
*/
QT_END_NAMESPACE

View File

@ -44,6 +44,7 @@
#define QSSL_H
#include <QtCore/qglobal.h>
#include <QtCore/QFlags>
QT_BEGIN_HEADER
@ -86,8 +87,18 @@ namespace QSsl {
SecureProtocols,
UnknownProtocol = -1
};
enum SslOption {
SslOptionDisableEmptyFragments = 0x01,
SslOptionDisableSessionTickets = 0x02,
SslOptionDisableCompression = 0x04,
SslOptionDisableServerNameIndication = 0x08
};
Q_DECLARE_FLAGS(SslOptions, SslOption)
}
Q_DECLARE_OPERATORS_FOR_FLAGS(QSsl::SslOptions)
QT_END_NAMESPACE
QT_END_HEADER

View File

@ -167,7 +167,8 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const
d->caCertificates == other.d->caCertificates &&
d->protocol == other.d->protocol &&
d->peerVerifyMode == other.d->peerVerifyMode &&
d->peerVerifyDepth == other.d->peerVerifyDepth;
d->peerVerifyDepth == other.d->peerVerifyDepth &&
d->sslOptions == other.d->sslOptions;
}
/*!
@ -199,7 +200,8 @@ bool QSslConfiguration::isNull() const
d->localCertificate.isNull() &&
d->privateKey.isNull() &&
d->peerCertificate.isNull() &&
d->peerCertificateChain.count() == 0);
d->peerCertificateChain.count() == 0 &&
d->sslOptions == 0);
}
/*!
@ -506,6 +508,29 @@ void QSslConfiguration::setCaCertificates(const QList<QSslCertificate> &certific
d->caCertificates = certificates;
}
/*!
Enables or disables an SSL compatibility option.
\sa testSSlOption()
*/
void QSslConfiguration::setSslOption(QSsl::SslOption option, bool on)
{
if (on)
d->sslOptions |= option;
else
d->sslOptions &= ~option;
}
/*!
Returns true if the specified SSL compatibility option is enabled.
\sa testSSlOption()
*/
bool QSslConfiguration::testSslOption(QSsl::SslOption option) const
{
return d->sslOptions & option;
}
/*!
Returns the default SSL configuration to be used in new SSL
connections.

View File

@ -59,6 +59,7 @@
#include <QtCore/qshareddata.h>
#include <QtNetwork/qsslsocket.h>
#include <QtNetwork/qssl.h>
QT_BEGIN_HEADER
@ -118,6 +119,9 @@ public:
QList<QSslCertificate> caCertificates() const;
void setCaCertificates(const QList<QSslCertificate> &certificates);
void setSslOption(QSsl::SslOption option, bool on);
bool testSslOption(QSsl::SslOption option) const;
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);

View File

@ -98,6 +98,8 @@ public:
QSslSocket::PeerVerifyMode peerVerifyMode;
int peerVerifyDepth;
QSsl::SslOptions sslOptions;
// in qsslsocket.cpp:
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);

View File

@ -897,6 +897,7 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration)
d->configuration.peerVerifyDepth = configuration.peerVerifyDepth();
d->configuration.peerVerifyMode = configuration.peerVerifyMode();
d->configuration.protocol = configuration.protocol();
d->configuration.sslOptions = configuration.d->sslOptions;
d->allowRootCertOnDemandLoading = false;
}
@ -2049,6 +2050,7 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri
ptr->protocol = global->protocol;
ptr->peerVerifyMode = global->peerVerifyMode;
ptr->peerVerifyDepth = global->peerVerifyDepth;
ptr->sslOptions = global->sslOptions;
}
/*!

View File

@ -285,12 +285,29 @@ init_context:
return false;
}
// Enable all bug workarounds.
if (configuration.protocol == QSsl::TlsV1SslV3 || configuration.protocol == QSsl::SecureProtocols) {
q_SSL_CTX_set_options(ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
} else {
q_SSL_CTX_set_options(ctx, SSL_OP_ALL);
}
// Enable bug workarounds.
long options;
if (configuration.protocol == QSsl::TlsV1SslV3 || configuration.protocol == QSsl::SecureProtocols)
options = SSL_OP_ALL|SSL_OP_NO_SSLv2;
else
options = SSL_OP_ALL;
// This option is disabled by default, so we need to be able to clear it
if (configuration.sslOptions & QSsl::SslOptionDisableEmptyFragments)
options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
else
options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
#ifdef SSL_OP_NO_TICKET
if (configuration.sslOptions & QSsl::SslOptionDisableSessionTickets)
options |= SSL_OP_NO_TICKET;
#endif
#ifdef SSL_OP_NO_COMPRESSION
if (configuration.sslOptions & QSsl::SslOptionDisableCompression)
options |= SSL_OP_NO_COMPRESSION;
#endif
q_SSL_CTX_set_options(ctx, options);
// Initialize ciphers
QByteArray cipherString;
@ -426,7 +443,9 @@ init_context:
tlsHostName = hostName;
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)) {
if (!ace.isEmpty()
&& !QHostAddress().setAddress(tlsHostName)
&& !(configuration.sslOptions & QSsl::SslOptionDisableServerNameIndication)) {
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
if (!q_SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, ace.data()))
#else

View File

@ -0,0 +1,92 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtNetwork/qsslconfiguration.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QTextStream>
#include <stdio.h>
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
if (argc < 3) {
QTextStream out(stdout);
out << "Usage: " << argv[0] << " host port [options]" << endl;
out << "The options can be one or more of the following:" << endl;
out << "enable_empty_fragments" << endl;
out << "disable_session_tickets" << endl;
out << "disable_compression" << endl;
out << "disable_sni" << endl;
return 1;
}
QString host = QString::fromLocal8Bit(argv[1]);
int port = QString::fromLocal8Bit(argv[2]).toInt();
QSslConfiguration config = QSslConfiguration::defaultConfiguration();
for (int i=3; i < argc; i++) {
QString option = QString::fromLocal8Bit(argv[i]);
if (option == QStringLiteral("enable_empty_fragments"))
config.setSslOption(QSsl::SslOptionDisableEmptyFragments, false);
else if (option == QStringLiteral("disable_session_tickets"))
config.setSslOption(QSsl::SslOptionDisableSessionTickets, true);
else if (option == QStringLiteral("disable_compression"))
config.setSslOption(QSsl::SslOptionDisableCompression, true);
else if (option == QStringLiteral("disable_sni"))
config.setSslOption(QSsl::SslOptionDisableServerNameIndication, true);
}
QSslConfiguration::setDefaultConfiguration(config);
QSslSocket socket;
//socket.setSslConfiguration(config);
socket.connectToHostEncrypted(host, port);
if ( !socket.waitForEncrypted() ) {
qDebug() << socket.errorString();
return 1;
}
return 0;
}

View File

@ -0,0 +1,12 @@
load(qttest_p4)
TEMPLATE = app
TARGET = tst_qssloptions
DEPENDPATH += .
INCLUDEPATH += .
QT -= gui
QT += network
#CONFIG += release
SOURCES += main.cpp