1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Copyright (C) 2014 Governikus GmbH & Co. KG. |
5 | ** Contact: https://www.qt.io/licensing/ |
6 | ** |
7 | ** This file is part of the test suite of the Qt Toolkit. |
8 | ** |
9 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
10 | ** Commercial License Usage |
11 | ** Licensees holding valid commercial Qt licenses may use this file in |
12 | ** accordance with the commercial license agreement provided with the |
13 | ** Software or, alternatively, in accordance with the terms contained in |
14 | ** a written agreement between you and The Qt Company. For licensing terms |
15 | ** and conditions see https://www.qt.io/terms-conditions. For further |
16 | ** information use the contact form at https://www.qt.io/contact-us. |
17 | ** |
18 | ** GNU General Public License Usage |
19 | ** Alternatively, this file may be used under the terms of the GNU |
20 | ** General Public License version 3 as published by the Free Software |
21 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
22 | ** included in the packaging of this file. Please review the following |
23 | ** information to ensure the GNU General Public License requirements will |
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
25 | ** |
26 | ** $QT_END_LICENSE$ |
27 | ** |
28 | ****************************************************************************/ |
29 | |
30 | #include <QtCore/qglobal.h> |
31 | #include <QtCore/qthread.h> |
32 | #include <QtCore/qelapsedtimer.h> |
33 | #include <QtCore/qrandom.h> |
34 | #include <QtCore/qscopeguard.h> |
35 | #include <QtNetwork/qhostaddress.h> |
36 | #include <QtNetwork/qhostinfo.h> |
37 | #include <QtNetwork/qnetworkproxy.h> |
38 | #include <QtNetwork/qsslcipher.h> |
39 | #include <QtNetwork/qsslconfiguration.h> |
40 | #include <QtNetwork/qsslkey.h> |
41 | #include <QtNetwork/qsslsocket.h> |
42 | #include <QtNetwork/qtcpserver.h> |
43 | #include <QtNetwork/qsslpresharedkeyauthenticator.h> |
44 | #include <QtTest/QtTest> |
45 | |
46 | #include <QNetworkProxy> |
47 | #include <QAuthenticator> |
48 | |
49 | #include "private/qhostinfo_p.h" |
50 | #include "private/qiodevice_p.h" // for QIODEVICE_BUFFERSIZE |
51 | |
52 | #include "../../../network-settings.h" |
53 | |
54 | #ifndef QT_NO_SSL |
55 | |
56 | #ifndef QT_NO_OPENSSL |
57 | #include "private/qsslsocket_openssl_p.h" |
58 | #include "private/qsslsocket_openssl_symbols_p.h" |
59 | #endif // QT_NO_OPENSSL |
60 | |
61 | #include "private/qsslsocket_p.h" |
62 | #include "private/qsslconfiguration_p.h" |
63 | |
64 | Q_DECLARE_METATYPE(QSslSocket::SslMode) |
65 | typedef QVector<QSslError::SslError> SslErrorList; |
66 | Q_DECLARE_METATYPE(SslErrorList) |
67 | Q_DECLARE_METATYPE(QSslError) |
68 | Q_DECLARE_METATYPE(QSslKey) |
69 | Q_DECLARE_METATYPE(QSsl::SslProtocol) |
70 | Q_DECLARE_METATYPE(QSslSocket::PeerVerifyMode); |
71 | typedef QSharedPointer<QSslSocket> QSslSocketPtr; |
72 | |
73 | // Non-OpenSSL backends are not able to report a specific error code |
74 | // for self-signed certificates. |
75 | #ifndef QT_NO_OPENSSL |
76 | #define FLUKE_CERTIFICATE_ERROR QSslError::SelfSignedCertificate |
77 | #else |
78 | #define FLUKE_CERTIFICATE_ERROR QSslError::CertificateUntrusted |
79 | #endif // QT_NO_OPENSSL |
80 | |
81 | #endif // QT_NO_OPENSSL |
82 | |
83 | // Detect ALPN (Application-Layer Protocol Negotiation) support |
84 | #undef ALPN_SUPPORTED // Undef the variable first to be safe |
85 | #if defined(OPENSSL_VERSION_NUMBER) && !defined(OPENSSL_NO_TLSEXT) |
86 | #define ALPN_SUPPORTED 1 |
87 | #endif |
88 | |
89 | #if QT_CONFIG(schannel) && !defined(Q_CC_MINGW) |
90 | #define ALPN_SUPPORTED 1 |
91 | #endif |
92 | |
93 | #if defined Q_OS_HPUX && defined Q_CC_GNU |
94 | // This error is delivered every time we try to use the fluke CA |
95 | // certificate. For now we work around this bug. Task 202317. |
96 | #define QSSLSOCKET_CERTUNTRUSTED_WORKAROUND |
97 | #endif |
98 | |
99 | // Use this cipher to force PSK key sharing. |
100 | // Also, it's a cipher w/o auth, to check that we emit the signals warning |
101 | // about the identity of the peer. |
102 | #ifndef QT_NO_OPENSSL |
103 | static const QString PSK_CIPHER_WITHOUT_AUTH = QStringLiteral("PSK-AES256-CBC-SHA" ); |
104 | static const quint16 PSK_SERVER_PORT = 4433; |
105 | static const QByteArray PSK_CLIENT_PRESHAREDKEY = QByteArrayLiteral("\x1a\x2b\x3c\x4d\x5e\x6f" ); |
106 | static const QByteArray PSK_SERVER_IDENTITY_HINT = QByteArrayLiteral("QtTestServerHint" ); |
107 | static const QByteArray PSK_CLIENT_IDENTITY = QByteArrayLiteral("Client_identity" ); |
108 | |
109 | QT_BEGIN_NAMESPACE |
110 | void qt_ForceTlsSecurityLevel(); |
111 | QT_END_NAMESPACE |
112 | |
113 | #endif // !QT_NO_OPENSSL |
114 | |
115 | class tst_QSslSocket : public QObject |
116 | { |
117 | Q_OBJECT |
118 | |
119 | int proxyAuthCalled; |
120 | |
121 | public: |
122 | tst_QSslSocket(); |
123 | |
124 | static void enterLoop(int secs) |
125 | { |
126 | ++loopLevel; |
127 | QTestEventLoop::instance().enterLoop(secs); |
128 | } |
129 | |
130 | static bool timeout() |
131 | { |
132 | return QTestEventLoop::instance().timeout(); |
133 | } |
134 | |
135 | #ifndef QT_NO_SSL |
136 | QSslSocketPtr newSocket(); |
137 | |
138 | #ifndef QT_NO_OPENSSL |
139 | enum PskConnectTestType { |
140 | PskConnectDoNotHandlePsk, |
141 | PskConnectEmptyCredentials, |
142 | PskConnectWrongCredentials, |
143 | PskConnectWrongIdentity, |
144 | PskConnectWrongPreSharedKey, |
145 | PskConnectRightCredentialsPeerVerifyFailure, |
146 | PskConnectRightCredentialsVerifyPeer, |
147 | PskConnectRightCredentialsDoNotVerifyPeer, |
148 | }; |
149 | #endif |
150 | #endif |
151 | |
152 | public slots: |
153 | void initTestCase_data(); |
154 | void initTestCase(); |
155 | void init(); |
156 | void cleanup(); |
157 | #ifndef QT_NO_NETWORKPROXY |
158 | void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth); |
159 | #endif |
160 | |
161 | #ifndef QT_NO_SSL |
162 | private slots: |
163 | void constructing(); |
164 | void configNoOnDemandLoad(); |
165 | void simpleConnect(); |
166 | void simpleConnectWithIgnore(); |
167 | |
168 | // API tests |
169 | void sslErrors_data(); |
170 | void sslErrors(); |
171 | void ciphers(); |
172 | void connectToHostEncrypted(); |
173 | void connectToHostEncryptedWithVerificationPeerName(); |
174 | void sessionCipher(); |
175 | void flush(); |
176 | void isEncrypted(); |
177 | void localCertificate(); |
178 | void mode(); |
179 | void peerCertificate(); |
180 | void peerCertificateChain(); |
181 | void privateKey(); |
182 | #ifndef QT_NO_OPENSSL |
183 | void privateKeyOpaque(); |
184 | #endif |
185 | void protocol(); |
186 | void protocolServerSide_data(); |
187 | void protocolServerSide(); |
188 | #ifndef QT_NO_OPENSSL |
189 | void serverCipherPreferences(); |
190 | #endif // QT_NO_OPENSSL |
191 | void setCaCertificates(); |
192 | void setLocalCertificate(); |
193 | void localCertificateChain(); |
194 | void setLocalCertificateChain(); |
195 | void tlsConfiguration(); |
196 | void setSocketDescriptor(); |
197 | void setSslConfiguration_data(); |
198 | void setSslConfiguration(); |
199 | void waitForEncrypted(); |
200 | void waitForEncryptedMinusOne(); |
201 | void waitForConnectedEncryptedReadyRead(); |
202 | void startClientEncryption(); |
203 | void startServerEncryption(); |
204 | void addDefaultCaCertificate(); |
205 | void defaultCaCertificates(); |
206 | void defaultCiphers(); |
207 | void resetDefaultCiphers(); |
208 | void setDefaultCaCertificates(); |
209 | void setDefaultCiphers(); |
210 | void supportedCiphers(); |
211 | void systemCaCertificates(); |
212 | void wildcardCertificateNames(); |
213 | void isMatchingHostname(); |
214 | void wildcard(); |
215 | void setEmptyKey(); |
216 | void spontaneousWrite(); |
217 | void setReadBufferSize(); |
218 | void setReadBufferSize_task_250027(); |
219 | void waitForMinusOne(); |
220 | void verifyMode(); |
221 | void verifyDepth(); |
222 | #ifndef QT_NO_OPENSSL |
223 | void verifyAndDefaultConfiguration(); |
224 | #endif // QT_NO_OPENSSL |
225 | void disconnectFromHostWhenConnecting(); |
226 | void disconnectFromHostWhenConnected(); |
227 | #ifndef QT_NO_OPENSSL |
228 | void closeWhileEmittingSocketError(); |
229 | #endif |
230 | void resetProxy(); |
231 | void ignoreSslErrorsList_data(); |
232 | void ignoreSslErrorsList(); |
233 | void ignoreSslErrorsListWithSlot_data(); |
234 | void ignoreSslErrorsListWithSlot(); |
235 | void abortOnSslErrors(); |
236 | void readFromClosedSocket(); |
237 | void writeBigChunk(); |
238 | void blacklistedCertificates(); |
239 | void versionAccessors(); |
240 | #ifndef QT_NO_OPENSSL |
241 | void sslOptions(); |
242 | #endif |
243 | void encryptWithoutConnecting(); |
244 | void resume_data(); |
245 | void resume(); |
246 | void qtbug18498_peek(); |
247 | void qtbug18498_peek2(); |
248 | void dhServer(); |
249 | #ifndef QT_NO_OPENSSL |
250 | void dhServerCustomParamsNull(); |
251 | void dhServerCustomParams(); |
252 | #endif |
253 | void ecdhServer(); |
254 | void verifyClientCertificate_data(); |
255 | void verifyClientCertificate(); |
256 | void readBufferMaxSize(); |
257 | |
258 | void allowedProtocolNegotiation(); |
259 | |
260 | #ifndef QT_NO_OPENSSL |
261 | void simplePskConnect_data(); |
262 | void simplePskConnect(); |
263 | void ephemeralServerKey_data(); |
264 | void ephemeralServerKey(); |
265 | void pskServer(); |
266 | void forwardReadChannelFinished(); |
267 | void signatureAlgorithm_data(); |
268 | void signatureAlgorithm(); |
269 | #endif |
270 | |
271 | void disabledProtocols_data(); |
272 | void disabledProtocols(); |
273 | |
274 | void oldErrorsOnSocketReuse(); |
275 | |
276 | void setEmptyDefaultConfiguration(); // this test should be last |
277 | |
278 | protected slots: |
279 | |
280 | static void exitLoop() |
281 | { |
282 | // Safe exit - if we aren't in an event loop, don't |
283 | // exit one. |
284 | if (loopLevel > 0) { |
285 | --loopLevel; |
286 | QTestEventLoop::instance().exitLoop(); |
287 | } |
288 | } |
289 | |
290 | void ignoreErrorSlot() |
291 | { |
292 | socket->ignoreSslErrors(); |
293 | } |
294 | void abortOnErrorSlot() |
295 | { |
296 | QSslSocket *sock = static_cast<QSslSocket *>(sender()); |
297 | sock->abort(); |
298 | } |
299 | void untrustedWorkaroundSlot(const QList<QSslError> &errors) |
300 | { |
301 | if (errors.size() == 1 && |
302 | (errors.first().error() == QSslError::CertificateUntrusted || |
303 | errors.first().error() == QSslError::SelfSignedCertificate)) |
304 | socket->ignoreSslErrors(); |
305 | } |
306 | void ignoreErrorListSlot(const QList<QSslError> &errors); |
307 | |
308 | private: |
309 | QSslSocket *socket; |
310 | QList<QSslError> storedExpectedSslErrors; |
311 | #endif // QT_NO_SSL |
312 | private: |
313 | static int loopLevel; |
314 | public: |
315 | static QString testDataDir; |
316 | }; |
317 | QString tst_QSslSocket::testDataDir; |
318 | |
319 | #ifndef QT_NO_SSL |
320 | #ifndef QT_NO_OPENSSL |
321 | Q_DECLARE_METATYPE(tst_QSslSocket::PskConnectTestType) |
322 | #endif |
323 | #endif |
324 | |
325 | int tst_QSslSocket::loopLevel = 0; |
326 | |
327 | namespace { |
328 | |
329 | QString httpServerCertChainPath() |
330 | { |
331 | // DOCKERTODO: note how we use CA certificate on the real server. The docker container |
332 | // is using a different cert with a "special" CN. Check if it's important! |
333 | #ifdef QT_TEST_SERVER |
334 | return tst_QSslSocket::testDataDir + QStringLiteral("certs/qt-test-server-cert.pem" ); |
335 | #else |
336 | return tst_QSslSocket::testDataDir + QStringLiteral("certs/qt-test-server-cacert.pem" ); |
337 | #endif // QT_TEST_SERVER |
338 | } |
339 | |
340 | } // unnamed namespace |
341 | |
342 | tst_QSslSocket::tst_QSslSocket() |
343 | { |
344 | #ifndef QT_NO_SSL |
345 | qRegisterMetaType<QList<QSslError> >(typeName: "QList<QSslError>" ); |
346 | qRegisterMetaType<QSslError>(typeName: "QSslError" ); |
347 | qRegisterMetaType<QAbstractSocket::SocketState>(typeName: "QAbstractSocket::SocketState" ); |
348 | qRegisterMetaType<QAbstractSocket::SocketError>(typeName: "QAbstractSocket::SocketError" ); |
349 | |
350 | #ifndef QT_NO_OPENSSL |
351 | qRegisterMetaType<QSslPreSharedKeyAuthenticator *>(); |
352 | qRegisterMetaType<tst_QSslSocket::PskConnectTestType>(); |
353 | #endif |
354 | #endif |
355 | } |
356 | |
357 | enum ProxyTests { |
358 | NoProxy = 0x00, |
359 | Socks5Proxy = 0x01, |
360 | HttpProxy = 0x02, |
361 | TypeMask = 0x0f, |
362 | |
363 | NoAuth = 0x00, |
364 | AuthBasic = 0x10, |
365 | AuthNtlm = 0x20, |
366 | AuthMask = 0xf0 |
367 | }; |
368 | |
369 | void tst_QSslSocket::initTestCase_data() |
370 | { |
371 | QTest::addColumn<bool>(name: "setProxy" ); |
372 | QTest::addColumn<int>(name: "proxyType" ); |
373 | |
374 | QTest::newRow(dataTag: "WithoutProxy" ) << false << 0; |
375 | QTest::newRow(dataTag: "WithSocks5Proxy" ) << true << int(Socks5Proxy); |
376 | QTest::newRow(dataTag: "WithSocks5ProxyAuth" ) << true << int(Socks5Proxy | AuthBasic); |
377 | |
378 | QTest::newRow(dataTag: "WithHttpProxy" ) << true << int(HttpProxy); |
379 | QTest::newRow(dataTag: "WithHttpProxyBasicAuth" ) << true << int(HttpProxy | AuthBasic); |
380 | // uncomment the line below when NTLM works |
381 | // QTest::newRow("WithHttpProxyNtlmAuth") << true << int(HttpProxy | AuthNtlm); |
382 | } |
383 | |
384 | void tst_QSslSocket::initTestCase() |
385 | { |
386 | testDataDir = QFileInfo(QFINDTESTDATA("certs" )).absolutePath(); |
387 | if (testDataDir.isEmpty()) |
388 | testDataDir = QCoreApplication::applicationDirPath(); |
389 | if (!testDataDir.endsWith(s: QLatin1String("/" ))) |
390 | testDataDir += QLatin1String("/" ); |
391 | #ifndef QT_NO_SSL |
392 | qDebug(msg: "Using SSL library %s (%ld)" , |
393 | qPrintable(QSslSocket::sslLibraryVersionString()), |
394 | QSslSocket::sslLibraryVersionNumber()); |
395 | #ifdef QT_TEST_SERVER |
396 | QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::socksProxyServerName(), 1080)); |
397 | QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::socksProxyServerName(), 1081)); |
398 | QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpProxyServerName(), 3128)); |
399 | QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpProxyServerName(), 3129)); |
400 | QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpProxyServerName(), 3130)); |
401 | QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpServerName(), 443)); |
402 | QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::imapServerName(), 993)); |
403 | QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::echoServerName(), 13)); |
404 | #else |
405 | if (!QtNetworkSettings::verifyTestNetworkSettings()) |
406 | QSKIP("No network test server available" ); |
407 | #endif // QT_TEST_SERVER |
408 | #endif // QT_NO_SSL |
409 | } |
410 | |
411 | void tst_QSslSocket::init() |
412 | { |
413 | QFETCH_GLOBAL(bool, setProxy); |
414 | if (setProxy) { |
415 | #ifndef QT_NO_NETWORKPROXY |
416 | QFETCH_GLOBAL(int, proxyType); |
417 | const QString socksProxyAddr = QtNetworkSettings::socksProxyServerIp().toString(); |
418 | const QString httpProxyAddr = QtNetworkSettings::httpProxyServerIp().toString(); |
419 | QNetworkProxy proxy; |
420 | |
421 | switch (proxyType) { |
422 | case Socks5Proxy: |
423 | proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, socksProxyAddr, 1080); |
424 | break; |
425 | |
426 | case Socks5Proxy | AuthBasic: |
427 | proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, socksProxyAddr, 1081); |
428 | break; |
429 | |
430 | case HttpProxy | NoAuth: |
431 | proxy = QNetworkProxy(QNetworkProxy::HttpProxy, httpProxyAddr, 3128); |
432 | break; |
433 | |
434 | case HttpProxy | AuthBasic: |
435 | proxy = QNetworkProxy(QNetworkProxy::HttpProxy, httpProxyAddr, 3129); |
436 | break; |
437 | |
438 | case HttpProxy | AuthNtlm: |
439 | proxy = QNetworkProxy(QNetworkProxy::HttpProxy, httpProxyAddr, 3130); |
440 | break; |
441 | } |
442 | QNetworkProxy::setApplicationProxy(proxy); |
443 | #else // !QT_NO_NETWORKPROXY |
444 | QSKIP("No proxy support" ); |
445 | #endif // QT_NO_NETWORKPROXY |
446 | } |
447 | |
448 | #ifndef QT_NO_OPENSSL |
449 | QT_PREPEND_NAMESPACE(qt_ForceTlsSecurityLevel)(); |
450 | #endif // QT_NO_OPENSSL |
451 | |
452 | qt_qhostinfo_clear_cache(); |
453 | } |
454 | |
455 | void tst_QSslSocket::cleanup() |
456 | { |
457 | #ifndef QT_NO_NETWORKPROXY |
458 | QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); |
459 | #endif |
460 | } |
461 | |
462 | #ifndef QT_NO_SSL |
463 | QSslSocketPtr tst_QSslSocket::newSocket() |
464 | { |
465 | const auto socket = QSslSocketPtr::create(); |
466 | |
467 | proxyAuthCalled = 0; |
468 | connect(asender: socket.data(), SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), |
469 | SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), |
470 | atype: Qt::DirectConnection); |
471 | |
472 | return socket; |
473 | } |
474 | #endif |
475 | |
476 | #ifndef QT_NO_NETWORKPROXY |
477 | void tst_QSslSocket::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth) |
478 | { |
479 | ++proxyAuthCalled; |
480 | auth->setUser("qsockstest" ); |
481 | auth->setPassword("password" ); |
482 | } |
483 | #endif // !QT_NO_NETWORKPROXY |
484 | |
485 | #ifndef QT_NO_SSL |
486 | |
487 | void tst_QSslSocket::constructing() |
488 | { |
489 | const char readNotOpenMessage[] = "QIODevice::read (QSslSocket): device not open" ; |
490 | const char writeNotOpenMessage[] = "QIODevice::write (QSslSocket): device not open" ; |
491 | |
492 | if (!QSslSocket::supportsSsl()) |
493 | return; |
494 | |
495 | QSslSocket socket; |
496 | |
497 | QCOMPARE(socket.state(), QSslSocket::UnconnectedState); |
498 | QCOMPARE(socket.mode(), QSslSocket::UnencryptedMode); |
499 | QVERIFY(!socket.isEncrypted()); |
500 | QCOMPARE(socket.bytesAvailable(), qint64(0)); |
501 | QCOMPARE(socket.bytesToWrite(), qint64(0)); |
502 | QVERIFY(!socket.canReadLine()); |
503 | QVERIFY(socket.atEnd()); |
504 | QCOMPARE(socket.localCertificate(), QSslCertificate()); |
505 | QCOMPARE(socket.sslConfiguration(), QSslConfiguration::defaultConfiguration()); |
506 | QCOMPARE(socket.errorString(), QString("Unknown error" )); |
507 | char c = '\0'; |
508 | QTest::ignoreMessage(type: QtWarningMsg, message: readNotOpenMessage); |
509 | QVERIFY(!socket.getChar(&c)); |
510 | QCOMPARE(c, '\0'); |
511 | QVERIFY(!socket.isOpen()); |
512 | QVERIFY(!socket.isReadable()); |
513 | QVERIFY(socket.isSequential()); |
514 | QVERIFY(!socket.isTextModeEnabled()); |
515 | QVERIFY(!socket.isWritable()); |
516 | QCOMPARE(socket.openMode(), QIODevice::NotOpen); |
517 | QTest::ignoreMessage(type: QtWarningMsg, message: readNotOpenMessage); |
518 | QVERIFY(socket.peek(2).isEmpty()); |
519 | QCOMPARE(socket.pos(), qint64(0)); |
520 | QTest::ignoreMessage(type: QtWarningMsg, message: writeNotOpenMessage); |
521 | QVERIFY(!socket.putChar('c')); |
522 | QTest::ignoreMessage(type: QtWarningMsg, message: readNotOpenMessage); |
523 | QVERIFY(socket.read(2).isEmpty()); |
524 | QTest::ignoreMessage(type: QtWarningMsg, message: readNotOpenMessage); |
525 | QCOMPARE(socket.read(0, 0), qint64(-1)); |
526 | QTest::ignoreMessage(type: QtWarningMsg, message: readNotOpenMessage); |
527 | QVERIFY(socket.readAll().isEmpty()); |
528 | QTest::ignoreMessage(type: QtWarningMsg, message: "QIODevice::readLine (QSslSocket): Called with maxSize < 2" ); |
529 | QCOMPARE(socket.readLine(0, 0), qint64(-1)); |
530 | char buf[10]; |
531 | QCOMPARE(socket.readLine(buf, sizeof(buf)), qint64(-1)); |
532 | QTest::ignoreMessage(type: QtWarningMsg, message: "QIODevice::seek (QSslSocket): Cannot call seek on a sequential device" ); |
533 | QVERIFY(!socket.reset()); |
534 | QTest::ignoreMessage(type: QtWarningMsg, message: "QIODevice::seek (QSslSocket): Cannot call seek on a sequential device" ); |
535 | QVERIFY(!socket.seek(2)); |
536 | QCOMPARE(socket.size(), qint64(0)); |
537 | QVERIFY(!socket.waitForBytesWritten(10)); |
538 | QVERIFY(!socket.waitForReadyRead(10)); |
539 | QTest::ignoreMessage(type: QtWarningMsg, message: writeNotOpenMessage); |
540 | QCOMPARE(socket.write(0, 0), qint64(-1)); |
541 | QTest::ignoreMessage(type: QtWarningMsg, message: writeNotOpenMessage); |
542 | QCOMPARE(socket.write(QByteArray()), qint64(-1)); |
543 | QCOMPARE(socket.error(), QAbstractSocket::UnknownSocketError); |
544 | QVERIFY(!socket.flush()); |
545 | QVERIFY(!socket.isValid()); |
546 | QCOMPARE(socket.localAddress(), QHostAddress()); |
547 | QCOMPARE(socket.localPort(), quint16(0)); |
548 | QCOMPARE(socket.peerAddress(), QHostAddress()); |
549 | QVERIFY(socket.peerName().isEmpty()); |
550 | QCOMPARE(socket.peerPort(), quint16(0)); |
551 | #ifndef QT_NO_NETWORKPROXY |
552 | QCOMPARE(socket.proxy().type(), QNetworkProxy::DefaultProxy); |
553 | #endif |
554 | QCOMPARE(socket.readBufferSize(), qint64(0)); |
555 | QCOMPARE(socket.socketDescriptor(), qintptr(-1)); |
556 | QCOMPARE(socket.socketType(), QAbstractSocket::TcpSocket); |
557 | QVERIFY(!socket.waitForConnected(10)); |
558 | QTest::ignoreMessage(type: QtWarningMsg, message: "QSslSocket::waitForDisconnected() is not allowed in UnconnectedState" ); |
559 | QVERIFY(!socket.waitForDisconnected(10)); |
560 | QCOMPARE(socket.protocol(), QSsl::SecureProtocols); |
561 | |
562 | QSslConfiguration savedDefault = QSslConfiguration::defaultConfiguration(); |
563 | |
564 | auto sslConfig = socket.sslConfiguration(); |
565 | sslConfig.setCaCertificates(QSslConfiguration::systemCaCertificates()); |
566 | socket.setSslConfiguration(sslConfig); |
567 | |
568 | auto defaultConfig = QSslConfiguration::defaultConfiguration(); |
569 | defaultConfig.setCaCertificates(QList<QSslCertificate>()); |
570 | defaultConfig.setCiphers(QList<QSslCipher>()); |
571 | QSslConfiguration::setDefaultConfiguration(defaultConfig); |
572 | |
573 | QVERIFY(!socket.sslConfiguration().caCertificates().isEmpty()); |
574 | QVERIFY(!socket.sslConfiguration().ciphers().isEmpty()); |
575 | |
576 | // verify the default as well: |
577 | QVERIFY(QSslConfiguration::defaultConfiguration().caCertificates().isEmpty()); |
578 | QVERIFY(QSslConfiguration::defaultConfiguration().ciphers().isEmpty()); |
579 | |
580 | QSslConfiguration::setDefaultConfiguration(savedDefault); |
581 | } |
582 | |
583 | void tst_QSslSocket::configNoOnDemandLoad() |
584 | { |
585 | QFETCH_GLOBAL(bool, setProxy); |
586 | if (setProxy) |
587 | return; // NoProxy is enough. |
588 | |
589 | // We noticed a peculiar situation, where a configuration |
590 | // set on a socket is not equal to the configuration we |
591 | // get back from the socket afterwards. |
592 | auto customConfig = QSslConfiguration::defaultConfiguration(); |
593 | // Setting CA certificates disables loading root certificates |
594 | // during verification: |
595 | customConfig.setCaCertificates(customConfig.caCertificates()); |
596 | |
597 | QSslSocket socket; |
598 | socket.setSslConfiguration(customConfig); |
599 | QCOMPARE(customConfig, socket.sslConfiguration()); |
600 | } |
601 | |
602 | void tst_QSslSocket::simpleConnect() |
603 | { |
604 | if (!QSslSocket::supportsSsl()) |
605 | return; |
606 | |
607 | QFETCH_GLOBAL(bool, setProxy); |
608 | if (setProxy) |
609 | return; |
610 | |
611 | QSslSocket socket; |
612 | QSignalSpy connectedSpy(&socket, SIGNAL(connected())); |
613 | QSignalSpy hostFoundSpy(&socket, SIGNAL(hostFound())); |
614 | QSignalSpy disconnectedSpy(&socket, SIGNAL(disconnected())); |
615 | QSignalSpy connectionEncryptedSpy(&socket, SIGNAL(encrypted())); |
616 | QSignalSpy (&socket, SIGNAL(sslErrors(QList<QSslError>))); |
617 | |
618 | connect(sender: &socket, SIGNAL(connected()), receiver: this, SLOT(exitLoop())); |
619 | connect(sender: &socket, SIGNAL(disconnected()), receiver: this, SLOT(exitLoop())); |
620 | connect(sender: &socket, SIGNAL(modeChanged(QSslSocket::SslMode)), receiver: this, SLOT(exitLoop())); |
621 | connect(sender: &socket, SIGNAL(encrypted()), receiver: this, SLOT(exitLoop())); |
622 | connect(sender: &socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), receiver: this, SLOT(exitLoop())); |
623 | connect(sender: &socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(exitLoop())); |
624 | |
625 | // Start connecting |
626 | socket.connectToHost(hostName: QtNetworkSettings::imapServerName(), port: 993); |
627 | QCOMPARE(socket.state(), QAbstractSocket::HostLookupState); |
628 | enterLoop(secs: 10); |
629 | |
630 | // Entered connecting state |
631 | QCOMPARE(socket.state(), QAbstractSocket::ConnectingState); |
632 | QCOMPARE(connectedSpy.count(), 0); |
633 | QCOMPARE(hostFoundSpy.count(), 1); |
634 | QCOMPARE(disconnectedSpy.count(), 0); |
635 | enterLoop(secs: 10); |
636 | |
637 | // Entered connected state |
638 | QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); |
639 | QCOMPARE(socket.mode(), QSslSocket::UnencryptedMode); |
640 | QVERIFY(!socket.isEncrypted()); |
641 | QCOMPARE(connectedSpy.count(), 1); |
642 | QCOMPARE(hostFoundSpy.count(), 1); |
643 | QCOMPARE(disconnectedSpy.count(), 0); |
644 | |
645 | // Enter encrypted mode |
646 | socket.startClientEncryption(); |
647 | QCOMPARE(socket.mode(), QSslSocket::SslClientMode); |
648 | QVERIFY(!socket.isEncrypted()); |
649 | QCOMPARE(connectionEncryptedSpy.count(), 0); |
650 | QCOMPARE(sslErrorsSpy.count(), 0); |
651 | |
652 | // Starting handshake |
653 | enterLoop(secs: 10); |
654 | QCOMPARE(sslErrorsSpy.count(), 1); |
655 | QCOMPARE(connectionEncryptedSpy.count(), 0); |
656 | QVERIFY(!socket.isEncrypted()); |
657 | QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState); |
658 | } |
659 | |
660 | void tst_QSslSocket::simpleConnectWithIgnore() |
661 | { |
662 | if (!QSslSocket::supportsSsl()) |
663 | return; |
664 | |
665 | QFETCH_GLOBAL(bool, setProxy); |
666 | if (setProxy) |
667 | return; |
668 | |
669 | QSslSocket socket; |
670 | this->socket = &socket; |
671 | QSignalSpy encryptedSpy(&socket, SIGNAL(encrypted())); |
672 | QSignalSpy (&socket, SIGNAL(sslErrors(QList<QSslError>))); |
673 | |
674 | connect(sender: &socket, SIGNAL(readyRead()), receiver: this, SLOT(exitLoop())); |
675 | connect(sender: &socket, SIGNAL(encrypted()), receiver: this, SLOT(exitLoop())); |
676 | connect(sender: &socket, SIGNAL(connected()), receiver: this, SLOT(exitLoop())); |
677 | connect(sender: &socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
678 | connect(sender: &socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(exitLoop())); |
679 | |
680 | // Start connecting |
681 | socket.connectToHost(hostName: QtNetworkSettings::imapServerName(), port: 993); |
682 | QVERIFY(socket.state() != QAbstractSocket::UnconnectedState); // something must be in progress |
683 | enterLoop(secs: 10); |
684 | |
685 | // Start handshake |
686 | QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); |
687 | socket.startClientEncryption(); |
688 | enterLoop(secs: 10); |
689 | |
690 | // Done; encryption should be enabled. |
691 | QCOMPARE(sslErrorsSpy.count(), 1); |
692 | QVERIFY(socket.isEncrypted()); |
693 | QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); |
694 | QCOMPARE(encryptedSpy.count(), 1); |
695 | |
696 | // Wait for incoming data |
697 | if (!socket.canReadLine()) |
698 | enterLoop(secs: 10); |
699 | |
700 | QByteArray data = socket.readAll(); |
701 | socket.disconnectFromHost(); |
702 | QVERIFY2(QtNetworkSettings::compareReplyIMAPSSL(data), data.constData()); |
703 | } |
704 | |
705 | void tst_QSslSocket::sslErrors_data() |
706 | { |
707 | QTest::addColumn<QString>(name: "host" ); |
708 | QTest::addColumn<int>(name: "port" ); |
709 | |
710 | QString name = QtNetworkSettings::serverLocalName(); |
711 | QTest::newRow(qPrintable(name)) << name << 993; |
712 | |
713 | name = QtNetworkSettings::httpServerIp().toString(); |
714 | QTest::newRow(qPrintable(name)) << name << 443; |
715 | } |
716 | |
717 | void tst_QSslSocket::sslErrors() |
718 | { |
719 | QFETCH(QString, host); |
720 | QFETCH(int, port); |
721 | |
722 | QSslSocketPtr socket = newSocket(); |
723 | #if QT_CONFIG(schannel) |
724 | // Needs to be < 1.2 because of the old certificate and <= 1.0 because of the mail server |
725 | socket->setProtocol(QSsl::SslProtocol::TlsV1_0); |
726 | #endif |
727 | QSignalSpy (socket.data(), SIGNAL(sslErrors(QList<QSslError>))); |
728 | QSignalSpy peerVerifyErrorSpy(socket.data(), SIGNAL(peerVerifyError(QSslError))); |
729 | |
730 | #ifdef QT_TEST_SERVER |
731 | // On the old test server we had the same certificate on different services. |
732 | // The idea of this test is to fail with 'HostNameMismatch', when we're using |
733 | // either serverLocalName() or IP address directly. With Docker we connect |
734 | // to IMAP server, and we have to connect using imapServerName() and passing |
735 | // 'host' as peerVerificationName to the overload of connectToHostEncrypted(). |
736 | if (port == 993) { |
737 | socket->connectToHostEncrypted(QtNetworkSettings::imapServerName(), port, host); |
738 | } else |
739 | #endif // QT_TEST_SERVER |
740 | { |
741 | socket->connectToHostEncrypted(hostName: host, port); |
742 | } |
743 | |
744 | if (!socket->waitForConnected()) |
745 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
746 | socket->waitForEncrypted(msecs: 10000); |
747 | |
748 | // check the SSL errors contain HostNameMismatch and an error due to |
749 | // the certificate being self-signed |
750 | SslErrorList sslErrors; |
751 | const auto socketSslErrors = socket->sslHandshakeErrors(); |
752 | for (const QSslError &err : socketSslErrors) |
753 | sslErrors << err.error(); |
754 | std::sort(first: sslErrors.begin(), last: sslErrors.end()); |
755 | QVERIFY(sslErrors.contains(QSslError::HostNameMismatch)); |
756 | QVERIFY(sslErrors.contains(FLUKE_CERTIFICATE_ERROR)); |
757 | |
758 | // check the same errors were emitted by sslErrors |
759 | QVERIFY(!sslErrorsSpy.isEmpty()); |
760 | SslErrorList emittedErrors; |
761 | const auto = qvariant_cast<QList<QSslError> >(v: qAsConst(t&: sslErrorsSpy).first().first()); |
762 | for (const QSslError &err : sslErrorsSpyErrors) |
763 | emittedErrors << err.error(); |
764 | std::sort(first: emittedErrors.begin(), last: emittedErrors.end()); |
765 | QCOMPARE(sslErrors, emittedErrors); |
766 | |
767 | // check the same errors were emitted by peerVerifyError |
768 | QVERIFY(!peerVerifyErrorSpy.isEmpty()); |
769 | SslErrorList peerErrors; |
770 | const QList<QVariantList> &peerVerifyList = peerVerifyErrorSpy; |
771 | for (const QVariantList &args : peerVerifyList) |
772 | peerErrors << qvariant_cast<QSslError>(v: args.first()).error(); |
773 | std::sort(first: peerErrors.begin(), last: peerErrors.end()); |
774 | QCOMPARE(sslErrors, peerErrors); |
775 | } |
776 | |
777 | void tst_QSslSocket::ciphers() |
778 | { |
779 | if (!QSslSocket::supportsSsl()) |
780 | return; |
781 | |
782 | QFETCH_GLOBAL(const bool, setProxy); |
783 | if (setProxy) { |
784 | // KISS(mart), we don't connect, no need to test the same thing |
785 | // many times! |
786 | return; |
787 | } |
788 | |
789 | QSslSocket socket; |
790 | QCOMPARE(socket.sslConfiguration().ciphers(), QSslConfiguration::defaultConfiguration().ciphers()); |
791 | |
792 | auto sslConfig = socket.sslConfiguration(); |
793 | sslConfig.setCiphers(QList<QSslCipher>()); |
794 | socket.setSslConfiguration(sslConfig); |
795 | QVERIFY(socket.sslConfiguration().ciphers().isEmpty()); |
796 | |
797 | sslConfig.setCiphers(QSslConfiguration::defaultConfiguration().ciphers()); |
798 | socket.setSslConfiguration(sslConfig); |
799 | QCOMPARE(socket.sslConfiguration().ciphers(), QSslConfiguration::defaultConfiguration().ciphers()); |
800 | |
801 | #ifndef QT_NO_OPENSSL |
802 | const auto ciphers = QSslConfiguration::defaultConfiguration().ciphers(); |
803 | for (const auto &cipher : ciphers) { |
804 | if (cipher.name().size() && cipher.protocol() != QSsl::UnknownProtocol) { |
805 | const QSslCipher aCopy(cipher.name(), cipher.protocol()); |
806 | QCOMPARE(aCopy, cipher); |
807 | break; |
808 | } |
809 | } |
810 | #endif // QT_NO_OPENSSL |
811 | } |
812 | |
813 | void tst_QSslSocket::connectToHostEncrypted() |
814 | { |
815 | if (!QSslSocket::supportsSsl()) |
816 | return; |
817 | |
818 | QSslSocketPtr socket = newSocket(); |
819 | #if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2 |
820 | socket->setProtocol(QSsl::SslProtocol::TlsV1_1); |
821 | #endif |
822 | this->socket = socket.data(); |
823 | auto config = socket->sslConfiguration(); |
824 | QVERIFY(config.addCaCertificates(httpServerCertChainPath())); |
825 | socket->setSslConfiguration(config); |
826 | #ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND |
827 | connect(socket.data(), SIGNAL(sslErrors(QList<QSslError>)), |
828 | this, SLOT(untrustedWorkaroundSlot(QList<QSslError>))); |
829 | #endif |
830 | |
831 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
832 | |
833 | // This should pass unconditionally when using fluke's CA certificate. |
834 | // or use untrusted certificate workaround |
835 | QFETCH_GLOBAL(bool, setProxy); |
836 | if (setProxy && !socket->waitForEncrypted(msecs: 10000)) |
837 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
838 | |
839 | socket->disconnectFromHost(); |
840 | QVERIFY(socket->waitForDisconnected()); |
841 | |
842 | QCOMPARE(socket->mode(), QSslSocket::SslClientMode); |
843 | |
844 | socket->connectToHost(hostName: QtNetworkSettings::echoServerName(), port: 13); |
845 | |
846 | QCOMPARE(socket->mode(), QSslSocket::UnencryptedMode); |
847 | |
848 | QVERIFY(socket->waitForDisconnected()); |
849 | } |
850 | |
851 | void tst_QSslSocket::connectToHostEncryptedWithVerificationPeerName() |
852 | { |
853 | if (!QSslSocket::supportsSsl()) |
854 | return; |
855 | |
856 | QSslSocketPtr socket = newSocket(); |
857 | #if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2 |
858 | socket->setProtocol(QSsl::SslProtocol::TlsV1_1); |
859 | #endif |
860 | this->socket = socket.data(); |
861 | |
862 | auto config = socket->sslConfiguration(); |
863 | config.addCaCertificates(path: httpServerCertChainPath()); |
864 | socket->setSslConfiguration(config); |
865 | #ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND |
866 | connect(socket.data(), SIGNAL(sslErrors(QList<QSslError>)), |
867 | this, SLOT(untrustedWorkaroundSlot(QList<QSslError>))); |
868 | #endif |
869 | |
870 | #ifdef QT_TEST_SERVER |
871 | socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443, QtNetworkSettings::httpServerName()); |
872 | #else |
873 | // Connect to the server with its local name, but use the full name for verification. |
874 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::serverLocalName(), port: 443, sslPeerName: QtNetworkSettings::httpServerName()); |
875 | #endif |
876 | |
877 | // This should pass unconditionally when using fluke's CA certificate. |
878 | QFETCH_GLOBAL(bool, setProxy); |
879 | if (setProxy && !socket->waitForEncrypted(msecs: 10000)) |
880 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
881 | |
882 | socket->disconnectFromHost(); |
883 | QVERIFY(socket->waitForDisconnected()); |
884 | |
885 | QCOMPARE(socket->mode(), QSslSocket::SslClientMode); |
886 | } |
887 | |
888 | void tst_QSslSocket::sessionCipher() |
889 | { |
890 | if (!QSslSocket::supportsSsl()) |
891 | return; |
892 | |
893 | QSslSocketPtr socket = newSocket(); |
894 | this->socket = socket.data(); |
895 | connect(sender: socket.data(), SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
896 | QVERIFY(socket->sessionCipher().isNull()); |
897 | socket->connectToHost(hostName: QtNetworkSettings::httpServerName(), port: 443 /* https */); |
898 | QVERIFY2(socket->waitForConnected(10000), qPrintable(socket->errorString())); |
899 | QVERIFY(socket->sessionCipher().isNull()); |
900 | socket->startClientEncryption(); |
901 | if (!socket->waitForEncrypted(msecs: 5000)) |
902 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
903 | QVERIFY(!socket->sessionCipher().isNull()); |
904 | |
905 | qDebug() << "Supported Ciphers:" << QSslConfiguration::supportedCiphers(); |
906 | qDebug() << "Default Ciphers:" << QSslConfiguration::defaultConfiguration().ciphers(); |
907 | qDebug() << "Session Cipher:" << socket->sessionCipher(); |
908 | |
909 | QVERIFY(QSslConfiguration::supportedCiphers().contains(socket->sessionCipher())); |
910 | socket->disconnectFromHost(); |
911 | QVERIFY(socket->waitForDisconnected()); |
912 | } |
913 | |
914 | void tst_QSslSocket::flush() |
915 | { |
916 | } |
917 | |
918 | void tst_QSslSocket::isEncrypted() |
919 | { |
920 | } |
921 | |
922 | void tst_QSslSocket::localCertificate() |
923 | { |
924 | if (!QSslSocket::supportsSsl()) |
925 | return; |
926 | |
927 | // This test does not make 100% sense yet. We just set some local CA/cert/key and use it |
928 | // to authenticate ourselves against the server. The server does not actually check this |
929 | // values. This test should just run the codepath inside qsslsocket_openssl.cpp |
930 | |
931 | QSslSocketPtr socket = newSocket(); |
932 | QList<QSslCertificate> localCert = QSslCertificate::fromPath(path: httpServerCertChainPath()); |
933 | |
934 | auto sslConfig = socket->sslConfiguration(); |
935 | sslConfig.setCaCertificates(localCert); |
936 | socket->setSslConfiguration(sslConfig); |
937 | |
938 | socket->setLocalCertificate(fileName: testDataDir + "certs/fluke.cert" ); |
939 | socket->setPrivateKey(fileName: testDataDir + "certs/fluke.key" ); |
940 | |
941 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
942 | QFETCH_GLOBAL(bool, setProxy); |
943 | if (setProxy && !socket->waitForEncrypted(msecs: 10000)) |
944 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
945 | } |
946 | |
947 | void tst_QSslSocket::mode() |
948 | { |
949 | } |
950 | |
951 | void tst_QSslSocket::peerCertificate() |
952 | { |
953 | } |
954 | |
955 | void tst_QSslSocket::peerCertificateChain() |
956 | { |
957 | if (!QSslSocket::supportsSsl()) |
958 | return; |
959 | |
960 | QSKIP("QTBUG-29941 - Unstable auto-test due to intermittently unreachable host" ); |
961 | |
962 | QSslSocketPtr socket = newSocket(); |
963 | this->socket = socket.data(); |
964 | QList<QSslCertificate> caCertificates = QSslCertificate::fromPath(path: httpServerCertChainPath()); |
965 | QCOMPARE(caCertificates.count(), 1); |
966 | auto config = socket->sslConfiguration(); |
967 | config.addCaCertificates(certificates: caCertificates); |
968 | socket->setSslConfiguration(config); |
969 | #ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND |
970 | connect(socket.data(), SIGNAL(sslErrors(QList<QSslError>)), |
971 | this, SLOT(untrustedWorkaroundSlot(QList<QSslError>))); |
972 | #endif |
973 | |
974 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
975 | QCOMPARE(socket->mode(), QSslSocket::UnencryptedMode); |
976 | QVERIFY(socket->peerCertificateChain().isEmpty()); |
977 | QFETCH_GLOBAL(bool, setProxy); |
978 | if (setProxy && !socket->waitForEncrypted(msecs: 10000)) |
979 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
980 | |
981 | QList<QSslCertificate> certChain = socket->peerCertificateChain(); |
982 | QVERIFY(certChain.count() > 0); |
983 | QCOMPARE(certChain.first(), socket->peerCertificate()); |
984 | |
985 | socket->disconnectFromHost(); |
986 | QVERIFY(socket->waitForDisconnected()); |
987 | |
988 | // connect again to a different server |
989 | socket->connectToHostEncrypted(hostName: "www.qt.io" , port: 443); |
990 | socket->ignoreSslErrors(); |
991 | QCOMPARE(socket->mode(), QSslSocket::UnencryptedMode); |
992 | QVERIFY(socket->peerCertificateChain().isEmpty()); |
993 | if (setProxy && !socket->waitForEncrypted(msecs: 10000)) |
994 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
995 | |
996 | QCOMPARE(socket->peerCertificateChain().first(), socket->peerCertificate()); |
997 | QVERIFY(socket->peerCertificateChain() != certChain); |
998 | |
999 | socket->disconnectFromHost(); |
1000 | QVERIFY(socket->waitForDisconnected()); |
1001 | |
1002 | // now do it again back to the original server |
1003 | socket->connectToHost(hostName: QtNetworkSettings::httpServerName(), port: 443); |
1004 | QCOMPARE(socket->mode(), QSslSocket::UnencryptedMode); |
1005 | QVERIFY(socket->peerCertificateChain().isEmpty()); |
1006 | QVERIFY2(socket->waitForConnected(10000), qPrintable(socket->errorString())); |
1007 | |
1008 | socket->startClientEncryption(); |
1009 | if (setProxy && !socket->waitForEncrypted(msecs: 10000)) |
1010 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
1011 | |
1012 | QCOMPARE(socket->peerCertificateChain().first(), socket->peerCertificate()); |
1013 | QCOMPARE(socket->peerCertificateChain(), certChain); |
1014 | |
1015 | socket->disconnectFromHost(); |
1016 | QVERIFY(socket->waitForDisconnected()); |
1017 | } |
1018 | |
1019 | void tst_QSslSocket::privateKey() |
1020 | { |
1021 | } |
1022 | |
1023 | #ifndef QT_NO_OPENSSL |
1024 | void tst_QSslSocket::privateKeyOpaque() |
1025 | { |
1026 | if (!QSslSocket::supportsSsl()) |
1027 | return; |
1028 | |
1029 | QFile file(testDataDir + "certs/fluke.key" ); |
1030 | QVERIFY(file.open(QIODevice::ReadOnly)); |
1031 | QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); |
1032 | QVERIFY(!key.isNull()); |
1033 | |
1034 | EVP_PKEY *pkey = q_EVP_PKEY_new(); |
1035 | q_EVP_PKEY_set1_RSA(a: pkey, b: reinterpret_cast<RSA *>(key.handle())); |
1036 | |
1037 | // This test does not make 100% sense yet. We just set some local CA/cert/key and use it |
1038 | // to authenticate ourselves against the server. The server does not actually check this |
1039 | // values. This test should just run the codepath inside qsslsocket_openssl.cpp |
1040 | |
1041 | QSslSocketPtr socket = newSocket(); |
1042 | QList<QSslCertificate> localCert = QSslCertificate::fromPath(path: httpServerCertChainPath()); |
1043 | |
1044 | auto sslConfig = socket->sslConfiguration(); |
1045 | sslConfig.setCaCertificates(localCert); |
1046 | socket->setSslConfiguration(sslConfig); |
1047 | |
1048 | socket->setLocalCertificate(fileName: testDataDir + "certs/fluke.cert" ); |
1049 | socket->setPrivateKey(QSslKey(reinterpret_cast<Qt::HANDLE>(pkey))); |
1050 | |
1051 | socket->setPeerVerifyMode(QSslSocket::QueryPeer); |
1052 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
1053 | QFETCH_GLOBAL(bool, setProxy); |
1054 | if (setProxy && !socket->waitForEncrypted(msecs: 10000)) |
1055 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
1056 | } |
1057 | #endif |
1058 | |
1059 | void tst_QSslSocket::protocol() |
1060 | { |
1061 | if (!QSslSocket::supportsSsl()) |
1062 | return; |
1063 | |
1064 | QSslSocketPtr socket = newSocket(); |
1065 | this->socket = socket.data(); |
1066 | QList<QSslCertificate> certs = QSslCertificate::fromPath(path: httpServerCertChainPath()); |
1067 | |
1068 | auto sslConfig = socket->sslConfiguration(); |
1069 | sslConfig.setCaCertificates(certs); |
1070 | socket->setSslConfiguration(sslConfig); |
1071 | |
1072 | #ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND |
1073 | connect(socket, SIGNAL(sslErrors(QList<QSslError>)), |
1074 | this, SLOT(untrustedWorkaroundSlot(QList<QSslError>))); |
1075 | #endif |
1076 | |
1077 | QCOMPARE(socket->protocol(), QSsl::SecureProtocols); |
1078 | QFETCH_GLOBAL(bool, setProxy); |
1079 | { |
1080 | // qt-test-server allows TLSV1. |
1081 | socket->setProtocol(QSsl::TlsV1_0); |
1082 | QCOMPARE(socket->protocol(), QSsl::TlsV1_0); |
1083 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
1084 | if (setProxy && !socket->waitForEncrypted()) |
1085 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
1086 | QCOMPARE(socket->protocol(), QSsl::TlsV1_0); |
1087 | socket->abort(); |
1088 | QCOMPARE(socket->protocol(), QSsl::TlsV1_0); |
1089 | socket->connectToHost(hostName: QtNetworkSettings::httpServerName(), port: 443); |
1090 | QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString())); |
1091 | socket->startClientEncryption(); |
1092 | if (setProxy && !socket->waitForEncrypted()) |
1093 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
1094 | QCOMPARE(socket->protocol(), QSsl::TlsV1_0); |
1095 | socket->abort(); |
1096 | } |
1097 | { |
1098 | // qt-test-server probably doesn't allow TLSV1.1 |
1099 | socket->setProtocol(QSsl::TlsV1_1); |
1100 | QCOMPARE(socket->protocol(), QSsl::TlsV1_1); |
1101 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
1102 | if (setProxy && !socket->waitForEncrypted()) |
1103 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
1104 | QCOMPARE(socket->protocol(), QSsl::TlsV1_1); |
1105 | socket->abort(); |
1106 | QCOMPARE(socket->protocol(), QSsl::TlsV1_1); |
1107 | socket->connectToHost(hostName: QtNetworkSettings::httpServerName(), port: 443); |
1108 | QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString())); |
1109 | socket->startClientEncryption(); |
1110 | if (setProxy && !socket->waitForEncrypted()) |
1111 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
1112 | QCOMPARE(socket->protocol(), QSsl::TlsV1_1); |
1113 | socket->abort(); |
1114 | } |
1115 | { |
1116 | // qt-test-server probably doesn't allows TLSV1.2 |
1117 | socket->setProtocol(QSsl::TlsV1_2); |
1118 | QCOMPARE(socket->protocol(), QSsl::TlsV1_2); |
1119 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
1120 | if (setProxy && !socket->waitForEncrypted()) |
1121 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
1122 | QCOMPARE(socket->protocol(), QSsl::TlsV1_2); |
1123 | socket->abort(); |
1124 | QCOMPARE(socket->protocol(), QSsl::TlsV1_2); |
1125 | socket->connectToHost(hostName: QtNetworkSettings::httpServerName(), port: 443); |
1126 | QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString())); |
1127 | socket->startClientEncryption(); |
1128 | if (setProxy && !socket->waitForEncrypted()) |
1129 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
1130 | QCOMPARE(socket->protocol(), QSsl::TlsV1_2); |
1131 | socket->abort(); |
1132 | } |
1133 | |
1134 | #ifdef TLS1_3_VERSION |
1135 | { |
1136 | // qt-test-server probably doesn't allow TLSV1.3 |
1137 | socket->setProtocol(QSsl::TlsV1_3); |
1138 | QCOMPARE(socket->protocol(), QSsl::TlsV1_3); |
1139 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
1140 | if (setProxy && !socket->waitForEncrypted()) |
1141 | QSKIP("TLS 1.3 is not supported by the test server or the test is flaky - see QTBUG-29941" ); |
1142 | QCOMPARE(socket->protocol(), QSsl::TlsV1_3); |
1143 | socket->abort(); |
1144 | QCOMPARE(socket->protocol(), QSsl::TlsV1_3); |
1145 | socket->connectToHost(hostName: QtNetworkSettings::httpServerName(), port: 443); |
1146 | QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString())); |
1147 | socket->startClientEncryption(); |
1148 | if (setProxy && !socket->waitForEncrypted()) |
1149 | QSKIP("TLS 1.3 is not supported by the test server or the test is flaky - see QTBUG-29941" ); |
1150 | QCOMPARE(socket->sessionProtocol(), QSsl::TlsV1_3); |
1151 | socket->abort(); |
1152 | } |
1153 | #endif // TLS1_3_VERSION |
1154 | { |
1155 | // qt-test-server allows SSLV3, so it allows AnyProtocol. |
1156 | socket->setProtocol(QSsl::AnyProtocol); |
1157 | QCOMPARE(socket->protocol(), QSsl::AnyProtocol); |
1158 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
1159 | if (setProxy && !socket->waitForEncrypted()) |
1160 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
1161 | QCOMPARE(socket->protocol(), QSsl::AnyProtocol); |
1162 | socket->abort(); |
1163 | QCOMPARE(socket->protocol(), QSsl::AnyProtocol); |
1164 | socket->connectToHost(hostName: QtNetworkSettings::httpServerName(), port: 443); |
1165 | QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString())); |
1166 | socket->startClientEncryption(); |
1167 | if (setProxy && !socket->waitForEncrypted()) |
1168 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
1169 | QCOMPARE(socket->protocol(), QSsl::AnyProtocol); |
1170 | socket->abort(); |
1171 | } |
1172 | { |
1173 | // qt-test-server allows TlsV1, so it allows TlsV1SslV3 |
1174 | socket->setProtocol(QSsl::TlsV1SslV3); |
1175 | QCOMPARE(socket->protocol(), QSsl::TlsV1SslV3); |
1176 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
1177 | if (setProxy && !socket->waitForEncrypted()) |
1178 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
1179 | QCOMPARE(socket->protocol(), QSsl::TlsV1SslV3); |
1180 | socket->abort(); |
1181 | QCOMPARE(socket->protocol(), QSsl::TlsV1SslV3); |
1182 | socket->connectToHost(hostName: QtNetworkSettings::httpServerName(), port: 443); |
1183 | if (setProxy && !socket->waitForConnected()) |
1184 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
1185 | socket->startClientEncryption(); |
1186 | if (setProxy && !socket->waitForEncrypted()) |
1187 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
1188 | QCOMPARE(socket->protocol(), QSsl::TlsV1SslV3); |
1189 | socket->abort(); |
1190 | } |
1191 | } |
1192 | |
1193 | class SslServer : public QTcpServer |
1194 | { |
1195 | Q_OBJECT |
1196 | public: |
1197 | SslServer(const QString &keyFile = tst_QSslSocket::testDataDir + "certs/fluke.key" , |
1198 | const QString &certFile = tst_QSslSocket::testDataDir + "certs/fluke.cert" , |
1199 | const QString &interFile = QString()) |
1200 | : socket(0), |
1201 | config(QSslConfiguration::defaultConfiguration()), |
1202 | ignoreSslErrors(true), |
1203 | peerVerifyMode(QSslSocket::AutoVerifyPeer), |
1204 | protocol(QSsl::TlsV1_0), |
1205 | m_keyFile(keyFile), |
1206 | m_certFile(certFile), |
1207 | m_interFile(interFile) |
1208 | { } |
1209 | QSslSocket *socket; |
1210 | QSslConfiguration config; |
1211 | QString addCaCertificates; |
1212 | bool ignoreSslErrors; |
1213 | QSslSocket::PeerVerifyMode peerVerifyMode; |
1214 | QSsl::SslProtocol protocol; |
1215 | QString m_keyFile; |
1216 | QString m_certFile; |
1217 | QString m_interFile; |
1218 | QList<QSslCipher> ciphers; |
1219 | |
1220 | signals: |
1221 | void socketError(QAbstractSocket::SocketError); |
1222 | |
1223 | protected: |
1224 | void incomingConnection(qintptr socketDescriptor) |
1225 | { |
1226 | QSslConfiguration configuration = config; |
1227 | socket = new QSslSocket(this); |
1228 | configuration.setPeerVerifyMode(peerVerifyMode); |
1229 | configuration.setProtocol(protocol); |
1230 | if (ignoreSslErrors) |
1231 | connect(sender: socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
1232 | connect(sender: socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), receiver: this, SIGNAL(socketError(QAbstractSocket::SocketError))); |
1233 | |
1234 | QFile file(m_keyFile); |
1235 | QVERIFY(file.open(QIODevice::ReadOnly)); |
1236 | QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); |
1237 | QVERIFY(!key.isNull()); |
1238 | configuration.setPrivateKey(key); |
1239 | |
1240 | // Add CA certificates to verify client certificate |
1241 | if (!addCaCertificates.isEmpty()) { |
1242 | QList<QSslCertificate> caCert = QSslCertificate::fromPath(path: addCaCertificates); |
1243 | QVERIFY(!caCert.isEmpty()); |
1244 | QVERIFY(!caCert.first().isNull()); |
1245 | configuration.addCaCertificates(certificates: caCert); |
1246 | } |
1247 | |
1248 | // If we have a cert issued directly from the CA |
1249 | if (m_interFile.isEmpty()) { |
1250 | QList<QSslCertificate> localCert = QSslCertificate::fromPath(path: m_certFile); |
1251 | QVERIFY(!localCert.isEmpty()); |
1252 | QVERIFY(!localCert.first().isNull()); |
1253 | configuration.setLocalCertificate(localCert.first()); |
1254 | } else { |
1255 | QList<QSslCertificate> localCert = QSslCertificate::fromPath(path: m_certFile); |
1256 | QVERIFY(!localCert.isEmpty()); |
1257 | QVERIFY(!localCert.first().isNull()); |
1258 | |
1259 | QList<QSslCertificate> interCert = QSslCertificate::fromPath(path: m_interFile); |
1260 | QVERIFY(!interCert.isEmpty()); |
1261 | QVERIFY(!interCert.first().isNull()); |
1262 | |
1263 | configuration.setLocalCertificateChain(localCert + interCert); |
1264 | } |
1265 | |
1266 | if (!ciphers.isEmpty()) |
1267 | configuration.setCiphers(ciphers); |
1268 | socket->setSslConfiguration(configuration); |
1269 | |
1270 | QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState)); |
1271 | QVERIFY(!socket->peerAddress().isNull()); |
1272 | QVERIFY(socket->peerPort() != 0); |
1273 | QVERIFY(!socket->localAddress().isNull()); |
1274 | QVERIFY(socket->localPort() != 0); |
1275 | |
1276 | socket->startServerEncryption(); |
1277 | } |
1278 | |
1279 | protected slots: |
1280 | void ignoreErrorSlot() |
1281 | { |
1282 | socket->ignoreSslErrors(); |
1283 | } |
1284 | }; |
1285 | |
1286 | void tst_QSslSocket::protocolServerSide_data() |
1287 | { |
1288 | #ifdef Q_OS_WINRT |
1289 | QSKIP("Server-side encryption is not implemented on WinRT." ); |
1290 | #endif |
1291 | QTest::addColumn<QSsl::SslProtocol>(name: "serverProtocol" ); |
1292 | QTest::addColumn<QSsl::SslProtocol>(name: "clientProtocol" ); |
1293 | QTest::addColumn<bool>(name: "works" ); |
1294 | |
1295 | QTest::newRow(dataTag: "tls1.0-tls1.0" ) << QSsl::TlsV1_0 << QSsl::TlsV1_0 << true; |
1296 | QTest::newRow(dataTag: "tls1ssl3-tls1ssl3" ) << QSsl::TlsV1SslV3 << QSsl::TlsV1SslV3 << true; |
1297 | QTest::newRow(dataTag: "any-any" ) << QSsl::AnyProtocol << QSsl::AnyProtocol << true; |
1298 | QTest::newRow(dataTag: "secure-secure" ) << QSsl::SecureProtocols << QSsl::SecureProtocols << true; |
1299 | |
1300 | QTest::newRow(dataTag: "tls1-tls1ssl3" ) << QSsl::TlsV1_0 << QSsl::TlsV1SslV3 << true; |
1301 | QTest::newRow(dataTag: "tls1.0-secure" ) << QSsl::TlsV1_0 << QSsl::SecureProtocols << true; |
1302 | QTest::newRow(dataTag: "tls1.0-any" ) << QSsl::TlsV1_0 << QSsl::AnyProtocol << true; |
1303 | |
1304 | QTest::newRow(dataTag: "tls1ssl3-tls1.0" ) << QSsl::TlsV1SslV3 << QSsl::TlsV1_0 << true; |
1305 | QTest::newRow(dataTag: "tls1ssl3-secure" ) << QSsl::TlsV1SslV3 << QSsl::SecureProtocols << true; |
1306 | QTest::newRow(dataTag: "tls1ssl3-any" ) << QSsl::TlsV1SslV3 << QSsl::AnyProtocol << true; |
1307 | |
1308 | QTest::newRow(dataTag: "secure-tls1.0" ) << QSsl::SecureProtocols << QSsl::TlsV1_0 << true; |
1309 | QTest::newRow(dataTag: "secure-tls1ssl3" ) << QSsl::SecureProtocols << QSsl::TlsV1SslV3 << true; |
1310 | QTest::newRow(dataTag: "secure-any" ) << QSsl::SecureProtocols << QSsl::AnyProtocol << true; |
1311 | |
1312 | QTest::newRow(dataTag: "tls1.0orlater-tls1.0" ) << QSsl::TlsV1_0OrLater << QSsl::TlsV1_0 << true; |
1313 | QTest::newRow(dataTag: "tls1.0orlater-tls1.1" ) << QSsl::TlsV1_0OrLater << QSsl::TlsV1_1 << true; |
1314 | QTest::newRow(dataTag: "tls1.0orlater-tls1.2" ) << QSsl::TlsV1_0OrLater << QSsl::TlsV1_2 << true; |
1315 | #ifdef TLS1_3_VERSION |
1316 | QTest::newRow(dataTag: "tls1.0orlater-tls1.3" ) << QSsl::TlsV1_0OrLater << QSsl::TlsV1_3 << true; |
1317 | #endif |
1318 | |
1319 | QTest::newRow(dataTag: "tls1.1orlater-tls1.0" ) << QSsl::TlsV1_1OrLater << QSsl::TlsV1_0 << false; |
1320 | QTest::newRow(dataTag: "tls1.1orlater-tls1.1" ) << QSsl::TlsV1_1OrLater << QSsl::TlsV1_1 << true; |
1321 | QTest::newRow(dataTag: "tls1.1orlater-tls1.2" ) << QSsl::TlsV1_1OrLater << QSsl::TlsV1_2 << true; |
1322 | |
1323 | #ifdef TLS1_3_VERSION |
1324 | QTest::newRow(dataTag: "tls1.1orlater-tls1.3" ) << QSsl::TlsV1_1OrLater << QSsl::TlsV1_3 << true; |
1325 | #endif |
1326 | |
1327 | QTest::newRow(dataTag: "tls1.2orlater-tls1.0" ) << QSsl::TlsV1_2OrLater << QSsl::TlsV1_0 << false; |
1328 | QTest::newRow(dataTag: "tls1.2orlater-tls1.1" ) << QSsl::TlsV1_2OrLater << QSsl::TlsV1_1 << false; |
1329 | QTest::newRow(dataTag: "tls1.2orlater-tls1.2" ) << QSsl::TlsV1_2OrLater << QSsl::TlsV1_2 << true; |
1330 | #ifdef TLS1_3_VERSION |
1331 | QTest::newRow(dataTag: "tls1.2orlater-tls1.3" ) << QSsl::TlsV1_2OrLater << QSsl::TlsV1_3 << true; |
1332 | #endif |
1333 | #ifdef TLS1_3_VERSION |
1334 | QTest::newRow(dataTag: "tls1.3orlater-tls1.0" ) << QSsl::TlsV1_3OrLater << QSsl::TlsV1_0 << false; |
1335 | QTest::newRow(dataTag: "tls1.3orlater-tls1.1" ) << QSsl::TlsV1_3OrLater << QSsl::TlsV1_1 << false; |
1336 | QTest::newRow(dataTag: "tls1.3orlater-tls1.2" ) << QSsl::TlsV1_3OrLater << QSsl::TlsV1_2 << false; |
1337 | QTest::newRow(dataTag: "tls1.3orlater-tls1.3" ) << QSsl::TlsV1_3OrLater << QSsl::TlsV1_3 << true; |
1338 | #endif // TLS1_3_VERSION |
1339 | |
1340 | QTest::newRow(dataTag: "any-tls1.0" ) << QSsl::AnyProtocol << QSsl::TlsV1_0 << true; |
1341 | QTest::newRow(dataTag: "any-tls1ssl3" ) << QSsl::AnyProtocol << QSsl::TlsV1SslV3 << true; |
1342 | QTest::newRow(dataTag: "any-secure" ) << QSsl::AnyProtocol << QSsl::SecureProtocols << true; |
1343 | } |
1344 | |
1345 | void tst_QSslSocket::protocolServerSide() |
1346 | { |
1347 | if (!QSslSocket::supportsSsl()) { |
1348 | qWarning(msg: "SSL not supported, skipping test" ); |
1349 | return; |
1350 | } |
1351 | |
1352 | QFETCH_GLOBAL(bool, setProxy); |
1353 | if (setProxy) |
1354 | return; |
1355 | |
1356 | QFETCH(QSsl::SslProtocol, serverProtocol); |
1357 | SslServer server; |
1358 | server.protocol = serverProtocol; |
1359 | QVERIFY(server.listen()); |
1360 | |
1361 | QEventLoop loop; |
1362 | connect(sender: &server, SIGNAL(socketError(QAbstractSocket::SocketError)), receiver: &loop, SLOT(quit())); |
1363 | QTimer::singleShot(msec: 5000, receiver: &loop, SLOT(quit())); |
1364 | |
1365 | QSslSocket client; |
1366 | socket = &client; |
1367 | QFETCH(QSsl::SslProtocol, clientProtocol); |
1368 | socket->setProtocol(clientProtocol); |
1369 | // upon SSL wrong version error, errorOccurred will be triggered, not sslErrors |
1370 | connect(sender: socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), receiver: &loop, SLOT(quit())); |
1371 | connect(sender: socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
1372 | connect(sender: socket, SIGNAL(encrypted()), receiver: &loop, SLOT(quit())); |
1373 | |
1374 | client.connectToHostEncrypted(hostName: QHostAddress(QHostAddress::LocalHost).toString(), port: server.serverPort()); |
1375 | |
1376 | loop.exec(); |
1377 | |
1378 | QFETCH(bool, works); |
1379 | QAbstractSocket::SocketState expectedState = (works) ? QAbstractSocket::ConnectedState : QAbstractSocket::UnconnectedState; |
1380 | // Determine whether the client or the server caused the event loop |
1381 | // to quit due to a socket error, and investigate the culprit. |
1382 | if (client.error() != QAbstractSocket::UnknownSocketError) { |
1383 | // It can happen that the client, after TCP connection established, before |
1384 | // incomingConnection() slot fired, hits TLS initialization error and stops |
1385 | // the loop, so the server socket is not created yet. |
1386 | if (server.socket) |
1387 | QVERIFY(server.socket->error() == QAbstractSocket::UnknownSocketError); |
1388 | |
1389 | QCOMPARE(client.state(), expectedState); |
1390 | } else if (server.socket->error() != QAbstractSocket::UnknownSocketError) { |
1391 | QVERIFY(client.error() == QAbstractSocket::UnknownSocketError); |
1392 | QCOMPARE(server.socket->state(), expectedState); |
1393 | } |
1394 | |
1395 | QCOMPARE(client.isEncrypted(), works); |
1396 | } |
1397 | |
1398 | #ifndef QT_NO_OPENSSL |
1399 | |
1400 | void tst_QSslSocket::serverCipherPreferences() |
1401 | { |
1402 | #ifdef Q_OS_WINRT |
1403 | QSKIP("Server-side encryption is not implemented on WinRT." ); |
1404 | #endif |
1405 | if (!QSslSocket::supportsSsl()) { |
1406 | qWarning(msg: "SSL not supported, skipping test" ); |
1407 | return; |
1408 | } |
1409 | |
1410 | QFETCH_GLOBAL(bool, setProxy); |
1411 | if (setProxy) |
1412 | return; |
1413 | |
1414 | // First using the default (server preference) |
1415 | { |
1416 | SslServer server; |
1417 | server.ciphers = {QSslCipher("AES128-SHA" ), QSslCipher("AES256-SHA" )}; |
1418 | QVERIFY(server.listen()); |
1419 | |
1420 | QEventLoop loop; |
1421 | QTimer::singleShot(msec: 5000, receiver: &loop, SLOT(quit())); |
1422 | |
1423 | QSslSocket client; |
1424 | socket = &client; |
1425 | |
1426 | auto sslConfig = socket->sslConfiguration(); |
1427 | sslConfig.setCiphers({QSslCipher("AES256-SHA" ), QSslCipher("AES128-SHA" )}); |
1428 | socket->setSslConfiguration(sslConfig); |
1429 | |
1430 | // upon SSL wrong version error, errorOccurred will be triggered, not sslErrors |
1431 | connect(sender: socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), receiver: &loop, SLOT(quit())); |
1432 | connect(sender: socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
1433 | connect(sender: socket, SIGNAL(encrypted()), receiver: &loop, SLOT(quit())); |
1434 | |
1435 | client.connectToHostEncrypted(hostName: QHostAddress(QHostAddress::LocalHost).toString(), port: server.serverPort()); |
1436 | |
1437 | loop.exec(); |
1438 | |
1439 | QVERIFY(client.isEncrypted()); |
1440 | QCOMPARE(client.sessionCipher().name(), QString("AES128-SHA" )); |
1441 | } |
1442 | |
1443 | { |
1444 | // Now using the client preferences |
1445 | SslServer server; |
1446 | QSslConfiguration config = QSslConfiguration::defaultConfiguration(); |
1447 | config.setSslOption(option: QSsl::SslOptionDisableServerCipherPreference, on: true); |
1448 | server.config = config; |
1449 | server.ciphers = {QSslCipher("AES128-SHA" ), QSslCipher("AES256-SHA" )}; |
1450 | QVERIFY(server.listen()); |
1451 | |
1452 | QEventLoop loop; |
1453 | QTimer::singleShot(msec: 5000, receiver: &loop, SLOT(quit())); |
1454 | |
1455 | QSslSocket client; |
1456 | socket = &client; |
1457 | |
1458 | auto sslConfig = socket->sslConfiguration(); |
1459 | sslConfig.setCiphers({QSslCipher("AES256-SHA" ), QSslCipher("AES128-SHA" )}); |
1460 | socket->setSslConfiguration(sslConfig); |
1461 | |
1462 | // upon SSL wrong version error, errorOccurred will be triggered, not sslErrors |
1463 | connect(sender: socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), receiver: &loop, SLOT(quit())); |
1464 | connect(sender: socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
1465 | connect(sender: socket, SIGNAL(encrypted()), receiver: &loop, SLOT(quit())); |
1466 | |
1467 | client.connectToHostEncrypted(hostName: QHostAddress(QHostAddress::LocalHost).toString(), port: server.serverPort()); |
1468 | |
1469 | loop.exec(); |
1470 | |
1471 | QVERIFY(client.isEncrypted()); |
1472 | QCOMPARE(client.sessionCipher().name(), QString("AES256-SHA" )); |
1473 | } |
1474 | } |
1475 | |
1476 | #endif // QT_NO_OPENSSL |
1477 | |
1478 | |
1479 | void tst_QSslSocket::setCaCertificates() |
1480 | { |
1481 | if (!QSslSocket::supportsSsl()) |
1482 | return; |
1483 | |
1484 | QSslSocket socket; |
1485 | QCOMPARE(socket.sslConfiguration().caCertificates(), |
1486 | QSslConfiguration::defaultConfiguration().caCertificates()); |
1487 | |
1488 | auto sslConfig = socket.sslConfiguration(); |
1489 | sslConfig.setCaCertificates( |
1490 | QSslCertificate::fromPath(path: testDataDir + "certs/qt-test-server-cacert.pem" )); |
1491 | socket.setSslConfiguration(sslConfig); |
1492 | QCOMPARE(socket.sslConfiguration().caCertificates().size(), 1); |
1493 | |
1494 | sslConfig.setCaCertificates(QSslConfiguration::defaultConfiguration().caCertificates()); |
1495 | socket.setSslConfiguration(sslConfig); |
1496 | QCOMPARE(socket.sslConfiguration().caCertificates(), |
1497 | QSslConfiguration::defaultConfiguration().caCertificates()); |
1498 | } |
1499 | |
1500 | void tst_QSslSocket::setLocalCertificate() |
1501 | { |
1502 | } |
1503 | |
1504 | void tst_QSslSocket::localCertificateChain() |
1505 | { |
1506 | if (!QSslSocket::supportsSsl()) |
1507 | return; |
1508 | |
1509 | QSslSocket socket; |
1510 | socket.setLocalCertificate(fileName: testDataDir + "certs/fluke.cert" ); |
1511 | |
1512 | QSslConfiguration conf = socket.sslConfiguration(); |
1513 | QList<QSslCertificate> chain = conf.localCertificateChain(); |
1514 | QCOMPARE(chain.size(), 1); |
1515 | QCOMPARE(chain[0], conf.localCertificate()); |
1516 | QCOMPARE(chain[0], socket.localCertificate()); |
1517 | } |
1518 | |
1519 | void tst_QSslSocket::setLocalCertificateChain() |
1520 | { |
1521 | #ifdef Q_OS_WINRT |
1522 | QSKIP("Server-side encryption is not implemented on WinRT." ); |
1523 | #endif |
1524 | if (!QSslSocket::supportsSsl()) |
1525 | return; |
1526 | |
1527 | QFETCH_GLOBAL(bool, setProxy); |
1528 | if (setProxy) |
1529 | return; |
1530 | |
1531 | SslServer server(testDataDir + "certs/leaf.key" , |
1532 | testDataDir + "certs/leaf.crt" , |
1533 | testDataDir + "certs/inter.crt" ); |
1534 | |
1535 | QVERIFY(server.listen()); |
1536 | |
1537 | QEventLoop loop; |
1538 | QTimer::singleShot(msec: 5000, receiver: &loop, SLOT(quit())); |
1539 | |
1540 | const QScopedPointer<QSslSocket, QScopedPointerDeleteLater> client(new QSslSocket); |
1541 | socket = client.data(); |
1542 | connect(sender: socket, SIGNAL(encrypted()), receiver: &loop, SLOT(quit())); |
1543 | connect(sender: socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), receiver: &loop, SLOT(quit())); |
1544 | connect(sender: socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
1545 | |
1546 | socket->connectToHostEncrypted(hostName: QHostAddress(QHostAddress::LocalHost).toString(), port: server.serverPort()); |
1547 | loop.exec(); |
1548 | |
1549 | QList<QSslCertificate> chain = socket->peerCertificateChain(); |
1550 | #if QT_CONFIG(schannel) |
1551 | QEXPECT_FAIL("" , "Schannel cannot send intermediate certificates not " |
1552 | "located in a system certificate store" , |
1553 | Abort); |
1554 | #endif |
1555 | QCOMPARE(chain.size(), 2); |
1556 | QCOMPARE(chain[0].serialNumber(), QByteArray("10:a0:ad:77:58:f6:6e:ae:46:93:a3:43:f9:59:8a:9e" )); |
1557 | QCOMPARE(chain[1].serialNumber(), QByteArray("3b:eb:99:c5:ea:d8:0b:5d:0b:97:5d:4f:06:75:4b:e1" )); |
1558 | } |
1559 | |
1560 | void tst_QSslSocket::tlsConfiguration() |
1561 | { |
1562 | QFETCH_GLOBAL(const bool, setProxy); |
1563 | if (setProxy) |
1564 | return; |
1565 | // Test some things not covered by any other auto-test. |
1566 | QSslSocket socket; |
1567 | auto tlsConfig = socket.sslConfiguration(); |
1568 | QVERIFY(tlsConfig.sessionCipher().isNull()); |
1569 | QCOMPARE(tlsConfig.addCaCertificates(QStringLiteral("nonexisting/chain.crt" )), false); |
1570 | QCOMPARE(tlsConfig.sessionProtocol(), QSsl::UnknownProtocol); |
1571 | QSslConfiguration nullConfig; |
1572 | QVERIFY(nullConfig.isNull()); |
1573 | #ifndef QT_NO_OPENSSL |
1574 | nullConfig.setEllipticCurves(tlsConfig.ellipticCurves()); |
1575 | QCOMPARE(nullConfig.ellipticCurves(), tlsConfig.ellipticCurves()); |
1576 | #endif |
1577 | QMap<QByteArray, QVariant> backendConfig; |
1578 | backendConfig["DTLSMTU" ] = QVariant::fromValue(value: 1024); |
1579 | backendConfig["DTLSTIMEOUTMS" ] = QVariant::fromValue(value: 1000); |
1580 | nullConfig.setBackendConfiguration(backendConfig); |
1581 | QCOMPARE(nullConfig.backendConfiguration(), backendConfig); |
1582 | QTest::ignoreMessage(type: QtWarningMsg, message: "QSslConfiguration::setPeerVerifyDepth: cannot set negative depth of -1000" ); |
1583 | nullConfig.setPeerVerifyDepth(-1000); |
1584 | QVERIFY(nullConfig.peerVerifyDepth() != -1000); |
1585 | nullConfig.setPeerVerifyDepth(100); |
1586 | QCOMPARE(nullConfig.peerVerifyDepth(), 100); |
1587 | } |
1588 | |
1589 | void tst_QSslSocket::setSocketDescriptor() |
1590 | { |
1591 | #ifdef Q_OS_WINRT |
1592 | QSKIP("Server-side encryption is not implemented on WinRT." ); |
1593 | #endif |
1594 | if (!QSslSocket::supportsSsl()) |
1595 | return; |
1596 | |
1597 | QFETCH_GLOBAL(bool, setProxy); |
1598 | if (setProxy) |
1599 | return; |
1600 | |
1601 | SslServer server; |
1602 | QVERIFY(server.listen()); |
1603 | |
1604 | QEventLoop loop; |
1605 | QTimer::singleShot(msec: 5000, receiver: &loop, SLOT(quit())); |
1606 | |
1607 | QSslSocket client; |
1608 | socket = &client; |
1609 | connect(sender: socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
1610 | connect(sender: socket, SIGNAL(encrypted()), receiver: &loop, SLOT(quit())); |
1611 | |
1612 | client.connectToHostEncrypted(hostName: QHostAddress(QHostAddress::LocalHost).toString(), port: server.serverPort()); |
1613 | |
1614 | loop.exec(); |
1615 | |
1616 | QCOMPARE(client.state(), QAbstractSocket::ConnectedState); |
1617 | QVERIFY(client.isEncrypted()); |
1618 | QVERIFY(!client.peerAddress().isNull()); |
1619 | QVERIFY(client.peerPort() != 0); |
1620 | QVERIFY(!client.localAddress().isNull()); |
1621 | QVERIFY(client.localPort() != 0); |
1622 | } |
1623 | |
1624 | void tst_QSslSocket::setSslConfiguration_data() |
1625 | { |
1626 | #if QT_CONFIG(securetransport) |
1627 | QSKIP("Skipping the cases with certificate, SecureTransport does not like old certificate on the test server" ); |
1628 | #endif // securetransport |
1629 | QTest::addColumn<QSslConfiguration>(name: "configuration" ); |
1630 | QTest::addColumn<bool>(name: "works" ); |
1631 | |
1632 | QTest::newRow(dataTag: "empty" ) << QSslConfiguration() << false; |
1633 | QSslConfiguration conf = QSslConfiguration::defaultConfiguration(); |
1634 | QTest::newRow(dataTag: "default" ) << conf << false; // does not contain test server cert |
1635 | QList<QSslCertificate> testServerCert = QSslCertificate::fromPath(path: httpServerCertChainPath()); |
1636 | conf.setCaCertificates(testServerCert); |
1637 | QTest::newRow(dataTag: "set-root-cert" ) << conf << true; |
1638 | conf.setProtocol(QSsl::SecureProtocols); |
1639 | QTest::newRow(dataTag: "secure" ) << conf << true; |
1640 | } |
1641 | |
1642 | void tst_QSslSocket::setSslConfiguration() |
1643 | { |
1644 | if (!QSslSocket::supportsSsl()) |
1645 | return; |
1646 | |
1647 | QSslSocketPtr socket = newSocket(); |
1648 | QFETCH(QSslConfiguration, configuration); |
1649 | socket->setSslConfiguration(configuration); |
1650 | #if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2 |
1651 | socket->setProtocol(QSsl::SslProtocol::TlsV1_1); |
1652 | #endif |
1653 | this->socket = socket.data(); |
1654 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
1655 | QFETCH(bool, works); |
1656 | QFETCH_GLOBAL(bool, setProxy); |
1657 | if (setProxy && (socket->waitForEncrypted(msecs: 10000) != works)) |
1658 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
1659 | if (works) { |
1660 | socket->disconnectFromHost(); |
1661 | QVERIFY2(socket->waitForDisconnected(), qPrintable(socket->errorString())); |
1662 | } |
1663 | } |
1664 | |
1665 | void tst_QSslSocket::waitForEncrypted() |
1666 | { |
1667 | if (!QSslSocket::supportsSsl()) |
1668 | return; |
1669 | |
1670 | QSslSocketPtr socket = newSocket(); |
1671 | this->socket = socket.data(); |
1672 | |
1673 | connect(sender: this->socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
1674 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
1675 | |
1676 | QFETCH_GLOBAL(bool, setProxy); |
1677 | if (setProxy && !socket->waitForEncrypted(msecs: 10000)) |
1678 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
1679 | } |
1680 | |
1681 | void tst_QSslSocket::waitForEncryptedMinusOne() |
1682 | { |
1683 | #ifdef Q_OS_WIN |
1684 | QSKIP("QTBUG-24451 - indefinite wait may hang" ); |
1685 | #endif |
1686 | if (!QSslSocket::supportsSsl()) |
1687 | return; |
1688 | |
1689 | QSslSocketPtr socket = newSocket(); |
1690 | this->socket = socket.data(); |
1691 | |
1692 | connect(sender: this->socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
1693 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
1694 | |
1695 | QFETCH_GLOBAL(bool, setProxy); |
1696 | if (setProxy && !socket->waitForEncrypted(msecs: -1)) |
1697 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
1698 | } |
1699 | |
1700 | void tst_QSslSocket::waitForConnectedEncryptedReadyRead() |
1701 | { |
1702 | if (!QSslSocket::supportsSsl()) |
1703 | return; |
1704 | |
1705 | QSslSocketPtr socket = newSocket(); |
1706 | this->socket = socket.data(); |
1707 | |
1708 | connect(sender: this->socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
1709 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::imapServerName(), port: 993); |
1710 | |
1711 | QVERIFY2(socket->waitForConnected(10000), qPrintable(socket->errorString())); |
1712 | QFETCH_GLOBAL(bool, setProxy); |
1713 | if (setProxy && !socket->waitForEncrypted(msecs: 10000)) |
1714 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
1715 | |
1716 | // We only do this if we have no bytes available to read already because readyRead will |
1717 | // not be emitted again. |
1718 | if (socket->bytesAvailable() == 0) |
1719 | QVERIFY(socket->waitForReadyRead(10000)); |
1720 | |
1721 | QVERIFY(!socket->peerCertificate().isNull()); |
1722 | QVERIFY(!socket->peerCertificateChain().isEmpty()); |
1723 | } |
1724 | |
1725 | void tst_QSslSocket::startClientEncryption() |
1726 | { |
1727 | } |
1728 | |
1729 | void tst_QSslSocket::startServerEncryption() |
1730 | { |
1731 | } |
1732 | |
1733 | void tst_QSslSocket::addDefaultCaCertificate() |
1734 | { |
1735 | if (!QSslSocket::supportsSsl()) |
1736 | return; |
1737 | |
1738 | // Reset the global CA chain |
1739 | auto sslConfig = QSslConfiguration::defaultConfiguration(); |
1740 | sslConfig.setCaCertificates(QSslConfiguration::systemCaCertificates()); |
1741 | QSslConfiguration::setDefaultConfiguration(sslConfig); |
1742 | |
1743 | QList<QSslCertificate> flukeCerts = QSslCertificate::fromPath(path: httpServerCertChainPath()); |
1744 | QCOMPARE(flukeCerts.size(), 1); |
1745 | QList<QSslCertificate> globalCerts = QSslConfiguration::defaultConfiguration().caCertificates(); |
1746 | QVERIFY(!globalCerts.contains(flukeCerts.first())); |
1747 | sslConfig.addCaCertificate(certificate: flukeCerts.first()); |
1748 | QSslConfiguration::setDefaultConfiguration(sslConfig); |
1749 | QCOMPARE(QSslConfiguration::defaultConfiguration().caCertificates().size(), |
1750 | globalCerts.size() + 1); |
1751 | QVERIFY(QSslConfiguration::defaultConfiguration().caCertificates() |
1752 | .contains(flukeCerts.first())); |
1753 | |
1754 | // Restore the global CA chain |
1755 | sslConfig = QSslConfiguration::defaultConfiguration(); |
1756 | sslConfig.setCaCertificates(QSslConfiguration::systemCaCertificates()); |
1757 | QSslConfiguration::setDefaultConfiguration(sslConfig); |
1758 | } |
1759 | |
1760 | void tst_QSslSocket::defaultCaCertificates() |
1761 | { |
1762 | if (!QSslSocket::supportsSsl()) |
1763 | return; |
1764 | |
1765 | QList<QSslCertificate> certs = QSslConfiguration::defaultConfiguration().caCertificates(); |
1766 | QVERIFY(certs.size() > 1); |
1767 | QCOMPARE(certs, QSslConfiguration::systemCaCertificates()); |
1768 | } |
1769 | |
1770 | void tst_QSslSocket::defaultCiphers() |
1771 | { |
1772 | if (!QSslSocket::supportsSsl()) |
1773 | return; |
1774 | |
1775 | QList<QSslCipher> ciphers = QSslConfiguration::defaultConfiguration().ciphers(); |
1776 | QVERIFY(ciphers.size() > 1); |
1777 | |
1778 | QSslSocket socket; |
1779 | QCOMPARE(socket.sslConfiguration().defaultConfiguration().ciphers(), ciphers); |
1780 | QCOMPARE(socket.sslConfiguration().ciphers(), ciphers); |
1781 | } |
1782 | |
1783 | void tst_QSslSocket::resetDefaultCiphers() |
1784 | { |
1785 | } |
1786 | |
1787 | void tst_QSslSocket::setDefaultCaCertificates() |
1788 | { |
1789 | } |
1790 | |
1791 | void tst_QSslSocket::setDefaultCiphers() |
1792 | { |
1793 | } |
1794 | |
1795 | void tst_QSslSocket::supportedCiphers() |
1796 | { |
1797 | if (!QSslSocket::supportsSsl()) |
1798 | return; |
1799 | |
1800 | QList<QSslCipher> ciphers = QSslConfiguration::supportedCiphers(); |
1801 | QVERIFY(ciphers.size() > 1); |
1802 | |
1803 | QSslSocket socket; |
1804 | QCOMPARE(socket.sslConfiguration().supportedCiphers(), ciphers); |
1805 | } |
1806 | |
1807 | void tst_QSslSocket::systemCaCertificates() |
1808 | { |
1809 | if (!QSslSocket::supportsSsl()) |
1810 | return; |
1811 | |
1812 | QList<QSslCertificate> certs = QSslConfiguration::systemCaCertificates(); |
1813 | QVERIFY(certs.size() > 1); |
1814 | QCOMPARE(certs, QSslConfiguration::defaultConfiguration().systemCaCertificates()); |
1815 | } |
1816 | |
1817 | void tst_QSslSocket::wildcardCertificateNames() |
1818 | { |
1819 | // Passing CN matches |
1820 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("www.example.com" ), QString("www.example.com" )), true ); |
1821 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("WWW.EXAMPLE.COM" ), QString("www.example.com" )), true ); |
1822 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.example.com" ), QString("www.example.com" )), true ); |
1823 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("xxx*.example.com" ), QString("xxxwww.example.com" )), true ); |
1824 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("f*.example.com" ), QString("foo.example.com" )), true ); |
1825 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("192.168.0.0" ), QString("192.168.0.0" )), true ); |
1826 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("foo.éxample.com" ), QString("foo.xn--xample-9ua.com" )), true ); |
1827 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.éxample.com" ), QString("foo.xn--xample-9ua.com" )), true ); |
1828 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("xn--kcry6tjko.example.org" ), QString("xn--kcry6tjko.example.org" )), true); |
1829 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.xn--kcry6tjko.example.org" ), QString("xn--kcr.xn--kcry6tjko.example.org" )), true); |
1830 | |
1831 | // Failing CN matches |
1832 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("xxx.example.com" ), QString("www.example.com" )), false ); |
1833 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*" ), QString("www.example.com" )), false ); |
1834 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.*.com" ), QString("www.example.com" )), false ); |
1835 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.example.com" ), QString("baa.foo.example.com" )), false ); |
1836 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("f*.example.com" ), QString("baa.example.com" )), false ); |
1837 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.com" ), QString("example.com" )), false ); |
1838 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*fail.com" ), QString("example.com" )), false ); |
1839 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.example." ), QString("www.example." )), false ); |
1840 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.example." ), QString("www.example" )), false ); |
1841 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("" ), QString("www" )), false ); |
1842 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*" ), QString("www" )), false ); |
1843 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.168.0.0" ), QString("192.168.0.0" )), false ); |
1844 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("xn--kcry6tjko*.example.org" ), QString("xn--kcry6tjkoanc.example.org" )), false ); // RFC 6125 §7.2 |
1845 | QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("Ã¥*.example.org" ), QString("xn--la-xia.example.org" )), false ); |
1846 | } |
1847 | |
1848 | void tst_QSslSocket::isMatchingHostname() |
1849 | { |
1850 | // with normalization: (the certificate has *.SCHÄUFELE.DE as a CN) |
1851 | // openssl req -x509 -nodes -subj "/CN=*.SCHÄUFELE.DE" -newkey rsa:512 -keyout /dev/null -out xn--schufele-2za.crt |
1852 | QList<QSslCertificate> certs = QSslCertificate::fromPath(path: testDataDir + "certs/xn--schufele-2za.crt" ); |
1853 | QVERIFY(!certs.isEmpty()); |
1854 | QSslCertificate cert = certs.first(); |
1855 | |
1856 | QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("WWW.SCHÄUFELE.DE" )), true); |
1857 | QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("www.xn--schufele-2za.de" )), true); |
1858 | QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("www.schäufele.de" )), true); |
1859 | QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("föo.schäufele.de" )), true); |
1860 | |
1861 | QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("foo.foo.xn--schufele-2za.de" )), false); |
1862 | QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("www.schaufele.de" )), false); |
1863 | QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("www.schufele.de" )), false); |
1864 | |
1865 | /* Generated with the following command (only valid with openssl >= 1.1.1 due to "-addext"): |
1866 | openssl req -x509 -nodes -subj "/CN=example.org" \ |
1867 | -addext "subjectAltName = IP:192.5.8.16, IP:fe80::3c29:2fa1:dd44:765" \ |
1868 | -newkey rsa:2048 -keyout /dev/null -out subjectAltNameIP.crt |
1869 | */ |
1870 | certs = QSslCertificate::fromPath(path: testDataDir + "certs/subjectAltNameIP.crt" ); |
1871 | QVERIFY(!certs.isEmpty()); |
1872 | cert = certs.first(); |
1873 | QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("192.5.8.16" )), true); |
1874 | QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("fe80::3c29:2fa1:dd44:765" )), true); |
1875 | |
1876 | /* openssl req -x509 -nodes -new -newkey rsa -keyout /dev/null -out 127-0-0-1-as-CN.crt \ |
1877 | -subj "/CN=127.0.0.1" |
1878 | */ |
1879 | certs = QSslCertificate::fromPath(path: testDataDir + "certs/127-0-0-1-as-CN.crt" ); |
1880 | QVERIFY(!certs.isEmpty()); |
1881 | cert = certs.first(); |
1882 | QCOMPARE(QSslSocketPrivate::isMatchingHostname(cert, QString::fromUtf8("127.0.0.1" )), true); |
1883 | } |
1884 | |
1885 | void tst_QSslSocket::wildcard() |
1886 | { |
1887 | QSKIP("TODO: solve wildcard problem" ); |
1888 | |
1889 | if (!QSslSocket::supportsSsl()) |
1890 | return; |
1891 | |
1892 | // Fluke runs an apache server listening on port 4443, serving the |
1893 | // wildcard fluke.*.troll.no. The DNS entry for |
1894 | // fluke.wildcard.dev.troll.no, served by ares (root for dev.troll.no), |
1895 | // returns the CNAME fluke.troll.no for this domain. The web server |
1896 | // responds with the wildcard, and QSslSocket should accept that as a |
1897 | // valid connection. This was broken in 4.3.0. |
1898 | QSslSocketPtr socket = newSocket(); |
1899 | auto config = socket->sslConfiguration(); |
1900 | config.addCaCertificates(path: QLatin1String("certs/aspiriniks.ca.crt" )); |
1901 | socket->setSslConfiguration(config); |
1902 | this->socket = socket.data(); |
1903 | #ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND |
1904 | connect(socket, SIGNAL(sslErrors(QList<QSslError>)), |
1905 | this, SLOT(untrustedWorkaroundSlot(QList<QSslError>))); |
1906 | #endif |
1907 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::wildcardServerName(), port: 4443); |
1908 | |
1909 | QFETCH_GLOBAL(bool, setProxy); |
1910 | if (setProxy && !socket->waitForEncrypted(msecs: 3000)) |
1911 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
1912 | |
1913 | QSslCertificate certificate = socket->peerCertificate(); |
1914 | QVERIFY(certificate.subjectInfo(QSslCertificate::CommonName).contains(QString(QtNetworkSettings::serverLocalName() + ".*." + QtNetworkSettings::serverDomainName()))); |
1915 | QVERIFY(certificate.issuerInfo(QSslCertificate::CommonName).contains(QtNetworkSettings::serverName())); |
1916 | |
1917 | socket->close(); |
1918 | } |
1919 | |
1920 | class SslServer2 : public QTcpServer |
1921 | { |
1922 | protected: |
1923 | void incomingConnection(qintptr socketDescriptor) |
1924 | { |
1925 | QSslSocket *socket = new QSslSocket(this); |
1926 | socket->ignoreSslErrors(); |
1927 | |
1928 | // Only set the certificate |
1929 | QList<QSslCertificate> localCert = QSslCertificate::fromPath(path: tst_QSslSocket::testDataDir + "certs/fluke.cert" ); |
1930 | QVERIFY(!localCert.isEmpty()); |
1931 | QVERIFY(!localCert.first().isNull()); |
1932 | socket->setLocalCertificate(localCert.first()); |
1933 | |
1934 | QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState)); |
1935 | |
1936 | socket->startServerEncryption(); |
1937 | } |
1938 | }; |
1939 | |
1940 | void tst_QSslSocket::setEmptyKey() |
1941 | { |
1942 | #ifdef Q_OS_WINRT |
1943 | QSKIP("Server-side encryption is not implemented on WinRT." ); |
1944 | #endif |
1945 | if (!QSslSocket::supportsSsl()) |
1946 | return; |
1947 | |
1948 | QFETCH_GLOBAL(bool, setProxy); |
1949 | if (setProxy) |
1950 | return; |
1951 | |
1952 | SslServer2 server; |
1953 | server.listen(); |
1954 | |
1955 | QSslSocket socket; |
1956 | socket.connectToHostEncrypted(hostName: "127.0.0.1" , port: server.serverPort()); |
1957 | |
1958 | QTestEventLoop::instance().enterLoop(secs: 2); |
1959 | |
1960 | QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); |
1961 | QCOMPARE(socket.error(), QAbstractSocket::UnknownSocketError); |
1962 | } |
1963 | |
1964 | void tst_QSslSocket::spontaneousWrite() |
1965 | { |
1966 | #ifdef Q_OS_WINRT |
1967 | QSKIP("Server-side encryption is not implemented on WinRT." ); |
1968 | #endif |
1969 | QFETCH_GLOBAL(bool, setProxy); |
1970 | if (setProxy) |
1971 | return; |
1972 | |
1973 | SslServer server; |
1974 | QSslSocket *receiver = new QSslSocket(this); |
1975 | connect(asender: receiver, SIGNAL(readyRead()), SLOT(exitLoop())); |
1976 | |
1977 | // connect two sockets to each other: |
1978 | QVERIFY(server.listen(QHostAddress::LocalHost)); |
1979 | receiver->connectToHost(hostName: "127.0.0.1" , port: server.serverPort()); |
1980 | QVERIFY(receiver->waitForConnected(5000)); |
1981 | QVERIFY(server.waitForNewConnection(0)); |
1982 | |
1983 | QSslSocket *sender = server.socket; |
1984 | QVERIFY(sender); |
1985 | QCOMPARE(sender->state(), QAbstractSocket::ConnectedState); |
1986 | receiver->setObjectName("receiver" ); |
1987 | sender->setObjectName("sender" ); |
1988 | receiver->ignoreSslErrors(); |
1989 | receiver->startClientEncryption(); |
1990 | |
1991 | // SSL handshake: |
1992 | connect(asender: receiver, SIGNAL(encrypted()), SLOT(exitLoop())); |
1993 | enterLoop(secs: 1); |
1994 | QVERIFY(!timeout()); |
1995 | QVERIFY(sender->isEncrypted()); |
1996 | QVERIFY(receiver->isEncrypted()); |
1997 | |
1998 | // make sure there's nothing to be received on the sender: |
1999 | while (sender->waitForReadyRead(msecs: 10) || receiver->waitForBytesWritten(msecs: 10)) {} |
2000 | |
2001 | // spontaneously write something: |
2002 | QByteArray data("Hello World" ); |
2003 | sender->write(data); |
2004 | |
2005 | // check if the other side receives it: |
2006 | enterLoop(secs: 1); |
2007 | QVERIFY(!timeout()); |
2008 | QCOMPARE(receiver->bytesAvailable(), qint64(data.size())); |
2009 | QCOMPARE(receiver->readAll(), data); |
2010 | } |
2011 | |
2012 | void tst_QSslSocket::setReadBufferSize() |
2013 | { |
2014 | #ifdef Q_OS_WINRT |
2015 | QSKIP("Server-side encryption is not implemented on WinRT." ); |
2016 | #endif |
2017 | QFETCH_GLOBAL(bool, setProxy); |
2018 | if (setProxy) |
2019 | return; |
2020 | |
2021 | SslServer server; |
2022 | QSslSocket *receiver = new QSslSocket(this); |
2023 | connect(asender: receiver, SIGNAL(readyRead()), SLOT(exitLoop())); |
2024 | |
2025 | // connect two sockets to each other: |
2026 | QVERIFY(server.listen(QHostAddress::LocalHost)); |
2027 | receiver->connectToHost(hostName: "127.0.0.1" , port: server.serverPort()); |
2028 | QVERIFY(receiver->waitForConnected(5000)); |
2029 | QVERIFY(server.waitForNewConnection(0)); |
2030 | |
2031 | QSslSocket *sender = server.socket; |
2032 | QVERIFY(sender); |
2033 | QCOMPARE(sender->state(), QAbstractSocket::ConnectedState); |
2034 | receiver->setObjectName("receiver" ); |
2035 | sender->setObjectName("sender" ); |
2036 | receiver->ignoreSslErrors(); |
2037 | receiver->startClientEncryption(); |
2038 | |
2039 | // SSL handshake: |
2040 | connect(asender: receiver, SIGNAL(encrypted()), SLOT(exitLoop())); |
2041 | enterLoop(secs: 1); |
2042 | QVERIFY(!timeout()); |
2043 | QVERIFY(sender->isEncrypted()); |
2044 | QVERIFY(receiver->isEncrypted()); |
2045 | |
2046 | QByteArray data(2048, 'b'); |
2047 | receiver->setReadBufferSize(39 * 1024); // make it a non-multiple of the data.size() |
2048 | |
2049 | // saturate the incoming buffer |
2050 | while (sender->state() == QAbstractSocket::ConnectedState && |
2051 | receiver->state() == QAbstractSocket::ConnectedState && |
2052 | receiver->bytesAvailable() < receiver->readBufferSize()) { |
2053 | sender->write(data); |
2054 | //qDebug() << receiver->bytesAvailable() << "<" << receiver->readBufferSize() << (receiver->bytesAvailable() < receiver->readBufferSize()); |
2055 | |
2056 | while (sender->bytesToWrite()) |
2057 | QVERIFY(sender->waitForBytesWritten(10)); |
2058 | |
2059 | // drain it: |
2060 | while (receiver->bytesAvailable() < receiver->readBufferSize() && |
2061 | receiver->waitForReadyRead(msecs: 10)) {} |
2062 | } |
2063 | |
2064 | //qDebug() << sender->bytesToWrite() << "bytes to write"; |
2065 | //qDebug() << receiver->bytesAvailable() << "bytes available"; |
2066 | |
2067 | // send a bit more |
2068 | sender->write(data); |
2069 | sender->write(data); |
2070 | sender->write(data); |
2071 | sender->write(data); |
2072 | QVERIFY(sender->waitForBytesWritten(10)); |
2073 | |
2074 | qint64 oldBytesAvailable = receiver->bytesAvailable(); |
2075 | |
2076 | // now unset the read buffer limit and iterate |
2077 | receiver->setReadBufferSize(0); |
2078 | enterLoop(secs: 1); |
2079 | QVERIFY(!timeout()); |
2080 | |
2081 | QVERIFY(receiver->bytesAvailable() > oldBytesAvailable); |
2082 | } |
2083 | |
2084 | class SetReadBufferSize_task_250027_handler : public QObject { |
2085 | Q_OBJECT |
2086 | public slots: |
2087 | void readyReadSlot() { |
2088 | QTestEventLoop::instance().exitLoop(); |
2089 | } |
2090 | void waitSomeMore(QSslSocket *socket) { |
2091 | QElapsedTimer t; |
2092 | t.start(); |
2093 | while (!socket->encryptedBytesAvailable()) { |
2094 | QCoreApplication::processEvents(flags: QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents, maxtime: 250); |
2095 | if (t.elapsed() > 1000 || socket->state() != QAbstractSocket::ConnectedState) |
2096 | return; |
2097 | } |
2098 | } |
2099 | }; |
2100 | |
2101 | void tst_QSslSocket::setReadBufferSize_task_250027() |
2102 | { |
2103 | QSKIP("QTBUG-29730 - flakey test blocking integration" ); |
2104 | |
2105 | // do not execute this when a proxy is set. |
2106 | QFETCH_GLOBAL(bool, setProxy); |
2107 | if (setProxy) |
2108 | return; |
2109 | |
2110 | QSslSocketPtr socket = newSocket(); |
2111 | socket->setReadBufferSize(1000); // limit to 1 kb/sec |
2112 | socket->ignoreSslErrors(); |
2113 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
2114 | socket->ignoreSslErrors(); |
2115 | QVERIFY2(socket->waitForConnected(10*1000), qPrintable(socket->errorString())); |
2116 | if (setProxy && !socket->waitForEncrypted(msecs: 10*1000)) |
2117 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
2118 | |
2119 | // exit the event loop as soon as we receive a readyRead() |
2120 | SetReadBufferSize_task_250027_handler setReadBufferSize_task_250027_handler; |
2121 | connect(sender: socket.data(), SIGNAL(readyRead()), receiver: &setReadBufferSize_task_250027_handler, SLOT(readyReadSlot())); |
2122 | |
2123 | // provoke a response by sending a request |
2124 | socket->write(data: "GET /qtest/fluke.gif HTTP/1.0\n" ); // this file is 27 KB |
2125 | socket->write(data: "Host: " ); |
2126 | socket->write(data: QtNetworkSettings::httpServerName().toLocal8Bit().constData()); |
2127 | socket->write(data: "\n" ); |
2128 | socket->write(data: "Connection: close\n" ); |
2129 | socket->write(data: "\n" ); |
2130 | socket->flush(); |
2131 | |
2132 | QTestEventLoop::instance().enterLoop(secs: 10); |
2133 | setReadBufferSize_task_250027_handler.waitSomeMore(socket: socket.data()); |
2134 | QByteArray firstRead = socket->readAll(); |
2135 | // First read should be some data, but not the whole file |
2136 | QVERIFY(firstRead.size() > 0 && firstRead.size() < 20*1024); |
2137 | |
2138 | QTestEventLoop::instance().enterLoop(secs: 10); |
2139 | setReadBufferSize_task_250027_handler.waitSomeMore(socket: socket.data()); |
2140 | QByteArray secondRead = socket->readAll(); |
2141 | // second read should be some more data |
2142 | |
2143 | int secondReadSize = secondRead.size(); |
2144 | |
2145 | if (secondReadSize <= 0) { |
2146 | QEXPECT_FAIL("" , "QTBUG-29730" , Continue); |
2147 | } |
2148 | |
2149 | QVERIFY(secondReadSize > 0); |
2150 | |
2151 | socket->close(); |
2152 | } |
2153 | |
2154 | class SslServer3 : public QTcpServer |
2155 | { |
2156 | Q_OBJECT |
2157 | public: |
2158 | SslServer3() : socket(0) { } |
2159 | QSslSocket *socket; |
2160 | |
2161 | protected: |
2162 | void incomingConnection(qintptr socketDescriptor) |
2163 | { |
2164 | socket = new QSslSocket(this); |
2165 | connect(sender: socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
2166 | |
2167 | QFile file(tst_QSslSocket::testDataDir + "certs/fluke.key" ); |
2168 | QVERIFY(file.open(QIODevice::ReadOnly)); |
2169 | QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); |
2170 | QVERIFY(!key.isNull()); |
2171 | socket->setPrivateKey(key); |
2172 | |
2173 | QList<QSslCertificate> localCert = QSslCertificate::fromPath(path: tst_QSslSocket::testDataDir |
2174 | + "certs/fluke.cert" ); |
2175 | QVERIFY(!localCert.isEmpty()); |
2176 | QVERIFY(!localCert.first().isNull()); |
2177 | socket->setLocalCertificate(localCert.first()); |
2178 | |
2179 | QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState)); |
2180 | QVERIFY(!socket->peerAddress().isNull()); |
2181 | QVERIFY(socket->peerPort() != 0); |
2182 | QVERIFY(!socket->localAddress().isNull()); |
2183 | QVERIFY(socket->localPort() != 0); |
2184 | } |
2185 | |
2186 | protected slots: |
2187 | void ignoreErrorSlot() |
2188 | { |
2189 | socket->ignoreSslErrors(); |
2190 | } |
2191 | }; |
2192 | |
2193 | class ThreadedSslServer: public QThread |
2194 | { |
2195 | Q_OBJECT |
2196 | public: |
2197 | QSemaphore dataReadSemaphore; |
2198 | int serverPort; |
2199 | bool ok; |
2200 | |
2201 | ThreadedSslServer() : serverPort(-1), ok(false) |
2202 | { } |
2203 | |
2204 | ~ThreadedSslServer() |
2205 | { |
2206 | if (isRunning()) wait(time: 2000); |
2207 | QVERIFY(ok); |
2208 | } |
2209 | |
2210 | signals: |
2211 | void listening(); |
2212 | |
2213 | protected: |
2214 | void run() |
2215 | { |
2216 | // if all goes well (no timeouts), this thread will sleep for a total of 500 ms |
2217 | // (i.e., 5 times 100 ms, one sleep for each operation) |
2218 | |
2219 | SslServer3 server; |
2220 | server.listen(address: QHostAddress::LocalHost); |
2221 | serverPort = server.serverPort(); |
2222 | emit listening(); |
2223 | |
2224 | // delayed acceptance: |
2225 | QTest::qSleep(ms: 100); |
2226 | bool ret = server.waitForNewConnection(msec: 2000); |
2227 | Q_UNUSED(ret); |
2228 | |
2229 | // delayed start of encryption |
2230 | QTest::qSleep(ms: 100); |
2231 | QSslSocket *socket = server.socket; |
2232 | if (!socket || !socket->isValid()) |
2233 | return; // error |
2234 | socket->ignoreSslErrors(); |
2235 | socket->startServerEncryption(); |
2236 | if (!socket->waitForEncrypted(msecs: 2000)) |
2237 | return; // error |
2238 | |
2239 | // delayed reading data |
2240 | QTest::qSleep(ms: 100); |
2241 | if (!socket->waitForReadyRead(msecs: 2000) && socket->bytesAvailable() == 0) |
2242 | return; // error |
2243 | socket->readAll(); |
2244 | dataReadSemaphore.release(); |
2245 | |
2246 | // delayed sending data |
2247 | QTest::qSleep(ms: 100); |
2248 | socket->write(data: "Hello, World" ); |
2249 | while (socket->bytesToWrite()) |
2250 | if (!socket->waitForBytesWritten(msecs: 2000)) |
2251 | return; // error |
2252 | |
2253 | // delayed replying (reading then sending) |
2254 | QTest::qSleep(ms: 100); |
2255 | if (!socket->waitForReadyRead(msecs: 2000)) |
2256 | return; // error |
2257 | socket->write(data: "Hello, World" ); |
2258 | while (socket->bytesToWrite()) |
2259 | if (!socket->waitForBytesWritten(msecs: 2000)) |
2260 | return; // error |
2261 | |
2262 | // delayed disconnection: |
2263 | QTest::qSleep(ms: 100); |
2264 | socket->disconnectFromHost(); |
2265 | if (!socket->waitForDisconnected(msecs: 2000)) |
2266 | return; // error |
2267 | |
2268 | delete socket; |
2269 | ok = true; |
2270 | } |
2271 | }; |
2272 | |
2273 | void tst_QSslSocket::waitForMinusOne() |
2274 | { |
2275 | #ifdef Q_OS_WIN |
2276 | QSKIP("QTBUG-24451 - indefinite wait may hang" ); |
2277 | #endif |
2278 | #ifdef Q_OS_WINRT // This can stay in case the one above goes away |
2279 | QSKIP("Server-side encryption is not implemented on WinRT." ); |
2280 | #endif |
2281 | QFETCH_GLOBAL(bool, setProxy); |
2282 | if (setProxy) |
2283 | return; |
2284 | |
2285 | ThreadedSslServer server; |
2286 | connect(asender: &server, SIGNAL(listening()), SLOT(exitLoop())); |
2287 | |
2288 | // start the thread and wait for it to be ready |
2289 | server.start(); |
2290 | enterLoop(secs: 1); |
2291 | QVERIFY(!timeout()); |
2292 | |
2293 | // connect to the server |
2294 | QSslSocket socket; |
2295 | QTest::qSleep(ms: 100); |
2296 | socket.connectToHost(hostName: "127.0.0.1" , port: server.serverPort); |
2297 | QVERIFY(socket.waitForConnected(-1)); |
2298 | socket.ignoreSslErrors(); |
2299 | socket.startClientEncryption(); |
2300 | |
2301 | // first verification: this waiting should take 200 ms |
2302 | if (!socket.waitForEncrypted(msecs: -1)) |
2303 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
2304 | QVERIFY(socket.isEncrypted()); |
2305 | QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); |
2306 | QCOMPARE(socket.bytesAvailable(), Q_INT64_C(0)); |
2307 | |
2308 | // second verification: write and make sure the other side got it (100 ms) |
2309 | socket.write(data: "How are you doing?" ); |
2310 | QVERIFY(socket.bytesToWrite() != 0); |
2311 | QVERIFY(socket.waitForBytesWritten(-1)); |
2312 | QVERIFY(server.dataReadSemaphore.tryAcquire(1, 2500)); |
2313 | |
2314 | // third verification: it should wait for 100 ms: |
2315 | QVERIFY(socket.waitForReadyRead(-1)); |
2316 | QVERIFY(socket.isEncrypted()); |
2317 | QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); |
2318 | QVERIFY(socket.bytesAvailable() != 0); |
2319 | |
2320 | // fourth verification: deadlock prevention: |
2321 | // we write and then wait for reading; the other side needs to receive before |
2322 | // replying (100 ms delay) |
2323 | socket.write(data: "I'm doing just fine!" ); |
2324 | QVERIFY(socket.bytesToWrite() != 0); |
2325 | QVERIFY(socket.waitForReadyRead(-1)); |
2326 | |
2327 | // fifth verification: it should wait for 200 ms more |
2328 | QVERIFY(socket.waitForDisconnected(-1)); |
2329 | |
2330 | // sixth verification: reading from a disconnected socket returns -1 |
2331 | // once we deplete the read buffer |
2332 | QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState); |
2333 | socket.readAll(); |
2334 | char aux; |
2335 | QCOMPARE(socket.read(&aux, 1), -1); |
2336 | } |
2337 | |
2338 | class VerifyServer : public QTcpServer |
2339 | { |
2340 | Q_OBJECT |
2341 | public: |
2342 | VerifyServer() : socket(0) { } |
2343 | QSslSocket *socket; |
2344 | |
2345 | protected: |
2346 | void incomingConnection(qintptr socketDescriptor) |
2347 | { |
2348 | socket = new QSslSocket(this); |
2349 | |
2350 | socket->setPrivateKey(fileName: tst_QSslSocket::testDataDir + "certs/fluke.key" ); |
2351 | socket->setLocalCertificate(fileName: tst_QSslSocket::testDataDir + "certs/fluke.cert" ); |
2352 | socket->setSocketDescriptor(socketDescriptor); |
2353 | socket->startServerEncryption(); |
2354 | } |
2355 | }; |
2356 | |
2357 | void tst_QSslSocket::verifyMode() |
2358 | { |
2359 | #ifdef Q_OS_WINRT |
2360 | QSKIP("Server-side encryption is not implemented on WinRT." ); |
2361 | #endif |
2362 | QFETCH_GLOBAL(bool, setProxy); |
2363 | if (setProxy) |
2364 | return; |
2365 | |
2366 | QSslSocket socket; |
2367 | #if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2 |
2368 | socket.setProtocol(QSsl::SslProtocol::TlsV1_1); |
2369 | #endif |
2370 | QCOMPARE(socket.peerVerifyMode(), QSslSocket::AutoVerifyPeer); |
2371 | socket.setPeerVerifyMode(QSslSocket::VerifyNone); |
2372 | QCOMPARE(socket.peerVerifyMode(), QSslSocket::VerifyNone); |
2373 | socket.setPeerVerifyMode(QSslSocket::VerifyNone); |
2374 | socket.setPeerVerifyMode(QSslSocket::VerifyPeer); |
2375 | QCOMPARE(socket.peerVerifyMode(), QSslSocket::VerifyPeer); |
2376 | |
2377 | socket.connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
2378 | if (socket.waitForEncrypted()) |
2379 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
2380 | |
2381 | QList<QSslError> expectedErrors = QList<QSslError>() |
2382 | << QSslError(FLUKE_CERTIFICATE_ERROR, socket.peerCertificate()); |
2383 | QCOMPARE(socket.sslHandshakeErrors(), expectedErrors); |
2384 | socket.abort(); |
2385 | |
2386 | VerifyServer server; |
2387 | server.listen(); |
2388 | |
2389 | QSslSocket clientSocket; |
2390 | clientSocket.connectToHostEncrypted(hostName: "127.0.0.1" , port: server.serverPort()); |
2391 | clientSocket.ignoreSslErrors(); |
2392 | |
2393 | QEventLoop loop; |
2394 | QTimer::singleShot(msec: 5000, receiver: &loop, SLOT(quit())); |
2395 | connect(sender: &clientSocket, SIGNAL(encrypted()), receiver: &loop, SLOT(quit())); |
2396 | loop.exec(); |
2397 | |
2398 | QVERIFY(clientSocket.isEncrypted()); |
2399 | QVERIFY(server.socket->sslHandshakeErrors().isEmpty()); |
2400 | } |
2401 | |
2402 | void tst_QSslSocket::verifyDepth() |
2403 | { |
2404 | QSslSocket socket; |
2405 | QCOMPARE(socket.peerVerifyDepth(), 0); |
2406 | socket.setPeerVerifyDepth(1); |
2407 | QCOMPARE(socket.peerVerifyDepth(), 1); |
2408 | QTest::ignoreMessage(type: QtWarningMsg, message: "QSslSocket::setPeerVerifyDepth: cannot set negative depth of -1" ); |
2409 | socket.setPeerVerifyDepth(-1); |
2410 | QCOMPARE(socket.peerVerifyDepth(), 1); |
2411 | } |
2412 | |
2413 | #ifndef QT_NO_OPENSSL |
2414 | void tst_QSslSocket::verifyAndDefaultConfiguration() |
2415 | { |
2416 | QFETCH_GLOBAL(const bool, setProxy); |
2417 | if (setProxy) |
2418 | return; |
2419 | const auto defaultCACertificates = QSslConfiguration::defaultConfiguration().caCertificates(); |
2420 | const auto chainGuard = qScopeGuard(f: [&defaultCACertificates]{ |
2421 | auto conf = QSslConfiguration::defaultConfiguration(); |
2422 | conf.setCaCertificates(defaultCACertificates); |
2423 | QSslConfiguration::setDefaultConfiguration(conf); |
2424 | }); |
2425 | |
2426 | auto chain = QSslCertificate::fromPath(path: testDataDir + QStringLiteral("certs/qtiochain.crt" ), format: QSsl::Pem); |
2427 | QCOMPARE(chain.size(), 2); |
2428 | QVERIFY(!chain.at(0).isNull()); |
2429 | QVERIFY(!chain.at(1).isNull()); |
2430 | auto errors = QSslCertificate::verify(certificateChain: chain); |
2431 | // At least, test that 'verify' did not alter the default configuration: |
2432 | QCOMPARE(defaultCACertificates, QSslConfiguration::defaultConfiguration().caCertificates()); |
2433 | if (!errors.isEmpty()) |
2434 | QSKIP("The certificate for qt.io could not be trusted, skipping the rest of the test" ); |
2435 | #ifdef Q_OS_WINDOWS |
2436 | const auto fakeCaChain = QSslCertificate::fromPath(testDataDir + QStringLiteral("certs/fluke.cert" )); |
2437 | QCOMPARE(fakeCaChain.size(), 1); |
2438 | const auto caCert = fakeCaChain.at(0); |
2439 | QVERIFY(!caCert.isNull()); |
2440 | auto conf = QSslConfiguration::defaultConfiguration(); |
2441 | conf.setCaCertificates({caCert}); |
2442 | QSslConfiguration::setDefaultConfiguration(conf); |
2443 | errors = QSslCertificate::verify(chain); |
2444 | QVERIFY(errors.size() > 0); |
2445 | QCOMPARE(QSslConfiguration::defaultConfiguration().caCertificates(), QList<QSslCertificate>() << caCert); |
2446 | #endif |
2447 | } |
2448 | #endif // QT_NO_OPENSSL |
2449 | |
2450 | void tst_QSslSocket::disconnectFromHostWhenConnecting() |
2451 | { |
2452 | QSslSocketPtr socket = newSocket(); |
2453 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::imapServerName(), port: 993); |
2454 | socket->ignoreSslErrors(); |
2455 | socket->write(data: "XXXX LOGOUT\r\n" ); |
2456 | QAbstractSocket::SocketState state = socket->state(); |
2457 | // without proxy, the state will be HostLookupState; |
2458 | // with proxy, the state will be ConnectingState. |
2459 | QVERIFY(socket->state() == QAbstractSocket::HostLookupState || |
2460 | socket->state() == QAbstractSocket::ConnectingState); |
2461 | socket->disconnectFromHost(); |
2462 | // the state of the socket must be the same before and after calling |
2463 | // disconnectFromHost() |
2464 | QCOMPARE(state, socket->state()); |
2465 | QVERIFY(socket->state() == QAbstractSocket::HostLookupState || |
2466 | socket->state() == QAbstractSocket::ConnectingState); |
2467 | if (!socket->waitForDisconnected(msecs: 10000)) |
2468 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
2469 | QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState); |
2470 | // we did not call close, so the socket must be still open |
2471 | QVERIFY(socket->isOpen()); |
2472 | QCOMPARE(socket->bytesToWrite(), qint64(0)); |
2473 | |
2474 | // don't forget to login |
2475 | QCOMPARE((int) socket->write("USER ftptest\r\n" ), 14); |
2476 | |
2477 | } |
2478 | |
2479 | void tst_QSslSocket::disconnectFromHostWhenConnected() |
2480 | { |
2481 | QSslSocketPtr socket = newSocket(); |
2482 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::imapServerName(), port: 993); |
2483 | socket->ignoreSslErrors(); |
2484 | if (!socket->waitForEncrypted(msecs: 5000)) |
2485 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
2486 | socket->write(data: "XXXX LOGOUT\r\n" ); |
2487 | QCOMPARE(socket->state(), QAbstractSocket::ConnectedState); |
2488 | socket->disconnectFromHost(); |
2489 | QCOMPARE(socket->state(), QAbstractSocket::ClosingState); |
2490 | QVERIFY(socket->waitForDisconnected(5000)); |
2491 | QCOMPARE(socket->bytesToWrite(), qint64(0)); |
2492 | } |
2493 | |
2494 | #ifndef QT_NO_OPENSSL |
2495 | |
2496 | class BrokenPskHandshake : public QTcpServer |
2497 | { |
2498 | public: |
2499 | void socketError(QAbstractSocket::SocketError error) |
2500 | { |
2501 | Q_UNUSED(error); |
2502 | QSslSocket *clientSocket = qobject_cast<QSslSocket *>(object: sender()); |
2503 | Q_ASSERT(clientSocket); |
2504 | clientSocket->close(); |
2505 | QTestEventLoop::instance().exitLoop(); |
2506 | } |
2507 | private: |
2508 | |
2509 | void incomingConnection(qintptr handle) override |
2510 | { |
2511 | if (!socket.setSocketDescriptor(socketDescriptor: handle)) |
2512 | return; |
2513 | |
2514 | QSslConfiguration serverConfig(QSslConfiguration::defaultConfiguration()); |
2515 | serverConfig.setPreSharedKeyIdentityHint("abcdefghijklmnop" ); |
2516 | socket.setSslConfiguration(serverConfig); |
2517 | socket.startServerEncryption(); |
2518 | } |
2519 | |
2520 | QSslSocket socket; |
2521 | }; |
2522 | |
2523 | void tst_QSslSocket::closeWhileEmittingSocketError() |
2524 | { |
2525 | QFETCH_GLOBAL(bool, setProxy); |
2526 | if (setProxy) |
2527 | return; |
2528 | |
2529 | BrokenPskHandshake handshake; |
2530 | if (!handshake.listen()) |
2531 | QSKIP("failed to start TLS server" ); |
2532 | |
2533 | QSslSocket clientSocket; |
2534 | QSslConfiguration clientConfig(QSslConfiguration::defaultConfiguration()); |
2535 | clientConfig.setPeerVerifyMode(QSslSocket::VerifyNone); |
2536 | clientSocket.setSslConfiguration(clientConfig); |
2537 | |
2538 | QSignalSpy socketErrorSpy(&clientSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError))); |
2539 | connect(sender: &clientSocket, signal: &QSslSocket::errorOccurred, receiver: &handshake, slot: &BrokenPskHandshake::socketError); |
2540 | |
2541 | clientSocket.connectToHostEncrypted(QStringLiteral("127.0.0.1" ), port: handshake.serverPort()); |
2542 | // Make sure we have some data buffered so that close will try to flush: |
2543 | clientSocket.write(data: QByteArray(1000000, Qt::Uninitialized)); |
2544 | |
2545 | QTestEventLoop::instance().enterLoopMSecs(ms: 1000); |
2546 | QVERIFY(!QTestEventLoop::instance().timeout()); |
2547 | |
2548 | QCOMPARE(socketErrorSpy.count(), 1); |
2549 | } |
2550 | |
2551 | #endif // QT_NO_OPENSSL |
2552 | |
2553 | void tst_QSslSocket::resetProxy() |
2554 | { |
2555 | #ifndef QT_NO_NETWORKPROXY |
2556 | QFETCH_GLOBAL(bool, setProxy); |
2557 | if (setProxy) |
2558 | return; |
2559 | |
2560 | // check fix for bug 199941 |
2561 | |
2562 | QNetworkProxy goodProxy(QNetworkProxy::NoProxy); |
2563 | QNetworkProxy badProxy(QNetworkProxy::HttpProxy, "thisCannotWorkAbsolutelyNotForSure" , 333); |
2564 | |
2565 | // make sure the connection works, and then set a nonsense proxy, and then |
2566 | // make sure it does not work anymore |
2567 | QSslSocket socket; |
2568 | auto config = socket.sslConfiguration(); |
2569 | config.addCaCertificates(path: httpServerCertChainPath()); |
2570 | socket.setSslConfiguration(config); |
2571 | socket.setProxy(goodProxy); |
2572 | socket.connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
2573 | QVERIFY2(socket.waitForConnected(10000), qPrintable(socket.errorString())); |
2574 | socket.abort(); |
2575 | socket.setProxy(badProxy); |
2576 | socket.connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
2577 | QVERIFY(! socket.waitForConnected(10000)); |
2578 | |
2579 | // don't forget to login |
2580 | QCOMPARE((int) socket.write("USER ftptest\r\n" ), 14); |
2581 | QCOMPARE((int) socket.write("PASS password\r\n" ), 15); |
2582 | |
2583 | enterLoop(secs: 10); |
2584 | |
2585 | // now the other way round: |
2586 | // set the nonsense proxy and make sure the connection does not work, |
2587 | // and then set the right proxy and make sure it works |
2588 | QSslSocket socket2; |
2589 | auto config2 = socket.sslConfiguration(); |
2590 | config2.addCaCertificates(path: httpServerCertChainPath()); |
2591 | socket2.setSslConfiguration(config2); |
2592 | socket2.setProxy(badProxy); |
2593 | socket2.connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
2594 | QVERIFY(! socket2.waitForConnected(10000)); |
2595 | socket2.abort(); |
2596 | socket2.setProxy(goodProxy); |
2597 | socket2.connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
2598 | QVERIFY2(socket2.waitForConnected(10000), qPrintable(socket.errorString())); |
2599 | #endif // QT_NO_NETWORKPROXY |
2600 | } |
2601 | |
2602 | void tst_QSslSocket::ignoreSslErrorsList_data() |
2603 | { |
2604 | QTest::addColumn<QList<QSslError> >(name: "expectedSslErrors" ); |
2605 | QTest::addColumn<int>(name: "expectedSslErrorSignalCount" ); |
2606 | |
2607 | // construct the list of errors that we will get with the SSL handshake and that we will ignore |
2608 | QList<QSslError> expectedSslErrors; |
2609 | // fromPath gives us a list of certs, but it actually only contains one |
2610 | QList<QSslCertificate> certs = QSslCertificate::fromPath(path: httpServerCertChainPath()); |
2611 | QSslError rightError(FLUKE_CERTIFICATE_ERROR, certs.at(i: 0)); |
2612 | QSslError wrongError(FLUKE_CERTIFICATE_ERROR); |
2613 | |
2614 | |
2615 | QTest::newRow(dataTag: "SSL-failure-empty-list" ) << expectedSslErrors << 1; |
2616 | expectedSslErrors.append(t: wrongError); |
2617 | QTest::newRow(dataTag: "SSL-failure-wrong-error" ) << expectedSslErrors << 1; |
2618 | expectedSslErrors.append(t: rightError); |
2619 | QTest::newRow(dataTag: "allErrorsInExpectedList1" ) << expectedSslErrors << 0; |
2620 | expectedSslErrors.removeAll(t: wrongError); |
2621 | QTest::newRow(dataTag: "allErrorsInExpectedList2" ) << expectedSslErrors << 0; |
2622 | expectedSslErrors.removeAll(t: rightError); |
2623 | QTest::newRow(dataTag: "SSL-failure-empty-list-again" ) << expectedSslErrors << 1; |
2624 | } |
2625 | |
2626 | void tst_QSslSocket::ignoreSslErrorsList() |
2627 | { |
2628 | QSslSocket socket; |
2629 | connect(sender: &socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), |
2630 | receiver: this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); |
2631 | |
2632 | QSslCertificate cert; |
2633 | |
2634 | QFETCH(QList<QSslError>, expectedSslErrors); |
2635 | socket.ignoreSslErrors(errors: expectedSslErrors); |
2636 | |
2637 | QFETCH(int, expectedSslErrorSignalCount); |
2638 | QSignalSpy (&socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError))); |
2639 | |
2640 | socket.connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
2641 | |
2642 | bool expectEncryptionSuccess = (expectedSslErrorSignalCount == 0); |
2643 | if (socket.waitForEncrypted(msecs: 10000) != expectEncryptionSuccess) |
2644 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
2645 | QCOMPARE(sslErrorsSpy.count(), expectedSslErrorSignalCount); |
2646 | } |
2647 | |
2648 | void tst_QSslSocket::ignoreSslErrorsListWithSlot_data() |
2649 | { |
2650 | ignoreSslErrorsList_data(); |
2651 | } |
2652 | |
2653 | // this is not a test, just a slot called in the test below |
2654 | void tst_QSslSocket::ignoreErrorListSlot(const QList<QSslError> &) |
2655 | { |
2656 | socket->ignoreSslErrors(errors: storedExpectedSslErrors); |
2657 | } |
2658 | |
2659 | void tst_QSslSocket::ignoreSslErrorsListWithSlot() |
2660 | { |
2661 | QSslSocket socket; |
2662 | this->socket = &socket; |
2663 | |
2664 | QFETCH(QList<QSslError>, expectedSslErrors); |
2665 | // store the errors to ignore them later in the slot connected below |
2666 | storedExpectedSslErrors = expectedSslErrors; |
2667 | connect(sender: &socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), |
2668 | receiver: this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); |
2669 | connect(sender: &socket, SIGNAL(sslErrors(QList<QSslError>)), |
2670 | receiver: this, SLOT(ignoreErrorListSlot(QList<QSslError>))); |
2671 | socket.connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
2672 | |
2673 | QFETCH(int, expectedSslErrorSignalCount); |
2674 | bool expectEncryptionSuccess = (expectedSslErrorSignalCount == 0); |
2675 | QFETCH_GLOBAL(bool, setProxy); |
2676 | if (setProxy && (socket.waitForEncrypted(msecs: 10000) != expectEncryptionSuccess)) |
2677 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
2678 | } |
2679 | |
2680 | void tst_QSslSocket::abortOnSslErrors() |
2681 | { |
2682 | #ifdef Q_OS_WINRT |
2683 | QSKIP("Server-side encryption is not implemented on WinRT." ); |
2684 | #endif |
2685 | QFETCH_GLOBAL(bool, setProxy); |
2686 | if (setProxy) |
2687 | return; |
2688 | |
2689 | SslServer server; |
2690 | QVERIFY(server.listen()); |
2691 | |
2692 | QSslSocket clientSocket; |
2693 | connect(sender: &clientSocket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(abortOnErrorSlot())); |
2694 | clientSocket.connectToHostEncrypted(hostName: "127.0.0.1" , port: server.serverPort()); |
2695 | clientSocket.ignoreSslErrors(); |
2696 | |
2697 | QEventLoop loop; |
2698 | QTimer::singleShot(msec: 1000, receiver: &loop, SLOT(quit())); |
2699 | loop.exec(); |
2700 | |
2701 | QCOMPARE(clientSocket.state(), QAbstractSocket::UnconnectedState); |
2702 | } |
2703 | |
2704 | // make sure a closed socket has no bytesAvailable() |
2705 | // related to https://bugs.webkit.org/show_bug.cgi?id=28016 |
2706 | void tst_QSslSocket::readFromClosedSocket() |
2707 | { |
2708 | QSslSocketPtr socket = newSocket(); |
2709 | #if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2 |
2710 | socket->setProtocol(QSsl::SslProtocol::TlsV1_1); |
2711 | #endif |
2712 | socket->ignoreSslErrors(); |
2713 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
2714 | socket->ignoreSslErrors(); |
2715 | socket->waitForConnected(); |
2716 | socket->waitForEncrypted(); |
2717 | // provoke a response by sending a request |
2718 | socket->write(data: "GET /qtest/fluke.gif HTTP/1.1\n" ); |
2719 | socket->write(data: "Host: " ); |
2720 | socket->write(data: QtNetworkSettings::httpServerName().toLocal8Bit().constData()); |
2721 | socket->write(data: "\n" ); |
2722 | socket->write(data: "\n" ); |
2723 | socket->waitForBytesWritten(); |
2724 | socket->waitForReadyRead(); |
2725 | QFETCH_GLOBAL(bool, setProxy); |
2726 | if (setProxy && (socket->state() != QAbstractSocket::ConnectedState)) |
2727 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
2728 | QVERIFY(socket->bytesAvailable()); |
2729 | socket->close(); |
2730 | QVERIFY(!socket->bytesAvailable()); |
2731 | QVERIFY(!socket->bytesToWrite()); |
2732 | QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState); |
2733 | } |
2734 | |
2735 | void tst_QSslSocket::writeBigChunk() |
2736 | { |
2737 | if (!QSslSocket::supportsSsl()) |
2738 | return; |
2739 | |
2740 | QSslSocketPtr socket = newSocket(); |
2741 | this->socket = socket.data(); |
2742 | |
2743 | connect(sender: this->socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
2744 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
2745 | |
2746 | QByteArray data; |
2747 | // Originally, the test had this: '1024*1024*10; // 10 MB' |
2748 | data.resize(size: 1024 * 1024 * 10); |
2749 | // Init with garbage. Needed so TLS cannot compress it in an efficient way. |
2750 | QRandomGenerator::global()->fillRange(buffer: reinterpret_cast<quint32 *>(data.data()), |
2751 | count: data.size() / int(sizeof(quint32))); |
2752 | |
2753 | if (!socket->waitForEncrypted(msecs: 10000)) |
2754 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
2755 | QString errorBefore = socket->errorString(); |
2756 | |
2757 | int ret = socket->write(data: data.constData(), len: data.size()); |
2758 | QCOMPARE(data.size(), ret); |
2759 | |
2760 | // spin the event loop once so QSslSocket::transmit() gets called |
2761 | QCoreApplication::processEvents(); |
2762 | QString errorAfter = socket->errorString(); |
2763 | |
2764 | // no better way to do this right now since the error is the same as the default error. |
2765 | if (socket->errorString().startsWith(s: QLatin1String("Unable to write data" ))) |
2766 | { |
2767 | qWarning() << socket->error() << socket->errorString(); |
2768 | QFAIL("Error while writing! Check if the OpenSSL BIO size is limited?!" ); |
2769 | } |
2770 | // also check the error string. If another error (than UnknownError) occurred, it should be different than before |
2771 | QVERIFY2(errorBefore == errorAfter || socket->error() == QAbstractSocket::RemoteHostClosedError, |
2772 | QByteArray("unexpected error: " ).append(qPrintable(errorAfter))); |
2773 | |
2774 | // check that everything has been written to OpenSSL |
2775 | QCOMPARE(socket->bytesToWrite(), 0); |
2776 | |
2777 | socket->close(); |
2778 | } |
2779 | |
2780 | void tst_QSslSocket::blacklistedCertificates() |
2781 | { |
2782 | #ifdef Q_OS_WINRT |
2783 | QSKIP("Server-side encryption is not implemented on WinRT." ); |
2784 | #endif |
2785 | QFETCH_GLOBAL(bool, setProxy); |
2786 | if (setProxy) |
2787 | return; |
2788 | |
2789 | SslServer server(testDataDir + "certs/fake-login.live.com.key" , testDataDir + "certs/fake-login.live.com.pem" ); |
2790 | QSslSocket *receiver = new QSslSocket(this); |
2791 | connect(asender: receiver, SIGNAL(readyRead()), SLOT(exitLoop())); |
2792 | |
2793 | // connect two sockets to each other: |
2794 | QVERIFY(server.listen(QHostAddress::LocalHost)); |
2795 | receiver->connectToHost(hostName: "127.0.0.1" , port: server.serverPort()); |
2796 | QVERIFY(receiver->waitForConnected(5000)); |
2797 | if (!server.waitForNewConnection(msec: 0)) |
2798 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
2799 | |
2800 | QSslSocket *sender = server.socket; |
2801 | QVERIFY(sender); |
2802 | QCOMPARE(sender->state(), QAbstractSocket::ConnectedState); |
2803 | receiver->setObjectName("receiver" ); |
2804 | sender->setObjectName("sender" ); |
2805 | receiver->startClientEncryption(); |
2806 | |
2807 | connect(asender: receiver, SIGNAL(sslErrors(QList<QSslError>)), SLOT(exitLoop())); |
2808 | connect(asender: receiver, SIGNAL(encrypted()), SLOT(exitLoop())); |
2809 | enterLoop(secs: 1); |
2810 | QList<QSslError> sslErrors = receiver->sslHandshakeErrors(); |
2811 | QVERIFY(sslErrors.count() > 0); |
2812 | // there are more errors (self signed cert and hostname mismatch), but we only care about the blacklist error |
2813 | QCOMPARE(sslErrors.at(0).error(), QSslError::CertificateBlacklisted); |
2814 | } |
2815 | |
2816 | void tst_QSslSocket::versionAccessors() |
2817 | { |
2818 | if (!QSslSocket::supportsSsl()) |
2819 | return; |
2820 | |
2821 | qDebug() << QSslSocket::sslLibraryVersionString(); |
2822 | qDebug() << QString::number(QSslSocket::sslLibraryVersionNumber(), base: 16); |
2823 | } |
2824 | |
2825 | #ifndef QT_NO_OPENSSL |
2826 | void tst_QSslSocket::sslOptions() |
2827 | { |
2828 | if (!QSslSocket::supportsSsl()) |
2829 | return; |
2830 | |
2831 | #ifdef SSL_OP_NO_COMPRESSION |
2832 | QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols, |
2833 | QSslConfigurationPrivate::defaultSslOptions), |
2834 | long(SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_COMPRESSION|SSL_OP_CIPHER_SERVER_PREFERENCE)); |
2835 | #else |
2836 | QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols, |
2837 | QSslConfigurationPrivate::defaultSslOptions), |
2838 | long(SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_CIPHER_SERVER_PREFERENCE)); |
2839 | #endif |
2840 | |
2841 | QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols, |
2842 | QSsl::SslOptionDisableEmptyFragments |
2843 | |QSsl::SslOptionDisableLegacyRenegotiation), |
2844 | long(SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_CIPHER_SERVER_PREFERENCE)); |
2845 | |
2846 | #ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION |
2847 | QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols, |
2848 | QSsl::SslOptionDisableEmptyFragments), |
2849 | long((SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION|SSL_OP_CIPHER_SERVER_PREFERENCE))); |
2850 | #endif |
2851 | |
2852 | #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS |
2853 | QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols, |
2854 | QSsl::SslOptionDisableLegacyRenegotiation), |
2855 | long((SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_CIPHER_SERVER_PREFERENCE) & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS)); |
2856 | #endif |
2857 | |
2858 | #ifdef SSL_OP_NO_TICKET |
2859 | QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols, |
2860 | QSsl::SslOptionDisableEmptyFragments |
2861 | |QSsl::SslOptionDisableLegacyRenegotiation |
2862 | |QSsl::SslOptionDisableSessionTickets), |
2863 | long((SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TICKET|SSL_OP_CIPHER_SERVER_PREFERENCE))); |
2864 | #endif |
2865 | |
2866 | #ifdef SSL_OP_NO_TICKET |
2867 | #ifdef SSL_OP_NO_COMPRESSION |
2868 | QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols, |
2869 | QSsl::SslOptionDisableEmptyFragments |
2870 | |QSsl::SslOptionDisableLegacyRenegotiation |
2871 | |QSsl::SslOptionDisableSessionTickets |
2872 | |QSsl::SslOptionDisableCompression), |
2873 | 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))); |
2874 | #endif |
2875 | #endif |
2876 | } |
2877 | #endif |
2878 | |
2879 | void tst_QSslSocket::encryptWithoutConnecting() |
2880 | { |
2881 | if (!QSslSocket::supportsSsl()) |
2882 | return; |
2883 | |
2884 | QTest::ignoreMessage(type: QtWarningMsg, |
2885 | message: "QSslSocket::startClientEncryption: cannot start handshake when not connected" ); |
2886 | |
2887 | QSslSocket sock; |
2888 | sock.startClientEncryption(); |
2889 | } |
2890 | |
2891 | void tst_QSslSocket::resume_data() |
2892 | { |
2893 | QTest::addColumn<bool>(name: "ignoreErrorsAfterPause" ); |
2894 | QTest::addColumn<QList<QSslError> >(name: "errorsToIgnore" ); |
2895 | QTest::addColumn<bool>(name: "expectSuccess" ); |
2896 | |
2897 | QList<QSslError> errorsList; |
2898 | QTest::newRow(dataTag: "DoNotIgnoreErrors" ) << false << QList<QSslError>() << false; |
2899 | QTest::newRow(dataTag: "ignoreAllErrors" ) << true << QList<QSslError>() << true; |
2900 | |
2901 | // Note, httpServerCertChainPath() it's ... because we use the same certificate on |
2902 | // different services. We'll be actually connecting to IMAP server. |
2903 | QList<QSslCertificate> certs = QSslCertificate::fromPath(path: httpServerCertChainPath()); |
2904 | QSslError rightError(FLUKE_CERTIFICATE_ERROR, certs.at(i: 0)); |
2905 | QSslError wrongError(FLUKE_CERTIFICATE_ERROR); |
2906 | errorsList.append(t: wrongError); |
2907 | QTest::newRow(dataTag: "ignoreSpecificErrors-Wrong" ) << true << errorsList << false; |
2908 | errorsList.clear(); |
2909 | errorsList.append(t: rightError); |
2910 | QTest::newRow(dataTag: "ignoreSpecificErrors-Right" ) << true << errorsList << true; |
2911 | } |
2912 | |
2913 | void tst_QSslSocket::resume() |
2914 | { |
2915 | // make sure the server certificate is not in the list of accepted certificates, |
2916 | // we want to trigger the sslErrors signal |
2917 | auto sslConfig = QSslConfiguration::defaultConfiguration(); |
2918 | sslConfig.setCaCertificates(QSslConfiguration::systemCaCertificates()); |
2919 | QSslConfiguration::setDefaultConfiguration(sslConfig); |
2920 | |
2921 | QFETCH(bool, ignoreErrorsAfterPause); |
2922 | QFETCH(QList<QSslError>, errorsToIgnore); |
2923 | QFETCH(bool, expectSuccess); |
2924 | |
2925 | QSslSocket socket; |
2926 | socket.setPauseMode(QAbstractSocket::PauseOnSslErrors); |
2927 | |
2928 | QSignalSpy sslErrorSpy(&socket, SIGNAL(sslErrors(QList<QSslError>))); |
2929 | QSignalSpy encryptedSpy(&socket, SIGNAL(encrypted())); |
2930 | QSignalSpy errorSpy(&socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError))); |
2931 | |
2932 | connect(sender: &socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: &QTestEventLoop::instance(), SLOT(exitLoop())); |
2933 | connect(sender: &socket, SIGNAL(encrypted()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop())); |
2934 | connect(sender: &socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), |
2935 | receiver: this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); |
2936 | connect(sender: &socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), receiver: &QTestEventLoop::instance(), SLOT(exitLoop())); |
2937 | |
2938 | socket.connectToHostEncrypted(hostName: QtNetworkSettings::imapServerName(), port: 993); |
2939 | QTestEventLoop::instance().enterLoop(secs: 10); |
2940 | QFETCH_GLOBAL(bool, setProxy); |
2941 | if (setProxy && QTestEventLoop::instance().timeout()) |
2942 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
2943 | QCOMPARE(sslErrorSpy.count(), 1); |
2944 | QCOMPARE(errorSpy.count(), 0); |
2945 | QCOMPARE(encryptedSpy.count(), 0); |
2946 | QVERIFY(!socket.isEncrypted()); |
2947 | if (ignoreErrorsAfterPause) { |
2948 | if (errorsToIgnore.empty()) |
2949 | socket.ignoreSslErrors(); |
2950 | else |
2951 | socket.ignoreSslErrors(errors: errorsToIgnore); |
2952 | } |
2953 | socket.resume(); |
2954 | QTestEventLoop::instance().enterLoop(secs: 10); |
2955 | QVERIFY(!QTestEventLoop::instance().timeout()); // quit by encrypted() or error() signal |
2956 | if (expectSuccess) { |
2957 | QCOMPARE(encryptedSpy.count(), 1); |
2958 | QVERIFY(socket.isEncrypted()); |
2959 | QCOMPARE(errorSpy.count(), 0); |
2960 | socket.disconnectFromHost(); |
2961 | QVERIFY(socket.waitForDisconnected(10000)); |
2962 | } else { |
2963 | QCOMPARE(encryptedSpy.count(), 0); |
2964 | QVERIFY(!socket.isEncrypted()); |
2965 | QCOMPARE(errorSpy.count(), 1); |
2966 | QCOMPARE(socket.error(), QAbstractSocket::SslHandshakeFailedError); |
2967 | } |
2968 | } |
2969 | |
2970 | class WebSocket : public QSslSocket |
2971 | { |
2972 | Q_OBJECT |
2973 | public: |
2974 | explicit WebSocket(qintptr socketDescriptor, |
2975 | const QString &keyFile = tst_QSslSocket::testDataDir + "certs/fluke.key" , |
2976 | const QString &certFile = tst_QSslSocket::testDataDir + "certs/fluke.cert" ); |
2977 | |
2978 | protected slots: |
2979 | void onReadyReadFirstBytes(void); |
2980 | |
2981 | private: |
2982 | void _startServerEncryption(void); |
2983 | |
2984 | QString m_keyFile; |
2985 | QString m_certFile; |
2986 | |
2987 | private: |
2988 | Q_DISABLE_COPY(WebSocket) |
2989 | }; |
2990 | |
2991 | WebSocket::WebSocket (qintptr socketDescriptor, const QString &keyFile, const QString &certFile) |
2992 | : m_keyFile(keyFile), |
2993 | m_certFile(certFile) |
2994 | { |
2995 | QVERIFY(setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState, QIODevice::ReadWrite | QIODevice::Unbuffered)); |
2996 | connect (sender: this, SIGNAL(readyRead()), receiver: this, SLOT(onReadyReadFirstBytes())); |
2997 | } |
2998 | |
2999 | void WebSocket::_startServerEncryption (void) |
3000 | { |
3001 | QFile file(m_keyFile); |
3002 | QVERIFY(file.open(QIODevice::ReadOnly)); |
3003 | QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); |
3004 | QVERIFY(!key.isNull()); |
3005 | setPrivateKey(key); |
3006 | |
3007 | QList<QSslCertificate> localCert = QSslCertificate::fromPath(path: m_certFile); |
3008 | QVERIFY(!localCert.isEmpty()); |
3009 | QVERIFY(!localCert.first().isNull()); |
3010 | setLocalCertificate(localCert.first()); |
3011 | |
3012 | QVERIFY(!peerAddress().isNull()); |
3013 | QVERIFY(peerPort() != 0); |
3014 | QVERIFY(!localAddress().isNull()); |
3015 | QVERIFY(localPort() != 0); |
3016 | |
3017 | setProtocol(QSsl::AnyProtocol); |
3018 | setPeerVerifyMode(QSslSocket::VerifyNone); |
3019 | ignoreSslErrors(); |
3020 | startServerEncryption(); |
3021 | } |
3022 | |
3023 | void WebSocket::onReadyReadFirstBytes (void) |
3024 | { |
3025 | peek(maxlen: 1); |
3026 | disconnect(sender: this,SIGNAL(readyRead()), receiver: this, SLOT(onReadyReadFirstBytes())); |
3027 | _startServerEncryption(); |
3028 | } |
3029 | |
3030 | class SslServer4 : public QTcpServer |
3031 | { |
3032 | Q_OBJECT |
3033 | public: |
3034 | |
3035 | QScopedPointer<WebSocket> socket; |
3036 | |
3037 | protected: |
3038 | void incomingConnection(qintptr socketDescriptor) override |
3039 | { |
3040 | socket.reset(other: new WebSocket(socketDescriptor)); |
3041 | } |
3042 | }; |
3043 | |
3044 | void tst_QSslSocket::qtbug18498_peek() |
3045 | { |
3046 | #ifdef Q_OS_WINRT |
3047 | QSKIP("Server-side encryption is not implemented on WinRT." ); |
3048 | #endif |
3049 | QFETCH_GLOBAL(bool, setProxy); |
3050 | if (setProxy) |
3051 | return; |
3052 | |
3053 | SslServer4 server; |
3054 | QVERIFY(server.listen(QHostAddress::LocalHost)); |
3055 | |
3056 | QSslSocket client; |
3057 | client.connectToHost(hostName: "127.0.0.1" , port: server.serverPort()); |
3058 | QVERIFY(client.waitForConnected(5000)); |
3059 | QVERIFY(server.waitForNewConnection(1000)); |
3060 | client.ignoreSslErrors(); |
3061 | |
3062 | int encryptedCounter = 2; |
3063 | connect(sender: &client, signal: &QSslSocket::encrypted, context: this, slot: [&encryptedCounter](){ |
3064 | if (!--encryptedCounter) |
3065 | exitLoop(); |
3066 | }); |
3067 | WebSocket *serversocket = server.socket.data(); |
3068 | connect(sender: serversocket, signal: &QSslSocket::encrypted, context: this, slot: [&encryptedCounter](){ |
3069 | if (!--encryptedCounter) |
3070 | exitLoop(); |
3071 | }); |
3072 | connect(sender: &client, SIGNAL(disconnected()), receiver: this, SLOT(exitLoop())); |
3073 | |
3074 | client.startClientEncryption(); |
3075 | QVERIFY(serversocket); |
3076 | |
3077 | enterLoop(secs: 1); |
3078 | QVERIFY(!timeout()); |
3079 | QVERIFY(serversocket->isEncrypted()); |
3080 | QVERIFY(client.isEncrypted()); |
3081 | |
3082 | QByteArray data("abc123" ); |
3083 | client.write(data: data.data()); |
3084 | |
3085 | connect(sender: serversocket, SIGNAL(readyRead()), receiver: this, SLOT(exitLoop())); |
3086 | enterLoop(secs: 1); |
3087 | QVERIFY(!timeout()); |
3088 | |
3089 | QByteArray peek1_data; |
3090 | peek1_data.reserve(asize: data.size()); |
3091 | QByteArray peek2_data; |
3092 | QByteArray read_data; |
3093 | |
3094 | int lngth = serversocket->peek(data: peek1_data.data(), maxlen: 10); |
3095 | peek1_data.resize(size: lngth); |
3096 | |
3097 | peek2_data = serversocket->peek(maxlen: 10); |
3098 | read_data = serversocket->readAll(); |
3099 | |
3100 | QCOMPARE(peek1_data, data); |
3101 | QCOMPARE(peek2_data, data); |
3102 | QCOMPARE(read_data, data); |
3103 | } |
3104 | |
3105 | class SslServer5 : public QTcpServer |
3106 | { |
3107 | Q_OBJECT |
3108 | public: |
3109 | SslServer5() : socket(0) {} |
3110 | QSslSocket *socket; |
3111 | |
3112 | protected: |
3113 | void incomingConnection(qintptr socketDescriptor) |
3114 | { |
3115 | socket = new QSslSocket; |
3116 | socket->setSocketDescriptor(socketDescriptor); |
3117 | } |
3118 | }; |
3119 | |
3120 | void tst_QSslSocket::qtbug18498_peek2() |
3121 | { |
3122 | #ifdef Q_OS_WINRT |
3123 | QSKIP("Server-side encryption is not implemented on WinRT." ); |
3124 | #endif |
3125 | QFETCH_GLOBAL(bool, setProxy); |
3126 | if (setProxy) |
3127 | return; |
3128 | |
3129 | SslServer5 listener; |
3130 | QVERIFY(listener.listen(QHostAddress::Any)); |
3131 | QScopedPointer<QSslSocket> client(new QSslSocket); |
3132 | client->connectToHost(address: QHostAddress::LocalHost, port: listener.serverPort()); |
3133 | QVERIFY(client->waitForConnected(5000)); |
3134 | QVERIFY(listener.waitForNewConnection(1000)); |
3135 | |
3136 | QScopedPointer<QSslSocket> server(listener.socket); |
3137 | |
3138 | QVERIFY(server->write("HELLO\r\n" , 7)); |
3139 | QTRY_COMPARE(client->bytesAvailable(), 7); |
3140 | char c; |
3141 | QCOMPARE(client->peek(&c,1), 1); |
3142 | QCOMPARE(c, 'H'); |
3143 | QCOMPARE(client->read(&c,1), 1); |
3144 | QCOMPARE(c, 'H'); |
3145 | QByteArray b = client->peek(maxlen: 2); |
3146 | QCOMPARE(b, QByteArray("EL" )); |
3147 | char a[3]; |
3148 | QVERIFY(client->peek(a, 2) == 2); |
3149 | QCOMPARE(a[0], 'E'); |
3150 | QCOMPARE(a[1], 'L'); |
3151 | QCOMPARE(client->readAll(), QByteArray("ELLO\r\n" )); |
3152 | |
3153 | //check data split between QIODevice and plain socket buffers. |
3154 | QByteArray bigblock; |
3155 | bigblock.fill(c: '#', QIODEVICE_BUFFERSIZE + 1024); |
3156 | QVERIFY(client->write(QByteArray("head" ))); |
3157 | QVERIFY(client->write(bigblock)); |
3158 | QTRY_COMPARE(server->bytesAvailable(), bigblock.length() + 4); |
3159 | QCOMPARE(server->read(4), QByteArray("head" )); |
3160 | QCOMPARE(server->peek(bigblock.length()), bigblock); |
3161 | b.reserve(asize: bigblock.length()); |
3162 | b.resize(size: server->peek(data: b.data(), maxlen: bigblock.length())); |
3163 | QCOMPARE(b, bigblock); |
3164 | |
3165 | //check oversized peek |
3166 | QCOMPARE(server->peek(bigblock.length() * 3), bigblock); |
3167 | b.reserve(asize: bigblock.length() * 3); |
3168 | b.resize(size: server->peek(data: b.data(), maxlen: bigblock.length() * 3)); |
3169 | QCOMPARE(b, bigblock); |
3170 | |
3171 | QCOMPARE(server->readAll(), bigblock); |
3172 | |
3173 | QVERIFY(client->write("STARTTLS\r\n" )); |
3174 | QTRY_COMPARE(server->bytesAvailable(), 10); |
3175 | QCOMPARE(server->peek(&c,1), 1); |
3176 | QCOMPARE(c, 'S'); |
3177 | b = server->peek(maxlen: 3); |
3178 | QCOMPARE(b, QByteArray("STA" )); |
3179 | QCOMPARE(server->read(5), QByteArray("START" )); |
3180 | QVERIFY(server->peek(a, 3) == 3); |
3181 | QCOMPARE(a[0], 'T'); |
3182 | QCOMPARE(a[1], 'L'); |
3183 | QCOMPARE(a[2], 'S'); |
3184 | QCOMPARE(server->readAll(), QByteArray("TLS\r\n" )); |
3185 | |
3186 | QFile file(testDataDir + "certs/fluke.key" ); |
3187 | QVERIFY(file.open(QIODevice::ReadOnly)); |
3188 | QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); |
3189 | QVERIFY(!key.isNull()); |
3190 | server->setPrivateKey(key); |
3191 | |
3192 | QList<QSslCertificate> localCert = QSslCertificate::fromPath(path: testDataDir + "certs/fluke.cert" ); |
3193 | QVERIFY(!localCert.isEmpty()); |
3194 | QVERIFY(!localCert.first().isNull()); |
3195 | server->setLocalCertificate(localCert.first()); |
3196 | |
3197 | server->setProtocol(QSsl::AnyProtocol); |
3198 | server->setPeerVerifyMode(QSslSocket::VerifyNone); |
3199 | |
3200 | server->ignoreSslErrors(); |
3201 | client->ignoreSslErrors(); |
3202 | |
3203 | server->startServerEncryption(); |
3204 | client->startClientEncryption(); |
3205 | |
3206 | QVERIFY(server->write("hello\r\n" , 7)); |
3207 | QTRY_COMPARE(client->bytesAvailable(), 7); |
3208 | QVERIFY(server->mode() == QSslSocket::SslServerMode && client->mode() == QSslSocket::SslClientMode); |
3209 | QCOMPARE(client->peek(&c,1), 1); |
3210 | QCOMPARE(c, 'h'); |
3211 | QCOMPARE(client->read(&c,1), 1); |
3212 | QCOMPARE(c, 'h'); |
3213 | b = client->peek(maxlen: 2); |
3214 | QCOMPARE(b, QByteArray("el" )); |
3215 | QCOMPARE(client->readAll(), QByteArray("ello\r\n" )); |
3216 | |
3217 | QVERIFY(client->write("goodbye\r\n" )); |
3218 | QTRY_COMPARE(server->bytesAvailable(), 9); |
3219 | QCOMPARE(server->peek(&c,1), 1); |
3220 | QCOMPARE(c, 'g'); |
3221 | QCOMPARE(server->readAll(), QByteArray("goodbye\r\n" )); |
3222 | client->disconnectFromHost(); |
3223 | QVERIFY(client->waitForDisconnected(5000)); |
3224 | } |
3225 | |
3226 | void tst_QSslSocket::dhServer() |
3227 | { |
3228 | #ifdef Q_OS_WINRT |
3229 | QSKIP("Server-side encryption is not implemented on WinRT." ); |
3230 | #endif |
3231 | if (!QSslSocket::supportsSsl()) |
3232 | QSKIP("No SSL support" ); |
3233 | |
3234 | QFETCH_GLOBAL(bool, setProxy); |
3235 | if (setProxy) |
3236 | return; |
3237 | |
3238 | SslServer server; |
3239 | server.ciphers = {QSslCipher("DHE-RSA-AES256-SHA" ), QSslCipher("DHE-DSS-AES256-SHA" )}; |
3240 | QVERIFY(server.listen()); |
3241 | |
3242 | QEventLoop loop; |
3243 | QTimer::singleShot(msec: 5000, receiver: &loop, SLOT(quit())); |
3244 | |
3245 | QSslSocket client; |
3246 | socket = &client; |
3247 | connect(sender: socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), receiver: &loop, SLOT(quit())); |
3248 | connect(sender: socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
3249 | connect(sender: socket, SIGNAL(encrypted()), receiver: &loop, SLOT(quit())); |
3250 | |
3251 | client.connectToHostEncrypted(hostName: QHostAddress(QHostAddress::LocalHost).toString(), port: server.serverPort()); |
3252 | |
3253 | loop.exec(); |
3254 | QCOMPARE(client.state(), QAbstractSocket::ConnectedState); |
3255 | } |
3256 | |
3257 | #ifndef QT_NO_OPENSSL |
3258 | void tst_QSslSocket::dhServerCustomParamsNull() |
3259 | { |
3260 | if (!QSslSocket::supportsSsl()) |
3261 | QSKIP("No SSL support" ); |
3262 | |
3263 | QFETCH_GLOBAL(bool, setProxy); |
3264 | if (setProxy) |
3265 | return; |
3266 | |
3267 | SslServer server; |
3268 | server.ciphers = {QSslCipher("DHE-RSA-AES256-SHA" ), QSslCipher("DHE-DSS-AES256-SHA" )}; |
3269 | |
3270 | QSslConfiguration cfg = server.config; |
3271 | cfg.setDiffieHellmanParameters(QSslDiffieHellmanParameters()); |
3272 | server.config = cfg; |
3273 | |
3274 | QVERIFY(server.listen()); |
3275 | |
3276 | QEventLoop loop; |
3277 | QTimer::singleShot(msec: 5000, receiver: &loop, SLOT(quit())); |
3278 | |
3279 | QSslSocket client; |
3280 | socket = &client; |
3281 | connect(sender: socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), receiver: &loop, SLOT(quit())); |
3282 | connect(sender: socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
3283 | connect(sender: socket, SIGNAL(encrypted()), receiver: &loop, SLOT(quit())); |
3284 | |
3285 | client.connectToHostEncrypted(hostName: QHostAddress(QHostAddress::LocalHost).toString(), port: server.serverPort()); |
3286 | |
3287 | loop.exec(); |
3288 | |
3289 | QVERIFY(client.state() != QAbstractSocket::ConnectedState); |
3290 | } |
3291 | #endif // QT_NO_OPENSSL |
3292 | |
3293 | #ifndef QT_NO_OPENSSL |
3294 | void tst_QSslSocket::dhServerCustomParams() |
3295 | { |
3296 | if (!QSslSocket::supportsSsl()) |
3297 | QSKIP("No SSL support" ); |
3298 | |
3299 | QFETCH_GLOBAL(bool, setProxy); |
3300 | if (setProxy) |
3301 | return; |
3302 | |
3303 | SslServer server; |
3304 | server.ciphers = {QSslCipher("DHE-RSA-AES256-SHA" ), QSslCipher("DHE-DSS-AES256-SHA" )}; |
3305 | |
3306 | QSslConfiguration cfg = server.config; |
3307 | |
3308 | // Custom 2048-bit DH parameters generated with 'openssl dhparam -outform DER -out out.der -check -2 2048' |
3309 | const auto dh = QSslDiffieHellmanParameters::fromEncoded(encoded: QByteArray::fromBase64(QByteArrayLiteral( |
3310 | "MIIBCAKCAQEAvVA7b8keTfjFutCtTJmP/pnQfw/prKa+GMed/pBWjrC4N1YwnI8h/A861d9WE/VWY7XMTjvjX3/0" |
3311 | "aaU8wEe0EXNpFdlTH+ZMQctQTSJOyQH0RCTwJfDGPCPT9L+c9GKwEKWORH38Earip986HJc0w3UbnfIwXUdsWHiXi" |
3312 | "Z6r3cpyBmTKlsXTFiDVAOUXSiO8d/zOb6zHZbDfyB/VbtZRmnA7TXVn9oMzC0g9+FXHdrV4K+XfdvNZdCegvoAZiy" |
3313 | "R6ZQgNG9aZ36/AQekhg060hp55f9HDPgXqYeNeXBiferjUtU7S9b3s83XhOJAr01/0Tf5dENwCfg2gK36TM8cC4wI" |
3314 | "BAg==" )), format: QSsl::Der); |
3315 | cfg.setDiffieHellmanParameters(dh); |
3316 | |
3317 | server.config = cfg; |
3318 | |
3319 | QVERIFY(server.listen()); |
3320 | |
3321 | QEventLoop loop; |
3322 | QTimer::singleShot(msec: 5000, receiver: &loop, SLOT(quit())); |
3323 | |
3324 | QSslSocket client; |
3325 | socket = &client; |
3326 | connect(sender: socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), receiver: &loop, SLOT(quit())); |
3327 | connect(sender: socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
3328 | connect(sender: socket, SIGNAL(encrypted()), receiver: &loop, SLOT(quit())); |
3329 | |
3330 | client.connectToHostEncrypted(hostName: QHostAddress(QHostAddress::LocalHost).toString(), port: server.serverPort()); |
3331 | |
3332 | loop.exec(); |
3333 | |
3334 | QVERIFY(client.state() == QAbstractSocket::ConnectedState); |
3335 | } |
3336 | #endif // QT_NO_OPENSSL |
3337 | |
3338 | void tst_QSslSocket::ecdhServer() |
3339 | { |
3340 | #ifdef Q_OS_WINRT |
3341 | QSKIP("Server-side encryption is not implemented on WinRT." ); |
3342 | #endif |
3343 | if (!QSslSocket::supportsSsl()) { |
3344 | qWarning(msg: "SSL not supported, skipping test" ); |
3345 | return; |
3346 | } |
3347 | |
3348 | QFETCH_GLOBAL(bool, setProxy); |
3349 | if (setProxy) |
3350 | return; |
3351 | |
3352 | SslServer server; |
3353 | server.ciphers = {QSslCipher("ECDHE-RSA-AES128-SHA" )}; |
3354 | QVERIFY(server.listen()); |
3355 | |
3356 | QEventLoop loop; |
3357 | QTimer::singleShot(msec: 5000, receiver: &loop, SLOT(quit())); |
3358 | |
3359 | QSslSocket client; |
3360 | socket = &client; |
3361 | connect(sender: socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), receiver: &loop, SLOT(quit())); |
3362 | connect(sender: socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
3363 | connect(sender: socket, SIGNAL(encrypted()), receiver: &loop, SLOT(quit())); |
3364 | |
3365 | client.connectToHostEncrypted(hostName: QHostAddress(QHostAddress::LocalHost).toString(), port: server.serverPort()); |
3366 | |
3367 | loop.exec(); |
3368 | QCOMPARE(client.state(), QAbstractSocket::ConnectedState); |
3369 | } |
3370 | |
3371 | void tst_QSslSocket::verifyClientCertificate_data() |
3372 | { |
3373 | QTest::addColumn<QSslSocket::PeerVerifyMode>(name: "peerVerifyMode" ); |
3374 | QTest::addColumn<QList<QSslCertificate> >(name: "clientCerts" ); |
3375 | QTest::addColumn<QSslKey>(name: "clientKey" ); |
3376 | QTest::addColumn<bool>(name: "works" ); |
3377 | |
3378 | // no certificate |
3379 | QList<QSslCertificate> noCerts; |
3380 | QSslKey noKey; |
3381 | |
3382 | QTest::newRow(dataTag: "NoCert:AutoVerifyPeer" ) << QSslSocket::AutoVerifyPeer << noCerts << noKey << true; |
3383 | QTest::newRow(dataTag: "NoCert:QueryPeer" ) << QSslSocket::QueryPeer << noCerts << noKey << true; |
3384 | QTest::newRow(dataTag: "NoCert:VerifyNone" ) << QSslSocket::VerifyNone << noCerts << noKey << true; |
3385 | QTest::newRow(dataTag: "NoCert:VerifyPeer" ) << QSslSocket::VerifyPeer << noCerts << noKey << false; |
3386 | |
3387 | // self-signed certificate |
3388 | QList<QSslCertificate> flukeCerts = QSslCertificate::fromPath(path: testDataDir + "certs/fluke.cert" ); |
3389 | QCOMPARE(flukeCerts.size(), 1); |
3390 | |
3391 | QFile flukeFile(testDataDir + "certs/fluke.key" ); |
3392 | QVERIFY(flukeFile.open(QIODevice::ReadOnly)); |
3393 | QSslKey flukeKey(flukeFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); |
3394 | QVERIFY(!flukeKey.isNull()); |
3395 | |
3396 | QTest::newRow(dataTag: "SelfSignedCert:AutoVerifyPeer" ) << QSslSocket::AutoVerifyPeer << flukeCerts << flukeKey << true; |
3397 | QTest::newRow(dataTag: "SelfSignedCert:QueryPeer" ) << QSslSocket::QueryPeer << flukeCerts << flukeKey << true; |
3398 | QTest::newRow(dataTag: "SelfSignedCert:VerifyNone" ) << QSslSocket::VerifyNone << flukeCerts << flukeKey << true; |
3399 | QTest::newRow(dataTag: "SelfSignedCert:VerifyPeer" ) << QSslSocket::VerifyPeer << flukeCerts << flukeKey << false; |
3400 | |
3401 | // valid certificate, but wrong usage (server certificate) |
3402 | QList<QSslCertificate> serverCerts = QSslCertificate::fromPath(path: testDataDir + "certs/bogus-server.crt" ); |
3403 | QCOMPARE(serverCerts.size(), 1); |
3404 | |
3405 | QFile serverFile(testDataDir + "certs/bogus-server.key" ); |
3406 | QVERIFY(serverFile.open(QIODevice::ReadOnly)); |
3407 | QSslKey serverKey(serverFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); |
3408 | QVERIFY(!serverKey.isNull()); |
3409 | |
3410 | QTest::newRow(dataTag: "ValidServerCert:AutoVerifyPeer" ) << QSslSocket::AutoVerifyPeer << serverCerts << serverKey << true; |
3411 | QTest::newRow(dataTag: "ValidServerCert:QueryPeer" ) << QSslSocket::QueryPeer << serverCerts << serverKey << true; |
3412 | QTest::newRow(dataTag: "ValidServerCert:VerifyNone" ) << QSslSocket::VerifyNone << serverCerts << serverKey << true; |
3413 | QTest::newRow(dataTag: "ValidServerCert:VerifyPeer" ) << QSslSocket::VerifyPeer << serverCerts << serverKey << false; |
3414 | |
3415 | // valid certificate, correct usage (client certificate) |
3416 | QList<QSslCertificate> validCerts = QSslCertificate::fromPath(path: testDataDir + "certs/bogus-client.crt" ); |
3417 | QCOMPARE(validCerts.size(), 1); |
3418 | |
3419 | QFile validFile(testDataDir + "certs/bogus-client.key" ); |
3420 | QVERIFY(validFile.open(QIODevice::ReadOnly)); |
3421 | QSslKey validKey(validFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); |
3422 | QVERIFY(!validKey.isNull()); |
3423 | |
3424 | QTest::newRow(dataTag: "ValidClientCert:AutoVerifyPeer" ) << QSslSocket::AutoVerifyPeer << validCerts << validKey << true; |
3425 | QTest::newRow(dataTag: "ValidClientCert:QueryPeer" ) << QSslSocket::QueryPeer << validCerts << validKey << true; |
3426 | QTest::newRow(dataTag: "ValidClientCert:VerifyNone" ) << QSslSocket::VerifyNone << validCerts << validKey << true; |
3427 | QTest::newRow(dataTag: "ValidClientCert:VerifyPeer" ) << QSslSocket::VerifyPeer << validCerts << validKey << true; |
3428 | |
3429 | // valid certificate, correct usage (client certificate), with chain |
3430 | validCerts += QSslCertificate::fromPath(path: testDataDir + "certs/bogus-ca.crt" ); |
3431 | QCOMPARE(validCerts.size(), 2); |
3432 | |
3433 | QTest::newRow(dataTag: "ValidChainedClientCert:AutoVerifyPeer" ) << QSslSocket::AutoVerifyPeer << validCerts << validKey << true; |
3434 | QTest::newRow(dataTag: "ValidChainedClientCert:QueryPeer" ) << QSslSocket::QueryPeer << validCerts << validKey << true; |
3435 | QTest::newRow(dataTag: "ValidChainedClientCert:VerifyNone" ) << QSslSocket::VerifyNone << validCerts << validKey << true; |
3436 | QTest::newRow(dataTag: "ValidChainedClientCert:VerifyPeer" ) << QSslSocket::VerifyPeer << validCerts << validKey << true; |
3437 | } |
3438 | |
3439 | void tst_QSslSocket::verifyClientCertificate() |
3440 | { |
3441 | #if QT_CONFIG(securetransport) |
3442 | // We run both client and server on the same machine, |
3443 | // this means, client can update keychain with client's certificates, |
3444 | // and server later will use the same certificates from the same |
3445 | // keychain thus making tests fail (wrong number of certificates, |
3446 | // success instead of failure etc.). |
3447 | QSKIP("This test can not work with Secure Transport" ); |
3448 | #endif // QT_CONFIG(securetransport) |
3449 | #ifdef Q_OS_WINRT |
3450 | QSKIP("Server-side encryption is not implemented on WinRT." ); |
3451 | #endif |
3452 | if (!QSslSocket::supportsSsl()) { |
3453 | qWarning(msg: "SSL not supported, skipping test" ); |
3454 | return; |
3455 | } |
3456 | |
3457 | QFETCH_GLOBAL(bool, setProxy); |
3458 | if (setProxy) |
3459 | return; |
3460 | |
3461 | QFETCH(QSslSocket::PeerVerifyMode, peerVerifyMode); |
3462 | #if QT_CONFIG(schannel) |
3463 | if (peerVerifyMode == QSslSocket::QueryPeer || peerVerifyMode == QSslSocket::AutoVerifyPeer) |
3464 | QSKIP("Schannel doesn't tackle requesting a certificate and not receiving one." ); |
3465 | #endif |
3466 | |
3467 | SslServer server; |
3468 | server.addCaCertificates = testDataDir + "certs/bogus-ca.crt" ; |
3469 | server.ignoreSslErrors = false; |
3470 | server.peerVerifyMode = peerVerifyMode; |
3471 | QVERIFY(server.listen()); |
3472 | |
3473 | QEventLoop loop; |
3474 | QTimer::singleShot(msec: 5000, receiver: &loop, SLOT(quit())); |
3475 | |
3476 | QFETCH(QList<QSslCertificate>, clientCerts); |
3477 | QFETCH(QSslKey, clientKey); |
3478 | QSslSocket client; |
3479 | client.setLocalCertificateChain(clientCerts); |
3480 | client.setPrivateKey(clientKey); |
3481 | socket = &client; |
3482 | |
3483 | connect(sender: socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
3484 | connect(sender: socket, SIGNAL(disconnected()), receiver: &loop, SLOT(quit())); |
3485 | connect(sender: socket, SIGNAL(encrypted()), receiver: &loop, SLOT(quit())); |
3486 | |
3487 | client.connectToHostEncrypted(hostName: QHostAddress(QHostAddress::LocalHost).toString(), port: server.serverPort()); |
3488 | |
3489 | loop.exec(); |
3490 | |
3491 | QFETCH(bool, works); |
3492 | QAbstractSocket::SocketState expectedState = (works) ? QAbstractSocket::ConnectedState : QAbstractSocket::UnconnectedState; |
3493 | |
3494 | // check server socket |
3495 | QVERIFY(server.socket); |
3496 | |
3497 | QCOMPARE(server.socket->state(), expectedState); |
3498 | QCOMPARE(server.socket->isEncrypted(), works); |
3499 | |
3500 | if (peerVerifyMode == QSslSocket::VerifyNone || clientCerts.isEmpty()) { |
3501 | QVERIFY(server.socket->peerCertificate().isNull()); |
3502 | QVERIFY(server.socket->peerCertificateChain().isEmpty()); |
3503 | } else { |
3504 | QCOMPARE(server.socket->peerCertificate(), clientCerts.first()); |
3505 | #if QT_CONFIG(schannel) |
3506 | if (clientCerts.count() == 1 && server.socket->peerCertificateChain().count() == 2) { |
3507 | QEXPECT_FAIL("" , |
3508 | "Schannel includes the entire chain, not just the leaf and intermediates" , |
3509 | Continue); |
3510 | } |
3511 | #endif |
3512 | QCOMPARE(server.socket->peerCertificateChain(), clientCerts); |
3513 | } |
3514 | |
3515 | // check client socket |
3516 | QCOMPARE(client.state(), expectedState); |
3517 | QCOMPARE(client.isEncrypted(), works); |
3518 | } |
3519 | |
3520 | void tst_QSslSocket::readBufferMaxSize() |
3521 | { |
3522 | #if QT_CONFIG(securetransport) || QT_CONFIG(schannel) |
3523 | // QTBUG-55170: |
3524 | // SecureTransport back-end was ignoring read-buffer |
3525 | // size limit, resulting (potentially) in a constantly |
3526 | // growing internal buffer. |
3527 | // The test's logic is: we set a small read buffer size on a client |
3528 | // socket (to some ridiculously small value), server sends us |
3529 | // a bunch of bytes , we ignore readReady signal so |
3530 | // that socket's internal buffer size stays |
3531 | // >= readBufferMaxSize, we wait for a quite long time |
3532 | // (which previously would be enough to read completely) |
3533 | // and we check socket's bytesAvaiable to be less than sent. |
3534 | QFETCH_GLOBAL(bool, setProxy); |
3535 | if (setProxy) |
3536 | return; |
3537 | |
3538 | SslServer server; |
3539 | QVERIFY(server.listen()); |
3540 | |
3541 | QEventLoop loop; |
3542 | |
3543 | QSslSocketPtr client(new QSslSocket); |
3544 | socket = client.data(); |
3545 | connect(socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), &loop, SLOT(quit())); |
3546 | connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot())); |
3547 | connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit())); |
3548 | |
3549 | client->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), |
3550 | server.serverPort()); |
3551 | |
3552 | // Wait for 'encrypted' first: |
3553 | QTimer::singleShot(5000, &loop, SLOT(quit())); |
3554 | loop.exec(); |
3555 | |
3556 | QCOMPARE(client->state(), QAbstractSocket::ConnectedState); |
3557 | QCOMPARE(client->mode(), QSslSocket::SslClientMode); |
3558 | |
3559 | client->setReadBufferSize(10); |
3560 | const QByteArray message(int(0xffff), 'a'); |
3561 | server.socket->write(message); |
3562 | |
3563 | QTimer::singleShot(5000, &loop, SLOT(quit())); |
3564 | loop.exec(); |
3565 | |
3566 | int readSoFar = client->bytesAvailable(); |
3567 | QVERIFY(readSoFar > 0 && readSoFar < message.size()); |
3568 | // Now, let's check that we still can read the rest of it: |
3569 | QCOMPARE(client->readAll().size(), readSoFar); |
3570 | |
3571 | client->setReadBufferSize(0); |
3572 | |
3573 | QTimer::singleShot(1500, &loop, SLOT(quit())); |
3574 | loop.exec(); |
3575 | |
3576 | QCOMPARE(client->bytesAvailable() + readSoFar, message.size()); |
3577 | #else |
3578 | // Not needed, QSslSocket works correctly with other back-ends. |
3579 | #endif // QT_CONFIG(securetransport) || QT_CONFIG(schannel) |
3580 | } |
3581 | |
3582 | void tst_QSslSocket::setEmptyDefaultConfiguration() // this test should be last, as it has some side effects |
3583 | { |
3584 | // used to produce a crash in QSslConfigurationPrivate::deepCopyDefaultConfiguration, QTBUG-13265 |
3585 | |
3586 | if (!QSslSocket::supportsSsl()) |
3587 | return; |
3588 | |
3589 | QSslConfiguration emptyConf; |
3590 | QSslConfiguration::setDefaultConfiguration(emptyConf); |
3591 | |
3592 | QSslSocketPtr client = newSocket(); |
3593 | socket = client.data(); |
3594 | |
3595 | connect(sender: socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
3596 | socket->connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
3597 | QFETCH_GLOBAL(bool, setProxy); |
3598 | if (setProxy && socket->waitForEncrypted(msecs: 4000)) |
3599 | QSKIP("Skipping flaky test - See QTBUG-29941" ); |
3600 | } |
3601 | |
3602 | void tst_QSslSocket::allowedProtocolNegotiation() |
3603 | { |
3604 | #ifndef ALPN_SUPPORTED |
3605 | QSKIP("ALPN is unsupported, skipping test" ); |
3606 | #endif |
3607 | |
3608 | #if QT_CONFIG(schannel) |
3609 | if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8_1) |
3610 | QSKIP("ALPN is not supported on this version of Windows using Schannel." ); |
3611 | #endif |
3612 | |
3613 | QFETCH_GLOBAL(bool, setProxy); |
3614 | if (setProxy) |
3615 | return; |
3616 | |
3617 | |
3618 | const QByteArray expectedNegotiated("cool-protocol" ); |
3619 | QList<QByteArray> serverProtos; |
3620 | serverProtos << expectedNegotiated << "not-so-cool-protocol" ; |
3621 | QList<QByteArray> clientProtos; |
3622 | clientProtos << "uber-cool-protocol" << expectedNegotiated << "not-so-cool-protocol" ; |
3623 | |
3624 | |
3625 | SslServer server; |
3626 | server.config.setAllowedNextProtocols(serverProtos); |
3627 | QVERIFY(server.listen()); |
3628 | |
3629 | QSslSocket clientSocket; |
3630 | auto configuration = clientSocket.sslConfiguration(); |
3631 | configuration.setAllowedNextProtocols(clientProtos); |
3632 | clientSocket.setSslConfiguration(configuration); |
3633 | |
3634 | clientSocket.connectToHostEncrypted(hostName: "127.0.0.1" , port: server.serverPort()); |
3635 | clientSocket.ignoreSslErrors(); |
3636 | |
3637 | QEventLoop loop; |
3638 | QTimer::singleShot(msec: 5000, receiver: &loop, SLOT(quit())); |
3639 | connect(sender: &clientSocket, SIGNAL(encrypted()), receiver: &loop, SLOT(quit())); |
3640 | loop.exec(); |
3641 | |
3642 | QVERIFY(server.socket->sslConfiguration().nextNegotiatedProtocol() == |
3643 | clientSocket.sslConfiguration().nextNegotiatedProtocol()); |
3644 | QVERIFY(server.socket->sslConfiguration().nextNegotiatedProtocol() == expectedNegotiated); |
3645 | } |
3646 | |
3647 | #ifndef QT_NO_OPENSSL |
3648 | class PskProvider : public QObject |
3649 | { |
3650 | Q_OBJECT |
3651 | |
3652 | public: |
3653 | bool m_server; |
3654 | QByteArray m_identity; |
3655 | QByteArray m_psk; |
3656 | |
3657 | explicit PskProvider(QObject *parent = 0) |
3658 | : QObject(parent), m_server(false) |
3659 | { |
3660 | } |
3661 | |
3662 | void setIdentity(const QByteArray &identity) |
3663 | { |
3664 | m_identity = identity; |
3665 | } |
3666 | |
3667 | void setPreSharedKey(const QByteArray &psk) |
3668 | { |
3669 | m_psk = psk; |
3670 | } |
3671 | |
3672 | public slots: |
3673 | void providePsk(QSslPreSharedKeyAuthenticator *authenticator) |
3674 | { |
3675 | QVERIFY(authenticator); |
3676 | QCOMPARE(authenticator->identityHint(), PSK_SERVER_IDENTITY_HINT); |
3677 | if (m_server) |
3678 | QCOMPARE(authenticator->maximumIdentityLength(), 0); |
3679 | else |
3680 | QVERIFY(authenticator->maximumIdentityLength() > 0); |
3681 | |
3682 | QVERIFY(authenticator->maximumPreSharedKeyLength() > 0); |
3683 | |
3684 | if (!m_identity.isEmpty()) { |
3685 | authenticator->setIdentity(m_identity); |
3686 | QCOMPARE(authenticator->identity(), m_identity); |
3687 | } |
3688 | |
3689 | if (!m_psk.isEmpty()) { |
3690 | authenticator->setPreSharedKey(m_psk); |
3691 | QCOMPARE(authenticator->preSharedKey(), m_psk); |
3692 | } |
3693 | } |
3694 | }; |
3695 | |
3696 | class PskServer : public QTcpServer |
3697 | { |
3698 | Q_OBJECT |
3699 | public: |
3700 | PskServer() |
3701 | : socket(0), |
3702 | config(QSslConfiguration::defaultConfiguration()), |
3703 | ignoreSslErrors(true), |
3704 | peerVerifyMode(QSslSocket::AutoVerifyPeer), |
3705 | protocol(QSsl::TlsV1_0), |
3706 | m_pskProvider() |
3707 | { |
3708 | m_pskProvider.m_server = true; |
3709 | } |
3710 | QSslSocket *socket; |
3711 | QSslConfiguration config; |
3712 | bool ignoreSslErrors; |
3713 | QSslSocket::PeerVerifyMode peerVerifyMode; |
3714 | QSsl::SslProtocol protocol; |
3715 | QList<QSslCipher> ciphers; |
3716 | PskProvider m_pskProvider; |
3717 | |
3718 | protected: |
3719 | void incomingConnection(qintptr socketDescriptor) |
3720 | { |
3721 | socket = new QSslSocket(this); |
3722 | socket->setSslConfiguration(config); |
3723 | socket->setPeerVerifyMode(peerVerifyMode); |
3724 | socket->setProtocol(protocol); |
3725 | if (ignoreSslErrors) |
3726 | connect(sender: socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
3727 | |
3728 | if (!ciphers.isEmpty()) { |
3729 | auto sslConfig = socket->sslConfiguration(); |
3730 | sslConfig.setCiphers(ciphers); |
3731 | socket->setSslConfiguration(sslConfig); |
3732 | } |
3733 | |
3734 | QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState)); |
3735 | QVERIFY(!socket->peerAddress().isNull()); |
3736 | QVERIFY(socket->peerPort() != 0); |
3737 | QVERIFY(!socket->localAddress().isNull()); |
3738 | QVERIFY(socket->localPort() != 0); |
3739 | |
3740 | connect(sender: socket, signal: &QSslSocket::preSharedKeyAuthenticationRequired, receiver: &m_pskProvider, slot: &PskProvider::providePsk); |
3741 | |
3742 | socket->startServerEncryption(); |
3743 | } |
3744 | |
3745 | protected slots: |
3746 | void ignoreErrorSlot() |
3747 | { |
3748 | socket->ignoreSslErrors(); |
3749 | } |
3750 | }; |
3751 | void tst_QSslSocket::simplePskConnect_data() |
3752 | { |
3753 | QTest::addColumn<PskConnectTestType>(name: "pskTestType" ); |
3754 | QTest::newRow(dataTag: "PskConnectDoNotHandlePsk" ) << PskConnectDoNotHandlePsk; |
3755 | QTest::newRow(dataTag: "PskConnectEmptyCredentials" ) << PskConnectEmptyCredentials; |
3756 | QTest::newRow(dataTag: "PskConnectWrongCredentials" ) << PskConnectWrongCredentials; |
3757 | QTest::newRow(dataTag: "PskConnectWrongIdentity" ) << PskConnectWrongIdentity; |
3758 | QTest::newRow(dataTag: "PskConnectWrongPreSharedKey" ) << PskConnectWrongPreSharedKey; |
3759 | QTest::newRow(dataTag: "PskConnectRightCredentialsPeerVerifyFailure" ) << PskConnectRightCredentialsPeerVerifyFailure; |
3760 | QTest::newRow(dataTag: "PskConnectRightCredentialsVerifyPeer" ) << PskConnectRightCredentialsVerifyPeer; |
3761 | QTest::newRow(dataTag: "PskConnectRightCredentialsDoNotVerifyPeer" ) << PskConnectRightCredentialsDoNotVerifyPeer; |
3762 | } |
3763 | |
3764 | void tst_QSslSocket::simplePskConnect() |
3765 | { |
3766 | QFETCH(PskConnectTestType, pskTestType); |
3767 | QSKIP("This test requires change 1f8cab2c3bcd91335684c95afa95ae71e00a94e4 on the network test server, QTQAINFRA-917" ); |
3768 | |
3769 | if (!QSslSocket::supportsSsl()) |
3770 | QSKIP("No SSL support" ); |
3771 | |
3772 | bool pskCipherFound = false; |
3773 | const QList<QSslCipher> supportedCiphers = QSslConfiguration::supportedCiphers(); |
3774 | for (const QSslCipher &cipher : supportedCiphers) { |
3775 | if (cipher.name() == PSK_CIPHER_WITHOUT_AUTH) { |
3776 | pskCipherFound = true; |
3777 | break; |
3778 | } |
3779 | } |
3780 | |
3781 | if (!pskCipherFound) |
3782 | QSKIP("SSL implementation does not support the necessary PSK cipher(s)" ); |
3783 | |
3784 | QFETCH_GLOBAL(bool, setProxy); |
3785 | if (setProxy) |
3786 | QSKIP("This test must not be going through a proxy" ); |
3787 | |
3788 | QSslSocket socket; |
3789 | this->socket = &socket; |
3790 | |
3791 | QSignalSpy connectedSpy(&socket, SIGNAL(connected())); |
3792 | QVERIFY(connectedSpy.isValid()); |
3793 | |
3794 | QSignalSpy hostFoundSpy(&socket, SIGNAL(hostFound())); |
3795 | QVERIFY(hostFoundSpy.isValid()); |
3796 | |
3797 | QSignalSpy disconnectedSpy(&socket, SIGNAL(disconnected())); |
3798 | QVERIFY(disconnectedSpy.isValid()); |
3799 | |
3800 | QSignalSpy connectionEncryptedSpy(&socket, SIGNAL(encrypted())); |
3801 | QVERIFY(connectionEncryptedSpy.isValid()); |
3802 | |
3803 | QSignalSpy (&socket, SIGNAL(sslErrors(QList<QSslError>))); |
3804 | QVERIFY(sslErrorsSpy.isValid()); |
3805 | |
3806 | QSignalSpy (&socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError))); |
3807 | QVERIFY(socketErrorsSpy.isValid()); |
3808 | |
3809 | QSignalSpy peerVerifyErrorSpy(&socket, SIGNAL(peerVerifyError(QSslError))); |
3810 | QVERIFY(peerVerifyErrorSpy.isValid()); |
3811 | |
3812 | QSignalSpy pskAuthenticationRequiredSpy(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*))); |
3813 | QVERIFY(pskAuthenticationRequiredSpy.isValid()); |
3814 | |
3815 | connect(sender: &socket, SIGNAL(connected()), receiver: this, SLOT(exitLoop())); |
3816 | connect(sender: &socket, SIGNAL(disconnected()), receiver: this, SLOT(exitLoop())); |
3817 | connect(sender: &socket, SIGNAL(modeChanged(QSslSocket::SslMode)), receiver: this, SLOT(exitLoop())); |
3818 | connect(sender: &socket, SIGNAL(encrypted()), receiver: this, SLOT(exitLoop())); |
3819 | connect(sender: &socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(exitLoop())); |
3820 | connect(sender: &socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), receiver: this, SLOT(exitLoop())); |
3821 | connect(sender: &socket, SIGNAL(peerVerifyError(QSslError)), receiver: this, SLOT(exitLoop())); |
3822 | connect(sender: &socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), receiver: this, SLOT(exitLoop())); |
3823 | |
3824 | // force a PSK cipher w/o auth |
3825 | auto sslConfig = socket.sslConfiguration(); |
3826 | sslConfig.setCiphers({QSslCipher(PSK_CIPHER_WITHOUT_AUTH)}); |
3827 | socket.setSslConfiguration(sslConfig); |
3828 | |
3829 | PskProvider provider; |
3830 | |
3831 | switch (pskTestType) { |
3832 | case PskConnectDoNotHandlePsk: |
3833 | // don't connect to the provider |
3834 | break; |
3835 | |
3836 | case PskConnectEmptyCredentials: |
3837 | // connect to the psk provider, but don't actually provide any PSK nor identity |
3838 | connect(sender: &socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), receiver: &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*))); |
3839 | break; |
3840 | |
3841 | case PskConnectWrongCredentials: |
3842 | // provide totally wrong credentials |
3843 | provider.setIdentity(PSK_CLIENT_IDENTITY.left(len: PSK_CLIENT_IDENTITY.length() - 1)); |
3844 | provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY.left(len: PSK_CLIENT_PRESHAREDKEY.length() - 1)); |
3845 | connect(sender: &socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), receiver: &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*))); |
3846 | break; |
3847 | |
3848 | case PskConnectWrongIdentity: |
3849 | // right PSK, wrong identity |
3850 | provider.setIdentity(PSK_CLIENT_IDENTITY.left(len: PSK_CLIENT_IDENTITY.length() - 1)); |
3851 | provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY); |
3852 | connect(sender: &socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), receiver: &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*))); |
3853 | break; |
3854 | |
3855 | case PskConnectWrongPreSharedKey: |
3856 | // right identity, wrong PSK |
3857 | provider.setIdentity(PSK_CLIENT_IDENTITY); |
3858 | provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY.left(len: PSK_CLIENT_PRESHAREDKEY.length() - 1)); |
3859 | connect(sender: &socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), receiver: &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*))); |
3860 | break; |
3861 | |
3862 | case PskConnectRightCredentialsPeerVerifyFailure: |
3863 | // right identity, right PSK, but since we can't verify the other peer, we'll fail |
3864 | provider.setIdentity(PSK_CLIENT_IDENTITY); |
3865 | provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY); |
3866 | connect(sender: &socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), receiver: &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*))); |
3867 | break; |
3868 | |
3869 | case PskConnectRightCredentialsVerifyPeer: |
3870 | // right identity, right PSK, verify the peer (but ignore the failure) and establish the connection |
3871 | provider.setIdentity(PSK_CLIENT_IDENTITY); |
3872 | provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY); |
3873 | connect(sender: &socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), receiver: &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*))); |
3874 | connect(sender: &socket, SIGNAL(peerVerifyError(QSslError)), receiver: this, SLOT(ignoreErrorSlot())); |
3875 | break; |
3876 | |
3877 | case PskConnectRightCredentialsDoNotVerifyPeer: |
3878 | // right identity, right PSK, do not verify the peer and establish the connection |
3879 | provider.setIdentity(PSK_CLIENT_IDENTITY); |
3880 | provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY); |
3881 | connect(sender: &socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), receiver: &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*))); |
3882 | socket.setPeerVerifyMode(QSslSocket::VerifyNone); |
3883 | break; |
3884 | } |
3885 | |
3886 | // check the peer verification mode |
3887 | switch (pskTestType) { |
3888 | case PskConnectDoNotHandlePsk: |
3889 | case PskConnectEmptyCredentials: |
3890 | case PskConnectWrongCredentials: |
3891 | case PskConnectWrongIdentity: |
3892 | case PskConnectWrongPreSharedKey: |
3893 | case PskConnectRightCredentialsPeerVerifyFailure: |
3894 | case PskConnectRightCredentialsVerifyPeer: |
3895 | QCOMPARE(socket.peerVerifyMode(), QSslSocket::AutoVerifyPeer); |
3896 | break; |
3897 | |
3898 | case PskConnectRightCredentialsDoNotVerifyPeer: |
3899 | QCOMPARE(socket.peerVerifyMode(), QSslSocket::VerifyNone); |
3900 | break; |
3901 | } |
3902 | |
3903 | // Start connecting |
3904 | socket.connectToHost(hostName: QtNetworkSettings::serverName(), port: PSK_SERVER_PORT); |
3905 | QCOMPARE(socket.state(), QAbstractSocket::HostLookupState); |
3906 | enterLoop(secs: 10); |
3907 | |
3908 | // Entered connecting state |
3909 | QCOMPARE(socket.state(), QAbstractSocket::ConnectingState); |
3910 | QCOMPARE(connectedSpy.count(), 0); |
3911 | QCOMPARE(hostFoundSpy.count(), 1); |
3912 | QCOMPARE(disconnectedSpy.count(), 0); |
3913 | enterLoop(secs: 10); |
3914 | |
3915 | // Entered connected state |
3916 | QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); |
3917 | QCOMPARE(socket.mode(), QSslSocket::UnencryptedMode); |
3918 | QVERIFY(!socket.isEncrypted()); |
3919 | QCOMPARE(connectedSpy.count(), 1); |
3920 | QCOMPARE(hostFoundSpy.count(), 1); |
3921 | QCOMPARE(disconnectedSpy.count(), 0); |
3922 | |
3923 | // Enter encrypted mode |
3924 | socket.startClientEncryption(); |
3925 | QCOMPARE(socket.mode(), QSslSocket::SslClientMode); |
3926 | QVERIFY(!socket.isEncrypted()); |
3927 | QCOMPARE(connectionEncryptedSpy.count(), 0); |
3928 | QCOMPARE(sslErrorsSpy.count(), 0); |
3929 | QCOMPARE(peerVerifyErrorSpy.count(), 0); |
3930 | |
3931 | // Start handshake. |
3932 | enterLoop(secs: 10); |
3933 | |
3934 | // We must get the PSK signal in all cases |
3935 | QCOMPARE(pskAuthenticationRequiredSpy.count(), 1); |
3936 | |
3937 | switch (pskTestType) { |
3938 | case PskConnectDoNotHandlePsk: |
3939 | case PskConnectEmptyCredentials: |
3940 | case PskConnectWrongCredentials: |
3941 | case PskConnectWrongIdentity: |
3942 | case PskConnectWrongPreSharedKey: |
3943 | // Handshake failure |
3944 | QCOMPARE(socketErrorsSpy.count(), 1); |
3945 | QCOMPARE(qvariant_cast<QAbstractSocket::SocketError>(socketErrorsSpy.at(0).at(0)), QAbstractSocket::SslHandshakeFailedError); |
3946 | QCOMPARE(sslErrorsSpy.count(), 0); |
3947 | QCOMPARE(peerVerifyErrorSpy.count(), 0); |
3948 | QCOMPARE(connectionEncryptedSpy.count(), 0); |
3949 | QVERIFY(!socket.isEncrypted()); |
3950 | break; |
3951 | |
3952 | case PskConnectRightCredentialsPeerVerifyFailure: |
3953 | // Peer verification failure |
3954 | QCOMPARE(socketErrorsSpy.count(), 1); |
3955 | QCOMPARE(qvariant_cast<QAbstractSocket::SocketError>(socketErrorsSpy.at(0).at(0)), QAbstractSocket::SslHandshakeFailedError); |
3956 | QCOMPARE(sslErrorsSpy.count(), 1); |
3957 | QCOMPARE(peerVerifyErrorSpy.count(), 1); |
3958 | QCOMPARE(connectionEncryptedSpy.count(), 0); |
3959 | QVERIFY(!socket.isEncrypted()); |
3960 | break; |
3961 | |
3962 | case PskConnectRightCredentialsVerifyPeer: |
3963 | // Peer verification failure, but ignore it and keep connecting |
3964 | QCOMPARE(socketErrorsSpy.count(), 0); |
3965 | QCOMPARE(sslErrorsSpy.count(), 1); |
3966 | QCOMPARE(peerVerifyErrorSpy.count(), 1); |
3967 | QCOMPARE(connectionEncryptedSpy.count(), 1); |
3968 | QVERIFY(socket.isEncrypted()); |
3969 | QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); |
3970 | break; |
3971 | |
3972 | case PskConnectRightCredentialsDoNotVerifyPeer: |
3973 | // No peer verification => no failure |
3974 | QCOMPARE(socketErrorsSpy.count(), 0); |
3975 | QCOMPARE(sslErrorsSpy.count(), 0); |
3976 | QCOMPARE(peerVerifyErrorSpy.count(), 0); |
3977 | QCOMPARE(connectionEncryptedSpy.count(), 1); |
3978 | QVERIFY(socket.isEncrypted()); |
3979 | QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); |
3980 | break; |
3981 | } |
3982 | |
3983 | // check writing |
3984 | switch (pskTestType) { |
3985 | case PskConnectDoNotHandlePsk: |
3986 | case PskConnectEmptyCredentials: |
3987 | case PskConnectWrongCredentials: |
3988 | case PskConnectWrongIdentity: |
3989 | case PskConnectWrongPreSharedKey: |
3990 | case PskConnectRightCredentialsPeerVerifyFailure: |
3991 | break; |
3992 | |
3993 | case PskConnectRightCredentialsVerifyPeer: |
3994 | case PskConnectRightCredentialsDoNotVerifyPeer: |
3995 | socket.write(data: "Hello from Qt TLS/PSK!" ); |
3996 | QVERIFY(socket.waitForBytesWritten()); |
3997 | break; |
3998 | } |
3999 | |
4000 | // disconnect |
4001 | switch (pskTestType) { |
4002 | case PskConnectDoNotHandlePsk: |
4003 | case PskConnectEmptyCredentials: |
4004 | case PskConnectWrongCredentials: |
4005 | case PskConnectWrongIdentity: |
4006 | case PskConnectWrongPreSharedKey: |
4007 | case PskConnectRightCredentialsPeerVerifyFailure: |
4008 | break; |
4009 | |
4010 | case PskConnectRightCredentialsVerifyPeer: |
4011 | case PskConnectRightCredentialsDoNotVerifyPeer: |
4012 | socket.disconnectFromHost(); |
4013 | enterLoop(secs: 10); |
4014 | break; |
4015 | } |
4016 | |
4017 | QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState); |
4018 | QCOMPARE(disconnectedSpy.count(), 1); |
4019 | } |
4020 | |
4021 | void tst_QSslSocket::ephemeralServerKey_data() |
4022 | { |
4023 | #ifdef Q_OS_WINRT |
4024 | QSKIP("Server-side encryption is not implemented on WinRT." ); |
4025 | #endif |
4026 | QTest::addColumn<QString>(name: "cipher" ); |
4027 | QTest::addColumn<bool>(name: "emptyKey" ); |
4028 | |
4029 | QTest::newRow(dataTag: "ForwardSecrecyCipher" ) << "ECDHE-RSA-AES256-SHA" << (QSslSocket::sslLibraryVersionNumber() < 0x10002000L); |
4030 | } |
4031 | |
4032 | void tst_QSslSocket::ephemeralServerKey() |
4033 | { |
4034 | QFETCH_GLOBAL(bool, setProxy); |
4035 | if (!QSslSocket::supportsSsl() || setProxy) |
4036 | return; |
4037 | |
4038 | QFETCH(QString, cipher); |
4039 | QFETCH(bool, emptyKey); |
4040 | SslServer server; |
4041 | server.config.setCiphers(QList<QSslCipher>() << QSslCipher(cipher)); |
4042 | QVERIFY(server.listen()); |
4043 | QSslSocketPtr client = newSocket(); |
4044 | socket = client.data(); |
4045 | connect(sender: socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(ignoreErrorSlot())); |
4046 | QSignalSpy spy(client.data(), &QSslSocket::encrypted); |
4047 | |
4048 | client->connectToHostEncrypted(hostName: QHostAddress(QHostAddress::LocalHost).toString(), port: server.serverPort()); |
4049 | spy.wait(); |
4050 | |
4051 | QCOMPARE(spy.count(), 1); |
4052 | QVERIFY(server.config.ephemeralServerKey().isNull()); |
4053 | QCOMPARE(client->sslConfiguration().ephemeralServerKey().isNull(), emptyKey); |
4054 | } |
4055 | |
4056 | void tst_QSslSocket::pskServer() |
4057 | { |
4058 | #ifdef Q_OS_WINRT |
4059 | QSKIP("Server-side encryption is not implemented on WinRT." ); |
4060 | #endif |
4061 | #if QT_CONFIG(schannel) |
4062 | QSKIP("Schannel does not have PSK support implemented." ); |
4063 | #endif |
4064 | QFETCH_GLOBAL(bool, setProxy); |
4065 | if (!QSslSocket::supportsSsl() || setProxy) |
4066 | return; |
4067 | |
4068 | QSslSocket socket; |
4069 | this->socket = &socket; |
4070 | |
4071 | QSignalSpy connectedSpy(&socket, SIGNAL(connected())); |
4072 | QVERIFY(connectedSpy.isValid()); |
4073 | |
4074 | QSignalSpy disconnectedSpy(&socket, SIGNAL(disconnected())); |
4075 | QVERIFY(disconnectedSpy.isValid()); |
4076 | |
4077 | QSignalSpy connectionEncryptedSpy(&socket, SIGNAL(encrypted())); |
4078 | QVERIFY(connectionEncryptedSpy.isValid()); |
4079 | |
4080 | QSignalSpy pskAuthenticationRequiredSpy(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*))); |
4081 | QVERIFY(pskAuthenticationRequiredSpy.isValid()); |
4082 | |
4083 | connect(sender: &socket, SIGNAL(connected()), receiver: this, SLOT(exitLoop())); |
4084 | connect(sender: &socket, SIGNAL(disconnected()), receiver: this, SLOT(exitLoop())); |
4085 | connect(sender: &socket, SIGNAL(modeChanged(QSslSocket::SslMode)), receiver: this, SLOT(exitLoop())); |
4086 | connect(sender: &socket, SIGNAL(encrypted()), receiver: this, SLOT(exitLoop())); |
4087 | connect(sender: &socket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(exitLoop())); |
4088 | connect(sender: &socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), receiver: this, SLOT(exitLoop())); |
4089 | connect(sender: &socket, SIGNAL(peerVerifyError(QSslError)), receiver: this, SLOT(exitLoop())); |
4090 | connect(sender: &socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), receiver: this, SLOT(exitLoop())); |
4091 | |
4092 | // force a PSK cipher w/o auth |
4093 | auto sslConfig = socket.sslConfiguration(); |
4094 | sslConfig.setCiphers({QSslCipher(PSK_CIPHER_WITHOUT_AUTH)}); |
4095 | socket.setSslConfiguration(sslConfig); |
4096 | |
4097 | PskProvider provider; |
4098 | provider.setIdentity(PSK_CLIENT_IDENTITY); |
4099 | provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY); |
4100 | connect(sender: &socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), receiver: &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*))); |
4101 | socket.setPeerVerifyMode(QSslSocket::VerifyNone); |
4102 | |
4103 | PskServer server; |
4104 | server.m_pskProvider.setIdentity(provider.m_identity); |
4105 | server.m_pskProvider.setPreSharedKey(provider.m_psk); |
4106 | server.config.setPreSharedKeyIdentityHint(PSK_SERVER_IDENTITY_HINT); |
4107 | QVERIFY(server.listen()); |
4108 | |
4109 | // Start connecting |
4110 | socket.connectToHost(hostName: QHostAddress(QHostAddress::LocalHost).toString(), port: server.serverPort()); |
4111 | enterLoop(secs: 5); |
4112 | |
4113 | // Entered connected state |
4114 | QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); |
4115 | QCOMPARE(socket.mode(), QSslSocket::UnencryptedMode); |
4116 | QVERIFY(!socket.isEncrypted()); |
4117 | QCOMPARE(connectedSpy.count(), 1); |
4118 | QCOMPARE(disconnectedSpy.count(), 0); |
4119 | |
4120 | // Enter encrypted mode |
4121 | socket.startClientEncryption(); |
4122 | QCOMPARE(socket.mode(), QSslSocket::SslClientMode); |
4123 | QVERIFY(!socket.isEncrypted()); |
4124 | QCOMPARE(connectionEncryptedSpy.count(), 0); |
4125 | |
4126 | // Start handshake. |
4127 | enterLoop(secs: 10); |
4128 | |
4129 | // We must get the PSK signal in all cases |
4130 | QCOMPARE(pskAuthenticationRequiredSpy.count(), 1); |
4131 | |
4132 | QCOMPARE(connectionEncryptedSpy.count(), 1); |
4133 | QVERIFY(socket.isEncrypted()); |
4134 | QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); |
4135 | |
4136 | // check writing |
4137 | socket.write(data: "Hello from Qt TLS/PSK!" ); |
4138 | QVERIFY(socket.waitForBytesWritten()); |
4139 | |
4140 | // disconnect |
4141 | socket.disconnectFromHost(); |
4142 | enterLoop(secs: 10); |
4143 | |
4144 | QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState); |
4145 | QCOMPARE(disconnectedSpy.count(), 1); |
4146 | } |
4147 | |
4148 | void tst_QSslSocket::signatureAlgorithm_data() |
4149 | { |
4150 | if (!QSslSocket::supportsSsl()) |
4151 | QSKIP("Signature algorithms cannot be tested without SSL support" ); |
4152 | |
4153 | if (QSslSocket::sslLibraryVersionNumber() >= 0x10101000L) { |
4154 | // FIXME: investigate if this test makes any sense with TLS 1.3. |
4155 | QSKIP("Test is not valid for TLS 1.3/OpenSSL 1.1.1" ); |
4156 | } |
4157 | |
4158 | QTest::addColumn<QByteArrayList>(name: "serverSigAlgPairs" ); |
4159 | QTest::addColumn<QSsl::SslProtocol>(name: "serverProtocol" ); |
4160 | QTest::addColumn<QByteArrayList>(name: "clientSigAlgPairs" ); |
4161 | QTest::addColumn<QSsl::SslProtocol>(name: "clientProtocol" ); |
4162 | QTest::addColumn<QAbstractSocket::SocketState>(name: "state" ); |
4163 | |
4164 | const QByteArray dsaSha1("DSA+SHA1" ); |
4165 | const QByteArray ecdsaSha1("ECDSA+SHA1" ); |
4166 | const QByteArray ecdsaSha512("ECDSA+SHA512" ); |
4167 | const QByteArray rsaSha256("RSA+SHA256" ); |
4168 | const QByteArray rsaSha384("RSA+SHA384" ); |
4169 | const QByteArray rsaSha512("RSA+SHA512" ); |
4170 | |
4171 | QTest::newRow(dataTag: "match_TlsV1_2" ) |
4172 | << QByteArrayList({rsaSha256}) |
4173 | << QSsl::TlsV1_2 |
4174 | << QByteArrayList({rsaSha256}) |
4175 | << QSsl::AnyProtocol |
4176 | << QAbstractSocket::ConnectedState; |
4177 | QTest::newRow(dataTag: "no_hashalg_match_TlsV1_2" ) |
4178 | << QByteArrayList({rsaSha256}) |
4179 | << QSsl::TlsV1_2 |
4180 | << QByteArrayList({rsaSha512}) |
4181 | << QSsl::AnyProtocol |
4182 | << QAbstractSocket::UnconnectedState; |
4183 | QTest::newRow(dataTag: "no_sigalg_match_TlsV1_2" ) |
4184 | << QByteArrayList({ecdsaSha512}) |
4185 | << QSsl::TlsV1_2 |
4186 | << QByteArrayList({rsaSha512}) |
4187 | << QSsl::AnyProtocol |
4188 | << QAbstractSocket::UnconnectedState; |
4189 | QTest::newRow(dataTag: "no_cipher_match_AnyProtocol" ) |
4190 | << QByteArrayList({rsaSha512}) |
4191 | << QSsl::AnyProtocol |
4192 | << QByteArrayList({ecdsaSha512}) |
4193 | << QSsl::AnyProtocol |
4194 | << QAbstractSocket::UnconnectedState; |
4195 | QTest::newRow(dataTag: "match_multiple-choice" ) |
4196 | << QByteArrayList({dsaSha1, rsaSha256, rsaSha384, rsaSha512}) |
4197 | << QSsl::AnyProtocol |
4198 | << QByteArrayList({ecdsaSha1, rsaSha384, rsaSha512, ecdsaSha512}) |
4199 | << QSsl::AnyProtocol |
4200 | << QAbstractSocket::ConnectedState; |
4201 | QTest::newRow(dataTag: "match_client_longer" ) |
4202 | << QByteArrayList({dsaSha1, rsaSha256}) |
4203 | << QSsl::AnyProtocol |
4204 | << QByteArrayList({ecdsaSha1, ecdsaSha512, rsaSha256}) |
4205 | << QSsl::AnyProtocol |
4206 | << QAbstractSocket::ConnectedState; |
4207 | QTest::newRow(dataTag: "match_server_longer" ) |
4208 | << QByteArrayList({ecdsaSha1, ecdsaSha512, rsaSha256}) |
4209 | << QSsl::AnyProtocol |
4210 | << QByteArrayList({dsaSha1, rsaSha256}) |
4211 | << QSsl::AnyProtocol |
4212 | << QAbstractSocket::ConnectedState; |
4213 | |
4214 | // signature algorithms do not match, but are ignored because the tls version is not v1.2 |
4215 | QTest::newRow(dataTag: "client_ignore_TlsV1_1" ) |
4216 | << QByteArrayList({rsaSha256}) |
4217 | << QSsl::TlsV1_1 |
4218 | << QByteArrayList({rsaSha512}) |
4219 | << QSsl::AnyProtocol |
4220 | << QAbstractSocket::ConnectedState; |
4221 | QTest::newRow(dataTag: "server_ignore_TlsV1_1" ) |
4222 | << QByteArrayList({rsaSha256}) |
4223 | << QSsl::AnyProtocol |
4224 | << QByteArrayList({rsaSha512}) |
4225 | << QSsl::TlsV1_1 |
4226 | << QAbstractSocket::ConnectedState; |
4227 | QTest::newRow(dataTag: "client_ignore_TlsV1_0" ) |
4228 | << QByteArrayList({rsaSha256}) |
4229 | << QSsl::TlsV1_0 |
4230 | << QByteArrayList({rsaSha512}) |
4231 | << QSsl::AnyProtocol |
4232 | << QAbstractSocket::ConnectedState; |
4233 | QTest::newRow(dataTag: "server_ignore_TlsV1_0" ) |
4234 | << QByteArrayList({rsaSha256}) |
4235 | << QSsl::AnyProtocol |
4236 | << QByteArrayList({rsaSha512}) |
4237 | << QSsl::TlsV1_0 |
4238 | << QAbstractSocket::ConnectedState; |
4239 | } |
4240 | |
4241 | void tst_QSslSocket::signatureAlgorithm() |
4242 | { |
4243 | QFETCH_GLOBAL(bool, setProxy); |
4244 | if (setProxy) |
4245 | QSKIP("Test not adapted for use with proxying" ); |
4246 | |
4247 | QFETCH(QByteArrayList, serverSigAlgPairs); |
4248 | QFETCH(QSsl::SslProtocol, serverProtocol); |
4249 | QFETCH(QByteArrayList, clientSigAlgPairs); |
4250 | QFETCH(QSsl::SslProtocol, clientProtocol); |
4251 | QFETCH(QAbstractSocket::SocketState, state); |
4252 | |
4253 | SslServer server; |
4254 | server.protocol = serverProtocol; |
4255 | server.config.setCiphers({QSslCipher("ECDHE-RSA-AES256-SHA" )}); |
4256 | server.config.setBackendConfigurationOption(QByteArrayLiteral("SignatureAlgorithms" ), value: serverSigAlgPairs.join(sep: ':')); |
4257 | QVERIFY(server.listen()); |
4258 | |
4259 | QSslConfiguration clientConfig = QSslConfiguration::defaultConfiguration(); |
4260 | clientConfig.setProtocol(clientProtocol); |
4261 | clientConfig.setBackendConfigurationOption(QByteArrayLiteral("SignatureAlgorithms" ), value: clientSigAlgPairs.join(sep: ':')); |
4262 | QSslSocket client; |
4263 | client.setSslConfiguration(clientConfig); |
4264 | socket = &client; |
4265 | |
4266 | QEventLoop loop; |
4267 | QTimer::singleShot(interval: 5000, receiver: &loop, slot: &QEventLoop::quit); |
4268 | connect(sender: socket, signal: &QAbstractSocket::errorOccurred, receiver: &loop, slot: &QEventLoop::quit); |
4269 | connect(sender: socket, signal: QOverload<const QList<QSslError> &>::of(ptr: &QSslSocket::sslErrors), receiver: this, slot: &tst_QSslSocket::ignoreErrorSlot); |
4270 | connect(sender: socket, signal: &QSslSocket::encrypted, receiver: &loop, slot: &QEventLoop::quit); |
4271 | |
4272 | client.connectToHostEncrypted(hostName: QHostAddress(QHostAddress::LocalHost).toString(), port: server.serverPort()); |
4273 | loop.exec(); |
4274 | socket = nullptr; |
4275 | QCOMPARE(client.state(), state); |
4276 | } |
4277 | |
4278 | void tst_QSslSocket::forwardReadChannelFinished() |
4279 | { |
4280 | if (!QSslSocket::supportsSsl()) |
4281 | QSKIP("Needs SSL" ); |
4282 | QFETCH_GLOBAL(bool, setProxy); |
4283 | if (setProxy) |
4284 | QSKIP("This test doesn't work via a proxy" ); |
4285 | |
4286 | QSslSocket socket; |
4287 | QSignalSpy readChannelFinishedSpy(&socket, &QAbstractSocket::readChannelFinished); |
4288 | connect(sender: &socket, signal: &QSslSocket::encrypted, slot: [&socket]() { |
4289 | const auto data = QString("GET /ip HTTP/1.0\r\nHost: %1\r\n\r\nAccept: */*\r\n\r\n" ) |
4290 | .arg(a: QtNetworkSettings::serverLocalName()).toUtf8(); |
4291 | socket.write(data); |
4292 | }); |
4293 | connect(sender: &socket, signal: &QSslSocket::readChannelFinished, |
4294 | receiver: &QTestEventLoop::instance(), slot: &QTestEventLoop::exitLoop); |
4295 | socket.connectToHostEncrypted(hostName: QtNetworkSettings::httpServerName(), port: 443); |
4296 | enterLoop(secs: 10); |
4297 | QVERIFY(readChannelFinishedSpy.count()); |
4298 | } |
4299 | |
4300 | #endif // QT_NO_OPENSSL |
4301 | |
4302 | void tst_QSslSocket::disabledProtocols_data() |
4303 | { |
4304 | QTest::addColumn<QSsl::SslProtocol>(name: "disabledProtocol" ); |
4305 | QTest::newRow(dataTag: "SslV2" ) << QSsl::SslV2; |
4306 | QTest::newRow(dataTag: "SslV3" ) << QSsl::SslV3; |
4307 | } |
4308 | |
4309 | void tst_QSslSocket::disabledProtocols() |
4310 | { |
4311 | QFETCH_GLOBAL(const bool, setProxy); |
4312 | if (setProxy) |
4313 | return; |
4314 | |
4315 | QFETCH(const QSsl::SslProtocol, disabledProtocol); |
4316 | const int timeoutMS = 500; |
4317 | // Test a client socket. |
4318 | { |
4319 | // 0. connectToHostEncrypted: client-side, non-blocking API, error is discovered |
4320 | // early, preventing any real connection from ever starting. |
4321 | QSslSocket socket; |
4322 | socket.setProtocol(disabledProtocol); |
4323 | QCOMPARE(socket.error(), QAbstractSocket::UnknownSocketError); |
4324 | socket.connectToHostEncrypted(QStringLiteral("doesnotmatter.org" ), port: 1010); |
4325 | QCOMPARE(socket.error(), QAbstractSocket::SslInvalidUserDataError); |
4326 | QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState); |
4327 | } |
4328 | { |
4329 | // 1. startClientEncryption: client-side, non blocking API, but wants a socket in |
4330 | // the 'connected' state (otherwise just returns false not setting any error code). |
4331 | SslServer server; |
4332 | QVERIFY(server.listen()); |
4333 | |
4334 | QSslSocket socket; |
4335 | QCOMPARE(socket.error(), QAbstractSocket::UnknownSocketError); |
4336 | |
4337 | socket.connectToHost(address: QHostAddress::LocalHost, port: server.serverPort()); |
4338 | QVERIFY(socket.waitForConnected(timeoutMS)); |
4339 | |
4340 | socket.setProtocol(disabledProtocol); |
4341 | socket.startClientEncryption(); |
4342 | QCOMPARE(socket.error(), QAbstractSocket::SslInvalidUserDataError); |
4343 | } |
4344 | { |
4345 | // 2. waitForEncrypted: client-side, blocking API plus requires from us |
4346 | // to call ... connectToHostEncrypted(), which will notice an error and |
4347 | // will prevent any connect at all. Nothing to test. |
4348 | } |
4349 | |
4350 | // Test a server side, relatively simple: server does not connect, it listens/accepts |
4351 | // and then calls startServerEncryption() (which must fall). |
4352 | { |
4353 | SslServer server; |
4354 | server.protocol = disabledProtocol; |
4355 | QVERIFY(server.listen()); |
4356 | |
4357 | QTestEventLoop loop; |
4358 | connect(sender: &server, signal: &SslServer::socketError, slot: [&loop](QAbstractSocket::SocketError) |
4359 | {loop.exitLoop();}); |
4360 | |
4361 | QTcpSocket client; |
4362 | client.connectToHost(address: QHostAddress::LocalHost, port: server.serverPort()); |
4363 | loop.enterLoopMSecs(ms: timeoutMS); |
4364 | QVERIFY(!loop.timeout()); |
4365 | QVERIFY(server.socket); |
4366 | QCOMPARE(server.socket->error(), QAbstractSocket::SslInvalidUserDataError); |
4367 | } |
4368 | } |
4369 | |
4370 | void tst_QSslSocket::oldErrorsOnSocketReuse() |
4371 | { |
4372 | QFETCH_GLOBAL(bool, setProxy); |
4373 | if (setProxy) |
4374 | return; // not relevant |
4375 | SslServer server; |
4376 | server.protocol = QSsl::TlsV1_1; |
4377 | server.m_certFile = testDataDir + "certs/fluke.cert" ; |
4378 | server.m_keyFile = testDataDir + "certs/fluke.key" ; |
4379 | QVERIFY(server.listen(QHostAddress::SpecialAddress::LocalHost)); |
4380 | |
4381 | QSslSocket socket; |
4382 | socket.setProtocol(QSsl::TlsV1_1); |
4383 | QList<QSslError> errorList; |
4384 | auto connection = connect(sender: &socket, signal: QOverload<const QList<QSslError> &>::of(ptr: &QSslSocket::sslErrors), |
4385 | slot: [&socket, &errorList](const QList<QSslError> &errors) { |
4386 | errorList += errors; |
4387 | socket.ignoreSslErrors(errors); |
4388 | socket.resume(); |
4389 | }); |
4390 | |
4391 | socket.connectToHostEncrypted(hostName: QString::fromLatin1(str: "localhost" ), port: server.serverPort()); |
4392 | QVERIFY(QTest::qWaitFor([&socket](){ return socket.isEncrypted(); })); |
4393 | socket.disconnectFromHost(); |
4394 | if (socket.state() != QAbstractSocket::UnconnectedState) { |
4395 | QVERIFY(QTest::qWaitFor( |
4396 | [&socket](){ |
4397 | return socket.state() == QAbstractSocket::UnconnectedState; |
4398 | })); |
4399 | } |
4400 | |
4401 | auto oldList = errorList; |
4402 | errorList.clear(); |
4403 | server.close(); |
4404 | server.m_certFile = testDataDir + "certs/bogus-client.crt" ; |
4405 | server.m_keyFile = testDataDir + "certs/bogus-client.key" ; |
4406 | QVERIFY(server.listen(QHostAddress::SpecialAddress::LocalHost)); |
4407 | |
4408 | socket.connectToHostEncrypted(hostName: QString::fromLatin1(str: "localhost" ), port: server.serverPort()); |
4409 | QVERIFY(QTest::qWaitFor([&socket](){ return socket.isEncrypted(); })); |
4410 | |
4411 | for (const auto &error : oldList) { |
4412 | QVERIFY2(!errorList.contains(error), |
4413 | "The new errors should not contain any of the old ones" ); |
4414 | } |
4415 | } |
4416 | |
4417 | #endif // QT_NO_SSL |
4418 | |
4419 | QTEST_MAIN(tst_QSslSocket) |
4420 | |
4421 | #include "tst_qsslsocket.moc" |
4422 | |