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 |
45 | typedef QSharedPointer<QSslSocket> QSslSocketPtr; |
46 | |
47 | QT_BEGIN_NAMESPACE |
48 | void qt_ForceTlsSecurityLevel(); |
49 | QT_END_NAMESPACE |
50 | |
51 | #endif |
52 | |
53 | class tst_QSslSocket_onDemandCertificates_member : public QObject |
54 | { |
55 | Q_OBJECT |
56 | |
57 | int proxyAuthCalled; |
58 | |
59 | public: |
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 | |
69 | public 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 |
77 | private slots: |
78 | void onDemandRootCertLoadingMemberMethods(); |
79 | |
80 | private: |
81 | QSslSocket *socket = nullptr; |
82 | #endif // QT_NO_OPENSSL |
83 | }; |
84 | |
85 | enum 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 | |
97 | void 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 | |
112 | void 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 | |
126 | void 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 | |
162 | void tst_QSslSocket_onDemandCertificates_member::cleanup() |
163 | { |
164 | QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); |
165 | } |
166 | |
167 | #ifndef QT_NO_OPENSSL |
168 | QSslSocketPtr 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 | |
181 | void 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 | |
190 | static 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 | |
220 | void 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 | |
270 | QTEST_MAIN(tst_QSslSocket_onDemandCertificates_member) |
271 | #include "tst_qsslsocket_onDemandCertificates_member.moc" |
272 | |