1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtNetwork>
31#include <QtTest/QtTest>
32
33#include <QNetworkProxy>
34#include <QAuthenticator>
35
36#ifdef QT_BUILD_INTERNAL
37# include "private/qhostinfo_p.h"
38# ifndef QT_NO_OPENSSL
39# include "private/qsslsocket_p.h"
40# endif // !QT_NO_OPENSSL
41#endif // QT_BUILD_INTERNAL
42#include "../../../network-settings.h"
43
44#ifndef QT_NO_OPENSSL
45typedef QSharedPointer<QSslSocket> QSslSocketPtr;
46
47QT_BEGIN_NAMESPACE
48void qt_ForceTlsSecurityLevel();
49QT_END_NAMESPACE
50
51#endif
52
53class tst_QSslSocket_onDemandCertificates_member : public QObject
54{
55 Q_OBJECT
56
57 int proxyAuthCalled;
58
59public:
60
61#ifndef QT_NO_OPENSSL
62 tst_QSslSocket_onDemandCertificates_member()
63 {
64 QT_PREPEND_NAMESPACE(qt_ForceTlsSecurityLevel)();
65 }
66 QSslSocketPtr newSocket();
67#endif
68
69public slots:
70 void initTestCase_data();
71 void initTestCase();
72 void init();
73 void cleanup();
74 void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth);
75
76#ifndef QT_NO_OPENSSL
77private slots:
78 void onDemandRootCertLoadingMemberMethods();
79
80private:
81 QSslSocket *socket = nullptr;
82#endif // QT_NO_OPENSSL
83};
84
85enum ProxyTests {
86 NoProxy = 0x00,
87 Socks5Proxy = 0x01,
88 HttpProxy = 0x02,
89 TypeMask = 0x0f,
90
91 NoAuth = 0x00,
92 AuthBasic = 0x10,
93 AuthNtlm = 0x20,
94 AuthMask = 0xf0
95};
96
97void tst_QSslSocket_onDemandCertificates_member::initTestCase_data()
98{
99 QTest::addColumn<bool>(name: "setProxy");
100 QTest::addColumn<int>(name: "proxyType");
101
102 QTest::newRow(dataTag: "WithoutProxy") << false << 0;
103 QTest::newRow(dataTag: "WithSocks5Proxy") << true << int(Socks5Proxy);
104 QTest::newRow(dataTag: "WithSocks5ProxyAuth") << true << int(Socks5Proxy | AuthBasic);
105
106 QTest::newRow(dataTag: "WithHttpProxy") << true << int(HttpProxy);
107 QTest::newRow(dataTag: "WithHttpProxyBasicAuth") << true << int(HttpProxy | AuthBasic);
108 // uncomment the line below when NTLM works
109// QTest::newRow("WithHttpProxyNtlmAuth") << true << int(HttpProxy | AuthNtlm);
110}
111
112void tst_QSslSocket_onDemandCertificates_member::initTestCase()
113{
114#ifdef QT_TEST_SERVER
115 QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::socksProxyServerName(), 1080));
116 QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::socksProxyServerName(), 1081));
117 QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpProxyServerName(), 3128));
118 QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpProxyServerName(), 3129));
119 QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpProxyServerName(), 3130));
120#else
121 if (!QtNetworkSettings::verifyTestNetworkSettings())
122 QSKIP("No network test server available");
123#endif // QT_TEST_SERVER
124}
125
126void tst_QSslSocket_onDemandCertificates_member::init()
127{
128 QFETCH_GLOBAL(bool, setProxy);
129 if (setProxy) {
130 QFETCH_GLOBAL(int, proxyType);
131 const auto socksAddr = QtNetworkSettings::socksProxyServerIp().toString();
132 const auto squidAddr = QtNetworkSettings::httpProxyServerIp().toString();
133 QNetworkProxy proxy;
134
135 switch (proxyType) {
136 case Socks5Proxy:
137 proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, socksAddr, 1080);
138 break;
139
140 case Socks5Proxy | AuthBasic:
141 proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, socksAddr, 1081);
142 break;
143
144 case HttpProxy | NoAuth:
145 proxy = QNetworkProxy(QNetworkProxy::HttpProxy, squidAddr, 3128);
146 break;
147
148 case HttpProxy | AuthBasic:
149 proxy = QNetworkProxy(QNetworkProxy::HttpProxy, squidAddr, 3129);
150 break;
151
152 case HttpProxy | AuthNtlm:
153 proxy = QNetworkProxy(QNetworkProxy::HttpProxy, squidAddr, 3130);
154 break;
155 }
156 QNetworkProxy::setApplicationProxy(proxy);
157 }
158
159 qt_qhostinfo_clear_cache();
160}
161
162void tst_QSslSocket_onDemandCertificates_member::cleanup()
163{
164 QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy);
165}
166
167#ifndef QT_NO_OPENSSL
168QSslSocketPtr tst_QSslSocket_onDemandCertificates_member::newSocket()
169{
170 QSslSocket *socket = new QSslSocket;
171
172 proxyAuthCalled = 0;
173 connect(asender: socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
174 SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
175 atype: Qt::DirectConnection);
176
177 return QSslSocketPtr(socket);
178}
179#endif
180
181void tst_QSslSocket_onDemandCertificates_member::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth)
182{
183 ++proxyAuthCalled;
184 auth->setUser("qsockstest");
185 auth->setPassword("password");
186}
187
188#ifndef QT_NO_OPENSSL
189
190static bool waitForEncrypted(QSslSocket *socket)
191{
192 Q_ASSERT(socket);
193
194 QEventLoop eventLoop;
195
196 QTimer connectionTimeoutWatcher;
197 connectionTimeoutWatcher.setSingleShot(true);
198 connectionTimeoutWatcher.connect(sender: &connectionTimeoutWatcher, signal: &QTimer::timeout,
199 slot: [&eventLoop]() {
200 eventLoop.exit();
201 });
202
203 bool encrypted = false;
204 socket->connect(sender: socket, signal: &QSslSocket::encrypted, slot: [&eventLoop, &encrypted](){
205 eventLoop.exit();
206 encrypted = true;
207 });
208
209 socket->connect(sender: socket, signal: QOverload<const QList<QSslError>&>::of(ptr: &QSslSocket::sslErrors),
210 slot: [&eventLoop](){
211 eventLoop.exit();
212 });
213
214 // Wait for 30 s. maximum - the default timeout in our QSslSocket::waitForEncrypted ...
215 connectionTimeoutWatcher.start(msec: 30000);
216 eventLoop.exec();
217 return encrypted;
218}
219
220void tst_QSslSocket_onDemandCertificates_member::onDemandRootCertLoadingMemberMethods()
221{
222 const QString host("www.qt.io");
223
224 // not using any root certs -> should not work
225 QSslSocketPtr socket2 = newSocket();
226 this->socket = socket2.data();
227 auto sslConfig = socket2->sslConfiguration();
228 sslConfig.setCaCertificates(QList<QSslCertificate>());
229 socket2->setSslConfiguration(sslConfig);
230 socket2->connectToHostEncrypted(hostName: host, port: 443);
231 QVERIFY(!waitForEncrypted(socket2.data()));
232
233 // default: using on demand loading -> should work
234 QSslSocketPtr socket = newSocket();
235 this->socket = socket.data();
236 socket->connectToHostEncrypted(hostName: host, port: 443);
237 QVERIFY2(waitForEncrypted(socket.data()), qPrintable(socket->errorString()));
238
239 // not using any root certs again -> should not work
240 QSslSocketPtr socket3 = newSocket();
241 this->socket = socket3.data();
242 sslConfig = socket3->sslConfiguration();
243 sslConfig.setCaCertificates(QList<QSslCertificate>());
244 socket3->setSslConfiguration(sslConfig);
245 socket3->connectToHostEncrypted(hostName: host, port: 443);
246 QVERIFY(!waitForEncrypted(socket3.data()));
247
248 // setting empty SSL configuration explicitly -> depends on on-demand loading
249 QSslSocketPtr socket4 = newSocket();
250 this->socket = socket4.data();
251 QSslConfiguration conf;
252 socket4->setSslConfiguration(conf);
253 socket4->connectToHostEncrypted(hostName: host, port: 443);
254#ifdef QT_BUILD_INTERNAL
255 const bool works = QSslSocketPrivate::rootCertOnDemandLoadingSupported();
256#if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
257 QCOMPARE(works, true);
258#elif defined(Q_OS_MAC)
259 QCOMPARE(works, false);
260#endif // other platforms: undecided.
261 // When we *allow* on-demand loading, we enable it by default; so, on Unix,
262 // it will work without setting any certificates. Otherwise, the configuration
263 // contains an empty set of certificates, so on-demand loading shall fail.
264 QCOMPARE(waitForEncrypted(socket4.data()), works);
265#endif // QT_BUILD_INTERNAL
266}
267
268#endif // QT_NO_OPENSSL
269
270QTEST_MAIN(tst_QSslSocket_onDemandCertificates_member)
271#include "tst_qsslsocket_onDemandCertificates_member.moc"
272

source code of qtbase/tests/auto/network/ssl/qsslsocket_onDemandCertificates_member/tst_qsslsocket_onDemandCertificates_member.cpp