QSslConfiguration: toggle on demand loading of root certs properly

make sure we keep track of when we can load root certs and when we
cannot (we cannot when the developer set the certs explicitly). This is
implemented the same way for QSslSocket already, and needs to be
duplicated because we have 2 methods for setting CA certificates: one in
QSslSocket and one in QSslConfiguration.
In addition, adapt the auto test which checks whether setting a default
QSslConfiguration works: There is no way to set on demand loading
through the API, so it should be enabled by default.

Task-number: QTBUG-29103
Change-Id: I5146128aaa385dfcc0ad1e0ef81a92d9350ec5f2
Reviewed-by: Richard J. Moore <rich@kde.org>
This commit is contained in:
Peter Hartmann 2013-01-14 14:43:52 +01:00 committed by The Qt Project
parent 786a6466e8
commit ce35c0db0d
7 changed files with 90 additions and 5 deletions

View File

@ -181,6 +181,7 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const
d->protocol == other.d->protocol &&
d->peerVerifyMode == other.d->peerVerifyMode &&
d->peerVerifyDepth == other.d->peerVerifyDepth &&
d->allowRootCertOnDemandLoading == other.d->allowRootCertOnDemandLoading &&
d->sslOptions == other.d->sslOptions;
}
@ -208,6 +209,7 @@ bool QSslConfiguration::isNull() const
return (d->protocol == QSsl::SecureProtocols &&
d->peerVerifyMode == QSslSocket::AutoVerifyPeer &&
d->peerVerifyDepth == 0 &&
d->allowRootCertOnDemandLoading == true &&
d->caCertificates.count() == 0 &&
d->ciphers.count() == 0 &&
d->localCertificate.isNull() &&
@ -519,6 +521,7 @@ QList<QSslCertificate> QSslConfiguration::caCertificates() const
void QSslConfiguration::setCaCertificates(const QList<QSslCertificate> &certificates)
{
d->caCertificates = certificates;
d->allowRootCertOnDemandLoading = false;
}
/*!

View File

@ -83,6 +83,7 @@ public:
: protocol(QSsl::SecureProtocols),
peerVerifyMode(QSslSocket::AutoVerifyPeer),
peerVerifyDepth(0),
allowRootCertOnDemandLoading(true),
sslOptions(QSslConfigurationPrivate::defaultSslOptions)
{ }
@ -98,6 +99,7 @@ public:
QSsl::SslProtocol protocol;
QSslSocket::PeerVerifyMode peerVerifyMode;
int peerVerifyDepth;
bool allowRootCertOnDemandLoading;
QSsl::SslOptions sslOptions;

View File

@ -903,7 +903,12 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration)
d->configuration.peerVerifyMode = configuration.peerVerifyMode();
d->configuration.protocol = configuration.protocol();
d->configuration.sslOptions = configuration.d->sslOptions;
d->allowRootCertOnDemandLoading = false;
// if the CA certificates were set explicitly (either via
// QSslConfiguration::setCaCertificates() or QSslSocket::setCaCertificates(),
// we cannot load the certificates on demand
if (!configuration.d->allowRootCertOnDemandLoading)
d->allowRootCertOnDemandLoading = false;
}
/*!
@ -2378,6 +2383,14 @@ QByteArray QSslSocketPrivate::peek(qint64 maxSize)
}
}
/*!
\internal
*/
bool QSslSocketPrivate::rootCertOnDemandLoadingSupported()
{
return s_loadRootCertsOnDemand;
}
/*!
\internal
*/

View File

@ -182,6 +182,8 @@ public:
virtual QSslCipher sessionCipher() const = 0;
virtual void continueHandshake() = 0;
Q_AUTOTEST_EXPORT static bool rootCertOnDemandLoadingSupported();
private:
static bool ensureLibraryLoaded();
static void ensureCiphersAndCertsLoaded();

View File

@ -46,7 +46,10 @@
#include <QNetworkProxy>
#include <QAuthenticator>
#ifdef QT_BUILD_INTERNAL
#include "private/qhostinfo_p.h"
#include "private/qsslsocket_p.h"
#endif
#include "../../../network-settings.h"
@ -211,12 +214,31 @@ void tst_QSslSocket_onDemandCertificates_member::onDemandRootCertLoadingMemberMe
socket3->connectToHostEncrypted(host, 443);
QVERIFY(!socket3->waitForEncrypted());
// setting empty SSL configuration explicitly -> should not work
// setting empty SSL configuration explicitly -> depends on on-demand loading
QSslSocketPtr socket4 = newSocket();
this->socket = socket4.data();
socket4->setSslConfiguration(QSslConfiguration());
QSslConfiguration conf;
socket4->setSslConfiguration(conf);
socket4->connectToHostEncrypted(host, 443);
QVERIFY(!socket4->waitForEncrypted());
#ifdef QT_BUILD_INTERNAL
bool rootCertLoadingAllowed = QSslSocketPrivate::rootCertOnDemandLoadingSupported();
#if defined(Q_OS_LINUX) || defined (Q_OS_BLACKBERRY)
QCOMPARE(rootCertLoadingAllowed, true);
#elif defined(Q_OS_MAC)
QCOMPARE(rootCertLoadingAllowed, false);
#endif // other platforms: undecided (Windows: depends on the version)
// when we allow on demand loading, it is enabled by default,
// so on Unix it will work without setting any certificates. Otherwise,
// the configuration contains an empty set of certificates
// and will fail.
bool works;
#if defined (Q_OS_WIN)
works = false; // on Windows, this won't work even though we use on demand loading
#else
works = rootCertLoadingAllowed;
#endif
QCOMPARE(socket4->waitForEncrypted(), works);
#endif // QT_BUILD_INTERNAL
}
#endif // QT_NO_OPENSSL

View File

@ -46,8 +46,13 @@
#include <QtNetwork/qnetworkreply.h>
#include <QtNetwork/qnetworkrequest.h>
#include <QtNetwork/qnetworkaccessmanager.h>
#include <QtNetwork/qsslconfiguration.h>
#include "../../auto/network-settings.h"
#ifdef QT_BUILD_INTERNAL
#include "private/qsslsocket_p.h"
#endif
#define BANDWIDTH_LIMIT_BYTES (1024*100)
#define TIME_ESTIMATION_SECONDS (97)
@ -58,6 +63,8 @@ private slots:
void initTestCase();
void limiting_data();
void limiting();
void setSslConfiguration_data();
void setSslConfiguration();
};
QNetworkReply *reply;
@ -129,6 +136,42 @@ void tst_qnetworkreply::limiting()
QVERIFY(!QTestEventLoop::instance().timeout());
}
void tst_qnetworkreply::setSslConfiguration_data()
{
QTest::addColumn<QUrl>("url");
QTest::addColumn<bool>("works");
QTest::newRow("codereview.qt-project.org") << QUrl("https://codereview.qt-project.org") << true;
QTest::newRow("test-server") << QUrl("https://" + QtNetworkSettings::serverName() + "/") << false;
}
void tst_qnetworkreply::setSslConfiguration()
{
QFETCH(QUrl, url);
QNetworkRequest request(url);
QSslConfiguration conf = request.sslConfiguration();
conf.setProtocol(QSsl::TlsV1_0); // TLS 1.0 will be used anyway, just make sure we change the configuration
request.setSslConfiguration(conf);
QNetworkAccessManager manager;
reply = manager.get(request);
QObject::connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(15);
QVERIFY(!QTestEventLoop::instance().timeout());
#ifdef QT_BUILD_INTERNAL
QFETCH(bool, works);
bool rootCertLoadingAllowed = QSslSocketPrivate::rootCertOnDemandLoadingSupported();
#if defined(Q_OS_LINUX) || defined (Q_OS_BLACKBERRY)
QCOMPARE(rootCertLoadingAllowed, true);
#elif defined(Q_OS_MAC)
QCOMPARE(rootCertLoadingAllowed, false);
#endif // other platforms: undecided (Windows: depends on the version)
if (works) {
QCOMPARE(reply->error(), QNetworkReply::NoError);
} else {
QCOMPARE(reply->error(), QNetworkReply::SslHandshakeFailedError);
}
#endif
}
QTEST_MAIN(tst_qnetworkreply)

View File

@ -2,7 +2,7 @@ TEMPLATE = app
TARGET = tst_qnetworkreply
QT -= gui
QT += network testlib
QT += core-private network network-private testlib
SOURCES += main.cpp
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0