qt5base-lts/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
Tor Arne Vestbø eb2a7738a4 Skip instead of fail tests when test server is not available
We were being inconsistent in how we handled this, some tests skipping
while others using QVERIFY. It makes more sense to skip the tests, since
the problem is a missing pre-condition of the test, not the test itself
being bad or exposing real failures in the implementation.

Change-Id: I20eacfe12dbce0b0d926e48cbe2d2772819fa4a5
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
2020-01-14 00:15:16 +01:00

4445 lines
160 KiB
C++

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2014 Governikus GmbH & Co. KG.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtCore/qglobal.h>
#include <QtCore/qthread.h>
#include <QtCore/qelapsedtimer.h>
#include <QtNetwork/qhostaddress.h>
#include <QtNetwork/qhostinfo.h>
#include <QtNetwork/qnetworkproxy.h>
#include <QtNetwork/qsslcipher.h>
#include <QtNetwork/qsslconfiguration.h>
#include <QtNetwork/qsslkey.h>
#include <QtNetwork/qsslsocket.h>
#include <QtNetwork/qtcpserver.h>
#include <QtNetwork/qsslpresharedkeyauthenticator.h>
#include <QtTest/QtTest>
#include <QNetworkProxy>
#include <QAuthenticator>
#include "private/qhostinfo_p.h"
#include "private/qiodevice_p.h" // for QIODEVICE_BUFFERSIZE
#include "../../../network-settings.h"
#ifndef QT_NO_SSL
#ifndef QT_NO_OPENSSL
#include "private/qsslsocket_openssl_p.h"
#include "private/qsslsocket_openssl_symbols_p.h"
#endif
#include "private/qsslsocket_p.h"
#include "private/qsslconfiguration_p.h"
Q_DECLARE_METATYPE(QSslSocket::SslMode)
typedef QVector<QSslError::SslError> SslErrorList;
Q_DECLARE_METATYPE(SslErrorList)
Q_DECLARE_METATYPE(QSslError)
Q_DECLARE_METATYPE(QSslKey)
Q_DECLARE_METATYPE(QSsl::SslProtocol)
Q_DECLARE_METATYPE(QSslSocket::PeerVerifyMode);
typedef QSharedPointer<QSslSocket> QSslSocketPtr;
// Non-OpenSSL backends are not able to report a specific error code
// for self-signed certificates.
#ifndef QT_NO_OPENSSL
#define FLUKE_CERTIFICATE_ERROR QSslError::SelfSignedCertificate
#else
#define FLUKE_CERTIFICATE_ERROR QSslError::CertificateUntrusted
#endif
#endif // QT_NO_SSL
// Detect ALPN (Application-Layer Protocol Negotiation) support
#undef ALPN_SUPPORTED // Undef the variable first to be safe
#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(OPENSSL_NO_TLSEXT)
#define ALPN_SUPPORTED 1
#endif
#if QT_CONFIG(schannel) && !defined(Q_CC_MINGW)
#define ALPN_SUPPORTED 1
#endif
#if defined Q_OS_HPUX && defined Q_CC_GNU
// This error is delivered every time we try to use the fluke CA
// certificate. For now we work around this bug. Task 202317.
#define QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
#endif
// Use this cipher to force PSK key sharing.
// Also, it's a cipher w/o auth, to check that we emit the signals warning
// about the identity of the peer.
static const QString PSK_CIPHER_WITHOUT_AUTH = QStringLiteral("PSK-AES256-CBC-SHA");
static const quint16 PSK_SERVER_PORT = 4433;
static const QByteArray PSK_CLIENT_PRESHAREDKEY = QByteArrayLiteral("\x1a\x2b\x3c\x4d\x5e\x6f");
static const QByteArray PSK_SERVER_IDENTITY_HINT = QByteArrayLiteral("QtTestServerHint");
static const QByteArray PSK_CLIENT_IDENTITY = QByteArrayLiteral("Client_identity");
class tst_QSslSocket : public QObject
{
Q_OBJECT
int proxyAuthCalled;
public:
tst_QSslSocket();
static void enterLoop(int secs)
{
++loopLevel;
QTestEventLoop::instance().enterLoop(secs);
}
static bool timeout()
{
return QTestEventLoop::instance().timeout();
}
#ifndef QT_NO_SSL
QSslSocketPtr newSocket();
#ifndef QT_NO_OPENSSL
enum PskConnectTestType {
PskConnectDoNotHandlePsk,
PskConnectEmptyCredentials,
PskConnectWrongCredentials,
PskConnectWrongIdentity,
PskConnectWrongPreSharedKey,
PskConnectRightCredentialsPeerVerifyFailure,
PskConnectRightCredentialsVerifyPeer,
PskConnectRightCredentialsDoNotVerifyPeer,
};
#endif
#endif
public slots:
void initTestCase_data();
void initTestCase();
void init();
void cleanup();
#ifndef QT_NO_NETWORKPROXY
void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth);
#endif
#ifndef QT_NO_SSL
private slots:
void constructing();
void simpleConnect();
void simpleConnectWithIgnore();
// API tests
void sslErrors_data();
void sslErrors();
void addCaCertificate();
void addCaCertificates();
void addCaCertificates2();
void ciphers();
void connectToHostEncrypted();
void connectToHostEncryptedWithVerificationPeerName();
void sessionCipher();
void flush();
void isEncrypted();
void localCertificate();
void mode();
void peerCertificate();
void peerCertificateChain();
void privateKey();
#ifndef QT_NO_OPENSSL
void privateKeyOpaque();
#endif
void protocol();
void protocolServerSide_data();
void protocolServerSide();
#ifndef QT_NO_OPENSSL
void serverCipherPreferences();
#endif // QT_NO_OPENSSL
void setCaCertificates();
void setLocalCertificate();
void localCertificateChain();
void setLocalCertificateChain();
void setPrivateKey();
void setSocketDescriptor();
void setSslConfiguration_data();
void setSslConfiguration();
void waitForEncrypted();
void waitForEncryptedMinusOne();
void waitForConnectedEncryptedReadyRead();
void startClientEncryption();
void startServerEncryption();
void addDefaultCaCertificate();
void addDefaultCaCertificates();
void addDefaultCaCertificates2();
void defaultCaCertificates();
void defaultCiphers();
void resetDefaultCiphers();
void setDefaultCaCertificates();
void setDefaultCiphers();
void supportedCiphers();
void systemCaCertificates();
void wildcardCertificateNames();
void isMatchingHostname();
void wildcard();
void setEmptyKey();
void spontaneousWrite();
void setReadBufferSize();
void setReadBufferSize_task_250027();
void waitForMinusOne();
void verifyMode();
void verifyDepth();
void disconnectFromHostWhenConnecting();
void disconnectFromHostWhenConnected();
#ifndef QT_NO_OPENSSL
void closeWhileEmittingSocketError();
#endif
void resetProxy();
void ignoreSslErrorsList_data();
void ignoreSslErrorsList();
void ignoreSslErrorsListWithSlot_data();
void ignoreSslErrorsListWithSlot();
void abortOnSslErrors();
void readFromClosedSocket();
void writeBigChunk();
void blacklistedCertificates();
void versionAccessors();
#ifndef QT_NO_OPENSSL
void sslOptions();
#endif
void encryptWithoutConnecting();
void resume_data();
void resume();
void qtbug18498_peek();
void qtbug18498_peek2();
void dhServer();
#ifndef QT_NO_OPENSSL
void dhServerCustomParamsNull();
void dhServerCustomParams();
#endif
void ecdhServer();
void verifyClientCertificate_data();
void verifyClientCertificate();
void readBufferMaxSize();
void allowedProtocolNegotiation();
#ifndef QT_NO_OPENSSL
void simplePskConnect_data();
void simplePskConnect();
void ephemeralServerKey_data();
void ephemeralServerKey();
void pskServer();
void forwardReadChannelFinished();
void signatureAlgorithm_data();
void signatureAlgorithm();
#endif
void disabledProtocols_data();
void disabledProtocols();
void oldErrorsOnSocketReuse();
void setEmptyDefaultConfiguration(); // this test should be last
protected slots:
static void exitLoop()
{
// Safe exit - if we aren't in an event loop, don't
// exit one.
if (loopLevel > 0) {
--loopLevel;
QTestEventLoop::instance().exitLoop();
}
}
void ignoreErrorSlot()
{
socket->ignoreSslErrors();
}
void abortOnErrorSlot()
{
QSslSocket *sock = static_cast<QSslSocket *>(sender());
sock->abort();
}
void untrustedWorkaroundSlot(const QList<QSslError> &errors)
{
if (errors.size() == 1 &&
(errors.first().error() == QSslError::CertificateUntrusted ||
errors.first().error() == QSslError::SelfSignedCertificate))
socket->ignoreSslErrors();
}
void ignoreErrorListSlot(const QList<QSslError> &errors);
private:
QSslSocket *socket;
QList<QSslError> storedExpectedSslErrors;
#endif // QT_NO_SSL
private:
static int loopLevel;
public:
static QString testDataDir;
};
QString tst_QSslSocket::testDataDir;
#ifndef QT_NO_SSL
#ifndef QT_NO_OPENSSL
Q_DECLARE_METATYPE(tst_QSslSocket::PskConnectTestType)
#endif
#endif
int tst_QSslSocket::loopLevel = 0;
namespace {
QString httpServerCertChainPath()
{
// DOCKERTODO: note how we use CA certificate on the real server. The docker container
// is using a different cert with a "special" CN. Check if it's important!
#ifdef QT_TEST_SERVER
return tst_QSslSocket::testDataDir + QStringLiteral("certs/qt-test-server-cert.pem");
#else
return tst_QSslSocket::testDataDir + QStringLiteral("certs/qt-test-server-cacert.pem");
#endif // QT_TEST_SERVER
}
} // unnamed namespace
tst_QSslSocket::tst_QSslSocket()
{
#ifndef QT_NO_SSL
qRegisterMetaType<QList<QSslError> >("QList<QSslError>");
qRegisterMetaType<QSslError>("QSslError");
qRegisterMetaType<QAbstractSocket::SocketState>("QAbstractSocket::SocketState");
qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
#ifndef QT_NO_OPENSSL
qRegisterMetaType<QSslPreSharedKeyAuthenticator *>();
qRegisterMetaType<tst_QSslSocket::PskConnectTestType>();
#endif
#endif
}
enum ProxyTests {
NoProxy = 0x00,
Socks5Proxy = 0x01,
HttpProxy = 0x02,
TypeMask = 0x0f,
NoAuth = 0x00,
AuthBasic = 0x10,
AuthNtlm = 0x20,
AuthMask = 0xf0
};
void tst_QSslSocket::initTestCase_data()
{
QTest::addColumn<bool>("setProxy");
QTest::addColumn<int>("proxyType");
QTest::newRow("WithoutProxy") << false << 0;
QTest::newRow("WithSocks5Proxy") << true << int(Socks5Proxy);
QTest::newRow("WithSocks5ProxyAuth") << true << int(Socks5Proxy | AuthBasic);
QTest::newRow("WithHttpProxy") << true << int(HttpProxy);
QTest::newRow("WithHttpProxyBasicAuth") << true << int(HttpProxy | AuthBasic);
// uncomment the line below when NTLM works
// QTest::newRow("WithHttpProxyNtlmAuth") << true << int(HttpProxy | AuthNtlm);
}
void tst_QSslSocket::initTestCase()
{
testDataDir = QFileInfo(QFINDTESTDATA("certs")).absolutePath();
if (testDataDir.isEmpty())
testDataDir = QCoreApplication::applicationDirPath();
if (!testDataDir.endsWith(QLatin1String("/")))
testDataDir += QLatin1String("/");
#ifndef QT_NO_SSL
qDebug("Using SSL library %s (%ld)",
qPrintable(QSslSocket::sslLibraryVersionString()),
QSslSocket::sslLibraryVersionNumber());
#ifdef QT_TEST_SERVER
QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::socksProxyServerName(), 1080));
QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::socksProxyServerName(), 1081));
QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpProxyServerName(), 3128));
QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpProxyServerName(), 3129));
QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpProxyServerName(), 3130));
QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpServerName(), 443));
QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::imapServerName(), 993));
QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::echoServerName(), 13));
#else
if (!QtNetworkSettings::verifyTestNetworkSettings())
QSKIP("No network test server available");
#endif // QT_TEST_SERVER
#endif // QT_NO_SSL
}
void tst_QSslSocket::init()
{
QFETCH_GLOBAL(bool, setProxy);
if (setProxy) {
#ifndef QT_NO_NETWORKPROXY
QFETCH_GLOBAL(int, proxyType);
const QString socksProxyAddr = QtNetworkSettings::socksProxyServerIp().toString();
const QString httpProxyAddr = QtNetworkSettings::httpProxyServerIp().toString();
QNetworkProxy proxy;
switch (proxyType) {
case Socks5Proxy:
proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, socksProxyAddr, 1080);
break;
case Socks5Proxy | AuthBasic:
proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, socksProxyAddr, 1081);
break;
case HttpProxy | NoAuth:
proxy = QNetworkProxy(QNetworkProxy::HttpProxy, httpProxyAddr, 3128);
break;
case HttpProxy | AuthBasic:
proxy = QNetworkProxy(QNetworkProxy::HttpProxy, httpProxyAddr, 3129);
break;
case HttpProxy | AuthNtlm:
proxy = QNetworkProxy(QNetworkProxy::HttpProxy, httpProxyAddr, 3130);
break;
}
QNetworkProxy::setApplicationProxy(proxy);
#else // !QT_NO_NETWORKPROXY
QSKIP("No proxy support");
#endif // QT_NO_NETWORKPROXY
}
qt_qhostinfo_clear_cache();
}
void tst_QSslSocket::cleanup()
{
#ifndef QT_NO_NETWORKPROXY
QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy);
#endif
}
#ifndef QT_NO_SSL
QSslSocketPtr tst_QSslSocket::newSocket()
{
const auto socket = QSslSocketPtr::create();
proxyAuthCalled = 0;
connect(socket.data(), SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
Qt::DirectConnection);
return socket;
}
#endif
#ifndef QT_NO_NETWORKPROXY
void tst_QSslSocket::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth)
{
++proxyAuthCalled;
auth->setUser("qsockstest");
auth->setPassword("password");
}
#endif // !QT_NO_NETWORKPROXY
#ifndef QT_NO_SSL
void tst_QSslSocket::constructing()
{
const char readNotOpenMessage[] = "QIODevice::read (QSslSocket): device not open";
const char writeNotOpenMessage[] = "QIODevice::write (QSslSocket): device not open";
if (!QSslSocket::supportsSsl())
return;
QSslSocket socket;
QCOMPARE(socket.state(), QSslSocket::UnconnectedState);
QCOMPARE(socket.mode(), QSslSocket::UnencryptedMode);
QVERIFY(!socket.isEncrypted());
QCOMPARE(socket.bytesAvailable(), qint64(0));
QCOMPARE(socket.bytesToWrite(), qint64(0));
QVERIFY(!socket.canReadLine());
QVERIFY(socket.atEnd());
QCOMPARE(socket.localCertificate(), QSslCertificate());
QCOMPARE(socket.sslConfiguration(), QSslConfiguration::defaultConfiguration());
QCOMPARE(socket.errorString(), QString("Unknown error"));
char c = '\0';
QTest::ignoreMessage(QtWarningMsg, readNotOpenMessage);
QVERIFY(!socket.getChar(&c));
QCOMPARE(c, '\0');
QVERIFY(!socket.isOpen());
QVERIFY(!socket.isReadable());
QVERIFY(socket.isSequential());
QVERIFY(!socket.isTextModeEnabled());
QVERIFY(!socket.isWritable());
QCOMPARE(socket.openMode(), QIODevice::NotOpen);
QTest::ignoreMessage(QtWarningMsg, readNotOpenMessage);
QVERIFY(socket.peek(2).isEmpty());
QCOMPARE(socket.pos(), qint64(0));
QTest::ignoreMessage(QtWarningMsg, writeNotOpenMessage);
QVERIFY(!socket.putChar('c'));
QTest::ignoreMessage(QtWarningMsg, readNotOpenMessage);
QVERIFY(socket.read(2).isEmpty());
QTest::ignoreMessage(QtWarningMsg, readNotOpenMessage);
QCOMPARE(socket.read(0, 0), qint64(-1));
QTest::ignoreMessage(QtWarningMsg, readNotOpenMessage);
QVERIFY(socket.readAll().isEmpty());
QTest::ignoreMessage(QtWarningMsg, "QIODevice::readLine (QSslSocket): Called with maxSize < 2");
QCOMPARE(socket.readLine(0, 0), qint64(-1));
char buf[10];
QCOMPARE(socket.readLine(buf, sizeof(buf)), qint64(-1));
QTest::ignoreMessage(QtWarningMsg, "QIODevice::seek (QSslSocket): Cannot call seek on a sequential device");
QVERIFY(!socket.reset());
QTest::ignoreMessage(QtWarningMsg, "QIODevice::seek (QSslSocket): Cannot call seek on a sequential device");
QVERIFY(!socket.seek(2));
QCOMPARE(socket.size(), qint64(0));
QVERIFY(!socket.waitForBytesWritten(10));
QVERIFY(!socket.waitForReadyRead(10));
QTest::ignoreMessage(QtWarningMsg, writeNotOpenMessage);
QCOMPARE(socket.write(0, 0), qint64(-1));
QTest::ignoreMessage(QtWarningMsg, writeNotOpenMessage);
QCOMPARE(socket.write(QByteArray()), qint64(-1));
QCOMPARE(socket.error(), QAbstractSocket::UnknownSocketError);
QVERIFY(!socket.flush());
QVERIFY(!socket.isValid());
QCOMPARE(socket.localAddress(), QHostAddress());
QCOMPARE(socket.localPort(), quint16(0));
QCOMPARE(socket.peerAddress(), QHostAddress());
QVERIFY(socket.peerName().isEmpty());
QCOMPARE(socket.peerPort(), quint16(0));
#ifndef QT_NO_NETWORKPROXY
QCOMPARE(socket.proxy().type(), QNetworkProxy::DefaultProxy);
#endif
QCOMPARE(socket.readBufferSize(), qint64(0));
QCOMPARE(socket.socketDescriptor(), (qintptr)-1);
QCOMPARE(socket.socketType(), QAbstractSocket::TcpSocket);
QVERIFY(!socket.waitForConnected(10));
QTest::ignoreMessage(QtWarningMsg, "QSslSocket::waitForDisconnected() is not allowed in UnconnectedState");
QVERIFY(!socket.waitForDisconnected(10));
QCOMPARE(socket.protocol(), QSsl::SecureProtocols);
QSslConfiguration savedDefault = QSslConfiguration::defaultConfiguration();
// verify that changing the default config doesn't affect this socket
// (on Unix, the ca certs might be empty, depending on whether we load
// them on demand or not, so set them explicitly)
#if QT_DEPRECATED_SINCE(5, 5)
socket.setCaCertificates(QSslSocket::systemCaCertificates());
QSslSocket::setDefaultCaCertificates(QList<QSslCertificate>());
QSslSocket::setDefaultCiphers(QList<QSslCipher>());
QVERIFY(!socket.caCertificates().isEmpty());
QVERIFY(!socket.ciphers().isEmpty());
// verify the default as well:
QVERIFY(QSslConfiguration::defaultConfiguration().caCertificates().isEmpty());
QVERIFY(QSslConfiguration::defaultConfiguration().ciphers().isEmpty());
QSslConfiguration::setDefaultConfiguration(savedDefault);
#endif
auto sslConfig = socket.sslConfiguration();
sslConfig.setCaCertificates(QSslConfiguration::systemCaCertificates());
socket.setSslConfiguration(sslConfig);
auto defaultConfig = QSslConfiguration::defaultConfiguration();
defaultConfig.setCaCertificates(QList<QSslCertificate>());
defaultConfig.setCiphers(QList<QSslCipher>());
QSslConfiguration::setDefaultConfiguration(defaultConfig);
QVERIFY(!socket.sslConfiguration().caCertificates().isEmpty());
QVERIFY(!socket.sslConfiguration().ciphers().isEmpty());
// verify the default as well:
QVERIFY(QSslConfiguration::defaultConfiguration().caCertificates().isEmpty());
QVERIFY(QSslConfiguration::defaultConfiguration().ciphers().isEmpty());
QSslConfiguration::setDefaultConfiguration(savedDefault);
}
void tst_QSslSocket::simpleConnect()
{
if (!QSslSocket::supportsSsl())
return;
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
QSslSocket socket;
QSignalSpy connectedSpy(&socket, SIGNAL(connected()));
QSignalSpy hostFoundSpy(&socket, SIGNAL(hostFound()));
QSignalSpy disconnectedSpy(&socket, SIGNAL(disconnected()));
QSignalSpy connectionEncryptedSpy(&socket, SIGNAL(encrypted()));
QSignalSpy sslErrorsSpy(&socket, SIGNAL(sslErrors(QList<QSslError>)));
connect(&socket, SIGNAL(connected()), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(disconnected()), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(modeChanged(QSslSocket::SslMode)), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(encrypted()), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(exitLoop()));
// Start connecting
socket.connectToHost(QtNetworkSettings::imapServerName(), 993);
QCOMPARE(socket.state(), QAbstractSocket::HostLookupState);
enterLoop(10);
// Entered connecting state
QCOMPARE(socket.state(), QAbstractSocket::ConnectingState);
QCOMPARE(connectedSpy.count(), 0);
QCOMPARE(hostFoundSpy.count(), 1);
QCOMPARE(disconnectedSpy.count(), 0);
enterLoop(10);
// Entered connected state
QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
QCOMPARE(socket.mode(), QSslSocket::UnencryptedMode);
QVERIFY(!socket.isEncrypted());
QCOMPARE(connectedSpy.count(), 1);
QCOMPARE(hostFoundSpy.count(), 1);
QCOMPARE(disconnectedSpy.count(), 0);
// Enter encrypted mode
socket.startClientEncryption();
QCOMPARE(socket.mode(), QSslSocket::SslClientMode);
QVERIFY(!socket.isEncrypted());
QCOMPARE(connectionEncryptedSpy.count(), 0);
QCOMPARE(sslErrorsSpy.count(), 0);
// Starting handshake
enterLoop(10);
QCOMPARE(sslErrorsSpy.count(), 1);
QCOMPARE(connectionEncryptedSpy.count(), 0);
QVERIFY(!socket.isEncrypted());
QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState);
}
void tst_QSslSocket::simpleConnectWithIgnore()
{
if (!QSslSocket::supportsSsl())
return;
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
QSslSocket socket;
this->socket = &socket;
QSignalSpy encryptedSpy(&socket, SIGNAL(encrypted()));
QSignalSpy sslErrorsSpy(&socket, SIGNAL(sslErrors(QList<QSslError>)));
connect(&socket, SIGNAL(readyRead()), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(encrypted()), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(connected()), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
connect(&socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(exitLoop()));
// Start connecting
socket.connectToHost(QtNetworkSettings::imapServerName(), 993);
QVERIFY(socket.state() != QAbstractSocket::UnconnectedState); // something must be in progress
enterLoop(10);
// Start handshake
QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
socket.startClientEncryption();
enterLoop(10);
// Done; encryption should be enabled.
QCOMPARE(sslErrorsSpy.count(), 1);
QVERIFY(socket.isEncrypted());
QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
QCOMPARE(encryptedSpy.count(), 1);
// Wait for incoming data
if (!socket.canReadLine())
enterLoop(10);
QByteArray data = socket.readAll();
socket.disconnectFromHost();
QVERIFY2(QtNetworkSettings::compareReplyIMAPSSL(data), data.constData());
}
void tst_QSslSocket::sslErrors_data()
{
QTest::addColumn<QString>("host");
QTest::addColumn<int>("port");
QString name = QtNetworkSettings::serverLocalName();
QTest::newRow(qPrintable(name)) << name << 993;
name = QtNetworkSettings::httpServerIp().toString();
QTest::newRow(qPrintable(name)) << name << 443;
}
void tst_QSslSocket::sslErrors()
{
QFETCH(QString, host);
QFETCH(int, port);
QSslSocketPtr socket = newSocket();
#if QT_CONFIG(schannel)
// Needs to be < 1.2 because of the old certificate and <= 1.0 because of the mail server
socket->setProtocol(QSsl::SslProtocol::TlsV1_0);
#endif
QSignalSpy sslErrorsSpy(socket.data(), SIGNAL(sslErrors(QList<QSslError>)));
QSignalSpy peerVerifyErrorSpy(socket.data(), SIGNAL(peerVerifyError(QSslError)));
#ifdef QT_TEST_SERVER
// On the old test server we had the same certificate on different services.
// The idea of this test is to fail with 'HostNameMismatch', when we're using
// either serverLocalName() or IP address directly. With Docker we connect
// to IMAP server, and we have to connect using imapServerName() and passing
// 'host' as peerVerificationName to the overload of connectToHostEncrypted().
if (port == 993) {
socket->connectToHostEncrypted(QtNetworkSettings::imapServerName(), port, host);
} else
#endif // QT_TEST_SERVER
{
socket->connectToHostEncrypted(host, port);
}
if (!socket->waitForConnected())
QSKIP("Skipping flaky test - See QTBUG-29941");
socket->waitForEncrypted(10000);
// check the SSL errors contain HostNameMismatch and an error due to
// the certificate being self-signed
SslErrorList sslErrors;
const auto socketSslErrors = socket->sslErrors();
for (const QSslError &err : socketSslErrors)
sslErrors << err.error();
std::sort(sslErrors.begin(), sslErrors.end());
QVERIFY(sslErrors.contains(QSslError::HostNameMismatch));
QVERIFY(sslErrors.contains(FLUKE_CERTIFICATE_ERROR));
// check the same errors were emitted by sslErrors
QVERIFY(!sslErrorsSpy.isEmpty());
SslErrorList emittedErrors;
const auto sslErrorsSpyErrors = qvariant_cast<QList<QSslError> >(qAsConst(sslErrorsSpy).first().first());
for (const QSslError &err : sslErrorsSpyErrors)
emittedErrors << err.error();
std::sort(emittedErrors.begin(), emittedErrors.end());
QCOMPARE(sslErrors, emittedErrors);
// check the same errors were emitted by peerVerifyError
QVERIFY(!peerVerifyErrorSpy.isEmpty());
SslErrorList peerErrors;
const QList<QVariantList> &peerVerifyList = peerVerifyErrorSpy;
for (const QVariantList &args : peerVerifyList)
peerErrors << qvariant_cast<QSslError>(args.first()).error();
std::sort(peerErrors.begin(), peerErrors.end());
QCOMPARE(sslErrors, peerErrors);
}
void tst_QSslSocket::addCaCertificate()
{
if (!QSslSocket::supportsSsl())
return;
}
void tst_QSslSocket::addCaCertificates()
{
if (!QSslSocket::supportsSsl())
return;
}
void tst_QSslSocket::addCaCertificates2()
{
if (!QSslSocket::supportsSsl())
return;
}
void tst_QSslSocket::ciphers()
{
if (!QSslSocket::supportsSsl())
return;
#if QT_DEPRECATED_SINCE(5, 5)
{
QSslSocket socket;
QCOMPARE(socket.ciphers(), QSslSocket::defaultCiphers());
socket.setCiphers(QList<QSslCipher>());
QVERIFY(socket.ciphers().isEmpty());
socket.setCiphers(socket.defaultCiphers());
QCOMPARE(socket.ciphers(), QSslSocket::defaultCiphers());
socket.setCiphers(socket.defaultCiphers());
QCOMPARE(socket.ciphers(), QSslSocket::defaultCiphers());
// Task 164356
socket.setCiphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
}
#endif
QSslSocket socket;
QCOMPARE(socket.sslConfiguration().ciphers(), QSslConfiguration::defaultConfiguration().ciphers());
auto sslConfig = socket.sslConfiguration();
sslConfig.setCiphers(QList<QSslCipher>());
socket.setSslConfiguration(sslConfig);
QVERIFY(socket.sslConfiguration().ciphers().isEmpty());
sslConfig.setCiphers(QSslConfiguration::defaultConfiguration().ciphers());
socket.setSslConfiguration(sslConfig);
QCOMPARE(socket.sslConfiguration().ciphers(), QSslConfiguration::defaultConfiguration().ciphers());
sslConfig.setCiphers(QSslConfiguration::defaultConfiguration().ciphers());
socket.setSslConfiguration(sslConfig);
QCOMPARE(socket.sslConfiguration().ciphers(), QSslConfiguration::defaultConfiguration().ciphers());
// Task 164356
sslConfig.setCiphers({QSslCipher("ALL"), QSslCipher("!ADH"), QSslCipher("!LOW"),
QSslCipher("!EXP"), QSslCipher("!MD5"), QSslCipher("@STRENGTH")});
socket.setSslConfiguration(sslConfig);
}
void tst_QSslSocket::connectToHostEncrypted()
{
if (!QSslSocket::supportsSsl())
return;
QSslSocketPtr socket = newSocket();
#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2
socket->setProtocol(QSsl::SslProtocol::TlsV1_1);
#endif
this->socket = socket.data();
QVERIFY(socket->addCaCertificates(httpServerCertChainPath()));
#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
connect(socket.data(), SIGNAL(sslErrors(QList<QSslError>)),
this, SLOT(untrustedWorkaroundSlot(QList<QSslError>)));
#endif
socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
// This should pass unconditionally when using fluke's CA certificate.
// or use untrusted certificate workaround
QFETCH_GLOBAL(bool, setProxy);
if (setProxy && !socket->waitForEncrypted(10000))
QSKIP("Skipping flaky test - See QTBUG-29941");
socket->disconnectFromHost();
QVERIFY(socket->waitForDisconnected());
QCOMPARE(socket->mode(), QSslSocket::SslClientMode);
socket->connectToHost(QtNetworkSettings::echoServerName(), 13);
QCOMPARE(socket->mode(), QSslSocket::UnencryptedMode);
QVERIFY(socket->waitForDisconnected());
}
void tst_QSslSocket::connectToHostEncryptedWithVerificationPeerName()
{
if (!QSslSocket::supportsSsl())
return;
QSslSocketPtr socket = newSocket();
#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2
socket->setProtocol(QSsl::SslProtocol::TlsV1_1);
#endif
this->socket = socket.data();
socket->addCaCertificates(httpServerCertChainPath());
#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
connect(socket.data(), SIGNAL(sslErrors(QList<QSslError>)),
this, SLOT(untrustedWorkaroundSlot(QList<QSslError>)));
#endif
#ifdef QT_TEST_SERVER
socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443, QtNetworkSettings::httpServerName());
#else
// Connect to the server with its local name, but use the full name for verification.
socket->connectToHostEncrypted(QtNetworkSettings::serverLocalName(), 443, QtNetworkSettings::httpServerName());
#endif
// This should pass unconditionally when using fluke's CA certificate.
QFETCH_GLOBAL(bool, setProxy);
if (setProxy && !socket->waitForEncrypted(10000))
QSKIP("Skipping flaky test - See QTBUG-29941");
socket->disconnectFromHost();
QVERIFY(socket->waitForDisconnected());
QCOMPARE(socket->mode(), QSslSocket::SslClientMode);
}
void tst_QSslSocket::sessionCipher()
{
if (!QSslSocket::supportsSsl())
return;
QSslSocketPtr socket = newSocket();
this->socket = socket.data();
connect(socket.data(), SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
QVERIFY(socket->sessionCipher().isNull());
socket->connectToHost(QtNetworkSettings::httpServerName(), 443 /* https */);
QVERIFY2(socket->waitForConnected(10000), qPrintable(socket->errorString()));
QVERIFY(socket->sessionCipher().isNull());
socket->startClientEncryption();
if (!socket->waitForEncrypted(5000))
QSKIP("Skipping flaky test - See QTBUG-29941");
QVERIFY(!socket->sessionCipher().isNull());
qDebug() << "Supported Ciphers:" << QSslConfiguration::supportedCiphers();
qDebug() << "Default Ciphers:" << QSslConfiguration::defaultConfiguration().ciphers();
qDebug() << "Session Cipher:" << socket->sessionCipher();
#if QT_DEPRECATED_SINCE(5, 5)
QVERIFY(QSslSocket::supportedCiphers().contains(socket->sessionCipher()));
#endif
QVERIFY(QSslConfiguration::supportedCiphers().contains(socket->sessionCipher()));
socket->disconnectFromHost();
QVERIFY(socket->waitForDisconnected());
}
void tst_QSslSocket::flush()
{
}
void tst_QSslSocket::isEncrypted()
{
}
void tst_QSslSocket::localCertificate()
{
if (!QSslSocket::supportsSsl())
return;
// This test does not make 100% sense yet. We just set some local CA/cert/key and use it
// to authenticate ourselves against the server. The server does not actually check this
// values. This test should just run the codepath inside qsslsocket_openssl.cpp
QSslSocketPtr socket = newSocket();
QList<QSslCertificate> localCert = QSslCertificate::fromPath(httpServerCertChainPath());
auto sslConfig = socket->sslConfiguration();
sslConfig.setCaCertificates(localCert);
socket->setSslConfiguration(sslConfig);
socket->setLocalCertificate(testDataDir + "certs/fluke.cert");
socket->setPrivateKey(testDataDir + "certs/fluke.key");
socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
QFETCH_GLOBAL(bool, setProxy);
if (setProxy && !socket->waitForEncrypted(10000))
QSKIP("Skipping flaky test - See QTBUG-29941");
}
void tst_QSslSocket::mode()
{
}
void tst_QSslSocket::peerCertificate()
{
}
void tst_QSslSocket::peerCertificateChain()
{
if (!QSslSocket::supportsSsl())
return;
QSKIP("QTBUG-29941 - Unstable auto-test due to intermittently unreachable host");
QSslSocketPtr socket = newSocket();
this->socket = socket.data();
QList<QSslCertificate> caCertificates = QSslCertificate::fromPath(httpServerCertChainPath());
QCOMPARE(caCertificates.count(), 1);
socket->addCaCertificates(caCertificates);
#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
connect(socket.data(), SIGNAL(sslErrors(QList<QSslError>)),
this, SLOT(untrustedWorkaroundSlot(QList<QSslError>)));
#endif
socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
QCOMPARE(socket->mode(), QSslSocket::UnencryptedMode);
QVERIFY(socket->peerCertificateChain().isEmpty());
QFETCH_GLOBAL(bool, setProxy);
if (setProxy && !socket->waitForEncrypted(10000))
QSKIP("Skipping flaky test - See QTBUG-29941");
QList<QSslCertificate> certChain = socket->peerCertificateChain();
QVERIFY(certChain.count() > 0);
QCOMPARE(certChain.first(), socket->peerCertificate());
socket->disconnectFromHost();
QVERIFY(socket->waitForDisconnected());
// connect again to a different server
socket->connectToHostEncrypted("www.qt.io", 443);
socket->ignoreSslErrors();
QCOMPARE(socket->mode(), QSslSocket::UnencryptedMode);
QVERIFY(socket->peerCertificateChain().isEmpty());
if (setProxy && !socket->waitForEncrypted(10000))
QSKIP("Skipping flaky test - See QTBUG-29941");
QCOMPARE(socket->peerCertificateChain().first(), socket->peerCertificate());
QVERIFY(socket->peerCertificateChain() != certChain);
socket->disconnectFromHost();
QVERIFY(socket->waitForDisconnected());
// now do it again back to the original server
socket->connectToHost(QtNetworkSettings::httpServerName(), 443);
QCOMPARE(socket->mode(), QSslSocket::UnencryptedMode);
QVERIFY(socket->peerCertificateChain().isEmpty());
QVERIFY2(socket->waitForConnected(10000), qPrintable(socket->errorString()));
socket->startClientEncryption();
if (setProxy && !socket->waitForEncrypted(10000))
QSKIP("Skipping flaky test - See QTBUG-29941");
QCOMPARE(socket->peerCertificateChain().first(), socket->peerCertificate());
QCOMPARE(socket->peerCertificateChain(), certChain);
socket->disconnectFromHost();
QVERIFY(socket->waitForDisconnected());
}
void tst_QSslSocket::privateKey()
{
}
#ifndef QT_NO_OPENSSL
void tst_QSslSocket::privateKeyOpaque()
{
if (!QSslSocket::supportsSsl())
return;
QFile file(testDataDir + "certs/fluke.key");
QVERIFY(file.open(QIODevice::ReadOnly));
QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
QVERIFY(!key.isNull());
EVP_PKEY *pkey = q_EVP_PKEY_new();
q_EVP_PKEY_set1_RSA(pkey, reinterpret_cast<RSA *>(key.handle()));
// This test does not make 100% sense yet. We just set some local CA/cert/key and use it
// to authenticate ourselves against the server. The server does not actually check this
// values. This test should just run the codepath inside qsslsocket_openssl.cpp
QSslSocketPtr socket = newSocket();
QList<QSslCertificate> localCert = QSslCertificate::fromPath(httpServerCertChainPath());
auto sslConfig = socket->sslConfiguration();
sslConfig.setCaCertificates(localCert);
socket->setSslConfiguration(sslConfig);
socket->setLocalCertificate(testDataDir + "certs/fluke.cert");
socket->setPrivateKey(QSslKey(reinterpret_cast<Qt::HANDLE>(pkey)));
socket->setPeerVerifyMode(QSslSocket::QueryPeer);
socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
QFETCH_GLOBAL(bool, setProxy);
if (setProxy && !socket->waitForEncrypted(10000))
QSKIP("Skipping flaky test - See QTBUG-29941");
}
#endif
void tst_QSslSocket::protocol()
{
if (!QSslSocket::supportsSsl())
return;
QSslSocketPtr socket = newSocket();
this->socket = socket.data();
QList<QSslCertificate> certs = QSslCertificate::fromPath(httpServerCertChainPath());
auto sslConfig = socket->sslConfiguration();
sslConfig.setCaCertificates(certs);
socket->setSslConfiguration(sslConfig);
#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
connect(socket, SIGNAL(sslErrors(QList<QSslError>)),
this, SLOT(untrustedWorkaroundSlot(QList<QSslError>)));
#endif
QCOMPARE(socket->protocol(), QSsl::SecureProtocols);
QFETCH_GLOBAL(bool, setProxy);
{
// qt-test-server allows TLSV1.
socket->setProtocol(QSsl::TlsV1_0);
QCOMPARE(socket->protocol(), QSsl::TlsV1_0);
socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
if (setProxy && !socket->waitForEncrypted())
QSKIP("Skipping flaky test - See QTBUG-29941");
QCOMPARE(socket->protocol(), QSsl::TlsV1_0);
socket->abort();
QCOMPARE(socket->protocol(), QSsl::TlsV1_0);
socket->connectToHost(QtNetworkSettings::httpServerName(), 443);
QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString()));
socket->startClientEncryption();
if (setProxy && !socket->waitForEncrypted())
QSKIP("Skipping flaky test - See QTBUG-29941");
QCOMPARE(socket->protocol(), QSsl::TlsV1_0);
socket->abort();
}
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
{
// qt-test-server probably doesn't allow TLSV1.1
socket->setProtocol(QSsl::TlsV1_1);
QCOMPARE(socket->protocol(), QSsl::TlsV1_1);
socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
if (setProxy && !socket->waitForEncrypted())
QSKIP("Skipping flaky test - See QTBUG-29941");
QCOMPARE(socket->protocol(), QSsl::TlsV1_1);
socket->abort();
QCOMPARE(socket->protocol(), QSsl::TlsV1_1);
socket->connectToHost(QtNetworkSettings::httpServerName(), 443);
QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString()));
socket->startClientEncryption();
if (setProxy && !socket->waitForEncrypted())
QSKIP("Skipping flaky test - See QTBUG-29941");
QCOMPARE(socket->protocol(), QSsl::TlsV1_1);
socket->abort();
}
{
// qt-test-server probably doesn't allows TLSV1.2
socket->setProtocol(QSsl::TlsV1_2);
QCOMPARE(socket->protocol(), QSsl::TlsV1_2);
socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
if (setProxy && !socket->waitForEncrypted())
QSKIP("Skipping flaky test - See QTBUG-29941");
QCOMPARE(socket->protocol(), QSsl::TlsV1_2);
socket->abort();
QCOMPARE(socket->protocol(), QSsl::TlsV1_2);
socket->connectToHost(QtNetworkSettings::httpServerName(), 443);
QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString()));
socket->startClientEncryption();
if (setProxy && !socket->waitForEncrypted())
QSKIP("Skipping flaky test - See QTBUG-29941");
QCOMPARE(socket->protocol(), QSsl::TlsV1_2);
socket->abort();
}
#endif
#ifdef TLS1_3_VERSION
{
// qt-test-server probably doesn't allow TLSV1.3
socket->setProtocol(QSsl::TlsV1_3);
QCOMPARE(socket->protocol(), QSsl::TlsV1_3);
socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
if (setProxy && !socket->waitForEncrypted())
QSKIP("TLS 1.3 is not supported by the test server or the test is flaky - see QTBUG-29941");
QCOMPARE(socket->protocol(), QSsl::TlsV1_3);
socket->abort();
QCOMPARE(socket->protocol(), QSsl::TlsV1_3);
socket->connectToHost(QtNetworkSettings::httpServerName(), 443);
QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString()));
socket->startClientEncryption();
if (setProxy && !socket->waitForEncrypted())
QSKIP("TLS 1.3 is not supported by the test server or the test is flaky - see QTBUG-29941");
QCOMPARE(socket->sessionProtocol(), QSsl::TlsV1_3);
socket->abort();
}
#endif // TLS1_3_VERSION
{
// qt-test-server allows SSLV3, so it allows AnyProtocol.
socket->setProtocol(QSsl::AnyProtocol);
QCOMPARE(socket->protocol(), QSsl::AnyProtocol);
socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
if (setProxy && !socket->waitForEncrypted())
QSKIP("Skipping flaky test - See QTBUG-29941");
QCOMPARE(socket->protocol(), QSsl::AnyProtocol);
socket->abort();
QCOMPARE(socket->protocol(), QSsl::AnyProtocol);
socket->connectToHost(QtNetworkSettings::httpServerName(), 443);
QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString()));
socket->startClientEncryption();
if (setProxy && !socket->waitForEncrypted())
QSKIP("Skipping flaky test - See QTBUG-29941");
QCOMPARE(socket->protocol(), QSsl::AnyProtocol);
socket->abort();
}
{
// qt-test-server allows TlsV1, so it allows TlsV1SslV3
socket->setProtocol(QSsl::TlsV1SslV3);
QCOMPARE(socket->protocol(), QSsl::TlsV1SslV3);
socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
if (setProxy && !socket->waitForEncrypted())
QSKIP("Skipping flaky test - See QTBUG-29941");
QCOMPARE(socket->protocol(), QSsl::TlsV1SslV3);
socket->abort();
QCOMPARE(socket->protocol(), QSsl::TlsV1SslV3);
socket->connectToHost(QtNetworkSettings::httpServerName(), 443);
if (setProxy && !socket->waitForConnected())
QSKIP("Skipping flaky test - See QTBUG-29941");
socket->startClientEncryption();
if (setProxy && !socket->waitForEncrypted())
QSKIP("Skipping flaky test - See QTBUG-29941");
QCOMPARE(socket->protocol(), QSsl::TlsV1SslV3);
socket->abort();
}
}
class SslServer : public QTcpServer
{
Q_OBJECT
public:
SslServer(const QString &keyFile = tst_QSslSocket::testDataDir + "certs/fluke.key",
const QString &certFile = tst_QSslSocket::testDataDir + "certs/fluke.cert",
const QString &interFile = QString())
: socket(0),
config(QSslConfiguration::defaultConfiguration()),
ignoreSslErrors(true),
peerVerifyMode(QSslSocket::AutoVerifyPeer),
protocol(QSsl::TlsV1_0),
m_keyFile(keyFile),
m_certFile(certFile),
m_interFile(interFile)
{ }
QSslSocket *socket;
QSslConfiguration config;
QString addCaCertificates;
bool ignoreSslErrors;
QSslSocket::PeerVerifyMode peerVerifyMode;
QSsl::SslProtocol protocol;
QString m_keyFile;
QString m_certFile;
QString m_interFile;
QList<QSslCipher> ciphers;
signals:
void socketError(QAbstractSocket::SocketError);
protected:
void incomingConnection(qintptr socketDescriptor)
{
socket = new QSslSocket(this);
socket->setSslConfiguration(config);
socket->setPeerVerifyMode(peerVerifyMode);
socket->setProtocol(protocol);
if (ignoreSslErrors)
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(socketError(QAbstractSocket::SocketError)));
QFile file(m_keyFile);
QVERIFY(file.open(QIODevice::ReadOnly));
QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
QVERIFY(!key.isNull());
socket->setPrivateKey(key);
// Add CA certificates to verify client certificate
if (!addCaCertificates.isEmpty()) {
QList<QSslCertificate> caCert = QSslCertificate::fromPath(addCaCertificates);
QVERIFY(!caCert.isEmpty());
QVERIFY(!caCert.first().isNull());
socket->addCaCertificates(caCert);
}
// If we have a cert issued directly from the CA
if (m_interFile.isEmpty()) {
QList<QSslCertificate> localCert = QSslCertificate::fromPath(m_certFile);
QVERIFY(!localCert.isEmpty());
QVERIFY(!localCert.first().isNull());
socket->setLocalCertificate(localCert.first());
}
else {
QList<QSslCertificate> localCert = QSslCertificate::fromPath(m_certFile);
QVERIFY(!localCert.isEmpty());
QVERIFY(!localCert.first().isNull());
QList<QSslCertificate> interCert = QSslCertificate::fromPath(m_interFile);
QVERIFY(!interCert.isEmpty());
QVERIFY(!interCert.first().isNull());
socket->setLocalCertificateChain(localCert + interCert);
}
if (!ciphers.isEmpty()) {
auto sslConfig = socket->sslConfiguration();
sslConfig.setCiphers(ciphers);
socket->setSslConfiguration(sslConfig);
}
QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState));
QVERIFY(!socket->peerAddress().isNull());
QVERIFY(socket->peerPort() != 0);
QVERIFY(!socket->localAddress().isNull());
QVERIFY(socket->localPort() != 0);
socket->startServerEncryption();
}
protected slots:
void ignoreErrorSlot()
{
socket->ignoreSslErrors();
}
};
void tst_QSslSocket::protocolServerSide_data()
{
#ifdef Q_OS_WINRT
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
QTest::addColumn<QSsl::SslProtocol>("serverProtocol");
QTest::addColumn<QSsl::SslProtocol>("clientProtocol");
QTest::addColumn<bool>("works");
QTest::newRow("tls1.0-tls1.0") << QSsl::TlsV1_0 << QSsl::TlsV1_0 << true;
QTest::newRow("tls1ssl3-tls1ssl3") << QSsl::TlsV1SslV3 << QSsl::TlsV1SslV3 << true;
QTest::newRow("any-any") << QSsl::AnyProtocol << QSsl::AnyProtocol << true;
QTest::newRow("secure-secure") << QSsl::SecureProtocols << QSsl::SecureProtocols << true;
QTest::newRow("tls1-tls1ssl3") << QSsl::TlsV1_0 << QSsl::TlsV1SslV3 << true;
QTest::newRow("tls1.0-secure") << QSsl::TlsV1_0 << QSsl::SecureProtocols << true;
QTest::newRow("tls1.0-any") << QSsl::TlsV1_0 << QSsl::AnyProtocol << true;
QTest::newRow("tls1ssl3-tls1.0") << QSsl::TlsV1SslV3 << QSsl::TlsV1_0 << true;
QTest::newRow("tls1ssl3-secure") << QSsl::TlsV1SslV3 << QSsl::SecureProtocols << true;
QTest::newRow("tls1ssl3-any") << QSsl::TlsV1SslV3 << QSsl::AnyProtocol << true;
QTest::newRow("secure-tls1.0") << QSsl::SecureProtocols << QSsl::TlsV1_0 << true;
QTest::newRow("secure-tls1ssl3") << QSsl::SecureProtocols << QSsl::TlsV1SslV3 << true;
QTest::newRow("secure-any") << QSsl::SecureProtocols << QSsl::AnyProtocol << true;
QTest::newRow("tls1.0orlater-tls1.0") << QSsl::TlsV1_0OrLater << QSsl::TlsV1_0 << true;
QTest::newRow("tls1.0orlater-tls1.1") << QSsl::TlsV1_0OrLater << QSsl::TlsV1_1 << true;
QTest::newRow("tls1.0orlater-tls1.2") << QSsl::TlsV1_0OrLater << QSsl::TlsV1_2 << true;
#ifdef TLS1_3_VERSION
QTest::newRow("tls1.0orlater-tls1.3") << QSsl::TlsV1_0OrLater << QSsl::TlsV1_3 << true;
#endif
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;
#ifdef TLS1_3_VERSION
QTest::newRow("tls1.1orlater-tls1.3") << QSsl::TlsV1_1OrLater << QSsl::TlsV1_3 << true;
#endif
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;
#ifdef TLS1_3_VERSION
QTest::newRow("tls1.2orlater-tls1.3") << QSsl::TlsV1_2OrLater << QSsl::TlsV1_3 << true;
#endif
#ifdef TLS1_3_VERSION
QTest::newRow("tls1.3orlater-tls1.0") << QSsl::TlsV1_3OrLater << QSsl::TlsV1_0 << false;
QTest::newRow("tls1.3orlater-tls1.1") << QSsl::TlsV1_3OrLater << QSsl::TlsV1_1 << false;
QTest::newRow("tls1.3orlater-tls1.2") << QSsl::TlsV1_3OrLater << QSsl::TlsV1_2 << false;
QTest::newRow("tls1.3orlater-tls1.3") << QSsl::TlsV1_3OrLater << QSsl::TlsV1_3 << true;
#endif // TLS1_3_VERSION
QTest::newRow("any-tls1.0") << QSsl::AnyProtocol << QSsl::TlsV1_0 << true;
QTest::newRow("any-tls1ssl3") << QSsl::AnyProtocol << QSsl::TlsV1SslV3 << true;
QTest::newRow("any-secure") << QSsl::AnyProtocol << QSsl::SecureProtocols << true;
}
void tst_QSslSocket::protocolServerSide()
{
if (!QSslSocket::supportsSsl()) {
qWarning("SSL not supported, skipping test");
return;
}
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
QFETCH(QSsl::SslProtocol, serverProtocol);
SslServer server;
server.protocol = serverProtocol;
QVERIFY(server.listen());
QEventLoop loop;
connect(&server, SIGNAL(socketError(QAbstractSocket::SocketError)), &loop, SLOT(quit()));
QTimer::singleShot(5000, &loop, SLOT(quit()));
QSslSocket client;
socket = &client;
QFETCH(QSsl::SslProtocol, clientProtocol);
socket->setProtocol(clientProtocol);
// upon SSL wrong version error, error will be triggered, not sslErrors
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit()));
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));
client.connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
loop.exec();
QFETCH(bool, works);
QAbstractSocket::SocketState expectedState = (works) ? QAbstractSocket::ConnectedState : QAbstractSocket::UnconnectedState;
// Determine whether the client or the server caused the event loop
// to quit due to a socket error, and investigate the culprit.
if (client.error() != QAbstractSocket::UnknownSocketError) {
// It can happen that the client, after TCP connection established, before
// incomingConnection() slot fired, hits TLS initialization error and stops
// the loop, so the server socket is not created yet.
if (server.socket)
QVERIFY(server.socket->error() == QAbstractSocket::UnknownSocketError);
QCOMPARE(client.state(), expectedState);
} else if (server.socket->error() != QAbstractSocket::UnknownSocketError) {
QVERIFY(client.error() == QAbstractSocket::UnknownSocketError);
QCOMPARE(server.socket->state(), expectedState);
}
QCOMPARE(client.isEncrypted(), works);
}
#ifndef QT_NO_OPENSSL
void tst_QSslSocket::serverCipherPreferences()
{
#ifdef Q_OS_WINRT
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
if (!QSslSocket::supportsSsl()) {
qWarning("SSL not supported, skipping test");
return;
}
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
// First using the default (server preference)
{
SslServer server;
server.ciphers = {QSslCipher("AES128-SHA"), QSslCipher("AES256-SHA")};
QVERIFY(server.listen());
QEventLoop loop;
QTimer::singleShot(5000, &loop, SLOT(quit()));
QSslSocket client;
socket = &client;
auto sslConfig = socket->sslConfiguration();
sslConfig.setCiphers({QSslCipher("AES256-SHA"), QSslCipher("AES128-SHA")});
socket->setSslConfiguration(sslConfig);
// upon SSL wrong version error, error will be triggered, not sslErrors
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit()));
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));
client.connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
loop.exec();
QVERIFY(client.isEncrypted());
QCOMPARE(client.sessionCipher().name(), QString("AES128-SHA"));
}
{
// Now using the client preferences
SslServer server;
QSslConfiguration config = QSslConfiguration::defaultConfiguration();
config.setSslOption(QSsl::SslOptionDisableServerCipherPreference, true);
server.config = config;
server.ciphers = {QSslCipher("AES128-SHA"), QSslCipher("AES256-SHA")};
QVERIFY(server.listen());
QEventLoop loop;
QTimer::singleShot(5000, &loop, SLOT(quit()));
QSslSocket client;
socket = &client;
auto sslConfig = socket->sslConfiguration();
sslConfig.setCiphers({QSslCipher("AES256-SHA"), QSslCipher("AES128-SHA")});
socket->setSslConfiguration(sslConfig);
// upon SSL wrong version error, error will be triggered, not sslErrors
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit()));
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));
client.connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
loop.exec();
QVERIFY(client.isEncrypted());
QCOMPARE(client.sessionCipher().name(), QString("AES256-SHA"));
}
}
#endif // QT_NO_OPENSSL
void tst_QSslSocket::setCaCertificates()
{
if (!QSslSocket::supportsSsl())
return;
#if QT_DEPRECATED_SINCE(5, 5)
{
QSslSocket socket;
QCOMPARE(socket.caCertificates(), QSslSocket::defaultCaCertificates());
socket.setCaCertificates(QSslCertificate::fromPath(testDataDir + "certs/qt-test-server-cacert.pem"));
QCOMPARE(socket.caCertificates().size(), 1);
socket.setCaCertificates(socket.defaultCaCertificates());
QCOMPARE(socket.caCertificates(), QSslSocket::defaultCaCertificates());
}
#endif
QSslSocket socket;
QCOMPARE(socket.sslConfiguration().caCertificates(),
QSslConfiguration::defaultConfiguration().caCertificates());
auto sslConfig = socket.sslConfiguration();
sslConfig.setCaCertificates(
QSslCertificate::fromPath(testDataDir + "certs/qt-test-server-cacert.pem"));
socket.setSslConfiguration(sslConfig);
QCOMPARE(socket.sslConfiguration().caCertificates().size(), 1);
sslConfig.setCaCertificates(QSslConfiguration::defaultConfiguration().caCertificates());
socket.setSslConfiguration(sslConfig);
QCOMPARE(socket.sslConfiguration().caCertificates(),
QSslConfiguration::defaultConfiguration().caCertificates());
}
void tst_QSslSocket::setLocalCertificate()
{
}
void tst_QSslSocket::localCertificateChain()
{
if (!QSslSocket::supportsSsl())
return;
QSslSocket socket;
socket.setLocalCertificate(testDataDir + "certs/fluke.cert");
QSslConfiguration conf = socket.sslConfiguration();
QList<QSslCertificate> chain = conf.localCertificateChain();
QCOMPARE(chain.size(), 1);
QCOMPARE(chain[0], conf.localCertificate());
QCOMPARE(chain[0], socket.localCertificate());
}
void tst_QSslSocket::setLocalCertificateChain()
{
#ifdef Q_OS_WINRT
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
if (!QSslSocket::supportsSsl())
return;
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
SslServer server(testDataDir + "certs/leaf.key",
testDataDir + "certs/leaf.crt",
testDataDir + "certs/inter.crt");
QVERIFY(server.listen());
QEventLoop loop;
QTimer::singleShot(5000, &loop, SLOT(quit()));
const QScopedPointer<QSslSocket, QScopedPointerDeleteLater> client(new QSslSocket);
socket = client.data();
connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit()));
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
socket->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
loop.exec();
QList<QSslCertificate> chain = socket->peerCertificateChain();
#if QT_CONFIG(schannel)
QEXPECT_FAIL("", "Schannel cannot send intermediate certificates not "
"located in a system certificate store",
Abort);
#endif
QCOMPARE(chain.size(), 2);
QCOMPARE(chain[0].serialNumber(), QByteArray("10:a0:ad:77:58:f6:6e:ae:46:93:a3:43:f9:59:8a:9e"));
QCOMPARE(chain[1].serialNumber(), QByteArray("3b:eb:99:c5:ea:d8:0b:5d:0b:97:5d:4f:06:75:4b:e1"));
}
void tst_QSslSocket::setPrivateKey()
{
}
void tst_QSslSocket::setSocketDescriptor()
{
#ifdef Q_OS_WINRT
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
if (!QSslSocket::supportsSsl())
return;
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
SslServer server;
QVERIFY(server.listen());
QEventLoop loop;
QTimer::singleShot(5000, &loop, SLOT(quit()));
QSslSocket client;
socket = &client;
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));
client.connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
loop.exec();
QCOMPARE(client.state(), QAbstractSocket::ConnectedState);
QVERIFY(client.isEncrypted());
QVERIFY(!client.peerAddress().isNull());
QVERIFY(client.peerPort() != 0);
QVERIFY(!client.localAddress().isNull());
QVERIFY(client.localPort() != 0);
}
void tst_QSslSocket::setSslConfiguration_data()
{
QTest::addColumn<QSslConfiguration>("configuration");
QTest::addColumn<bool>("works");
QTest::newRow("empty") << QSslConfiguration() << false;
QSslConfiguration conf = QSslConfiguration::defaultConfiguration();
QTest::newRow("default") << conf << false; // does not contain test server cert
QList<QSslCertificate> testServerCert = QSslCertificate::fromPath(httpServerCertChainPath());
conf.setCaCertificates(testServerCert);
QTest::newRow("set-root-cert") << conf << true;
conf.setProtocol(QSsl::SecureProtocols);
QTest::newRow("secure") << conf << true;
}
void tst_QSslSocket::setSslConfiguration()
{
if (!QSslSocket::supportsSsl())
return;
QSslSocketPtr socket = newSocket();
QFETCH(QSslConfiguration, configuration);
socket->setSslConfiguration(configuration);
#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2
socket->setProtocol(QSsl::SslProtocol::TlsV1_1);
#endif
this->socket = socket.data();
socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
QFETCH(bool, works);
QFETCH_GLOBAL(bool, setProxy);
if (setProxy && (socket->waitForEncrypted(10000) != works))
QSKIP("Skipping flaky test - See QTBUG-29941");
if (works) {
socket->disconnectFromHost();
QVERIFY2(socket->waitForDisconnected(), qPrintable(socket->errorString()));
}
}
void tst_QSslSocket::waitForEncrypted()
{
if (!QSslSocket::supportsSsl())
return;
QSslSocketPtr socket = newSocket();
this->socket = socket.data();
connect(this->socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
QFETCH_GLOBAL(bool, setProxy);
if (setProxy && !socket->waitForEncrypted(10000))
QSKIP("Skipping flaky test - See QTBUG-29941");
}
void tst_QSslSocket::waitForEncryptedMinusOne()
{
#ifdef Q_OS_WIN
QSKIP("QTBUG-24451 - indefinite wait may hang");
#endif
if (!QSslSocket::supportsSsl())
return;
QSslSocketPtr socket = newSocket();
this->socket = socket.data();
connect(this->socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
QFETCH_GLOBAL(bool, setProxy);
if (setProxy && !socket->waitForEncrypted(-1))
QSKIP("Skipping flaky test - See QTBUG-29941");
}
void tst_QSslSocket::waitForConnectedEncryptedReadyRead()
{
if (!QSslSocket::supportsSsl())
return;
QSslSocketPtr socket = newSocket();
this->socket = socket.data();
connect(this->socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
socket->connectToHostEncrypted(QtNetworkSettings::imapServerName(), 993);
QVERIFY2(socket->waitForConnected(10000), qPrintable(socket->errorString()));
QFETCH_GLOBAL(bool, setProxy);
if (setProxy && !socket->waitForEncrypted(10000))
QSKIP("Skipping flaky test - See QTBUG-29941");
// We only do this if we have no bytes available to read already because readyRead will
// not be emitted again.
if (socket->bytesAvailable() == 0)
QVERIFY(socket->waitForReadyRead(10000));
QVERIFY(!socket->peerCertificate().isNull());
QVERIFY(!socket->peerCertificateChain().isEmpty());
}
void tst_QSslSocket::startClientEncryption()
{
}
void tst_QSslSocket::startServerEncryption()
{
}
void tst_QSslSocket::addDefaultCaCertificate()
{
if (!QSslSocket::supportsSsl())
return;
#if QT_DEPRECATED_SINCE(5, 5)
{
// Reset the global CA chain
QSslSocket::setDefaultCaCertificates(QSslSocket::systemCaCertificates());
QList<QSslCertificate> flukeCerts = QSslCertificate::fromPath(httpServerCertChainPath());
QCOMPARE(flukeCerts.size(), 1);
QList<QSslCertificate> globalCerts = QSslSocket::defaultCaCertificates();
QVERIFY(!globalCerts.contains(flukeCerts.first()));
QSslSocket::addDefaultCaCertificate(flukeCerts.first());
QCOMPARE(QSslSocket::defaultCaCertificates().size(), globalCerts.size() + 1);
QVERIFY(QSslSocket::defaultCaCertificates().contains(flukeCerts.first()));
// Restore the global CA chain
QSslSocket::setDefaultCaCertificates(QSslSocket::systemCaCertificates());
}
#endif
// Reset the global CA chain
auto sslConfig = QSslConfiguration::defaultConfiguration();
sslConfig.setCaCertificates(QSslConfiguration::systemCaCertificates());
QSslConfiguration::setDefaultConfiguration(sslConfig);
QList<QSslCertificate> flukeCerts = QSslCertificate::fromPath(httpServerCertChainPath());
QCOMPARE(flukeCerts.size(), 1);
QList<QSslCertificate> globalCerts = QSslConfiguration::defaultConfiguration().caCertificates();
QVERIFY(!globalCerts.contains(flukeCerts.first()));
QSslSocket::addDefaultCaCertificate(flukeCerts.first());
QCOMPARE(QSslConfiguration::defaultConfiguration().caCertificates().size(),
globalCerts.size() + 1);
QVERIFY(QSslConfiguration::defaultConfiguration().caCertificates()
.contains(flukeCerts.first()));
// Restore the global CA chain
sslConfig = QSslConfiguration::defaultConfiguration();
sslConfig.setCaCertificates(QSslConfiguration::systemCaCertificates());
QSslConfiguration::setDefaultConfiguration(sslConfig);
}
void tst_QSslSocket::addDefaultCaCertificates()
{
}
void tst_QSslSocket::addDefaultCaCertificates2()
{
}
void tst_QSslSocket::defaultCaCertificates()
{
if (!QSslSocket::supportsSsl())
return;
#if QT_DEPRECATED_SINCE(5, 5)
{
QList<QSslCertificate> certs = QSslSocket::defaultCaCertificates();
QVERIFY(certs.size() > 1);
QCOMPARE(certs, QSslSocket::systemCaCertificates());
}
#endif
QList<QSslCertificate> certs = QSslConfiguration::defaultConfiguration().caCertificates();
QVERIFY(certs.size() > 1);
QCOMPARE(certs, QSslConfiguration::systemCaCertificates());
}
void tst_QSslSocket::defaultCiphers()
{
if (!QSslSocket::supportsSsl())
return;
#if QT_DEPRECATED_SINCE(5, 5)
{
QList<QSslCipher> ciphers = QSslSocket::defaultCiphers();
QVERIFY(ciphers.size() > 1);
QSslSocket socket;
QCOMPARE(socket.defaultCiphers(), ciphers);
QCOMPARE(socket.ciphers(), ciphers);
}
#endif
QList<QSslCipher> ciphers = QSslConfiguration::defaultConfiguration().ciphers();
QVERIFY(ciphers.size() > 1);
QSslSocket socket;
QCOMPARE(socket.sslConfiguration().defaultConfiguration().ciphers(), ciphers);
QCOMPARE(socket.sslConfiguration().ciphers(), ciphers);
}
void tst_QSslSocket::resetDefaultCiphers()
{
}
void tst_QSslSocket::setDefaultCaCertificates()
{
}
void tst_QSslSocket::setDefaultCiphers()
{
}
void tst_QSslSocket::supportedCiphers()
{
if (!QSslSocket::supportsSsl())
return;
#if QT_DEPRECATED_SINCE(5, 5)
{
QList<QSslCipher> ciphers = QSslSocket::supportedCiphers();
QVERIFY(ciphers.size() > 1);
QSslSocket socket;
QCOMPARE(socket.supportedCiphers(), ciphers);
}
#endif
QList<QSslCipher> ciphers = QSslConfiguration::supportedCiphers();
QVERIFY(ciphers.size() > 1);
QSslSocket socket;
QCOMPARE(socket.sslConfiguration().supportedCiphers(), ciphers);
}
void tst_QSslSocket::systemCaCertificates()
{
if (!QSslSocket::supportsSsl())
return;
#if QT_DEPRECATED_SINCE(5, 5)
{
QList<QSslCertificate> certs = QSslSocket::systemCaCertificates();
QVERIFY(certs.size() > 1);
QCOMPARE(certs, QSslSocket::defaultCaCertificates());
}
#endif
QList<QSslCertificate> certs = QSslConfiguration::systemCaCertificates();
QVERIFY(certs.size() > 1);
QCOMPARE(certs, QSslConfiguration::defaultConfiguration().systemCaCertificates());
}
void tst_QSslSocket::wildcardCertificateNames()
{
// Passing CN matches
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("www.example.com"), QString("www.example.com")), true );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("WWW.EXAMPLE.COM"), QString("www.example.com")), true );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.example.com"), QString("www.example.com")), true );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("xxx*.example.com"), QString("xxxwww.example.com")), true );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("f*.example.com"), QString("foo.example.com")), true );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("192.168.0.0"), QString("192.168.0.0")), true );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("foo.éxample.com"), QString("foo.xn--xample-9ua.com")), true );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.éxample.com"), QString("foo.xn--xample-9ua.com")), true );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("xn--kcry6tjko.example.org"), QString("xn--kcry6tjko.example.org")), true);
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.xn--kcry6tjko.example.org"), QString("xn--kcr.xn--kcry6tjko.example.org")), true);
// Failing CN matches
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("xxx.example.com"), QString("www.example.com")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*"), QString("www.example.com")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.*.com"), QString("www.example.com")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.example.com"), QString("baa.foo.example.com")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("f*.example.com"), QString("baa.example.com")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.com"), QString("example.com")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*fail.com"), QString("example.com")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.example."), QString("www.example.")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.example."), QString("www.example")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString(""), QString("www")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*"), QString("www")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.168.0.0"), QString("192.168.0.0")), false );
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("xn--kcry6tjko*.example.org"), QString("xn--kcry6tjkoanc.example.org")), false ); // RFC 6125 §7.2
QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("å*.example.org"), QString("xn--la-xia.example.org")), false );
}
void tst_QSslSocket::isMatchingHostname()
{
// with normalization: (the certificate has *.SCHÄUFELE.DE as a CN)
// openssl req -x509 -nodes -subj "/CN=*.SCHÄUFELE.DE" -newkey rsa:512 -keyout /dev/null -out xn--schufele-2za.crt
QList<QSslCertificate> certs = QSslCertificate::fromPath(testDataDir + "certs/xn--schufele-2za.crt");
QVERIFY(!certs.isEmpty());
QSslCertificate cert = certs.first();
QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("WWW.SCHÄUFELE.DE")), true);
QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("www.xn--schufele-2za.de")), true);
QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("www.schäufele.de")), true);
QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("föo.schäufele.de")), true);
QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("foo.foo.xn--schufele-2za.de")), false);
QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("www.schaufele.de")), false);
QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("www.schufele.de")), false);
/* Generated with the following command (only valid with openssl >= 1.1.1 due to "-addext"):
openssl req -x509 -nodes -subj "/CN=example.org" \
-addext "subjectAltName = IP:192.5.8.16, IP:fe80::3c29:2fa1:dd44:765" \
-newkey rsa:2048 -keyout /dev/null -out subjectAltNameIP.crt
*/
certs = QSslCertificate::fromPath(testDataDir + "certs/subjectAltNameIP.crt");
QVERIFY(!certs.isEmpty());
cert = certs.first();
QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("192.5.8.16")), true);
QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("fe80::3c29:2fa1:dd44:765")), true);
/* openssl req -x509 -nodes -new -newkey rsa -keyout /dev/null -out 127-0-0-1-as-CN.crt \
-subj "/CN=127.0.0.1"
*/
certs = QSslCertificate::fromPath(testDataDir + "certs/127-0-0-1-as-CN.crt");
QVERIFY(!certs.isEmpty());
cert = certs.first();
QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("127.0.0.1")), true);
}
void tst_QSslSocket::wildcard()
{
QSKIP("TODO: solve wildcard problem");
if (!QSslSocket::supportsSsl())
return;
// Fluke runs an apache server listening on port 4443, serving the
// wildcard fluke.*.troll.no. The DNS entry for
// fluke.wildcard.dev.troll.no, served by ares (root for dev.troll.no),
// returns the CNAME fluke.troll.no for this domain. The web server
// responds with the wildcard, and QSslSocket should accept that as a
// valid connection. This was broken in 4.3.0.
QSslSocketPtr socket = newSocket();
socket->addCaCertificates(QLatin1String("certs/aspiriniks.ca.crt"));
this->socket = socket.data();
#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
connect(socket, SIGNAL(sslErrors(QList<QSslError>)),
this, SLOT(untrustedWorkaroundSlot(QList<QSslError>)));
#endif
socket->connectToHostEncrypted(QtNetworkSettings::wildcardServerName(), 4443);
QFETCH_GLOBAL(bool, setProxy);
if (setProxy && !socket->waitForEncrypted(3000))
QSKIP("Skipping flaky test - See QTBUG-29941");
QSslCertificate certificate = socket->peerCertificate();
QVERIFY(certificate.subjectInfo(QSslCertificate::CommonName).contains(QString(QtNetworkSettings::serverLocalName() + ".*." + QtNetworkSettings::serverDomainName())));
QVERIFY(certificate.issuerInfo(QSslCertificate::CommonName).contains(QtNetworkSettings::serverName()));
socket->close();
}
class SslServer2 : public QTcpServer
{
protected:
void incomingConnection(qintptr socketDescriptor)
{
QSslSocket *socket = new QSslSocket(this);
socket->ignoreSslErrors();
// Only set the certificate
QList<QSslCertificate> localCert = QSslCertificate::fromPath(tst_QSslSocket::testDataDir + "certs/fluke.cert");
QVERIFY(!localCert.isEmpty());
QVERIFY(!localCert.first().isNull());
socket->setLocalCertificate(localCert.first());
QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState));
socket->startServerEncryption();
}
};
void tst_QSslSocket::setEmptyKey()
{
#ifdef Q_OS_WINRT
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
if (!QSslSocket::supportsSsl())
return;
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
SslServer2 server;
server.listen();
QSslSocket socket;
socket.connectToHostEncrypted("127.0.0.1", server.serverPort());
QTestEventLoop::instance().enterLoop(2);
QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
QCOMPARE(socket.error(), QAbstractSocket::UnknownSocketError);
}
void tst_QSslSocket::spontaneousWrite()
{
#ifdef Q_OS_WINRT
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
SslServer server;
QSslSocket *receiver = new QSslSocket(this);
connect(receiver, SIGNAL(readyRead()), SLOT(exitLoop()));
// connect two sockets to each other:
QVERIFY(server.listen(QHostAddress::LocalHost));
receiver->connectToHost("127.0.0.1", server.serverPort());
QVERIFY(receiver->waitForConnected(5000));
QVERIFY(server.waitForNewConnection(0));
QSslSocket *sender = server.socket;
QVERIFY(sender);
QCOMPARE(sender->state(), QAbstractSocket::ConnectedState);
receiver->setObjectName("receiver");
sender->setObjectName("sender");
receiver->ignoreSslErrors();
receiver->startClientEncryption();
// SSL handshake:
connect(receiver, SIGNAL(encrypted()), SLOT(exitLoop()));
enterLoop(1);
QVERIFY(!timeout());
QVERIFY(sender->isEncrypted());
QVERIFY(receiver->isEncrypted());
// make sure there's nothing to be received on the sender:
while (sender->waitForReadyRead(10) || receiver->waitForBytesWritten(10)) {}
// spontaneously write something:
QByteArray data("Hello World");
sender->write(data);
// check if the other side receives it:
enterLoop(1);
QVERIFY(!timeout());
QCOMPARE(receiver->bytesAvailable(), qint64(data.size()));
QCOMPARE(receiver->readAll(), data);
}
void tst_QSslSocket::setReadBufferSize()
{
#ifdef Q_OS_WINRT
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
SslServer server;
QSslSocket *receiver = new QSslSocket(this);
connect(receiver, SIGNAL(readyRead()), SLOT(exitLoop()));
// connect two sockets to each other:
QVERIFY(server.listen(QHostAddress::LocalHost));
receiver->connectToHost("127.0.0.1", server.serverPort());
QVERIFY(receiver->waitForConnected(5000));
QVERIFY(server.waitForNewConnection(0));
QSslSocket *sender = server.socket;
QVERIFY(sender);
QCOMPARE(sender->state(), QAbstractSocket::ConnectedState);
receiver->setObjectName("receiver");
sender->setObjectName("sender");
receiver->ignoreSslErrors();
receiver->startClientEncryption();
// SSL handshake:
connect(receiver, SIGNAL(encrypted()), SLOT(exitLoop()));
enterLoop(1);
QVERIFY(!timeout());
QVERIFY(sender->isEncrypted());
QVERIFY(receiver->isEncrypted());
QByteArray data(2048, 'b');
receiver->setReadBufferSize(39 * 1024); // make it a non-multiple of the data.size()
// saturate the incoming buffer
while (sender->state() == QAbstractSocket::ConnectedState &&
receiver->state() == QAbstractSocket::ConnectedState &&
receiver->bytesAvailable() < receiver->readBufferSize()) {
sender->write(data);
//qDebug() << receiver->bytesAvailable() << "<" << receiver->readBufferSize() << (receiver->bytesAvailable() < receiver->readBufferSize());
while (sender->bytesToWrite())
QVERIFY(sender->waitForBytesWritten(10));
// drain it:
while (receiver->bytesAvailable() < receiver->readBufferSize() &&
receiver->waitForReadyRead(10)) {}
}
//qDebug() << sender->bytesToWrite() << "bytes to write";
//qDebug() << receiver->bytesAvailable() << "bytes available";
// send a bit more
sender->write(data);
sender->write(data);
sender->write(data);
sender->write(data);
QVERIFY(sender->waitForBytesWritten(10));
qint64 oldBytesAvailable = receiver->bytesAvailable();
// now unset the read buffer limit and iterate
receiver->setReadBufferSize(0);
enterLoop(1);
QVERIFY(!timeout());
QVERIFY(receiver->bytesAvailable() > oldBytesAvailable);
}
class SetReadBufferSize_task_250027_handler : public QObject {
Q_OBJECT
public slots:
void readyReadSlot() {
QTestEventLoop::instance().exitLoop();
}
void waitSomeMore(QSslSocket *socket) {
QElapsedTimer t;
t.start();
while (!socket->encryptedBytesAvailable()) {
QCoreApplication::processEvents(QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents, 250);
if (t.elapsed() > 1000 || socket->state() != QAbstractSocket::ConnectedState)
return;
}
}
};
void tst_QSslSocket::setReadBufferSize_task_250027()
{
QSKIP("QTBUG-29730 - flakey test blocking integration");
// do not execute this when a proxy is set.
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
QSslSocketPtr socket = newSocket();
socket->setReadBufferSize(1000); // limit to 1 kb/sec
socket->ignoreSslErrors();
socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
socket->ignoreSslErrors();
QVERIFY2(socket->waitForConnected(10*1000), qPrintable(socket->errorString()));
if (setProxy && !socket->waitForEncrypted(10*1000))
QSKIP("Skipping flaky test - See QTBUG-29941");
// exit the event loop as soon as we receive a readyRead()
SetReadBufferSize_task_250027_handler setReadBufferSize_task_250027_handler;
connect(socket.data(), SIGNAL(readyRead()), &setReadBufferSize_task_250027_handler, SLOT(readyReadSlot()));
// provoke a response by sending a request
socket->write("GET /qtest/fluke.gif HTTP/1.0\n"); // this file is 27 KB
socket->write("Host: ");
socket->write(QtNetworkSettings::httpServerName().toLocal8Bit().constData());
socket->write("\n");
socket->write("Connection: close\n");
socket->write("\n");
socket->flush();
QTestEventLoop::instance().enterLoop(10);
setReadBufferSize_task_250027_handler.waitSomeMore(socket.data());
QByteArray firstRead = socket->readAll();
// First read should be some data, but not the whole file
QVERIFY(firstRead.size() > 0 && firstRead.size() < 20*1024);
QTestEventLoop::instance().enterLoop(10);
setReadBufferSize_task_250027_handler.waitSomeMore(socket.data());
QByteArray secondRead = socket->readAll();
// second read should be some more data
int secondReadSize = secondRead.size();
if (secondReadSize <= 0) {
QEXPECT_FAIL("", "QTBUG-29730", Continue);
}
QVERIFY(secondReadSize > 0);
socket->close();
}
class SslServer3 : public QTcpServer
{
Q_OBJECT
public:
SslServer3() : socket(0) { }
QSslSocket *socket;
protected:
void incomingConnection(qintptr socketDescriptor)
{
socket = new QSslSocket(this);
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
QFile file(tst_QSslSocket::testDataDir + "certs/fluke.key");
QVERIFY(file.open(QIODevice::ReadOnly));
QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
QVERIFY(!key.isNull());
socket->setPrivateKey(key);
QList<QSslCertificate> localCert = QSslCertificate::fromPath(tst_QSslSocket::testDataDir
+ "certs/fluke.cert");
QVERIFY(!localCert.isEmpty());
QVERIFY(!localCert.first().isNull());
socket->setLocalCertificate(localCert.first());
QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState));
QVERIFY(!socket->peerAddress().isNull());
QVERIFY(socket->peerPort() != 0);
QVERIFY(!socket->localAddress().isNull());
QVERIFY(socket->localPort() != 0);
}
protected slots:
void ignoreErrorSlot()
{
socket->ignoreSslErrors();
}
};
class ThreadedSslServer: public QThread
{
Q_OBJECT
public:
QSemaphore dataReadSemaphore;
int serverPort;
bool ok;
ThreadedSslServer() : serverPort(-1), ok(false)
{ }
~ThreadedSslServer()
{
if (isRunning()) wait(2000);
QVERIFY(ok);
}
signals:
void listening();
protected:
void run()
{
// if all goes well (no timeouts), this thread will sleep for a total of 500 ms
// (i.e., 5 times 100 ms, one sleep for each operation)
SslServer3 server;
server.listen(QHostAddress::LocalHost);
serverPort = server.serverPort();
emit listening();
// delayed acceptance:
QTest::qSleep(100);
bool ret = server.waitForNewConnection(2000);
Q_UNUSED(ret);
// delayed start of encryption
QTest::qSleep(100);
QSslSocket *socket = server.socket;
if (!socket || !socket->isValid())
return; // error
socket->ignoreSslErrors();
socket->startServerEncryption();
if (!socket->waitForEncrypted(2000))
return; // error
// delayed reading data
QTest::qSleep(100);
if (!socket->waitForReadyRead(2000) && socket->bytesAvailable() == 0)
return; // error
socket->readAll();
dataReadSemaphore.release();
// delayed sending data
QTest::qSleep(100);
socket->write("Hello, World");
while (socket->bytesToWrite())
if (!socket->waitForBytesWritten(2000))
return; // error
// delayed replying (reading then sending)
QTest::qSleep(100);
if (!socket->waitForReadyRead(2000))
return; // error
socket->write("Hello, World");
while (socket->bytesToWrite())
if (!socket->waitForBytesWritten(2000))
return; // error
// delayed disconnection:
QTest::qSleep(100);
socket->disconnectFromHost();
if (!socket->waitForDisconnected(2000))
return; // error
delete socket;
ok = true;
}
};
void tst_QSslSocket::waitForMinusOne()
{
#ifdef Q_OS_WIN
QSKIP("QTBUG-24451 - indefinite wait may hang");
#endif
#ifdef Q_OS_WINRT // This can stay in case the one above goes away
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
ThreadedSslServer server;
connect(&server, SIGNAL(listening()), SLOT(exitLoop()));
// start the thread and wait for it to be ready
server.start();
enterLoop(1);
QVERIFY(!timeout());
// connect to the server
QSslSocket socket;
QTest::qSleep(100);
socket.connectToHost("127.0.0.1", server.serverPort);
QVERIFY(socket.waitForConnected(-1));
socket.ignoreSslErrors();
socket.startClientEncryption();
// first verification: this waiting should take 200 ms
if (!socket.waitForEncrypted(-1))
QSKIP("Skipping flaky test - See QTBUG-29941");
QVERIFY(socket.isEncrypted());
QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
QCOMPARE(socket.bytesAvailable(), Q_INT64_C(0));
// second verification: write and make sure the other side got it (100 ms)
socket.write("How are you doing?");
QVERIFY(socket.bytesToWrite() != 0);
QVERIFY(socket.waitForBytesWritten(-1));
QVERIFY(server.dataReadSemaphore.tryAcquire(1, 2500));
// third verification: it should wait for 100 ms:
QVERIFY(socket.waitForReadyRead(-1));
QVERIFY(socket.isEncrypted());
QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
QVERIFY(socket.bytesAvailable() != 0);
// fourth verification: deadlock prevention:
// we write and then wait for reading; the other side needs to receive before
// replying (100 ms delay)
socket.write("I'm doing just fine!");
QVERIFY(socket.bytesToWrite() != 0);
QVERIFY(socket.waitForReadyRead(-1));
// fifth verification: it should wait for 200 ms more
QVERIFY(socket.waitForDisconnected(-1));
// sixth verification: reading from a disconnected socket returns -1
// once we deplete the read buffer
QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState);
socket.readAll();
char aux;
QCOMPARE(socket.read(&aux, 1), -1);
}
class VerifyServer : public QTcpServer
{
Q_OBJECT
public:
VerifyServer() : socket(0) { }
QSslSocket *socket;
protected:
void incomingConnection(qintptr socketDescriptor)
{
socket = new QSslSocket(this);
socket->setPrivateKey(tst_QSslSocket::testDataDir + "certs/fluke.key");
socket->setLocalCertificate(tst_QSslSocket::testDataDir + "certs/fluke.cert");
socket->setSocketDescriptor(socketDescriptor);
socket->startServerEncryption();
}
};
void tst_QSslSocket::verifyMode()
{
#ifdef Q_OS_WINRT
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
QSslSocket socket;
#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2
socket.setProtocol(QSsl::SslProtocol::TlsV1_1);
#endif
QCOMPARE(socket.peerVerifyMode(), QSslSocket::AutoVerifyPeer);
socket.setPeerVerifyMode(QSslSocket::VerifyNone);
QCOMPARE(socket.peerVerifyMode(), QSslSocket::VerifyNone);
socket.setPeerVerifyMode(QSslSocket::VerifyNone);
socket.setPeerVerifyMode(QSslSocket::VerifyPeer);
QCOMPARE(socket.peerVerifyMode(), QSslSocket::VerifyPeer);
socket.connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
if (socket.waitForEncrypted())
QSKIP("Skipping flaky test - See QTBUG-29941");
QList<QSslError> expectedErrors = QList<QSslError>()
<< QSslError(FLUKE_CERTIFICATE_ERROR, socket.peerCertificate());
QCOMPARE(socket.sslErrors(), expectedErrors);
socket.abort();
VerifyServer server;
server.listen();
QSslSocket clientSocket;
clientSocket.connectToHostEncrypted("127.0.0.1", server.serverPort());
clientSocket.ignoreSslErrors();
QEventLoop loop;
QTimer::singleShot(5000, &loop, SLOT(quit()));
connect(&clientSocket, SIGNAL(encrypted()), &loop, SLOT(quit()));
loop.exec();
QVERIFY(clientSocket.isEncrypted());
QVERIFY(server.socket->sslErrors().isEmpty());
}
void tst_QSslSocket::verifyDepth()
{
QSslSocket socket;
QCOMPARE(socket.peerVerifyDepth(), 0);
socket.setPeerVerifyDepth(1);
QCOMPARE(socket.peerVerifyDepth(), 1);
QTest::ignoreMessage(QtWarningMsg, "QSslSocket::setPeerVerifyDepth: cannot set negative depth of -1");
socket.setPeerVerifyDepth(-1);
QCOMPARE(socket.peerVerifyDepth(), 1);
}
void tst_QSslSocket::disconnectFromHostWhenConnecting()
{
QSslSocketPtr socket = newSocket();
socket->connectToHostEncrypted(QtNetworkSettings::imapServerName(), 993);
socket->ignoreSslErrors();
socket->write("XXXX LOGOUT\r\n");
QAbstractSocket::SocketState state = socket->state();
// without proxy, the state will be HostLookupState;
// with proxy, the state will be ConnectingState.
QVERIFY(socket->state() == QAbstractSocket::HostLookupState ||
socket->state() == QAbstractSocket::ConnectingState);
socket->disconnectFromHost();
// the state of the socket must be the same before and after calling
// disconnectFromHost()
QCOMPARE(state, socket->state());
QVERIFY(socket->state() == QAbstractSocket::HostLookupState ||
socket->state() == QAbstractSocket::ConnectingState);
if (!socket->waitForDisconnected(10000))
QSKIP("Skipping flaky test - See QTBUG-29941");
QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState);
// we did not call close, so the socket must be still open
QVERIFY(socket->isOpen());
QCOMPARE(socket->bytesToWrite(), qint64(0));
// don't forget to login
QCOMPARE((int) socket->write("USER ftptest\r\n"), 14);
}
void tst_QSslSocket::disconnectFromHostWhenConnected()
{
QSslSocketPtr socket = newSocket();
socket->connectToHostEncrypted(QtNetworkSettings::imapServerName(), 993);
socket->ignoreSslErrors();
if (!socket->waitForEncrypted(5000))
QSKIP("Skipping flaky test - See QTBUG-29941");
socket->write("XXXX LOGOUT\r\n");
QCOMPARE(socket->state(), QAbstractSocket::ConnectedState);
socket->disconnectFromHost();
QCOMPARE(socket->state(), QAbstractSocket::ClosingState);
QVERIFY(socket->waitForDisconnected(5000));
QCOMPARE(socket->bytesToWrite(), qint64(0));
}
#ifndef QT_NO_OPENSSL
class BrokenPskHandshake : public QTcpServer
{
public:
void socketError(QAbstractSocket::SocketError error)
{
Q_UNUSED(error);
QSslSocket *clientSocket = qobject_cast<QSslSocket *>(sender());
Q_ASSERT(clientSocket);
clientSocket->close();
QTestEventLoop::instance().exitLoop();
}
private:
void incomingConnection(qintptr handle) override
{
if (!socket.setSocketDescriptor(handle))
return;
QSslConfiguration serverConfig(QSslConfiguration::defaultConfiguration());
serverConfig.setPreSharedKeyIdentityHint("abcdefghijklmnop");
socket.setSslConfiguration(serverConfig);
socket.startServerEncryption();
}
QSslSocket socket;
};
void tst_QSslSocket::closeWhileEmittingSocketError()
{
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
BrokenPskHandshake handshake;
if (!handshake.listen())
QSKIP("failed to start TLS server");
QSslSocket clientSocket;
QSslConfiguration clientConfig(QSslConfiguration::defaultConfiguration());
clientConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
clientSocket.setSslConfiguration(clientConfig);
QSignalSpy socketErrorSpy(&clientSocket, SIGNAL(error(QAbstractSocket::SocketError)));
void (QSslSocket::*errorSignal)(QAbstractSocket::SocketError) = &QSslSocket::error;
connect(&clientSocket, errorSignal, &handshake, &BrokenPskHandshake::socketError);
clientSocket.connectToHostEncrypted(QStringLiteral("127.0.0.1"), handshake.serverPort());
// Make sure we have some data buffered so that close will try to flush:
clientSocket.write(QByteArray(1000000, Qt::Uninitialized));
QTestEventLoop::instance().enterLoopMSecs(1000);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(socketErrorSpy.count(), 1);
}
#endif // QT_NO_OPENSSL
void tst_QSslSocket::resetProxy()
{
#ifndef QT_NO_NETWORKPROXY
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
// check fix for bug 199941
QNetworkProxy goodProxy(QNetworkProxy::NoProxy);
QNetworkProxy badProxy(QNetworkProxy::HttpProxy, "thisCannotWorkAbsolutelyNotForSure", 333);
// make sure the connection works, and then set a nonsense proxy, and then
// make sure it does not work anymore
QSslSocket socket;
socket.addCaCertificates(httpServerCertChainPath());
socket.setProxy(goodProxy);
socket.connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
QVERIFY2(socket.waitForConnected(10000), qPrintable(socket.errorString()));
socket.abort();
socket.setProxy(badProxy);
socket.connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
QVERIFY(! socket.waitForConnected(10000));
// don't forget to login
QCOMPARE((int) socket.write("USER ftptest\r\n"), 14);
QCOMPARE((int) socket.write("PASS password\r\n"), 15);
enterLoop(10);
// now the other way round:
// set the nonsense proxy and make sure the connection does not work,
// and then set the right proxy and make sure it works
QSslSocket socket2;
socket2.addCaCertificates(httpServerCertChainPath());
socket2.setProxy(badProxy);
socket2.connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
QVERIFY(! socket2.waitForConnected(10000));
socket2.abort();
socket2.setProxy(goodProxy);
socket2.connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
QVERIFY2(socket2.waitForConnected(10000), qPrintable(socket.errorString()));
#endif // QT_NO_NETWORKPROXY
}
void tst_QSslSocket::ignoreSslErrorsList_data()
{
QTest::addColumn<QList<QSslError> >("expectedSslErrors");
QTest::addColumn<int>("expectedSslErrorSignalCount");
// construct the list of errors that we will get with the SSL handshake and that we will ignore
QList<QSslError> expectedSslErrors;
// fromPath gives us a list of certs, but it actually only contains one
QList<QSslCertificate> certs = QSslCertificate::fromPath(httpServerCertChainPath());
QSslError rightError(FLUKE_CERTIFICATE_ERROR, certs.at(0));
QSslError wrongError(FLUKE_CERTIFICATE_ERROR);
QTest::newRow("SSL-failure-empty-list") << expectedSslErrors << 1;
expectedSslErrors.append(wrongError);
QTest::newRow("SSL-failure-wrong-error") << expectedSslErrors << 1;
expectedSslErrors.append(rightError);
QTest::newRow("allErrorsInExpectedList1") << expectedSslErrors << 0;
expectedSslErrors.removeAll(wrongError);
QTest::newRow("allErrorsInExpectedList2") << expectedSslErrors << 0;
expectedSslErrors.removeAll(rightError);
QTest::newRow("SSL-failure-empty-list-again") << expectedSslErrors << 1;
}
void tst_QSslSocket::ignoreSslErrorsList()
{
QSslSocket socket;
connect(&socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
// this->socket = &socket;
QSslCertificate cert;
QFETCH(QList<QSslError>, expectedSslErrors);
socket.ignoreSslErrors(expectedSslErrors);
QFETCH(int, expectedSslErrorSignalCount);
QSignalSpy sslErrorsSpy(&socket, SIGNAL(error(QAbstractSocket::SocketError)));
socket.connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
bool expectEncryptionSuccess = (expectedSslErrorSignalCount == 0);
if (socket.waitForEncrypted(10000) != expectEncryptionSuccess)
QSKIP("Skipping flaky test - See QTBUG-29941");
QCOMPARE(sslErrorsSpy.count(), expectedSslErrorSignalCount);
}
void tst_QSslSocket::ignoreSslErrorsListWithSlot_data()
{
ignoreSslErrorsList_data();
}
// this is not a test, just a slot called in the test below
void tst_QSslSocket::ignoreErrorListSlot(const QList<QSslError> &)
{
socket->ignoreSslErrors(storedExpectedSslErrors);
}
void tst_QSslSocket::ignoreSslErrorsListWithSlot()
{
QSslSocket socket;
this->socket = &socket;
QFETCH(QList<QSslError>, expectedSslErrors);
// store the errors to ignore them later in the slot connected below
storedExpectedSslErrors = expectedSslErrors;
connect(&socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
connect(&socket, SIGNAL(sslErrors(QList<QSslError>)),
this, SLOT(ignoreErrorListSlot(QList<QSslError>)));
socket.connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
QFETCH(int, expectedSslErrorSignalCount);
bool expectEncryptionSuccess = (expectedSslErrorSignalCount == 0);
QFETCH_GLOBAL(bool, setProxy);
if (setProxy && (socket.waitForEncrypted(10000) != expectEncryptionSuccess))
QSKIP("Skipping flaky test - See QTBUG-29941");
}
void tst_QSslSocket::abortOnSslErrors()
{
#ifdef Q_OS_WINRT
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
SslServer server;
QVERIFY(server.listen());
QSslSocket clientSocket;
connect(&clientSocket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(abortOnErrorSlot()));
clientSocket.connectToHostEncrypted("127.0.0.1", server.serverPort());
clientSocket.ignoreSslErrors();
QEventLoop loop;
QTimer::singleShot(1000, &loop, SLOT(quit()));
loop.exec();
QCOMPARE(clientSocket.state(), QAbstractSocket::UnconnectedState);
}
// make sure a closed socket has no bytesAvailable()
// related to https://bugs.webkit.org/show_bug.cgi?id=28016
void tst_QSslSocket::readFromClosedSocket()
{
QSslSocketPtr socket = newSocket();
#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2
socket->setProtocol(QSsl::SslProtocol::TlsV1_1);
#endif
socket->ignoreSslErrors();
socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
socket->ignoreSslErrors();
socket->waitForConnected();
socket->waitForEncrypted();
// provoke a response by sending a request
socket->write("GET /qtest/fluke.gif HTTP/1.1\n");
socket->write("Host: ");
socket->write(QtNetworkSettings::httpServerName().toLocal8Bit().constData());
socket->write("\n");
socket->write("\n");
socket->waitForBytesWritten();
socket->waitForReadyRead();
QFETCH_GLOBAL(bool, setProxy);
if (setProxy && (socket->state() != QAbstractSocket::ConnectedState))
QSKIP("Skipping flaky test - See QTBUG-29941");
QVERIFY(socket->bytesAvailable());
socket->close();
QVERIFY(!socket->bytesAvailable());
QVERIFY(!socket->bytesToWrite());
QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState);
}
void tst_QSslSocket::writeBigChunk()
{
if (!QSslSocket::supportsSsl())
return;
QSslSocketPtr socket = newSocket();
this->socket = socket.data();
connect(this->socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
QByteArray data;
data.resize(1024*1024*10); // 10 MB
// init with garbage. needed so ssl cannot compress it in an efficient way.
// ### Qt 6: update to a random engine
for (size_t i = 0; i < data.size() / sizeof(int); i++) {
int r = qrand();
data.data()[i*sizeof(int)] = r;
}
if (!socket->waitForEncrypted(10000))
QSKIP("Skipping flaky test - See QTBUG-29941");
QString errorBefore = socket->errorString();
int ret = socket->write(data.constData(), data.size());
QCOMPARE(data.size(), ret);
// spin the event loop once so QSslSocket::transmit() gets called
QCoreApplication::processEvents();
QString errorAfter = socket->errorString();
// no better way to do this right now since the error is the same as the default error.
if (socket->errorString().startsWith(QLatin1String("Unable to write data")))
{
qWarning() << socket->error() << socket->errorString();
QFAIL("Error while writing! Check if the OpenSSL BIO size is limited?!");
}
// also check the error string. If another error (than UnknownError) occurred, it should be different than before
QVERIFY2(errorBefore == errorAfter || socket->error() == QAbstractSocket::RemoteHostClosedError,
QByteArray("unexpected error: ").append(qPrintable(errorAfter)));
// check that everything has been written to OpenSSL
QCOMPARE(socket->bytesToWrite(), 0);
socket->close();
}
void tst_QSslSocket::blacklistedCertificates()
{
#ifdef Q_OS_WINRT
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
SslServer server(testDataDir + "certs/fake-login.live.com.key", testDataDir + "certs/fake-login.live.com.pem");
QSslSocket *receiver = new QSslSocket(this);
connect(receiver, SIGNAL(readyRead()), SLOT(exitLoop()));
// connect two sockets to each other:
QVERIFY(server.listen(QHostAddress::LocalHost));
receiver->connectToHost("127.0.0.1", server.serverPort());
QVERIFY(receiver->waitForConnected(5000));
if (!server.waitForNewConnection(0))
QSKIP("Skipping flaky test - See QTBUG-29941");
QSslSocket *sender = server.socket;
QVERIFY(sender);
QCOMPARE(sender->state(), QAbstractSocket::ConnectedState);
receiver->setObjectName("receiver");
sender->setObjectName("sender");
receiver->startClientEncryption();
connect(receiver, SIGNAL(sslErrors(QList<QSslError>)), SLOT(exitLoop()));
connect(receiver, SIGNAL(encrypted()), SLOT(exitLoop()));
enterLoop(1);
QList<QSslError> sslErrors = receiver->sslErrors();
QVERIFY(sslErrors.count() > 0);
// there are more errors (self signed cert and hostname mismatch), but we only care about the blacklist error
QCOMPARE(sslErrors.at(0).error(), QSslError::CertificateBlacklisted);
}
void tst_QSslSocket::versionAccessors()
{
if (!QSslSocket::supportsSsl())
return;
qDebug() << QSslSocket::sslLibraryVersionString();
qDebug() << QString::number(QSslSocket::sslLibraryVersionNumber(), 16);
}
#ifndef QT_NO_OPENSSL
void tst_QSslSocket::sslOptions()
{
if (!QSslSocket::supportsSsl())
return;
#ifdef SSL_OP_NO_COMPRESSION
QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols,
QSslConfigurationPrivate::defaultSslOptions),
long(SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_COMPRESSION|SSL_OP_CIPHER_SERVER_PREFERENCE));
#else
QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols,
QSslConfigurationPrivate::defaultSslOptions),
long(SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_CIPHER_SERVER_PREFERENCE));
#endif
QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols,
QSsl::SslOptionDisableEmptyFragments
|QSsl::SslOptionDisableLegacyRenegotiation),
long(SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_CIPHER_SERVER_PREFERENCE));
#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols,
QSsl::SslOptionDisableEmptyFragments),
long((SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION|SSL_OP_CIPHER_SERVER_PREFERENCE)));
#endif
#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols,
QSsl::SslOptionDisableLegacyRenegotiation),
long((SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_CIPHER_SERVER_PREFERENCE) & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS));
#endif
#ifdef SSL_OP_NO_TICKET
QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols,
QSsl::SslOptionDisableEmptyFragments
|QSsl::SslOptionDisableLegacyRenegotiation
|QSsl::SslOptionDisableSessionTickets),
long((SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TICKET|SSL_OP_CIPHER_SERVER_PREFERENCE)));
#endif
#ifdef SSL_OP_NO_TICKET
#ifdef SSL_OP_NO_COMPRESSION
QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols,
QSsl::SslOptionDisableEmptyFragments
|QSsl::SslOptionDisableLegacyRenegotiation
|QSsl::SslOptionDisableSessionTickets
|QSsl::SslOptionDisableCompression),
long((SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TICKET|SSL_OP_NO_COMPRESSION|SSL_OP_CIPHER_SERVER_PREFERENCE)));
#endif
#endif
}
#endif
void tst_QSslSocket::encryptWithoutConnecting()
{
if (!QSslSocket::supportsSsl())
return;
QTest::ignoreMessage(QtWarningMsg,
"QSslSocket::startClientEncryption: cannot start handshake when not connected");
QSslSocket sock;
sock.startClientEncryption();
}
void tst_QSslSocket::resume_data()
{
QTest::addColumn<bool>("ignoreErrorsAfterPause");
QTest::addColumn<QList<QSslError> >("errorsToIgnore");
QTest::addColumn<bool>("expectSuccess");
QList<QSslError> errorsList;
QTest::newRow("DoNotIgnoreErrors") << false << QList<QSslError>() << false;
QTest::newRow("ignoreAllErrors") << true << QList<QSslError>() << true;
// Note, httpServerCertChainPath() it's ... because we use the same certificate on
// different services. We'll be actually connecting to IMAP server.
QList<QSslCertificate> certs = QSslCertificate::fromPath(httpServerCertChainPath());
QSslError rightError(FLUKE_CERTIFICATE_ERROR, certs.at(0));
QSslError wrongError(FLUKE_CERTIFICATE_ERROR);
errorsList.append(wrongError);
QTest::newRow("ignoreSpecificErrors-Wrong") << true << errorsList << false;
errorsList.clear();
errorsList.append(rightError);
QTest::newRow("ignoreSpecificErrors-Right") << true << errorsList << true;
}
void tst_QSslSocket::resume()
{
// make sure the server certificate is not in the list of accepted certificates,
// we want to trigger the sslErrors signal
auto sslConfig = QSslConfiguration::defaultConfiguration();
sslConfig.setCaCertificates(QSslConfiguration::systemCaCertificates());
QSslConfiguration::setDefaultConfiguration(sslConfig);
QFETCH(bool, ignoreErrorsAfterPause);
QFETCH(QList<QSslError>, errorsToIgnore);
QFETCH(bool, expectSuccess);
QSslSocket socket;
socket.setPauseMode(QAbstractSocket::PauseOnSslErrors);
QSignalSpy sslErrorSpy(&socket, SIGNAL(sslErrors(QList<QSslError>)));
QSignalSpy encryptedSpy(&socket, SIGNAL(encrypted()));
QSignalSpy errorSpy(&socket, SIGNAL(error(QAbstractSocket::SocketError)));
connect(&socket, SIGNAL(sslErrors(QList<QSslError>)), &QTestEventLoop::instance(), SLOT(exitLoop()));
connect(&socket, SIGNAL(encrypted()), &QTestEventLoop::instance(), SLOT(exitLoop()));
connect(&socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), &QTestEventLoop::instance(), SLOT(exitLoop()));
socket.connectToHostEncrypted(QtNetworkSettings::imapServerName(), 993);
QTestEventLoop::instance().enterLoop(10);
QFETCH_GLOBAL(bool, setProxy);
if (setProxy && QTestEventLoop::instance().timeout())
QSKIP("Skipping flaky test - See QTBUG-29941");
QCOMPARE(sslErrorSpy.count(), 1);
QCOMPARE(errorSpy.count(), 0);
QCOMPARE(encryptedSpy.count(), 0);
QVERIFY(!socket.isEncrypted());
if (ignoreErrorsAfterPause) {
if (errorsToIgnore.empty())
socket.ignoreSslErrors();
else
socket.ignoreSslErrors(errorsToIgnore);
}
socket.resume();
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout()); // quit by encrypted() or error() signal
if (expectSuccess) {
QCOMPARE(encryptedSpy.count(), 1);
QVERIFY(socket.isEncrypted());
QCOMPARE(errorSpy.count(), 0);
socket.disconnectFromHost();
QVERIFY(socket.waitForDisconnected(10000));
} else {
QCOMPARE(encryptedSpy.count(), 0);
QVERIFY(!socket.isEncrypted());
QCOMPARE(errorSpy.count(), 1);
QCOMPARE(socket.error(), QAbstractSocket::SslHandshakeFailedError);
}
}
class WebSocket : public QSslSocket
{
Q_OBJECT
public:
explicit WebSocket(qintptr socketDescriptor,
const QString &keyFile = tst_QSslSocket::testDataDir + "certs/fluke.key",
const QString &certFile = tst_QSslSocket::testDataDir + "certs/fluke.cert");
protected slots:
void onReadyReadFirstBytes(void);
private:
void _startServerEncryption(void);
QString m_keyFile;
QString m_certFile;
private:
Q_DISABLE_COPY(WebSocket)
};
WebSocket::WebSocket (qintptr socketDescriptor, const QString &keyFile, const QString &certFile)
: m_keyFile(keyFile),
m_certFile(certFile)
{
QVERIFY(setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState, QIODevice::ReadWrite | QIODevice::Unbuffered));
connect (this, SIGNAL(readyRead()), this, SLOT(onReadyReadFirstBytes()));
}
void WebSocket::_startServerEncryption (void)
{
QFile file(m_keyFile);
QVERIFY(file.open(QIODevice::ReadOnly));
QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
QVERIFY(!key.isNull());
setPrivateKey(key);
QList<QSslCertificate> localCert = QSslCertificate::fromPath(m_certFile);
QVERIFY(!localCert.isEmpty());
QVERIFY(!localCert.first().isNull());
setLocalCertificate(localCert.first());
QVERIFY(!peerAddress().isNull());
QVERIFY(peerPort() != 0);
QVERIFY(!localAddress().isNull());
QVERIFY(localPort() != 0);
setProtocol(QSsl::AnyProtocol);
setPeerVerifyMode(QSslSocket::VerifyNone);
ignoreSslErrors();
startServerEncryption();
}
void WebSocket::onReadyReadFirstBytes (void)
{
peek(1);
disconnect(this,SIGNAL(readyRead()), this, SLOT(onReadyReadFirstBytes()));
_startServerEncryption();
}
class SslServer4 : public QTcpServer
{
Q_OBJECT
public:
QScopedPointer<WebSocket> socket;
protected:
void incomingConnection(qintptr socketDescriptor) override
{
socket.reset(new WebSocket(socketDescriptor));
}
};
void tst_QSslSocket::qtbug18498_peek()
{
#ifdef Q_OS_WINRT
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
SslServer4 server;
QVERIFY(server.listen(QHostAddress::LocalHost));
QSslSocket client;
client.connectToHost("127.0.0.1", server.serverPort());
QVERIFY(client.waitForConnected(5000));
QVERIFY(server.waitForNewConnection(1000));
client.ignoreSslErrors();
int encryptedCounter = 2;
connect(&client, &QSslSocket::encrypted, this, [&encryptedCounter](){
if (!--encryptedCounter)
exitLoop();
});
WebSocket *serversocket = server.socket.data();
connect(serversocket, &QSslSocket::encrypted, this, [&encryptedCounter](){
if (!--encryptedCounter)
exitLoop();
});
connect(&client, SIGNAL(disconnected()), this, SLOT(exitLoop()));
client.startClientEncryption();
QVERIFY(serversocket);
enterLoop(1);
QVERIFY(!timeout());
QVERIFY(serversocket->isEncrypted());
QVERIFY(client.isEncrypted());
QByteArray data("abc123");
client.write(data.data());
connect(serversocket, SIGNAL(readyRead()), this, SLOT(exitLoop()));
enterLoop(1);
QVERIFY(!timeout());
QByteArray peek1_data;
peek1_data.reserve(data.size());
QByteArray peek2_data;
QByteArray read_data;
int lngth = serversocket->peek(peek1_data.data(), 10);
peek1_data.resize(lngth);
peek2_data = serversocket->peek(10);
read_data = serversocket->readAll();
QCOMPARE(peek1_data, data);
QCOMPARE(peek2_data, data);
QCOMPARE(read_data, data);
}
class SslServer5 : public QTcpServer
{
Q_OBJECT
public:
SslServer5() : socket(0) {}
QSslSocket *socket;
protected:
void incomingConnection(qintptr socketDescriptor)
{
socket = new QSslSocket;
socket->setSocketDescriptor(socketDescriptor);
}
};
void tst_QSslSocket::qtbug18498_peek2()
{
#ifdef Q_OS_WINRT
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
SslServer5 listener;
QVERIFY(listener.listen(QHostAddress::Any));
QScopedPointer<QSslSocket> client(new QSslSocket);
client->connectToHost(QHostAddress::LocalHost, listener.serverPort());
QVERIFY(client->waitForConnected(5000));
QVERIFY(listener.waitForNewConnection(1000));
QScopedPointer<QSslSocket> server(listener.socket);
QVERIFY(server->write("HELLO\r\n", 7));
QTRY_COMPARE(client->bytesAvailable(), 7);
char c;
QCOMPARE(client->peek(&c,1), 1);
QCOMPARE(c, 'H');
QCOMPARE(client->read(&c,1), 1);
QCOMPARE(c, 'H');
QByteArray b = client->peek(2);
QCOMPARE(b, QByteArray("EL"));
char a[3];
QVERIFY(client->peek(a, 2) == 2);
QCOMPARE(a[0], 'E');
QCOMPARE(a[1], 'L');
QCOMPARE(client->readAll(), QByteArray("ELLO\r\n"));
//check data split between QIODevice and plain socket buffers.
QByteArray bigblock;
bigblock.fill('#', QIODEVICE_BUFFERSIZE + 1024);
QVERIFY(client->write(QByteArray("head")));
QVERIFY(client->write(bigblock));
QTRY_COMPARE(server->bytesAvailable(), bigblock.length() + 4);
QCOMPARE(server->read(4), QByteArray("head"));
QCOMPARE(server->peek(bigblock.length()), bigblock);
b.reserve(bigblock.length());
b.resize(server->peek(b.data(), bigblock.length()));
QCOMPARE(b, bigblock);
//check oversized peek
QCOMPARE(server->peek(bigblock.length() * 3), bigblock);
b.reserve(bigblock.length() * 3);
b.resize(server->peek(b.data(), bigblock.length() * 3));
QCOMPARE(b, bigblock);
QCOMPARE(server->readAll(), bigblock);
QVERIFY(client->write("STARTTLS\r\n"));
QTRY_COMPARE(server->bytesAvailable(), 10);
QCOMPARE(server->peek(&c,1), 1);
QCOMPARE(c, 'S');
b = server->peek(3);
QCOMPARE(b, QByteArray("STA"));
QCOMPARE(server->read(5), QByteArray("START"));
QVERIFY(server->peek(a, 3) == 3);
QCOMPARE(a[0], 'T');
QCOMPARE(a[1], 'L');
QCOMPARE(a[2], 'S');
QCOMPARE(server->readAll(), QByteArray("TLS\r\n"));
QFile file(testDataDir + "certs/fluke.key");
QVERIFY(file.open(QIODevice::ReadOnly));
QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
QVERIFY(!key.isNull());
server->setPrivateKey(key);
QList<QSslCertificate> localCert = QSslCertificate::fromPath(testDataDir + "certs/fluke.cert");
QVERIFY(!localCert.isEmpty());
QVERIFY(!localCert.first().isNull());
server->setLocalCertificate(localCert.first());
server->setProtocol(QSsl::AnyProtocol);
server->setPeerVerifyMode(QSslSocket::VerifyNone);
server->ignoreSslErrors();
client->ignoreSslErrors();
server->startServerEncryption();
client->startClientEncryption();
QVERIFY(server->write("hello\r\n", 7));
QTRY_COMPARE(client->bytesAvailable(), 7);
QVERIFY(server->mode() == QSslSocket::SslServerMode && client->mode() == QSslSocket::SslClientMode);
QCOMPARE(client->peek(&c,1), 1);
QCOMPARE(c, 'h');
QCOMPARE(client->read(&c,1), 1);
QCOMPARE(c, 'h');
b = client->peek(2);
QCOMPARE(b, QByteArray("el"));
QCOMPARE(client->readAll(), QByteArray("ello\r\n"));
QVERIFY(client->write("goodbye\r\n"));
QTRY_COMPARE(server->bytesAvailable(), 9);
QCOMPARE(server->peek(&c,1), 1);
QCOMPARE(c, 'g');
QCOMPARE(server->readAll(), QByteArray("goodbye\r\n"));
client->disconnectFromHost();
QVERIFY(client->waitForDisconnected(5000));
}
void tst_QSslSocket::dhServer()
{
#ifdef Q_OS_WINRT
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
if (!QSslSocket::supportsSsl())
QSKIP("No SSL support");
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
SslServer server;
server.ciphers = {QSslCipher("DHE-RSA-AES256-SHA"), QSslCipher("DHE-DSS-AES256-SHA")};
QVERIFY(server.listen());
QEventLoop loop;
QTimer::singleShot(5000, &loop, SLOT(quit()));
QSslSocket client;
socket = &client;
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit()));
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));
client.connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
loop.exec();
QCOMPARE(client.state(), QAbstractSocket::ConnectedState);
}
#ifndef QT_NO_OPENSSL
void tst_QSslSocket::dhServerCustomParamsNull()
{
if (!QSslSocket::supportsSsl())
QSKIP("No SSL support");
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
SslServer server;
server.ciphers = {QSslCipher("DHE-RSA-AES256-SHA"), QSslCipher("DHE-DSS-AES256-SHA")};
QSslConfiguration cfg = server.config;
cfg.setDiffieHellmanParameters(QSslDiffieHellmanParameters());
server.config = cfg;
QVERIFY(server.listen());
QEventLoop loop;
QTimer::singleShot(5000, &loop, SLOT(quit()));
QSslSocket client;
socket = &client;
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit()));
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));
client.connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
loop.exec();
QVERIFY(client.state() != QAbstractSocket::ConnectedState);
}
#endif // QT_NO_OPENSSL
#ifndef QT_NO_OPENSSL
void tst_QSslSocket::dhServerCustomParams()
{
if (!QSslSocket::supportsSsl())
QSKIP("No SSL support");
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
SslServer server;
server.ciphers = {QSslCipher("DHE-RSA-AES256-SHA"), QSslCipher("DHE-DSS-AES256-SHA")};
QSslConfiguration cfg = server.config;
// Custom 2048-bit DH parameters generated with 'openssl dhparam -outform DER -out out.der -check -2 2048'
const auto dh = QSslDiffieHellmanParameters::fromEncoded(QByteArray::fromBase64(QByteArrayLiteral(
"MIIBCAKCAQEAvVA7b8keTfjFutCtTJmP/pnQfw/prKa+GMed/pBWjrC4N1YwnI8h/A861d9WE/VWY7XMTjvjX3/0"
"aaU8wEe0EXNpFdlTH+ZMQctQTSJOyQH0RCTwJfDGPCPT9L+c9GKwEKWORH38Earip986HJc0w3UbnfIwXUdsWHiXi"
"Z6r3cpyBmTKlsXTFiDVAOUXSiO8d/zOb6zHZbDfyB/VbtZRmnA7TXVn9oMzC0g9+FXHdrV4K+XfdvNZdCegvoAZiy"
"R6ZQgNG9aZ36/AQekhg060hp55f9HDPgXqYeNeXBiferjUtU7S9b3s83XhOJAr01/0Tf5dENwCfg2gK36TM8cC4wI"
"BAg==")), QSsl::Der);
cfg.setDiffieHellmanParameters(dh);
server.config = cfg;
QVERIFY(server.listen());
QEventLoop loop;
QTimer::singleShot(5000, &loop, SLOT(quit()));
QSslSocket client;
socket = &client;
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit()));
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));
client.connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
loop.exec();
QVERIFY(client.state() == QAbstractSocket::ConnectedState);
}
#endif // QT_NO_OPENSSL
void tst_QSslSocket::ecdhServer()
{
#ifdef Q_OS_WINRT
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
if (!QSslSocket::supportsSsl()) {
qWarning("SSL not supported, skipping test");
return;
}
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
SslServer server;
server.ciphers = {QSslCipher("ECDHE-RSA-AES128-SHA")};
QVERIFY(server.listen());
QEventLoop loop;
QTimer::singleShot(5000, &loop, SLOT(quit()));
QSslSocket client;
socket = &client;
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit()));
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));
client.connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
loop.exec();
QCOMPARE(client.state(), QAbstractSocket::ConnectedState);
}
void tst_QSslSocket::verifyClientCertificate_data()
{
QTest::addColumn<QSslSocket::PeerVerifyMode>("peerVerifyMode");
QTest::addColumn<QList<QSslCertificate> >("clientCerts");
QTest::addColumn<QSslKey>("clientKey");
QTest::addColumn<bool>("works");
// no certificate
QList<QSslCertificate> noCerts;
QSslKey noKey;
QTest::newRow("NoCert:AutoVerifyPeer") << QSslSocket::AutoVerifyPeer << noCerts << noKey << true;
QTest::newRow("NoCert:QueryPeer") << QSslSocket::QueryPeer << noCerts << noKey << true;
QTest::newRow("NoCert:VerifyNone") << QSslSocket::VerifyNone << noCerts << noKey << true;
QTest::newRow("NoCert:VerifyPeer") << QSslSocket::VerifyPeer << noCerts << noKey << false;
// self-signed certificate
QList<QSslCertificate> flukeCerts = QSslCertificate::fromPath(testDataDir + "certs/fluke.cert");
QCOMPARE(flukeCerts.size(), 1);
QFile flukeFile(testDataDir + "certs/fluke.key");
QVERIFY(flukeFile.open(QIODevice::ReadOnly));
QSslKey flukeKey(flukeFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
QVERIFY(!flukeKey.isNull());
QTest::newRow("SelfSignedCert:AutoVerifyPeer") << QSslSocket::AutoVerifyPeer << flukeCerts << flukeKey << true;
QTest::newRow("SelfSignedCert:QueryPeer") << QSslSocket::QueryPeer << flukeCerts << flukeKey << true;
QTest::newRow("SelfSignedCert:VerifyNone") << QSslSocket::VerifyNone << flukeCerts << flukeKey << true;
QTest::newRow("SelfSignedCert:VerifyPeer") << QSslSocket::VerifyPeer << flukeCerts << flukeKey << false;
// valid certificate, but wrong usage (server certificate)
QList<QSslCertificate> serverCerts = QSslCertificate::fromPath(testDataDir + "certs/bogus-server.crt");
QCOMPARE(serverCerts.size(), 1);
QFile serverFile(testDataDir + "certs/bogus-server.key");
QVERIFY(serverFile.open(QIODevice::ReadOnly));
QSslKey serverKey(serverFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
QVERIFY(!serverKey.isNull());
QTest::newRow("ValidServerCert:AutoVerifyPeer") << QSslSocket::AutoVerifyPeer << serverCerts << serverKey << true;
QTest::newRow("ValidServerCert:QueryPeer") << QSslSocket::QueryPeer << serverCerts << serverKey << true;
QTest::newRow("ValidServerCert:VerifyNone") << QSslSocket::VerifyNone << serverCerts << serverKey << true;
QTest::newRow("ValidServerCert:VerifyPeer") << QSslSocket::VerifyPeer << serverCerts << serverKey << false;
// valid certificate, correct usage (client certificate)
QList<QSslCertificate> validCerts = QSslCertificate::fromPath(testDataDir + "certs/bogus-client.crt");
QCOMPARE(validCerts.size(), 1);
QFile validFile(testDataDir + "certs/bogus-client.key");
QVERIFY(validFile.open(QIODevice::ReadOnly));
QSslKey validKey(validFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
QVERIFY(!validKey.isNull());
QTest::newRow("ValidClientCert:AutoVerifyPeer") << QSslSocket::AutoVerifyPeer << validCerts << validKey << true;
QTest::newRow("ValidClientCert:QueryPeer") << QSslSocket::QueryPeer << validCerts << validKey << true;
QTest::newRow("ValidClientCert:VerifyNone") << QSslSocket::VerifyNone << validCerts << validKey << true;
QTest::newRow("ValidClientCert:VerifyPeer") << QSslSocket::VerifyPeer << validCerts << validKey << true;
// valid certificate, correct usage (client certificate), with chain
validCerts += QSslCertificate::fromPath(testDataDir + "certs/bogus-ca.crt");
QCOMPARE(validCerts.size(), 2);
QTest::newRow("ValidChainedClientCert:AutoVerifyPeer") << QSslSocket::AutoVerifyPeer << validCerts << validKey << true;
QTest::newRow("ValidChainedClientCert:QueryPeer") << QSslSocket::QueryPeer << validCerts << validKey << true;
QTest::newRow("ValidChainedClientCert:VerifyNone") << QSslSocket::VerifyNone << validCerts << validKey << true;
QTest::newRow("ValidChainedClientCert:VerifyPeer") << QSslSocket::VerifyPeer << validCerts << validKey << true;
}
void tst_QSslSocket::verifyClientCertificate()
{
#if QT_CONFIG(securetransport)
// We run both client and server on the same machine,
// this means, client can update keychain with client's certificates,
// and server later will use the same certificates from the same
// keychain thus making tests fail (wrong number of certificates,
// success instead of failure etc.).
QSKIP("This test can not work with Secure Transport");
#endif // QT_CONFIG(securetransport)
#ifdef Q_OS_WINRT
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
if (!QSslSocket::supportsSsl()) {
qWarning("SSL not supported, skipping test");
return;
}
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
QFETCH(QSslSocket::PeerVerifyMode, peerVerifyMode);
#if QT_CONFIG(schannel)
if (peerVerifyMode == QSslSocket::QueryPeer || peerVerifyMode == QSslSocket::AutoVerifyPeer)
QSKIP("Schannel doesn't tackle requesting a certificate and not receiving one.");
#endif
SslServer server;
server.addCaCertificates = testDataDir + "certs/bogus-ca.crt";
server.ignoreSslErrors = false;
server.peerVerifyMode = peerVerifyMode;
QVERIFY(server.listen());
QEventLoop loop;
QTimer::singleShot(5000, &loop, SLOT(quit()));
QFETCH(QList<QSslCertificate>, clientCerts);
QFETCH(QSslKey, clientKey);
QSslSocket client;
client.setLocalCertificateChain(clientCerts);
client.setPrivateKey(clientKey);
socket = &client;
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
connect(socket, SIGNAL(disconnected()), &loop, SLOT(quit()));
connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));
client.connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
loop.exec();
QFETCH(bool, works);
QAbstractSocket::SocketState expectedState = (works) ? QAbstractSocket::ConnectedState : QAbstractSocket::UnconnectedState;
// check server socket
QVERIFY(server.socket);
#if QT_CONFIG(schannel)
// As additional info to the QEXPECT_FAIL below:
// This is because schannel treats it as an error (client side) if you don't have a certificate
// when asked for one.
QEXPECT_FAIL("NoCert:VerifyPeer",
"The client disconnects first, which causes the event "
"loop to quit before the server disconnects.", Continue);
#endif
QCOMPARE(server.socket->state(), expectedState);
QCOMPARE(server.socket->isEncrypted(), works);
if (peerVerifyMode == QSslSocket::VerifyNone || clientCerts.isEmpty()) {
QVERIFY(server.socket->peerCertificate().isNull());
QVERIFY(server.socket->peerCertificateChain().isEmpty());
} else {
QCOMPARE(server.socket->peerCertificate(), clientCerts.first());
#if QT_CONFIG(schannel)
if (clientCerts.count() == 1 && server.socket->peerCertificateChain().count() == 2) {
QEXPECT_FAIL("",
"Schannel includes the entire chain, not just the leaf and intermediates",
Continue);
}
#endif
QCOMPARE(server.socket->peerCertificateChain(), clientCerts);
}
// check client socket
QCOMPARE(client.state(), expectedState);
QCOMPARE(client.isEncrypted(), works);
}
void tst_QSslSocket::readBufferMaxSize()
{
#if QT_CONFIG(securetransport) || QT_CONFIG(schannel)
// QTBUG-55170:
// SecureTransport back-end was ignoring read-buffer
// size limit, resulting (potentially) in a constantly
// growing internal buffer.
// The test's logic is: we set a small read buffer size on a client
// socket (to some ridiculously small value), server sends us
// a bunch of bytes , we ignore readReady signal so
// that socket's internal buffer size stays
// >= readBufferMaxSize, we wait for a quite long time
// (which previously would be enough to read completely)
// and we check socket's bytesAvaiable to be less than sent.
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
SslServer server;
QVERIFY(server.listen());
QEventLoop loop;
QSslSocketPtr client(new QSslSocket);
socket = client.data();
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit()));
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));
client->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(),
server.serverPort());
// Wait for 'encrypted' first:
QTimer::singleShot(5000, &loop, SLOT(quit()));
loop.exec();
QCOMPARE(client->state(), QAbstractSocket::ConnectedState);
QCOMPARE(client->mode(), QSslSocket::SslClientMode);
client->setReadBufferSize(10);
const QByteArray message(int(0xffff), 'a');
server.socket->write(message);
QTimer::singleShot(5000, &loop, SLOT(quit()));
loop.exec();
int readSoFar = client->bytesAvailable();
QVERIFY(readSoFar > 0 && readSoFar < message.size());
// Now, let's check that we still can read the rest of it:
QCOMPARE(client->readAll().size(), readSoFar);
client->setReadBufferSize(0);
QTimer::singleShot(1500, &loop, SLOT(quit()));
loop.exec();
QCOMPARE(client->bytesAvailable() + readSoFar, message.size());
#else
// Not needed, QSslSocket works correctly with other back-ends.
#endif // QT_CONFIG(securetransport) || QT_CONFIG(schannel)
}
void tst_QSslSocket::setEmptyDefaultConfiguration() // this test should be last, as it has some side effects
{
// used to produce a crash in QSslConfigurationPrivate::deepCopyDefaultConfiguration, QTBUG-13265
if (!QSslSocket::supportsSsl())
return;
QSslConfiguration emptyConf;
QSslConfiguration::setDefaultConfiguration(emptyConf);
QSslSocketPtr client = newSocket();
socket = client.data();
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
QFETCH_GLOBAL(bool, setProxy);
if (setProxy && socket->waitForEncrypted(4000))
QSKIP("Skipping flaky test - See QTBUG-29941");
}
void tst_QSslSocket::allowedProtocolNegotiation()
{
#ifndef ALPN_SUPPORTED
QSKIP("ALPN is unsupported, skipping test");
#endif
#if QT_CONFIG(schannel)
if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8_1)
QSKIP("ALPN is not supported on this version of Windows using Schannel.");
#endif
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
const QByteArray expectedNegotiated("cool-protocol");
QList<QByteArray> serverProtos;
serverProtos << expectedNegotiated << "not-so-cool-protocol";
QList<QByteArray> clientProtos;
clientProtos << "uber-cool-protocol" << expectedNegotiated << "not-so-cool-protocol";
SslServer server;
server.config.setAllowedNextProtocols(serverProtos);
QVERIFY(server.listen());
QSslSocket clientSocket;
auto configuration = clientSocket.sslConfiguration();
configuration.setAllowedNextProtocols(clientProtos);
clientSocket.setSslConfiguration(configuration);
clientSocket.connectToHostEncrypted("127.0.0.1", server.serverPort());
clientSocket.ignoreSslErrors();
QEventLoop loop;
QTimer::singleShot(5000, &loop, SLOT(quit()));
connect(&clientSocket, SIGNAL(encrypted()), &loop, SLOT(quit()));
loop.exec();
QVERIFY(server.socket->sslConfiguration().nextNegotiatedProtocol() ==
clientSocket.sslConfiguration().nextNegotiatedProtocol());
QVERIFY(server.socket->sslConfiguration().nextNegotiatedProtocol() == expectedNegotiated);
}
#ifndef QT_NO_OPENSSL
class PskProvider : public QObject
{
Q_OBJECT
public:
bool m_server;
QByteArray m_identity;
QByteArray m_psk;
explicit PskProvider(QObject *parent = 0)
: QObject(parent), m_server(false)
{
}
void setIdentity(const QByteArray &identity)
{
m_identity = identity;
}
void setPreSharedKey(const QByteArray &psk)
{
m_psk = psk;
}
public slots:
void providePsk(QSslPreSharedKeyAuthenticator *authenticator)
{
QVERIFY(authenticator);
QCOMPARE(authenticator->identityHint(), PSK_SERVER_IDENTITY_HINT);
if (m_server)
QCOMPARE(authenticator->maximumIdentityLength(), 0);
else
QVERIFY(authenticator->maximumIdentityLength() > 0);
QVERIFY(authenticator->maximumPreSharedKeyLength() > 0);
if (!m_identity.isEmpty()) {
authenticator->setIdentity(m_identity);
QCOMPARE(authenticator->identity(), m_identity);
}
if (!m_psk.isEmpty()) {
authenticator->setPreSharedKey(m_psk);
QCOMPARE(authenticator->preSharedKey(), m_psk);
}
}
};
class PskServer : public QTcpServer
{
Q_OBJECT
public:
PskServer()
: socket(0),
config(QSslConfiguration::defaultConfiguration()),
ignoreSslErrors(true),
peerVerifyMode(QSslSocket::AutoVerifyPeer),
protocol(QSsl::TlsV1_0),
m_pskProvider()
{
m_pskProvider.m_server = true;
}
QSslSocket *socket;
QSslConfiguration config;
bool ignoreSslErrors;
QSslSocket::PeerVerifyMode peerVerifyMode;
QSsl::SslProtocol protocol;
QList<QSslCipher> ciphers;
PskProvider m_pskProvider;
protected:
void incomingConnection(qintptr socketDescriptor)
{
socket = new QSslSocket(this);
socket->setSslConfiguration(config);
socket->setPeerVerifyMode(peerVerifyMode);
socket->setProtocol(protocol);
if (ignoreSslErrors)
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
if (!ciphers.isEmpty()) {
auto sslConfig = socket->sslConfiguration();
sslConfig.setCiphers(ciphers);
socket->setSslConfiguration(sslConfig);
}
QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState));
QVERIFY(!socket->peerAddress().isNull());
QVERIFY(socket->peerPort() != 0);
QVERIFY(!socket->localAddress().isNull());
QVERIFY(socket->localPort() != 0);
connect(socket, &QSslSocket::preSharedKeyAuthenticationRequired, &m_pskProvider, &PskProvider::providePsk);
socket->startServerEncryption();
}
protected slots:
void ignoreErrorSlot()
{
socket->ignoreSslErrors();
}
};
void tst_QSslSocket::simplePskConnect_data()
{
QTest::addColumn<PskConnectTestType>("pskTestType");
QTest::newRow("PskConnectDoNotHandlePsk") << PskConnectDoNotHandlePsk;
QTest::newRow("PskConnectEmptyCredentials") << PskConnectEmptyCredentials;
QTest::newRow("PskConnectWrongCredentials") << PskConnectWrongCredentials;
QTest::newRow("PskConnectWrongIdentity") << PskConnectWrongIdentity;
QTest::newRow("PskConnectWrongPreSharedKey") << PskConnectWrongPreSharedKey;
QTest::newRow("PskConnectRightCredentialsPeerVerifyFailure") << PskConnectRightCredentialsPeerVerifyFailure;
QTest::newRow("PskConnectRightCredentialsVerifyPeer") << PskConnectRightCredentialsVerifyPeer;
QTest::newRow("PskConnectRightCredentialsDoNotVerifyPeer") << PskConnectRightCredentialsDoNotVerifyPeer;
}
void tst_QSslSocket::simplePskConnect()
{
QFETCH(PskConnectTestType, pskTestType);
QSKIP("This test requires change 1f8cab2c3bcd91335684c95afa95ae71e00a94e4 on the network test server, QTQAINFRA-917");
if (!QSslSocket::supportsSsl())
QSKIP("No SSL support");
bool pskCipherFound = false;
const QList<QSslCipher> supportedCiphers = QSslConfiguration::supportedCiphers();
for (const QSslCipher &cipher : supportedCiphers) {
if (cipher.name() == PSK_CIPHER_WITHOUT_AUTH) {
pskCipherFound = true;
break;
}
}
if (!pskCipherFound)
QSKIP("SSL implementation does not support the necessary PSK cipher(s)");
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
QSKIP("This test must not be going through a proxy");
QSslSocket socket;
this->socket = &socket;
QSignalSpy connectedSpy(&socket, SIGNAL(connected()));
QVERIFY(connectedSpy.isValid());
QSignalSpy hostFoundSpy(&socket, SIGNAL(hostFound()));
QVERIFY(hostFoundSpy.isValid());
QSignalSpy disconnectedSpy(&socket, SIGNAL(disconnected()));
QVERIFY(disconnectedSpy.isValid());
QSignalSpy connectionEncryptedSpy(&socket, SIGNAL(encrypted()));
QVERIFY(connectionEncryptedSpy.isValid());
QSignalSpy sslErrorsSpy(&socket, SIGNAL(sslErrors(QList<QSslError>)));
QVERIFY(sslErrorsSpy.isValid());
QSignalSpy socketErrorsSpy(&socket, SIGNAL(error(QAbstractSocket::SocketError)));
QVERIFY(socketErrorsSpy.isValid());
QSignalSpy peerVerifyErrorSpy(&socket, SIGNAL(peerVerifyError(QSslError)));
QVERIFY(peerVerifyErrorSpy.isValid());
QSignalSpy pskAuthenticationRequiredSpy(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)));
QVERIFY(pskAuthenticationRequiredSpy.isValid());
connect(&socket, SIGNAL(connected()), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(disconnected()), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(modeChanged(QSslSocket::SslMode)), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(encrypted()), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(peerVerifyError(QSslError)), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(exitLoop()));
// force a PSK cipher w/o auth
auto sslConfig = socket.sslConfiguration();
sslConfig.setCiphers({QSslCipher(PSK_CIPHER_WITHOUT_AUTH)});
socket.setSslConfiguration(sslConfig);
PskProvider provider;
switch (pskTestType) {
case PskConnectDoNotHandlePsk:
// don't connect to the provider
break;
case PskConnectEmptyCredentials:
// connect to the psk provider, but don't actually provide any PSK nor identity
connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*)));
break;
case PskConnectWrongCredentials:
// provide totally wrong credentials
provider.setIdentity(PSK_CLIENT_IDENTITY.left(PSK_CLIENT_IDENTITY.length() - 1));
provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY.left(PSK_CLIENT_PRESHAREDKEY.length() - 1));
connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*)));
break;
case PskConnectWrongIdentity:
// right PSK, wrong identity
provider.setIdentity(PSK_CLIENT_IDENTITY.left(PSK_CLIENT_IDENTITY.length() - 1));
provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY);
connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*)));
break;
case PskConnectWrongPreSharedKey:
// right identity, wrong PSK
provider.setIdentity(PSK_CLIENT_IDENTITY);
provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY.left(PSK_CLIENT_PRESHAREDKEY.length() - 1));
connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*)));
break;
case PskConnectRightCredentialsPeerVerifyFailure:
// right identity, right PSK, but since we can't verify the other peer, we'll fail
provider.setIdentity(PSK_CLIENT_IDENTITY);
provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY);
connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*)));
break;
case PskConnectRightCredentialsVerifyPeer:
// right identity, right PSK, verify the peer (but ignore the failure) and establish the connection
provider.setIdentity(PSK_CLIENT_IDENTITY);
provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY);
connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*)));
connect(&socket, SIGNAL(peerVerifyError(QSslError)), this, SLOT(ignoreErrorSlot()));
break;
case PskConnectRightCredentialsDoNotVerifyPeer:
// right identity, right PSK, do not verify the peer and establish the connection
provider.setIdentity(PSK_CLIENT_IDENTITY);
provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY);
connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*)));
socket.setPeerVerifyMode(QSslSocket::VerifyNone);
break;
}
// check the peer verification mode
switch (pskTestType) {
case PskConnectDoNotHandlePsk:
case PskConnectEmptyCredentials:
case PskConnectWrongCredentials:
case PskConnectWrongIdentity:
case PskConnectWrongPreSharedKey:
case PskConnectRightCredentialsPeerVerifyFailure:
case PskConnectRightCredentialsVerifyPeer:
QCOMPARE(socket.peerVerifyMode(), QSslSocket::AutoVerifyPeer);
break;
case PskConnectRightCredentialsDoNotVerifyPeer:
QCOMPARE(socket.peerVerifyMode(), QSslSocket::VerifyNone);
break;
}
// Start connecting
socket.connectToHost(QtNetworkSettings::serverName(), PSK_SERVER_PORT);
QCOMPARE(socket.state(), QAbstractSocket::HostLookupState);
enterLoop(10);
// Entered connecting state
QCOMPARE(socket.state(), QAbstractSocket::ConnectingState);
QCOMPARE(connectedSpy.count(), 0);
QCOMPARE(hostFoundSpy.count(), 1);
QCOMPARE(disconnectedSpy.count(), 0);
enterLoop(10);
// Entered connected state
QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
QCOMPARE(socket.mode(), QSslSocket::UnencryptedMode);
QVERIFY(!socket.isEncrypted());
QCOMPARE(connectedSpy.count(), 1);
QCOMPARE(hostFoundSpy.count(), 1);
QCOMPARE(disconnectedSpy.count(), 0);
// Enter encrypted mode
socket.startClientEncryption();
QCOMPARE(socket.mode(), QSslSocket::SslClientMode);
QVERIFY(!socket.isEncrypted());
QCOMPARE(connectionEncryptedSpy.count(), 0);
QCOMPARE(sslErrorsSpy.count(), 0);
QCOMPARE(peerVerifyErrorSpy.count(), 0);
// Start handshake.
enterLoop(10);
// We must get the PSK signal in all cases
QCOMPARE(pskAuthenticationRequiredSpy.count(), 1);
switch (pskTestType) {
case PskConnectDoNotHandlePsk:
case PskConnectEmptyCredentials:
case PskConnectWrongCredentials:
case PskConnectWrongIdentity:
case PskConnectWrongPreSharedKey:
// Handshake failure
QCOMPARE(socketErrorsSpy.count(), 1);
QCOMPARE(qvariant_cast<QAbstractSocket::SocketError>(socketErrorsSpy.at(0).at(0)), QAbstractSocket::SslHandshakeFailedError);
QCOMPARE(sslErrorsSpy.count(), 0);
QCOMPARE(peerVerifyErrorSpy.count(), 0);
QCOMPARE(connectionEncryptedSpy.count(), 0);
QVERIFY(!socket.isEncrypted());
break;
case PskConnectRightCredentialsPeerVerifyFailure:
// Peer verification failure
QCOMPARE(socketErrorsSpy.count(), 1);
QCOMPARE(qvariant_cast<QAbstractSocket::SocketError>(socketErrorsSpy.at(0).at(0)), QAbstractSocket::SslHandshakeFailedError);
QCOMPARE(sslErrorsSpy.count(), 1);
QCOMPARE(peerVerifyErrorSpy.count(), 1);
QCOMPARE(connectionEncryptedSpy.count(), 0);
QVERIFY(!socket.isEncrypted());
break;
case PskConnectRightCredentialsVerifyPeer:
// Peer verification failure, but ignore it and keep connecting
QCOMPARE(socketErrorsSpy.count(), 0);
QCOMPARE(sslErrorsSpy.count(), 1);
QCOMPARE(peerVerifyErrorSpy.count(), 1);
QCOMPARE(connectionEncryptedSpy.count(), 1);
QVERIFY(socket.isEncrypted());
QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
break;
case PskConnectRightCredentialsDoNotVerifyPeer:
// No peer verification => no failure
QCOMPARE(socketErrorsSpy.count(), 0);
QCOMPARE(sslErrorsSpy.count(), 0);
QCOMPARE(peerVerifyErrorSpy.count(), 0);
QCOMPARE(connectionEncryptedSpy.count(), 1);
QVERIFY(socket.isEncrypted());
QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
break;
}
// check writing
switch (pskTestType) {
case PskConnectDoNotHandlePsk:
case PskConnectEmptyCredentials:
case PskConnectWrongCredentials:
case PskConnectWrongIdentity:
case PskConnectWrongPreSharedKey:
case PskConnectRightCredentialsPeerVerifyFailure:
break;
case PskConnectRightCredentialsVerifyPeer:
case PskConnectRightCredentialsDoNotVerifyPeer:
socket.write("Hello from Qt TLS/PSK!");
QVERIFY(socket.waitForBytesWritten());
break;
}
// disconnect
switch (pskTestType) {
case PskConnectDoNotHandlePsk:
case PskConnectEmptyCredentials:
case PskConnectWrongCredentials:
case PskConnectWrongIdentity:
case PskConnectWrongPreSharedKey:
case PskConnectRightCredentialsPeerVerifyFailure:
break;
case PskConnectRightCredentialsVerifyPeer:
case PskConnectRightCredentialsDoNotVerifyPeer:
socket.disconnectFromHost();
enterLoop(10);
break;
}
QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState);
QCOMPARE(disconnectedSpy.count(), 1);
}
void tst_QSslSocket::ephemeralServerKey_data()
{
#ifdef Q_OS_WINRT
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
QTest::addColumn<QString>("cipher");
QTest::addColumn<bool>("emptyKey");
#if !QT_CONFIG(opensslv11) // 1.1 drops support for RC4-SHA
QTest::newRow("NonForwardSecrecyCipher") << "RC4-SHA" << true;
#endif // !opensslv11
QTest::newRow("ForwardSecrecyCipher") << "ECDHE-RSA-AES256-SHA" << (QSslSocket::sslLibraryVersionNumber() < 0x10002000L);
}
void tst_QSslSocket::ephemeralServerKey()
{
QFETCH_GLOBAL(bool, setProxy);
if (!QSslSocket::supportsSsl() || setProxy)
return;
QFETCH(QString, cipher);
QFETCH(bool, emptyKey);
SslServer server;
server.config.setCiphers(QList<QSslCipher>() << QSslCipher(cipher));
QVERIFY(server.listen());
QSslSocketPtr client = newSocket();
socket = client.data();
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
QSignalSpy spy(client.data(), &QSslSocket::encrypted);
client->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
spy.wait();
QCOMPARE(spy.count(), 1);
QVERIFY(server.config.ephemeralServerKey().isNull());
QCOMPARE(client->sslConfiguration().ephemeralServerKey().isNull(), emptyKey);
}
void tst_QSslSocket::pskServer()
{
#ifdef Q_OS_WINRT
QSKIP("Server-side encryption is not implemented on WinRT.");
#endif
#if QT_CONFIG(schannel)
QSKIP("Schannel does not have PSK support implemented.");
#endif
QFETCH_GLOBAL(bool, setProxy);
if (!QSslSocket::supportsSsl() || setProxy)
return;
QSslSocket socket;
this->socket = &socket;
QSignalSpy connectedSpy(&socket, SIGNAL(connected()));
QVERIFY(connectedSpy.isValid());
QSignalSpy disconnectedSpy(&socket, SIGNAL(disconnected()));
QVERIFY(disconnectedSpy.isValid());
QSignalSpy connectionEncryptedSpy(&socket, SIGNAL(encrypted()));
QVERIFY(connectionEncryptedSpy.isValid());
QSignalSpy pskAuthenticationRequiredSpy(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)));
QVERIFY(pskAuthenticationRequiredSpy.isValid());
connect(&socket, SIGNAL(connected()), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(disconnected()), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(modeChanged(QSslSocket::SslMode)), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(encrypted()), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(peerVerifyError(QSslError)), this, SLOT(exitLoop()));
connect(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(exitLoop()));
// force a PSK cipher w/o auth
auto sslConfig = socket.sslConfiguration();
sslConfig.setCiphers({QSslCipher(PSK_CIPHER_WITHOUT_AUTH)});
socket.setSslConfiguration(sslConfig);
PskProvider provider;
provider.setIdentity(PSK_CLIENT_IDENTITY);
provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY);
connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*)));
socket.setPeerVerifyMode(QSslSocket::VerifyNone);
PskServer server;
server.m_pskProvider.setIdentity(provider.m_identity);
server.m_pskProvider.setPreSharedKey(provider.m_psk);
server.config.setPreSharedKeyIdentityHint(PSK_SERVER_IDENTITY_HINT);
QVERIFY(server.listen());
// Start connecting
socket.connectToHost(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
enterLoop(5);
// Entered connected state
QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
QCOMPARE(socket.mode(), QSslSocket::UnencryptedMode);
QVERIFY(!socket.isEncrypted());
QCOMPARE(connectedSpy.count(), 1);
QCOMPARE(disconnectedSpy.count(), 0);
// Enter encrypted mode
socket.startClientEncryption();
QCOMPARE(socket.mode(), QSslSocket::SslClientMode);
QVERIFY(!socket.isEncrypted());
QCOMPARE(connectionEncryptedSpy.count(), 0);
// Start handshake.
enterLoop(10);
// We must get the PSK signal in all cases
QCOMPARE(pskAuthenticationRequiredSpy.count(), 1);
QCOMPARE(connectionEncryptedSpy.count(), 1);
QVERIFY(socket.isEncrypted());
QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
// check writing
socket.write("Hello from Qt TLS/PSK!");
QVERIFY(socket.waitForBytesWritten());
// disconnect
socket.disconnectFromHost();
enterLoop(10);
QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState);
QCOMPARE(disconnectedSpy.count(), 1);
}
void tst_QSslSocket::signatureAlgorithm_data()
{
if (!QSslSocket::supportsSsl())
QSKIP("Signature algorithms cannot be tested without SSL support");
if (QSslSocket::sslLibraryVersionNumber() < 0x10002000L)
QSKIP("Signature algorithms cannot be tested with OpenSSL < 1.0.2");
if (QSslSocket::sslLibraryVersionNumber() >= 0x10101000L) {
// FIXME: investigate if this test makes any sense with TLS 1.3.
QSKIP("Test is not valid for TLS 1.3/OpenSSL 1.1.1");
}
QTest::addColumn<QByteArrayList>("serverSigAlgPairs");
QTest::addColumn<QSsl::SslProtocol>("serverProtocol");
QTest::addColumn<QByteArrayList>("clientSigAlgPairs");
QTest::addColumn<QSsl::SslProtocol>("clientProtocol");
QTest::addColumn<QAbstractSocket::SocketState>("state");
const QByteArray dsaSha1("DSA+SHA1");
const QByteArray ecdsaSha1("ECDSA+SHA1");
const QByteArray ecdsaSha512("ECDSA+SHA512");
const QByteArray rsaSha256("RSA+SHA256");
const QByteArray rsaSha384("RSA+SHA384");
const QByteArray rsaSha512("RSA+SHA512");
QTest::newRow("match_TlsV1_2")
<< QByteArrayList({rsaSha256})
<< QSsl::TlsV1_2
<< QByteArrayList({rsaSha256})
<< QSsl::AnyProtocol
<< QAbstractSocket::ConnectedState;
QTest::newRow("no_hashalg_match_TlsV1_2")
<< QByteArrayList({rsaSha256})
<< QSsl::TlsV1_2
<< QByteArrayList({rsaSha512})
<< QSsl::AnyProtocol
<< QAbstractSocket::UnconnectedState;
QTest::newRow("no_sigalg_match_TlsV1_2")
<< QByteArrayList({ecdsaSha512})
<< QSsl::TlsV1_2
<< QByteArrayList({rsaSha512})
<< QSsl::AnyProtocol
<< QAbstractSocket::UnconnectedState;
QTest::newRow("no_cipher_match_AnyProtocol")
<< QByteArrayList({rsaSha512})
<< QSsl::AnyProtocol
<< QByteArrayList({ecdsaSha512})
<< QSsl::AnyProtocol
<< QAbstractSocket::UnconnectedState;
QTest::newRow("match_multiple-choice")
<< QByteArrayList({dsaSha1, rsaSha256, rsaSha384, rsaSha512})
<< QSsl::AnyProtocol
<< QByteArrayList({ecdsaSha1, rsaSha384, rsaSha512, ecdsaSha512})
<< QSsl::AnyProtocol
<< QAbstractSocket::ConnectedState;
QTest::newRow("match_client_longer")
<< QByteArrayList({dsaSha1, rsaSha256})
<< QSsl::AnyProtocol
<< QByteArrayList({ecdsaSha1, ecdsaSha512, rsaSha256})
<< QSsl::AnyProtocol
<< QAbstractSocket::ConnectedState;
QTest::newRow("match_server_longer")
<< QByteArrayList({ecdsaSha1, ecdsaSha512, rsaSha256})
<< QSsl::AnyProtocol
<< QByteArrayList({dsaSha1, rsaSha256})
<< QSsl::AnyProtocol
<< QAbstractSocket::ConnectedState;
// signature algorithms do not match, but are ignored because the tls version is not v1.2
QTest::newRow("client_ignore_TlsV1_1")
<< QByteArrayList({rsaSha256})
<< QSsl::TlsV1_1
<< QByteArrayList({rsaSha512})
<< QSsl::AnyProtocol
<< QAbstractSocket::ConnectedState;
QTest::newRow("server_ignore_TlsV1_1")
<< QByteArrayList({rsaSha256})
<< QSsl::AnyProtocol
<< QByteArrayList({rsaSha512})
<< QSsl::TlsV1_1
<< QAbstractSocket::ConnectedState;
QTest::newRow("client_ignore_TlsV1_0")
<< QByteArrayList({rsaSha256})
<< QSsl::TlsV1_0
<< QByteArrayList({rsaSha512})
<< QSsl::AnyProtocol
<< QAbstractSocket::ConnectedState;
QTest::newRow("server_ignore_TlsV1_0")
<< QByteArrayList({rsaSha256})
<< QSsl::AnyProtocol
<< QByteArrayList({rsaSha512})
<< QSsl::TlsV1_0
<< QAbstractSocket::ConnectedState;
}
void tst_QSslSocket::signatureAlgorithm()
{
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
QSKIP("Test not adapted for use with proxying");
QFETCH(QByteArrayList, serverSigAlgPairs);
QFETCH(QSsl::SslProtocol, serverProtocol);
QFETCH(QByteArrayList, clientSigAlgPairs);
QFETCH(QSsl::SslProtocol, clientProtocol);
QFETCH(QAbstractSocket::SocketState, state);
SslServer server;
server.protocol = serverProtocol;
server.config.setCiphers({QSslCipher("ECDHE-RSA-AES256-SHA")});
server.config.setBackendConfigurationOption(QByteArrayLiteral("SignatureAlgorithms"), serverSigAlgPairs.join(':'));
QVERIFY(server.listen());
QSslConfiguration clientConfig = QSslConfiguration::defaultConfiguration();
clientConfig.setProtocol(clientProtocol);
clientConfig.setBackendConfigurationOption(QByteArrayLiteral("SignatureAlgorithms"), clientSigAlgPairs.join(':'));
QSslSocket client;
client.setSslConfiguration(clientConfig);
socket = &client;
QEventLoop loop;
QTimer::singleShot(5000, &loop, &QEventLoop::quit);
connect(socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), &loop, &QEventLoop::quit);
connect(socket, QOverload<const QList<QSslError> &>::of(&QSslSocket::sslErrors), this, &tst_QSslSocket::ignoreErrorSlot);
connect(socket, &QSslSocket::encrypted, &loop, &QEventLoop::quit);
client.connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
loop.exec();
socket = nullptr;
QCOMPARE(client.state(), state);
}
void tst_QSslSocket::forwardReadChannelFinished()
{
if (!QSslSocket::supportsSsl())
QSKIP("Needs SSL");
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
QSKIP("This test doesn't work via a proxy");
QSslSocket socket;
QSignalSpy readChannelFinishedSpy(&socket, &QAbstractSocket::readChannelFinished);
connect(&socket, &QSslSocket::encrypted, [&socket]() {
const auto data = QString("GET /ip HTTP/1.0\r\nHost: %1\r\n\r\nAccept: */*\r\n\r\n")
.arg(QtNetworkSettings::serverLocalName()).toUtf8();
socket.write(data);
});
connect(&socket, &QSslSocket::readChannelFinished,
&QTestEventLoop::instance(), &QTestEventLoop::exitLoop);
socket.connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443);
enterLoop(10);
QVERIFY(readChannelFinishedSpy.count());
}
#endif // QT_NO_OPENSSL
void tst_QSslSocket::disabledProtocols_data()
{
QTest::addColumn<QSsl::SslProtocol>("disabledProtocol");
QTest::newRow("SslV2") << QSsl::SslV2;
QTest::newRow("SslV3") << QSsl::SslV3;
}
void tst_QSslSocket::disabledProtocols()
{
QFETCH_GLOBAL(const bool, setProxy);
if (setProxy)
return;
QFETCH(const QSsl::SslProtocol, disabledProtocol);
const int timeoutMS = 500;
// Test a client socket.
{
// 0. connectToHostEncrypted: client-side, non-blocking API, error is discovered
// early, preventing any real connection from ever starting.
QSslSocket socket;
socket.setProtocol(disabledProtocol);
QCOMPARE(socket.error(), QAbstractSocket::UnknownSocketError);
socket.connectToHostEncrypted(QStringLiteral("doesnotmatter.org"), 1010);
QCOMPARE(socket.error(), QAbstractSocket::SslInvalidUserDataError);
QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState);
}
{
// 1. startClientEncryption: client-side, non blocking API, but wants a socket in
// the 'connected' state (otherwise just returns false not setting any error code).
SslServer server;
QVERIFY(server.listen());
QSslSocket socket;
QCOMPARE(socket.error(), QAbstractSocket::UnknownSocketError);
socket.connectToHost(QHostAddress::LocalHost, server.serverPort());
QVERIFY(socket.waitForConnected(timeoutMS));
socket.setProtocol(disabledProtocol);
socket.startClientEncryption();
QCOMPARE(socket.error(), QAbstractSocket::SslInvalidUserDataError);
}
{
// 2. waitForEncrypted: client-side, blocking API plus requires from us
// to call ... connectToHostEncrypted(), which will notice an error and
// will prevent any connect at all. Nothing to test.
}
// Test a server side, relatively simple: server does not connect, it listens/accepts
// and then calls startServerEncryption() (which must fall).
{
SslServer server;
server.protocol = disabledProtocol;
QVERIFY(server.listen());
QTestEventLoop loop;
connect(&server, &SslServer::socketError, [&loop](QAbstractSocket::SocketError)
{loop.exitLoop();});
QTcpSocket client;
client.connectToHost(QHostAddress::LocalHost, server.serverPort());
loop.enterLoopMSecs(timeoutMS);
QVERIFY(!loop.timeout());
QVERIFY(server.socket);
QCOMPARE(server.socket->error(), QAbstractSocket::SslInvalidUserDataError);
}
}
void tst_QSslSocket::oldErrorsOnSocketReuse()
{
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return; // not relevant
SslServer server;
server.protocol = QSsl::TlsV1_1;
server.m_certFile = testDataDir + "certs/fluke.cert";
server.m_keyFile = testDataDir + "certs/fluke.key";
QVERIFY(server.listen(QHostAddress::SpecialAddress::LocalHost));
QSslSocket socket;
socket.setProtocol(QSsl::TlsV1_1);
QList<QSslError> errorList;
auto connection = connect(&socket, QOverload<const QList<QSslError> &>::of(&QSslSocket::sslErrors),
[&socket, &errorList](const QList<QSslError> &errors) {
errorList += errors;
socket.ignoreSslErrors(errors);
socket.resume();
});
socket.connectToHostEncrypted(QString::fromLatin1("localhost"), server.serverPort());
QVERIFY(QTest::qWaitFor([&socket](){ return socket.isEncrypted(); }));
socket.disconnectFromHost();
if (socket.state() != QAbstractSocket::UnconnectedState) {
QVERIFY(QTest::qWaitFor(
[&socket](){
return socket.state() == QAbstractSocket::UnconnectedState;
}));
}
auto oldList = errorList;
errorList.clear();
server.close();
server.m_certFile = testDataDir + "certs/bogus-client.crt";
server.m_keyFile = testDataDir + "certs/bogus-client.key";
QVERIFY(server.listen(QHostAddress::SpecialAddress::LocalHost));
socket.connectToHostEncrypted(QString::fromLatin1("localhost"), server.serverPort());
QVERIFY(QTest::qWaitFor([&socket](){ return socket.isEncrypted(); }));
for (const auto &error : oldList) {
QVERIFY2(!errorList.contains(error),
"The new errors should not contain any of the old ones");
}
}
#endif // QT_NO_SSL
QTEST_MAIN(tst_QSslSocket)
#include "tst_qsslsocket.moc"