summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/network/ssl/qssl.cpp30
-rw-r--r--src/network/ssl/qssl.h11
-rw-r--r--src/network/ssl/qsslconfiguration.cpp29
-rw-r--r--src/network/ssl/qsslconfiguration.h4
-rw-r--r--src/network/ssl/qsslconfiguration_p.h2
-rw-r--r--src/network/ssl/qsslsocket.cpp2
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp33
-rw-r--r--tests/manual/qssloptions/main.cpp92
-rw-r--r--tests/manual/qssloptions/qssloptions.pro12
9 files changed, 206 insertions, 9 deletions
diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp
index 586c894..08a05ff 100644
--- a/src/network/ssl/qssl.cpp
+++ b/src/network/ssl/qssl.cpp
@@ -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
diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h
index 2ecd1c3..453d4da 100644
--- a/src/network/ssl/qssl.h
+++ b/src/network/ssl/qssl.h
@@ -44,6 +44,7 @@
#define QSSL_H
#include <QtCore/qglobal.h>
+#include <QtCore/QFlags>
QT_BEGIN_HEADER
@@ -81,8 +82,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
diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp
index 69d3b66..e24076e 100644
--- a/src/network/ssl/qsslconfiguration.cpp
+++ b/src/network/ssl/qsslconfiguration.cpp
@@ -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);
}
/*!
@@ -507,6 +509,29 @@ void QSslConfiguration::setCaCertificates(const QList<QSslCertificate> &certific
}
/*!
+ 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.
diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h
index 258b454..ff8c8fc 100644
--- a/src/network/ssl/qsslconfiguration.h
+++ b/src/network/ssl/qsslconfiguration.h
@@ -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);
diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h
index af80e4c..b83edb9 100644
--- a/src/network/ssl/qsslconfiguration_p.h
+++ b/src/network/ssl/qsslconfiguration_p.h
@@ -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);
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index df61fb6..3ac8f18 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -896,6 +896,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;
}
@@ -2027,6 +2028,7 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri
ptr->protocol = global->protocol;
ptr->peerVerifyMode = global->peerVerifyMode;
ptr->peerVerifyDepth = global->peerVerifyDepth;
+ ptr->sslOptions = global->sslOptions;
}
/*!
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 8e53974..3942209 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -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;
@@ -419,7 +436,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
diff --git a/tests/manual/qssloptions/main.cpp b/tests/manual/qssloptions/main.cpp
new file mode 100644
index 0000000..6f2f361
--- /dev/null
+++ b/tests/manual/qssloptions/main.cpp
@@ -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 == QLatin1String("enable_empty_fragments"))
+ config.setSslOption(QSsl::SslOptionDisableEmptyFragments, false);
+ else if (option == QLatin1String("disable_session_tickets"))
+ config.setSslOption(QSsl::SslOptionDisableSessionTickets, true);
+ else if (option == QLatin1String("disable_compression"))
+ config.setSslOption(QSsl::SslOptionDisableCompression, true);
+ else if (option == QLatin1String("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;
+}
diff --git a/tests/manual/qssloptions/qssloptions.pro b/tests/manual/qssloptions/qssloptions.pro
new file mode 100644
index 0000000..c1c8446
--- /dev/null
+++ b/tests/manual/qssloptions/qssloptions.pro
@@ -0,0 +1,12 @@
+load(qttest_p4)
+TEMPLATE = app
+TARGET = tst_qssloptions
+DEPENDPATH += .
+INCLUDEPATH += .
+
+QT -= gui
+QT += network
+
+#CONFIG += release
+
+SOURCES += main.cpp