summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
authorTobias Hunger <tobias.hunger@qt.io>2019-04-16 16:32:08 +0200
committerTobias Hunger <tobias.hunger@qt.io>2019-04-16 16:32:08 +0200
commit6630937e63ae5797487b86743a7733c8ae5cc42c (patch)
tree3d53dacf6430f9099e1fb20835881205de674961 /src/network
parent37ed6dae00640f9cc980ffda05347c12a7eb5d7e (diff)
parentc7af193d2e49e9f10b86262e63d8d13abf72b5cf (diff)
Merge commit 'dev' into 'wip/cmake-merge'
Diffstat (limited to 'src/network')
-rw-r--r--src/network/CMakeLists.txt33
-rw-r--r--src/network/access/http2/bitstreams_p.h2
-rw-r--r--src/network/access/http2/hpacktable.cpp209
-rw-r--r--src/network/access/http2/hpacktable_p.h11
-rw-r--r--src/network/access/qabstractnetworkcache.cpp18
-rw-r--r--src/network/access/qabstractnetworkcache.h4
-rw-r--r--src/network/access/qftp.cpp6
-rw-r--r--src/network/access/qftp_p.h6
-rw-r--r--src/network/access/qhstspolicy.h4
-rw-r--r--src/network/access/qhstsstore_p.h2
-rw-r--r--src/network/access/qhttp2protocolhandler.cpp6
-rw-r--r--src/network/access/qhttpmultipart.h4
-rw-r--r--src/network/access/qhttpmultipart_p.h2
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp37
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h10
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp6
-rw-r--r--src/network/access/qhttpnetworkreply.cpp3
-rw-r--r--src/network/access/qhttpnetworkreply_p.h2
-rw-r--r--src/network/access/qhttpnetworkrequest.cpp15
-rw-r--r--src/network/access/qhttpnetworkrequest_p.h3
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp15
-rw-r--r--src/network/access/qhttpthreaddelegate_p.h10
-rw-r--r--src/network/access/qnetworkaccessauthenticationmanager_p.h4
-rw-r--r--src/network/access/qnetworkaccesscachebackend.cpp17
-rw-r--r--src/network/access/qnetworkaccessftpbackend.cpp2
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp44
-rw-r--r--src/network/access/qnetworkaccessmanager.h3
-rw-r--r--src/network/access/qnetworkcookie.h4
-rw-r--r--src/network/access/qnetworkdiskcache_p.h4
-rw-r--r--src/network/access/qnetworkfile.cpp12
-rw-r--r--src/network/access/qnetworkreplyfileimpl_p.h1
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp35
-rw-r--r--src/network/access/qnetworkreplyimpl_p.h2
-rw-r--r--src/network/access/qnetworkreplywasmimpl.cpp83
-rw-r--r--src/network/access/qnetworkreplywasmimpl_p.h4
-rw-r--r--src/network/access/qnetworkrequest.cpp33
-rw-r--r--src/network/access/qnetworkrequest.h6
-rw-r--r--src/network/bearer/qbearerengine_p.h2
-rw-r--r--src/network/bearer/qbearerplugin_p.h2
-rw-r--r--src/network/bearer/qnetworkconfiguration.cpp4
-rw-r--r--src/network/bearer/qnetworkconfiguration.h4
-rw-r--r--src/network/bearer/qnetworkconfiguration_p.h2
-rw-r--r--src/network/bearer/qnetworksession.cpp21
-rw-r--r--src/network/configure.cmake104
-rw-r--r--src/network/configure.json136
-rw-r--r--src/network/configure.pri6
-rw-r--r--src/network/doc/snippets/code/src_network_bearer_qnetworksession.cpp60
-rw-r--r--src/network/doc/snippets/code/src_network_kernel_qnetworkdatagram.cpp64
-rw-r--r--src/network/doc/snippets/code/src_network_kernel_qnetworkinterface.cpp46
-rw-r--r--src/network/doc/snippets/code/src_network_socket_qudpsocket.cpp4
-rw-r--r--src/network/doc/snippets/code/src_network_ssl_qsslpresharedkeyauthenticator.cpp53
-rw-r--r--src/network/doc/src/ssl.qdoc5
-rw-r--r--src/network/kernel/kernel.pri5
-rw-r--r--src/network/kernel/qauthenticator.cpp406
-rw-r--r--src/network/kernel/qauthenticator_p.h17
-rw-r--r--src/network/kernel/qdnslookup.h20
-rw-r--r--src/network/kernel/qdnslookup_p.h2
-rw-r--r--src/network/kernel/qdnslookup_unix.cpp41
-rw-r--r--src/network/kernel/qhostaddress.cpp5
-rw-r--r--src/network/kernel/qhostaddress.h8
-rw-r--r--src/network/kernel/qhostinfo.cpp33
-rw-r--r--src/network/kernel/qhostinfo.h11
-rw-r--r--src/network/kernel/qhostinfo_p.h2
-rw-r--r--src/network/kernel/qhostinfo_unix.cpp33
-rw-r--r--src/network/kernel/qnetworkdatagram.cpp15
-rw-r--r--src/network/kernel/qnetworkdatagram.h6
-rw-r--r--src/network/kernel/qnetworkinterface.cpp8
-rw-r--r--src/network/kernel/qnetworkinterface.h8
-rw-r--r--src/network/kernel/qnetworkinterface_p.h2
-rw-r--r--src/network/kernel/qnetworkinterface_unix.cpp7
-rw-r--r--src/network/kernel/qnetworkinterface_unix_p.h2
-rw-r--r--src/network/kernel/qnetworkproxy.h8
-rw-r--r--src/network/kernel/qnetworkproxy_win.cpp4
-rw-r--r--src/network/socket/qabstractsocket.cpp46
-rw-r--r--src/network/socket/qabstractsocket.h2
-rw-r--r--src/network/socket/qabstractsocket_p.h3
-rw-r--r--src/network/socket/qabstractsocketengine_p.h14
-rw-r--r--src/network/socket/qhttpsocketengine.cpp10
-rw-r--r--src/network/socket/qhttpsocketengine_p.h10
-rw-r--r--src/network/socket/qlocalserver.cpp4
-rw-r--r--src/network/socket/qlocalserver_p.h2
-rw-r--r--src/network/socket/qlocalsocket_p.h1
-rw-r--r--src/network/socket/qlocalsocket_win.cpp20
-rw-r--r--src/network/socket/qnativesocketengine.cpp8
-rw-r--r--src/network/socket/qnativesocketengine_p.h12
-rw-r--r--src/network/socket/qnativesocketengine_unix.cpp14
-rw-r--r--src/network/socket/qnativesocketengine_win.cpp26
-rw-r--r--src/network/socket/qnativesocketengine_winrt_p.h2
-rw-r--r--src/network/socket/qsocks5socketengine.cpp27
-rw-r--r--src/network/socket/qsocks5socketengine_p.h12
-rw-r--r--src/network/socket/qtcpserver.cpp2
-rw-r--r--src/network/socket/qudpsocket.cpp3
-rw-r--r--src/network/ssl/qasn1element_p.h6
-rw-r--r--src/network/ssl/qdtls.cpp1
-rw-r--r--src/network/ssl/qdtls.h2
-rw-r--r--src/network/ssl/qocsp_p.h74
-rw-r--r--src/network/ssl/qocspresponse.cpp258
-rw-r--r--src/network/ssl/qocspresponse.h116
-rw-r--r--src/network/ssl/qocspresponse_p.h84
-rw-r--r--src/network/ssl/qpassworddigestor.cpp2
-rw-r--r--src/network/ssl/qssl.cpp28
-rw-r--r--src/network/ssl/qssl.h11
-rw-r--r--src/network/ssl/qsslcertificate.cpp3
-rw-r--r--src/network/ssl/qsslcertificate.h8
-rw-r--r--src/network/ssl/qsslcertificate_openssl.cpp50
-rw-r--r--src/network/ssl/qsslcertificate_p.h20
-rw-r--r--src/network/ssl/qsslcertificate_qt.cpp36
-rw-r--r--src/network/ssl/qsslcertificate_schannel.cpp62
-rw-r--r--src/network/ssl/qsslcertificateextension.h4
-rw-r--r--src/network/ssl/qsslcipher.h4
-rw-r--r--src/network/ssl/qsslconfiguration.cpp41
-rw-r--r--src/network/ssl/qsslconfiguration.h7
-rw-r--r--src/network/ssl/qsslconfiguration_p.h6
-rw-r--r--src/network/ssl/qsslcontext_openssl.cpp24
-rw-r--r--src/network/ssl/qsslcontext_openssl11.cpp104
-rw-r--r--src/network/ssl/qsslcontext_openssl_p.h2
-rw-r--r--src/network/ssl/qsslcontext_opensslpre11.cpp37
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters.cpp14
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters.h24
-rw-r--r--src/network/ssl/qsslellipticcurve.cpp2
-rw-r--r--src/network/ssl/qsslellipticcurve.h18
-rw-r--r--src/network/ssl/qsslellipticcurve_dummy.cpp2
-rw-r--r--src/network/ssl/qsslellipticcurve_openssl.cpp2
-rw-r--r--src/network/ssl/qsslerror.cpp47
-rw-r--r--src/network/ssl/qsslerror.h18
-rw-r--r--src/network/ssl/qsslkey.h4
-rw-r--r--src/network/ssl/qsslkey_mac.cpp22
-rw-r--r--src/network/ssl/qsslkey_openssl.cpp55
-rw-r--r--src/network/ssl/qsslkey_p.cpp8
-rw-r--r--src/network/ssl/qsslkey_p.h10
-rw-r--r--src/network/ssl/qsslkey_qt.cpp77
-rw-r--r--src/network/ssl/qsslkey_schannel.cpp178
-rw-r--r--src/network/ssl/qsslkey_winrt.cpp9
-rw-r--r--src/network/ssl/qsslpresharedkeyauthenticator.cpp21
-rw-r--r--src/network/ssl/qsslpresharedkeyauthenticator.h4
-rw-r--r--src/network/ssl/qsslsocket.cpp122
-rw-r--r--src/network/ssl/qsslsocket.h5
-rw-r--r--src/network/ssl/qsslsocket_mac.cpp52
-rw-r--r--src/network/ssl/qsslsocket_mac_p.h4
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp459
-rw-r--r--src/network/ssl/qsslsocket_openssl11.cpp20
-rw-r--r--src/network/ssl/qsslsocket_openssl11_symbols_p.h22
-rw-r--r--src/network/ssl/qsslsocket_openssl_p.h13
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp198
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h99
-rw-r--r--src/network/ssl/qsslsocket_opensslpre11.cpp20
-rw-r--r--src/network/ssl/qsslsocket_opensslpre11_symbols_p.h47
-rw-r--r--src/network/ssl/qsslsocket_p.h23
-rw-r--r--src/network/ssl/qsslsocket_qt.cpp24
-rw-r--r--src/network/ssl/qsslsocket_schannel.cpp1994
-rw-r--r--src/network/ssl/qsslsocket_schannel_p.h155
-rw-r--r--src/network/ssl/qsslsocket_winrt.cpp14
-rw-r--r--src/network/ssl/ssl.pri22
153 files changed, 5722 insertions, 1112 deletions
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
index 180afb7cc5..02d31fc468 100644
--- a/src/network/CMakeLists.txt
+++ b/src/network/CMakeLists.txt
@@ -1,3 +1,5 @@
+# Generated from network.pro.
+
#####################################################################
## Network Module:
#####################################################################
@@ -146,6 +148,8 @@ extend_target(Network CONDITION UNIX
kernel/qhostinfo_unix.cpp
socket/qnativesocketengine_unix.cpp
socket/qnet_unix_p.h
+ LIBRARIES
+ ${CMAKE_DL_LIBS}
)
# special case: Do not add this to android!
@@ -208,6 +212,11 @@ extend_target(Network CONDITION APPLE_OSX AND NOT APPLE_UIKIT
${FWSystemConfiguration}
)
+extend_target(Network CONDITION QT_FEATURE_gssapi
+ LIBRARIES
+ gssapi_krb5
+)
+
extend_target(Network CONDITION APPLE_UIKIT
SOURCES
kernel/qnetworkinterface_uikit_p.h
@@ -285,6 +294,7 @@ extend_target(Network CONDITION NOT QT_FEATURE_openssl
extend_target(Network CONDITION QT_FEATURE_ssl
SOURCES
+ ssl/qocspresponse.cpp ssl/qocspresponse.h ssl/qocspresponse_p.h
ssl/qsslcipher.cpp ssl/qsslcipher.h ssl/qsslcipher_p.h
ssl/qsslconfiguration.cpp ssl/qsslconfiguration.h ssl/qsslconfiguration_p.h
ssl/qssldiffiehellmanparameters.cpp ssl/qssldiffiehellmanparameters.h ssl/qssldiffiehellmanparameters_p.h
@@ -305,6 +315,22 @@ extend_target(Network CONDITION QT_FEATURE_ssl AND WINRT
ssl/qsslsocket_winrt.cpp ssl/qsslsocket_winrt_p.h
)
+extend_target(Network CONDITION QT_FEATURE_schannel AND QT_FEATURE_ssl
+ SOURCES
+ ssl/qsslcertificate_schannel.cpp
+ ssl/qssldiffiehellmanparameters_dummy.cpp
+ ssl/qsslellipticcurve_dummy.cpp
+ ssl/qsslkey_qt.cpp
+ ssl/qsslkey_schannel.cpp
+ ssl/qsslsocket_qt.cpp
+ ssl/qsslsocket_schannel.cpp ssl/qsslsocket_schannel_p.h
+ LIBRARIES
+ Crypt32
+ Secur32
+ bcrypt
+ ncrypt
+)
+
extend_target(Network CONDITION QT_FEATURE_securetransport AND QT_FEATURE_ssl
SOURCES
ssl/qssldiffiehellmanparameters_dummy.cpp
@@ -337,6 +363,11 @@ extend_target(Network CONDITION QT_FEATURE_dtls AND QT_FEATURE_openssl AND QT_FE
ssl/qdtls_openssl.cpp ssl/qdtls_openssl_p.h
)
+extend_target(Network CONDITION QT_FEATURE_ocsp AND QT_FEATURE_openssl AND QT_FEATURE_ssl
+ SOURCES
+ ssl/qocsp_p.h
+)
+
extend_target(Network CONDITION QT_FEATURE_openssl AND QT_FEATURE_opensslv11 AND QT_FEATURE_ssl
SOURCES
ssl/qsslcontext_openssl11.cpp
@@ -383,5 +414,5 @@ extend_target(Network CONDITION QT_FEATURE_openssl AND QT_FEATURE_ssl AND WIN32
extend_target(Network CONDITION WIN32 LIBRARIES ws2_32) # special case: mkspecs/common/msvc-desktop.conf
add_qt_docs(
- ./doc/qtnetwork.qdocconf
+ doc/qtnetwork.qdocconf
)
diff --git a/src/network/access/http2/bitstreams_p.h b/src/network/access/http2/bitstreams_p.h
index 9eba319dc2..ca272062a6 100644
--- a/src/network/access/http2/bitstreams_p.h
+++ b/src/network/access/http2/bitstreams_p.h
@@ -89,7 +89,7 @@ public:
void clear();
private:
- Q_DISABLE_COPY(BitOStream);
+ Q_DISABLE_COPY_MOVE(BitOStream);
std::vector<uchar> &buffer;
quint64 bitsSet;
diff --git a/src/network/access/http2/hpacktable.cpp b/src/network/access/http2/hpacktable.cpp
index a90ee72d52..fddb5feca5 100644
--- a/src/network/access/http2/hpacktable.cpp
+++ b/src/network/access/http2/hpacktable.cpp
@@ -42,6 +42,7 @@
#include <QtCore/qdebug.h>
#include <algorithm>
+#include <cstddef>
#include <cstring>
#include <limits>
@@ -61,7 +62,7 @@ HeaderSize entry_size(const QByteArray &name, const QByteArray &value)
// for counting the number of references to the name and value would have
// 32 octets of overhead."
- const unsigned sum = unsigned(name.size()) + value.size();
+ const unsigned sum = unsigned(name.size() + value.size());
if (std::numeric_limits<unsigned>::max() - 32 < sum)
return HeaderSize();
return HeaderSize(true, quint32(sum + 32));
@@ -75,7 +76,7 @@ int compare(const QByteArray &lhs, const QByteArray &rhs)
if (const int minLen = std::min(lhs.size(), rhs.size())) {
// We use memcmp, since strings in headers are allowed
// to contain '\0'.
- const int cmp = std::memcmp(lhs.constData(), rhs.constData(), minLen);
+ const int cmp = std::memcmp(lhs.constData(), rhs.constData(), std::size_t(minLen));
if (cmp)
return cmp;
}
@@ -138,82 +139,6 @@ bool FieldLookupTable::SearchEntry::operator < (const SearchEntry &rhs)const
return offset > rhs.offset;
}
-// This data is from HPACK's specs and it's quite
-// conveniently sorted == works with binary search as it is.
-// Later this can probably change and instead of simple
-// vector we'll just reuse FieldLookupTable.
-// TODO: it makes sense to generate this table while ...
-// configuring/building Qt (some script downloading/parsing/generating
-// would be quite handy).
-const std::vector<HeaderField> &staticTable()
-{
- static std::vector<HeaderField> table = {
- {":authority", ""},
- {":method", "GET"},
- {":method", "POST"},
- {":path", "/"},
- {":path", "/index.html"},
- {":scheme", "http"},
- {":scheme", "https"},
- {":status", "200"},
- {":status", "204"},
- {":status", "206"},
- {":status", "304"},
- {":status", "400"},
- {":status", "404"},
- {":status", "500"},
- {"accept-charset", ""},
- {"accept-encoding", "gzip, deflate"},
- {"accept-language", ""},
- {"accept-ranges", ""},
- {"accept", ""},
- {"access-control-allow-origin", ""},
- {"age", ""},
- {"allow", ""},
- {"authorization", ""},
- {"cache-control", ""},
- {"content-disposition", ""},
- {"content-encoding", ""},
- {"content-language", ""},
- {"content-length", ""},
- {"content-location", ""},
- {"content-range", ""},
- {"content-type", ""},
- {"cookie", ""},
- {"date", ""},
- {"etag", ""},
- {"expect", ""},
- {"expires", ""},
- {"from", ""},
- {"host", ""},
- {"if-match", ""},
- {"if-modified-since", ""},
- {"if-none-match", ""},
- {"if-range", ""},
- {"if-unmodified-since", ""},
- {"last-modified", ""},
- {"link", ""},
- {"location", ""},
- {"max-forwards", ""},
- {"proxy-authenticate", ""},
- {"proxy-authorization", ""},
- {"range", ""},
- {"referer", ""},
- {"refresh", ""},
- {"retry-after", ""},
- {"server", ""},
- {"set-cookie", ""},
- {"strict-transport-security", ""},
- {"transfer-encoding", ""},
- {"user-agent", ""},
- {"vary", ""},
- {"via", ""},
- {"www-authenticate", ""}
- };
-
- return table;
-}
-
FieldLookupTable::FieldLookupTable(quint32 maxSize, bool use)
: maxTableSize(maxSize),
tableCapacity(maxSize),
@@ -296,12 +221,12 @@ void FieldLookupTable::evictEntry()
quint32 FieldLookupTable::numberOfEntries() const
{
- return quint32(staticTable().size()) + nDynamic;
+ return quint32(staticPart().size()) + nDynamic;
}
quint32 FieldLookupTable::numberOfStaticEntries() const
{
- return quint32(staticTable().size());
+ return quint32(staticPart().size());
}
quint32 FieldLookupTable::numberOfDynamicEntries() const
@@ -326,24 +251,18 @@ void FieldLookupTable::clearDynamicTable()
bool FieldLookupTable::indexIsValid(quint32 index) const
{
- return index && index <= staticTable().size() + nDynamic;
+ return index && index <= staticPart().size() + nDynamic;
}
quint32 FieldLookupTable::indexOf(const QByteArray &name, const QByteArray &value)const
{
// Start from the static part first:
- const auto &table = staticTable();
+ const auto &table = staticPart();
const HeaderField field(name, value);
- const auto staticPos = std::lower_bound(table.begin(), table.end(), field,
- [](const HeaderField &lhs, const HeaderField &rhs) {
- int cmp = compare(lhs.name, rhs.name);
- if (cmp)
- return cmp < 0;
- return compare(lhs.value, rhs.value) < 0;
- });
+ const auto staticPos = findInStaticPart(field, CompareMode::nameAndValue);
if (staticPos != table.end()) {
if (staticPos->name == name && staticPos->value == value)
- return staticPos - table.begin() + 1;
+ return quint32(staticPos - table.begin() + 1);
}
// Now we have to lookup in our dynamic part ...
@@ -366,15 +285,12 @@ quint32 FieldLookupTable::indexOf(const QByteArray &name, const QByteArray &valu
quint32 FieldLookupTable::indexOf(const QByteArray &name) const
{
// Start from the static part first:
- const auto &table = staticTable();
+ const auto &table = staticPart();
const HeaderField field(name, QByteArray());
- const auto staticPos = std::lower_bound(table.begin(), table.end(), field,
- [](const HeaderField &lhs, const HeaderField &rhs) {
- return compare(lhs.name, rhs.name) < 0;
- });
+ const auto staticPos = findInStaticPart(field, CompareMode::nameOnly);
if (staticPos != table.end()) {
if (staticPos->name == name)
- return staticPos - table.begin() + 1;
+ return quint32(staticPos - table.begin() + 1);
}
// Now we have to lookup in our dynamic part ...
@@ -402,7 +318,7 @@ bool FieldLookupTable::field(quint32 index, QByteArray *name, QByteArray *value)
if (!indexIsValid(index))
return false;
- const auto &table = staticTable();
+ const auto &table = staticPart();
if (index - 1 < table.size()) {
*name = table[index - 1].name;
*value = table[index - 1].value;
@@ -477,7 +393,7 @@ quint32 FieldLookupTable::keyToIndex(const SearchEntry &key) const
Q_ASSERT(offset < ChunkSize);
Q_ASSERT(chunkIndex || offset >= begin);
- return quint32(offset + chunkIndex * ChunkSize - begin + 1 + staticTable().size());
+ return quint32(offset + chunkIndex * ChunkSize - begin + 1 + staticPart().size());
}
FieldLookupTable::SearchEntry FieldLookupTable::frontKey() const
@@ -526,6 +442,103 @@ void FieldLookupTable::setMaxDynamicTableSize(quint32 size)
updateDynamicTableSize(size);
}
+// This data is from the HPACK's specs and it's quite conveniently sorted,
+// except ... 'accept' is in the wrong position, see how we handle it below.
+const std::vector<HeaderField> &FieldLookupTable::staticPart()
+{
+ static std::vector<HeaderField> table = {
+ {":authority", ""},
+ {":method", "GET"},
+ {":method", "POST"},
+ {":path", "/"},
+ {":path", "/index.html"},
+ {":scheme", "http"},
+ {":scheme", "https"},
+ {":status", "200"},
+ {":status", "204"},
+ {":status", "206"},
+ {":status", "304"},
+ {":status", "400"},
+ {":status", "404"},
+ {":status", "500"},
+ {"accept-charset", ""},
+ {"accept-encoding", "gzip, deflate"},
+ {"accept-language", ""},
+ {"accept-ranges", ""},
+ {"accept", ""},
+ {"access-control-allow-origin", ""},
+ {"age", ""},
+ {"allow", ""},
+ {"authorization", ""},
+ {"cache-control", ""},
+ {"content-disposition", ""},
+ {"content-encoding", ""},
+ {"content-language", ""},
+ {"content-length", ""},
+ {"content-location", ""},
+ {"content-range", ""},
+ {"content-type", ""},
+ {"cookie", ""},
+ {"date", ""},
+ {"etag", ""},
+ {"expect", ""},
+ {"expires", ""},
+ {"from", ""},
+ {"host", ""},
+ {"if-match", ""},
+ {"if-modified-since", ""},
+ {"if-none-match", ""},
+ {"if-range", ""},
+ {"if-unmodified-since", ""},
+ {"last-modified", ""},
+ {"link", ""},
+ {"location", ""},
+ {"max-forwards", ""},
+ {"proxy-authenticate", ""},
+ {"proxy-authorization", ""},
+ {"range", ""},
+ {"referer", ""},
+ {"refresh", ""},
+ {"retry-after", ""},
+ {"server", ""},
+ {"set-cookie", ""},
+ {"strict-transport-security", ""},
+ {"transfer-encoding", ""},
+ {"user-agent", ""},
+ {"vary", ""},
+ {"via", ""},
+ {"www-authenticate", ""}
+ };
+
+ return table;
+}
+
+std::vector<HeaderField>::const_iterator FieldLookupTable::findInStaticPart(const HeaderField &field, CompareMode mode)
+{
+ const auto &table = staticPart();
+ const auto acceptPos = table.begin() + 18;
+ if (field.name == "accept") {
+ if (mode == CompareMode::nameAndValue && field.value != "")
+ return table.end();
+ return acceptPos;
+ }
+
+ auto predicate = [mode](const HeaderField &lhs, const HeaderField &rhs) {
+ const int cmp = compare(lhs.name, rhs.name);
+ if (cmp)
+ return cmp < 0;
+ else if (mode == CompareMode::nameAndValue)
+ return compare(lhs.value, rhs.value) < 0;
+ return false;
+ };
+
+ const auto staticPos = std::lower_bound(table.begin(), acceptPos, field, predicate);
+ if (staticPos != acceptPos)
+ return staticPos;
+
+ return std::lower_bound(acceptPos + 1, table.end(), field, predicate);
+}
+
}
QT_END_NAMESPACE
diff --git a/src/network/access/http2/hpacktable_p.h b/src/network/access/http2/hpacktable_p.h
index aaea89b986..587d86f09c 100644
--- a/src/network/access/http2/hpacktable_p.h
+++ b/src/network/access/http2/hpacktable_p.h
@@ -173,6 +173,8 @@ public:
bool updateDynamicTableSize(quint32 size);
void setMaxDynamicTableSize(quint32 size);
+ static const std::vector<HeaderField> &staticPart();
+
private:
// Table's maximum size is controlled
// by SETTINGS_HEADER_TABLE_SIZE (HTTP/2, 6.5.2).
@@ -225,9 +227,16 @@ private:
quint32 indexOfChunk(const Chunk *chunk) const;
quint32 keyToIndex(const SearchEntry &key) const;
+ enum class CompareMode {
+ nameOnly,
+ nameAndValue
+ };
+
+ static std::vector<HeaderField>::const_iterator findInStaticPart(const HeaderField &field, CompareMode mode);
+
mutable QByteArray dummyDst;
- Q_DISABLE_COPY(FieldLookupTable);
+ Q_DISABLE_COPY_MOVE(FieldLookupTable)
};
}
diff --git a/src/network/access/qabstractnetworkcache.cpp b/src/network/access/qabstractnetworkcache.cpp
index 2b670b2cce..4e217294c4 100644
--- a/src/network/access/qabstractnetworkcache.cpp
+++ b/src/network/access/qabstractnetworkcache.cpp
@@ -191,8 +191,8 @@ bool QNetworkCacheMetaData::isValid() const
Some cache implementations can keep these cache items in memory for performance reasons,
but for security reasons they should not be written to disk.
- Specifically with http, documents marked with Pragma: no-cache, or have a Cache-control set to
- no-store or no-cache or any https document that doesn't have "Cache-control: public" set will
+ Specifically with http, documents with Cache-control set to no-store or any
+ https document that doesn't have "Cache-control: public" set will
set the saveToDisk to false.
\sa setSaveToDisk()
@@ -331,11 +331,11 @@ QDataStream &operator<<(QDataStream &out, const QNetworkCacheMetaData &metaData)
static inline QDataStream &operator<<(QDataStream &out, const QNetworkCacheMetaData::AttributesMap &hash)
{
out << quint32(hash.size());
- QNetworkCacheMetaData::AttributesMap::ConstIterator it = hash.end();
- QNetworkCacheMetaData::AttributesMap::ConstIterator begin = hash.begin();
- while (it != begin) {
- --it;
+ QNetworkCacheMetaData::AttributesMap::ConstIterator it = hash.begin();
+ QNetworkCacheMetaData::AttributesMap::ConstIterator end = hash.end();
+ while (it != end) {
out << int(it.key()) << it.value();
+ ++it;
}
return out;
}
@@ -383,7 +383,7 @@ static inline QDataStream &operator>>(QDataStream &in, QNetworkCacheMetaData::At
int k;
QVariant t;
in >> k >> t;
- hash.insertMulti(QNetworkRequest::Attribute(k), t);
+ hash.insert(QNetworkRequest::Attribute(k), t);
}
if (in.status() != QDataStream::Ok)
@@ -475,7 +475,7 @@ QAbstractNetworkCache::~QAbstractNetworkCache()
the QIODevice when done with it.
If there is no cache for \a url, the url is invalid, or if there
- is an internal cache error 0 is returned.
+ is an internal cache error \nullptr is returned.
In the base class this is a pure virtual function.
@@ -496,7 +496,7 @@ QAbstractNetworkCache::~QAbstractNetworkCache()
Returns the device that should be populated with the data for
the cache item \a metaData. When all of the data has been written
insert() should be called. If metaData is invalid or the url in
- the metadata is invalid 0 is returned.
+ the metadata is invalid \nullptr is returned.
The cache owns the device and will take care of deleting it when
it is inserted or removed.
diff --git a/src/network/access/qabstractnetworkcache.h b/src/network/access/qabstractnetworkcache.h
index 678bae2d6e..b604323c41 100644
--- a/src/network/access/qabstractnetworkcache.h
+++ b/src/network/access/qabstractnetworkcache.h
@@ -68,11 +68,11 @@ public:
~QNetworkCacheMetaData();
#ifdef Q_COMPILER_RVALUE_REFS
- QNetworkCacheMetaData &operator=(QNetworkCacheMetaData &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QNetworkCacheMetaData &operator=(QNetworkCacheMetaData &&other) noexcept { swap(other); return *this; }
#endif
QNetworkCacheMetaData &operator=(const QNetworkCacheMetaData &other);
- void swap(QNetworkCacheMetaData &other) Q_DECL_NOTHROW
+ void swap(QNetworkCacheMetaData &other) noexcept
{ qSwap(d, other.d); }
bool operator==(const QNetworkCacheMetaData &other) const;
diff --git a/src/network/access/qftp.cpp b/src/network/access/qftp.cpp
index feece4ebaf..4e399f018f 100644
--- a/src/network/access/qftp.cpp
+++ b/src/network/access/qftp.cpp
@@ -1826,8 +1826,8 @@ int QFtp::cd(const QString &dir)
is data available to read. You can then read the data with the
read() or readAll() functions.
- If \a dev is not 0, the data is written directly to the device \a
- dev. Make sure that the \a dev pointer is valid for the duration
+ If \a dev is not \nullptr, the data is written directly to the device
+ \a dev. Make sure that the \a dev pointer is valid for the duration
of the operation (it is safe to delete it when the
commandFinished() signal is emitted). In this case the readyRead()
signal is \e not emitted and you cannot read data with the
@@ -2154,7 +2154,7 @@ QFtp::Command QFtp::currentCommand() const
\internal
Returns the QIODevice pointer that is used by the FTP command to read data
from or store data to. If there is no current FTP command being executed or
- if the command does not use an IO device, this function returns 0.
+ if the command does not use an IO device, this function returns \nullptr.
This function can be used to delete the QIODevice in the slot connected to
the commandFinished() signal.
diff --git a/src/network/access/qftp_p.h b/src/network/access/qftp_p.h
index bba1f9b09d..91d78d1351 100644
--- a/src/network/access/qftp_p.h
+++ b/src/network/access/qftp_p.h
@@ -67,7 +67,7 @@ class Q_AUTOTEST_EXPORT QFtp : public QObject
Q_OBJECT
public:
- explicit QFtp(QObject *parent = 0);
+ explicit QFtp(QObject *parent = nullptr);
virtual ~QFtp();
enum State {
@@ -118,7 +118,7 @@ public:
int setTransferMode(TransferMode mode);
int list(const QString &dir = QString());
int cd(const QString &dir);
- int get(const QString &file, QIODevice *dev=0, TransferType type = Binary);
+ int get(const QString &file, QIODevice *dev=nullptr, TransferType type = Binary);
int put(const QByteArray &data, const QString &file, TransferType type = Binary);
int put(QIODevice *dev, const QString &file, TransferType type = Binary);
int remove(const QString &file);
@@ -158,7 +158,7 @@ Q_SIGNALS:
void done(bool);
private:
- Q_DISABLE_COPY(QFtp)
+ Q_DISABLE_COPY_MOVE(QFtp)
Q_DECLARE_PRIVATE(QFtp)
Q_PRIVATE_SLOT(d_func(), void _q_startNextCommand())
diff --git a/src/network/access/qhstspolicy.h b/src/network/access/qhstspolicy.h
index 176a8fa635..f1b2ee99e5 100644
--- a/src/network/access/qhstspolicy.h
+++ b/src/network/access/qhstspolicy.h
@@ -65,10 +65,10 @@ public:
QUrl::ParsingMode mode = QUrl::DecodedMode);
QHstsPolicy(const QHstsPolicy &rhs);
QHstsPolicy &operator=(const QHstsPolicy &rhs);
- QHstsPolicy &operator=(QHstsPolicy &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QHstsPolicy &operator=(QHstsPolicy &&other) noexcept { swap(other); return *this; }
~QHstsPolicy();
- void swap(QHstsPolicy &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QHstsPolicy &other) noexcept { qSwap(d, other.d); }
void setHost(const QString &host, QUrl::ParsingMode mode = QUrl::DecodedMode);
QString host(QUrl::ComponentFormattingOptions options = QUrl::FullyDecoded) const;
diff --git a/src/network/access/qhstsstore_p.h b/src/network/access/qhstsstore_p.h
index e82596b250..5338d15592 100644
--- a/src/network/access/qhstsstore_p.h
+++ b/src/network/access/qhstsstore_p.h
@@ -87,7 +87,7 @@ private:
QVector<QHstsPolicy> observedPolicies;
QSettings store;
- Q_DISABLE_COPY(QHstsStore)
+ Q_DISABLE_COPY_MOVE(QHstsStore)
};
QT_END_NAMESPACE
diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp
index df7f87efd4..35aee6e3e1 100644
--- a/src/network/access/qhttp2protocolhandler.cpp
+++ b/src/network/access/qhttp2protocolhandler.cpp
@@ -198,7 +198,7 @@ QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *chan
}
}
- if (!channel->ssl) {
+ if (!channel->ssl && m_connection->connectionType() != QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
// We upgraded from HTTP/1.1 to HTTP/2. channel->request was already sent
// as HTTP/1.1 request. The response with status code 101 triggered
// protocol switch and now we are waiting for the real response, sent
@@ -1056,6 +1056,7 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader
Qt::ConnectionType connectionType)
{
const auto httpReply = stream.reply();
+ const auto &httpRequest = stream.request();
Q_ASSERT(httpReply || stream.state == Stream::remoteReserved);
if (!httpReply) {
@@ -1115,6 +1116,9 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader
if (QHttpNetworkReply::isHttpRedirect(statusCode) && redirectUrl.isValid())
httpReply->setRedirectUrl(redirectUrl);
+ if (httpReplyPrivate->isCompressed() && httpRequest.d->autoDecompress)
+ httpReplyPrivate->removeAutoDecompressHeader();
+
if (QHttpNetworkReply::isHttpRedirect(statusCode)
|| statusCode == 401 || statusCode == 407) {
// These are the status codes that can trigger uploadByteDevice->reset()
diff --git a/src/network/access/qhttpmultipart.h b/src/network/access/qhttpmultipart.h
index 78585a704d..f718d51d0c 100644
--- a/src/network/access/qhttpmultipart.h
+++ b/src/network/access/qhttpmultipart.h
@@ -61,11 +61,11 @@ public:
QHttpPart(const QHttpPart &other);
~QHttpPart();
#ifdef Q_COMPILER_RVALUE_REFS
- QHttpPart &operator=(QHttpPart &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QHttpPart &operator=(QHttpPart &&other) noexcept { swap(other); return *this; }
#endif
QHttpPart &operator=(const QHttpPart &other);
- void swap(QHttpPart &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QHttpPart &other) noexcept { qSwap(d, other.d); }
bool operator==(const QHttpPart &other) const;
inline bool operator!=(const QHttpPart &other) const
diff --git a/src/network/access/qhttpmultipart_p.h b/src/network/access/qhttpmultipart_p.h
index 363e0b346c..ead1eadf3b 100644
--- a/src/network/access/qhttpmultipart_p.h
+++ b/src/network/access/qhttpmultipart_p.h
@@ -64,7 +64,7 @@ QT_BEGIN_NAMESPACE
class QHttpPartPrivate: public QSharedData, public QNetworkHeadersPrivate
{
public:
- inline QHttpPartPrivate() : bodyDevice(0), headerCreated(false), readPointer(0)
+ inline QHttpPartPrivate() : bodyDevice(nullptr), headerCreated(false), readPointer(0)
{
}
~QHttpPartPrivate()
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index c58fd24a44..0a37122fc6 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -398,11 +398,12 @@ void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthentica
{
Q_ASSERT(auth);
- // NTLM is a multi phase authentication. Copying credentials between authenticators would mess things up.
+ // NTLM and Negotiate do multi-phase authentication.
+ // Copying credentialsbetween authenticators would mess things up.
if (fromChannel >= 0) {
- if (!isProxy && channels[fromChannel].authMethod == QAuthenticatorPrivate::Ntlm)
- return;
- if (isProxy && channels[fromChannel].proxyAuthMethod == QAuthenticatorPrivate::Ntlm)
+ const QHttpNetworkConnectionChannel &channel = channels[fromChannel];
+ const QAuthenticatorPrivate::Method method = isProxy ? channel.proxyAuthMethod : channel.authMethod;
+ if (method == QAuthenticatorPrivate::Ntlm || method == QAuthenticatorPrivate::Negotiate)
return;
}
@@ -592,7 +593,7 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket,
if ((channels[i].authMethod != QAuthenticatorPrivate::Ntlm && request.headerField("Authorization").isEmpty()) || channels[i].lastStatus == 401) {
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator);
if (priv && priv->method != QAuthenticatorPrivate::None) {
- QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false));
+ QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false), request.url().host());
request.setHeaderField("Authorization", response);
channels[i].authenticationCredentialsSent = true;
}
@@ -604,7 +605,7 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket,
if (!(channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) {
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator);
if (priv && priv->method != QAuthenticatorPrivate::None) {
- QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false));
+ QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false), networkProxy.hostName());
request.setHeaderField("Proxy-Authorization", response);
channels[i].proxyCredentialsSent = true;
}
@@ -641,7 +642,7 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor
else { // SPDY, HTTP/2 ('h2' mode)
if (!pair.second->d_func()->requestIsPrepared)
prepareRequest(pair);
- channels[0].spdyRequestsToSend.insertMulti(request.priority(), pair);
+ channels[0].spdyRequestsToSend.insert(request.priority(), pair);
}
#ifndef Q_OS_WINRT
@@ -677,7 +678,7 @@ void QHttpNetworkConnectionPrivate::fillHttp2Queue()
for (auto &pair : highPriorityQueue) {
if (!pair.second->d_func()->requestIsPrepared)
prepareRequest(pair);
- channels[0].spdyRequestsToSend.insertMulti(QHttpNetworkRequest::HighPriority, pair);
+ channels[0].spdyRequestsToSend.insert(QHttpNetworkRequest::HighPriority, pair);
}
highPriorityQueue.clear();
@@ -685,7 +686,7 @@ void QHttpNetworkConnectionPrivate::fillHttp2Queue()
for (auto &pair : lowPriorityQueue) {
if (!pair.second->d_func()->requestIsPrepared)
prepareRequest(pair);
- channels[0].spdyRequestsToSend.insertMulti(pair.first.priority(), pair);
+ channels[0].spdyRequestsToSend.insert(pair.first.priority(), pair);
}
lowPriorityQueue.clear();
@@ -1317,7 +1318,7 @@ QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16
: QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt, connectionType)), parent)
{
Q_D(QHttpNetworkConnection);
- d->networkSession = qMove(networkSession);
+ d->networkSession = std::move(networkSession);
d->init();
}
@@ -1329,7 +1330,7 @@ QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QS
connectionType)), parent)
{
Q_D(QHttpNetworkConnection);
- d->networkSession = qMove(networkSession);
+ d->networkSession = std::move(networkSession);
d->init();
}
#else
@@ -1476,7 +1477,7 @@ QSharedPointer<QSslContext> QHttpNetworkConnection::sslContext()
void QHttpNetworkConnection::setSslContext(QSharedPointer<QSslContext> context)
{
Q_D(QHttpNetworkConnection);
- d->sslContext = qMove(context);
+ d->sslContext = std::move(context);
}
void QHttpNetworkConnection::ignoreSslErrors(int channel)
@@ -1518,6 +1519,18 @@ void QHttpNetworkConnection::preConnectFinished()
d_func()->preConnectRequests--;
}
+QString QHttpNetworkConnection::peerVerifyName() const
+{
+ Q_D(const QHttpNetworkConnection);
+ return d->peerVerifyName;
+}
+
+void QHttpNetworkConnection::setPeerVerifyName(const QString &peerName)
+{
+ Q_D(QHttpNetworkConnection);
+ d->peerVerifyName = peerName;
+}
+
#ifndef QT_NO_NETWORKPROXY
// only called from QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired, not
// from QHttpNetworkConnectionChannel::handleAuthenticationChallenge
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index 91827a6eb1..2f3c334248 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -101,10 +101,10 @@ public:
#ifndef QT_NO_BEARERMANAGEMENT
explicit QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false,
ConnectionType connectionType = ConnectionTypeHTTP,
- QObject *parent = 0, QSharedPointer<QNetworkSession> networkSession
+ QObject *parent = nullptr, QSharedPointer<QNetworkSession> networkSession
= QSharedPointer<QNetworkSession>());
QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80,
- bool encrypt = false, QObject *parent = 0,
+ bool encrypt = false, QObject *parent = nullptr,
QSharedPointer<QNetworkSession> networkSession = QSharedPointer<QNetworkSession>(),
ConnectionType connectionType = ConnectionTypeHTTP);
#else
@@ -154,9 +154,11 @@ public:
void preConnectFinished();
+ QString peerVerifyName() const;
+ void setPeerVerifyName(const QString &peerName);
private:
Q_DECLARE_PRIVATE(QHttpNetworkConnection)
- Q_DISABLE_COPY(QHttpNetworkConnection)
+ Q_DISABLE_COPY_MOVE(QHttpNetworkConnection)
friend class QHttpThreadDelegate;
friend class QHttpNetworkReply;
friend class QHttpNetworkReplyPrivate;
@@ -289,6 +291,8 @@ public:
Http2::ProtocolParameters http2Parameters;
+ QString peerVerifyName;
+
friend class QHttpNetworkConnectionChannel;
};
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 5726925cb0..f79a4d1dc6 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -392,6 +392,7 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
if (!connection->sslContext().isNull())
QSslSocketPrivate::checkSettingSslContext(sslSocket, connection->sslContext());
+ sslSocket->setPeerVerifyName(connection->d_func()->peerVerifyName);
sslSocket->connectToHostEncrypted(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
if (ignoreAllSslErrors)
sslSocket->ignoreSslErrors();
@@ -966,7 +967,10 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
} else if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
// Try to reconnect/resend before sending an error.
// While "Reading" the _q_disconnected() will handle this.
- if (reconnectAttempts-- > 0) {
+ // If we're using ssl then the protocolHandler is not initialized until
+ // "encrypted" has been emitted, since retrying requires the protocolHandler (asserted)
+ // we will not try if encryption is not done.
+ if (!pendingEncrypt && reconnectAttempts-- > 0) {
resendCurrentRequest();
return;
} else {
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
index c9c3172304..a8b635c45a 100644
--- a/src/network/access/qhttpnetworkreply.cpp
+++ b/src/network/access/qhttpnetworkreply.cpp
@@ -444,6 +444,9 @@ QAuthenticatorPrivate::Method QHttpNetworkReplyPrivate::authenticationMethod(boo
} else if (method < QAuthenticatorPrivate::DigestMd5
&& line.startsWith("digest")) {
method = QAuthenticatorPrivate::DigestMd5;
+ } else if (method < QAuthenticatorPrivate::Negotiate
+ && line.startsWith("negotiate")) {
+ method = QAuthenticatorPrivate::Negotiate;
}
}
return method;
diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h
index 863e21ea3e..12cfe359aa 100644
--- a/src/network/access/qhttpnetworkreply_p.h
+++ b/src/network/access/qhttpnetworkreply_p.h
@@ -89,7 +89,7 @@ class Q_AUTOTEST_EXPORT QHttpNetworkReply : public QObject, public QHttpNetworkH
Q_OBJECT
public:
- explicit QHttpNetworkReply(const QUrl &url = QUrl(), QObject *parent = 0);
+ explicit QHttpNetworkReply(const QUrl &url = QUrl(), QObject *parent = nullptr);
virtual ~QHttpNetworkReply();
QUrl url() const override;
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp
index 8de9760710..a3f71b8d2f 100644
--- a/src/network/access/qhttpnetworkrequest.cpp
+++ b/src/network/access/qhttpnetworkrequest.cpp
@@ -66,7 +66,8 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest
ssl(other.ssl),
preConnect(other.preConnect),
redirectCount(other.redirectCount),
- redirectPolicy(other.redirectPolicy)
+ redirectPolicy(other.redirectPolicy),
+ peerVerifyName(other.peerVerifyName)
{
}
@@ -90,7 +91,8 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot
&& (withCredentials == other.withCredentials)
&& (ssl == other.ssl)
&& (preConnect == other.preConnect)
- && (redirectPolicy == other.redirectPolicy);
+ && (redirectPolicy == other.redirectPolicy)
+ && (peerVerifyName == other.peerVerifyName);
}
QByteArray QHttpNetworkRequest::methodName() const
@@ -397,6 +399,15 @@ int QHttpNetworkRequest::minorVersion() const
return 1;
}
+QString QHttpNetworkRequest::peerVerifyName() const
+{
+ return d->peerVerifyName;
+}
+
+void QHttpNetworkRequest::setPeerVerifyName(const QString &peerName)
+{
+ d->peerVerifyName = peerName;
+}
QT_END_NAMESPACE
diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h
index bc797537ae..fb4896195b 100644
--- a/src/network/access/qhttpnetworkrequest_p.h
+++ b/src/network/access/qhttpnetworkrequest_p.h
@@ -147,6 +147,8 @@ public:
QByteArray methodName() const;
QByteArray uri(bool throughProxy) const;
+ QString peerVerifyName() const;
+ void setPeerVerifyName(const QString &peerName);
private:
QSharedDataPointer<QHttpNetworkRequestPrivate> d;
friend class QHttpNetworkRequestPrivate;
@@ -182,6 +184,7 @@ public:
bool preConnect;
int redirectCount;
QNetworkRequest::RedirectPolicy redirectPolicy;
+ QString peerVerifyName;
};
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
index 0e97acdd9d..6fb4710d77 100644
--- a/src/network/access/qhttpthreaddelegate.cpp
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -123,7 +123,7 @@ static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const
}
-static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy)
+static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy, const QString &peerVerifyName)
{
QString result;
QUrl copy = url;
@@ -170,7 +170,8 @@ static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy)
#else
Q_UNUSED(proxy)
#endif
-
+ if (!peerVerifyName.isEmpty())
+ result += QLatin1Char(':') + peerVerifyName;
return "http-connection:" + std::move(result).toLatin1();
}
@@ -188,7 +189,7 @@ public:
QHttpNetworkConnection::ConnectionType connectionType,
QSharedPointer<QNetworkSession> networkSession)
: QHttpNetworkConnection(hostName, port, encrypt, connectionType, /*parent=*/0,
- qMove(networkSession))
+ std::move(networkSession))
#endif
{
setExpires(true);
@@ -317,12 +318,12 @@ void QHttpThreadDelegate::startRequest()
#ifndef QT_NO_NETWORKPROXY
if (transparentProxy.type() != QNetworkProxy::NoProxy)
- cacheKey = makeCacheKey(urlCopy, &transparentProxy);
+ cacheKey = makeCacheKey(urlCopy, &transparentProxy, httpRequest.peerVerifyName());
else if (cacheProxy.type() != QNetworkProxy::NoProxy)
- cacheKey = makeCacheKey(urlCopy, &cacheProxy);
+ cacheKey = makeCacheKey(urlCopy, &cacheProxy, httpRequest.peerVerifyName());
else
#endif
- cacheKey = makeCacheKey(urlCopy, 0);
+ cacheKey = makeCacheKey(urlCopy, 0, httpRequest.peerVerifyName());
// the http object is actually a QHttpNetworkConnection
@@ -352,7 +353,7 @@ void QHttpThreadDelegate::startRequest()
httpConnection->setTransparentProxy(transparentProxy);
httpConnection->setCacheProxy(cacheProxy);
#endif
-
+ httpConnection->setPeerVerifyName(httpRequest.peerVerifyName());
// cache the QHttpNetworkConnection corresponding to this cache key
connections.localData()->addEntry(cacheKey, httpConnection);
} else {
diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h
index 019a8b8b74..6184b39b30 100644
--- a/src/network/access/qhttpthreaddelegate_p.h
+++ b/src/network/access/qhttpthreaddelegate_p.h
@@ -82,7 +82,7 @@ class QHttpThreadDelegate : public QObject
{
Q_OBJECT
public:
- explicit QHttpThreadDelegate(QObject *parent = 0);
+ explicit QHttpThreadDelegate(QObject *parent = nullptr);
~QHttpThreadDelegate();
@@ -207,7 +207,7 @@ public:
: QNonContiguousByteDevice(),
wantDataPending(false),
m_amount(0),
- m_data(0),
+ m_data(nullptr),
m_atEnd(aE),
m_size(s),
m_pos(0)
@@ -240,12 +240,12 @@ public:
// Do nothing, we already sent a wantData signal and wait for results
len = 0;
}
- return 0;
+ return nullptr;
}
bool advanceReadPointer(qint64 a) override
{
- if (m_data == 0)
+ if (m_data == nullptr)
return false;
m_amount -= a;
@@ -269,7 +269,7 @@ public:
bool reset() override
{
m_amount = 0;
- m_data = 0;
+ m_data = nullptr;
m_dataArray.clear();
if (wantDataPending) {
diff --git a/src/network/access/qnetworkaccessauthenticationmanager_p.h b/src/network/access/qnetworkaccessauthenticationmanager_p.h
index 548675728f..31111ca2a5 100644
--- a/src/network/access/qnetworkaccessauthenticationmanager_p.h
+++ b/src/network/access/qnetworkaccessauthenticationmanager_p.h
@@ -90,12 +90,12 @@ public:
void cacheCredentials(const QUrl &url, const QAuthenticator *auth);
QNetworkAuthenticationCredential fetchCachedCredentials(const QUrl &url,
- const QAuthenticator *auth = 0);
+ const QAuthenticator *auth = nullptr);
#ifndef QT_NO_NETWORKPROXY
void cacheProxyCredentials(const QNetworkProxy &proxy, const QAuthenticator *auth);
QNetworkAuthenticationCredential fetchCachedProxyCredentials(const QNetworkProxy &proxy,
- const QAuthenticator *auth = 0);
+ const QAuthenticator *auth = nullptr);
#endif
void clearCache();
diff --git a/src/network/access/qnetworkaccesscachebackend.cpp b/src/network/access/qnetworkaccesscachebackend.cpp
index 0c9a88596d..22fdc5bb0b 100644
--- a/src/network/access/qnetworkaccesscachebackend.cpp
+++ b/src/network/access/qnetworkaccesscachebackend.cpp
@@ -87,15 +87,16 @@ bool QNetworkAccessCacheBackend::sendCacheContents()
setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, attributes.value(QNetworkRequest::HttpReasonPhraseAttribute));
// set the raw headers
- QNetworkCacheMetaData::RawHeaderList rawHeaders = item.rawHeaders();
- QNetworkCacheMetaData::RawHeaderList::ConstIterator it = rawHeaders.constBegin(),
- end = rawHeaders.constEnd();
- for ( ; it != end; ++it) {
- if (it->first.toLower() == "cache-control" &&
- it->second.toLower().contains("must-revalidate")) {
- return false;
+ const QNetworkCacheMetaData::RawHeaderList rawHeaders = item.rawHeaders();
+ for (const auto &header : rawHeaders) {
+ if (header.first.toLower() == "cache-control") {
+ const QByteArray cacheControlValue = header.second.toLower();
+ if (cacheControlValue.contains("must-revalidate")
+ || cacheControlValue.contains("no-cache")) {
+ return false;
+ }
}
- setRawHeader(it->first, it->second);
+ setRawHeader(header.first, header.second);
}
// handle a possible redirect
diff --git a/src/network/access/qnetworkaccessftpbackend.cpp b/src/network/access/qnetworkaccessftpbackend.cpp
index fd6589b396..5ad820eba0 100644
--- a/src/network/access/qnetworkaccessftpbackend.cpp
+++ b/src/network/access/qnetworkaccessftpbackend.cpp
@@ -351,7 +351,7 @@ void QNetworkAccessFtpBackend::ftpDone()
}
} else if (state == Statting) {
// statted successfully, send the actual request
- emit metaDataChanged();
+ metaDataChanged();
state = Transferring;
QFtp::TransferType type = QFtp::Binary;
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index 263469ce38..50b9488594 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -494,8 +494,8 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
//
connect(&d->networkConfigurationManager, SIGNAL(onlineStateChanged(bool)),
SLOT(_q_onlineStateChanged(bool)));
- connect(&d->networkConfigurationManager, SIGNAL(configurationChanged(const QNetworkConfiguration &)),
- SLOT(_q_configurationChanged(const QNetworkConfiguration &)));
+ connect(&d->networkConfigurationManager, SIGNAL(configurationChanged(QNetworkConfiguration)),
+ SLOT(_q_configurationChanged(QNetworkConfiguration)));
#endif
}
@@ -1181,9 +1181,37 @@ QSharedPointer<QNetworkSession> QNetworkAccessManagerPrivate::getNetworkSession(
\sa connectToHost(), get(), post(), put(), deleteResource()
*/
+
void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quint16 port,
const QSslConfiguration &sslConfiguration)
{
+ connectToHostEncrypted(hostName, port, sslConfiguration, QString());
+}
+
+/*!
+ \since 5.13
+ \overload
+
+ Initiates a connection to the host given by \a hostName at port \a port, using
+ \a sslConfiguration with \a peerName set to be the hostName used for certificate
+ validation. This function is useful to complete the TCP and SSL handshake
+ to a host before the HTTPS request is made, resulting in a lower network latency.
+
+ \note Preconnecting a SPDY connection can be done by calling setAllowedNextProtocols()
+ on \a sslConfiguration with QSslConfiguration::NextProtocolSpdy3_0 contained in
+ the list of allowed protocols. When using SPDY, one single connection per host is
+ enough, i.e. calling this method multiple times per host will not result in faster
+ network transactions.
+
+ \note This function has no possibility to report errors.
+
+ \sa connectToHost(), get(), post(), put(), deleteResource()
+*/
+
+void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quint16 port,
+ const QSslConfiguration &sslConfiguration,
+ const QString &peerName)
+{
QUrl url;
url.setHost(hostName);
url.setPort(port);
@@ -1198,6 +1226,7 @@ void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quin
QSslConfiguration::NextProtocolSpdy3_0))
request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true);
+ request.setPeerVerifyName(peerName);
get(request);
}
#endif
@@ -1361,7 +1390,8 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
QString scheme = req.url().scheme();
#ifdef Q_OS_WASM
- if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) {
+ // Support http, https, and relateive urls
+ if (scheme == QLatin1String("http") || scheme == QLatin1String("https") || scheme.isEmpty()) {
QNetworkReplyWasmImpl *reply = new QNetworkReplyWasmImpl(this);
QNetworkReplyWasmImplPrivate *priv = reply->d_func();
priv->manager = this;
@@ -1848,6 +1878,7 @@ void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &co
if (config.isValid())
newSession = QSharedNetworkSessionManager::getSession(config);
+ QNetworkSession::State oldState = QNetworkSession::Invalid;
if (networkSessionStrongRef) {
//do nothing if new and old session are the same
if (networkSessionStrongRef == newSession)
@@ -1859,6 +1890,7 @@ void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &co
q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)));
QObject::disconnect(networkSessionStrongRef.data(), SIGNAL(error(QNetworkSession::SessionError)),
q, SLOT(_q_networkSessionFailed(QNetworkSession::SessionError)));
+ oldState = networkSessionStrongRef->state();
}
//switch to new session (null if config was invalid)
@@ -1884,7 +1916,11 @@ void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &co
QObject::connect(networkSessionStrongRef.data(), SIGNAL(error(QNetworkSession::SessionError)),
q, SLOT(_q_networkSessionFailed(QNetworkSession::SessionError)));
- _q_networkSessionStateChanged(networkSessionStrongRef->state());
+ const QNetworkSession::State newState = networkSessionStrongRef->state();
+ if (newState != oldState) {
+ QMetaObject::invokeMethod(q, "_q_networkSessionStateChanged", Qt::QueuedConnection,
+ Q_ARG(QNetworkSession::State, newState));
+ }
}
void QNetworkAccessManagerPrivate::_q_networkSessionClosed()
diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h
index 67b3a8b71b..7e2f7683d0 100644
--- a/src/network/access/qnetworkaccessmanager.h
+++ b/src/network/access/qnetworkaccessmanager.h
@@ -158,6 +158,9 @@ public:
#ifndef QT_NO_SSL
void connectToHostEncrypted(const QString &hostName, quint16 port = 443,
const QSslConfiguration &sslConfiguration = QSslConfiguration::defaultConfiguration());
+ void connectToHostEncrypted(const QString &hostName, quint16 port,
+ const QSslConfiguration &sslConfiguration,
+ const QString &peerName);
#endif
void connectToHost(const QString &hostName, quint16 port = 80);
diff --git a/src/network/access/qnetworkcookie.h b/src/network/access/qnetworkcookie.h
index e462b98555..58c504f9ae 100644
--- a/src/network/access/qnetworkcookie.h
+++ b/src/network/access/qnetworkcookie.h
@@ -67,11 +67,11 @@ public:
QNetworkCookie(const QNetworkCookie &other);
~QNetworkCookie();
#ifdef Q_COMPILER_RVALUE_REFS
- QNetworkCookie &operator=(QNetworkCookie &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QNetworkCookie &operator=(QNetworkCookie &&other) noexcept { swap(other); return *this; }
#endif
QNetworkCookie &operator=(const QNetworkCookie &other);
- void swap(QNetworkCookie &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QNetworkCookie &other) noexcept { qSwap(d, other.d); }
bool operator==(const QNetworkCookie &other) const;
inline bool operator!=(const QNetworkCookie &other) const
diff --git a/src/network/access/qnetworkdiskcache_p.h b/src/network/access/qnetworkdiskcache_p.h
index f7988e7dda..c797e63830 100644
--- a/src/network/access/qnetworkdiskcache_p.h
+++ b/src/network/access/qnetworkdiskcache_p.h
@@ -67,7 +67,7 @@ class QFile;
class QCacheItem
{
public:
- QCacheItem() : file(0)
+ QCacheItem() : file(nullptr)
{
}
~QCacheItem()
@@ -85,7 +85,7 @@ public:
metaData = QNetworkCacheMetaData();
data.close();
delete file;
- file = 0;
+ file = nullptr;
}
void writeHeader(QFile *device) const;
void writeCompressedData(QFile *device) const;
diff --git a/src/network/access/qnetworkfile.cpp b/src/network/access/qnetworkfile.cpp
index 374dd26e2e..b7c91f28d8 100644
--- a/src/network/access/qnetworkfile.cpp
+++ b/src/network/access/qnetworkfile.cpp
@@ -65,21 +65,21 @@ void QNetworkFile::open()
if (fi.isDir()) {
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend",
"Cannot open %1: Path is a directory").arg(fileName());
- error(QNetworkReply::ContentOperationNotPermittedError, msg);
+ emit error(QNetworkReply::ContentOperationNotPermittedError, msg);
} else {
- headerRead(QNetworkRequest::LastModifiedHeader, QVariant::fromValue(fi.lastModified()));
- headerRead(QNetworkRequest::ContentLengthHeader, QVariant::fromValue(fi.size()));
+ emit headerRead(QNetworkRequest::LastModifiedHeader, QVariant::fromValue(fi.lastModified()));
+ emit headerRead(QNetworkRequest::ContentLengthHeader, QVariant::fromValue(fi.size()));
opened = QFile::open(QIODevice::ReadOnly | QIODevice::Unbuffered);
if (!opened) {
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend",
"Error opening %1: %2").arg(fileName(), errorString());
if (exists())
- error(QNetworkReply::ContentAccessDenied, msg);
+ emit error(QNetworkReply::ContentAccessDenied, msg);
else
- error(QNetworkReply::ContentNotFoundError, msg);
+ emit error(QNetworkReply::ContentNotFoundError, msg);
}
}
- finished(opened);
+ emit finished(opened);
}
void QNetworkFile::close()
diff --git a/src/network/access/qnetworkreplyfileimpl_p.h b/src/network/access/qnetworkreplyfileimpl_p.h
index 55aece0bed..48d82abd3f 100644
--- a/src/network/access/qnetworkreplyfileimpl_p.h
+++ b/src/network/access/qnetworkreplyfileimpl_p.h
@@ -80,6 +80,7 @@ public:
private Q_SLOTS:
void fileOpenFinished(bool isOpen);
+private:
Q_DECLARE_PRIVATE(QNetworkReplyFileImpl)
};
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index 8750a841f6..f801ef0c88 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -524,6 +524,8 @@ bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &h
QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
if (cacheControl.contains("must-revalidate"))
return false;
+ if (cacheControl.contains("no-cache"))
+ return false;
}
QDateTime currentDateTime = QDateTime::currentDateTimeUtc();
@@ -785,6 +787,7 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
if (request.attribute(QNetworkRequest::EmitAllUploadProgressSignalsAttribute).toBool())
emitAllUploadProgressSignals = true;
+ httpRequest.setPeerVerifyName(newHttpRequest.peerVerifyName());
// Create the HTTP thread delegate
QHttpThreadDelegate *delegate = new QHttpThreadDelegate;
@@ -917,7 +920,7 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
// From http thread to user thread:
QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)),
q, SLOT(wantUploadDataSlot(qint64)));
- QObject::connect(forwardUploadDevice,SIGNAL(processedData(qint64, qint64)),
+ QObject::connect(forwardUploadDevice,SIGNAL(processedData(qint64,qint64)),
q, SLOT(sentUploadDataSlot(qint64,qint64)));
QObject::connect(forwardUploadDevice, SIGNAL(resetData(bool*)),
q, SLOT(resetUploadDataSlot(bool*)),
@@ -1561,7 +1564,7 @@ bool QNetworkReplyHttpImplPrivate::sendCacheContents(const QNetworkCacheMetaData
QIODevice *contents = nc->data(url);
if (!contents) {
#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
- qDebug() << "Can not send cache, the contents are 0" << url;
+ qDebug() << "Cannot send cache, the contents are 0" << url;
#endif
return false;
}
@@ -1676,13 +1679,13 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
|| header == "content-range"
|| header == "content-type")
continue;
-
- // For MS servers that send "Content-Length: 0" on 304 responses
- // ignore this too
- if (header == "content-length")
- continue;
}
+ // IIS has been known to send "Content-Length: 0" on 304 responses, so
+ // ignore this too
+ if (header == "content-length" && statusCode == 304)
+ continue;
+
#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
QByteArray n = q->rawHeader(header);
QByteArray o;
@@ -1730,18 +1733,8 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
if (httpRequest.operation() == QHttpNetworkRequest::Get) {
canDiskCache = true;
- // 14.32
- // HTTP/1.1 caches SHOULD treat "Pragma: no-cache" as if the client
- // had sent "Cache-Control: no-cache".
- it = cacheHeaders.findRawHeader("pragma");
- if (it != cacheHeaders.rawHeaders.constEnd()
- && it->second == "no-cache")
- canDiskCache = false;
-
// HTTP/1.1. Check the Cache-Control header
- if (cacheControl.contains("no-cache"))
- canDiskCache = false;
- else if (cacheControl.contains("no-store"))
+ if (cacheControl.contains("no-store"))
canDiskCache = false;
// responses to POST might be cacheable
@@ -1879,11 +1872,9 @@ void QNetworkReplyHttpImplPrivate::_q_startOperation()
{
Q_Q(QNetworkReplyHttpImpl);
- // ensure this function is only being called once
- if (state == Working) {
- qDebug() << "QNetworkReplyHttpImplPrivate::_q_startOperation was called more than once" << url;
+ if (state == Working) // ensure this function is only being called once
return;
- }
+
state = Working;
#ifndef QT_NO_BEARERMANAGEMENT
diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h
index f4e8284ab6..4881e84e9c 100644
--- a/src/network/access/qnetworkreplyimpl_p.h
+++ b/src/network/access/qnetworkreplyimpl_p.h
@@ -74,7 +74,7 @@ class QNetworkReplyImpl: public QNetworkReply
{
Q_OBJECT
public:
- QNetworkReplyImpl(QObject *parent = 0);
+ QNetworkReplyImpl(QObject *parent = nullptr);
~QNetworkReplyImpl();
virtual void abort() override;
diff --git a/src/network/access/qnetworkreplywasmimpl.cpp b/src/network/access/qnetworkreplywasmimpl.cpp
index 23ca62acd4..bb6ef07741 100644
--- a/src/network/access/qnetworkreplywasmimpl.cpp
+++ b/src/network/access/qnetworkreplywasmimpl.cpp
@@ -102,6 +102,8 @@ static void q_loadCallback(val event)
return;
}
QString statusText = QString::fromStdString(xhr["statusText"].as<std::string>());
+ int readyState = xhr["readyState"].as<int>();
+
if (status == 200 || status == 203) {
QString responseString;
const std::string responseType = xhr["responseType"].as<std::string>();
@@ -112,13 +114,15 @@ static void q_loadCallback(val event)
QString::fromStdWString(val::global("JSON").call<std::wstring>("stringify", xhr["response"]));
} else if (responseType == "arraybuffer" || responseType == "blob") {
// handle this data in the FileReader, triggered by the call to readAsArrayBuffer
+ val blob = xhr["response"];
+
val reader = val::global("FileReader").new_();
- reader.set("onload", val::module_property("QNetworkReplyWasmImplPrivate_readBinary"));
+ reader.set("onload", val::module_property("qt_QNetworkReplyWasmImplPrivate_readBinary"));
reader.set("data-handler", xhr["data-handler"]);
- reader.call<void>("readAsArrayBuffer", xhr["response"]);
+
+ reader.call<void>("readAsArrayBuffer", blob);
}
- int readyState = xhr["readyState"].as<int>();
if (readyState == 4) { // done
reply->setReplyAttributes(xhr["data-handler"].as<quintptr>(), status, statusText);
@@ -167,15 +171,15 @@ static void q_readBinary(val event)
reinterpret_cast<quintptr>(buffer.data()), size);
destinationTypedArray.call<void>("set", sourceTypedArray);
reply->dataReceived(buffer, buffer.size());
+ QCoreApplication::processEvents();
}
-
-EMSCRIPTEN_BINDINGS(network_module) {
- function("QNetworkReplyWasmImplPrivate_requestErrorCallback", q_requestErrorCallback);
- function("QNetworkReplyWasmImplPrivate_progressCallback", q_progressCallback);
- function("QNetworkReplyWasmImplPrivate_loadCallback", q_loadCallback);
- function("QNetworkReplyWasmImplPrivate_responseHeadersCallback", q_responseHeadersCallback);
- function("QNetworkReplyWasmImplPrivate_readBinary", q_readBinary);
+EMSCRIPTEN_BINDINGS(qtNetworkModule) {
+ function("qt_QNetworkReplyWasmImplPrivate_requestErrorCallback", q_requestErrorCallback);
+ function("qt_QNetworkReplyWasmImplPrivate_progressCallback", q_progressCallback);
+ function("qt_QNetworkReplyWasmImplPrivate_loadCallback", q_loadCallback);
+ function("qt_QNetworkReplyWasmImplPrivate_responseHeadersCallback", q_responseHeadersCallback);
+ function("qt_QNetworkReplyWasmImplPrivate_readBinary", q_readBinary);
}
QNetworkReplyWasmImplPrivate::QNetworkReplyWasmImplPrivate()
@@ -222,11 +226,18 @@ QByteArray QNetworkReplyWasmImpl::methodName() const
void QNetworkReplyWasmImpl::close()
{
+ setFinished(true);
+ emit finished();
+
QNetworkReply::close();
}
void QNetworkReplyWasmImpl::abort()
{
+ Q_D(const QNetworkReplyWasmImpl);
+ setError( QNetworkReply::OperationCanceledError, "Operation canceled" );
+ d->doAbort();
+
close();
}
@@ -234,9 +245,6 @@ qint64 QNetworkReplyWasmImpl::bytesAvailable() const
{
Q_D(const QNetworkReplyWasmImpl);
- if (!d->isFinished)
- return QNetworkReply::bytesAvailable();
-
return QNetworkReply::bytesAvailable() + d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
}
@@ -258,7 +266,7 @@ qint64 QNetworkReplyWasmImpl::readData(char *data, qint64 maxlen)
Q_D(QNetworkReplyWasmImpl);
qint64 howMuch = qMin(maxlen, (d->downloadBuffer.size() - d->downloadBufferReadPosition));
- memcpy(data, d->downloadBuffer.constData(), howMuch);
+ memcpy(data, d->downloadBuffer.constData() + d->downloadBufferReadPosition, howMuch);
d->downloadBufferReadPosition += howMuch;
return howMuch;
@@ -307,32 +315,29 @@ void QNetworkReplyWasmImplPrivate::setReplyAttributes(quintptr data, int statusC
handler->q_func()->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, statusReason);
}
+void QNetworkReplyWasmImplPrivate::doAbort() const
+{
+ m_xhr.call<void>("abort");
+}
+
void QNetworkReplyWasmImplPrivate::doSendRequest()
{
Q_Q(QNetworkReplyWasmImpl);
totalDownloadSize = 0;
- val xhr = val::global("XMLHttpRequest").new_();
+ m_xhr = val::global("XMLHttpRequest").new_();
std::string verb = q->methodName().toStdString();
- QUrl url;
QString extraDataString;
- if (request.url().hasQuery()) { //strip query from url
- extraDataString = request.url().query(QUrl::FullyEncoded);
- QString urlStr = request.url().toString();
- url.setUrl(urlStr.left(urlStr.indexOf("?")));
- } else {
- url = request.url();
- }
- xhr.call<void>("open", verb, url.toString().toStdString());
+ m_xhr.call<void>("open", verb, request.url().toString().toStdString());
- xhr.set("onerror", val::module_property("QNetworkReplyWasmImplPrivate_requestErrorCallback"));
- xhr.set("onload", val::module_property("QNetworkReplyWasmImplPrivate_loadCallback"));
- xhr.set("onprogress", val::module_property("QNetworkReplyWasmImplPrivate_progressCallback"));
- xhr.set("onreadystatechange", val::module_property("QNetworkReplyWasmImplPrivate_responseHeadersCallback"));
+ m_xhr.set("onerror", val::module_property("qt_QNetworkReplyWasmImplPrivate_requestErrorCallback"));
+ m_xhr.set("onload", val::module_property("qt_QNetworkReplyWasmImplPrivate_loadCallback"));
+ m_xhr.set("onprogress", val::module_property("qt_QNetworkReplyWasmImplPrivate_progressCallback"));
+ m_xhr.set("onreadystatechange", val::module_property("qt_QNetworkReplyWasmImplPrivate_responseHeadersCallback"));
- xhr.set("data-handler", val(quintptr(reinterpret_cast<void *>(this))));
+ m_xhr.set("data-handler", val(quintptr(reinterpret_cast<void *>(this))));
QByteArray contentType = request.rawHeader("Content-Type");
@@ -351,11 +356,10 @@ void QNetworkReplyWasmImplPrivate::doSendRequest()
}
if (contentType.contains("json")) {
if (!extraDataString.isEmpty()) {
- xhr.set("responseType", val("json"));
+ m_xhr.set("responseType", val("json"));
dataToSend = val(extraDataString.toStdString());
}
- }
- if (contentType.contains("form")) { //construct form data
+ } else if (contentType.contains("form")) { //construct form data
if (!extraDataString.isEmpty()) {
val formData = val::global("FormData").new_();
QStringList formList = extraDataString.split('&');
@@ -365,12 +369,14 @@ void QNetworkReplyWasmImplPrivate::doSendRequest()
}
dataToSend = formData;
}
+ } else {
+ m_xhr.set("responseType", val("blob"));
}
// set request headers
for (auto header : request.rawHeaderList()) {
- xhr.call<void>("setRequestHeader", header.toStdString(), request.rawHeader(header).toStdString());
+ m_xhr.call<void>("setRequestHeader", header.toStdString(), request.rawHeader(header).toStdString());
}
- xhr.call<void>("send", dataToSend);
+ m_xhr.call<void>("send", dataToSend);
}
void QNetworkReplyWasmImplPrivate::emitReplyError(QNetworkReply::NetworkError errorCode, const QString &errorString)
@@ -414,10 +420,13 @@ void QNetworkReplyWasmImplPrivate::dataReceived(const QByteArray &buffer, int bu
downloadBuffer.append(buffer, bufferSize);
+ emit q->readyRead();
+
if (downloadBufferCurrentSize == totalDownloadSize) {
- q->setFinished(true);
- emit q->finished();
- }
+ q->setFinished(true);
+ emit q->readChannelFinished();
+ emit q->finished();
+ }
}
//taken from qnetworkrequest.cpp
diff --git a/src/network/access/qnetworkreplywasmimpl_p.h b/src/network/access/qnetworkreplywasmimpl_p.h
index 69c90de41a..e1e6bf4e24 100644
--- a/src/network/access/qnetworkreplywasmimpl_p.h
+++ b/src/network/access/qnetworkreplywasmimpl_p.h
@@ -62,6 +62,7 @@
#include <emscripten.h>
#include <emscripten/html5.h>
+#include <emscripten/val.h>
QT_BEGIN_NAMESPACE
@@ -134,6 +135,9 @@ public:
QIODevice *outgoingData;
QSharedPointer<QRingBuffer> outgoingDataBuffer;
+ emscripten::val m_xhr = emscripten::val::null();
+ void doAbort() const;
+
static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url);
Q_DECLARE_PUBLIC(QNetworkReplyWasmImpl)
};
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index 689eecfbb9..f15c43cdd8 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -438,6 +438,7 @@ public:
if (other.sslConfiguration)
sslConfiguration = new QSslConfiguration(*other.sslConfiguration);
#endif
+ peerVerifyName = other.peerVerifyName;
}
inline bool operator==(const QNetworkRequestPrivate &other) const
@@ -446,7 +447,8 @@ public:
priority == other.priority &&
rawHeaders == other.rawHeaders &&
attributes == other.attributes &&
- maxRedirectsAllowed == other.maxRedirectsAllowed;
+ maxRedirectsAllowed == other.maxRedirectsAllowed &&
+ peerVerifyName == other.peerVerifyName;
// don't compare cookedHeaders
}
@@ -456,6 +458,7 @@ public:
mutable QSslConfiguration *sslConfiguration;
#endif
int maxRedirectsAllowed;
+ QString peerVerifyName;
};
/*!
@@ -714,7 +717,7 @@ void QNetworkRequest::setOriginatingObject(QObject *object)
\since 4.6
Returns a reference to the object that initiated this
- network request; returns 0 if not set or the object has
+ network request; returns \nullptr if not set or the object has
been destroyed.
\sa setOriginatingObject()
@@ -789,6 +792,32 @@ void QNetworkRequest::setMaximumRedirectsAllowed(int maxRedirectsAllowed)
d->maxRedirectsAllowed = maxRedirectsAllowed;
}
+/*!
+ \since 5.13
+
+ Returns the host name set for the certificate validation, as set by
+ setPeerVerifyName. By default this returns a null string.
+
+ \sa setPeerVerifyName
+*/
+QString QNetworkRequest::peerVerifyName() const
+{
+ return d->peerVerifyName;
+}
+
+/*!
+ \since 5.13
+
+ Sets \a peerName as host name for the certificate validation, instead of the one used for the
+ TCP connection.
+
+ \sa peerVerifyName
+*/
+void QNetworkRequest::setPeerVerifyName(const QString &peerName)
+{
+ d->peerVerifyName = peerName;
+}
+
static QByteArray headerName(QNetworkRequest::KnownHeaders header)
{
switch (header) {
diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h
index 8462eae8c8..2515ff6ead 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -131,11 +131,11 @@ public:
QNetworkRequest(const QNetworkRequest &other);
~QNetworkRequest();
#ifdef Q_COMPILER_RVALUE_REFS
- QNetworkRequest &operator=(QNetworkRequest &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QNetworkRequest &operator=(QNetworkRequest &&other) noexcept { swap(other); return *this; }
#endif
QNetworkRequest &operator=(const QNetworkRequest &other);
- void swap(QNetworkRequest &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QNetworkRequest &other) noexcept { qSwap(d, other.d); }
bool operator==(const QNetworkRequest &other) const;
inline bool operator!=(const QNetworkRequest &other) const
@@ -173,6 +173,8 @@ public:
int maximumRedirectsAllowed() const;
void setMaximumRedirectsAllowed(int maximumRedirectsAllowed);
+ QString peerVerifyName() const;
+ void setPeerVerifyName(const QString &peerName);
private:
QSharedDataPointer<QNetworkRequestPrivate> d;
friend class QNetworkRequestPrivate;
diff --git a/src/network/bearer/qbearerengine_p.h b/src/network/bearer/qbearerengine_p.h
index 5fc2578a78..a5a020a857 100644
--- a/src/network/bearer/qbearerengine_p.h
+++ b/src/network/bearer/qbearerengine_p.h
@@ -77,7 +77,7 @@ class Q_NETWORK_EXPORT QBearerEngine : public QObject
friend class QNetworkConfigurationManagerPrivate;
public:
- explicit QBearerEngine(QObject *parent = 0);
+ explicit QBearerEngine(QObject *parent = nullptr);
virtual ~QBearerEngine();
virtual bool hasIdentifier(const QString &id) = 0;
diff --git a/src/network/bearer/qbearerplugin_p.h b/src/network/bearer/qbearerplugin_p.h
index 0cdde3c06c..ac787d0541 100644
--- a/src/network/bearer/qbearerplugin_p.h
+++ b/src/network/bearer/qbearerplugin_p.h
@@ -68,7 +68,7 @@ class Q_NETWORK_EXPORT QBearerEnginePlugin : public QObject
{
Q_OBJECT
public:
- explicit QBearerEnginePlugin(QObject *parent = 0);
+ explicit QBearerEnginePlugin(QObject *parent = nullptr);
virtual ~QBearerEnginePlugin();
virtual QBearerEngine *create(const QString &key) const = 0;
diff --git a/src/network/bearer/qnetworkconfiguration.cpp b/src/network/bearer/qnetworkconfiguration.cpp
index 3367de80c8..f5ced0693a 100644
--- a/src/network/bearer/qnetworkconfiguration.cpp
+++ b/src/network/bearer/qnetworkconfiguration.cpp
@@ -347,6 +347,8 @@ int QNetworkConfiguration::connectTimeout() const
This allows control of the timeout used by \c QAbstractSocket
to establish a connection.
+ \note \a timeout is in millisecond.
+
\warning This will have no effect if the bearer plugin doesn't have
the CanStartAndStopInterfaces capability.
@@ -516,7 +518,7 @@ QNetworkConfiguration::BearerType QNetworkConfiguration::bearerTypeFamily() cons
/*!
Returns the type of bearer used by this network configuration as a string.
- The string is not translated and therefore can not be shown to the user. The subsequent table
+ The string is not translated and therefore cannot be shown to the user. The subsequent table
shows the fixed mappings between BearerType and the bearer type name for known types. If the
BearerType is unknown this function may return additional information if it is available;
otherwise an empty string will be returned.
diff --git a/src/network/bearer/qnetworkconfiguration.h b/src/network/bearer/qnetworkconfiguration.h
index e7b74034fc..41b6e6f020 100644
--- a/src/network/bearer/qnetworkconfiguration.h
+++ b/src/network/bearer/qnetworkconfiguration.h
@@ -56,12 +56,12 @@ public:
QNetworkConfiguration();
QNetworkConfiguration(const QNetworkConfiguration& other);
#ifdef Q_COMPILER_RVALUE_REFS
- QNetworkConfiguration &operator=(QNetworkConfiguration &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QNetworkConfiguration &operator=(QNetworkConfiguration &&other) noexcept { swap(other); return *this; }
#endif
QNetworkConfiguration &operator=(const QNetworkConfiguration &other);
~QNetworkConfiguration();
- void swap(QNetworkConfiguration &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QNetworkConfiguration &other) noexcept { qSwap(d, other.d); }
bool operator==(const QNetworkConfiguration &other) const;
inline bool operator!=(const QNetworkConfiguration &other) const
diff --git a/src/network/bearer/qnetworkconfiguration_p.h b/src/network/bearer/qnetworkconfiguration_p.h
index 2fdb490ea0..1b1ece39b7 100644
--- a/src/network/bearer/qnetworkconfiguration_p.h
+++ b/src/network/bearer/qnetworkconfiguration_p.h
@@ -97,7 +97,7 @@ public:
static Q_CONSTEXPR int DefaultTimeout = 30000;
private:
- Q_DISABLE_COPY(QNetworkConfigurationPrivate)
+ Q_DISABLE_COPY_MOVE(QNetworkConfigurationPrivate)
};
QT_END_NAMESPACE
diff --git a/src/network/bearer/qnetworksession.cpp b/src/network/bearer/qnetworksession.cpp
index e5562e3a0b..471d322998 100644
--- a/src/network/bearer/qnetworksession.cpp
+++ b/src/network/bearer/qnetworksession.cpp
@@ -323,11 +323,7 @@ void QNetworkSession::open()
The following example waits up to one second for the session to be opened:
- \code
- session->open();
- if (session->waitForOpened(1000))
- qDebug("Open!");
- \endcode
+ \snippet code/src_network_bearer_qnetworksession.cpp 0
If \a msecs is -1, this function will not time out.
@@ -491,20 +487,7 @@ QString QNetworkSession::errorString() const
The main purpose of this key is to determine which Internet access point is used
if the session is based on a \l{QNetworkConfiguration::ServiceNetwork}{ServiceNetwork}.
The following code snippet highlights the difference:
- \code
- QNetworkConfigurationManager mgr;
- QNetworkConfiguration ap = mgr.defaultConfiguration();
- QNetworkSession *session = new QNetworkSession(ap);
- ... //code activates session
-
- QString ident = session->sessionProperty("ActiveConfiguration").toString();
- if ( ap.type() == QNetworkConfiguration::ServiceNetwork ) {
- Q_ASSERT( ap.identifier() != ident );
- Q_ASSERT( ap.children().contains( mgr.configurationFromIdentifier(ident) ) );
- } else if ( ap.type() == QNetworkConfiguration::InternetAccessPoint ) {
- Q_ASSERT( ap.identifier() == ident );
- }
- \endcode
+ \snippet code/src_network_bearer_qnetworksession.cpp 1
\row
\li UserChoiceConfiguration
\li If the session \l isOpen() and is bound to a QNetworkConfiguration of type
diff --git a/src/network/configure.cmake b/src/network/configure.cmake
index 38a3ff3099..0bb54a6f7d 100644
--- a/src/network/configure.cmake
+++ b/src/network/configure.cmake
@@ -108,6 +108,25 @@ socklen_t sctpInitMsgSize = sizeof(sctpInitMsg);
"# FIXME: use: network
)
+# openssl11
+qt_config_compile_test(openssl11
+ LABEL "OpenSSL 1.1 support"
+"
+#include <openssl/opensslv.h>
+#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER-0 < 0x10100000L
+# error OpenSSL >= 1.1 is required
+#endif
+int main(int argc, char **argv)
+{
+ (void)argc; (void)argv;
+ /* BEGIN TEST: */
+
+ /* END TEST: */
+ return 0;
+}
+"# FIXME: use: openssl
+)
+
# dtls
qt_config_compile_test(dtls
LABEL "DTLS support in OpenSSL"
@@ -127,6 +146,43 @@ int main(int argc, char **argv)
"# FIXME: use: openssl
)
+# ocsp
+qt_config_compile_test(ocsp
+ LABEL "OCSP stapling support in OpenSSL"
+"
+#include <openssl/ssl.h>
+#include <openssl/ocsp.h>
+#if defined(OPENSSL_NO_OCSP) || defined(OPENSSL_NO_TLSEXT)
+# error OpenSSL without OCSP stapling
+#endif
+int main(int argc, char **argv)
+{
+ (void)argc; (void)argv;
+ /* BEGIN TEST: */
+
+ /* END TEST: */
+ return 0;
+}
+"# FIXME: use: openssl
+)
+
+# gssapi
+qt_config_compile_test(gssapi
+ LABEL "KRB5 GSSAPI support"
+"
+#include <gssapi/gssapi.h>
+
+int main(int argc, char **argv)
+{
+ (void)argc; (void)argv;
+ /* BEGIN TEST: */
+gss_ctx_id_t ctx;
+ /* END TEST: */
+ return 0;
+}
+"# FIXME: qmake: LIBS += -lgssapi_krb5
+)
+
#### Features
@@ -158,35 +214,51 @@ qt_feature("linux_netlink" PRIVATE
)
qt_feature("openssl" PRIVATE
LABEL "OpenSSL"
- AUTODETECT NOT WINRT AND NOT WASM
- CONDITION NOT QT_FEATURE_securetransport AND ( QT_FEATURE_openssl_linked OR OPENSSL_INCLUDE_DIR )
- ENABLE INPUT_openssl STREQUAL 'yes' OR INPUT_openssl STREQUAL 'linked' OR INPUT_openssl STREQUAL 'runtime'
- DISABLE INPUT_openssl STREQUAL 'no' OR INPUT_ssl STREQUAL 'no'
+ CONDITION QT_FEATURE_openssl_runtime OR QT_FEATURE_openssl_linked
+ ENABLE false
)
qt_feature_definition("openssl" "QT_NO_OPENSSL" NEGATE)
+qt_feature("openssl_runtime"
+ AUTODETECT NOT WINRT AND NOT WASM
+ CONDITION NOT QT_FEATURE_securetransport AND NOT QT_FEATURE_schannel AND OPENSSL_INCLUDE_DIR
+ ENABLE INPUT_openssl STREQUAL 'yes' OR INPUT_openssl STREQUAL 'runtime'
+ DISABLE INPUT_openssl STREQUAL 'no' OR INPUT_openssl STREQUAL 'linked' OR INPUT_ssl STREQUAL 'no'
+)
qt_feature("openssl_linked" PRIVATE
LABEL " Qt directly linked to OpenSSL"
- CONDITION NOT QT_FEATURE_securetransport AND OpenSSL_FOUND
+ AUTODETECT OFF
+ CONDITION NOT QT_FEATURE_securetransport AND NOT QT_FEATURE_schannel AND OpenSSL_FOUND
ENABLE INPUT_openssl STREQUAL 'linked'
- DISABLE ( NOT INPUT_openssl STREQUAL 'linked' )
)
qt_feature_definition("openssl_linked" "QT_LINKED_OPENSSL")
-qt_feature("securetransport" PRIVATE
+qt_feature("securetransport" PUBLIC
LABEL "SecureTransport"
CONDITION APPLE AND ( INPUT_openssl STREQUAL '' OR INPUT_openssl STREQUAL 'no' )
DISABLE INPUT_securetransport STREQUAL 'no' OR INPUT_ssl STREQUAL 'no'
)
qt_feature_definition("securetransport" "QT_SECURETRANSPORT")
+qt_feature("schannel" PUBLIC
+ LABEL "Schannel"
+ CONDITION INPUT_schannel STREQUAL 'yes' AND WIN32 AND NOT WINRT AND ( INPUT_openssl STREQUAL '' OR INPUT_openssl STREQUAL 'no' )
+ DISABLE INPUT_schannel STREQUAL 'no' OR INPUT_ssl STREQUAL 'no'
+)
+qt_feature_definition("schannel" "QT_SCHANNEL")
qt_feature("ssl" PUBLIC
LABEL "SSL"
- CONDITION WINRT OR QT_FEATURE_securetransport OR QT_FEATURE_openssl
+ CONDITION WINRT OR QT_FEATURE_securetransport OR QT_FEATURE_openssl OR QT_FEATURE_schannel
)
qt_feature_definition("ssl" "QT_NO_SSL" NEGATE VALUE "1")
qt_feature("dtls" PUBLIC
SECTION "Networking"
LABEL "DTLS"
PURPOSE "Provides a DTLS implementation"
- CONDITION QT_FEATURE_openssl AND TEST_dtls
+ CONDITION QT_FEATURE_openssl AND QT_FEATURE_udpsocket AND TEST_dtls
+)
+qt_feature("ocsp" PUBLIC
+ SECTION "Networking"
+ LABEL "OCSP-stapling"
+ PURPOSE "Provides OCSP stapling support"
+ CONDITION QT_FEATURE_opensslv11 AND TEST_ocsp
)
qt_feature("opensslv11" PUBLIC
LABEL "OpenSSL 1.1"
@@ -267,3 +339,17 @@ qt_feature("dnslookup" PUBLIC
LABEL "QDnsLookup"
PURPOSE "Provides API for DNS lookups."
)
+qt_feature("gssapi" PUBLIC
+ SECTION "Networking"
+ LABEL "GSSAPI"
+ PURPOSE "Enable SPNEGO authentication through GSSAPI"
+ CONDITION NOT WIN32 AND TEST_gssapi
+)
+qt_feature_definition("gssapi" "QT_NO_GSSAPI" NEGATE VALUE "1")
+qt_feature("sspi" PUBLIC
+ SECTION "Networking"
+ LABEL "SSPI"
+ PURPOSE "Enable NTLM/SPNEGO authentication through SSPI"
+ CONDITION WIN32 AND NOT WINRT
+)
+qt_feature_definition("sspi" "QT_NO_SSPI" NEGATE VALUE "1")
diff --git a/src/network/configure.json b/src/network/configure.json
index 9acc4cd7a6..7225a6251d 100644
--- a/src/network/configure.json
+++ b/src/network/configure.json
@@ -15,8 +15,10 @@
"openssl-linked": { "type": "void", "name": "openssl", "value": "linked" },
"openssl-runtime": { "type": "void", "name": "openssl", "value": "runtime" },
"dtls": "boolean",
+ "ocsp": "boolean",
"sctp": "boolean",
"securetransport": "boolean",
+ "schannel": "boolean",
"ssl": "boolean",
"system-proxies": "boolean"
}
@@ -26,13 +28,16 @@
"corewlan": {
"label": "CoreWLan",
"export": "",
- "test": "corewlan",
+ "test": {
+ "lang": "objc++",
+ "include": [ "CoreWLAN/CoreWLAN.h", "CoreWLAN/CWInterface.h" ],
+ "main": "[CWInterface interfaceWithName:@\"en2\"];"
+ },
"sources": [
"-framework CoreWLAN -framework Foundation"
]
},
"network": {
- "export": "",
"sources": [
{ "type": "makeSpec", "spec": "NETWORK" }
]
@@ -40,13 +45,13 @@
"libproxy": {
"label": "libproxy",
"test": {
- "include": [ "proxy.h" ],
"main": [
"pxProxyFactory *factory = px_proxy_factory_new();",
"px_proxy_factory_get_proxies(factory, \"http://qt-project.org\");",
"px_proxy_factory_free(factory);"
]
},
+ "headers": "proxy.h",
"sources": [
"-lproxy"
]
@@ -54,7 +59,17 @@
"openssl_headers": {
"label": "OpenSSL Headers",
"export": "openssl",
- "test": "openssl",
+ "test": {
+ "tail": [
+ "#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER-0 < 0x10000000L",
+ "# error OpenSSL >= 1.0.0 is required",
+ "#endif",
+ "#if OPENSSL_VERSION_NUMBER-0 >= 0x10002000L && !defined(OPENSSL_NO_EC) && !defined(SSL_CTRL_SET_CURVES)",
+ "# error OpenSSL was reported as >= 1.0.2 but is missing required features, possibly it's libressl which is unsupported",
+ "#endif"
+ ]
+ },
+ "headers": [ "openssl/ssl.h", "openssl/opensslv.h" ],
"sources": [
{
"comment": "placeholder for OPENSSL_PATH",
@@ -64,23 +79,24 @@
},
"openssl": {
"label": "OpenSSL",
- "test": "openssl",
+ "test": {
+ "inherit": "openssl_headers",
+ "main": "SSL_free(SSL_new(0));"
+ },
"sources": [
{ "type": "openssl" },
{
- "comment": "placeholder for OPENSSL_{PATH,LIBS{,_{DEBUG,RELEASE}}}",
- "libs": "",
- "condition": "config.win32 && !features.shared"
+ "libs": "-lssleay32 -llibeay32 -lUser32 -lWs2_32 -lAdvapi32 -lGdi32",
+ "condition": "config.win32"
},
{
- "libs": "-lssleay32 -llibeay32",
- "condition": "config.win32 && features.shared"
+ "libs": "-llibssl -llibcrypto -lUser32 -lWs2_32 -lAdvapi32 -lCrypt32",
+ "condition": "config.msvc"
},
{
- "libs": "-llibssl -llibcrypto",
- "condition": "config.win32"
- },
- { "libs": "-lssl -lcrypto", "condition": "!config.win32" }
+ "libs": "-lssl -lcrypto",
+ "condition": "!config.msvc"
+ }
]
}
},
@@ -148,7 +164,14 @@
"openssl11": {
"label": "OpenSSL 1.1 support",
"type": "compile",
- "test": "unix/openssl11",
+ "test": {
+ "include": "openssl/opensslv.h",
+ "tail": [
+ "#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER-0 < 0x10100000L",
+ "# error OpenSSL >= 1.1 is required",
+ "#endif"
+ ]
+ },
"use": "openssl"
},
"dtls": {
@@ -163,6 +186,28 @@
]
},
"use": "openssl"
+ },
+ "ocsp": {
+ "label": "OCSP stapling support in OpenSSL",
+ "type": "compile",
+ "test": {
+ "include": ["openssl/ssl.h", "openssl/ocsp.h"],
+ "tail": [
+ "#if defined(OPENSSL_NO_OCSP) || defined(OPENSSL_NO_TLSEXT)",
+ "# error OpenSSL without OCSP stapling",
+ "#endif"
+ ]
+ },
+ "use": "openssl"
+ },
+ "gssapi": {
+ "label": "KRB5 GSSAPI support",
+ "type": "compile",
+ "test": {
+ "include": [ "gssapi/gssapi.h" ],
+ "main": ["gss_ctx_id_t ctx;"],
+ "qmake": "LIBS += -lgssapi_krb5"
+ }
}
},
@@ -196,21 +241,25 @@
},
"openssl": {
"label": "OpenSSL",
- "enable": "input.openssl == 'yes' || input.openssl == 'linked' || input.openssl == 'runtime'",
- "disable": "input.openssl == 'no' || input.ssl == 'no'",
- "autoDetect": "!config.winrt && !config.wasm",
- "condition": "!features.securetransport && (features.openssl-linked || libs.openssl_headers)",
+ "enable": "false",
+ "condition": "features.openssl-runtime || features.openssl-linked",
"output": [
"privateFeature",
{ "type": "publicQtConfig", "condition": "!features.openssl-linked" },
{ "type": "define", "negative": true, "name": "QT_NO_OPENSSL" }
]
},
+ "openssl-runtime": {
+ "autoDetect": "!config.winrt && !config.wasm",
+ "enable": "input.openssl == 'yes' || input.openssl == 'runtime'",
+ "disable": "input.openssl == 'no' || input.openssl == 'linked' || input.ssl == 'no'",
+ "condition": "!features.securetransport && !features.schannel && libs.openssl_headers"
+ },
"openssl-linked": {
"label": " Qt directly linked to OpenSSL",
+ "autoDetect": false,
"enable": "input.openssl == 'linked'",
- "disable": "input.openssl != 'linked'",
- "condition": "!features.securetransport && libs.openssl",
+ "condition": "!features.securetransport && !features.schannel && libs.openssl",
"output": [
"privateFeature",
{ "type": "define", "name": "QT_LINKED_OPENSSL" }
@@ -221,20 +270,36 @@
"disable": "input.securetransport == 'no' || input.ssl == 'no'",
"condition": "config.darwin && (input.openssl == '' || input.openssl == 'no')",
"output": [
- "privateFeature",
+ "publicFeature",
{ "type": "define", "name": "QT_SECURETRANSPORT" }
]
},
+ "schannel": {
+ "label": "Schannel",
+ "disable": "input.schannel == 'no' || input.ssl == 'no'",
+ "condition": "input.schannel == 'yes' && config.win32 && !config.winrt && (input.openssl == '' || input.openssl == 'no')",
+ "output": [
+ "publicFeature",
+ { "type": "define", "name": "QT_SCHANNEL" }
+ ]
+ },
"ssl": {
"label": "SSL",
- "condition": "config.winrt || features.securetransport || features.openssl",
+ "condition": "config.winrt || features.securetransport || features.openssl || features.schannel",
"output": [ "publicFeature", "feature" ]
},
"dtls": {
"label": "DTLS",
"purpose": "Provides a DTLS implementation",
"section": "Networking",
- "condition": "features.openssl && tests.dtls",
+ "condition": "features.openssl && features.udpsocket && tests.dtls",
+ "output": [ "publicFeature" ]
+ },
+ "ocsp": {
+ "label": "OCSP-stapling",
+ "purpose": "Provides OCSP stapling support",
+ "section": "Networking",
+ "condition": "features.opensslv11 && tests.ocsp",
"output": [ "publicFeature" ]
},
"opensslv11": {
@@ -318,6 +383,20 @@
"purpose": "Provides API for DNS lookups.",
"section": "Networking",
"output": [ "publicFeature" ]
+ },
+ "gssapi": {
+ "label": "GSSAPI",
+ "purpose": "Enable SPNEGO authentication through GSSAPI",
+ "section": "Networking",
+ "condition": "!config.win32 && tests.gssapi",
+ "output": [ "publicFeature", "feature" ]
+ },
+ "sspi": {
+ "label": "SSPI",
+ "purpose": "Enable NTLM/SPNEGO authentication through SSPI",
+ "section": "Networking",
+ "condition": "config.win32 && !config.winrt",
+ "output": [ "publicFeature", "feature" ]
}
},
@@ -354,12 +433,19 @@
"args": "securetransport",
"condition": "config.darwin"
},
+ {
+ "type": "feature",
+ "args": "schannel",
+ "condition": "config.win32 && !config.winrt"
+ },
"openssl",
"openssl-linked",
"opensslv11",
"dtls",
+ "ocsp",
"sctp",
- "system-proxies"
+ "system-proxies",
+ "gssapi"
]
}
]
diff --git a/src/network/configure.pri b/src/network/configure.pri
index fa502db1d1..ad4d711cba 100644
--- a/src/network/configure.pri
+++ b/src/network/configure.pri
@@ -1,10 +1,10 @@
# custom tests
defineTest(qtConfLibrary_openssl) {
- libs = $$getenv("OPENSSL_LIBS")
+ eval(libs = $$getenv("OPENSSL_LIBS"))
!isEmpty(libs) {
- eval($${1}.libs = $$libs)
- export($${1}.libs)
+ !qtConfResolveLibs($${1}.libs, $$libs): \
+ return(false)
return(true)
}
qtLog("$OPENSSL_LIBS is not set.")
diff --git a/src/network/doc/snippets/code/src_network_bearer_qnetworksession.cpp b/src/network/doc/snippets/code/src_network_bearer_qnetworksession.cpp
new file mode 100644
index 0000000000..b88b6d1768
--- /dev/null
+++ b/src/network/doc/snippets/code/src_network_bearer_qnetworksession.cpp
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [0]
+ session->open();
+ if (session->waitForOpened(1000))
+ qDebug("Open!");
+//! [0]
+
+//! [1]
+ QNetworkConfigurationManager mgr;
+ QNetworkConfiguration ap = mgr.defaultConfiguration();
+ QNetworkSession *session = new QNetworkSession(ap);
+ ... //code activates session
+
+ QString ident = session->sessionProperty("ActiveConfiguration").toString();
+ if ( ap.type() == QNetworkConfiguration::ServiceNetwork ) {
+ Q_ASSERT( ap.identifier() != ident );
+ Q_ASSERT( ap.children().contains( mgr.configurationFromIdentifier(ident) ) );
+ } else if ( ap.type() == QNetworkConfiguration::InternetAccessPoint ) {
+ Q_ASSERT( ap.identifier() == ident );
+ }
+ \endcode
+//! [1]
diff --git a/src/network/doc/snippets/code/src_network_kernel_qnetworkdatagram.cpp b/src/network/doc/snippets/code/src_network_kernel_qnetworkdatagram.cpp
new file mode 100644
index 0000000000..f81ca97681
--- /dev/null
+++ b/src/network/doc/snippets/code/src_network_kernel_qnetworkdatagram.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [0]
+ void Server::readPendingDatagrams()
+ {
+ while (udpSocket->hasPendingDatagrams()) {
+ QNetworkDatagram datagram = udpSocket->receiveDatagram();
+ QByteArray replyData = processThePayload(datagram.data());
+ udpSocket->writeDatagram(datagram.makeReply(replyData));
+ }
+ }
+//! [0]
+
+//! [1]
+ udpSocket->writeDatagram(std::move(datagram).makeReply(replyData));
+//! [1]
diff --git a/src/network/doc/snippets/code/src_network_kernel_qnetworkinterface.cpp b/src/network/doc/snippets/code/src_network_kernel_qnetworkinterface.cpp
new file mode 100644
index 0000000000..fc7fd7814a
--- /dev/null
+++ b/src/network/doc/snippets/code/src_network_kernel_qnetworkinterface.cpp
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [0]
+ QNetworkInterface::interfaceFromName(name).index()
+//! [0]
+
+//! [1]
+ QNetworkInterface::interfaceFromIndex(index).name()
+//! [1]
diff --git a/src/network/doc/snippets/code/src_network_socket_qudpsocket.cpp b/src/network/doc/snippets/code/src_network_socket_qudpsocket.cpp
index a98e31b10b..f6a28ce46c 100644
--- a/src/network/doc/snippets/code/src_network_socket_qudpsocket.cpp
+++ b/src/network/doc/snippets/code/src_network_socket_qudpsocket.cpp
@@ -54,8 +54,8 @@ void Server::initSocket()
udpSocket = new QUdpSocket(this);
udpSocket->bind(QHostAddress::LocalHost, 7755);
- connect(udpSocket, SIGNAL(readyRead()),
- this, SLOT(readPendingDatagrams()));
+ connect(udpSocket, &QUdpSocket::readyRead,
+ this, &Server::readPendingDatagrams);
}
void Server::readPendingDatagrams()
diff --git a/src/network/doc/snippets/code/src_network_ssl_qsslpresharedkeyauthenticator.cpp b/src/network/doc/snippets/code/src_network_ssl_qsslpresharedkeyauthenticator.cpp
new file mode 100644
index 0000000000..22e60840a3
--- /dev/null
+++ b/src/network/doc/snippets/code/src_network_ssl_qsslpresharedkeyauthenticator.cpp
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [0]
+ connect(socket, &QSslSocket::preSharedKeyAuthenticationRequired,
+ this, &AuthManager::handlePreSharedKeyAuthentication);
+//! [0]
+
+//! [1]
+ void AuthManager::handlePreSharedKeyAuthentication(QSslPreSharedKeyAuthenticator *authenticator)
+ {
+ authenticator->setIdentity("My Qt App");
+
+ const QByteArray key = deriveKey(authenticator->identityHint(), passphrase);
+ authenticator->setPreSharedKey(key);
+ }
+//! [1]
diff --git a/src/network/doc/src/ssl.qdoc b/src/network/doc/src/ssl.qdoc
index 840cec9e5b..a3af1d0477 100644
--- a/src/network/doc/src/ssl.qdoc
+++ b/src/network/doc/src/ssl.qdoc
@@ -36,9 +36,8 @@
the Secure Sockets Layer (SSL) protocol, using the \l{OpenSSL Toolkit}
to perform encryption and protocol handling.
- From Qt version 5.2 onwards, the officially supported version for OpenSSL
- is 1.0.0 or later. Versions >= 0.9.7 and < 1.0.0 might work, but are not
- guaranteed to work.
+ From Qt version 5.6 onwards, the officially supported version for OpenSSL
+ is 1.0.0 or later.
\annotatedlist ssl
diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri
index 11b80d59d5..b86119b200 100644
--- a/src/network/kernel/kernel.pri
+++ b/src/network/kernel/kernel.pri
@@ -39,8 +39,11 @@ qtConfig(dnslookup) {
unix {
!integrity:qtConfig(dnslookup): SOURCES += kernel/qdnslookup_unix.cpp
+
SOURCES += kernel/qhostinfo_unix.cpp
+ qtConfig(dlopen): QMAKE_USE_PRIVATE += libdl
+
qtConfig(linux-netlink): SOURCES += kernel/qnetworkinterface_linux.cpp
else: SOURCES += kernel/qnetworkinterface_unix.cpp
}
@@ -68,6 +71,8 @@ mac {
!uikit: LIBS_PRIVATE += -framework CoreServices -framework SystemConfiguration
}
+qtConfig(gssapi): LIBS_PRIVATE += -lgssapi_krb5
+
uikit:HEADERS += kernel/qnetworkinterface_uikit_p.h
osx:SOURCES += kernel/qnetworkproxy_mac.cpp
else:win32:!winrt: SOURCES += kernel/qnetworkproxy_win.cpp
diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp
index 47ce9ab0c6..3ca8806c2b 100644
--- a/src/network/kernel/qauthenticator.cpp
+++ b/src/network/kernel/qauthenticator.cpp
@@ -54,20 +54,29 @@
#include <qmutex.h>
#include <private/qmutexpool_p.h>
#include <rpc.h>
-#ifndef Q_OS_WINRT
+#endif
+
+#if QT_CONFIG(sspi) // SSPI
#define SECURITY_WIN32 1
#include <security.h>
-#endif
+#elif QT_CONFIG(gssapi) // GSSAPI
+#include <gssapi/gssapi.h>
#endif
QT_BEGIN_NAMESPACE
static QByteArray qNtlmPhase1();
static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data);
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
-static QByteArray qNtlmPhase1_SSPI(QAuthenticatorPrivate *ctx);
-static QByteArray qNtlmPhase3_SSPI(QAuthenticatorPrivate *ctx, const QByteArray& phase2data);
-#endif
+#if QT_CONFIG(sspi) // SSPI
+static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
+ const QString& host);
+static QByteArray qSspiContinue(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
+ const QString& host, const QByteArray& challenge = QByteArray());
+#elif QT_CONFIG(gssapi) // GSSAPI
+static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, const QString& host);
+static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx,
+ const QByteArray& challenge = QByteArray());
+#endif // gssapi
/*!
\class QAuthenticator
@@ -90,6 +99,7 @@ static QByteArray qNtlmPhase3_SSPI(QAuthenticatorPrivate *ctx, const QByteArray&
\li Basic
\li NTLM version 2
\li Digest-MD5
+ \li SPNEGO/Negotiate
\endlist
\target qauthenticator-options
@@ -133,6 +143,10 @@ static QByteArray qNtlmPhase3_SSPI(QAuthenticatorPrivate *ctx, const QByteArray&
The Digest-MD5 authentication mechanism supports no outgoing options.
+ \section2 SPNEGO/Negotiate
+
+ This authentication mechanism currently supports no incoming or outgoing options.
+
\sa QSslSocket
*/
@@ -187,7 +201,7 @@ QAuthenticator &QAuthenticator::operator=(const QAuthenticator &other)
d->options = other.d->options;
} else if (d->phase == QAuthenticatorPrivate::Start) {
delete d;
- d = 0;
+ d = nullptr;
}
return *this;
}
@@ -339,21 +353,25 @@ bool QAuthenticator::isNull() const
return !d;
}
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
-class QNtlmWindowsHandles
+#if QT_CONFIG(sspi) // SSPI
+class QSSPIWindowsHandles
{
public:
CredHandle credHandle;
CtxtHandle ctxHandle;
};
-#endif
+#elif QT_CONFIG(gssapi) // GSSAPI
+class QGssApiHandles
+{
+public:
+ gss_ctx_id_t gssCtx = nullptr;
+ gss_name_t targetName;
+};
+#endif // gssapi
QAuthenticatorPrivate::QAuthenticatorPrivate()
: method(None)
- #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
- , ntlmWindowsHandles(0)
- #endif
, hasFailed(false)
, phase(Start)
, nonceCount(0)
@@ -363,13 +381,7 @@ QAuthenticatorPrivate::QAuthenticatorPrivate()
nonceCount = 0;
}
-QAuthenticatorPrivate::~QAuthenticatorPrivate()
-{
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
- if (ntlmWindowsHandles)
- delete ntlmWindowsHandles;
-#endif
-}
+QAuthenticatorPrivate::~QAuthenticatorPrivate() = default;
void QAuthenticatorPrivate::updateCredentials()
{
@@ -424,6 +436,9 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
} else if (method < DigestMd5 && str.startsWith("digest")) {
method = DigestMd5;
headerVal = current.second.mid(7);
+ } else if (method < Negotiate && str.startsWith("negotiate")) {
+ method = Negotiate;
+ headerVal = current.second.mid(10);
}
}
@@ -439,6 +454,7 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
phase = Done;
break;
case Ntlm:
+ case Negotiate:
// work is done in calculateResponse()
break;
case DigestMd5: {
@@ -456,33 +472,36 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
}
}
-QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMethod, const QByteArray &path)
+QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMethod, const QByteArray &path, const QString& host)
{
+#if !QT_CONFIG(sspi) && !QT_CONFIG(gssapi)
+ Q_UNUSED(host);
+#endif
QByteArray response;
- const char *methodString = 0;
+ const char* methodString = nullptr;
switch(method) {
case QAuthenticatorPrivate::None:
methodString = "";
phase = Done;
break;
case QAuthenticatorPrivate::Basic:
- methodString = "Basic ";
+ methodString = "Basic";
response = user.toLatin1() + ':' + password.toLatin1();
response = response.toBase64();
phase = Done;
break;
case QAuthenticatorPrivate::DigestMd5:
- methodString = "Digest ";
+ methodString = "Digest";
response = digestMd5Response(challenge, requestMethod, path);
phase = Done;
break;
case QAuthenticatorPrivate::Ntlm:
- methodString = "NTLM ";
+ methodString = "NTLM";
if (challenge.isEmpty()) {
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
+#if QT_CONFIG(sspi) // SSPI
QByteArray phase1Token;
if (user.isEmpty()) // Only pull from system if no user was specified in authenticator
- phase1Token = qNtlmPhase1_SSPI(this);
+ phase1Token = qSspiStartup(this, method, host);
if (!phase1Token.isEmpty()) {
response = phase1Token.toBase64();
phase = Phase2;
@@ -496,10 +515,10 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet
phase = Phase2;
}
} else {
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
+#if QT_CONFIG(sspi) // SSPI
QByteArray phase3Token;
- if (ntlmWindowsHandles)
- phase3Token = qNtlmPhase3_SSPI(this, QByteArray::fromBase64(challenge));
+ if (sspiWindowsHandles)
+ phase3Token = qSspiContinue(this, method, host, QByteArray::fromBase64(challenge));
if (!phase3Token.isEmpty()) {
response = phase3Token.toBase64();
phase = Done;
@@ -512,8 +531,39 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet
}
break;
+ case QAuthenticatorPrivate::Negotiate:
+ methodString = "Negotiate";
+ if (challenge.isEmpty()) {
+ QByteArray phase1Token;
+#if QT_CONFIG(sspi) // SSPI
+ phase1Token = qSspiStartup(this, method, host);
+#elif QT_CONFIG(gssapi) // GSSAPI
+ phase1Token = qGssapiStartup(this, host);
+#endif
+
+ if (!phase1Token.isEmpty()) {
+ response = phase1Token.toBase64();
+ phase = Phase2;
+ } else {
+ phase = Done;
+ }
+ } else {
+ QByteArray phase3Token;
+#if QT_CONFIG(sspi) // SSPI
+ phase3Token = qSspiContinue(this, method, host, QByteArray::fromBase64(challenge));
+#elif QT_CONFIG(gssapi) // GSSAPI
+ phase3Token = qGssapiContinue(this, QByteArray::fromBase64(challenge));
+#endif
+ if (!phase3Token.isEmpty()) {
+ response = phase3Token.toBase64();
+ phase = Done;
+ }
+ }
+
+ break;
}
- return QByteArray(methodString) + response;
+
+ return QByteArray::fromRawData(methodString, qstrlen(methodString)) + ' ' + response;
}
@@ -699,9 +749,10 @@ QByteArray QAuthenticatorPrivate::digestMd5Response(const QByteArray &challenge,
return credentials;
}
-// ---------------------------- Digest Md5 code ----------------------------------------
+// ---------------------------- End of Digest Md5 code ---------------------------------
+// ---------------------------- NTLM code ----------------------------------------------
/*
* NTLM message flags.
@@ -1419,156 +1470,237 @@ static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phas
return rc;
}
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
+// ---------------------------- End of NTLM code ---------------------------------------
+
+#if QT_CONFIG(sspi) // SSPI
+// ---------------------------- SSPI code ----------------------------------------------
// See http://davenport.sourceforge.net/ntlm.html
// and libcurl http_ntlm.c
// Handle of secur32.dll
-static HMODULE securityDLLHandle = NULL;
+static HMODULE securityDLLHandle = nullptr;
// Pointer to SSPI dispatch table
-static PSecurityFunctionTable pSecurityFunctionTable = NULL;
-
+static PSecurityFunctionTable pSecurityFunctionTable = nullptr;
-static bool q_NTLM_SSPI_library_load()
+static bool q_SSPI_library_load()
{
static QBasicMutex mutex;
QMutexLocker l(&mutex);
// Initialize security interface
- if (pSecurityFunctionTable == NULL) {
+ if (pSecurityFunctionTable == nullptr) {
securityDLLHandle = LoadLibrary(L"secur32.dll");
- if (securityDLLHandle != NULL) {
+ if (securityDLLHandle != nullptr) {
INIT_SECURITY_INTERFACE pInitSecurityInterface =
reinterpret_cast<INIT_SECURITY_INTERFACE>(
reinterpret_cast<QFunctionPointer>(GetProcAddress(securityDLLHandle, "InitSecurityInterfaceW")));
- if (pInitSecurityInterface != NULL)
+ if (pInitSecurityInterface != nullptr)
pSecurityFunctionTable = pInitSecurityInterface();
}
}
- if (pSecurityFunctionTable == NULL)
+ if (pSecurityFunctionTable == nullptr)
return false;
return true;
}
-// Phase 1:
-static QByteArray qNtlmPhase1_SSPI(QAuthenticatorPrivate *ctx)
+static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
+ const QString& host)
{
- QByteArray result;
+ if (!q_SSPI_library_load())
+ return QByteArray();
+
+ TimeStamp expiry; // For Windows 9x compatibility of SSPI calls
- if (!q_NTLM_SSPI_library_load())
- return result;
+ if (!ctx->sspiWindowsHandles)
+ ctx->sspiWindowsHandles.reset(new QSSPIWindowsHandles);
+ memset(&ctx->sspiWindowsHandles->credHandle, 0, sizeof(CredHandle));
- // 1. The client obtains a representation of the credential set
- // for the user via the SSPI AcquireCredentialsHandle function.
- if (!ctx->ntlmWindowsHandles)
- ctx->ntlmWindowsHandles = new QNtlmWindowsHandles;
- memset(&ctx->ntlmWindowsHandles->credHandle, 0, sizeof(CredHandle));
- TimeStamp tsDummy;
+ // Acquire our credentials handle
SECURITY_STATUS secStatus = pSecurityFunctionTable->AcquireCredentialsHandle(
- NULL, (SEC_WCHAR*)L"NTLM", SECPKG_CRED_OUTBOUND, NULL, NULL,
- NULL, NULL, &ctx->ntlmWindowsHandles->credHandle, &tsDummy);
+ nullptr,
+ (SEC_WCHAR*)(method == QAuthenticatorPrivate::Negotiate ? L"Negotiate" : L"NTLM"),
+ SECPKG_CRED_OUTBOUND, nullptr, nullptr, nullptr, nullptr,
+ &ctx->sspiWindowsHandles->credHandle, &expiry
+ );
if (secStatus != SEC_E_OK) {
- delete ctx->ntlmWindowsHandles;
- ctx->ntlmWindowsHandles = 0;
- return result;
+ ctx->sspiWindowsHandles.reset(nullptr);
+ return QByteArray();
}
- // 2. The client calls the SSPI InitializeSecurityContext function
- // to obtain an authentication request token (in our case, a Type 1 message).
- // The client sends this token to the server.
- SecBufferDesc desc;
- SecBuffer buf;
- desc.ulVersion = SECBUFFER_VERSION;
- desc.cBuffers = 1;
- desc.pBuffers = &buf;
- buf.cbBuffer = 0;
- buf.BufferType = SECBUFFER_TOKEN;
- buf.pvBuffer = NULL;
- ULONG attrs;
-
- secStatus = pSecurityFunctionTable->InitializeSecurityContext(&ctx->ntlmWindowsHandles->credHandle, NULL,
- const_cast<SEC_WCHAR*>(L"") /* host */,
- ISC_REQ_ALLOCATE_MEMORY,
- 0, SECURITY_NETWORK_DREP,
- NULL, 0,
- &ctx->ntlmWindowsHandles->ctxHandle, &desc,
- &attrs, &tsDummy);
- if (secStatus == SEC_I_COMPLETE_AND_CONTINUE ||
- secStatus == SEC_I_CONTINUE_NEEDED) {
- pSecurityFunctionTable->CompleteAuthToken(&ctx->ntlmWindowsHandles->ctxHandle, &desc);
- } else if (secStatus != SEC_E_OK) {
- if ((const char*)buf.pvBuffer)
- pSecurityFunctionTable->FreeContextBuffer(buf.pvBuffer);
- pSecurityFunctionTable->FreeCredentialsHandle(&ctx->ntlmWindowsHandles->credHandle);
- delete ctx->ntlmWindowsHandles;
- ctx->ntlmWindowsHandles = 0;
- return result;
+ return qSspiContinue(ctx, method, host);
+}
+
+static QByteArray qSspiContinue(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
+ const QString &host, const QByteArray &challenge)
+{
+ QByteArray result;
+ SecBuffer challengeBuf;
+ SecBuffer responseBuf;
+ SecBufferDesc challengeDesc;
+ SecBufferDesc responseDesc;
+ unsigned long attrs;
+ TimeStamp expiry; // For Windows 9x compatibility of SSPI calls
+
+ if (!challenge.isEmpty())
+ {
+ // Setup the challenge "input" security buffer
+ challengeDesc.ulVersion = SECBUFFER_VERSION;
+ challengeDesc.cBuffers = 1;
+ challengeDesc.pBuffers = &challengeBuf;
+ challengeBuf.BufferType = SECBUFFER_TOKEN;
+ challengeBuf.pvBuffer = (PVOID)(challenge.data());
+ challengeBuf.cbBuffer = challenge.length();
}
- result = QByteArray((const char*)buf.pvBuffer, buf.cbBuffer);
- pSecurityFunctionTable->FreeContextBuffer(buf.pvBuffer);
+ // Setup the response "output" security buffer
+ responseDesc.ulVersion = SECBUFFER_VERSION;
+ responseDesc.cBuffers = 1;
+ responseDesc.pBuffers = &responseBuf;
+ responseBuf.BufferType = SECBUFFER_TOKEN;
+ responseBuf.pvBuffer = nullptr;
+ responseBuf.cbBuffer = 0;
+
+ // Calculate target (SPN for Negotiate, empty for NTLM)
+ std::wstring targetNameW = (method == QAuthenticatorPrivate::Negotiate
+ ? QLatin1String("HTTP/") + host : QString()).toStdWString();
+
+ // Generate our challenge-response message
+ SECURITY_STATUS secStatus = pSecurityFunctionTable->InitializeSecurityContext(
+ &ctx->sspiWindowsHandles->credHandle,
+ !challenge.isEmpty() ? &ctx->sspiWindowsHandles->ctxHandle : nullptr,
+ const_cast<wchar_t*>(targetNameW.data()),
+ ISC_REQ_ALLOCATE_MEMORY,
+ 0, SECURITY_NATIVE_DREP,
+ !challenge.isEmpty() ? &challengeDesc : nullptr,
+ 0, &ctx->sspiWindowsHandles->ctxHandle,
+ &responseDesc, &attrs,
+ &expiry
+ );
+
+ if (secStatus == SEC_I_COMPLETE_NEEDED || secStatus == SEC_I_COMPLETE_AND_CONTINUE) {
+ secStatus = pSecurityFunctionTable->CompleteAuthToken(&ctx->sspiWindowsHandles->ctxHandle,
+ &responseDesc);
+ }
+
+ if (secStatus != SEC_I_COMPLETE_AND_CONTINUE && secStatus != SEC_I_CONTINUE_NEEDED) {
+ pSecurityFunctionTable->FreeCredentialsHandle(&ctx->sspiWindowsHandles->credHandle);
+ pSecurityFunctionTable->DeleteSecurityContext(&ctx->sspiWindowsHandles->ctxHandle);
+ ctx->sspiWindowsHandles.reset(nullptr);
+ }
+
+ result = QByteArray((const char*)responseBuf.pvBuffer, responseBuf.cbBuffer);
+ pSecurityFunctionTable->FreeContextBuffer(responseBuf.pvBuffer);
+
return result;
}
-// Phase 2:
-// 3. The server receives the token from the client, and uses it as input to the
-// AcceptSecurityContext SSPI function. This creates a local security context on
-// the server to represent the client, and yields an authentication response token
-// (the Type 2 message), which is sent to the client.
+// ---------------------------- End of SSPI code ---------------------------------------
+
+#elif QT_CONFIG(gssapi) // GSSAPI
+
+// ---------------------------- GSSAPI code ----------------------------------------------
+// See postgres src/interfaces/libpq/fe-auth.c
+
+// Fetch all errors of a specific type
+static void q_GSSAPI_error_int(const char *message, OM_uint32 stat, int type)
+{
+ OM_uint32 minStat, msgCtx = 0;
+ gss_buffer_desc msg;
+
+ do {
+ gss_display_status(&minStat, stat, type, GSS_C_NO_OID, &msgCtx, &msg);
+ qDebug() << message << ": " << reinterpret_cast<const char*>(msg.value);
+ gss_release_buffer(&minStat, &msg);
+ } while (msgCtx);
+}
-// Phase 3:
-static QByteArray qNtlmPhase3_SSPI(QAuthenticatorPrivate *ctx, const QByteArray& phase2data)
+// GSSAPI errors contain two parts; extract both
+static void q_GSSAPI_error(const char *message, OM_uint32 majStat, OM_uint32 minStat)
{
- // 4. The client receives the response token from the server and calls
- // InitializeSecurityContext again, passing the server's token as input.
- // This provides us with another authentication request token (the Type 3 message).
- // The return value indicates that the security context was successfully initialized;
- // the token is sent to the server.
+ // Fetch major error codes
+ q_GSSAPI_error_int(message, majStat, GSS_C_GSS_CODE);
+ // Add the minor codes as well
+ q_GSSAPI_error_int(message, minStat, GSS_C_MECH_CODE);
+}
+
+// Send initial GSS authentication token
+static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, const QString &host)
+{
+ OM_uint32 majStat, minStat;
+
+ if (!ctx->gssApiHandles)
+ ctx->gssApiHandles.reset(new QGssApiHandles);
+
+ // Convert target name to internal form
+ QByteArray serviceName = QStringLiteral("HTTPS@%1").arg(host).toLocal8Bit();
+ gss_buffer_desc nameDesc = {static_cast<std::size_t>(serviceName.size()), serviceName.data()};
+
+ majStat = gss_import_name(&minStat, &nameDesc,
+ GSS_C_NT_HOSTBASED_SERVICE, &ctx->gssApiHandles->targetName);
+
+ if (majStat != GSS_S_COMPLETE) {
+ q_GSSAPI_error("gss_import_name error", majStat, minStat);
+ ctx->gssApiHandles.reset(nullptr);
+ return QByteArray();
+ }
+
+ // Call qGssapiContinue with GSS_C_NO_CONTEXT to get initial packet
+ ctx->gssApiHandles->gssCtx = GSS_C_NO_CONTEXT;
+ return qGssapiContinue(ctx);
+}
+
+// Continue GSS authentication with next token as needed
+static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, const QByteArray& challenge)
+{
+ OM_uint32 majStat, minStat, ignored;
QByteArray result;
+ gss_buffer_desc inBuf = {0, nullptr}; // GSS input token
+ gss_buffer_desc outBuf; // GSS output token
- if (pSecurityFunctionTable == NULL)
- return result;
-
- SecBuffer type_2, type_3;
- SecBufferDesc type_2_desc, type_3_desc;
- ULONG attrs;
- TimeStamp tsDummy; // For Windows 9x compatibility of SPPI calls
-
- type_2_desc.ulVersion = type_3_desc.ulVersion = SECBUFFER_VERSION;
- type_2_desc.cBuffers = type_3_desc.cBuffers = 1;
- type_2_desc.pBuffers = &type_2;
- type_3_desc.pBuffers = &type_3;
-
- type_2.BufferType = SECBUFFER_TOKEN;
- type_2.pvBuffer = (PVOID)phase2data.data();
- type_2.cbBuffer = phase2data.length();
- type_3.BufferType = SECBUFFER_TOKEN;
- type_3.pvBuffer = 0;
- type_3.cbBuffer = 0;
-
- SECURITY_STATUS secStatus = pSecurityFunctionTable->InitializeSecurityContext(&ctx->ntlmWindowsHandles->credHandle,
- &ctx->ntlmWindowsHandles->ctxHandle,
- const_cast<SEC_WCHAR*>(L"") /* host */,
- ISC_REQ_ALLOCATE_MEMORY,
- 0, SECURITY_NETWORK_DREP, &type_2_desc,
- 0, &ctx->ntlmWindowsHandles->ctxHandle, &type_3_desc,
- &attrs, &tsDummy);
-
- if (secStatus == SEC_E_OK && ((const char*)type_3.pvBuffer)) {
- result = QByteArray((const char*)type_3.pvBuffer, type_3.cbBuffer);
- pSecurityFunctionTable->FreeContextBuffer(type_3.pvBuffer);
+ if (!challenge.isEmpty()) {
+ inBuf.value = const_cast<char*>(challenge.data());
+ inBuf.length = challenge.length();
}
- pSecurityFunctionTable->FreeCredentialsHandle(&ctx->ntlmWindowsHandles->credHandle);
- pSecurityFunctionTable->DeleteSecurityContext(&ctx->ntlmWindowsHandles->ctxHandle);
- delete ctx->ntlmWindowsHandles;
- ctx->ntlmWindowsHandles = 0;
+ majStat = gss_init_sec_context(&minStat,
+ GSS_C_NO_CREDENTIAL,
+ &ctx->gssApiHandles->gssCtx,
+ ctx->gssApiHandles->targetName,
+ GSS_C_NO_OID,
+ GSS_C_MUTUAL_FLAG,
+ 0,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ challenge.isEmpty() ? GSS_C_NO_BUFFER : &inBuf,
+ nullptr,
+ &outBuf,
+ nullptr,
+ nullptr);
+
+ if (outBuf.length != 0)
+ result = QByteArray(reinterpret_cast<const char*>(outBuf.value), outBuf.length);
+ gss_release_buffer(&ignored, &outBuf);
+
+ if (majStat != GSS_S_COMPLETE && majStat != GSS_S_CONTINUE_NEEDED) {
+ q_GSSAPI_error("gss_init_sec_context error", majStat, minStat);
+ gss_release_name(&ignored, &ctx->gssApiHandles->targetName);
+ if (ctx->gssApiHandles->gssCtx)
+ gss_delete_sec_context(&ignored, &ctx->gssApiHandles->gssCtx, GSS_C_NO_BUFFER);
+ ctx->gssApiHandles.reset(nullptr);
+ }
+
+ if (majStat == GSS_S_COMPLETE) {
+ gss_release_name(&ignored, &ctx->gssApiHandles->targetName);
+ ctx->gssApiHandles.reset(nullptr);
+ }
return result;
}
-#endif // Q_OS_WIN && !Q_OS_WINRT
+
+// ---------------------------- End of GSSAPI code ----------------------------------------------
+
+#endif // gssapi
QT_END_NAMESPACE
diff --git a/src/network/kernel/qauthenticator_p.h b/src/network/kernel/qauthenticator_p.h
index 265cb7afe2..e201d22650 100644
--- a/src/network/kernel/qauthenticator_p.h
+++ b/src/network/kernel/qauthenticator_p.h
@@ -54,6 +54,7 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <qhash.h>
#include <qbytearray.h>
+#include <qscopedpointer.h>
#include <qstring.h>
#include <qauthenticator.h>
#include <qvariant.h>
@@ -61,14 +62,16 @@
QT_BEGIN_NAMESPACE
class QHttpResponseHeader;
-#ifdef Q_OS_WIN
-class QNtlmWindowsHandles;
+#if QT_CONFIG(sspi) // SSPI
+class QSSPIWindowsHandles;
+#elif QT_CONFIG(gssapi) // GSSAPI
+class QGssApiHandles;
#endif
class Q_AUTOTEST_EXPORT QAuthenticatorPrivate
{
public:
- enum Method { None, Basic, Ntlm, DigestMd5 };
+ enum Method { None, Basic, Ntlm, DigestMd5, Negotiate };
QAuthenticatorPrivate();
~QAuthenticatorPrivate();
@@ -79,8 +82,10 @@ public:
Method method;
QString realm;
QByteArray challenge;
-#ifdef Q_OS_WIN
- QNtlmWindowsHandles *ntlmWindowsHandles;
+#if QT_CONFIG(sspi) // SSPI
+ QScopedPointer<QSSPIWindowsHandles> sspiWindowsHandles;
+#elif QT_CONFIG(gssapi) // GSSAPI
+ QScopedPointer<QGssApiHandles> gssApiHandles;
#endif
bool hasFailed; //credentials have been tried but rejected by server.
@@ -100,7 +105,7 @@ public:
QString workstation;
QString userDomain;
- QByteArray calculateResponse(const QByteArray &method, const QByteArray &path);
+ QByteArray calculateResponse(const QByteArray &method, const QByteArray &path, const QString& host);
inline static QAuthenticatorPrivate *getPrivate(QAuthenticator &auth) { return auth.d; }
inline static const QAuthenticatorPrivate *getPrivate(const QAuthenticator &auth) { return auth.d; }
diff --git a/src/network/kernel/qdnslookup.h b/src/network/kernel/qdnslookup.h
index eebd0abe66..79a476b98f 100644
--- a/src/network/kernel/qdnslookup.h
+++ b/src/network/kernel/qdnslookup.h
@@ -65,12 +65,12 @@ public:
QDnsDomainNameRecord();
QDnsDomainNameRecord(const QDnsDomainNameRecord &other);
#ifdef Q_COMPILER_RVALUE_REFS
- QDnsDomainNameRecord &operator=(QDnsDomainNameRecord &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QDnsDomainNameRecord &operator=(QDnsDomainNameRecord &&other) noexcept { swap(other); return *this; }
#endif
QDnsDomainNameRecord &operator=(const QDnsDomainNameRecord &other);
~QDnsDomainNameRecord();
- void swap(QDnsDomainNameRecord &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QDnsDomainNameRecord &other) noexcept { qSwap(d, other.d); }
QString name() const;
quint32 timeToLive() const;
@@ -89,12 +89,12 @@ public:
QDnsHostAddressRecord();
QDnsHostAddressRecord(const QDnsHostAddressRecord &other);
#ifdef Q_COMPILER_RVALUE_REFS
- QDnsHostAddressRecord &operator=(QDnsHostAddressRecord &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QDnsHostAddressRecord &operator=(QDnsHostAddressRecord &&other) noexcept { swap(other); return *this; }
#endif
QDnsHostAddressRecord &operator=(const QDnsHostAddressRecord &other);
~QDnsHostAddressRecord();
- void swap(QDnsHostAddressRecord &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QDnsHostAddressRecord &other) noexcept { qSwap(d, other.d); }
QString name() const;
quint32 timeToLive() const;
@@ -113,12 +113,12 @@ public:
QDnsMailExchangeRecord();
QDnsMailExchangeRecord(const QDnsMailExchangeRecord &other);
#ifdef Q_COMPILER_RVALUE_REFS
- QDnsMailExchangeRecord &operator=(QDnsMailExchangeRecord &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QDnsMailExchangeRecord &operator=(QDnsMailExchangeRecord &&other) noexcept { swap(other); return *this; }
#endif
QDnsMailExchangeRecord &operator=(const QDnsMailExchangeRecord &other);
~QDnsMailExchangeRecord();
- void swap(QDnsMailExchangeRecord &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QDnsMailExchangeRecord &other) noexcept { qSwap(d, other.d); }
QString exchange() const;
QString name() const;
@@ -138,12 +138,12 @@ public:
QDnsServiceRecord();
QDnsServiceRecord(const QDnsServiceRecord &other);
#ifdef Q_COMPILER_RVALUE_REFS
- QDnsServiceRecord &operator=(QDnsServiceRecord &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QDnsServiceRecord &operator=(QDnsServiceRecord &&other) noexcept { swap(other); return *this; }
#endif
QDnsServiceRecord &operator=(const QDnsServiceRecord &other);
~QDnsServiceRecord();
- void swap(QDnsServiceRecord &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QDnsServiceRecord &other) noexcept { qSwap(d, other.d); }
QString name() const;
quint16 port() const;
@@ -165,12 +165,12 @@ public:
QDnsTextRecord();
QDnsTextRecord(const QDnsTextRecord &other);
#ifdef Q_COMPILER_RVALUE_REFS
- QDnsTextRecord &operator=(QDnsTextRecord &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QDnsTextRecord &operator=(QDnsTextRecord &&other) noexcept { swap(other); return *this; }
#endif
QDnsTextRecord &operator=(const QDnsTextRecord &other);
~QDnsTextRecord();
- void swap(QDnsTextRecord &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QDnsTextRecord &other) noexcept { qSwap(d, other.d); }
QString name() const;
quint32 timeToLive() const;
diff --git a/src/network/kernel/qdnslookup_p.h b/src/network/kernel/qdnslookup_p.h
index 2dc98e527a..8c3c2ed3e1 100644
--- a/src/network/kernel/qdnslookup_p.h
+++ b/src/network/kernel/qdnslookup_p.h
@@ -95,7 +95,7 @@ public:
QDnsLookupPrivate()
: isFinished(false)
, type(QDnsLookup::A)
- , runnable(0)
+ , runnable(nullptr)
{ }
void _q_lookupFinished(const QDnsLookupReply &reply);
diff --git a/src/network/kernel/qdnslookup_unix.cpp b/src/network/kernel/qdnslookup_unix.cpp
index ce1ec6442a..ee7484ab35 100644
--- a/src/network/kernel/qdnslookup_unix.cpp
+++ b/src/network/kernel/qdnslookup_unix.cpp
@@ -59,6 +59,10 @@
# include <gnu/lib-names.h>
#endif
+#if defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen)
+# include <dlfcn.h>
+#endif
+
#include <cstring>
QT_BEGIN_NAMESPACE
@@ -87,6 +91,18 @@ struct QDnsLookupStateDeleter
}
};
+static QFunctionPointer resolveSymbol(QLibrary &lib, const char *sym)
+{
+ if (lib.isLoaded())
+ return lib.resolve(sym);
+
+#if defined(RTLD_DEFAULT) && (defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen))
+ return reinterpret_cast<QFunctionPointer>(dlsym(RTLD_DEFAULT, sym));
+#else
+ return nullptr;
+#endif
+}
+
static bool resolveLibraryInternal()
{
QLibrary lib;
@@ -96,31 +112,30 @@ static bool resolveLibraryInternal()
#endif
{
lib.setFileName(QLatin1String("resolv"));
- if (!lib.load())
- return false;
+ lib.load();
}
- local_dn_expand = dn_expand_proto(lib.resolve("__dn_expand"));
+ local_dn_expand = dn_expand_proto(resolveSymbol(lib, "__dn_expand"));
if (!local_dn_expand)
- local_dn_expand = dn_expand_proto(lib.resolve("dn_expand"));
+ local_dn_expand = dn_expand_proto(resolveSymbol(lib, "dn_expand"));
- local_res_nclose = res_nclose_proto(lib.resolve("__res_nclose"));
+ local_res_nclose = res_nclose_proto(resolveSymbol(lib, "__res_nclose"));
if (!local_res_nclose)
- local_res_nclose = res_nclose_proto(lib.resolve("res_9_nclose"));
+ local_res_nclose = res_nclose_proto(resolveSymbol(lib, "res_9_nclose"));
if (!local_res_nclose)
- local_res_nclose = res_nclose_proto(lib.resolve("res_nclose"));
+ local_res_nclose = res_nclose_proto(resolveSymbol(lib, "res_nclose"));
- local_res_ninit = res_ninit_proto(lib.resolve("__res_ninit"));
+ local_res_ninit = res_ninit_proto(resolveSymbol(lib, "__res_ninit"));
if (!local_res_ninit)
- local_res_ninit = res_ninit_proto(lib.resolve("res_9_ninit"));
+ local_res_ninit = res_ninit_proto(resolveSymbol(lib, "res_9_ninit"));
if (!local_res_ninit)
- local_res_ninit = res_ninit_proto(lib.resolve("res_ninit"));
+ local_res_ninit = res_ninit_proto(resolveSymbol(lib, "res_ninit"));
- local_res_nquery = res_nquery_proto(lib.resolve("__res_nquery"));
+ local_res_nquery = res_nquery_proto(resolveSymbol(lib, "__res_nquery"));
if (!local_res_nquery)
- local_res_nquery = res_nquery_proto(lib.resolve("res_9_nquery"));
+ local_res_nquery = res_nquery_proto(resolveSymbol(lib, "res_9_nquery"));
if (!local_res_nquery)
- local_res_nquery = res_nquery_proto(lib.resolve("res_nquery"));
+ local_res_nquery = res_nquery_proto(resolveSymbol(lib, "res_nquery"));
return true;
}
diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp
index 27b5f570dc..644f4336ca 100644
--- a/src/network/kernel/qhostaddress.cpp
+++ b/src/network/kernel/qhostaddress.cpp
@@ -574,7 +574,8 @@ QHostAddress &QHostAddress::operator=(SpecialAddress address)
*/
/*!
- Sets the host address to null.
+ Sets the host address to null and sets the protocol to
+ QAbstractSocket::UnknownNetworkLayerProtocol.
\sa QHostAddress::Null
*/
@@ -1332,7 +1333,7 @@ QDebug operator<<(QDebug d, const QHostAddress &address)
\relates QHostAddress
Returns a hash of the host address \a key, using \a seed to seed the calculation.
*/
-uint qHash(const QHostAddress &key, uint seed) Q_DECL_NOTHROW
+uint qHash(const QHostAddress &key, uint seed) noexcept
{
return qHashBits(key.d->a6.c, 16, seed);
}
diff --git a/src/network/kernel/qhostaddress.h b/src/network/kernel/qhostaddress.h
index 00555f3d8e..f20da3304f 100644
--- a/src/network/kernel/qhostaddress.h
+++ b/src/network/kernel/qhostaddress.h
@@ -66,7 +66,7 @@ typedef QIPv6Address Q_IPV6ADDR;
class QHostAddress;
// qHash is a friend, but we can't use default arguments for friends (§8.3.6.4)
-Q_NETWORK_EXPORT uint qHash(const QHostAddress &key, uint seed = 0) Q_DECL_NOTHROW;
+Q_NETWORK_EXPORT uint qHash(const QHostAddress &key, uint seed = 0) noexcept;
class Q_NETWORK_EXPORT QHostAddress
{
@@ -103,7 +103,7 @@ public:
~QHostAddress();
#ifdef Q_COMPILER_RVALUE_REFS
- QHostAddress &operator=(QHostAddress &&other) Q_DECL_NOTHROW
+ QHostAddress &operator=(QHostAddress &&other) noexcept
{ swap(other); return *this; }
#endif
@@ -114,7 +114,7 @@ public:
#endif
QHostAddress &operator=(SpecialAddress address);
- void swap(QHostAddress &other) Q_DECL_NOTHROW { d.swap(other.d); }
+ void swap(QHostAddress &other) noexcept { d.swap(other.d); }
void setAddress(quint32 ip4Addr);
void setAddress(quint8 *ip6Addr); // ### Qt 6: remove me
@@ -157,7 +157,7 @@ public:
static QPair<QHostAddress, int> parseSubnet(const QString &subnet);
- friend Q_NETWORK_EXPORT uint qHash(const QHostAddress &key, uint seed) Q_DECL_NOTHROW;
+ friend Q_NETWORK_EXPORT uint qHash(const QHostAddress &key, uint seed) noexcept;
protected:
friend class QHostAddressPrivate;
QExplicitlySharedDataPointer<QHostAddressPrivate> d;
diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp
index 0973d0dd52..71d1aae35a 100644
--- a/src/network/kernel/qhostinfo.cpp
+++ b/src/network/kernel/qhostinfo.cpp
@@ -64,8 +64,8 @@ Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager)
namespace {
struct ToBeLookedUpEquals {
typedef bool result_type;
- explicit ToBeLookedUpEquals(const QString &toBeLookedUp) Q_DECL_NOTHROW : m_toBeLookedUp(toBeLookedUp) {}
- result_type operator()(QHostInfoRunnable* lookup) const Q_DECL_NOTHROW
+ explicit ToBeLookedUpEquals(const QString &toBeLookedUp) noexcept : m_toBeLookedUp(toBeLookedUp) {}
+ result_type operator()(QHostInfoRunnable* lookup) const noexcept
{
return m_toBeLookedUp == lookup->toBeLookedUp;
}
@@ -300,25 +300,6 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver,
*/
/*!
- \fn template<typename PointerToMemberFunction> int QHostInfo::lookupHost(const QString &name, const QObject *receiver, PointerToMemberFunction function)
-
- \since 5.9
-
- \overload
-
- Looks up the IP address(es) associated with host name \a name, and
- returns an ID for the lookup. When the result of the lookup is
- ready, the slot or signal \a function in \a receiver is called with
- a QHostInfo argument. The QHostInfo object can then be inspected
- to get the results of the lookup.
-
- \note There is no guarantee on the order the signals will be emitted
- if you start multiple requests with lookupHost().
-
- \sa abortHostLookup(), addresses(), error(), fromName()
-*/
-
-/*!
\fn template<typename Functor> int QHostInfo::lookupHost(const QString &name, Functor functor)
\since 5.9
@@ -354,6 +335,16 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver,
thread of \a context. The context's thread must have a running Qt
event loop.
+ Here is an alternative signature for the function:
+ \code
+ lookupHost(const QString &name, const QObject *receiver, PointerToMemberFunction function)
+ \endcode
+
+ In this case, when the result of the lookup is ready, the slot or
+ signal \c{function} in \c{receiver} is called with a QHostInfo
+ argument. The QHostInfo object can then be inspected to get the
+ results of the lookup.
+
\note There is no guarantee on the order the signals will be emitted
if you start multiple requests with lookupHost().
diff --git a/src/network/kernel/qhostinfo.h b/src/network/kernel/qhostinfo.h
index 75917a02a3..dc31cc08e4 100644
--- a/src/network/kernel/qhostinfo.h
+++ b/src/network/kernel/qhostinfo.h
@@ -63,10 +63,10 @@ public:
explicit QHostInfo(int lookupId = -1);
QHostInfo(const QHostInfo &d);
QHostInfo &operator=(const QHostInfo &d);
- QHostInfo &operator=(QHostInfo &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QHostInfo &operator=(QHostInfo &&other) noexcept { swap(other); return *this; }
~QHostInfo();
- void swap(QHostInfo &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QHostInfo &other) noexcept { qSwap(d, other.d); }
QString hostName() const;
void setHostName(const QString &name);
@@ -91,13 +91,10 @@ public:
static QString localDomainName();
#ifdef Q_CLANG_QDOC
- template<typename PointerToMemberFunction>
- static int QHostInfo::lookupHost(const QString &name, const QObject *receiver,
- PointerToMemberFunction function);
template<typename Functor>
- static int QHostInfo::lookupHost(const QString &name, Functor functor);
+ static int lookupHost(const QString &name, Functor functor);
template<typename Functor>
- static int QHostInfo::lookupHost(const QString &name, const QObject *context, Functor functor);
+ static int lookupHost(const QString &name, const QObject *context, Functor functor);
#else
// lookupHost to a QObject slot
template <typename Func>
diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h
index 8cce302166..da02163ddf 100644
--- a/src/network/kernel/qhostinfo_p.h
+++ b/src/network/kernel/qhostinfo_p.h
@@ -101,7 +101,7 @@ public Q_SLOTS:
{
if (slotObj) {
QHostInfo copy = info;
- void *args[2] = { 0, reinterpret_cast<void *>(&copy) };
+ void *args[2] = { nullptr, reinterpret_cast<void *>(&copy) };
slotObj->call(const_cast<QObject*>(receiver.data()), args);
slotObj->destroyIfLastRef();
} else {
diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp
index d22608e22f..e4810d68ee 100644
--- a/src/network/kernel/qhostinfo_unix.cpp
+++ b/src/network/kernel/qhostinfo_unix.cpp
@@ -66,6 +66,10 @@
# include <gnu/lib-names.h>
#endif
+#if defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen)
+# include <dlfcn.h>
+#endif
+
QT_BEGIN_NAMESPACE
// Almost always the same. If not, specify in qplatformdefs.h.
@@ -115,6 +119,18 @@ struct LibResolv
};
}
+static QFunctionPointer resolveSymbol(QLibrary &lib, const char *sym)
+{
+ if (lib.isLoaded())
+ return lib.resolve(sym);
+
+#if defined(RTLD_DEFAULT) && (defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen))
+ return reinterpret_cast<QFunctionPointer>(dlsym(RTLD_DEFAULT, sym));
+#else
+ return nullptr;
+#endif
+}
+
LibResolv::LibResolv()
{
QLibrary lib;
@@ -124,31 +140,30 @@ LibResolv::LibResolv()
#endif
{
lib.setFileName(QLatin1String("resolv"));
- if (!lib.load())
- return;
+ lib.load();
}
// res_ninit is required for localDomainName()
- local_res_ninit = res_ninit_proto(lib.resolve("__res_ninit"));
+ local_res_ninit = res_ninit_proto(resolveSymbol(lib, "__res_ninit"));
if (!local_res_ninit)
- local_res_ninit = res_ninit_proto(lib.resolve("res_ninit"));
+ local_res_ninit = res_ninit_proto(resolveSymbol(lib, "res_ninit"));
if (local_res_ninit) {
// we must now find res_nclose
- local_res_nclose = res_nclose_proto(lib.resolve("res_nclose"));
+ local_res_nclose = res_nclose_proto(resolveSymbol(lib, "res_nclose"));
if (!local_res_nclose)
- local_res_nclose = res_nclose_proto(lib.resolve("__res_nclose"));
+ local_res_nclose = res_nclose_proto(resolveSymbol(lib, "__res_nclose"));
if (!local_res_nclose)
local_res_ninit = nullptr;
}
if (ReinitNecessary || !local_res_ninit) {
- local_res_init = res_init_proto(lib.resolve("__res_init"));
+ local_res_init = res_init_proto(resolveSymbol(lib, "__res_init"));
if (!local_res_init)
- local_res_init = res_init_proto(lib.resolve("res_init"));
+ local_res_init = res_init_proto(resolveSymbol(lib, "res_init"));
if (local_res_init && !local_res_ninit) {
// if we can't get a thread-safe context, we have to use the global _res state
- local_res = res_state_ptr(lib.resolve("_res"));
+ local_res = res_state_ptr(resolveSymbol(lib, "_res"));
}
}
}
diff --git a/src/network/kernel/qnetworkdatagram.cpp b/src/network/kernel/qnetworkdatagram.cpp
index 50421fa7f5..c8c87d4549 100644
--- a/src/network/kernel/qnetworkdatagram.cpp
+++ b/src/network/kernel/qnetworkdatagram.cpp
@@ -450,16 +450,7 @@ void QNetworkDatagram::setData(const QByteArray &data)
way of responding to a datagram back to the original sender.
Example:
- \code
- void Server::readPendingDatagrams()
- {
- while (udpSocket->hasPendingDatagrams()) {
- QNetworkDatagram datagram = udpSocket->receiveDatagram();
- QByteArray replyData = processThePayload(datagram.data());
- udpSocket->writeDatagram(datagram.makeReply(replyData));
- }
- }
- \endcode
+ \snippet code/src_network_kernel_qnetworkdatagram.cpp 0
This function is especially convenient since it will automatically copy
parameters from this datagram to the new datagram as appropriate:
@@ -491,9 +482,7 @@ void QNetworkDatagram::setData(const QByteArray &data)
overloads, so it is a good idea to make sure this object is rvalue, if
possible, before calling makeReply, so as to make better use of move
semantics. To achieve that, the example above would use:
- \code
- udpSocket->writeDatagram(std::move(datagram).makeReply(replyData));
- \endcode
+ \snippet code/src_network_kernel_qnetworkdatagram.cpp 1
*/
diff --git a/src/network/kernel/qnetworkdatagram.h b/src/network/kernel/qnetworkdatagram.h
index 1acb44a1e0..70958fea42 100644
--- a/src/network/kernel/qnetworkdatagram.h
+++ b/src/network/kernel/qnetworkdatagram.h
@@ -61,13 +61,13 @@ public:
~QNetworkDatagram()
{ if (d) destroy(d); }
- QNetworkDatagram(QNetworkDatagram &&other) Q_DECL_NOTHROW
+ QNetworkDatagram(QNetworkDatagram &&other) noexcept
: d(other.d)
{ other.d = nullptr; }
- QNetworkDatagram &operator=(QNetworkDatagram &&other) Q_DECL_NOTHROW
+ QNetworkDatagram &operator=(QNetworkDatagram &&other) noexcept
{ swap(other); return *this; }
- void swap(QNetworkDatagram &other) Q_DECL_NOTHROW
+ void swap(QNetworkDatagram &other) noexcept
{ qSwap(d, other.d); }
void clear();
diff --git a/src/network/kernel/qnetworkinterface.cpp b/src/network/kernel/qnetworkinterface.cpp
index 75a26848a6..d43dba3e0c 100644
--- a/src/network/kernel/qnetworkinterface.cpp
+++ b/src/network/kernel/qnetworkinterface.cpp
@@ -798,9 +798,7 @@ QList<QNetworkAddressEntry> QNetworkInterface::addressEntries() const
no interface with that name. This function should produce the same result
as the following code, but will probably execute faster.
- \code
- QNetworkInterface::interfaceFromName(name).index()
- \endcode
+ \snippet code/src_network_kernel_qnetworkinterface.cpp 0
\sa interfaceFromName(), interfaceNameFromIndex(), QNetworkDatagram::interfaceIndex()
*/
@@ -858,9 +856,7 @@ QNetworkInterface QNetworkInterface::interfaceFromIndex(int index)
produce the same result as the following code, but will probably execute
faster.
- \code
- QNetworkInterface::interfaceFromIndex(index).name()
- \endcode
+ \snippet code/src_network_kernel_qnetworkinterface.cpp 1
\sa interfaceFromIndex(), interfaceIndexFromName(), QNetworkDatagram::interfaceIndex()
*/
diff --git a/src/network/kernel/qnetworkinterface.h b/src/network/kernel/qnetworkinterface.h
index 148fd5e10d..1d3286118e 100644
--- a/src/network/kernel/qnetworkinterface.h
+++ b/src/network/kernel/qnetworkinterface.h
@@ -65,12 +65,12 @@ public:
QNetworkAddressEntry();
QNetworkAddressEntry(const QNetworkAddressEntry &other);
#ifdef Q_COMPILER_RVALUE_REFS
- QNetworkAddressEntry &operator=(QNetworkAddressEntry &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QNetworkAddressEntry &operator=(QNetworkAddressEntry &&other) noexcept { swap(other); return *this; }
#endif
QNetworkAddressEntry &operator=(const QNetworkAddressEntry &other);
~QNetworkAddressEntry();
- void swap(QNetworkAddressEntry &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QNetworkAddressEntry &other) noexcept { qSwap(d, other.d); }
bool operator==(const QNetworkAddressEntry &other) const;
inline bool operator!=(const QNetworkAddressEntry &other) const
@@ -143,12 +143,12 @@ public:
QNetworkInterface();
QNetworkInterface(const QNetworkInterface &other);
#ifdef Q_COMPILER_RVALUE_REFS
- QNetworkInterface &operator=(QNetworkInterface &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QNetworkInterface &operator=(QNetworkInterface &&other) noexcept { swap(other); return *this; }
#endif
QNetworkInterface &operator=(const QNetworkInterface &other);
~QNetworkInterface();
- void swap(QNetworkInterface &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QNetworkInterface &other) noexcept { qSwap(d, other.d); }
bool isValid() const;
diff --git a/src/network/kernel/qnetworkinterface_p.h b/src/network/kernel/qnetworkinterface_p.h
index 87a46b75fa..44e27a7e34 100644
--- a/src/network/kernel/qnetworkinterface_p.h
+++ b/src/network/kernel/qnetworkinterface_p.h
@@ -82,7 +82,7 @@ public:
class QNetworkInterfacePrivate: public QSharedData
{
public:
- QNetworkInterfacePrivate() : index(0), flags(0)
+ QNetworkInterfacePrivate() : index(0), flags(nullptr)
{ }
~QNetworkInterfacePrivate()
{ }
diff --git a/src/network/kernel/qnetworkinterface_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp
index d69fc47667..c28c5ea9e6 100644
--- a/src/network/kernel/qnetworkinterface_unix.cpp
+++ b/src/network/kernel/qnetworkinterface_unix.cpp
@@ -38,6 +38,7 @@
**
****************************************************************************/
+#include "qbytearray.h"
#include "qset.h"
#include "qnetworkinterface.h"
#include "qnetworkinterface_p.h"
@@ -463,8 +464,10 @@ static QNetworkInterface::InterfaceType probeIfType(int socket, int iftype, stru
case IFM_ETHER:
return QNetworkInterface::Ethernet;
+#ifdef IFM_FDDI
case IFM_FDDI:
return QNetworkInterface::Fddi;
+#endif
case IFM_IEEE80211:
return QNetworkInterface::Ieee80211;
@@ -500,7 +503,7 @@ static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
iface->flags = convertFlags(ptr->ifa_flags);
iface->hardwareAddress = iface->makeHwAddress(sdl->sdl_alen, (uchar*)LLADDR(sdl));
- strlcpy(mediareq.ifm_name, ptr->ifa_name, sizeof(mediareq.ifm_name));
+ qstrncpy(mediareq.ifm_name, ptr->ifa_name, sizeof(mediareq.ifm_name));
iface->type = probeIfType(openSocket(socket), sdl->sdl_type, &mediareq);
iface->mtu = getMtu(socket, &req);
}
@@ -524,7 +527,7 @@ static void getAddressExtraInfo(QNetworkAddressEntry *entry, struct sockaddr *sa
return;
}
- strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ qstrncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
// get flags
ifr.ifr_addr = *reinterpret_cast<struct sockaddr_in6 *>(sa);
diff --git a/src/network/kernel/qnetworkinterface_unix_p.h b/src/network/kernel/qnetworkinterface_unix_p.h
index c085194e3c..553af5a303 100644
--- a/src/network/kernel/qnetworkinterface_unix_p.h
+++ b/src/network/kernel/qnetworkinterface_unix_p.h
@@ -80,7 +80,7 @@ QT_BEGIN_NAMESPACE
static QNetworkInterface::InterfaceFlags convertFlags(uint rawFlags)
{
- QNetworkInterface::InterfaceFlags flags = 0;
+ QNetworkInterface::InterfaceFlags flags = nullptr;
flags |= (rawFlags & IFF_UP) ? QNetworkInterface::IsUp : QNetworkInterface::InterfaceFlag(0);
flags |= (rawFlags & IFF_RUNNING) ? QNetworkInterface::IsRunning : QNetworkInterface::InterfaceFlag(0);
flags |= (rawFlags & IFF_BROADCAST) ? QNetworkInterface::CanBroadcast : QNetworkInterface::InterfaceFlag(0);
diff --git a/src/network/kernel/qnetworkproxy.h b/src/network/kernel/qnetworkproxy.h
index 7e3e6906a8..0b1bc02695 100644
--- a/src/network/kernel/qnetworkproxy.h
+++ b/src/network/kernel/qnetworkproxy.h
@@ -90,12 +90,12 @@ public:
#endif
QNetworkProxyQuery(const QNetworkProxyQuery &other);
#ifdef Q_COMPILER_RVALUE_REFS
- QNetworkProxyQuery &operator=(QNetworkProxyQuery &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QNetworkProxyQuery &operator=(QNetworkProxyQuery &&other) noexcept { swap(other); return *this; }
#endif
QNetworkProxyQuery &operator=(const QNetworkProxyQuery &other);
~QNetworkProxyQuery();
- void swap(QNetworkProxyQuery &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QNetworkProxyQuery &other) noexcept { qSwap(d, other.d); }
bool operator==(const QNetworkProxyQuery &other) const;
inline bool operator!=(const QNetworkProxyQuery &other) const
@@ -162,12 +162,12 @@ public:
const QString &user = QString(), const QString &password = QString());
QNetworkProxy(const QNetworkProxy &other);
#ifdef Q_COMPILER_RVALUE_REFS
- QNetworkProxy &operator=(QNetworkProxy &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QNetworkProxy &operator=(QNetworkProxy &&other) noexcept { swap(other); return *this; }
#endif
QNetworkProxy &operator=(const QNetworkProxy &other);
~QNetworkProxy();
- void swap(QNetworkProxy &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QNetworkProxy &other) noexcept { qSwap(d, other.d); }
bool operator==(const QNetworkProxy &other) const;
inline bool operator!=(const QNetworkProxy &other) const
diff --git a/src/network/kernel/qnetworkproxy_win.cpp b/src/network/kernel/qnetworkproxy_win.cpp
index db51732bd3..56397814b0 100644
--- a/src/network/kernel/qnetworkproxy_win.cpp
+++ b/src/network/kernel/qnetworkproxy_win.cpp
@@ -370,7 +370,7 @@ static QList<QNetworkProxy> parseServerList(const QNetworkProxyQuery &query, con
#if !defined(Q_OS_WINRT)
namespace {
class QRegistryWatcher {
- Q_DISABLE_COPY(QRegistryWatcher)
+ Q_DISABLE_COPY_MOVE(QRegistryWatcher)
public:
QRegistryWatcher() = default;
@@ -425,7 +425,7 @@ private:
class QWindowsSystemProxy
{
- Q_DISABLE_COPY(QWindowsSystemProxy)
+ Q_DISABLE_COPY_MOVE(QWindowsSystemProxy)
public:
QWindowsSystemProxy();
~QWindowsSystemProxy();
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
index 4d9fda00ce..9c8f29e18a 100644
--- a/src/network/socket/qabstractsocket.cpp
+++ b/src/network/socket/qabstractsocket.cpp
@@ -424,7 +424,7 @@
Note that by combining this option with ReuseAddressHint, you will
also allow your service to rebind an existing shared address. On
Unix, this is equivalent to the SO_REUSEADDR socket option. On Windows,
- this option is ignored.
+ this is the default behavior, so this option is ignored.
\value DontShareAddress Bind the address and port exclusively, so that
no other services are allowed to rebind. By passing this option to
@@ -444,7 +444,7 @@
\value DefaultForPlatform The default option for the current platform.
On Unix and \macos, this is equivalent to (DontShareAddress
- + ReuseAddressHint), and on Windows, its equivalent to ShareAddress.
+ + ReuseAddressHint), and on Windows, it is equivalent to ShareAddress.
*/
/*! \enum QAbstractSocket::PauseMode
@@ -455,7 +455,7 @@
The only notification currently supported is QSslSocket::sslErrors().
\value PauseNever Do not pause data transfer on the socket. This is the
- default and matches the behaviour of Qt 4.
+ default and matches the behavior of Qt 4.
\value PauseOnSslErrors Pause data transfer on the socket upon receiving an
SSL error notification. I.E. QSslSocket::sslErrors().
*/
@@ -914,7 +914,7 @@ void QAbstractSocketPrivate::resolveProxy(const QString &hostname, quint16 port)
proxies << proxy;
} else {
// try the application settings instead
- QNetworkProxyQuery query(hostname, port, QString(),
+ QNetworkProxyQuery query(hostname, port, protocolTag,
socketType == QAbstractSocket::TcpSocket ?
QNetworkProxyQuery::TcpSocket :
socketType == QAbstractSocket::SctpSocket ?
@@ -1538,11 +1538,9 @@ void QAbstractSocket::setPauseMode(PauseModes pauseMode)
Binds to \a address on port \a port, using the BindMode \a mode.
- Binds this socket to the address \a address and the port \a port.
-
For UDP sockets, after binding, the signal QUdpSocket::readyRead() is emitted
whenever a UDP datagram arrives on the specified address and port.
- Thus, This function is useful to write UDP servers.
+ Thus, this function is useful to write UDP servers.
For TCP sockets, this function may be used to specify which interface to use
for an outgoing connection, which is useful in case of multiple network
@@ -1551,7 +1549,7 @@ void QAbstractSocket::setPauseMode(PauseModes pauseMode)
By default, the socket is bound using the DefaultForPlatform BindMode.
If a port is not specified, a random port is chosen.
- On success, the functions returns \c true and the socket enters
+ On success, the function returns \c true and the socket enters
BoundState; otherwise it returns \c false.
*/
@@ -2961,6 +2959,38 @@ QNetworkProxy QAbstractSocket::proxy() const
Q_D(const QAbstractSocket);
return d->proxy;
}
+
+/*!
+ \since 5.13
+
+ Returns the protocol tag for this socket.
+ If the protocol tag is set then this is passed to QNetworkProxyQuery
+ when this is created internally to indicate the protocol tag to be
+ used.
+
+ \sa setProtocolTag(), QNetworkProxyQuery
+*/
+
+QString QAbstractSocket::protocolTag() const
+{
+ Q_D(const QAbstractSocket);
+ return d->protocolTag;
+}
+
+/*!
+ \since 5.13
+
+ Sets the protocol tag for this socket to \a tag.
+
+ \sa protocolTag()
+*/
+
+void QAbstractSocket::setProtocolTag(const QString &tag)
+{
+ Q_D(QAbstractSocket);
+ d->protocolTag = tag;
+}
+
#endif // QT_NO_NETWORKPROXY
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h
index 6d5e57ac52..de09195eeb 100644
--- a/src/network/socket/qabstractsocket.h
+++ b/src/network/socket/qabstractsocket.h
@@ -197,6 +197,8 @@ public:
#ifndef QT_NO_NETWORKPROXY
void setProxy(const QNetworkProxy &networkProxy);
QNetworkProxy proxy() const;
+ QString protocolTag() const;
+ void setProtocolTag(const QString &tag);
#endif
Q_SIGNALS:
diff --git a/src/network/socket/qabstractsocket_p.h b/src/network/socket/qabstractsocket_p.h
index 066a35ff85..5aa69d747e 100644
--- a/src/network/socket/qabstractsocket_p.h
+++ b/src/network/socket/qabstractsocket_p.h
@@ -83,7 +83,7 @@ public:
#ifndef QT_NO_NETWORKPROXY
inline void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator) override {
Q_Q(QAbstractSocket);
- q->proxyAuthenticationRequired(proxy, authenticator);
+ emit q->proxyAuthenticationRequired(proxy, authenticator);
}
#endif
@@ -124,6 +124,7 @@ public:
#ifndef QT_NO_NETWORKPROXY
QNetworkProxy proxy;
QNetworkProxy proxyInUse;
+ QString protocolTag;
void resolveProxy(const QString &hostName, quint16 port);
#else
inline void resolveProxy(const QString &, quint16) { }
diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h
index b15dd73c96..112e7032d6 100644
--- a/src/network/socket/qabstractsocketengine_p.h
+++ b/src/network/socket/qabstractsocketengine_p.h
@@ -88,7 +88,7 @@ public:
static QAbstractSocketEngine *createSocketEngine(QAbstractSocket::SocketType socketType, const QNetworkProxy &, QObject *parent);
static QAbstractSocketEngine *createSocketEngine(qintptr socketDescriptor, QObject *parent);
- QAbstractSocketEngine(QObject *parent = 0);
+ QAbstractSocketEngine(QObject *parent = nullptr);
enum SocketOption {
NonBlockingSocketOption,
@@ -155,7 +155,7 @@ public:
virtual qint64 pendingDatagramSize() const = 0;
#endif // QT_NO_UDPSOCKET
- virtual qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header = 0,
+ virtual qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header = nullptr,
PacketHeaderOptions = WantNone) = 0;
virtual qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header) = 0;
virtual qint64 bytesToWrite() const = 0;
@@ -163,11 +163,11 @@ public:
virtual int option(SocketOption option) const = 0;
virtual bool setOption(SocketOption option, int value) = 0;
- virtual bool waitForRead(int msecs = 30000, bool *timedOut = 0) = 0;
- virtual bool waitForWrite(int msecs = 30000, bool *timedOut = 0) = 0;
+ virtual bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) = 0;
+ virtual bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) = 0;
virtual bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
- int msecs = 30000, bool *timedOut = 0) = 0;
+ int msecs = 30000, bool *timedOut = nullptr) = 0;
QAbstractSocket::SocketError error() const;
QString errorString() const;
@@ -202,7 +202,7 @@ public Q_SLOTS:
public:
void setReceiver(QAbstractSocketEngineReceiver *receiver);
protected:
- QAbstractSocketEngine(QAbstractSocketEnginePrivate &dd, QObject* parent = 0);
+ QAbstractSocketEngine(QAbstractSocketEnginePrivate &dd, QObject* parent = nullptr);
void setError(QAbstractSocket::SocketError error, const QString &errorString) const;
void setState(QAbstractSocket::SocketState state);
@@ -215,7 +215,7 @@ protected:
private:
Q_DECLARE_PRIVATE(QAbstractSocketEngine)
- Q_DISABLE_COPY(QAbstractSocketEngine)
+ Q_DISABLE_COPY_MOVE(QAbstractSocketEngine)
};
class QAbstractSocketEnginePrivate : public QObjectPrivate
diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp
index 1dd99112c2..6be1e9443e 100644
--- a/src/network/socket/qhttpsocketengine.cpp
+++ b/src/network/socket/qhttpsocketengine.cpp
@@ -524,7 +524,7 @@ void QHttpSocketEngine::slotSocketConnected()
//qDebug() << "slotSocketConnected: priv=" << priv << (priv ? (int)priv->method : -1);
if (priv && priv->method != QAuthenticatorPrivate::None) {
d->credentialsSent = true;
- data += "Proxy-Authorization: " + priv->calculateResponse(method, path);
+ data += "Proxy-Authorization: " + priv->calculateResponse(method, path, d->proxy.hostName());
data += "\r\n";
}
data += "\r\n";
@@ -649,7 +649,7 @@ void QHttpSocketEngine::slotSocketReadNotification()
}
if (priv->phase == QAuthenticatorPrivate::Done)
- emit proxyAuthenticationRequired(d->proxy, &d->authenticator);
+ proxyAuthenticationRequired(d->proxy, &d->authenticator);
// priv->phase will get reset to QAuthenticatorPrivate::Start if the authenticator got modified in the signal above.
if (priv->phase == QAuthenticatorPrivate::Done) {
setError(QAbstractSocket::ProxyAuthenticationRequiredError, tr("Authentication required"));
@@ -771,7 +771,7 @@ void QHttpSocketEngine::emitPendingReadNotification()
Q_D(QHttpSocketEngine);
d->readNotificationPending = false;
if (d->readNotificationEnabled)
- emit readNotification();
+ readNotification();
}
void QHttpSocketEngine::emitPendingWriteNotification()
@@ -779,14 +779,14 @@ void QHttpSocketEngine::emitPendingWriteNotification()
Q_D(QHttpSocketEngine);
d->writeNotificationPending = false;
if (d->writeNotificationEnabled)
- emit writeNotification();
+ writeNotification();
}
void QHttpSocketEngine::emitPendingConnectionNotification()
{
Q_D(QHttpSocketEngine);
d->connectionNotificationPending = false;
- emit connectionNotification();
+ connectionNotification();
}
void QHttpSocketEngine::emitReadNotification()
diff --git a/src/network/socket/qhttpsocketengine_p.h b/src/network/socket/qhttpsocketengine_p.h
index cb7798694a..0c2c450c81 100644
--- a/src/network/socket/qhttpsocketengine_p.h
+++ b/src/network/socket/qhttpsocketengine_p.h
@@ -79,7 +79,7 @@ public:
ReadResponseContent,
ReadResponseHeader
};
- QHttpSocketEngine(QObject *parent = 0);
+ QHttpSocketEngine(QObject *parent = nullptr);
~QHttpSocketEngine();
bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol) override;
@@ -126,11 +126,11 @@ public:
int option(SocketOption option) const override;
bool setOption(SocketOption option, int value) override;
- bool waitForRead(int msecs = 30000, bool *timedOut = 0) override;
- bool waitForWrite(int msecs = 30000, bool *timedOut = 0) override;
+ bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) override;
+ bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) override;
bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
- int msecs = 30000, bool *timedOut = 0) override;
+ int msecs = 30000, bool *timedOut = nullptr) override;
bool isReadNotificationEnabled() const override;
void setReadNotificationEnabled(bool enable) override;
@@ -160,7 +160,7 @@ private:
bool readHttpHeader();
Q_DECLARE_PRIVATE(QHttpSocketEngine)
- Q_DISABLE_COPY(QHttpSocketEngine)
+ Q_DISABLE_COPY_MOVE(QHttpSocketEngine)
};
diff --git a/src/network/socket/qlocalserver.cpp b/src/network/socket/qlocalserver.cpp
index a9789b7d04..c5bd599a51 100644
--- a/src/network/socket/qlocalserver.cpp
+++ b/src/network/socket/qlocalserver.cpp
@@ -506,8 +506,8 @@ void QLocalServer::setMaxPendingConnections(int numConnections)
/*!
Waits for at most \a msec milliseconds or until an incoming connection
is available. Returns \c true if a connection is available; otherwise
- returns \c false. If the operation timed out and \a timedOut is not 0,
- *timedOut will be set to true.
+ returns \c false. If the operation timed out and \a timedOut is not
+ \nullptr, *timedOut will be set to true.
This is a blocking function call. Its use is ill-advised in a
single-threaded GUI application, since the whole application will stop
diff --git a/src/network/socket/qlocalserver_p.h b/src/network/socket/qlocalserver_p.h
index 2c073908cb..92616e59ce 100644
--- a/src/network/socket/qlocalserver_p.h
+++ b/src/network/socket/qlocalserver_p.h
@@ -78,7 +78,7 @@ class QLocalServerPrivate : public QObjectPrivate
public:
QLocalServerPrivate() :
#if !defined(QT_LOCALSOCKET_TCP) && !defined(Q_OS_WIN)
- listenSocket(-1), socketNotifier(0),
+ listenSocket(-1), socketNotifier(nullptr),
#endif
maxPendingConnections(30), error(QAbstractSocket::UnknownSocketError),
socketOptions(QLocalServer::NoOptions)
diff --git a/src/network/socket/qlocalsocket_p.h b/src/network/socket/qlocalsocket_p.h
index 8b72da397f..d93b53be0c 100644
--- a/src/network/socket/qlocalsocket_p.h
+++ b/src/network/socket/qlocalsocket_p.h
@@ -131,7 +131,6 @@ public:
#elif defined(Q_OS_WIN)
~QLocalSocketPrivate();
void destroyPipeHandles();
- void setErrorString(const QString &function);
void _q_canWrite();
void _q_pipeClosed();
void _q_winError(ulong windowsError, const QString &function);
diff --git a/src/network/socket/qlocalsocket_win.cpp b/src/network/socket/qlocalsocket_win.cpp
index 8e20f9efbe..4decbd5ded 100644
--- a/src/network/socket/qlocalsocket_win.cpp
+++ b/src/network/socket/qlocalsocket_win.cpp
@@ -50,12 +50,6 @@ void QLocalSocketPrivate::init()
q->connect(pipeReader, SIGNAL(winError(ulong,QString)), SLOT(_q_winError(ulong,QString)));
}
-void QLocalSocketPrivate::setErrorString(const QString &function)
-{
- DWORD windowsError = GetLastError();
- _q_winError(windowsError, function);
-}
-
void QLocalSocketPrivate::_q_winError(ulong windowsError, const QString &function)
{
Q_Q(QLocalSocket);
@@ -93,9 +87,9 @@ void QLocalSocketPrivate::_q_winError(ulong windowsError, const QString &functio
}
if (currentState != state) {
- q->emit stateChanged(state);
+ emit q->stateChanged(state);
if (state == QLocalSocket::UnconnectedState && currentState != QLocalSocket::ConnectingState)
- q->emit disconnected();
+ emit q->disconnected();
}
emit q->error(error);
}
@@ -127,7 +121,8 @@ void QLocalSocket::connectToServer(OpenMode openMode)
{
Q_D(QLocalSocket);
if (state() == ConnectedState || state() == ConnectingState) {
- setErrorString(tr("Trying to connect while connection is in progress"));
+ d->error = OperationError;
+ d->errorString = tr("Trying to connect while connection is in progress");
emit error(QLocalSocket::OperationError);
return;
}
@@ -137,8 +132,8 @@ void QLocalSocket::connectToServer(OpenMode openMode)
d->state = ConnectingState;
emit stateChanged(d->state);
if (d->serverName.isEmpty()) {
- d->error = QLocalSocket::ServerNotFoundError;
- setErrorString(QLocalSocket::tr("%1: Invalid name").arg(QLatin1String("QLocalSocket::connectToServer")));
+ d->error = ServerNotFoundError;
+ d->errorString = tr("%1: Invalid name").arg(QLatin1String("QLocalSocket::connectToServer"));
d->state = UnconnectedState;
emit error(d->error);
emit stateChanged(d->state);
@@ -177,7 +172,8 @@ void QLocalSocket::connectToServer(OpenMode openMode)
}
if (localSocket == INVALID_HANDLE_VALUE) {
- d->setErrorString(QLatin1String("QLocalSocket::connectToServer"));
+ const DWORD winError = GetLastError();
+ d->_q_winError(winError, QLatin1String("QLocalSocket::connectToServer"));
d->fullServerName = QString();
return;
}
diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp
index f78f874f40..7085fe3bbd 100644
--- a/src/network/socket/qnativesocketengine.cpp
+++ b/src/network/socket/qnativesocketengine.cpp
@@ -999,8 +999,8 @@ void QNativeSocketEngine::close()
/*!
Waits for \a msecs milliseconds or until the socket is ready for
- reading. If \a timedOut is not 0 and \a msecs milliseconds have
- passed, the value of \a timedOut is set to true.
+ reading. If \a timedOut is not \nullptr and \a msecs milliseconds
+ have passed, the value of \a timedOut is set to true.
Returns \c true if data is available for reading; otherwise returns
false.
@@ -1039,8 +1039,8 @@ bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut)
/*!
Waits for \a msecs milliseconds or until the socket is ready for
- writing. If \a timedOut is not 0 and \a msecs milliseconds have
- passed, the value of \a timedOut is set to true.
+ writing. If \a timedOut is not \nullptr and \a msecs milliseconds
+ have passed, the value of \a timedOut is set to true.
Returns \c true if data is available for writing; otherwise returns
false.
diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h
index aa61b74823..e5f0701d14 100644
--- a/src/network/socket/qnativesocketengine_p.h
+++ b/src/network/socket/qnativesocketengine_p.h
@@ -125,7 +125,7 @@ class Q_AUTOTEST_EXPORT QNativeSocketEngine : public QAbstractSocketEngine
{
Q_OBJECT
public:
- QNativeSocketEngine(QObject *parent = 0);
+ QNativeSocketEngine(QObject *parent = nullptr);
~QNativeSocketEngine();
bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol) override;
@@ -161,7 +161,7 @@ public:
qint64 pendingDatagramSize() const override;
#endif // QT_NO_UDPSOCKET
- qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = 0,
+ qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = nullptr,
PacketHeaderOptions = WantNone) override;
qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) override;
qint64 bytesToWrite() const override;
@@ -177,11 +177,11 @@ public:
int option(SocketOption option) const override;
bool setOption(SocketOption option, int value) override;
- bool waitForRead(int msecs = 30000, bool *timedOut = 0) override;
- bool waitForWrite(int msecs = 30000, bool *timedOut = 0) override;
+ bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) override;
+ bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) override;
bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
- int msecs = 30000, bool *timedOut = 0) override;
+ int msecs = 30000, bool *timedOut = nullptr) override;
bool isReadNotificationEnabled() const override;
void setReadNotificationEnabled(bool enable) override;
@@ -196,7 +196,7 @@ public Q_SLOTS:
private:
Q_DECLARE_PRIVATE(QNativeSocketEngine)
- Q_DISABLE_COPY(QNativeSocketEngine)
+ Q_DISABLE_COPY_MOVE(QNativeSocketEngine)
};
class QSocketNotifier;
diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp
index b380b0f7d6..24c17124dc 100644
--- a/src/network/socket/qnativesocketengine_unix.cpp
+++ b/src/network/socket/qnativesocketengine_unix.cpp
@@ -1383,20 +1383,24 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxSize)
// No data was available for reading
r = -2;
break;
- case EBADF:
- case EINVAL:
- case EIO:
- //error string is now set in read(), not here in nativeRead()
- break;
case ECONNRESET:
#if defined(Q_OS_VXWORKS)
case ESHUTDOWN:
#endif
r = 0;
break;
+ case ETIMEDOUT:
+ socketError = QAbstractSocket::SocketTimeoutError;
+ break;
default:
+ socketError = QAbstractSocket::NetworkError;
break;
}
+
+ if (r == -1) {
+ hasSetSocketError = true;
+ socketErrorString = qt_error_string();
+ }
}
#if defined (QNATIVESOCKETENGINE_DEBUG)
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
index c999bd2088..24e8eabb6e 100644
--- a/src/network/socket/qnativesocketengine_win.cpp
+++ b/src/network/socket/qnativesocketengine_win.cpp
@@ -1146,22 +1146,17 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
qint64 ret = -1;
int recvResult = 0;
DWORD flags;
- DWORD bufferCount = 5;
- WSABUF * buf = 0;
+ // We start at 1500 bytes (the MTU for Ethernet V2), which should catch
+ // almost all uses (effective MTU for UDP under IPv4 is 1468), except
+ // for localhost datagrams and those reassembled by the IP layer.
+ char udpMessagePeekBuffer[1500];
+ std::vector<WSABUF> buf;
for (;;) {
- // We start at 1500 bytes (the MTU for Ethernet V2), which should catch
- // almost all uses (effective MTU for UDP under IPv4 is 1468), except
- // for localhost datagrams and those reassembled by the IP layer.
- char udpMessagePeekBuffer[1500];
-
- buf = new WSABUF[bufferCount];
- for (DWORD i=0; i<bufferCount; i++) {
- buf[i].buf = udpMessagePeekBuffer;
- buf[i].len = sizeof(udpMessagePeekBuffer);
- }
+ buf.resize(buf.size() + 5, {sizeof(udpMessagePeekBuffer), udpMessagePeekBuffer});
+
flags = MSG_PEEK;
DWORD bytesRead = 0;
- recvResult = ::WSARecv(socketDescriptor, buf, bufferCount, &bytesRead, &flags, 0,0);
+ recvResult = ::WSARecv(socketDescriptor, buf.data(), DWORD(buf.size()), &bytesRead, &flags, nullptr, nullptr);
int err = WSAGetLastError();
if (recvResult != SOCKET_ERROR) {
ret = qint64(bytesRead);
@@ -1169,8 +1164,6 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
} else {
switch (err) {
case WSAEMSGSIZE:
- bufferCount += 5;
- delete[] buf;
continue;
case WSAECONNRESET:
case WSAENETRESET:
@@ -1185,9 +1178,6 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
}
}
- if (buf)
- delete[] buf;
-
#if defined (QNATIVESOCKETENGINE_DEBUG)
qDebug("QNativeSocketEnginePrivate::nativePendingDatagramSize() == %lli", ret);
#endif
diff --git a/src/network/socket/qnativesocketengine_winrt_p.h b/src/network/socket/qnativesocketengine_winrt_p.h
index 6688bfe35c..e1fe58bb97 100644
--- a/src/network/socket/qnativesocketengine_winrt_p.h
+++ b/src/network/socket/qnativesocketengine_winrt_p.h
@@ -188,7 +188,7 @@ private slots:
private:
Q_DECLARE_PRIVATE(QNativeSocketEngine)
- Q_DISABLE_COPY(QNativeSocketEngine)
+ Q_DISABLE_COPY_MOVE(QNativeSocketEngine)
};
class QNativeSocketEnginePrivate : public QAbstractSocketEnginePrivate
diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp
index 30bc8ae7d5..27657956df 100644
--- a/src/network/socket/qsocks5socketengine.cpp
+++ b/src/network/socket/qsocks5socketengine.cpp
@@ -372,7 +372,7 @@ QSocks5BindData *QSocks5BindStore::retrieve(qintptr socketDescriptor)
store.erase(it);
if (bindData) {
if (bindData->controlSocket->thread() != QThread::currentThread()) {
- qWarning("Can not access socks5 bind data from different thread");
+ qWarning("Cannot access socks5 bind data from different thread");
return 0;
}
} else {
@@ -706,7 +706,7 @@ void QSocks5SocketEnginePrivate::reauthenticate()
// we require authentication
QAuthenticator auth;
- emit q->proxyAuthenticationRequired(proxyInfo, &auth);
+ q->proxyAuthenticationRequired(proxyInfo, &auth);
if (!auth.user().isEmpty() || !auth.password().isEmpty()) {
// we have new credentials, let's try again
@@ -915,7 +915,7 @@ void QSocks5SocketEnginePrivate::_q_emitPendingReadNotification()
if (readNotificationEnabled) {
QSOCKS5_D_DEBUG << "emitting readNotification";
QPointer<QSocks5SocketEngine> qq = q;
- emit q->readNotification();
+ q->readNotification();
if (!qq)
return;
// check if there needs to be a new zero read notification
@@ -944,7 +944,7 @@ void QSocks5SocketEnginePrivate::_q_emitPendingWriteNotification()
Q_Q(QSocks5SocketEngine);
if (writeNotificationEnabled) {
QSOCKS5_D_DEBUG << "emitting writeNotification";
- emit q->writeNotification();
+ q->writeNotification();
}
}
@@ -964,7 +964,7 @@ void QSocks5SocketEnginePrivate::_q_emitPendingConnectionNotification()
connectionNotificationPending = false;
Q_Q(QSocks5SocketEngine);
QSOCKS5_D_DEBUG << "emitting connectionNotification";
- emit q->connectionNotification();
+ q->connectionNotification();
}
void QSocks5SocketEnginePrivate::emitConnectionNotification()
@@ -1383,23 +1383,6 @@ bool QSocks5SocketEngine::bind(const QHostAddress &addr, quint16 port)
d->localAddress = QHostAddress();
d->udpData->associatePort = d->localPort;
d->localPort = 0;
- QUdpSocket dummy;
-#ifndef QT_NO_BEARERMANAGEMENT
- dummy.setProperty("_q_networksession", property("_q_networksession"));
-#endif
- dummy.setProxy(QNetworkProxy::NoProxy);
- if (!dummy.bind()
- || writeDatagram(0,0, QIpPacketHeader(d->data->controlSocket->localAddress(), dummy.localPort())) != 0
- || !dummy.waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))
- || dummy.readDatagram(0,0, &d->localAddress, &d->localPort) != 0) {
- QSOCKS5_DEBUG << "udp actual address and port lookup failed";
- setState(QAbstractSocket::UnconnectedState);
- setError(dummy.error(), dummy.errorString());
- d->data->controlSocket->close();
- //### reset and error
- return false;
- }
- QSOCKS5_DEBUG << "udp actual address and port" << d->localAddress << ':' << d->localPort;
return true;
#endif // QT_NO_UDPSOCKET
}
diff --git a/src/network/socket/qsocks5socketengine_p.h b/src/network/socket/qsocks5socketengine_p.h
index 1942eff4ca..c256987e2d 100644
--- a/src/network/socket/qsocks5socketengine_p.h
+++ b/src/network/socket/qsocks5socketengine_p.h
@@ -65,7 +65,7 @@ class Q_AUTOTEST_EXPORT QSocks5SocketEngine : public QAbstractSocketEngine
{
Q_OBJECT
public:
- QSocks5SocketEngine(QObject *parent = 0);
+ QSocks5SocketEngine(QObject *parent = nullptr);
~QSocks5SocketEngine();
bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol) override;
@@ -104,7 +104,7 @@ public:
qint64 pendingDatagramSize() const override;
#endif // QT_NO_UDPSOCKET
- qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = 0,
+ qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = nullptr,
PacketHeaderOptions = WantNone) override;
qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) override;
qint64 bytesToWrite() const override;
@@ -112,11 +112,11 @@ public:
int option(SocketOption option) const override;
bool setOption(SocketOption option, int value) override;
- bool waitForRead(int msecs = 30000, bool *timedOut = 0) override;
- bool waitForWrite(int msecs = 30000, bool *timedOut = 0) override;
+ bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) override;
+ bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) override;
bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
- int msecs = 30000, bool *timedOut = 0) override;
+ int msecs = 30000, bool *timedOut = nullptr) override;
bool isReadNotificationEnabled() const override;
void setReadNotificationEnabled(bool enable) override;
@@ -127,7 +127,7 @@ public:
private:
Q_DECLARE_PRIVATE(QSocks5SocketEngine)
- Q_DISABLE_COPY(QSocks5SocketEngine)
+ Q_DISABLE_COPY_MOVE(QSocks5SocketEngine)
Q_PRIVATE_SLOT(d_func(), void _q_controlSocketConnected())
Q_PRIVATE_SLOT(d_func(), void _q_controlSocketReadNotification())
Q_PRIVATE_SLOT(d_func(), void _q_controlSocketError(QAbstractSocket::SocketError))
diff --git a/src/network/socket/qtcpserver.cpp b/src/network/socket/qtcpserver.cpp
index eddf789921..56c700ca8f 100644
--- a/src/network/socket/qtcpserver.cpp
+++ b/src/network/socket/qtcpserver.cpp
@@ -493,7 +493,7 @@ QHostAddress QTcpServer::serverAddress() const
Waits for at most \a msec milliseconds or until an incoming
connection is available. Returns \c true if a connection is
available; otherwise returns \c false. If the operation timed out
- and \a timedOut is not 0, *\a timedOut will be set to true.
+ and \a timedOut is not \nullptr, *\a timedOut will be set to true.
This is a blocking function call. Its use is disadvised in a
single-threaded GUI application, since the whole application will
diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp
index 85c4f4cbfd..0e3d516535 100644
--- a/src/network/socket/qudpsocket.cpp
+++ b/src/network/socket/qudpsocket.cpp
@@ -381,6 +381,7 @@ qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddre
*/
/*!
+ \since 5.8
\overload
Sends the datagram \a datagram to the host address and port numbers
@@ -431,6 +432,8 @@ qint64 QUdpSocket::writeDatagram(const QNetworkDatagram &datagram)
}
/*!
+ \since 5.8
+
Receives a datagram no larger than \a maxSize bytes and returns it in the
QNetworkDatagram object, along with the sender's host address and port. If
possible, this function will also try to determine the datagram's
diff --git a/src/network/ssl/qasn1element_p.h b/src/network/ssl/qasn1element_p.h
index 2068254a95..020b5aa1af 100644
--- a/src/network/ssl/qasn1element_p.h
+++ b/src/network/ssl/qasn1element_p.h
@@ -64,6 +64,7 @@ QT_BEGIN_NAMESPACE
#define RSA_ENCRYPTION_OID QByteArrayLiteral(RSADSI_OID "1.1.1")
#define DSA_ENCRYPTION_OID QByteArrayLiteral("1.2.840.10040.4.1")
#define EC_ENCRYPTION_OID QByteArrayLiteral("1.2.840.10045.2.1")
+#define DH_ENCRYPTION_OID QByteArrayLiteral(RSADSI_OID "1.3.1")
// These are mostly from the RFC for PKCS#5
// PKCS#5: https://tools.ietf.org/html/rfc8018#appendix-B
@@ -137,6 +138,7 @@ public:
Rfc822NameType = 0x81,
DnsNameType = 0x82,
UniformResourceIdentifierType = 0x86,
+ IpAddressType = 0x87,
// context specific
Context0Type = 0xA0,
@@ -154,10 +156,10 @@ public:
static QAsn1Element fromVector(const QVector<QAsn1Element> &items);
static QAsn1Element fromObjectId(const QByteArray &id);
- bool toBool(bool *ok = 0) const;
+ bool toBool(bool *ok = nullptr) const;
QDateTime toDateTime() const;
QMultiMap<QByteArray, QString> toInfo() const;
- qint64 toInteger(bool *ok = 0) const;
+ qint64 toInteger(bool *ok = nullptr) const;
QVector<QAsn1Element> toVector() const;
QByteArray toObjectId() const;
QByteArray toObjectName() const;
diff --git a/src/network/ssl/qdtls.cpp b/src/network/ssl/qdtls.cpp
index bbb22aa527..3185bfa124 100644
--- a/src/network/ssl/qdtls.cpp
+++ b/src/network/ssl/qdtls.cpp
@@ -370,6 +370,7 @@ void QDtlsBasePrivate::setConfiguration(const QSslConfiguration &configuration)
dtlsConfiguration.nextProtocolNegotiationStatus = configuration.nextProtocolNegotiationStatus();
dtlsConfiguration.dtlsCookieEnabled = configuration.dtlsCookieVerificationEnabled();
dtlsConfiguration.allowRootCertOnDemandLoading = configuration.d->allowRootCertOnDemandLoading;
+ dtlsConfiguration.backendConfig = configuration.backendConfiguration();
clearDtlsError();
}
diff --git a/src/network/ssl/qdtls.h b/src/network/ssl/qdtls.h
index 8505b00d5e..d057eadf19 100644
--- a/src/network/ssl/qdtls.h
+++ b/src/network/ssl/qdtls.h
@@ -48,7 +48,9 @@
#include <QtCore/qcryptographichash.h>
#include <QtCore/qobject.h>
+#ifndef Q_CLANG_QDOC
QT_REQUIRE_CONFIG(dtls);
+#endif
QT_BEGIN_NAMESPACE
diff --git a/src/network/ssl/qocsp_p.h b/src/network/ssl/qocsp_p.h
new file mode 100644
index 0000000000..71f59da0b4
--- /dev/null
+++ b/src/network/ssl/qocsp_p.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOCSP_P_H
+#define QOCSP_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+// Note, this file is a workaround: on 64-bit Windows one of OpenSSL
+// includes combined with openssl/ocsp.h results in macros from
+// wincrypt.h exposed. OpenSSL's own very "unique" and "inventive"
+// names like OCSP_RESPONSE or X509_NAME were asking to clash with
+// other entities (presumably macros) with the same names. Normally,
+// ossl_typ.h un-defines them, but due to a bug in OpenSSL, fails
+// to do this on Win 64. Thus we have to do it here. We only undef
+// 3 names, ossl_typ.h has more, but apparently we don't need them
+// (no name clash so far).
+
+QT_REQUIRE_CONFIG(ocsp);
+
+#ifdef Q_OS_WIN
+#undef X509_NAME
+#undef OCSP_REQUEST
+#undef OCSP_RESPONSE
+#endif // Q_OS_WIN
+
+#include <openssl/ocsp.h>
+
+#endif // QOCSP_P_H
diff --git a/src/network/ssl/qocspresponse.cpp b/src/network/ssl/qocspresponse.cpp
new file mode 100644
index 0000000000..79f0cfd1d4
--- /dev/null
+++ b/src/network/ssl/qocspresponse.cpp
@@ -0,0 +1,258 @@
+/****************************************************************************
+** Copyright (C) 2011 Richard J. Moore <rich@kde.org>
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qocspresponse_p.h"
+#include "qocspresponse.h"
+
+#include "qhashfunctions.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QOcspResponse
+ \brief This class represents Online Certificate Status Protocol response.
+ \since 5.13
+
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+ The QOcspResponse class represents the revocation status of a server's certficate,
+ received by the client-side socket during the TLS handshake. QSslSocket must be
+ configured with OCSP stapling enabled.
+
+ \sa QSslSocket, QSslSocket::ocspResponses(), certificateStatus(),
+ revocationReason(), responder(), subject(), QOcspCertificateStatus, QOcspRevocationReason,
+ QSslConfiguration::setOcspStaplingEnabled(), QSslConfiguration::ocspStaplingEnabled(),
+ QSslConfiguration::peerCertificate()
+*/
+
+/*!
+ \enum QOcspCertificateStatus
+ \brief Describes the Online Certificate Status
+ \relates QOcspResponse
+ \since 5.13
+
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+ \value Good The certificate is not revoked, but this does not necessarily
+ mean that the certificate was ever issued or that the time at which
+ the response was produced is within the certificate's validity interval.
+ \value Revoked This state indicates that the certificate has been revoked
+ (either permanently or temporarily - on hold).
+ \value Unknown This state indicates that the responder doesn't know about
+ the certificate being requested.
+
+ \sa QOcspRevocationReason
+*/
+
+/*!
+ \enum QOcspRevocationReason
+ \brief Describes the reason for revocation
+ \relates QOcspResponse
+ \since 5.13
+
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+
+ This enumeration describes revocation reasons, defined in \l{https://tools.ietf.org/html/rfc5280#section-5.3.1}{RFC 5280, section 5.3.1}
+
+ \value None
+ \value Unspecified
+ \value KeyCompromise
+ \value CACompromise
+ \value AffiliationChanged
+ \value Superseded
+ \value CessationOfOperation
+ \value CertificateHold
+ \value RemoveFromCRL
+*/
+
+/*!
+ \since 5.13
+
+ Creates a new response with status QOcspCertificateStatus::Unknown
+ and revocation reason QOcspRevocationReason::None.
+
+ \sa QOcspCertificateStatus
+*/
+QOcspResponse::QOcspResponse()
+ : d(new QOcspResponsePrivate)
+{
+}
+
+/*!
+ \since 5.13
+
+ Copy-constructs a QOcspResponse instance.
+*/
+QOcspResponse::QOcspResponse(const QOcspResponse &) = default;
+
+/*!
+ \since 5.13
+
+ Move-constructs a QOcspResponse instance.
+*/
+QOcspResponse::QOcspResponse(QOcspResponse &&) noexcept = default;
+
+/*!
+ \since 5.13
+
+ Destroys the response.
+*/
+QOcspResponse::~QOcspResponse() = default;
+
+/*!
+ \since 5.13
+
+ Copy-assigns and returns a reference to this response.
+*/
+QOcspResponse &QOcspResponse::operator=(const QOcspResponse &) = default;
+
+/*!
+ \since 5.13
+
+ Move-assigns to this QOcspResponse instance.
+*/
+QOcspResponse &QOcspResponse::operator=(QOcspResponse &&) noexcept = default;
+
+/*!
+ \fn void QOcspResponse::swap(QOcspResponse &other)
+ \since 5.13
+
+ Swaps this response with \a other.
+*/
+
+/*!
+ \since 5.13
+
+ Returns the certificate status.
+
+ \sa QOcspCertificateStatus
+*/
+QOcspCertificateStatus QOcspResponse::certificateStatus() const
+{
+ return d->certificateStatus;
+}
+
+/*!
+ \since 5.13
+
+ Returns the reason for revocation.
+*/
+QOcspRevocationReason QOcspResponse::revocationReason() const
+{
+ return d->revocationReason;
+}
+
+/*!
+ \since 5.13
+
+ This function returns a certificate used to sign OCSP response.
+*/
+QSslCertificate QOcspResponse::responder() const
+{
+ return d->signerCert;
+}
+
+/*!
+ \since 5.13
+
+ This function returns a certificate, for which this response was issued.
+*/
+QSslCertificate QOcspResponse::subject() const
+{
+ return d->subjectCert;
+}
+
+/*!
+ \fn bool operator==(const QOcspResponse &lhs, const QOcspResponse &rhs)
+
+ Returns \c true if \a lhs and \a rhs are the responses for the same
+ certificate, signed by the same responder, have the same
+ revocation reason and the same certificate status.
+
+ \since 5.13
+ \relates QOcspResponse
+ */
+Q_NETWORK_EXPORT bool operator==(const QOcspResponse &lhs, const QOcspResponse &rhs)
+{
+ return lhs.d == rhs.d || *lhs.d == *rhs.d;
+}
+
+/*!
+ \fn bool operator != (const QOcspResponse &lhs, const QOcspResponse &rhs)
+
+ Returns \c true if \a lhs and \a rhs are responses for different certificates,
+ or signed by different responders, or have different revocation reasons, or different
+ certificate statuses.
+
+ \since 5.13
+ \relates QOcspResponse
+*/
+
+/*!
+ \fn uint qHash(const QOcspResponse &response, uint seed)
+
+ Returns the hash value for the \a response, using \a seed to seed the calculation.
+
+ \since 5.13
+ \relates QHash
+*/
+uint qHash(const QOcspResponse &response, uint seed)
+{
+ const QOcspResponsePrivate *d = response.d.data();
+ Q_ASSERT(d);
+
+ QtPrivate::QHashCombine hasher;
+ uint hash = hasher(seed, int(d->certificateStatus));
+ hash = hasher(hash, int(d->revocationReason));
+ if (!d->signerCert.isNull())
+ hash = hasher(hash, d->signerCert);
+ if (!d->subjectCert.isNull())
+ hash = hasher(hash, d->subjectCert);
+
+ return hash;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qocspresponse.h b/src/network/ssl/qocspresponse.h
new file mode 100644
index 0000000000..0e134d236b
--- /dev/null
+++ b/src/network/ssl/qocspresponse.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+** Copyright (C) 2011 Richard J. Moore <rich@kde.org>
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOCSPRESPONSE_H
+#define QOCSPRESPONSE_H
+
+#include <QtNetwork/qtnetworkglobal.h>
+
+#include <QtCore/qshareddata.h>
+#include <QtCore/qmetatype.h>
+#include <QtCore/qobject.h>
+
+#ifndef Q_CLANG_QDOC
+QT_REQUIRE_CONFIG(ssl);
+#endif
+
+QT_BEGIN_NAMESPACE
+
+enum class QOcspCertificateStatus
+{
+ Good,
+ Revoked,
+ Unknown
+};
+
+enum class QOcspRevocationReason
+{
+ None = -1,
+ Unspecified,
+ KeyCompromise,
+ CACompromise,
+ AffiliationChanged,
+ Superseded,
+ CessationOfOperation,
+ CertificateHold,
+ RemoveFromCRL
+};
+
+class QOcspResponse;
+Q_NETWORK_EXPORT uint qHash(const QOcspResponse &response, uint seed = 0);
+
+class QOcspResponsePrivate;
+class Q_NETWORK_EXPORT QOcspResponse
+{
+public:
+
+ QOcspResponse();
+ QOcspResponse(const QOcspResponse &other);
+ QOcspResponse(QOcspResponse && other) noexcept;
+ ~QOcspResponse();
+
+ QOcspResponse &operator = (const QOcspResponse &other);
+ QOcspResponse &operator = (QOcspResponse &&other) noexcept;
+
+ QOcspCertificateStatus certificateStatus() const;
+ QOcspRevocationReason revocationReason() const;
+
+ class QSslCertificate responder() const;
+ QSslCertificate subject() const;
+
+ void swap(QOcspResponse &other) noexcept { d.swap(other.d); }
+
+private:
+
+ friend class QSslSocketBackendPrivate;
+ friend Q_NETWORK_EXPORT bool operator==(const QOcspResponse &lhs, const QOcspResponse &rhs);
+ friend Q_NETWORK_EXPORT uint qHash(const QOcspResponse &response, uint seed);
+
+ QSharedDataPointer<QOcspResponsePrivate> d;
+};
+
+inline bool operator!=(const QOcspResponse &lhs, const QOcspResponse &rhs) { return !(lhs == rhs); }
+
+Q_DECLARE_SHARED(QOcspResponse)
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QOcspResponse)
+
+#endif // QOCSPRESPONSE_H
diff --git a/src/network/ssl/qocspresponse_p.h b/src/network/ssl/qocspresponse_p.h
new file mode 100644
index 0000000000..e421b76899
--- /dev/null
+++ b/src/network/ssl/qocspresponse_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+** Copyright (C) 2011 Richard J. Moore <rich@kde.org>
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOCSPRESPONSE_P_H
+#define QOCSPRESPONSE_P_H
+
+#include <private/qtnetworkglobal_p.h>
+
+#include <qsslcertificate.h>
+#include <qocspresponse.h>
+
+#include <qshareddata.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QOcspResponsePrivate : public QSharedData
+{
+public:
+
+ QOcspCertificateStatus certificateStatus = QOcspCertificateStatus::Unknown;
+ QOcspRevocationReason revocationReason = QOcspRevocationReason::None;
+
+ QSslCertificate signerCert;
+ QSslCertificate subjectCert;
+};
+
+inline bool operator==(const QOcspResponsePrivate &lhs, const QOcspResponsePrivate &rhs)
+{
+ return lhs.certificateStatus == rhs.certificateStatus
+ && lhs.revocationReason == rhs.revocationReason
+ && lhs.signerCert == rhs.signerCert
+ && lhs.subjectCert == rhs.subjectCert;
+}
+
+QT_END_NAMESPACE
+
+#endif // QOCSPRESPONSE_P_H
diff --git a/src/network/ssl/qpassworddigestor.cpp b/src/network/ssl/qpassworddigestor.cpp
index 127d94e849..706fa1de05 100644
--- a/src/network/ssl/qpassworddigestor.cpp
+++ b/src/network/ssl/qpassworddigestor.cpp
@@ -75,7 +75,7 @@ namespace QPasswordDigestor {
\a salt must always be 8 bytes long!
\note This function is provided for use with legacy applications and all
- new applications are recommended to use \l {pbkdf2} {PBKDF2}.
+ new applications are recommended to use \l {deriveKeyPbkdf2} {PBKDF2}.
\sa deriveKeyPbkdf2, QCryptographicHash, QCryptographicHash::hashLength
*/
diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp
index 3a0983e8b5..c9fa7f85d9 100644
--- a/src/network/ssl/qssl.cpp
+++ b/src/network/ssl/qssl.cpp
@@ -71,7 +71,8 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl");
\value Rsa The RSA algorithm.
\value Dsa The DSA algorithm.
- \value Ec The Elliptic Curve algorithm
+ \value Ec The Elliptic Curve algorithm.
+ \value Dh The Diffie-Hellman algorithm.
\value Opaque A key that should be treated as a 'black box' by QSslKey.
The opaque key facility allows applications to add support for facilities
@@ -98,6 +99,9 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl");
\value DnsEntry A DNS host name entry; the entry contains a host name
entry that the certificate is valid for. The entry may contain wildcards.
+ \value IpAddressEntry An IP address entry; the entry contains an IP address
+ entry that the certificate is valid for, introduced in Qt 5.13.
+
\note In Qt 4, this enum was called \c {AlternateNameEntryType}. That name
is deprecated in Qt 5.
@@ -116,8 +120,8 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl");
Describes the protocol of the cipher.
- \value SslV3 SSLv3. When using the WinRT backend this option will also enable TLSv1.0
- \value SslV2 SSLv2. Note, SSLv2 support was removed in OpenSSL 1.1.
+ \value SslV3 SSLv3; not supported by QSslSocket.
+ \value SslV2 SSLv2; not supported by QSslSocket.
\value TlsV1_0 TLSv1.0
\value TlsV1_0OrLater TLSv1.0 and later versions. This option is not available when using the WinRT backend due to platform limitations.
\value TlsV1 Obsolete, means the same as TlsV1_0
@@ -129,20 +133,12 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl");
\value DtlsV1_0OrLater DTLSv1.0 and later versions.
\value DtlsV1_2 DTLSv1.2
\value DtlsV1_2OrLater DTLSv1.2 and later versions.
+ \value TlsV1_3 TLSv1.3. (Since Qt 5.12)
+ \value TlsV1_3OrLater TLSv1.3 and later versions. (Since Qt 5.12)
\value UnknownProtocol The cipher's protocol cannot be determined.
- \value AnyProtocol The socket understands SSLv2, SSLv3, TLSv1.0 and all
- supported later versions of TLS. This value is used by QSslSocket only.
- \value TlsV1SslV3 On the client side, this will send
- a TLS 1.0 Client Hello, enabling TLSv1_0 and SSLv3 connections.
- On the server side, this will enable both SSLv3 and TLSv1_0 connections.
- \value SecureProtocols The default option, using protocols known to be secure;
- currently behaves similar to TlsV1Ssl3 except denying SSLv3 connections that does
- not upgrade to TLS.
-
- \note most servers understand both SSL and TLS, but it is recommended to use
- TLS only for security reasons. However, SSL and TLS are not compatible with
- each other: if you get unexpected handshake failures, verify that you chose
- the correct setting for your protocol.
+ \value AnyProtocol Any supported protocol. This value is used by QSslSocket only.
+ \value TlsV1SslV3 Same as TlsV1_0.
+ \value SecureProtocols The default option, using protocols known to be secure.
*/
/*!
diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h
index dd268cd86d..42c7b5c56d 100644
--- a/src/network/ssl/qssl.h
+++ b/src/network/ssl/qssl.h
@@ -62,12 +62,14 @@ namespace QSsl {
Opaque,
Rsa,
Dsa,
- Ec
+ Ec,
+ Dh,
};
enum AlternativeNameEntryType {
EmailEntry,
- DnsEntry
+ DnsEntry,
+ IpAddressEntry
};
#if QT_DEPRECATED_SINCE(5,0)
@@ -91,12 +93,13 @@ namespace QSsl {
TlsV1_1OrLater,
TlsV1_2OrLater,
-#if QT_CONFIG(dtls) || defined(Q_CLANG_QDOC)
DtlsV1_0,
DtlsV1_0OrLater,
DtlsV1_2,
DtlsV1_2OrLater,
-#endif
+
+ TlsV1_3,
+ TlsV1_3OrLater,
UnknownProtocol = -1
};
diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp
index d153e0b929..0156b5bf96 100644
--- a/src/network/ssl/qsslcertificate.cpp
+++ b/src/network/ssl/qsslcertificate.cpp
@@ -121,6 +121,9 @@
#ifdef QT_SECURETRANSPORT
#include "qsslsocket_mac_p.h"
#endif
+#if QT_CONFIG(schannel)
+#include "qsslsocket_schannel_p.h"
+#endif
#include "qssl_p.h"
#include "qsslcertificate.h"
diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h
index 266fcdacb4..a6acfa2cc3 100644
--- a/src/network/ssl/qsslcertificate.h
+++ b/src/network/ssl/qsslcertificate.h
@@ -66,7 +66,7 @@ class QStringList;
class QSslCertificate;
// qHash is a friend, but we can't use default arguments for friends (§8.3.6.4)
-Q_NETWORK_EXPORT uint qHash(const QSslCertificate &key, uint seed = 0) Q_DECL_NOTHROW;
+Q_NETWORK_EXPORT uint qHash(const QSslCertificate &key, uint seed = 0) noexcept;
class QSslCertificatePrivate;
class Q_NETWORK_EXPORT QSslCertificate
@@ -89,11 +89,11 @@ public:
QSslCertificate(const QSslCertificate &other);
~QSslCertificate();
#ifdef Q_COMPILER_RVALUE_REFS
- QSslCertificate &operator=(QSslCertificate &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QSslCertificate &operator=(QSslCertificate &&other) noexcept { swap(other); return *this; }
#endif
QSslCertificate &operator=(const QSslCertificate &other);
- void swap(QSslCertificate &other) Q_DECL_NOTHROW
+ void swap(QSslCertificate &other) noexcept
{ qSwap(d, other.d); }
bool operator==(const QSslCertificate &other) const;
@@ -169,7 +169,7 @@ private:
friend class QSslCertificatePrivate;
friend class QSslSocketBackendPrivate;
- friend Q_NETWORK_EXPORT uint qHash(const QSslCertificate &key, uint seed) Q_DECL_NOTHROW;
+ friend Q_NETWORK_EXPORT uint qHash(const QSslCertificate &key, uint seed) noexcept;
};
Q_DECLARE_SHARED(QSslCertificate)
diff --git a/src/network/ssl/qsslcertificate_openssl.cpp b/src/network/ssl/qsslcertificate_openssl.cpp
index fa87cfeaaf..806c6426e4 100644
--- a/src/network/ssl/qsslcertificate_openssl.cpp
+++ b/src/network/ssl/qsslcertificate_openssl.cpp
@@ -44,13 +44,15 @@
#include "qsslkey_p.h"
#include "qsslcertificateextension_p.h"
+#include <QtCore/qendian.h>
+
#if QT_CONFIG(thread)
#include <QtCore/private/qmutexpool_p.h>
#endif
QT_BEGIN_NAMESPACE
// forward declaration
-static QMap<QByteArray, QString> _q_mapFromX509Name(X509_NAME *name);
+static QMultiMap<QByteArray, QString> _q_mapFromX509Name(X509_NAME *name);
bool QSslCertificate::operator==(const QSslCertificate &other) const
{
@@ -63,7 +65,7 @@ bool QSslCertificate::operator==(const QSslCertificate &other) const
return false;
}
-uint qHash(const QSslCertificate &key, uint seed) Q_DECL_NOTHROW
+uint qHash(const QSslCertificate &key, uint seed) noexcept
{
if (X509 * const x509 = key.d->x509) {
const EVP_MD *sha1 = q_EVP_sha1();
@@ -207,10 +209,14 @@ QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlter
STACK_OF(GENERAL_NAME) *altNames = (STACK_OF(GENERAL_NAME) *)q_X509_get_ext_d2i(
d->x509, NID_subject_alt_name, nullptr, nullptr);
+ auto altName = [](ASN1_IA5STRING *ia5, int len) {
+ const char *altNameStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(ia5));
+ return QString::fromLatin1(altNameStr, len);
+ };
if (altNames) {
for (int i = 0; i < q_sk_GENERAL_NAME_num(altNames); ++i) {
const GENERAL_NAME *genName = q_sk_GENERAL_NAME_value(altNames, i);
- if (genName->type != GEN_DNS && genName->type != GEN_EMAIL)
+ if (genName->type != GEN_DNS && genName->type != GEN_EMAIL && genName->type != GEN_IPADD)
continue;
int len = q_ASN1_STRING_length(genName->d.ia5);
@@ -219,12 +225,32 @@ QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlter
continue;
}
- const char *altNameStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(genName->d.ia5));
- const QString altName = QString::fromLatin1(altNameStr, len);
- if (genName->type == GEN_DNS)
- result.insert(QSsl::DnsEntry, altName);
- else if (genName->type == GEN_EMAIL)
- result.insert(QSsl::EmailEntry, altName);
+ switch (genName->type) {
+ case GEN_DNS:
+ result.insert(QSsl::DnsEntry, altName(genName->d.ia5, len));
+ break;
+ case GEN_EMAIL:
+ result.insert(QSsl::EmailEntry, altName(genName->d.ia5, len));
+ break;
+ case GEN_IPADD: {
+ QHostAddress ipAddress;
+ switch (len) {
+ case 4: // IPv4
+ ipAddress = QHostAddress(qFromBigEndian(*reinterpret_cast<quint32 *>(genName->d.iPAddress->data)));
+ break;
+ case 16: // IPv6
+ ipAddress = QHostAddress(reinterpret_cast<quint8 *>(genName->d.iPAddress->data));
+ break;
+ default: // Unknown IP address format
+ break;
+ }
+ if (!ipAddress.isNull())
+ result.insert(QSsl::IpAddressEntry, ipAddress.toString());
+ break;
+ }
+ default:
+ break;
+ }
}
q_OPENSSL_sk_pop_free((OPENSSL_STACK*)altNames, reinterpret_cast<void(*)(void*)>(q_GENERAL_NAME_free));
@@ -615,16 +641,16 @@ QByteArray QSslCertificatePrivate::asn1ObjectName(ASN1_OBJECT *object)
return asn1ObjectId(object);
}
-static QMap<QByteArray, QString> _q_mapFromX509Name(X509_NAME *name)
+static QMultiMap<QByteArray, QString> _q_mapFromX509Name(X509_NAME *name)
{
- QMap<QByteArray, QString> info;
+ QMultiMap<QByteArray, QString> info;
for (int i = 0; i < q_X509_NAME_entry_count(name); ++i) {
X509_NAME_ENTRY *e = q_X509_NAME_get_entry(name, i);
QByteArray name = QSslCertificatePrivate::asn1ObjectName(q_X509_NAME_ENTRY_get_object(e));
unsigned char *data = nullptr;
int size = q_ASN1_STRING_to_UTF8(&data, q_X509_NAME_ENTRY_get_data(e));
- info.insertMulti(name, QString::fromUtf8((char*)data, size));
+ info.insert(name, QString::fromUtf8((char*)data, size));
#if QT_CONFIG(opensslv11)
q_CRYPTO_free(data, nullptr, 0);
#else
diff --git a/src/network/ssl/qsslcertificate_p.h b/src/network/ssl/qsslcertificate_p.h
index dfdceab502..234cd45ceb 100644
--- a/src/network/ssl/qsslcertificate_p.h
+++ b/src/network/ssl/qsslcertificate_p.h
@@ -75,6 +75,10 @@ struct ASN1_OBJECT;
#include <windows.security.cryptography.certificates.h>
#endif
+#if QT_CONFIG(schannel)
+#include <wincrypt.h>
+#endif
+
QT_BEGIN_NAMESPACE
// forward declaration
@@ -83,7 +87,7 @@ class QSslCertificatePrivate
{
public:
QSslCertificatePrivate()
- : null(true), x509(0)
+ : null(true), x509(nullptr)
{
#ifndef QT_NO_SSL
QSslSocketPrivate::ensureInitialized();
@@ -96,14 +100,18 @@ public:
if (x509)
q_X509_free(x509);
#endif
+#if QT_CONFIG(schannel)
+ if (certificateContext)
+ CertFreeCertificateContext(certificateContext);
+#endif
}
bool null;
QByteArray versionString;
QByteArray serialNumberString;
- QMap<QByteArray, QString> issuerInfo;
- QMap<QByteArray, QString> subjectInfo;
+ QMultiMap<QByteArray, QString> issuerInfo;
+ QMultiMap<QByteArray, QString> subjectInfo;
QDateTime notValidAfter;
QDateTime notValidBefore;
@@ -143,6 +151,12 @@ public:
static QSslCertificate QSslCertificate_from_Certificate(ABI::Windows::Security::Cryptography::Certificates::ICertificate *iCertificate);
#endif
+
+#if QT_CONFIG(schannel)
+ const CERT_CONTEXT *certificateContext = nullptr;
+
+ static QSslCertificate QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext);
+#endif
};
QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslcertificate_qt.cpp b/src/network/ssl/qsslcertificate_qt.cpp
index dfdfd529e5..8b5035ad96 100644
--- a/src/network/ssl/qsslcertificate_qt.cpp
+++ b/src/network/ssl/qsslcertificate_qt.cpp
@@ -50,6 +50,8 @@
#include "qasn1element_p.h"
#include <QtCore/qdatastream.h>
+#include <QtCore/qendian.h>
+#include <QtNetwork/qhostaddress.h>
QT_BEGIN_NAMESPACE
@@ -62,7 +64,7 @@ bool QSslCertificate::operator==(const QSslCertificate &other) const
return d->derData == other.d->derData;
}
-uint qHash(const QSslCertificate &key, uint seed) Q_DECL_NOTHROW
+uint qHash(const QSslCertificate &key, uint seed) noexcept
{
// DER is the native encoding here, so toDer() is just "return d->derData":
return qHash(key.toDer(), seed);
@@ -139,7 +141,7 @@ QDateTime QSslCertificate::expiryDate() const
return d->notValidAfter;
}
-#ifndef Q_OS_WINRT // implemented in qsslcertificate_winrt.cpp
+#if !defined(Q_OS_WINRT) && !QT_CONFIG(schannel) // implemented in qsslcertificate_{winrt,schannel}.cpp
Qt::HANDLE QSslCertificate::handle() const
{
Q_UNIMPLEMENTED();
@@ -206,6 +208,10 @@ void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat f
: certificatesFromDer(data, 1);
if (!certs.isEmpty()) {
*this = *certs.first().d;
+#if QT_CONFIG(schannel)
+ if (certificateContext)
+ certificateContext = CertDuplicateCertificateContext(certificateContext);
+#endif
}
}
}
@@ -399,10 +405,32 @@ bool QSslCertificatePrivate::parse(const QByteArray &data)
QDataStream nameStream(sanElem.value());
QAsn1Element nameElem;
while (nameElem.read(nameStream)) {
- if (nameElem.type() == QAsn1Element::Rfc822NameType) {
+ switch (nameElem.type()) {
+ case QAsn1Element::Rfc822NameType:
subjectAlternativeNames.insert(QSsl::EmailEntry, nameElem.toString());
- } else if (nameElem.type() == QAsn1Element::DnsNameType) {
+ break;
+ case QAsn1Element::DnsNameType:
subjectAlternativeNames.insert(QSsl::DnsEntry, nameElem.toString());
+ break;
+ case QAsn1Element::IpAddressType: {
+ QHostAddress ipAddress;
+ QByteArray ipAddrValue = nameElem.value();
+ switch (ipAddrValue.length()) {
+ case 4: // IPv4
+ ipAddress = QHostAddress(qFromBigEndian(*reinterpret_cast<quint32 *>(ipAddrValue.data())));
+ break;
+ case 16: // IPv6
+ ipAddress = QHostAddress(reinterpret_cast<quint8 *>(ipAddrValue.data()));
+ break;
+ default: // Unknown IP address format
+ break;
+ }
+ if (!ipAddress.isNull())
+ subjectAlternativeNames.insert(QSsl::IpAddressEntry, ipAddress.toString());
+ break;
+ }
+ default:
+ break;
}
}
}
diff --git a/src/network/ssl/qsslcertificate_schannel.cpp b/src/network/ssl/qsslcertificate_schannel.cpp
new file mode 100644
index 0000000000..5ea713612a
--- /dev/null
+++ b/src/network/ssl/qsslcertificate_schannel.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsslcertificate.h"
+#include "qsslcertificate_p.h"
+
+#include <wincrypt.h>
+
+QT_BEGIN_NAMESPACE
+
+QSslCertificate QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext)
+{
+ QByteArray derData = QByteArray((const char *)certificateContext->pbCertEncoded,
+ certificateContext->cbCertEncoded);
+
+ QSslCertificate certificate(derData, QSsl::Der);
+ certificate.d->certificateContext = CertDuplicateCertificateContext(certificateContext);
+ return certificate;
+}
+
+Qt::HANDLE QSslCertificate::handle() const
+{
+ return Qt::HANDLE(d->certificateContext);
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslcertificateextension.h b/src/network/ssl/qsslcertificateextension.h
index c2910e1707..f862015312 100644
--- a/src/network/ssl/qsslcertificateextension.h
+++ b/src/network/ssl/qsslcertificateextension.h
@@ -56,12 +56,12 @@ public:
QSslCertificateExtension();
QSslCertificateExtension(const QSslCertificateExtension &other);
#ifdef Q_COMPILER_RVALUE_REFS
- QSslCertificateExtension &operator=(QSslCertificateExtension &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QSslCertificateExtension &operator=(QSslCertificateExtension &&other) noexcept { swap(other); return *this; }
#endif
QSslCertificateExtension &operator=(const QSslCertificateExtension &other);
~QSslCertificateExtension();
- void swap(QSslCertificateExtension &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QSslCertificateExtension &other) noexcept { qSwap(d, other.d); }
QString oid() const;
QString name() const;
diff --git a/src/network/ssl/qsslcipher.h b/src/network/ssl/qsslcipher.h
index c6328e0169..430fe9aa7c 100644
--- a/src/network/ssl/qsslcipher.h
+++ b/src/network/ssl/qsslcipher.h
@@ -60,12 +60,12 @@ public:
QSslCipher(const QString &name, QSsl::SslProtocol protocol);
QSslCipher(const QSslCipher &other);
#ifdef Q_COMPILER_RVALUE_REFS
- QSslCipher &operator=(QSslCipher &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QSslCipher &operator=(QSslCipher &&other) noexcept { swap(other); return *this; }
#endif
QSslCipher &operator=(const QSslCipher &other);
~QSslCipher();
- void swap(QSslCipher &other) Q_DECL_NOTHROW
+ void swap(QSslCipher &other) noexcept
{ qSwap(d, other.d); }
bool operator==(const QSslCipher &other) const;
diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp
index 3f732b4646..7e92d3a526 100644
--- a/src/network/ssl/qsslconfiguration.cpp
+++ b/src/network/ssl/qsslconfiguration.cpp
@@ -228,7 +228,8 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const
d->nextAllowedProtocols == other.d->nextAllowedProtocols &&
d->nextNegotiatedProtocol == other.d->nextNegotiatedProtocol &&
d->nextProtocolNegotiationStatus == other.d->nextProtocolNegotiationStatus &&
- d->dtlsCookieEnabled == other.d->dtlsCookieEnabled;
+ d->dtlsCookieEnabled == other.d->dtlsCookieEnabled &&
+ d->ocspStaplingEnabled == other.d->ocspStaplingEnabled;
}
/*!
@@ -272,7 +273,8 @@ bool QSslConfiguration::isNull() const
d->preSharedKeyIdentityHint.isNull() &&
d->nextAllowedProtocols.isEmpty() &&
d->nextNegotiatedProtocol.isNull() &&
- d->nextProtocolNegotiationStatus == QSslConfiguration::NextProtocolNegotiationNone);
+ d->nextProtocolNegotiationStatus == QSslConfiguration::NextProtocolNegotiationNone &&
+ d->ocspStaplingEnabled == false);
}
/*!
@@ -585,6 +587,8 @@ void QSslConfiguration::setPrivateKey(const QSslKey &key)
ciphers. You can revert to using the entire set by calling
setCiphers() with the list returned by QSslSocket::supportedCiphers().
+ \note This is not currently supported in the Schannel backend.
+
\sa setCiphers(), QSslSocket::supportedCiphers()
*/
QList<QSslCipher> QSslConfiguration::ciphers() const
@@ -600,6 +604,8 @@ QList<QSslCipher> QSslConfiguration::ciphers() const
Restricting the cipher suite must be done before the handshake
phase, where the session cipher is chosen.
+ \note This is not currently supported in the Schannel backend.
+
\sa ciphers(), QSslSocket::supportedCiphers()
*/
void QSslConfiguration::setCiphers(const QList<QSslCipher> &ciphers)
@@ -1094,6 +1100,37 @@ void QSslConfiguration::setDefaultDtlsConfiguration(const QSslConfiguration &con
#endif // dtls
+/*!
+ \since 5.13
+ If \a enabled is true, client QSslSocket will send a certificate status request
+ to its peer when initiating a handshake. During the handshake QSslSocket will
+ verify the server's response. This value must be set before the handshake
+ starts.
+
+ \sa ocspStaplingEnabled()
+*/
+void QSslConfiguration::setOcspStaplingEnabled(bool enabled)
+{
+#if QT_CONFIG(ocsp)
+ d->ocspStaplingEnabled = enabled;
+#else
+ if (enabled)
+ qCWarning(lcSsl, "Enabling OCSP-stapling requires the feature 'ocsp'");
+#endif // ocsp
+}
+
+/*!
+ \since 5.13
+ Returns true if OCSP stapling was enabled by setOCSPStaplingEnabled(),
+ otherwise false (which is the default value).
+
+ \sa setOcspStaplingEnabled()
+*/
+bool QSslConfiguration::ocspStaplingEnabled() const
+{
+ return d->ocspStaplingEnabled;
+}
+
/*! \internal
*/
bool QSslConfigurationPrivate::peerSessionWasShared(const QSslConfiguration &configuration) {
diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h
index 454ac0cee3..16704ba17b 100644
--- a/src/network/ssl/qsslconfiguration.h
+++ b/src/network/ssl/qsslconfiguration.h
@@ -86,11 +86,11 @@ public:
QSslConfiguration(const QSslConfiguration &other);
~QSslConfiguration();
#ifdef Q_COMPILER_RVALUE_REFS
- QSslConfiguration &operator=(QSslConfiguration &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QSslConfiguration &operator=(QSslConfiguration &&other) noexcept { swap(other); return *this; }
#endif
QSslConfiguration &operator=(const QSslConfiguration &other);
- void swap(QSslConfiguration &other) Q_DECL_NOTHROW
+ void swap(QSslConfiguration &other) noexcept
{ qSwap(d, other.d); }
bool operator==(const QSslConfiguration &other) const;
@@ -170,6 +170,9 @@ public:
static void setDefaultDtlsConfiguration(const QSslConfiguration &configuration);
#endif // dtls
+ void setOcspStaplingEnabled(bool enable);
+ bool ocspStaplingEnabled() const;
+
enum NextProtocolNegotiationStatus {
NextProtocolNegotiationNone,
NextProtocolNegotiationNegotiated,
diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h
index 6c23165c6a..83126bb9a0 100644
--- a/src/network/ssl/qsslconfiguration_p.h
+++ b/src/network/ssl/qsslconfiguration_p.h
@@ -143,6 +143,12 @@ public:
const bool dtlsCookieEnabled = false;
#endif // dtls
+#if QT_CONFIG(ocsp)
+ bool ocspStaplingEnabled = false;
+#else
+ const bool ocspStaplingEnabled = false;
+#endif
+
// in qsslsocket.cpp:
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);
diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp
index 35cca9f01a..e81e5582f4 100644
--- a/src/network/ssl/qsslcontext_openssl.cpp
+++ b/src/network/ssl/qsslcontext_openssl.cpp
@@ -243,12 +243,28 @@ QString QSslContext::errorString() const
return errorStr;
}
+#if QT_CONFIG(ocsp)
+extern "C" int qt_OCSP_status_server_callback(SSL *ssl, void *); // Defined in qsslsocket_openssl.cpp.
+#endif // ocsp
// static
void QSslContext::applyBackendConfig(QSslContext *sslContext)
{
- if (sslContext->sslConfiguration.backendConfiguration().isEmpty())
+ const QMap<QByteArray, QVariant> &conf = sslContext->sslConfiguration.backendConfiguration();
+ if (conf.isEmpty())
return;
+#if QT_CONFIG(ocsp)
+ auto ocspResponsePos = conf.find("Qt-OCSP-response");
+ if (ocspResponsePos != conf.end()) {
+ // This is our private, undocumented configuration option, existing only for
+ // the purpose of testing OCSP status responses. We don't even check this
+ // callback was set. If no - the test must fail.
+ q_SSL_CTX_set_tlsext_status_cb(sslContext->ctx, qt_OCSP_status_server_callback);
+ if (conf.size() == 1)
+ return;
+ }
+#endif // ocsp
+
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
if (QSslSocket::sslLibraryVersionNumber() >= 0x10002000L) {
QSharedPointer<SSL_CONF_CTX> cctx(q_SSL_CONF_CTX_new(), &q_SSL_CONF_CTX_free);
@@ -256,8 +272,10 @@ void QSslContext::applyBackendConfig(QSslContext *sslContext)
q_SSL_CONF_CTX_set_ssl_ctx(cctx.data(), sslContext->ctx);
q_SSL_CONF_CTX_set_flags(cctx.data(), SSL_CONF_FLAG_FILE);
- const auto &backendConfig = sslContext->sslConfiguration.backendConfiguration();
- for (auto i = backendConfig.constBegin(); i != backendConfig.constEnd(); ++i) {
+ for (auto i = conf.constBegin(); i != conf.constEnd(); ++i) {
+ if (i.key() == "Qt-OCSP-response") // This never goes to SSL_CONF_cmd().
+ continue;
+
if (!i.value().canConvert(QMetaType::QByteArray)) {
sslContext->errorCode = QSslError::UnspecifiedError;
sslContext->errorStr = msgErrorSettingBackendConfig(
diff --git a/src/network/ssl/qsslcontext_openssl11.cpp b/src/network/ssl/qsslcontext_openssl11.cpp
index 708cb7bb0e..db023b7331 100644
--- a/src/network/ssl/qsslcontext_openssl11.cpp
+++ b/src/network/ssl/qsslcontext_openssl11.cpp
@@ -95,17 +95,34 @@ init_context:
// SSL 2 is no longer supported, but chosen deliberately -> error
sslContext->ctx = nullptr;
unsupportedProtocol = true;
+ } else if (sslContext->sslConfiguration.protocol() == QSsl::SslV3) {
+ // SSL 3 is no longer supported, but chosen deliberately -> error
+ sslContext->ctx = nullptr;
+ unsupportedProtocol = true;
} else {
switch (sslContext->sslConfiguration.protocol()) {
-#if QT_CONFIG(dtls)
case QSsl::DtlsV1_0:
case QSsl::DtlsV1_0OrLater:
case QSsl::DtlsV1_2:
case QSsl::DtlsV1_2OrLater:
+#if QT_CONFIG(dtls)
isDtls = true;
sslContext->ctx = q_SSL_CTX_new(client ? q_DTLS_client_method() : q_DTLS_server_method());
- break;
+#else // dtls
+ sslContext->ctx = nullptr;
+ unsupportedProtocol = true;
+ qCWarning(lcSsl, "DTLS protocol requested, but feature 'dtls' is disabled");
+
#endif // dtls
+ break;
+ case QSsl::TlsV1_3:
+ case QSsl::TlsV1_3OrLater:
+#if !defined(TLS1_3_VERSION)
+ qCWarning(lcSsl, "TLS 1.3 is not supported");
+ sslContext->ctx = nullptr;
+ unsupportedProtocol = true;
+ break;
+#endif // TLS1_3_VERSION
default:
// The ssl options will actually control the supported methods
sslContext->ctx = q_SSL_CTX_new(client ? q_TLS_client_method() : q_TLS_server_method());
@@ -138,11 +155,6 @@ init_context:
long maxVersion = anyVersion;
switch (sslContext->sslConfiguration.protocol()) {
- // The single-protocol versions first:
- case QSsl::SslV3:
- minVersion = SSL3_VERSION;
- maxVersion = SSL3_VERSION;
- break;
case QSsl::TlsV1_0:
minVersion = TLS1_VERSION;
maxVersion = TLS1_VERSION;
@@ -155,12 +167,19 @@ init_context:
minVersion = TLS1_2_VERSION;
maxVersion = TLS1_2_VERSION;
break;
+ case QSsl::TlsV1_3:
+#ifdef TLS1_3_VERSION
+ minVersion = TLS1_3_VERSION;
+ maxVersion = TLS1_3_VERSION;
+#else
+ // This protocol is not supported by OpenSSL 1.1 and we handle
+ // it as an error (see the code above).
+ Q_UNREACHABLE();
+#endif // TLS1_3_VERSION
+ break;
// Ranges:
case QSsl::TlsV1SslV3:
case QSsl::AnyProtocol:
- minVersion = SSL3_VERSION;
- maxVersion = 0;
- break;
case QSsl::SecureProtocols:
case QSsl::TlsV1_0OrLater:
minVersion = TLS1_VERSION;
@@ -174,7 +193,6 @@ init_context:
minVersion = TLS1_2_VERSION;
maxVersion = 0;
break;
-#if QT_CONFIG(dtls)
case QSsl::DtlsV1_0:
minVersion = DTLS1_VERSION;
maxVersion = DTLS1_VERSION;
@@ -191,12 +209,23 @@ init_context:
minVersion = DTLS1_2_VERSION;
maxVersion = DTLS_MAX_VERSION;
break;
-#endif // dtls
- case QSsl::SslV2:
+ case QSsl::TlsV1_3OrLater:
+#ifdef TLS1_3_VERSION
+ minVersion = TLS1_3_VERSION;
+ maxVersion = 0;
+ break;
+#else
// This protocol is not supported by OpenSSL 1.1 and we handle
// it as an error (see the code above).
Q_UNREACHABLE();
break;
+#endif // TLS1_3_VERSION
+ case QSsl::SslV2:
+ case QSsl::SslV3:
+ // These protocols are not supported, and we handle
+ // them as an error (see the code above).
+ Q_UNREACHABLE();
+ break;
case QSsl::UnknownProtocol:
break;
}
@@ -223,23 +252,52 @@ init_context:
// http://www.openssl.org/docs/ssl/SSL_CTX_set_mode.html
q_SSL_CTX_set_mode(sslContext->ctx, SSL_MODE_RELEASE_BUFFERS);
+ auto filterCiphers = [](const QList<QSslCipher> &ciphers, bool selectTls13)
+ {
+ QByteArray cipherString;
+ bool first = true;
+
+ for (const QSslCipher &cipher : qAsConst(ciphers)) {
+ const bool isTls13Cipher = cipher.protocol() == QSsl::TlsV1_3 || cipher.protocol() == QSsl::TlsV1_3OrLater;
+ if (selectTls13 != isTls13Cipher)
+ continue;
+
+ if (first)
+ first = false;
+ else
+ cipherString.append(':');
+ cipherString.append(cipher.name().toLatin1());
+ }
+ return cipherString;
+ };
+
// Initialize ciphers
- QByteArray cipherString;
- bool first = true;
QList<QSslCipher> ciphers = sslContext->sslConfiguration.ciphers();
if (ciphers.isEmpty())
ciphers = isDtls ? q_getDefaultDtlsCiphers() : QSslSocketPrivate::defaultCiphers();
- for (const QSslCipher &cipher : qAsConst(ciphers)) {
- if (first)
- first = false;
- else
- cipherString.append(':');
- cipherString.append(cipher.name().toLatin1());
+ const QByteArray preTls13Ciphers = filterCiphers(ciphers, false);
+
+ if (preTls13Ciphers.size()) {
+ if (!q_SSL_CTX_set_cipher_list(sslContext->ctx, preTls13Ciphers.data())) {
+ sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return;
+ }
}
- if (!q_SSL_CTX_set_cipher_list(sslContext->ctx, cipherString.data())) {
- sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
+ const QByteArray tls13Ciphers = filterCiphers(ciphers, true);
+#ifdef TLS1_3_VERSION
+ if (tls13Ciphers.size()) {
+ if (!q_SSL_CTX_set_ciphersuites(sslContext->ctx, tls13Ciphers.data())) {
+ sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return;
+ }
+ }
+#endif // TLS1_3_VERSION
+ if (!preTls13Ciphers.size() && !tls13Ciphers.size()) {
+ sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QStringLiteral(""));
sslContext->errorCode = QSslError::UnspecifiedError;
return;
}
diff --git a/src/network/ssl/qsslcontext_openssl_p.h b/src/network/ssl/qsslcontext_openssl_p.h
index 48beebf134..1fa27279c7 100644
--- a/src/network/ssl/qsslcontext_openssl_p.h
+++ b/src/network/ssl/qsslcontext_openssl_p.h
@@ -89,7 +89,7 @@ public:
#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)
// must be public because we want to use it from an OpenSSL callback
struct NPNContext {
- NPNContext() : data(0),
+ NPNContext() : data(nullptr),
len(0),
status(QSslConfiguration::NextProtocolNegotiationNone)
{ }
diff --git a/src/network/ssl/qsslcontext_opensslpre11.cpp b/src/network/ssl/qsslcontext_opensslpre11.cpp
index c8be2ecb31..956c5c32ec 100644
--- a/src/network/ssl/qsslcontext_opensslpre11.cpp
+++ b/src/network/ssl/qsslcontext_opensslpre11.cpp
@@ -104,34 +104,30 @@ init_context:
isDtls = true;
sslContext->ctx = q_SSL_CTX_new(client ? q_DTLS_client_method() : q_DTLS_server_method());
break;
-#endif // dtls
- case QSsl::SslV2:
-#ifndef OPENSSL_NO_SSL2
- sslContext->ctx = q_SSL_CTX_new(client ? q_SSLv2_client_method() : q_SSLv2_server_method());
-#else
- // SSL 2 not supported by the system, but chosen deliberately -> error
- sslContext->ctx = 0;
+#else // dtls
+ case QSsl::DtlsV1_0:
+ case QSsl::DtlsV1_0OrLater:
+ case QSsl::DtlsV1_2:
+ case QSsl::DtlsV1_2OrLater:
+ sslContext->ctx = nullptr;
unsupportedProtocol = true;
-#endif
+ qCWarning(lcSsl, "DTLS protocol requested, but feature 'dtls' is disabled");
break;
+#endif // dtls
+ case QSsl::SslV2:
case QSsl::SslV3:
-#ifndef OPENSSL_NO_SSL3_METHOD
- sslContext->ctx = q_SSL_CTX_new(client ? q_SSLv3_client_method() : q_SSLv3_server_method());
-#else
- // SSL 3 not supported by the system, but chosen deliberately -> error
+ // We don't support SSLv2 / SSLv3.
sslContext->ctx = 0;
unsupportedProtocol = true;
-#endif
break;
case QSsl::SecureProtocols:
// SSLv2 and SSLv3 will be disabled by SSL options
// But we need q_SSLv23_server_method() otherwise AnyProtocol will be unable to connect on Win32.
- case QSsl::TlsV1SslV3:
- // SSLv2 will will be disabled by SSL options
case QSsl::AnyProtocol:
default:
sslContext->ctx = q_SSL_CTX_new(client ? q_SSLv23_client_method() : q_SSLv23_server_method());
break;
+ case QSsl::TlsV1SslV3:
case QSsl::TlsV1_0:
sslContext->ctx = q_SSL_CTX_new(client ? q_TLSv1_client_method() : q_TLSv1_server_method());
break;
@@ -168,6 +164,12 @@ init_context:
unsupportedProtocol = true;
#endif
break;
+ case QSsl::TlsV1_3:
+ case QSsl::TlsV1_3OrLater:
+ // TLS 1.3 is not supported by the system, but chosen deliberately -> error
+ sslContext->ctx = nullptr;
+ unsupportedProtocol = true;
+ break;
}
if (!client && isDtls && configuration.peerVerifyMode() != QSslSocket::VerifyNone) {
@@ -197,12 +199,9 @@ init_context:
long options = QSslSocketBackendPrivate::setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions);
q_SSL_CTX_set_options(sslContext->ctx, options);
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
// Tell OpenSSL to release memory early
// http://www.openssl.org/docs/ssl/SSL_CTX_set_mode.html
- if (q_SSLeay() >= 0x10000000L)
- q_SSL_CTX_set_mode(sslContext->ctx, SSL_MODE_RELEASE_BUFFERS);
-#endif
+ q_SSL_CTX_set_mode(sslContext->ctx, SSL_MODE_RELEASE_BUFFERS);
// Initialize ciphers
QByteArray cipherString;
diff --git a/src/network/ssl/qssldiffiehellmanparameters.cpp b/src/network/ssl/qssldiffiehellmanparameters.cpp
index 7fbcff2861..7807afaa30 100644
--- a/src/network/ssl/qssldiffiehellmanparameters.cpp
+++ b/src/network/ssl/qssldiffiehellmanparameters.cpp
@@ -136,7 +136,7 @@ QSslDiffieHellmanParameters QSslDiffieHellmanParameters::fromEncoded(const QByte
to check whether the Diffie-Hellman parameters were valid
and loaded correctly.
- In particular, if \a device is \c nullptr or not open for reading, an invalid
+ In particular, if \a device is \nullptr or not open for reading, an invalid
object will be returned.
\sa isValid()
@@ -213,7 +213,7 @@ QSslDiffieHellmanParameters &QSslDiffieHellmanParameters::operator=(const QSslDi
Setting an empty QSslDiffieHellmanParameters instance on a QSslSocket-based
server will disable Diffie-Hellman key exchange.
*/
-bool QSslDiffieHellmanParameters::isEmpty() const Q_DECL_NOTHROW
+bool QSslDiffieHellmanParameters::isEmpty() const noexcept
{
return d->derData.isNull() && d->error == QSslDiffieHellmanParameters::NoError;
}
@@ -229,7 +229,7 @@ bool QSslDiffieHellmanParameters::isEmpty() const Q_DECL_NOTHROW
\sa error()
*/
-bool QSslDiffieHellmanParameters::isValid() const Q_DECL_NOTHROW
+bool QSslDiffieHellmanParameters::isValid() const noexcept
{
return d->error == QSslDiffieHellmanParameters::NoError;
}
@@ -253,7 +253,7 @@ bool QSslDiffieHellmanParameters::isValid() const Q_DECL_NOTHROW
Returns the error that caused the QSslDiffieHellmanParameters object
to be invalid.
*/
-QSslDiffieHellmanParameters::Error QSslDiffieHellmanParameters::error() const Q_DECL_NOTHROW
+QSslDiffieHellmanParameters::Error QSslDiffieHellmanParameters::error() const noexcept
{
return d->error;
}
@@ -262,7 +262,7 @@ QSslDiffieHellmanParameters::Error QSslDiffieHellmanParameters::error() const Q_
Returns a human-readable description of the error that caused the
QSslDiffieHellmanParameters object to be invalid.
*/
-QString QSslDiffieHellmanParameters::errorString() const Q_DECL_NOTHROW
+QString QSslDiffieHellmanParameters::errorString() const noexcept
{
switch (d->error) {
case QSslDiffieHellmanParameters::NoError:
@@ -283,7 +283,7 @@ QString QSslDiffieHellmanParameters::errorString() const Q_DECL_NOTHROW
Returns \c true if \a lhs is equal to \a rhs; otherwise returns \c false.
*/
-bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) Q_DECL_NOTHROW
+bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) noexcept
{
return lhs.d->derData == rhs.d->derData;
}
@@ -316,7 +316,7 @@ QDebug operator<<(QDebug debug, const QSslDiffieHellmanParameters &dhparam)
Returns an hash value for \a dhparam, using \a seed to seed
the calculation.
*/
-uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed) Q_DECL_NOTHROW
+uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed) noexcept
{
return qHash(dhparam.d->derData, seed);
}
diff --git a/src/network/ssl/qssldiffiehellmanparameters.h b/src/network/ssl/qssldiffiehellmanparameters.h
index 497d2bebfb..f62a3b8f44 100644
--- a/src/network/ssl/qssldiffiehellmanparameters.h
+++ b/src/network/ssl/qssldiffiehellmanparameters.h
@@ -56,16 +56,16 @@ class QSslDiffieHellmanParametersPrivate;
class QSslDiffieHellmanParameters;
// qHash is a friend, but we can't use default arguments for friends (§8.3.6.4)
-Q_NETWORK_EXPORT uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed = 0) Q_DECL_NOTHROW;
+Q_NETWORK_EXPORT uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed = 0) noexcept;
#ifndef QT_NO_DEBUG_STREAM
class QDebug;
Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslDiffieHellmanParameters &dhparams);
#endif
-Q_NETWORK_EXPORT bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) Q_DECL_NOTHROW;
+Q_NETWORK_EXPORT bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) noexcept;
-inline bool operator!=(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) Q_DECL_NOTHROW
+inline bool operator!=(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) noexcept
{
return !operator==(lhs, rhs);
}
@@ -83,30 +83,30 @@ public:
Q_NETWORK_EXPORT QSslDiffieHellmanParameters();
Q_NETWORK_EXPORT QSslDiffieHellmanParameters(const QSslDiffieHellmanParameters &other);
- QSslDiffieHellmanParameters(QSslDiffieHellmanParameters &&other) Q_DECL_NOTHROW : d(other.d) { other.d = nullptr; }
+ QSslDiffieHellmanParameters(QSslDiffieHellmanParameters &&other) noexcept : d(other.d) { other.d = nullptr; }
Q_NETWORK_EXPORT ~QSslDiffieHellmanParameters();
Q_NETWORK_EXPORT QSslDiffieHellmanParameters &operator=(const QSslDiffieHellmanParameters &other);
- QSslDiffieHellmanParameters &operator=(QSslDiffieHellmanParameters &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QSslDiffieHellmanParameters &operator=(QSslDiffieHellmanParameters &&other) noexcept { swap(other); return *this; }
- void swap(QSslDiffieHellmanParameters &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QSslDiffieHellmanParameters &other) noexcept { qSwap(d, other.d); }
Q_NETWORK_EXPORT static QSslDiffieHellmanParameters fromEncoded(const QByteArray &encoded, QSsl::EncodingFormat format = QSsl::Pem);
Q_NETWORK_EXPORT static QSslDiffieHellmanParameters fromEncoded(QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem);
- Q_NETWORK_EXPORT bool isEmpty() const Q_DECL_NOTHROW;
- Q_NETWORK_EXPORT bool isValid() const Q_DECL_NOTHROW;
- Q_NETWORK_EXPORT Error error() const Q_DECL_NOTHROW;
- Q_NETWORK_EXPORT QString errorString() const Q_DECL_NOTHROW;
+ Q_NETWORK_EXPORT bool isEmpty() const noexcept;
+ Q_NETWORK_EXPORT bool isValid() const noexcept;
+ Q_NETWORK_EXPORT Error error() const noexcept;
+ Q_NETWORK_EXPORT QString errorString() const noexcept;
private:
QSslDiffieHellmanParametersPrivate *d;
friend class QSslContext;
- friend Q_NETWORK_EXPORT bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) Q_DECL_NOTHROW;
+ friend Q_NETWORK_EXPORT bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) noexcept;
#ifndef QT_NO_DEBUG_STREAM
friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslDiffieHellmanParameters &dhparam);
#endif
- friend Q_NETWORK_EXPORT uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed) Q_DECL_NOTHROW;
+ friend Q_NETWORK_EXPORT uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed) noexcept;
};
Q_DECLARE_SHARED(QSslDiffieHellmanParameters)
diff --git a/src/network/ssl/qsslellipticcurve.cpp b/src/network/ssl/qsslellipticcurve.cpp
index 88baa1ff6c..5608d32fa7 100644
--- a/src/network/ssl/qsslellipticcurve.cpp
+++ b/src/network/ssl/qsslellipticcurve.cpp
@@ -64,6 +64,8 @@ QT_BEGIN_NAMESPACE
QSslEllipticCurve instances can be compared for equality and can be used as keys
in QHash and QSet. They cannot be used as key in a QMap.
+
+ \note This class is currently only supported in OpenSSL.
*/
/*!
diff --git a/src/network/ssl/qsslellipticcurve.h b/src/network/ssl/qsslellipticcurve.h
index 57dda19bad..28de3a03b4 100644
--- a/src/network/ssl/qsslellipticcurve.h
+++ b/src/network/ssl/qsslellipticcurve.h
@@ -52,11 +52,11 @@ QT_BEGIN_NAMESPACE
class QSslEllipticCurve;
// qHash is a friend, but we can't use default arguments for friends (§8.3.6.4)
-Q_DECL_CONSTEXPR uint qHash(QSslEllipticCurve curve, uint seed = 0) Q_DECL_NOTHROW;
+Q_DECL_CONSTEXPR uint qHash(QSslEllipticCurve curve, uint seed = 0) noexcept;
class QSslEllipticCurve {
public:
- Q_DECL_CONSTEXPR QSslEllipticCurve() Q_DECL_NOTHROW
+ Q_DECL_CONSTEXPR QSslEllipticCurve() noexcept
: id(0)
{
}
@@ -67,18 +67,18 @@ public:
Q_REQUIRED_RESULT Q_NETWORK_EXPORT QString shortName() const;
Q_REQUIRED_RESULT Q_NETWORK_EXPORT QString longName() const;
- Q_DECL_CONSTEXPR bool isValid() const Q_DECL_NOTHROW
+ Q_DECL_CONSTEXPR bool isValid() const noexcept
{
return id != 0;
}
- Q_NETWORK_EXPORT bool isTlsNamedCurve() const Q_DECL_NOTHROW;
+ Q_NETWORK_EXPORT bool isTlsNamedCurve() const noexcept;
private:
int id;
- friend Q_DECL_CONSTEXPR bool operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs) Q_DECL_NOTHROW;
- friend Q_DECL_CONSTEXPR uint qHash(QSslEllipticCurve curve, uint seed) Q_DECL_NOTHROW;
+ friend Q_DECL_CONSTEXPR bool operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs) noexcept;
+ friend Q_DECL_CONSTEXPR uint qHash(QSslEllipticCurve curve, uint seed) noexcept;
friend class QSslContext;
friend class QSslSocketPrivate;
@@ -87,13 +87,13 @@ private:
Q_DECLARE_TYPEINFO(QSslEllipticCurve, Q_PRIMITIVE_TYPE);
-Q_DECL_CONSTEXPR inline uint qHash(QSslEllipticCurve curve, uint seed) Q_DECL_NOTHROW
+Q_DECL_CONSTEXPR inline uint qHash(QSslEllipticCurve curve, uint seed) noexcept
{ return qHash(curve.id, seed); }
-Q_DECL_CONSTEXPR inline bool operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs) Q_DECL_NOTHROW
+Q_DECL_CONSTEXPR inline bool operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs) noexcept
{ return lhs.id == rhs.id; }
-Q_DECL_CONSTEXPR inline bool operator!=(QSslEllipticCurve lhs, QSslEllipticCurve rhs) Q_DECL_NOTHROW
+Q_DECL_CONSTEXPR inline bool operator!=(QSslEllipticCurve lhs, QSslEllipticCurve rhs) noexcept
{ return !operator==(lhs, rhs); }
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/network/ssl/qsslellipticcurve_dummy.cpp b/src/network/ssl/qsslellipticcurve_dummy.cpp
index 93e081b9e0..1313e06875 100644
--- a/src/network/ssl/qsslellipticcurve_dummy.cpp
+++ b/src/network/ssl/qsslellipticcurve_dummy.cpp
@@ -63,7 +63,7 @@ QSslEllipticCurve QSslEllipticCurve::fromLongName(const QString &name)
return QSslEllipticCurve();
}
-bool QSslEllipticCurve::isTlsNamedCurve() const Q_DECL_NOTHROW
+bool QSslEllipticCurve::isTlsNamedCurve() const noexcept
{
return false;
}
diff --git a/src/network/ssl/qsslellipticcurve_openssl.cpp b/src/network/ssl/qsslellipticcurve_openssl.cpp
index 8cd14837f0..b5e38ada53 100644
--- a/src/network/ssl/qsslellipticcurve_openssl.cpp
+++ b/src/network/ssl/qsslellipticcurve_openssl.cpp
@@ -170,7 +170,7 @@ static const int tlsNamedCurveNIDs[] = {
static const size_t tlsNamedCurveNIDCount = sizeof(tlsNamedCurveNIDs) / sizeof(tlsNamedCurveNIDs[0]);
-bool QSslEllipticCurve::isTlsNamedCurve() const Q_DECL_NOTHROW
+bool QSslEllipticCurve::isTlsNamedCurve() const noexcept
{
const int * const tlsNamedCurveNIDsEnd = tlsNamedCurveNIDs + tlsNamedCurveNIDCount;
return std::find(tlsNamedCurveNIDs, tlsNamedCurveNIDsEnd, id) != tlsNamedCurveNIDsEnd;
diff --git a/src/network/ssl/qsslerror.cpp b/src/network/ssl/qsslerror.cpp
index 3f79d1a037..ff54c2ad20 100644
--- a/src/network/ssl/qsslerror.cpp
+++ b/src/network/ssl/qsslerror.cpp
@@ -86,6 +86,18 @@
\value UnspecifiedError
\value NoSslSupport
\value CertificateBlacklisted
+ \value OcspNoResponseFound
+ \value OcspMalformedRequest
+ \value OcspMalformedResponse
+ \value OcspInternalError
+ \value OcspTryLater
+ \value OcspSigRequred
+ \value OcspUnauthorized
+ \value OcspResponseCannotBeTrusted
+ \value OcspResponseCertIdUnknown
+ \value OcspResponseExpired
+ \value OcspStatusUnknown
+
\sa QSslError::errorString()
*/
@@ -292,6 +304,39 @@ QString QSslError::errorString() const
case CertificateBlacklisted:
errStr = QSslSocket::tr("The peer certificate is blacklisted");
break;
+ case OcspNoResponseFound:
+ errStr = QSslSocket::tr("No OCSP status response found");
+ break;
+ case OcspMalformedRequest:
+ errStr = QSslSocket::tr("The OCSP status request had invalid syntax");
+ break;
+ case OcspMalformedResponse:
+ errStr = QSslSocket::tr("OCSP response contains an unexpected number of SingleResponse structures");
+ break;
+ case OcspInternalError:
+ errStr = QSslSocket::tr("OCSP responder reached an inconsistent internal state");
+ break;
+ case OcspTryLater:
+ errStr = QSslSocket::tr("OCSP responder was unable to return a status for the requested certificate");
+ break;
+ case OcspSigRequred:
+ errStr = QSslSocket::tr("The server requires the client to sign the OCSP request in order to construct a response");
+ break;
+ case OcspUnauthorized:
+ errStr = QSslSocket::tr("The client is not authorized to request OCSP status from this server");
+ break;
+ case OcspResponseCannotBeTrusted:
+ errStr = QSslSocket::tr("OCSP responder's identity cannot be verified");
+ break;
+ case OcspResponseCertIdUnknown:
+ errStr = QSslSocket::tr("The identity of a certificate in an OCSP response cannot be established");
+ break;
+ case OcspResponseExpired:
+ errStr = QSslSocket::tr("The certificate status response has expired");
+ break;
+ case OcspStatusUnknown:
+ errStr = QSslSocket::tr("The certificate's status is unknown");
+ break;
default:
errStr = QSslSocket::tr("Unknown error");
break;
@@ -316,7 +361,7 @@ QSslCertificate QSslError::certificate() const
\since 5.4
\relates QHash
*/
-uint qHash(const QSslError &key, uint seed) Q_DECL_NOTHROW
+uint qHash(const QSslError &key, uint seed) noexcept
{
// 2x boost::hash_combine inlined:
seed ^= qHash(key.error()) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
diff --git a/src/network/ssl/qsslerror.h b/src/network/ssl/qsslerror.h
index d7c959423d..a9c46c8571 100644
--- a/src/network/ssl/qsslerror.h
+++ b/src/network/ssl/qsslerror.h
@@ -80,6 +80,18 @@ public:
HostNameMismatch,
NoSslSupport,
CertificateBlacklisted,
+ CertificateStatusUnknown,
+ OcspNoResponseFound,
+ OcspMalformedRequest,
+ OcspMalformedResponse,
+ OcspInternalError,
+ OcspTryLater,
+ OcspSigRequred,
+ OcspUnauthorized,
+ OcspResponseCannotBeTrusted,
+ OcspResponseCertIdUnknown,
+ OcspResponseExpired,
+ OcspStatusUnknown,
UnspecifiedError = -1
};
@@ -91,12 +103,12 @@ public:
QSslError(const QSslError &other);
- void swap(QSslError &other) Q_DECL_NOTHROW
+ void swap(QSslError &other) noexcept
{ qSwap(d, other.d); }
~QSslError();
#ifdef Q_COMPILER_RVALUE_REFS
- QSslError &operator=(QSslError &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QSslError &operator=(QSslError &&other) noexcept { swap(other); return *this; }
#endif
QSslError &operator=(const QSslError &other);
bool operator==(const QSslError &other) const;
@@ -112,7 +124,7 @@ private:
};
Q_DECLARE_SHARED(QSslError)
-Q_NETWORK_EXPORT uint qHash(const QSslError &key, uint seed = 0) Q_DECL_NOTHROW;
+Q_NETWORK_EXPORT uint qHash(const QSslError &key, uint seed = 0) noexcept;
#ifndef QT_NO_DEBUG_STREAM
class QDebug;
diff --git a/src/network/ssl/qsslkey.h b/src/network/ssl/qsslkey.h
index 6de02b1e44..a865f20a51 100644
--- a/src/network/ssl/qsslkey.h
+++ b/src/network/ssl/qsslkey.h
@@ -72,12 +72,12 @@ public:
explicit QSslKey(Qt::HANDLE handle, QSsl::KeyType type = QSsl::PrivateKey);
QSslKey(const QSslKey &other);
#ifdef Q_COMPILER_RVALUE_REFS
- QSslKey &operator=(QSslKey &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QSslKey &operator=(QSslKey &&other) noexcept { swap(other); return *this; }
#endif
QSslKey &operator=(const QSslKey &other);
~QSslKey();
- void swap(QSslKey &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QSslKey &other) noexcept { qSwap(d, other.d); }
bool isNull() const;
void clear();
diff --git a/src/network/ssl/qsslkey_mac.cpp b/src/network/ssl/qsslkey_mac.cpp
index d460cbfdab..814fe1c4bc 100644
--- a/src/network/ssl/qsslkey_mac.cpp
+++ b/src/network/ssl/qsslkey_mac.cpp
@@ -42,7 +42,9 @@
#include <CommonCrypto/CommonCrypto.h>
-QT_USE_NAMESPACE
+#include <cstddef>
+
+QT_BEGIN_NAMESPACE
static QByteArray wrapCCCrypt(CCOperation ccOp,
QSslKeyPrivate::Cipher cipher,
@@ -64,17 +66,23 @@ static QByteArray wrapCCCrypt(CCOperation ccOp,
blockSize = kCCBlockSizeRC2;
ccAlgorithm = kCCAlgorithmRC2;
break;
- };
+ case QSslKeyPrivate::Aes128Cbc:
+ case QSslKeyPrivate::Aes192Cbc:
+ case QSslKeyPrivate::Aes256Cbc:
+ blockSize = kCCBlockSizeAES128;
+ ccAlgorithm = kCCAlgorithmAES;
+ break;
+ }
size_t plainLength = 0;
QByteArray plain(data.size() + blockSize, 0);
CCCryptorStatus status = CCCrypt(
ccOp, ccAlgorithm, kCCOptionPKCS7Padding,
- key.constData(), key.size(),
+ key.constData(), std::size_t(key.size()),
iv.constData(),
- data.constData(), data.size(),
- plain.data(), plain.size(), &plainLength);
+ data.constData(), std::size_t(data.size()),
+ plain.data(), std::size_t(plain.size()), &plainLength);
if (status == kCCSuccess)
- return plain.left(plainLength);
+ return plain.left(int(plainLength));
return QByteArray();
}
@@ -87,3 +95,5 @@ QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const
{
return wrapCCCrypt(kCCEncrypt, cipher, data, key, iv);
}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslkey_openssl.cpp b/src/network/ssl/qsslkey_openssl.cpp
index 9a43e67772..888058df22 100644
--- a/src/network/ssl/qsslkey_openssl.cpp
+++ b/src/network/ssl/qsslkey_openssl.cpp
@@ -69,6 +69,11 @@ void QSslKeyPrivate::clear(bool deep)
q_DSA_free(dsa);
dsa = nullptr;
}
+ if (algorithm == QSsl::Dh && dh) {
+ if (deep)
+ q_DH_free(dh);
+ dh = nullptr;
+ }
#ifndef OPENSSL_NO_EC
if (algorithm == QSsl::Ec && ec) {
if (deep)
@@ -105,6 +110,12 @@ bool QSslKeyPrivate::fromEVP_PKEY(EVP_PKEY *pkey)
type = QSsl::PrivateKey;
dsa = q_EVP_PKEY_get1_DSA(pkey);
return true;
+ } else if (keyType == EVP_PKEY_DH) {
+ isNull = false;
+ algorithm = QSsl::Dh;
+ type = QSsl::PrivateKey;
+ dh = q_EVP_PKEY_get1_DH(pkey);
+ return true;
}
#ifndef OPENSSL_NO_EC
else if (keyType == EVP_PKEY_EC) {
@@ -160,6 +171,15 @@ void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhra
: q_PEM_read_bio_DSAPrivateKey(bio, &dsa, nullptr, phrase);
if (dsa && dsa == result)
isNull = false;
+ } else if (algorithm == QSsl::Dh) {
+ EVP_PKEY *result = (type == QSsl::PublicKey)
+ ? q_PEM_read_bio_PUBKEY(bio, nullptr, nullptr, phrase)
+ : q_PEM_read_bio_PrivateKey(bio, nullptr, nullptr, phrase);
+ if (result)
+ dh = q_EVP_PKEY_get1_DH(result);
+ if (dh)
+ isNull = false;
+ q_EVP_PKEY_free(result);
#ifndef OPENSSL_NO_EC
} else if (algorithm == QSsl::Ec) {
EC_KEY *result = (type == QSsl::PublicKey)
@@ -181,6 +201,7 @@ int QSslKeyPrivate::length() const
switch (algorithm) {
case QSsl::Rsa: return q_RSA_bits(rsa);
case QSsl::Dsa: return q_DSA_bits(dsa);
+ case QSsl::Dh: return q_DH_bits(dh);
#ifndef OPENSSL_NO_EC
case QSsl::Ec: return q_EC_GROUP_get_degree(q_EC_KEY_get0_group(ec));
#endif
@@ -215,7 +236,7 @@ QByteArray QSslKeyPrivate::toPem(const QByteArray &passPhrase) const
fail = true;
} else {
if (!q_PEM_write_bio_RSAPrivateKey(
- bio, rsa, cipher, const_cast<uchar *>((const uchar *)passPhrase.data()),
+ bio, rsa, cipher, (uchar *)passPhrase.data(),
passPhrase.size(), nullptr, nullptr)) {
fail = true;
}
@@ -226,20 +247,33 @@ QByteArray QSslKeyPrivate::toPem(const QByteArray &passPhrase) const
fail = true;
} else {
if (!q_PEM_write_bio_DSAPrivateKey(
- bio, dsa, cipher, const_cast<uchar *>((const uchar *)passPhrase.data()),
+ bio, dsa, cipher, (uchar *)passPhrase.data(),
passPhrase.size(), nullptr, nullptr)) {
fail = true;
}
}
+ } else if (algorithm == QSsl::Dh) {
+ EVP_PKEY *result = q_EVP_PKEY_new();
+ if (!result || !q_EVP_PKEY_set1_DH(result, dh)) {
+ fail = true;
+ } else if (type == QSsl::PublicKey) {
+ if (!q_PEM_write_bio_PUBKEY(bio, result))
+ fail = true;
+ } else if (!q_PEM_write_bio_PrivateKey(
+ bio, result, cipher, (uchar *)passPhrase.data(),
+ passPhrase.size(), nullptr, nullptr)) {
+ fail = true;
+ }
+ q_EVP_PKEY_free(result);
#ifndef OPENSSL_NO_EC
} else if (algorithm == QSsl::Ec) {
if (type == QSsl::PublicKey) {
if (!q_PEM_write_bio_EC_PUBKEY(bio, ec))
fail = true;
} else {
- if (!q_PEM_write_bio_ECPrivateKey(bio, ec, cipher,
- const_cast<uchar *>((const uchar *)passPhrase.data()),
- passPhrase.size(), nullptr, nullptr)) {
+ if (!q_PEM_write_bio_ECPrivateKey(
+ bio, ec, cipher, (uchar *)passPhrase.data(),
+ passPhrase.size(), nullptr, nullptr)) {
fail = true;
}
}
@@ -267,6 +301,8 @@ Qt::HANDLE QSslKeyPrivate::handle() const
return Qt::HANDLE(rsa);
case QSsl::Dsa:
return Qt::HANDLE(dsa);
+ case QSsl::Dh:
+ return Qt::HANDLE(dh);
#ifndef OPENSSL_NO_EC
case QSsl::Ec:
return Qt::HANDLE(ec);
@@ -297,6 +333,15 @@ static QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data,
type = q_EVP_rc2_cbc();
#endif
break;
+ case QSslKeyPrivate::Aes128Cbc:
+ type = q_EVP_aes_128_cbc();
+ break;
+ case QSslKeyPrivate::Aes192Cbc:
+ type = q_EVP_aes_192_cbc();
+ break;
+ case QSslKeyPrivate::Aes256Cbc:
+ type = q_EVP_aes_256_cbc();
+ break;
}
if (type == nullptr)
diff --git a/src/network/ssl/qsslkey_p.cpp b/src/network/ssl/qsslkey_p.cpp
index 28e3e2efd8..b29b38beab 100644
--- a/src/network/ssl/qsslkey_p.cpp
+++ b/src/network/ssl/qsslkey_p.cpp
@@ -116,6 +116,8 @@ QByteArray QSslKeyPrivate::pemHeader() const
return QByteArrayLiteral("-----BEGIN DSA PRIVATE KEY-----");
else if (algorithm == QSsl::Ec)
return QByteArrayLiteral("-----BEGIN EC PRIVATE KEY-----");
+ else if (algorithm == QSsl::Dh)
+ return QByteArrayLiteral("-----BEGIN PRIVATE KEY-----");
Q_UNREACHABLE();
return QByteArray();
@@ -141,6 +143,8 @@ QByteArray QSslKeyPrivate::pemFooter() const
return QByteArrayLiteral("-----END DSA PRIVATE KEY-----");
else if (algorithm == QSsl::Ec)
return QByteArrayLiteral("-----END EC PRIVATE KEY-----");
+ else if (algorithm == QSsl::Dh)
+ return QByteArrayLiteral("-----END PRIVATE KEY-----");
Q_UNREACHABLE();
return QByteArray();
@@ -535,7 +539,9 @@ QDebug operator<<(QDebug debug, const QSslKey &key)
debug << "QSslKey("
<< (key.type() == QSsl::PublicKey ? "PublicKey" : "PrivateKey")
<< ", " << (key.algorithm() == QSsl::Opaque ? "OPAQUE" :
- (key.algorithm() == QSsl::Rsa ? "RSA" : ((key.algorithm() == QSsl::Dsa) ? "DSA" : "EC")))
+ (key.algorithm() == QSsl::Rsa ? "RSA" :
+ (key.algorithm() == QSsl::Dsa ? "DSA" :
+ (key.algorithm() == QSsl::Dh ? "DH" : "EC"))))
<< ", " << key.length()
<< ')';
return debug;
diff --git a/src/network/ssl/qsslkey_p.h b/src/network/ssl/qsslkey_p.h
index 7ae2cc740b..dd1a31b0e5 100644
--- a/src/network/ssl/qsslkey_p.h
+++ b/src/network/ssl/qsslkey_p.h
@@ -68,7 +68,7 @@ class QSslKeyPrivate
public:
inline QSslKeyPrivate()
: algorithm(QSsl::Opaque)
- , opaque(0)
+ , opaque(nullptr)
{
clear(false);
}
@@ -105,7 +105,10 @@ public:
enum Cipher {
DesCbc,
DesEde3Cbc,
- Rc2Cbc
+ Rc2Cbc,
+ Aes128Cbc,
+ Aes192Cbc,
+ Aes256Cbc
};
Q_AUTOTEST_EXPORT static QByteArray decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv);
@@ -116,6 +119,7 @@ public:
EVP_PKEY *opaque;
RSA *rsa;
DSA *dsa;
+ DH *dh;
#ifndef OPENSSL_NO_EC
EC_KEY *ec;
#endif
@@ -129,7 +133,7 @@ public:
QAtomicInt ref;
private:
- Q_DISABLE_COPY(QSslKeyPrivate)
+ Q_DISABLE_COPY_MOVE(QSslKeyPrivate)
};
QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslkey_qt.cpp b/src/network/ssl/qsslkey_qt.cpp
index a13275f3bb..2662418a05 100644
--- a/src/network/ssl/qsslkey_qt.cpp
+++ b/src/network/ssl/qsslkey_qt.cpp
@@ -124,6 +124,37 @@ static int numberOfBits(const QByteArray &modulus)
return bits;
}
+static QByteArray deriveAesKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase, const QByteArray &iv)
+{
+ // This is somewhat simplified and shortened version of what OpenSSL does.
+ // See, for example, EVP_BytesToKey for the "algorithm" itself and elsewhere
+ // in their code for what they pass as arguments to EVP_BytesToKey when
+ // deriving encryption keys (when reading/writing pems files with encrypted
+ // keys).
+
+ Q_ASSERT(iv.size() >= 8);
+
+ QCryptographicHash hash(QCryptographicHash::Md5);
+
+ QByteArray data(passPhrase);
+ data.append(iv.data(), 8); // AKA PKCS5_SALT_LEN in OpenSSL.
+
+ hash.addData(data);
+
+ if (cipher == QSslKeyPrivate::Aes128Cbc)
+ return hash.result();
+
+ QByteArray key(hash.result());
+ hash.reset();
+ hash.addData(key);
+ hash.addData(data);
+
+ if (cipher == QSslKeyPrivate::Aes192Cbc)
+ return key.append(hash.result().constData(), 8);
+
+ return key.append(hash.result());
+}
+
static QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase, const QByteArray &iv)
{
QByteArray key;
@@ -145,6 +176,10 @@ static QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &pas
case QSslKeyPrivate::Rc2Cbc:
key = hash.result();
break;
+ case QSslKeyPrivate::Aes128Cbc:
+ case QSslKeyPrivate::Aes192Cbc:
+ case QSslKeyPrivate::Aes256Cbc:
+ return deriveAesKey(cipher, passPhrase, iv);
}
return key;
}
@@ -165,6 +200,7 @@ static int extractPkcs8KeyLength(const QVector<QAsn1Element> &items, QSslKeyPriv
switch (algorithm){
case QSsl::Rsa: return "RSA";
case QSsl::Dsa: return "DSA";
+ case QSsl::Dh: return "DH";
case QSsl::Ec: return "EC";
case QSsl::Opaque: return "Opaque";
}
@@ -217,6 +253,21 @@ static int extractPkcs8KeyLength(const QVector<QAsn1Element> &items, QSslKeyPriv
if (dsaInfo.size() != 3 || dsaInfo[0].type() != QAsn1Element::IntegerType)
return -1;
keyLength = numberOfBits(dsaInfo[0].value());
+ } else if (value == DH_ENCRYPTION_OID) {
+ if (Q_UNLIKELY(that->algorithm != QSsl::Dh)) {
+ // As above for RSA.
+ qWarning() << "QSslKey: Found DH when asked to use" << getName(that->algorithm)
+ << "\nLoading will fail.";
+ return -1;
+ }
+ // DH's structure is documented here:
+ // https://www.cryptsoft.com/pkcs11doc/STANDARD/v201-95.pdf in section 11.9.
+ if (pkcs8Info[1].type() != QAsn1Element::SequenceType)
+ return -1;
+ const QVector<QAsn1Element> dhInfo = pkcs8Info[1].toVector();
+ if (dhInfo.size() < 2 || dhInfo.size() > 3 || dhInfo[0].type() != QAsn1Element::IntegerType)
+ return -1;
+ keyLength = numberOfBits(dhInfo[0].value());
} else {
// in case of unexpected formats:
qWarning() << "QSslKey: Unsupported PKCS#8 key algorithm:" << value
@@ -268,6 +319,16 @@ void QSslKeyPrivate::decodeDer(const QByteArray &der, const QByteArray &passPhra
if (params.isEmpty() || params[0].type() != QAsn1Element::IntegerType)
return;
keyLength = numberOfBits(params[0].value());
+ } else if (algorithm == QSsl::Dh) {
+ if (infoItems[0].toObjectId() != DH_ENCRYPTION_OID)
+ return;
+ if (infoItems[1].type() != QAsn1Element::SequenceType)
+ return;
+ // key params
+ const QVector<QAsn1Element> params = infoItems[1].toVector();
+ if (params.isEmpty() || params[0].type() != QAsn1Element::IntegerType)
+ return;
+ keyLength = numberOfBits(params[0].value());
} else if (algorithm == QSsl::Ec) {
if (infoItems[0].toObjectId() != EC_ENCRYPTION_OID)
return;
@@ -307,6 +368,12 @@ void QSslKeyPrivate::decodeDer(const QByteArray &der, const QByteArray &passPhra
if (items.size() != 6 || items[1].type() != QAsn1Element::IntegerType)
return;
keyLength = numberOfBits(items[1].value());
+ } else if (algorithm == QSsl::Dh) {
+ if (versionHex != "00")
+ return;
+ if (items.size() < 5 || items.size() > 6 || items[1].type() != QAsn1Element::IntegerType)
+ return;
+ keyLength = numberOfBits(items[1].value());
} else if (algorithm == QSsl::Ec) {
if (versionHex != "01")
return;
@@ -346,6 +413,12 @@ void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhra
cipher = DesEde3Cbc;
} else if (dekInfo.first() == "RC2-CBC") {
cipher = Rc2Cbc;
+ } else if (dekInfo.first() == "AES-128-CBC") {
+ cipher = Aes128Cbc;
+ } else if (dekInfo.first() == "AES-192-CBC") {
+ cipher = Aes192Cbc;
+ } else if (dekInfo.first() == "AES-256-CBC") {
+ cipher = Aes256Cbc;
} else {
clear(deepClear);
return;
@@ -522,6 +595,10 @@ static EncryptionData readPbes2(const QVector<QAsn1Element> &element, const QByt
return {};
break;
} // @todo(?): case (RC5 , AES)
+ case QSslKeyPrivate::Cipher::Aes128Cbc:
+ case QSslKeyPrivate::Cipher::Aes192Cbc:
+ case QSslKeyPrivate::Cipher::Aes256Cbc:
+ Q_UNREACHABLE();
}
if (Q_LIKELY(keyDerivationAlgorithm == PKCS5_PBKDF2_ENCRYPTION_OID)) {
diff --git a/src/network/ssl/qsslkey_schannel.cpp b/src/network/ssl/qsslkey_schannel.cpp
new file mode 100644
index 0000000000..1e21d123f4
--- /dev/null
+++ b/src/network/ssl/qsslkey_schannel.cpp
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qssl_p.h"
+#include "qsslkey.h"
+#include "qsslkey_p.h"
+#include "qsslcertificate_p.h"
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qscopeguard.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+const wchar_t *getName(QSslKeyPrivate::Cipher cipher)
+{
+ switch (cipher) {
+ case QSslKeyPrivate::Cipher::DesCbc:
+ return BCRYPT_DES_ALGORITHM;
+ case QSslKeyPrivate::Cipher::DesEde3Cbc:
+ return BCRYPT_3DES_ALGORITHM;
+ case QSslKeyPrivate::Cipher::Rc2Cbc:
+ return BCRYPT_RC2_ALGORITHM;
+ case QSslKeyPrivate::Cipher::Aes128Cbc:
+ case QSslKeyPrivate::Cipher::Aes192Cbc:
+ case QSslKeyPrivate::Cipher::Aes256Cbc:
+ return BCRYPT_AES_ALGORITHM;
+ }
+ Q_UNREACHABLE();
+}
+
+BCRYPT_ALG_HANDLE getHandle(QSslKeyPrivate::Cipher cipher)
+{
+ BCRYPT_ALG_HANDLE handle;
+ NTSTATUS status = BCryptOpenAlgorithmProvider(
+ &handle, // phAlgorithm
+ getName(cipher), // pszAlgId
+ nullptr, // pszImplementation
+ 0 // dwFlags
+ );
+ if (status < 0) {
+ qCWarning(lcSsl, "Failed to open algorithm handle (%ld)!", status);
+ return nullptr;
+ }
+
+ return handle;
+}
+
+BCRYPT_KEY_HANDLE generateSymmetricKey(BCRYPT_ALG_HANDLE handle,
+ const QByteArray &key)
+{
+ BCRYPT_KEY_HANDLE keyHandle;
+ NTSTATUS status = BCryptGenerateSymmetricKey(
+ handle, // hAlgorithm
+ &keyHandle, // phKey
+ nullptr, // pbKeyObject (can ignore)
+ 0, // cbKeyObject (also ignoring)
+ reinterpret_cast<unsigned char *>(const_cast<char *>(key.data())), // pbSecret
+ ULONG(key.length()), // cbSecret
+ 0 // dwFlags
+ );
+ if (status < 0) {
+ qCWarning(lcSsl, "Failed to generate symmetric key (%ld)!", status);
+ return nullptr;
+ }
+
+ status = BCryptSetProperty(
+ keyHandle, // hObject
+ BCRYPT_CHAINING_MODE, // pszProperty
+ reinterpret_cast<UCHAR *>(const_cast<wchar_t *>(BCRYPT_CHAIN_MODE_CBC)), // pbInput
+ ARRAYSIZE(BCRYPT_CHAIN_MODE_CBC), // cbInput
+ 0 // dwFlags
+ );
+ if (status < 0) {
+ BCryptDestroyKey(keyHandle);
+ qCWarning(lcSsl, "Failed to change the symmetric key's chaining mode (%ld)!", status);
+ return nullptr;
+ }
+ return keyHandle;
+}
+
+QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, const QByteArray &key,
+ const QByteArray &iv, bool encrypt)
+{
+ BCRYPT_ALG_HANDLE handle = getHandle(cipher);
+ if (!handle)
+ return {};
+ auto handleDealloc = qScopeGuard([&handle]() {
+ BCryptCloseAlgorithmProvider(handle, 0);
+ });
+
+ BCRYPT_KEY_HANDLE keyHandle = generateSymmetricKey(handle, key);
+ if (!keyHandle)
+ return {};
+ auto keyHandleDealloc = qScopeGuard([&keyHandle]() {
+ BCryptDestroyKey(keyHandle);
+ });
+
+ QByteArray ivCopy = iv; // This gets modified, so we take a copy
+
+ ULONG sizeNeeded = 0;
+ QVarLengthArray<unsigned char> output;
+ auto cryptFunction = encrypt ? BCryptEncrypt : BCryptDecrypt;
+ for (int i = 0; i < 2; i++) {
+ output.resize(int(sizeNeeded));
+ auto input = reinterpret_cast<unsigned char *>(const_cast<char *>(data.data()));
+ // Need to call it twice because the first iteration lets us know the size needed.
+ NTSTATUS status = cryptFunction(
+ keyHandle, // hKey
+ input, // pbInput
+ ULONG(data.length()), // cbInput
+ nullptr, // pPaddingInfo
+ reinterpret_cast<unsigned char *>(ivCopy.data()), // pbIV
+ ULONG(ivCopy.length()), // cbIV
+ sizeNeeded ? output.data() : nullptr, // pbOutput
+ ULONG(output.length()), // cbOutput
+ &sizeNeeded, // pcbResult
+ BCRYPT_BLOCK_PADDING // dwFlags
+ );
+ if (status < 0) {
+ qCWarning(lcSsl, "%s failed (%ld)!", encrypt ? "Encrypt" : "Decrypt", status);
+ return {};
+ }
+ }
+
+ return QByteArray(reinterpret_cast<const char *>(output.constData()), int(sizeNeeded));
+}
+} // anonymous namespace
+
+QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key,
+ const QByteArray &iv)
+{
+ return doCrypt(cipher, data, key, iv, false);
+}
+
+QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key,
+ const QByteArray &iv)
+{
+ return doCrypt(cipher, data, key, iv, true);
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslkey_winrt.cpp b/src/network/ssl/qsslkey_winrt.cpp
index f2ed813965..69eaaa387f 100644
--- a/src/network/ssl/qsslkey_winrt.cpp
+++ b/src/network/ssl/qsslkey_winrt.cpp
@@ -83,6 +83,15 @@ struct SslKeyGlobal
hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"RC2_CBC").Get(),
&keyProviders[QSslKeyPrivate::Rc2Cbc]);
Q_ASSERT_SUCCEEDED(hr);
+ hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"AES_CBC").Get(),
+ &keyProviders[QSslKeyPrivate::Aes128Cbc]);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"AES_CBC").Get(),
+ &keyProviders[QSslKeyPrivate::Aes192Cbc]);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"AES_CBC").Get(),
+ &keyProviders[QSslKeyPrivate::Aes256Cbc]);
+ Q_ASSERT_SUCCEEDED(hr);
hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_CryptographicBuffer).Get(),
&bufferFactory);
diff --git a/src/network/ssl/qsslpresharedkeyauthenticator.cpp b/src/network/ssl/qsslpresharedkeyauthenticator.cpp
index 8e1313493f..01e1501763 100644
--- a/src/network/ssl/qsslpresharedkeyauthenticator.cpp
+++ b/src/network/ssl/qsslpresharedkeyauthenticator.cpp
@@ -83,32 +83,19 @@ QSslPreSharedKeyAuthenticatorPrivate::QSslPreSharedKeyAuthenticatorPrivate()
completing the PSK handshake. The client application needs to connect a
slot to the QSslSocket::preSharedKeyAuthenticationRequired() signal:
- \code
-
- connect(socket, &QSslSocket::preSharedKeyAuthenticationRequired,
- this, &AuthManager::handlePreSharedKeyAuthentication);
-
- \endcode
+ \snippet code/src_network_ssl_qsslpresharedkeyauthenticator.cpp 0
The signal carries a QSslPreSharedKeyAuthenticator object containing the
identity hint the server sent to the client, and which must be filled with the
corresponding client identity and the derived key:
- \code
-
- void AuthManager::handlePreSharedKeyAuthentication(QSslPreSharedKeyAuthenticator *authenticator)
- {
- authenticator->setIdentity("My Qt App");
-
- const QByteArray key = deriveKey(authenticator->identityHint(), passphrase);
- authenticator->setPreSharedKey(key);
- }
-
- \endcode
+ \snippet code/src_network_ssl_qsslpresharedkeyauthenticator.cpp 1
\note PSK ciphersuites are supported only when using OpenSSL 1.0.1 (or
greater) as the SSL backend.
+ \note PSK is currently only supported in OpenSSL.
+
\sa QSslSocket
*/
diff --git a/src/network/ssl/qsslpresharedkeyauthenticator.h b/src/network/ssl/qsslpresharedkeyauthenticator.h
index 423f7731b4..29d647b121 100644
--- a/src/network/ssl/qsslpresharedkeyauthenticator.h
+++ b/src/network/ssl/qsslpresharedkeyauthenticator.h
@@ -60,10 +60,10 @@ public:
Q_NETWORK_EXPORT QSslPreSharedKeyAuthenticator &operator=(const QSslPreSharedKeyAuthenticator &authenticator);
#ifdef Q_COMPILER_RVALUE_REFS
- QSslPreSharedKeyAuthenticator &operator=(QSslPreSharedKeyAuthenticator &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+ QSslPreSharedKeyAuthenticator &operator=(QSslPreSharedKeyAuthenticator &&other) noexcept { swap(other); return *this; }
#endif
- void swap(QSslPreSharedKeyAuthenticator &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QSslPreSharedKeyAuthenticator &other) noexcept { qSwap(d, other.d); }
Q_NETWORK_EXPORT QByteArray identityHint() const;
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index 5722f38f45..4e6caf3edd 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -133,7 +133,8 @@
\list
\li The socket's cryptographic cipher suite can be customized before
- the handshake phase with setCiphers() and setDefaultCiphers().
+ the handshake phase with QSslConfiguration::setCiphers()
+ and QSslConfiguration::setDefaultCiphers().
\li The socket's local certificate and private key can be customized
before the handshake phase with setLocalCertificate() and
setPrivateKey().
@@ -202,6 +203,7 @@
does not require this certificate to be valid. This is useful when you
want to display peer certificate details to the user without affecting the
actual SSL handshake. This mode is the default for servers.
+ Note: In Schannel this value acts the same as VerifyNone.
\value VerifyPeer QSslSocket will request a certificate from the peer
during the SSL handshake phase, and requires that this certificate is
@@ -312,6 +314,7 @@
#include "qssl_p.h"
#include "qsslsocket.h"
#include "qsslcipher.h"
+#include "qocspresponse.h"
#ifndef QT_NO_OPENSSL
#include "qsslsocket_openssl_p.h"
#endif
@@ -321,6 +324,9 @@
#ifdef QT_SECURETRANSPORT
#include "qsslsocket_mac_p.h"
#endif
+#if QT_CONFIG(schannel)
+#include "qsslsocket_schannel_p.h"
+#endif
#include "qsslconfiguration_p.h"
#include <QtCore/qdebug.h>
@@ -396,6 +402,9 @@ QSslSocket::~QSslSocket()
*/
void QSslSocket::resume()
{
+ Q_D(QSslSocket);
+ if (!d->paused)
+ return;
// continuing might emit signals, rather do this through the event loop
QMetaObject::invokeMethod(this, "_q_resumeImplementation", Qt::QueuedConnection);
}
@@ -456,6 +465,9 @@ void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port, O
return;
}
+ if (!d->verifyProtocolSupported("QSslSocket::connectToHostEncrypted:"))
+ return;
+
d->init();
d->autoStartHandshake = true;
d->initialized = true;
@@ -903,7 +915,8 @@ void QSslSocket::abort()
time without notice.
\sa localCertificate(), peerCertificate(), peerCertificateChain(),
- sessionCipher(), privateKey(), ciphers(), caCertificates()
+ sessionCipher(), privateKey(), QSslConfiguration::ciphers(),
+ QSslConfiguration::caCertificates()
*/
QSslConfiguration QSslSocket::sslConfiguration() const
{
@@ -927,7 +940,8 @@ QSslConfiguration QSslSocket::sslConfiguration() const
It is not possible to set the SSL-state related fields.
- \sa setLocalCertificate(), setPrivateKey(), setCaCertificates(), setCiphers()
+ \sa setLocalCertificate(), setPrivateKey(), QSslConfiguration::setCaCertificates(),
+ QSslConfiguration::setCiphers()
*/
void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration)
{
@@ -949,6 +963,9 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration)
d->configuration.nextAllowedProtocols = configuration.allowedNextProtocols();
d->configuration.nextNegotiatedProtocol = configuration.nextNegotiatedProtocol();
d->configuration.nextProtocolNegotiationStatus = configuration.nextProtocolNegotiationStatus();
+#if QT_CONFIG(ocsp)
+ d->configuration.ocspStaplingEnabled = configuration.ocspStaplingEnabled();
+#endif
// if the CA certificates were set explicitly (either via
// QSslConfiguration::setCaCertificates() or QSslSocket::setCaCertificates(),
@@ -1110,8 +1127,10 @@ QList<QSslCertificate> QSslSocket::peerCertificateChain() const
session cipher. This ordered list must be in place before the
handshake phase begins.
- \sa ciphers(), setCiphers(), setDefaultCiphers(), defaultCiphers(),
- supportedCiphers()
+ \sa QSslConfiguration::ciphers(), QSslConfiguration::setCiphers(),
+ QSslConfiguration::setCiphers(),
+ QSslConfiguration::ciphers(),
+ QSslConfiguration::supportedCiphers()
*/
QSslCipher QSslSocket::sessionCipher() const
{
@@ -1133,6 +1152,20 @@ QSsl::SslProtocol QSslSocket::sessionProtocol() const
return d->sessionProtocol();
}
+/*!
+ \since 5.13
+
+ This function returns Online Certificate Status Protocol responses that
+ a server may send during a TLS handshake using OCSP stapling. The vector
+ is empty if no definitive response or no response at all was received.
+
+ \sa QSslConfiguration::setOcspStaplingEnabled()
+*/
+QVector<QOcspResponse> QSslSocket::ocspResponses() const
+{
+ Q_D(const QSslSocket);
+ return d->ocspResponses;
+}
/*!
Sets the socket's private \l {QSslKey} {key} to \a key. The
@@ -1176,12 +1209,21 @@ void QSslSocket::setPrivateKey(const QSslKey &key)
void QSslSocket::setPrivateKey(const QString &fileName, QSsl::KeyAlgorithm algorithm,
QSsl::EncodingFormat format, const QByteArray &passPhrase)
{
- Q_D(QSslSocket);
QFile file(fileName);
- if (file.open(QIODevice::ReadOnly)) {
- d->configuration.privateKey = QSslKey(file.readAll(), algorithm,
- format, QSsl::PrivateKey, passPhrase);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qCWarning(lcSsl, "QSslSocket::setPrivateKey: Couldn't open file for reading");
+ return;
+ }
+
+ QSslKey key(file.readAll(), algorithm, format, QSsl::PrivateKey, passPhrase);
+ if (key.isNull()) {
+ qCWarning(lcSsl, "QSslSocket::setPrivateKey: "
+ "The specified file does not contain a valid key");
+ return;
}
+
+ Q_D(QSslSocket);
+ d->configuration.privateKey = key;
}
/*!
@@ -1195,6 +1237,7 @@ QSslKey QSslSocket::privateKey() const
return d->configuration.privateKey;
}
+#if QT_DEPRECATED_SINCE(5, 5)
/*!
\deprecated
@@ -1338,6 +1381,7 @@ QList<QSslCipher> QSslSocket::supportedCiphers()
{
return QSslSocketPrivate::supportedCiphers();
}
+#endif // #if QT_DEPRECATED_SINCE(5, 5)
/*!
Searches all files in the \a path for certificates encoded in the
@@ -1373,7 +1417,8 @@ bool QSslSocket::addCaCertificates(const QString &path, QSsl::EncodingFormat for
To add multiple certificates, use addCaCertificates().
- \sa caCertificates(), setCaCertificates()
+ \sa QSslConfiguration::caCertificates(),
+ QSslConfiguration::setCaCertificates()
*/
void QSslSocket::addCaCertificate(const QSslCertificate &certificate)
{
@@ -1388,7 +1433,7 @@ void QSslSocket::addCaCertificate(const QSslCertificate &certificate)
For more precise control, use addCaCertificate().
- \sa caCertificates(), addDefaultCaCertificate()
+ \sa QSslConfiguration::caCertificates(), addDefaultCaCertificate()
*/
void QSslSocket::addCaCertificates(const QList<QSslCertificate> &certificates)
{
@@ -1396,6 +1441,7 @@ void QSslSocket::addCaCertificates(const QList<QSslCertificate> &certificates)
d->configuration.caCertificates += certificates;
}
+#if QT_DEPRECATED_SINCE(5, 5)
/*!
\deprecated
@@ -1440,6 +1486,7 @@ QList<QSslCertificate> QSslSocket::caCertificates() const
Q_D(const QSslSocket);
return d->configuration.caCertificates;
}
+#endif // #if QT_DEPRECATED_SINCE(5, 5)
/*!
Searches all files in the \a path for certificates with the
@@ -1451,7 +1498,8 @@ QList<QSslCertificate> QSslSocket::caCertificates() const
Each SSL socket's CA certificate database is initialized to the
default CA certificate database.
- \sa defaultCaCertificates(), addCaCertificates(), addDefaultCaCertificate()
+ \sa QSslConfiguration::caCertificates(), addCaCertificates(),
+ addDefaultCaCertificate()
*/
bool QSslSocket::addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat encoding,
QRegExp::PatternSyntax syntax)
@@ -1464,7 +1512,7 @@ bool QSslSocket::addDefaultCaCertificates(const QString &path, QSsl::EncodingFor
SSL socket's CA certificate database is initialized to the default
CA certificate database.
- \sa defaultCaCertificates(), addCaCertificates()
+ \sa QSslConfiguration::defaultCaCertificates(), addCaCertificates()
*/
void QSslSocket::addDefaultCaCertificate(const QSslCertificate &certificate)
{
@@ -1476,13 +1524,14 @@ void QSslSocket::addDefaultCaCertificate(const QSslCertificate &certificate)
SSL socket's CA certificate database is initialized to the default
CA certificate database.
- \sa defaultCaCertificates(), addCaCertificates()
+ \sa QSslConfiguration::caCertificates(), addCaCertificates()
*/
void QSslSocket::addDefaultCaCertificates(const QList<QSslCertificate> &certificates)
{
QSslSocketPrivate::addDefaultCaCertificates(certificates);
}
+#if QT_DEPRECATED_SINCE(5, 5)
/*!
\deprecated
@@ -1550,6 +1599,7 @@ QList<QSslCertificate> QSslSocket::systemCaCertificates()
// we are calling ensureInitialized() in the method below
return QSslSocketPrivate::systemCaCertificates();
}
+#endif // #if QT_DEPRECATED_SINCE(5, 5)
/*!
Waits until the socket is connected, or \a msecs milliseconds,
@@ -1594,6 +1644,8 @@ bool QSslSocket::waitForEncrypted(int msecs)
return false;
if (d->mode == UnencryptedMode && !d->autoStartHandshake)
return false;
+ if (!d->verifyProtocolSupported("QSslSocket::waitForEncrypted:"))
+ return false;
QElapsedTimer stopWatch;
stopWatch.start();
@@ -1843,6 +1895,10 @@ void QSslSocket::startClientEncryption()
d->setErrorAndEmit(QAbstractSocket::SslInternalError, tr("TLS initialization failed"));
return;
}
+
+ if (!d->verifyProtocolSupported("QSslSocket::startClientEncryption:"))
+ return;
+
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << "QSslSocket::startClientEncryption()";
#endif
@@ -1886,6 +1942,9 @@ void QSslSocket::startServerEncryption()
d->setErrorAndEmit(QAbstractSocket::SslInternalError, tr("TLS initialization failed"));
return;
}
+ if (!d->verifyProtocolSupported("QSslSocket::startServerEncryption"))
+ return;
+
d->mode = SslServerMode;
emit modeChanged(d->mode);
d->startServerEncryption();
@@ -1971,6 +2030,7 @@ void QSslSocket::connectToHost(const QString &hostName, quint16 port, OpenMode o
d->createPlainSocket(openMode);
}
#ifndef QT_NO_NETWORKPROXY
+ d->plainSocket->setProtocolTag(d->protocolTag);
d->plainSocket->setProxy(proxy());
#endif
QIODevice::open(openMode);
@@ -2105,6 +2165,7 @@ void QSslSocketPrivate::init()
shutdown = false;
pendingClose = false;
flushTriggered = false;
+ ocspResponses.clear();
// we don't want to clear the ignoreErrorsList, so
// that it is possible setting it before connecting
@@ -2119,6 +2180,20 @@ void QSslSocketPrivate::init()
/*!
\internal
*/
+bool QSslSocketPrivate::verifyProtocolSupported(const char *where)
+{
+ if (configuration.protocol == QSsl::SslV2 || configuration.protocol == QSsl::SslV3) {
+ qCWarning(lcSsl) << where << "Attempted to use an unsupported protocol.";
+ setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
+ QSslSocket::tr("Attempted to use an unsupported protocol."));
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \internal
+*/
QList<QSslCipher> QSslSocketPrivate::defaultCiphers()
{
QSslSocketPrivate::ensureInitialized();
@@ -2321,6 +2396,9 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri
#if QT_CONFIG(dtls)
ptr->dtlsCookieEnabled = global->dtlsCookieEnabled;
#endif
+#if QT_CONFIG(ocsp)
+ ptr->ocspStaplingEnabled = global->ocspStaplingEnabled;
+#endif
}
/*!
@@ -2391,8 +2469,8 @@ void QSslSocketPrivate::createPlainSocket(QIODevice::OpenMode openMode)
q->connect(plainSocket, SIGNAL(bytesWritten(qint64)),
q, SLOT(_q_bytesWrittenSlot(qint64)),
Qt::DirectConnection);
- q->connect(plainSocket, SIGNAL(channelBytesWritten(int, qint64)),
- q, SLOT(_q_channelBytesWrittenSlot(int, qint64)),
+ q->connect(plainSocket, SIGNAL(channelBytesWritten(int,qint64)),
+ q, SLOT(_q_channelBytesWrittenSlot(int,qint64)),
Qt::DirectConnection);
q->connect(plainSocket, SIGNAL(readChannelFinished()),
q, SLOT(_q_readChannelFinishedSlot()),
@@ -2660,6 +2738,7 @@ void QSslSocketPrivate::_q_resumeImplementation()
if (verifyErrorsHaveBeenIgnored()) {
continueHandshake();
} else {
+ Q_ASSERT(!sslErrors.isEmpty());
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, sslErrors.constFirst().errorString());
plainSocket->disconnectFromHost();
return;
@@ -2818,6 +2897,17 @@ QSharedPointer<QSslContext> QSslSocketPrivate::sslContext(QSslSocket *socket)
bool QSslSocketPrivate::isMatchingHostname(const QSslCertificate &cert, const QString &peerName)
{
+ QHostAddress hostAddress(peerName);
+ if (!hostAddress.isNull()) {
+ const auto subjectAlternativeNames = cert.subjectAlternativeNames();
+ const auto ipAddresses = subjectAlternativeNames.equal_range(QSsl::AlternativeNameEntryType::IpAddressEntry);
+
+ for (auto it = ipAddresses.first; it != ipAddresses.second; it++) {
+ if (QHostAddress(*it).isEqual(hostAddress, QHostAddress::StrictConversion))
+ return true;
+ }
+ }
+
const QString lowerPeerName = QString::fromLatin1(QUrl::toAce(peerName));
const QStringList commonNames = cert.subjectInfo(QSslCertificate::CommonName);
diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h
index c66ebdde54..35943c7d7e 100644
--- a/src/network/ssl/qsslsocket.h
+++ b/src/network/ssl/qsslsocket.h
@@ -44,6 +44,7 @@
#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qlist.h>
#include <QtCore/qregexp.h>
+#include <QtCore/qvector.h>
#ifndef QT_NO_SSL
# include <QtNetwork/qtcpsocket.h>
# include <QtNetwork/qsslerror.h>
@@ -60,6 +61,7 @@ class QSslCertificate;
class QSslConfiguration;
class QSslEllipticCurve;
class QSslPreSharedKeyAuthenticator;
+class QOcspResponse;
class QSslSocketPrivate;
class Q_NETWORK_EXPORT QSslSocket : public QTcpSocket
@@ -142,6 +144,7 @@ public:
QList<QSslCertificate> peerCertificateChain() const;
QSslCipher sessionCipher() const;
QSsl::SslProtocol sessionProtocol() const;
+ QVector<QOcspResponse> ocspResponses() const;
// Private keys, for server sockets.
void setPrivateKey(const QSslKey &key);
@@ -228,7 +231,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_flushWriteBuffer())
Q_PRIVATE_SLOT(d_func(), void _q_flushReadBuffer())
Q_PRIVATE_SLOT(d_func(), void _q_resumeImplementation())
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) && !QT_CONFIG(schannel)
Q_PRIVATE_SLOT(d_func(), void _q_caRootLoaded(QSslCertificate,QSslCertificate))
#endif
friend class QSslSocketBackendPrivate;
diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp
index aa4fb4fe8e..487e975db6 100644
--- a/src/network/ssl/qsslsocket_mac.cpp
+++ b/src/network/ssl/qsslsocket_mac.cpp
@@ -89,7 +89,7 @@ struct EphemeralSecKeychain
~EphemeralSecKeychain();
SecKeychainRef keychain = nullptr;
- Q_DISABLE_COPY(EphemeralSecKeychain)
+ Q_DISABLE_COPY_MOVE(EphemeralSecKeychain)
};
EphemeralSecKeychain::EphemeralSecKeychain()
@@ -506,6 +506,8 @@ QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const
return QSsl::TlsV1_1;
case kTLSProtocol12:
return QSsl::TlsV1_2;
+ case kTLSProtocol13:
+ return QSsl::TlsV1_3;
default:
return QSsl::UnknownProtocol;
}
@@ -1107,16 +1109,27 @@ bool QSslSocketBackendPrivate::setSessionProtocol()
return false;
}
+ // SslV3 is unsupported.
+ if (configuration.protocol == QSsl::SslV3) {
+ qCDebug(lcSsl) << "protocol QSsl::SslV3 is disabled";
+ return false;
+ }
+
+ // SecureTransport has kTLSProtocol13 constant and also, kTLSProtocolMaxSupported.
+ // Calling SSLSetProtocolVersionMax/Min with any of these two constants results
+ // in errInvalidParam and a failure to set the protocol version. This means
+ // no TLS 1.3 on macOS and iOS.
+ switch (configuration.protocol) {
+ case QSsl::TlsV1_3:
+ case QSsl::TlsV1_3OrLater:
+ qCWarning(lcSsl) << plainSocket << "SecureTransport does not support TLS 1.3";
+ return false;
+ default:;
+ }
+
OSStatus err = errSecSuccess;
- if (configuration.protocol == QSsl::SslV3) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : SSLv3";
- #endif
- err = SSLSetProtocolVersionMin(context, kSSLProtocol3);
- if (err == errSecSuccess)
- err = SSLSetProtocolVersionMax(context, kSSLProtocol3);
- } else if (configuration.protocol == QSsl::TlsV1_0) {
+ if (configuration.protocol == QSsl::TlsV1_0) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.0";
#endif
@@ -1141,45 +1154,34 @@ bool QSslSocketBackendPrivate::setSessionProtocol()
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : any";
#endif
- // kSSLProtocol3, since kSSLProtocol2 is disabled:
- err = SSLSetProtocolVersionMin(context, kSSLProtocol3);
- if (err == errSecSuccess)
- err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
+ err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
} else if (configuration.protocol == QSsl::TlsV1SslV3) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : SSLv3 - TLSv1.2";
#endif
- err = SSLSetProtocolVersionMin(context, kSSLProtocol3);
+ err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
if (err == errSecSuccess)
- err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
+ err = SSLSetProtocolVersionMax(context, kTLSProtocol1);
} else if (configuration.protocol == QSsl::SecureProtocols) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1 - TLSv1.2";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
- if (err == errSecSuccess)
- err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
} else if (configuration.protocol == QSsl::TlsV1_0OrLater) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1 - TLSv1.2";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
- if (err == errSecSuccess)
- err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
} else if (configuration.protocol == QSsl::TlsV1_1OrLater) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.1 - TLSv1.2";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol11);
- if (err == errSecSuccess)
- err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
} else if (configuration.protocol == QSsl::TlsV1_2OrLater) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.2";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol12);
- if (err == errSecSuccess)
- err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
} else {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "no protocol version found in the configuration";
@@ -1205,7 +1207,7 @@ bool QSslSocketBackendPrivate::verifySessionProtocol() const
if (configuration.protocol == QSsl::AnyProtocol)
protocolOk = true;
else if (configuration.protocol == QSsl::TlsV1SslV3)
- protocolOk = (sessionProtocol() >= QSsl::SslV3);
+ protocolOk = (sessionProtocol() == QSsl::TlsV1_0);
else if (configuration.protocol == QSsl::SecureProtocols)
protocolOk = (sessionProtocol() >= QSsl::TlsV1_0);
else if (configuration.protocol == QSsl::TlsV1_0OrLater)
@@ -1214,6 +1216,8 @@ bool QSslSocketBackendPrivate::verifySessionProtocol() const
protocolOk = (sessionProtocol() >= QSsl::TlsV1_1);
else if (configuration.protocol == QSsl::TlsV1_2OrLater)
protocolOk = (sessionProtocol() >= QSsl::TlsV1_2);
+ else if (configuration.protocol == QSsl::TlsV1_3OrLater)
+ protocolOk = (sessionProtocol() >= QSsl::TlsV1_3OrLater);
else
protocolOk = (sessionProtocol() == configuration.protocol);
diff --git a/src/network/ssl/qsslsocket_mac_p.h b/src/network/ssl/qsslsocket_mac_p.h
index e37171e56a..48aca964a1 100644
--- a/src/network/ssl/qsslsocket_mac_p.h
+++ b/src/network/ssl/qsslsocket_mac_p.h
@@ -75,7 +75,7 @@ public:
private:
SSLContextRef context;
- Q_DISABLE_COPY(QSecureTransportContext)
+ Q_DISABLE_COPY_MOVE(QSecureTransportContext)
};
class QSslSocketBackendPrivate : public QSslSocketPrivate
@@ -129,7 +129,7 @@ private:
QSecureTransportContext context;
bool renegotiating = false;
- Q_DISABLE_COPY(QSslSocketBackendPrivate)
+ Q_DISABLE_COPY_MOVE(QSslSocketBackendPrivate)
};
QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 64501a75e8..6a8269b521 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -65,6 +65,7 @@
#include "qsslellipticcurve.h"
#include "qsslpresharedkeyauthenticator.h"
#include "qsslpresharedkeyauthenticator_p.h"
+#include "qocspresponse_p.h"
#ifdef Q_OS_WIN
#include "qwindowscarootfetcher_p.h"
@@ -83,16 +84,16 @@
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qscopedvaluerollback.h>
+#if QT_CONFIG(ocsp)
+#include "qocsp_p.h"
+#endif
+
+#include <algorithm>
+
#include <string.h>
QT_BEGIN_NAMESPACE
-#if defined(Q_OS_WIN)
- PtrCertOpenSystemStoreW QSslSocketPrivate::ptrCertOpenSystemStoreW = nullptr;
- PtrCertFindCertificateInStore QSslSocketPrivate::ptrCertFindCertificateInStore = nullptr;
- PtrCertCloseStore QSslSocketPrivate::ptrCertCloseStore = nullptr;
-#endif
-
bool QSslSocketPrivate::s_libraryLoaded = false;
bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
bool QSslSocketPrivate::s_loadRootCertsOnDemand = false;
@@ -137,6 +138,37 @@ static unsigned int q_ssl_psk_server_callback(SSL *ssl,
return d->tlsPskServerCallback(identity, psk, max_psk_len);
}
#endif
+
+#if QT_CONFIG(ocsp)
+
+int qt_OCSP_status_server_callback(SSL *ssl, void *ocspRequest)
+{
+ Q_UNUSED(ocspRequest)
+ if (!ssl)
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+
+ auto d = static_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData));
+ if (!d)
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+
+ Q_ASSERT(d->mode == QSslSocket::SslServerMode);
+ const QByteArray &response = d->ocspResponseDer;
+ Q_ASSERT(response.size());
+
+ unsigned char *derCopy = static_cast<unsigned char *>(q_OPENSSL_malloc(size_t(response.size())));
+ if (!derCopy)
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+
+ std::copy(response.data(), response.data() + response.size(), derCopy);
+ // We don't check the return value: internally OpenSSL simply assignes the
+ // pointer (it assumes it now owns this memory btw!) and the length.
+ q_SSL_set_tlsext_status_ocsp_resp(ssl, derCopy, response.size());
+
+ return SSL_TLSEXT_ERR_OK;
+}
+
+#endif // ocsp
+
} // extern "C"
QSslSocketBackendPrivate::QSslSocketBackendPrivate()
@@ -180,6 +212,8 @@ QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(const SSL_CIPHER
ciph.d->protocol = QSsl::TlsV1_1;
else if (protoString == QLatin1String("TLSv1.2"))
ciph.d->protocol = QSsl::TlsV1_2;
+ else if (protoString == QLatin1String("TLSv1.3"))
+ ciph.d->protocol = QSsl::TlsV1_3;
if (descriptionList.at(2).startsWith(QLatin1String("Kx=")))
ciph.d->keyExchangeMethod = descriptionList.at(2).mid(3).toString();
@@ -202,8 +236,120 @@ QSslErrorEntry QSslErrorEntry::fromStoreContext(X509_STORE_CTX *ctx)
};
}
+#if QT_CONFIG(ocsp)
+
+QSslError qt_OCSP_response_status_to_QSslError(long code)
+{
+ switch (code) {
+ case OCSP_RESPONSE_STATUS_MALFORMEDREQUEST:
+ return QSslError::OcspMalformedRequest;
+ case OCSP_RESPONSE_STATUS_INTERNALERROR:
+ return QSslError::OcspInternalError;
+ case OCSP_RESPONSE_STATUS_TRYLATER:
+ return QSslError::OcspTryLater;
+ case OCSP_RESPONSE_STATUS_SIGREQUIRED:
+ return QSslError::OcspSigRequred;
+ case OCSP_RESPONSE_STATUS_UNAUTHORIZED:
+ return QSslError::OcspUnauthorized;
+ case OCSP_RESPONSE_STATUS_SUCCESSFUL:
+ default:
+ return {};
+ }
+ Q_UNREACHABLE();
+}
+
+QOcspRevocationReason qt_OCSP_revocation_reason(int reason)
+{
+ switch (reason) {
+ case OCSP_REVOKED_STATUS_NOSTATUS:
+ return QOcspRevocationReason::None;
+ case OCSP_REVOKED_STATUS_UNSPECIFIED:
+ return QOcspRevocationReason::Unspecified;
+ case OCSP_REVOKED_STATUS_KEYCOMPROMISE:
+ return QOcspRevocationReason::KeyCompromise;
+ case OCSP_REVOKED_STATUS_CACOMPROMISE:
+ return QOcspRevocationReason::CACompromise;
+ case OCSP_REVOKED_STATUS_AFFILIATIONCHANGED:
+ return QOcspRevocationReason::AffiliationChanged;
+ case OCSP_REVOKED_STATUS_SUPERSEDED:
+ return QOcspRevocationReason::Superseded;
+ case OCSP_REVOKED_STATUS_CESSATIONOFOPERATION:
+ return QOcspRevocationReason::CessationOfOperation;
+ case OCSP_REVOKED_STATUS_CERTIFICATEHOLD:
+ return QOcspRevocationReason::CertificateHold;
+ case OCSP_REVOKED_STATUS_REMOVEFROMCRL:
+ return QOcspRevocationReason::RemoveFromCRL;
+ default:
+ return QOcspRevocationReason::None;
+ }
+
+ Q_UNREACHABLE();
+}
+
+bool qt_OCSP_certificate_match(OCSP_SINGLERESP *singleResponse, X509 *peerCert, X509 *issuer)
+{
+ // OCSP_basic_verify does verify that the responder is legit, the response is
+ // correctly signed, CertID is correct. But it does not know which certificate
+ // we were presented with by our peer, so it does not check if it's a response
+ // for our peer's certificate.
+ Q_ASSERT(singleResponse && peerCert && issuer);
+
+ const OCSP_CERTID *certId = q_OCSP_SINGLERESP_get0_id(singleResponse); // Does not increment refcount.
+ if (!certId) {
+ qCWarning(lcSsl, "A SingleResponse without CertID");
+ return false;
+ }
+
+ ASN1_OBJECT *md = nullptr;
+ ASN1_INTEGER *reportedSerialNumber = nullptr;
+ const int result = q_OCSP_id_get0_info(nullptr, &md, nullptr, &reportedSerialNumber, const_cast<OCSP_CERTID *>(certId));
+ if (result != 1 || !md || !reportedSerialNumber) {
+ qCWarning(lcSsl, "Failed to extract a hash and serial number from CertID structure");
+ return false;
+ }
+
+ if (!q_X509_get_serialNumber(peerCert)) {
+ // Is this possible at all? But we have to check this,
+ // ASN1_INTEGER_cmp (called from OCSP_id_cmp) dereferences
+ // without any checks at all.
+ qCWarning(lcSsl, "No serial number in peer's ceritificate");
+ return false;
+ }
+
+ const int nid = q_OBJ_obj2nid(md);
+ if (nid == NID_undef) {
+ qCWarning(lcSsl, "Unknown hash algorithm in CertID");
+ return false;
+ }
+
+ const EVP_MD *digest = q_EVP_get_digestbynid(nid); // Does not increment refcount.
+ if (!digest) {
+ qCWarning(lcSsl) << "No digest for nid" << nid;
+ return false;
+ }
+
+ OCSP_CERTID *recreatedId = q_OCSP_cert_to_id(digest, peerCert, issuer);
+ if (!recreatedId) {
+ qCWarning(lcSsl, "Failed to re-create CertID");
+ return false;
+ }
+ const QSharedPointer<OCSP_CERTID> guard(recreatedId, q_OCSP_CERTID_free);
+
+ if (q_OCSP_id_cmp(const_cast<OCSP_CERTID *>(certId), recreatedId)) {
+ qDebug(lcSsl, "Certificate ID mismatch");
+ return false;
+ }
+ // Bingo!
+ return true;
+}
+
+#endif // ocsp
+
// ### This list is shared between all threads, and protected by a
-// mutex. Investigate using thread local storage instead.
+// mutex. Investigate using thread local storage instead. Or better properly
+// use OpenSSL's ability to attach application data to an SSL/SSL_CTX
+// and extract it in a callback. See how it's done, for example, in PSK
+// callback or in DTLS verification callback.
struct QSslErrorList
{
QMutex mutex;
@@ -278,7 +424,7 @@ long QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SslProtocol protocol, Q
{
long options;
if (protocol == QSsl::TlsV1SslV3)
- options = SSL_OP_ALL|SSL_OP_NO_SSLv2;
+ options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3;
else if (protocol == QSsl::SecureProtocols)
options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3;
else if (protocol == QSsl::TlsV1_0OrLater)
@@ -291,6 +437,8 @@ long QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SslProtocol protocol, Q
options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1;
else if (protocol == QSsl::TlsV1_2OrLater)
options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1;
+ else if (protocol == QSsl::TlsV1_3OrLater)
+ options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2;
#endif
else
options = SSL_OP_ALL;
@@ -328,7 +476,8 @@ bool QSslSocketBackendPrivate::initSslContext()
{
Q_Q(QSslSocket);
- // If no external context was set (e.g. bei QHttpNetworkConnection) we will create a default context
+ // If no external context was set (e.g. by QHttpNetworkConnection) we will
+ // create a default context
if (!sslContextPointer) {
// create a deep copy of our configuration
QSslConfigurationPrivate *configurationCopy = new QSslConfigurationPrivate(configuration);
@@ -353,7 +502,7 @@ bool QSslSocketBackendPrivate::initSslContext()
if (configuration.protocol != QSsl::SslV2 &&
configuration.protocol != QSsl::SslV3 &&
configuration.protocol != QSsl::UnknownProtocol &&
- mode == QSslSocket::SslClientMode && QSslSocket::sslLibraryVersionNumber() >= 0x00090806fL) {
+ mode == QSslSocket::SslClientMode) {
// Set server hostname on TLS extension. RFC4366 section 3.1 requires it in ACE format.
QString tlsHostName = verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName;
if (tlsHostName.isEmpty())
@@ -408,12 +557,50 @@ bool QSslSocketBackendPrivate::initSslContext()
}
#endif
+#if QT_CONFIG(ocsp)
+ if (configuration.ocspStaplingEnabled) {
+ if (mode == QSslSocket::SslServerMode) {
+ setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
+ QSslSocket::tr("Server-side QSslSocket does not support OCSP stapling"));
+ return false;
+ }
+ if (q_SSL_set_tlsext_status_type(ssl, TLSEXT_STATUSTYPE_ocsp) != 1) {
+ setErrorAndEmit(QAbstractSocket::SslInternalError,
+ QSslSocket::tr("Failed to enable OCSP stapling"));
+ return false;
+ }
+ }
+
+ ocspResponseDer.clear();
+ auto responsePos = configuration.backendConfig.find("Qt-OCSP-response");
+ if (responsePos != configuration.backendConfig.end()) {
+ // This is our private, undocumented 'API' we use for the auto-testing of
+ // OCSP-stapling. It must be a der-encoded OCSP response, presumably set
+ // by tst_QOcsp.
+ const QVariant data(responsePos.value());
+ if (data.canConvert<QByteArray>())
+ ocspResponseDer = data.toByteArray();
+ }
+
+ if (ocspResponseDer.size()) {
+ if (mode != QSslSocket::SslServerMode) {
+ setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
+ QSslSocket::tr("Client-side sockets do not send OCSP responses"));
+ return false;
+ }
+ }
+#endif // ocsp
+
return true;
}
void QSslSocketBackendPrivate::destroySslContext()
{
if (ssl) {
+ // We do not send a shutdown alert here. Just mark the session as
+ // resumable for qhttpnetworkconnection's "optimization", otherwise
+ // OpenSSL won't start a session resumption.
+ q_SSL_shutdown(ssl);
q_SSL_free(ssl);
ssl = nullptr;
}
@@ -474,7 +661,12 @@ void QSslSocketPrivate::resetDefaultCiphers()
#else
SSL_CTX *myCtx = q_SSL_CTX_new(q_SSLv23_client_method());
#endif
+ // Note, we assert, not just silently return/bail out early:
+ // this should never happen and problems with OpenSSL's initialization
+ // must be caught before this (see supportsSsl()).
+ Q_ASSERT(myCtx);
SSL *mySsl = q_SSL_new(myCtx);
+ Q_ASSERT(mySsl);
QList<QSslCipher> ciphers;
QList<QSslCipher> defaultCiphers;
@@ -539,22 +731,20 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
#endif
QList<QSslCertificate> systemCerts;
#if defined(Q_OS_WIN)
- if (ptrCertOpenSystemStoreW && ptrCertFindCertificateInStore && ptrCertCloseStore) {
- HCERTSTORE hSystemStore;
- hSystemStore = ptrCertOpenSystemStoreW(0, L"ROOT");
- if (hSystemStore) {
- PCCERT_CONTEXT pc = nullptr;
- while (1) {
- pc = ptrCertFindCertificateInStore(hSystemStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY, nullptr, pc);
- if (!pc)
- break;
- QByteArray der(reinterpret_cast<const char *>(pc->pbCertEncoded),
- static_cast<int>(pc->cbCertEncoded));
- QSslCertificate cert(der, QSsl::Der);
- systemCerts.append(cert);
- }
- ptrCertCloseStore(hSystemStore, 0);
+ HCERTSTORE hSystemStore;
+ hSystemStore = CertOpenSystemStoreW(0, L"ROOT");
+ if (hSystemStore) {
+ PCCERT_CONTEXT pc = nullptr;
+ while (1) {
+ pc = CertFindCertificateInStore(hSystemStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY, nullptr, pc);
+ if (!pc)
+ break;
+ QByteArray der(reinterpret_cast<const char *>(pc->pbCertEncoded),
+ static_cast<int>(pc->cbCertEncoded));
+ QSslCertificate cert(der, QSsl::Der);
+ systemCerts.append(cert);
}
+ CertCloseStore(hSystemStore, 0);
}
#elif defined(Q_OS_UNIX)
QSet<QString> certFiles;
@@ -992,9 +1182,33 @@ bool QSslSocketBackendPrivate::startHandshake()
}
}
- bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer
- || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer
- && mode == QSslSocket::SslClientMode);
+ const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer
+ || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer
+ && mode == QSslSocket::SslClientMode);
+
+#if QT_CONFIG(ocsp)
+ // For now it's always QSslSocket::SslClientMode - initSslContext() will bail out early,
+ // if it's enabled in QSslSocket::SslServerMode. This can change.
+ if (!configuration.peerCertificate.isNull() && configuration.ocspStaplingEnabled && doVerifyPeer) {
+ if (!checkOcspStatus()) {
+ if (ocspErrors.isEmpty()) {
+ {
+ const ScopedBool bg(inSetAndEmitError, true);
+ setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, ocspErrorDescription);
+ }
+ q->abort();
+ return false;
+ }
+
+ for (const QSslError &error : ocspErrors) {
+ errors << error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+ }
+#endif // ocsp
// Check the peer certificate itself. First try the subject's common name
// (CN) as a wildcard, then try all alternate subject name DNS entries the
@@ -1241,6 +1455,193 @@ void QSslSocketBackendPrivate::_q_caRootLoaded(QSslCertificate cert, QSslCertifi
#endif
+#if QT_CONFIG(ocsp)
+
+bool QSslSocketBackendPrivate::checkOcspStatus()
+{
+ Q_ASSERT(ssl);
+ Q_ASSERT(mode == QSslSocket::SslClientMode); // See initSslContext() for SslServerMode
+ Q_ASSERT(configuration.peerVerifyMode != QSslSocket::VerifyNone);
+
+ ocspResponses.clear();
+ ocspErrorDescription.clear();
+ ocspErrors.clear();
+
+ const unsigned char *responseData = nullptr;
+ const long responseLength = q_SSL_get_tlsext_status_ocsp_resp(ssl, &responseData);
+ if (responseLength <= 0 || !responseData) {
+ ocspErrors.push_back(QSslError::OcspNoResponseFound);
+ return false;
+ }
+
+ OCSP_RESPONSE *response = q_d2i_OCSP_RESPONSE(nullptr, &responseData, responseLength);
+ if (!response) {
+ // Treat this as a fatal SslHandshakeError.
+ ocspErrorDescription = QSslSocket::tr("Failed to decode OCSP response");
+ return false;
+ }
+ const QSharedPointer<OCSP_RESPONSE> responseGuard(response, q_OCSP_RESPONSE_free);
+
+ const int ocspStatus = q_OCSP_response_status(response);
+ if (ocspStatus != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+ // It's not a definitive response, it's an error message (not signed by the responder).
+ ocspErrors.push_back(qt_OCSP_response_status_to_QSslError(ocspStatus));
+ return false;
+ }
+
+ OCSP_BASICRESP *basicResponse = q_OCSP_response_get1_basic(response);
+ if (!basicResponse) {
+ // SslHandshakeError.
+ ocspErrorDescription = QSslSocket::tr("Failed to extract basic OCSP response");
+ return false;
+ }
+ const QSharedPointer<OCSP_BASICRESP> basicResponseGuard(basicResponse, q_OCSP_BASICRESP_free);
+
+ SSL_CTX *ctx = q_SSL_get_SSL_CTX(ssl); // Does not increment refcount.
+ Q_ASSERT(ctx);
+ X509_STORE *store = q_SSL_CTX_get_cert_store(ctx); // Does not increment refcount.
+ if (!store) {
+ // SslHandshakeError.
+ ocspErrorDescription = QSslSocket::tr("No certificate verification store, cannot verify OCSP response");
+ return false;
+ }
+
+ STACK_OF(X509) *peerChain = q_SSL_get_peer_cert_chain(ssl); // Does not increment refcount.
+ X509 *peerX509 = q_SSL_get_peer_certificate(ssl);
+ Q_ASSERT(peerChain || peerX509);
+ const QSharedPointer<X509> peerX509Guard(peerX509, q_X509_free);
+ // OCSP_basic_verify with 0 as verificationFlags:
+ //
+ // 0) Tries to find the OCSP responder's certificate in either peerChain
+ // or basicResponse->certs. If not found, verification fails.
+ // 1) It checks the signature using the responder's public key.
+ // 2) Then it tries to validate the responder's cert (building a chain
+ // etc.)
+ // 3) It checks CertID in response.
+ // 4) Ensures the responder is authorized to sign the status respond.
+ //
+ // Here it's important to notice that it calls X509_cert_verify and
+ // as a result, possibly, our verification callback. Given this callback
+ // at the moment uses a global variable, we have to lock. This will change
+ // as soon as we fix our verification procedure.
+ // Also note, OpenSSL prior to 1.0.2b would only use bs->certs to
+ // verify the responder's chain (see their commit 4ba9a4265bd).
+ // Working this around - is too much fuss for ancient versions we
+ // are dropping quite soon anyway.
+ {
+ const unsigned long verificationFlags = 0;
+ const QMutexLocker locker(&_q_sslErrorList()->mutex);
+ // Before unlocking the mutex, startHandshake() stores errors (found in SSL_connect()
+ // or SSL_accept()) into the local variable, so it's safe to clear it here - as soon
+ // as we managed to lock, whoever had the lock before, already stored their own copy
+ // of errors.
+ _q_sslErrorList()->errors.clear();
+ const int success = q_OCSP_basic_verify(basicResponse, peerChain, store, verificationFlags);
+ if (success <= 0 || _q_sslErrorList()->errors.size()) {
+ _q_sslErrorList()->errors.clear();
+ ocspErrors.push_back(QSslError::OcspResponseCannotBeTrusted);
+ }
+ }
+
+ if (q_OCSP_resp_count(basicResponse) != 1) {
+ ocspErrors.push_back(QSslError::OcspMalformedResponse);
+ return false;
+ }
+
+ OCSP_SINGLERESP *singleResponse = q_OCSP_resp_get0(basicResponse, 0);
+ if (!singleResponse) {
+ ocspErrors.clear();
+ // A fatal problem -> SslHandshakeError.
+ ocspErrorDescription = QSslSocket::tr("Failed to decode a SingleResponse from OCSP status response");
+ return false;
+ }
+
+ // Let's make sure the response is for the correct certificate - we
+ // can re-create this CertID using our peer's certificate and its
+ // issuer's public key.
+ ocspResponses.push_back(QOcspResponse());
+ QOcspResponsePrivate *dResponse = ocspResponses.back().d.data();
+ dResponse->subjectCert = configuration.peerCertificate;
+ bool matchFound = false;
+ if (configuration.peerCertificate.isSelfSigned()) {
+ dResponse->signerCert = configuration.peerCertificate;
+ matchFound = qt_OCSP_certificate_match(singleResponse, peerX509, peerX509);
+ } else {
+ const STACK_OF(X509) *certs = q_SSL_get_peer_cert_chain(ssl);
+ if (!certs) // Oh, what a cataclysm! Last try:
+ certs = q_OCSP_resp_get0_certs(basicResponse);
+ if (certs) {
+ // It could be the first certificate in 'certs' is our peer's
+ // certificate. Since it was not captured by the 'self-signed' branch
+ // above, the CertID will not match and we'll just iterate on to the
+ // next certificate. So we start from 0, not 1.
+ for (int i = 0, e = q_sk_X509_num(certs); i < e; ++i) {
+ X509 *issuer = q_sk_X509_value(certs, i);
+ matchFound = qt_OCSP_certificate_match(singleResponse, peerX509, issuer);
+ if (matchFound) {
+ if (q_X509_check_issued(issuer, peerX509) == X509_V_OK) {
+ dResponse->signerCert = QSslCertificatePrivate::QSslCertificate_from_X509(issuer);
+ break;
+ }
+ matchFound = false;
+ }
+ }
+ }
+ }
+
+ if (!matchFound) {
+ dResponse->signerCert.clear();
+ ocspErrors.push_back({QSslError::OcspResponseCertIdUnknown, configuration.peerCertificate});
+ }
+
+ // Check if the response is valid time-wise:
+ ASN1_GENERALIZEDTIME *revTime = nullptr;
+ ASN1_GENERALIZEDTIME *thisUpdate = nullptr;
+ ASN1_GENERALIZEDTIME *nextUpdate = nullptr;
+ int reason;
+ const int certStatus = q_OCSP_single_get0_status(singleResponse, &reason, &revTime, &thisUpdate, &nextUpdate);
+ if (!thisUpdate) {
+ // This is unexpected, treat as SslHandshakeError, OCSP_check_validity assumes this pointer
+ // to be != nullptr.
+ ocspErrors.clear();
+ ocspResponses.clear();
+ ocspErrorDescription = QSslSocket::tr("Failed to extract 'this update time' from the SingleResponse");
+ return false;
+ }
+
+ // OCSP_check_validity(this, next, nsec, maxsec) does this check:
+ // this <= now <= next. They allow some freedom to account
+ // for delays/time inaccuracy.
+ // this > now + nsec ? -> NOT_YET_VALID
+ // if maxsec >= 0:
+ // now - maxsec > this ? -> TOO_OLD
+ // now - nsec > next ? -> EXPIRED
+ // next < this ? -> NEXT_BEFORE_THIS
+ // OK.
+ if (!q_OCSP_check_validity(thisUpdate, nextUpdate, 60, -1))
+ ocspErrors.push_back({QSslError::OcspResponseExpired, configuration.peerCertificate});
+
+ // And finally, the status:
+ switch (certStatus) {
+ case V_OCSP_CERTSTATUS_GOOD:
+ // This certificate was not found among the revoked ones.
+ dResponse->certificateStatus = QOcspCertificateStatus::Good;
+ break;
+ case V_OCSP_CERTSTATUS_REVOKED:
+ dResponse->certificateStatus = QOcspCertificateStatus::Revoked;
+ dResponse->revocationReason = qt_OCSP_revocation_reason(reason);
+ ocspErrors.push_back({QSslError::CertificateRevoked, configuration.peerCertificate});
+ break;
+ case V_OCSP_CERTSTATUS_UNKNOWN:
+ dResponse->certificateStatus = QOcspCertificateStatus::Unknown;
+ ocspErrors.push_back({QSslError::OcspStatusUnknown, configuration.peerCertificate});
+ }
+
+ return !ocspErrors.size();
+}
+
+#endif // ocsp
+
void QSslSocketBackendPrivate::disconnectFromHost()
{
if (ssl) {
@@ -1294,6 +1695,8 @@ QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const
return QSsl::TlsV1_1;
case 0x303:
return QSsl::TlsV1_2;
+ case 0x304:
+ return QSsl::TlsV1_3;
}
return QSsl::UnknownProtocol;
diff --git a/src/network/ssl/qsslsocket_openssl11.cpp b/src/network/ssl/qsslsocket_openssl11.cpp
index cbbf403672..b60b8be41f 100644
--- a/src/network/ssl/qsslsocket_openssl11.cpp
+++ b/src/network/ssl/qsslsocket_openssl11.cpp
@@ -88,8 +88,6 @@ bool QSslSocketPrivate::ensureLibraryLoaded()
const QMutexLocker locker(qt_opensslInitMutex);
if (!s_libraryLoaded) {
- s_libraryLoaded = true;
-
// Initialize OpenSSL.
if (q_OPENSSL_init_ssl(0, nullptr) != 1)
return false;
@@ -105,6 +103,8 @@ bool QSslSocketPrivate::ensureLibraryLoaded()
qWarning("Random number generator not seeded, disabling SSL support");
return false;
}
+
+ s_libraryLoaded = true;
}
return true;
}
@@ -122,21 +122,7 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded()
#if QT_CONFIG(library)
//load symbols needed to receive certificates from system store
-#if defined(Q_OS_WIN)
- HINSTANCE hLib = LoadLibraryW(L"Crypt32");
- if (hLib) {
- ptrCertOpenSystemStoreW = reinterpret_cast<PtrCertOpenSystemStoreW>(
- reinterpret_cast<QFunctionPointer>(GetProcAddress(hLib, "CertOpenSystemStoreW")));
- ptrCertFindCertificateInStore = reinterpret_cast<PtrCertFindCertificateInStore>(
- reinterpret_cast<QFunctionPointer>(GetProcAddress(hLib, "CertFindCertificateInStore")));
- ptrCertCloseStore = reinterpret_cast<PtrCertCloseStore>(
- reinterpret_cast<QFunctionPointer>(GetProcAddress(hLib, "CertCloseStore")));
- if (!ptrCertOpenSystemStoreW || !ptrCertFindCertificateInStore || !ptrCertCloseStore)
- qCWarning(lcSsl, "could not resolve symbols in crypt32 library"); // should never happen
- } else {
- qCWarning(lcSsl, "could not load crypt32 library"); // should never happen
- }
-#elif defined(Q_OS_QNX)
+#if defined(Q_OS_QNX)
s_loadRootCertsOnDemand = true;
#elif defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
// check whether we can enable on-demand root-cert loading (i.e. check whether the sym links are there)
diff --git a/src/network/ssl/qsslsocket_openssl11_symbols_p.h b/src/network/ssl/qsslsocket_openssl11_symbols_p.h
index 844c3437be..9d0a14360d 100644
--- a/src/network/ssl/qsslsocket_openssl11_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl11_symbols_p.h
@@ -82,14 +82,15 @@ Q_AUTOTEST_EXPORT const BIO_METHOD *q_BIO_s_mem();
int q_DSA_bits(DSA *a);
int q_EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *c);
+Q_AUTOTEST_EXPORT int q_EVP_PKEY_up_ref(EVP_PKEY *a);
int q_EVP_PKEY_base_id(EVP_PKEY *a);
int q_RSA_bits(RSA *a);
-int q_OPENSSL_sk_num(OPENSSL_STACK *a);
-void q_OPENSSL_sk_pop_free(OPENSSL_STACK *a, void (*b)(void *));
-OPENSSL_STACK *q_OPENSSL_sk_new_null();
-void q_OPENSSL_sk_push(OPENSSL_STACK *st, void *data);
-void q_OPENSSL_sk_free(OPENSSL_STACK *a);
-void * q_OPENSSL_sk_value(OPENSSL_STACK *a, int b);
+Q_AUTOTEST_EXPORT int q_OPENSSL_sk_num(OPENSSL_STACK *a);
+Q_AUTOTEST_EXPORT void q_OPENSSL_sk_pop_free(OPENSSL_STACK *a, void (*b)(void *));
+Q_AUTOTEST_EXPORT OPENSSL_STACK *q_OPENSSL_sk_new_null();
+Q_AUTOTEST_EXPORT void q_OPENSSL_sk_push(OPENSSL_STACK *st, void *data);
+Q_AUTOTEST_EXPORT void q_OPENSSL_sk_free(OPENSSL_STACK *a);
+Q_AUTOTEST_EXPORT void * q_OPENSSL_sk_value(OPENSSL_STACK *a, int b);
int q_SSL_session_reused(SSL *a);
unsigned long q_SSL_CTX_set_options(SSL_CTX *ctx, unsigned long op);
int q_OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings);
@@ -102,6 +103,7 @@ const SSL_METHOD *q_TLS_server_method();
ASN1_TIME *q_X509_getm_notBefore(X509 *a);
ASN1_TIME *q_X509_getm_notAfter(X509 *a);
+Q_AUTOTEST_EXPORT void q_X509_up_ref(X509 *a);
long q_X509_get_version(X509 *a);
EVP_PKEY *q_X509_get_pubkey(X509 *a);
void q_X509_STORE_set_verify_cb(X509_STORE *ctx, X509_STORE_CTX_verify_cb verify_cb);
@@ -130,6 +132,10 @@ const char *q_OpenSSL_version(int type);
unsigned long q_SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *session);
unsigned long q_SSL_set_options(SSL *s, unsigned long op);
+#ifdef TLS1_3_VERSION
+int q_SSL_CTX_set_ciphersuites(SSL_CTX *ctx, const char *str);
+#endif
+
#if QT_CONFIG(dtls)
// Functions and types required for DTLS support:
extern "C"
@@ -168,6 +174,10 @@ void q_BIO_set_init(BIO *a, int init);
int q_BIO_get_shutdown(BIO *a);
void q_BIO_set_shutdown(BIO *a, int shut);
+#if QT_CONFIG(ocsp)
+const OCSP_CERTID *q_OCSP_SINGLERESP_get0_id(const OCSP_SINGLERESP *x);
+#endif // ocsp
+
#define q_SSL_CTX_set_min_proto_version(ctx, version) \
q_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, version, nullptr)
diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h
index c16b9d5f76..c23234e291 100644
--- a/src/network/ssl/qsslsocket_openssl_p.h
+++ b/src/network/ssl/qsslsocket_openssl_p.h
@@ -69,6 +69,9 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qsslsocket_p.h"
+#include <QtCore/qvector.h>
+#include <QtCore/qstring.h>
+
#ifdef Q_OS_WIN
#include <qt_windows.h>
#if defined(OCSP_RESPONSE)
@@ -152,6 +155,16 @@ public:
void _q_caRootLoaded(QSslCertificate,QSslCertificate) override;
#endif
+#if QT_CONFIG(ocsp)
+ bool checkOcspStatus();
+#endif
+
+ // This decription will go to setErrorAndEmit(SslHandshakeError, ocspErrorDescription)
+ QString ocspErrorDescription;
+ // These will go to sslErrors()
+ QVector<QSslError> ocspErrors;
+ QByteArray ocspResponseDer;
+
Q_AUTOTEST_EXPORT static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions);
static QSslCipher QSslCipher_from_SSL_CIPHER(const SSL_CIPHER *cipher);
static QList<QSslCertificate> STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509);
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index 8af9b5d77e..e04d45c10c 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -150,6 +150,7 @@ DEFINEFUNC(BIO *, BIO_new, const BIO_METHOD *a, a, return nullptr, return)
DEFINEFUNC(const BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return nullptr, return)
DEFINEFUNC2(int, BN_is_word, BIGNUM *a, a, BN_ULONG w, w, return 0, return)
DEFINEFUNC(int, EVP_CIPHER_CTX_reset, EVP_CIPHER_CTX *c, c, return 0, return)
+DEFINEFUNC(int, EVP_PKEY_up_ref, EVP_PKEY *a, a, return 0, return)
DEFINEFUNC(int, EVP_PKEY_base_id, EVP_PKEY *a, a, return NID_undef, return)
DEFINEFUNC(int, RSA_bits, RSA *a, a, return 0, return)
DEFINEFUNC(int, DSA_bits, DSA *a, a, return 0, return)
@@ -161,6 +162,9 @@ DEFINEFUNC(void, OPENSSL_sk_free, OPENSSL_STACK *a, a, return, DUMMYARG)
DEFINEFUNC2(void *, OPENSSL_sk_value, OPENSSL_STACK *a, a, int b, b, return nullptr, return)
DEFINEFUNC(int, SSL_session_reused, SSL *a, a, return 0, return)
DEFINEFUNC2(unsigned long, SSL_CTX_set_options, SSL_CTX *ctx, ctx, unsigned long op, op, return 0, return)
+#ifdef TLS1_3_VERSION
+DEFINEFUNC2(int, SSL_CTX_set_ciphersuites, SSL_CTX *ctx, ctx, const char *str, str, return 0, return)
+#endif
DEFINEFUNC3(size_t, SSL_get_client_random, SSL *a, a, unsigned char *out, out, size_t outlen, outlen, return 0, return)
DEFINEFUNC3(size_t, SSL_SESSION_get_master_key, const SSL_SESSION *ses, ses, unsigned char *out, out, size_t outlen, outlen, return 0, return)
DEFINEFUNC6(int, CRYPTO_get_ex_new_index, int class_index, class_index, long argl, argl, void *argp, argp, CRYPTO_EX_new *new_func, new_func, CRYPTO_EX_dup *dup_func, dup_func, CRYPTO_EX_free *free_func, free_func, return -1, return)
@@ -169,6 +173,7 @@ DEFINEFUNC2(unsigned long, SSL_set_options, SSL *ssl, ssl, unsigned long op, op,
DEFINEFUNC(const SSL_METHOD *, TLS_method, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC(const SSL_METHOD *, TLS_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC(const SSL_METHOD *, TLS_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(void, X509_up_ref, X509 *a, a, return, DUMMYARG)
DEFINEFUNC(ASN1_TIME *, X509_getm_notBefore, X509 *a, a, return nullptr, return)
DEFINEFUNC(ASN1_TIME *, X509_getm_notAfter, X509 *a, a, return nullptr, return)
DEFINEFUNC(long, X509_get_version, X509 *a, a, return -1, return)
@@ -196,6 +201,35 @@ DEFINEFUNC2(int, BIO_meth_set_create, BIO_METHOD *biom, biom, DgramCreateCallbac
DEFINEFUNC2(int, BIO_meth_set_destroy, BIO_METHOD *biom, biom, DgramDestroyCallback dtr, dtr, return 0, return)
#endif // dtls
+#if QT_CONFIG(ocsp)
+DEFINEFUNC(const OCSP_CERTID *, OCSP_SINGLERESP_get0_id, const OCSP_SINGLERESP *x, x, return nullptr, return)
+DEFINEFUNC3(OCSP_RESPONSE *, d2i_OCSP_RESPONSE, OCSP_RESPONSE **a, a, const unsigned char **in, in, long len, len, return nullptr, return)
+DEFINEFUNC(void, OCSP_RESPONSE_free, OCSP_RESPONSE *rs, rs, return, DUMMYARG)
+DEFINEFUNC(OCSP_BASICRESP *, OCSP_response_get1_basic, OCSP_RESPONSE *resp, resp, return nullptr, return)
+DEFINEFUNC(void, OCSP_BASICRESP_free, OCSP_BASICRESP *bs, bs, return, DUMMYARG)
+DEFINEFUNC(int, OCSP_response_status, OCSP_RESPONSE *resp, resp, return OCSP_RESPONSE_STATUS_INTERNALERROR, return)
+DEFINEFUNC4(int, OCSP_basic_verify, OCSP_BASICRESP *bs, bs, STACK_OF(X509) *certs, certs, X509_STORE *st, st, unsigned long flags, flags, return -1, return)
+DEFINEFUNC(int, OCSP_resp_count, OCSP_BASICRESP *bs, bs, return 0, return)
+DEFINEFUNC2(OCSP_SINGLERESP *, OCSP_resp_get0, OCSP_BASICRESP *bs, bs, int idx, idx, return nullptr, return)
+DEFINEFUNC5(int, OCSP_single_get0_status, OCSP_SINGLERESP *single, single, int *reason, reason, ASN1_GENERALIZEDTIME **revtime, revtime,
+ ASN1_GENERALIZEDTIME **thisupd, thisupd, ASN1_GENERALIZEDTIME **nextupd, nextupd, return -1, return)
+DEFINEFUNC4(int, OCSP_check_validity, ASN1_GENERALIZEDTIME *thisupd, thisupd, ASN1_GENERALIZEDTIME *nextupd, nextupd, long nsec, nsec, long maxsec, maxsec, return 0, return)
+DEFINEFUNC3(OCSP_CERTID *, OCSP_cert_to_id, const EVP_MD *dgst, dgst, X509 *subject, subject, X509 *issuer, issuer, return nullptr, return)
+DEFINEFUNC(void, OCSP_CERTID_free, OCSP_CERTID *cid, cid, return, DUMMYARG)
+DEFINEFUNC5(int, OCSP_id_get0_info, ASN1_OCTET_STRING **piNameHash, piNameHash, ASN1_OBJECT **pmd, pmd,
+ ASN1_OCTET_STRING **piKeyHash, piKeyHash, ASN1_INTEGER **pserial, pserial, OCSP_CERTID *cid, cid,
+ return 0, return)
+DEFINEFUNC2(OCSP_RESPONSE *, OCSP_response_create, int status, status, OCSP_BASICRESP *bs, bs, return nullptr, return)
+DEFINEFUNC(const STACK_OF(X509) *, OCSP_resp_get0_certs, const OCSP_BASICRESP *bs, bs, return nullptr, return)
+DEFINEFUNC2(int, OCSP_id_cmp, OCSP_CERTID *a, a, OCSP_CERTID *b, b, return -1, return)
+DEFINEFUNC7(OCSP_SINGLERESP *, OCSP_basic_add1_status, OCSP_BASICRESP *r, r, OCSP_CERTID *c, c, int s, s,
+ int re, re, ASN1_TIME *rt, rt, ASN1_TIME *t, t, ASN1_TIME *n, n, return nullptr, return)
+DEFINEFUNC(OCSP_BASICRESP *, OCSP_BASICRESP_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC2(int, i2d_OCSP_RESPONSE, OCSP_RESPONSE *r, r, unsigned char **ppout, ppout, return 0, return)
+DEFINEFUNC6(int, OCSP_basic_sign, OCSP_BASICRESP *br, br, X509 *signer, signer, EVP_PKEY *key, key,
+ const EVP_MD *dg, dg, STACK_OF(X509) *cs, cs, unsigned long flags, flags, return 0, return)
+#endif // ocsp
+
DEFINEFUNC2(void, BIO_set_data, BIO *a, a, void *ptr, ptr, return, DUMMYARG)
DEFINEFUNC(void *, BIO_get_data, BIO *a, a, return nullptr, return)
DEFINEFUNC2(void, BIO_set_init, BIO *a, a, int init, init, return, DUMMYARG)
@@ -232,17 +266,10 @@ DEFINEFUNC6(void *, PEM_ASN1_write_bio, d2i_of_void *a, a, const char *b, b, BIO
DEFINEFUNC(int, sk_num, STACK *a, a, return -1, return)
DEFINEFUNC2(void, sk_pop_free, STACK *a, a, void (*b)(void*), b, return, DUMMYARG)
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
DEFINEFUNC(_STACK *, sk_new_null, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC2(void, sk_push, _STACK *a, a, void *b, b, return, DUMMYARG)
DEFINEFUNC(void, sk_free, _STACK *a, a, return, DUMMYARG)
DEFINEFUNC2(void *, sk_value, STACK *a, a, int b, b, return nullptr, return)
-#else
-DEFINEFUNC(STACK *, sk_new_null, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC2(void, sk_push, STACK *a, a, char *b, b, return, DUMMYARG)
-DEFINEFUNC(void, sk_free, STACK *a, a, return, DUMMYARG)
-DEFINEFUNC2(char *, sk_value, STACK *a, a, int b, b, return nullptr, return)
-#endif // OPENSSL_VERSION_NUMBER >= 0x10000000L
DEFINEFUNC(int, SSL_library_init, void, DUMMYARG, return -1, return)
DEFINEFUNC(void, SSL_load_error_strings, void, DUMMYARG, return, DUMMYARG)
@@ -251,49 +278,18 @@ DEFINEFUNC(void, SSL_load_error_strings, void, DUMMYARG, return, DUMMYARG)
DEFINEFUNC5(int, SSL_get_ex_new_index, long argl, argl, void *argp, argp, CRYPTO_EX_new *new_func, new_func, CRYPTO_EX_dup *dup_func, dup_func, CRYPTO_EX_free *free_func, free_func, return -1, return)
#endif // OPENSSL_VERSION_NUMBER >= 0x10001000L
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
-#ifndef OPENSSL_NO_SSL2
-DEFINEFUNC(const SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
-#endif
-#ifndef OPENSSL_NO_SSL3_METHOD
-DEFINEFUNC(const SSL_METHOD *, SSLv3_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
-#endif
DEFINEFUNC(const SSL_METHOD *, SSLv23_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC(const SSL_METHOD *, TLSv1_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
DEFINEFUNC(const SSL_METHOD *, TLSv1_1_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC(const SSL_METHOD *, TLSv1_2_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
#endif
-#ifndef OPENSSL_NO_SSL2
-DEFINEFUNC(const SSL_METHOD *, SSLv2_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
-#endif
-#ifndef OPENSSL_NO_SSL3_METHOD
-DEFINEFUNC(const SSL_METHOD *, SSLv3_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
-#endif
DEFINEFUNC(const SSL_METHOD *, SSLv23_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC(const SSL_METHOD *, TLSv1_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
DEFINEFUNC(const SSL_METHOD *, TLSv1_1_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC(const SSL_METHOD *, TLSv1_2_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
#endif
-#else
-#ifndef OPENSSL_NO_SSL2
-DEFINEFUNC(SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
-#endif
-#ifndef OPENSSL_NO_SSL3_METHOD
-DEFINEFUNC(SSL_METHOD *, SSLv3_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
-#endif
-DEFINEFUNC(SSL_METHOD *, SSLv23_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC(SSL_METHOD *, TLSv1_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
-#ifndef OPENSSL_NO_SSL2
-DEFINEFUNC(SSL_METHOD *, SSLv2_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
-#endif
-#ifndef OPENSSL_NO_SSL3_METHOD
-DEFINEFUNC(SSL_METHOD *, SSLv3_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
-#endif
-DEFINEFUNC(SSL_METHOD *, SSLv23_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC(SSL_METHOD *, TLSv1_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
-#endif
DEFINEFUNC(STACK_OF(X509) *, X509_STORE_CTX_get_chain, X509_STORE_CTX *a, a, return nullptr, return)
@@ -326,6 +322,7 @@ DEFINEFUNC(const char *, SSLeay_version, int a, a, return nullptr, return)
#endif // QT_CONFIG(opensslv11)
DEFINEFUNC(long, ASN1_INTEGER_get, ASN1_INTEGER *a, a, return 0, return)
+DEFINEFUNC2(int, ASN1_INTEGER_cmp, const ASN1_INTEGER *a, a, const ASN1_INTEGER *b, b, return 1, return)
DEFINEFUNC(int, ASN1_STRING_length, ASN1_STRING *a, a, return 0, return)
DEFINEFUNC2(int, ASN1_STRING_to_UTF8, unsigned char **a, a, ASN1_STRING *b, b, return 0, return)
DEFINEFUNC4(long, BIO_ctrl, BIO *a, a, int b, b, long c, c, void *d, d, return -1, return)
@@ -354,6 +351,7 @@ DEFINEFUNC5(int, EVP_CipherInit, EVP_CIPHER_CTX *ctx, ctx, const EVP_CIPHER *typ
DEFINEFUNC6(int, EVP_CipherInit_ex, EVP_CIPHER_CTX *ctx, ctx, const EVP_CIPHER *cipher, cipher, ENGINE *impl, impl, const unsigned char *key, key, const unsigned char *iv, iv, int enc, enc, return 0, return)
DEFINEFUNC5(int, EVP_CipherUpdate, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, const unsigned char *in, in, int inl, inl, return 0, return)
DEFINEFUNC3(int, EVP_CipherFinal, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, return 0, return)
+DEFINEFUNC(const EVP_MD *, EVP_get_digestbyname, const char *name, name, return nullptr, return)
#ifndef OPENSSL_NO_DES
DEFINEFUNC(const EVP_CIPHER *, EVP_des_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC(const EVP_CIPHER *, EVP_des_ede3_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
@@ -361,16 +359,24 @@ DEFINEFUNC(const EVP_CIPHER *, EVP_des_ede3_cbc, DUMMYARG, DUMMYARG, return null
#ifndef OPENSSL_NO_RC2
DEFINEFUNC(const EVP_CIPHER *, EVP_rc2_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
#endif
+#ifndef OPENSSL_NO_AES
+DEFINEFUNC(const EVP_CIPHER *, EVP_aes_128_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const EVP_CIPHER *, EVP_aes_192_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const EVP_CIPHER *, EVP_aes_256_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
+#endif
DEFINEFUNC(const EVP_MD *, EVP_sha1, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC3(int, EVP_PKEY_assign, EVP_PKEY *a, a, int b, b, char *c, c, return -1, return)
DEFINEFUNC2(int, EVP_PKEY_set1_RSA, EVP_PKEY *a, a, RSA *b, b, return -1, return)
DEFINEFUNC2(int, EVP_PKEY_set1_DSA, EVP_PKEY *a, a, DSA *b, b, return -1, return)
+DEFINEFUNC2(int, EVP_PKEY_set1_DH, EVP_PKEY *a, a, DH *b, b, return -1, return)
#ifndef OPENSSL_NO_EC
DEFINEFUNC2(int, EVP_PKEY_set1_EC_KEY, EVP_PKEY *a, a, EC_KEY *b, b, return -1, return)
#endif
+DEFINEFUNC2(int, EVP_PKEY_cmp, const EVP_PKEY *a, a, const EVP_PKEY *b, b, return -1, return)
DEFINEFUNC(void, EVP_PKEY_free, EVP_PKEY *a, a, return, DUMMYARG)
DEFINEFUNC(DSA *, EVP_PKEY_get1_DSA, EVP_PKEY *a, a, return nullptr, return)
DEFINEFUNC(RSA *, EVP_PKEY_get1_RSA, EVP_PKEY *a, a, return nullptr, return)
+DEFINEFUNC(DH *, EVP_PKEY_get1_DH, EVP_PKEY *a, a, return nullptr, return)
#ifndef OPENSSL_NO_EC
DEFINEFUNC(EC_KEY *, EVP_PKEY_get1_EC_KEY, EVP_PKEY *a, a, return nullptr, return)
#endif
@@ -396,6 +402,7 @@ DEFINEFUNC4(EC_KEY *, PEM_read_bio_ECPrivateKey, BIO *a, a, EC_KEY **b, b, pem_p
DEFINEFUNC4(DH *, PEM_read_bio_DHparams, BIO *a, a, DH **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
DEFINEFUNC7(int, PEM_write_bio_DSAPrivateKey, BIO *a, a, DSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
DEFINEFUNC7(int, PEM_write_bio_RSAPrivateKey, BIO *a, a, RSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
+DEFINEFUNC7(int, PEM_write_bio_PrivateKey, BIO *a, a, EVP_PKEY *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
#ifndef OPENSSL_NO_EC
DEFINEFUNC7(int, PEM_write_bio_ECPrivateKey, BIO *a, a, EC_KEY *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
#endif
@@ -408,6 +415,7 @@ DEFINEFUNC4(EC_KEY *, PEM_read_bio_EC_PUBKEY, BIO *a, a, EC_KEY **b, b, pem_pass
#endif
DEFINEFUNC2(int, PEM_write_bio_DSA_PUBKEY, BIO *a, a, DSA *b, b, return 0, return)
DEFINEFUNC2(int, PEM_write_bio_RSA_PUBKEY, BIO *a, a, RSA *b, b, return 0, return)
+DEFINEFUNC2(int, PEM_write_bio_PUBKEY, BIO *a, a, EVP_PKEY *b, b, return 0, return)
#ifndef OPENSSL_NO_EC
DEFINEFUNC2(int, PEM_write_bio_EC_PUBKEY, BIO *a, a, EC_KEY *b, b, return 0, return)
#endif
@@ -425,12 +433,9 @@ DEFINEFUNC(int, SSL_connect, SSL *a, a, return -1, return)
DEFINEFUNC(int, SSL_CTX_check_private_key, const SSL_CTX *a, a, return -1, return)
DEFINEFUNC4(long, SSL_CTX_ctrl, SSL_CTX *a, a, int b, b, long c, c, void *d, d, return -1, return)
DEFINEFUNC(void, SSL_CTX_free, SSL_CTX *a, a, return, DUMMYARG)
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
DEFINEFUNC(SSL_CTX *, SSL_CTX_new, const SSL_METHOD *a, a, return nullptr, return)
-#else
-DEFINEFUNC(SSL_CTX *, SSL_CTX_new, SSL_METHOD *a, a, return nullptr, return)
-#endif
DEFINEFUNC2(int, SSL_CTX_set_cipher_list, SSL_CTX *a, a, const char *b, b, return -1, return)
+DEFINEFUNC3(long, SSL_CTX_callback_ctrl, SSL_CTX *ctx, ctx, int dst, dst, GenericCallbackType cb, cb, return 0, return)
DEFINEFUNC(int, SSL_CTX_set_default_verify_paths, SSL_CTX *a, a, return -1, return)
DEFINEFUNC3(void, SSL_CTX_set_verify, SSL_CTX *a, a, int b, b, int (*c)(int, X509_STORE_CTX *), c, return, DUMMYARG)
DEFINEFUNC2(void, SSL_CTX_set_verify_depth, SSL_CTX *a, a, int b, b, return, DUMMYARG)
@@ -450,22 +455,14 @@ DEFINEFUNC3(int, SSL_CONF_cmd, SSL_CONF_CTX *a, a, const char *b, b, const char
#endif
DEFINEFUNC(void, SSL_free, SSL *a, a, return, DUMMYARG)
DEFINEFUNC(STACK_OF(SSL_CIPHER) *, SSL_get_ciphers, const SSL *a, a, return nullptr, return)
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
DEFINEFUNC(const SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return nullptr, return)
-#else
-DEFINEFUNC(SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return nullptr, return)
-#endif
DEFINEFUNC(int, SSL_version, const SSL *a, a, return 0, return)
DEFINEFUNC2(int, SSL_get_error, SSL *a, a, int b, b, return -1, return)
DEFINEFUNC(STACK_OF(X509) *, SSL_get_peer_cert_chain, SSL *a, a, return nullptr, return)
DEFINEFUNC(X509 *, SSL_get_peer_certificate, SSL *a, a, return nullptr, return)
-#if OPENSSL_VERSION_NUMBER >= 0x00908000L
-// 0.9.8 broke SC and BC by changing this function's signature.
DEFINEFUNC(long, SSL_get_verify_result, const SSL *a, a, return -1, return)
-#else
-DEFINEFUNC(long, SSL_get_verify_result, SSL *a, a, return -1, return)
-#endif
DEFINEFUNC(SSL *, SSL_new, SSL_CTX *a, a, return nullptr, return)
+DEFINEFUNC(SSL_CTX *, SSL_get_SSL_CTX, SSL *a, a, return nullptr, return)
DEFINEFUNC4(long, SSL_ctrl, SSL *a, a, int cmd, cmd, long larg, larg, void *parg, parg, return -1, return)
DEFINEFUNC3(int, SSL_read, SSL *a, a, void *b, b, int c, c, return -1, return)
DEFINEFUNC3(void, SSL_set_bio, SSL *a, a, BIO *b, b, BIO *c, c, return, DUMMYARG)
@@ -495,6 +492,9 @@ DEFINEFUNC(X509 *, X509_dup, X509 *a, a, return nullptr, return)
DEFINEFUNC2(void, X509_print, BIO *a, a, X509 *b, b, return, DUMMYARG);
DEFINEFUNC(ASN1_OBJECT *, X509_EXTENSION_get_object, X509_EXTENSION *a, a, return nullptr, return)
DEFINEFUNC(void, X509_free, X509 *a, a, return, DUMMYARG)
+//Q_AUTOTEST_EXPORT ASN1_TIME *q_X509_gmtime_adj(ASN1_TIME *s, long adj);
+DEFINEFUNC2(ASN1_TIME *, X509_gmtime_adj, ASN1_TIME *s, s, long adj, adj, return nullptr, return)
+DEFINEFUNC(void, ASN1_TIME_free, ASN1_TIME *t, t, return, DUMMYARG)
DEFINEFUNC2(X509_EXTENSION *, X509_get_ext, X509 *a, a, int b, b, return nullptr, return)
DEFINEFUNC(int, X509_get_ext_count, X509 *a, a, return 0, return)
DEFINEFUNC4(void *, X509_get_ext_d2i, X509 *a, a, int b, b, int *c, c, int *d, d, return nullptr, return)
@@ -505,11 +505,7 @@ DEFINEFUNC(ASN1_OCTET_STRING *, X509_EXTENSION_get_data, X509_EXTENSION *a, a, r
DEFINEFUNC(void, BASIC_CONSTRAINTS_free, BASIC_CONSTRAINTS *a, a, return, DUMMYARG)
DEFINEFUNC(void, AUTHORITY_KEYID_free, AUTHORITY_KEYID *a, a, return, DUMMYARG)
DEFINEFUNC(void, GENERAL_NAME_free, GENERAL_NAME *a, a, return, DUMMYARG)
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
DEFINEFUNC2(int, ASN1_STRING_print, BIO *a, a, const ASN1_STRING *b, b, return 0, return)
-#else
-DEFINEFUNC2(int, ASN1_STRING_print, BIO *a, a, ASN1_STRING *b, b, return 0, return)
-#endif
DEFINEFUNC2(int, X509_check_issued, X509 *a, a, X509 *b, b, return -1, return)
DEFINEFUNC(X509_NAME *, X509_get_issuer_name, X509 *a, a, return nullptr, return)
DEFINEFUNC(X509_NAME *, X509_get_subject_name, X509 *a, a, return nullptr, return)
@@ -574,6 +570,7 @@ DEFINEFUNC2(void, BIO_clear_flags, BIO *b, b, int flags, flags, return, DUMMYARG
DEFINEFUNC2(void *, BIO_get_ex_data, BIO *b, b, int idx, idx, return nullptr, return)
DEFINEFUNC3(int, BIO_set_ex_data, BIO *b, b, int idx, idx, void *data, data, return -1, return)
+DEFINEFUNC3(void *, CRYPTO_malloc, size_t num, num, const char *file, file, int line, line, return nullptr, return)
DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC(void, DH_free, DH *dh, dh, return, DUMMYARG)
DEFINEFUNC3(DH *, d2i_DHparams, DH**a, a, const unsigned char **pp, pp, long length, length, return nullptr, return)
@@ -821,6 +818,17 @@ static QPair<QLibrary*, QLibrary*> loadOpenSsl()
// reason, we will search a few common paths (see findAllLibSsl() above) in hopes
// we find one that works.
//
+ // If that fails, for OpenSSL 1.0 we also try some fallbacks -- look up
+ // libssl.so with a hardcoded soname. The reason is QTBUG-68156: the binary
+ // builds of Qt happen (at the time of this writing) on RHEL machines,
+ // which change SHLIB_VERSION_NUMBER to a non-portable string. When running
+ // those binaries on the target systems, this code won't pick up
+ // libssl.so.MODIFIED_SHLIB_VERSION_NUMBER because it doesn't exist there.
+ // Given that the only 1.0 supported release (at the time of this writing)
+ // is 1.0.2, with soname "1.0.0", give that a try too. Note that we mandate
+ // OpenSSL >= 1.0.0 with a configure-time check, and OpenSSL has kept binary
+ // compatibility between 1.0.0 and 1.0.2.
+ //
// It is important, however, to try the canonical name and the unversioned name
// without going through the loop. By not specifying a path, we let the system
// dlopen(3) function determine it for us. This will include any DT_RUNPATH or
@@ -841,6 +849,27 @@ static QPair<QLibrary*, QLibrary*> loadOpenSsl()
libssl->unload();
libcrypto->unload();
}
+
+#if !QT_CONFIG(opensslv11)
+ // first-and-half attempts: for OpenSSL 1.0 try to load some hardcoded sonames:
+ // - "1.0.0" is the official upstream one
+ // - "1.0.2" is found on some distributions (e.g. Debian) that patch OpenSSL
+ static const QLatin1String fallbackSonames[] = {
+ QLatin1String("1.0.0"),
+ QLatin1String("1.0.2")
+ };
+
+ for (auto fallbackSoname : fallbackSonames) {
+ libssl->setFileNameAndVersion(QLatin1String("ssl"), fallbackSoname);
+ libcrypto->setFileNameAndVersion(QLatin1String("crypto"), fallbackSoname);
+ if (libcrypto->load() && libssl->load()) {
+ return pair;
+ } else {
+ libssl->unload();
+ libcrypto->unload();
+ }
+ }
+#endif
#endif
#ifndef Q_OS_DARWIN
@@ -933,6 +962,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(OPENSSL_init_crypto)
RESOLVEFUNC(ASN1_STRING_get0_data)
RESOLVEFUNC(EVP_CIPHER_CTX_reset)
+ RESOLVEFUNC(EVP_PKEY_up_ref)
RESOLVEFUNC(EVP_PKEY_base_id)
RESOLVEFUNC(RSA_bits)
RESOLVEFUNC(OPENSSL_sk_new_null)
@@ -943,6 +973,9 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(OPENSSL_sk_value)
RESOLVEFUNC(DH_get0_pqg)
RESOLVEFUNC(SSL_CTX_set_options)
+#ifdef TLS1_3_VERSION
+ RESOLVEFUNC(SSL_CTX_set_ciphersuites)
+#endif // TLS 1.3 or OpenSSL > 1.1.1
RESOLVEFUNC(SSL_get_client_random)
RESOLVEFUNC(SSL_SESSION_get_master_key)
RESOLVEFUNC(SSL_session_reused)
@@ -952,6 +985,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(TLS_method)
RESOLVEFUNC(TLS_client_method)
RESOLVEFUNC(TLS_server_method)
+ RESOLVEFUNC(X509_up_ref)
RESOLVEFUNC(X509_STORE_CTX_get0_chain)
RESOLVEFUNC(X509_getm_notBefore)
RESOLVEFUNC(X509_getm_notAfter)
@@ -987,7 +1021,30 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(BIO_meth_set_create)
RESOLVEFUNC(BIO_meth_set_destroy)
#endif // dtls
-
+#if QT_CONFIG(ocsp)
+ RESOLVEFUNC(OCSP_SINGLERESP_get0_id)
+ RESOLVEFUNC(d2i_OCSP_RESPONSE)
+ RESOLVEFUNC(OCSP_RESPONSE_free)
+ RESOLVEFUNC(OCSP_response_status)
+ RESOLVEFUNC(OCSP_response_get1_basic)
+ RESOLVEFUNC(OCSP_BASICRESP_free)
+ RESOLVEFUNC(OCSP_basic_verify)
+ RESOLVEFUNC(OCSP_resp_count)
+ RESOLVEFUNC(OCSP_resp_get0)
+ RESOLVEFUNC(OCSP_single_get0_status)
+ RESOLVEFUNC(OCSP_check_validity)
+ RESOLVEFUNC(OCSP_cert_to_id)
+ RESOLVEFUNC(OCSP_id_get0_info)
+ RESOLVEFUNC(OCSP_resp_get0_certs)
+ RESOLVEFUNC(OCSP_basic_sign)
+ RESOLVEFUNC(OCSP_response_create)
+ RESOLVEFUNC(i2d_OCSP_RESPONSE)
+ RESOLVEFUNC(OCSP_basic_add1_status)
+ RESOLVEFUNC(OCSP_BASICRESP_new)
+ RESOLVEFUNC(OCSP_CERTID_free)
+ RESOLVEFUNC(OCSP_cert_to_id)
+ RESOLVEFUNC(OCSP_id_cmp)
+#endif // ocsp
RESOLVEFUNC(BIO_set_data)
RESOLVEFUNC(BIO_get_data)
RESOLVEFUNC(BIO_set_init)
@@ -1026,24 +1083,12 @@ bool q_resolveOpenSslSymbols()
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
RESOLVEFUNC(SSL_get_ex_new_index)
#endif
-#ifndef OPENSSL_NO_SSL2
- RESOLVEFUNC(SSLv2_client_method)
-#endif
-#ifndef OPENSSL_NO_SSL3_METHOD
- RESOLVEFUNC(SSLv3_client_method)
-#endif
RESOLVEFUNC(SSLv23_client_method)
RESOLVEFUNC(TLSv1_client_method)
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
RESOLVEFUNC(TLSv1_1_client_method)
RESOLVEFUNC(TLSv1_2_client_method)
#endif
-#ifndef OPENSSL_NO_SSL2
- RESOLVEFUNC(SSLv2_server_method)
-#endif
-#ifndef OPENSSL_NO_SSL3_METHOD
- RESOLVEFUNC(SSLv3_server_method)
-#endif
RESOLVEFUNC(SSLv23_server_method)
RESOLVEFUNC(TLSv1_server_method)
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
@@ -1094,6 +1139,7 @@ bool q_resolveOpenSslSymbols()
#endif // !opensslv11
RESOLVEFUNC(ASN1_INTEGER_get)
+ RESOLVEFUNC(ASN1_INTEGER_cmp)
RESOLVEFUNC(ASN1_STRING_length)
RESOLVEFUNC(ASN1_STRING_to_UTF8)
RESOLVEFUNC(BIO_ctrl)
@@ -1130,6 +1176,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(EVP_CipherInit_ex)
RESOLVEFUNC(EVP_CipherUpdate)
RESOLVEFUNC(EVP_CipherFinal)
+ RESOLVEFUNC(EVP_get_digestbyname)
#ifndef OPENSSL_NO_DES
RESOLVEFUNC(EVP_des_cbc)
RESOLVEFUNC(EVP_des_ede3_cbc)
@@ -1137,16 +1184,24 @@ bool q_resolveOpenSslSymbols()
#ifndef OPENSSL_NO_RC2
RESOLVEFUNC(EVP_rc2_cbc)
#endif
+#ifndef OPENSSL_NO_AES
+ RESOLVEFUNC(EVP_aes_128_cbc)
+ RESOLVEFUNC(EVP_aes_192_cbc)
+ RESOLVEFUNC(EVP_aes_256_cbc)
+#endif
RESOLVEFUNC(EVP_sha1)
RESOLVEFUNC(EVP_PKEY_assign)
RESOLVEFUNC(EVP_PKEY_set1_RSA)
RESOLVEFUNC(EVP_PKEY_set1_DSA)
+ RESOLVEFUNC(EVP_PKEY_set1_DH)
#ifndef OPENSSL_NO_EC
RESOLVEFUNC(EVP_PKEY_set1_EC_KEY)
#endif
+ RESOLVEFUNC(EVP_PKEY_cmp)
RESOLVEFUNC(EVP_PKEY_free)
RESOLVEFUNC(EVP_PKEY_get1_DSA)
RESOLVEFUNC(EVP_PKEY_get1_RSA)
+ RESOLVEFUNC(EVP_PKEY_get1_DH)
#ifndef OPENSSL_NO_EC
RESOLVEFUNC(EVP_PKEY_get1_EC_KEY)
#endif
@@ -1170,6 +1225,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(PEM_read_bio_DHparams)
RESOLVEFUNC(PEM_write_bio_DSAPrivateKey)
RESOLVEFUNC(PEM_write_bio_RSAPrivateKey)
+ RESOLVEFUNC(PEM_write_bio_PrivateKey)
#ifndef OPENSSL_NO_EC
RESOLVEFUNC(PEM_write_bio_ECPrivateKey)
#endif
@@ -1183,6 +1239,7 @@ bool q_resolveOpenSslSymbols()
#endif
RESOLVEFUNC(PEM_write_bio_DSA_PUBKEY)
RESOLVEFUNC(PEM_write_bio_RSA_PUBKEY)
+ RESOLVEFUNC(PEM_write_bio_PUBKEY)
#ifndef OPENSSL_NO_EC
RESOLVEFUNC(PEM_write_bio_EC_PUBKEY)
#endif
@@ -1199,6 +1256,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_CTX_free)
RESOLVEFUNC(SSL_CTX_new)
RESOLVEFUNC(SSL_CTX_set_cipher_list)
+ RESOLVEFUNC(SSL_CTX_callback_ctrl)
RESOLVEFUNC(SSL_CTX_set_default_verify_paths)
RESOLVEFUNC(SSL_CTX_set_verify)
RESOLVEFUNC(SSL_CTX_set_verify_depth)
@@ -1228,6 +1286,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_get_peer_certificate)
RESOLVEFUNC(SSL_get_verify_result)
RESOLVEFUNC(SSL_new)
+ RESOLVEFUNC(SSL_get_SSL_CTX)
RESOLVEFUNC(SSL_ctrl)
RESOLVEFUNC(SSL_read)
RESOLVEFUNC(SSL_set_accept_state)
@@ -1275,6 +1334,8 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(X509_digest)
RESOLVEFUNC(X509_EXTENSION_get_object)
RESOLVEFUNC(X509_free)
+ RESOLVEFUNC(X509_gmtime_adj)
+ RESOLVEFUNC(ASN1_TIME_free)
RESOLVEFUNC(X509_get_ext)
RESOLVEFUNC(X509_get_ext_count)
RESOLVEFUNC(X509_get_ext_d2i)
@@ -1312,6 +1373,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(DTLS_server_method)
RESOLVEFUNC(DTLS_client_method)
#endif // dtls
+ RESOLVEFUNC(CRYPTO_malloc)
RESOLVEFUNC(DH_new)
RESOLVEFUNC(DH_free)
RESOLVEFUNC(d2i_DHparams)
diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h
index bfdfbf0efc..fcf96dbd47 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -72,6 +72,10 @@
#include "qsslsocket_openssl_p.h"
#include <QtCore/qglobal.h>
+#if QT_CONFIG(ocsp)
+#include "qocsp_p.h"
+#endif
+
QT_BEGIN_NAMESPACE
#define DUMMYARG
@@ -224,6 +228,7 @@ QT_BEGIN_NAMESPACE
bool q_resolveOpenSslSymbols();
long q_ASN1_INTEGER_get(ASN1_INTEGER *a);
+int q_ASN1_INTEGER_cmp(const ASN1_INTEGER *x, const ASN1_INTEGER *y);
int q_ASN1_STRING_length(ASN1_STRING *a);
int q_ASN1_STRING_to_UTF8(unsigned char **a, ASN1_STRING *b);
long q_BIO_ctrl(BIO *a, int b, long c, void *d);
@@ -267,6 +272,8 @@ int q_EVP_CipherInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, const unsigned
int q_EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ENGINE *impl, const unsigned char *key, const unsigned char *iv, int enc);
int q_EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl);
int q_EVP_CipherFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);
+const EVP_MD *q_EVP_get_digestbyname(const char *name);
+
#ifndef OPENSSL_NO_DES
const EVP_CIPHER *q_EVP_des_cbc();
const EVP_CIPHER *q_EVP_des_ede3_cbc();
@@ -274,16 +281,24 @@ const EVP_CIPHER *q_EVP_des_ede3_cbc();
#ifndef OPENSSL_NO_RC2
const EVP_CIPHER *q_EVP_rc2_cbc();
#endif
-const EVP_MD *q_EVP_sha1();
+#ifndef OPENSSL_NO_AES
+const EVP_CIPHER *q_EVP_aes_128_cbc();
+const EVP_CIPHER *q_EVP_aes_192_cbc();
+const EVP_CIPHER *q_EVP_aes_256_cbc();
+#endif
+Q_AUTOTEST_EXPORT const EVP_MD *q_EVP_sha1();
int q_EVP_PKEY_assign(EVP_PKEY *a, int b, char *c);
Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_RSA(EVP_PKEY *a, RSA *b);
-int q_EVP_PKEY_set1_DSA(EVP_PKEY *a, DSA *b);
+Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_DSA(EVP_PKEY *a, DSA *b);
+Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_DH(EVP_PKEY *a, DH *b);
#ifndef OPENSSL_NO_EC
-int q_EVP_PKEY_set1_EC_KEY(EVP_PKEY *a, EC_KEY *b);
+Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_EC_KEY(EVP_PKEY *a, EC_KEY *b);
#endif
-void q_EVP_PKEY_free(EVP_PKEY *a);
+Q_AUTOTEST_EXPORT int q_EVP_PKEY_cmp(const EVP_PKEY *a, const EVP_PKEY *b);
+Q_AUTOTEST_EXPORT void q_EVP_PKEY_free(EVP_PKEY *a);
RSA *q_EVP_PKEY_get1_RSA(EVP_PKEY *a);
DSA *q_EVP_PKEY_get1_DSA(EVP_PKEY *a);
+DH *q_EVP_PKEY_get1_DH(EVP_PKEY *a);
#ifndef OPENSSL_NO_EC
EC_KEY *q_EVP_PKEY_get1_EC_KEY(EVP_PKEY *a);
#endif
@@ -297,6 +312,7 @@ int q_OBJ_ln2nid(const char *s);
int q_i2t_ASN1_OBJECT(char *buf, int buf_len, ASN1_OBJECT *obj);
int q_OBJ_obj2txt(char *buf, int buf_len, ASN1_OBJECT *obj, int no_name);
int q_OBJ_obj2nid(const ASN1_OBJECT *a);
+#define q_EVP_get_digestbynid(a) q_EVP_get_digestbyname(q_OBJ_nid2sn(a))
#ifdef SSLEAY_MACROS
// ### verify
void *q_PEM_ASN1_read_bio(d2i_of_void *a, const char *b, BIO *c, void **d, pem_password_cb *e,
@@ -314,6 +330,8 @@ int q_PEM_write_bio_DSAPrivateKey(BIO *a, DSA *b, const EVP_CIPHER *c, unsigned
int e, pem_password_cb *f, void *g);
int q_PEM_write_bio_RSAPrivateKey(BIO *a, RSA *b, const EVP_CIPHER *c, unsigned char *d,
int e, pem_password_cb *f, void *g);
+int q_PEM_write_bio_PrivateKey(BIO *a, EVP_PKEY *b, const EVP_CIPHER *c, unsigned char *d,
+ int e, pem_password_cb *f, void *g);
#ifndef OPENSSL_NO_EC
int q_PEM_write_bio_ECPrivateKey(BIO *a, EC_KEY *b, const EVP_CIPHER *c, unsigned char *d,
int e, pem_password_cb *f, void *g);
@@ -327,6 +345,7 @@ EC_KEY *q_PEM_read_bio_EC_PUBKEY(BIO *a, EC_KEY **b, pem_password_cb *c, void *d
#endif
int q_PEM_write_bio_DSA_PUBKEY(BIO *a, DSA *b);
int q_PEM_write_bio_RSA_PUBKEY(BIO *a, RSA *b);
+int q_PEM_write_bio_PUBKEY(BIO *a, EVP_PKEY *b);
#ifndef OPENSSL_NO_EC
int q_PEM_write_bio_EC_PUBKEY(BIO *a, EC_KEY *b);
#endif
@@ -344,15 +363,15 @@ int q_SSL_connect(SSL *a);
int q_SSL_CTX_check_private_key(const SSL_CTX *a);
long q_SSL_CTX_ctrl(SSL_CTX *a, int b, long c, void *d);
void q_SSL_CTX_free(SSL_CTX *a);
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
SSL_CTX *q_SSL_CTX_new(const SSL_METHOD *a);
-#else
-SSL_CTX *q_SSL_CTX_new(SSL_METHOD *a);
-#endif
int q_SSL_CTX_set_cipher_list(SSL_CTX *a, const char *b);
int q_SSL_CTX_set_default_verify_paths(SSL_CTX *a);
void q_SSL_CTX_set_verify(SSL_CTX *a, int b, int (*c)(int, X509_STORE_CTX *));
void q_SSL_CTX_set_verify_depth(SSL_CTX *a, int b);
+extern "C" {
+typedef void (*GenericCallbackType)();
+}
+long q_SSL_CTX_callback_ctrl(SSL_CTX *, int, GenericCallbackType);
int q_SSL_CTX_use_certificate(SSL_CTX *a, X509 *b);
int q_SSL_CTX_use_certificate_file(SSL_CTX *a, const char *b, int c);
int q_SSL_CTX_use_PrivateKey(SSL_CTX *a, EVP_PKEY *b);
@@ -369,17 +388,14 @@ int q_SSL_CONF_cmd(SSL_CONF_CTX *a, const char *b, const char *c);
#endif
void q_SSL_free(SSL *a);
STACK_OF(SSL_CIPHER) *q_SSL_get_ciphers(const SSL *a);
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
const SSL_CIPHER *q_SSL_get_current_cipher(SSL *a);
-#else
-SSL_CIPHER *q_SSL_get_current_cipher(SSL *a);
-#endif
int q_SSL_version(const SSL *a);
int q_SSL_get_error(SSL *a, int b);
STACK_OF(X509) *q_SSL_get_peer_cert_chain(SSL *a);
X509 *q_SSL_get_peer_certificate(SSL *a);
long q_SSL_get_verify_result(const SSL *a);
SSL *q_SSL_new(SSL_CTX *a);
+SSL_CTX *q_SSL_get_SSL_CTX(SSL *a);
long q_SSL_ctrl(SSL *ssl,int cmd, long larg, void *parg);
int q_SSL_read(SSL *a, void *b, int c);
void q_SSL_set_bio(SSL *a, BIO *b, BIO *c);
@@ -414,7 +430,9 @@ X509 *q_X509_dup(X509 *a);
void q_X509_print(BIO *a, X509*b);
int q_X509_digest(const X509 *x509, const EVP_MD *type, unsigned char *md, unsigned int *len);
ASN1_OBJECT *q_X509_EXTENSION_get_object(X509_EXTENSION *a);
-void q_X509_free(X509 *a);
+Q_AUTOTEST_EXPORT void q_X509_free(X509 *a);
+Q_AUTOTEST_EXPORT ASN1_TIME *q_X509_gmtime_adj(ASN1_TIME *s, long adj);
+Q_AUTOTEST_EXPORT void q_ASN1_TIME_free(ASN1_TIME *t);
X509_EXTENSION *q_X509_get_ext(X509 *a, int b);
int q_X509_get_ext_count(X509 *a);
void *q_X509_get_ext_d2i(X509 *a, int b, int *c, int *d);
@@ -424,11 +442,7 @@ int q_X509_EXTENSION_get_critical(X509_EXTENSION *a);
ASN1_OCTET_STRING *q_X509_EXTENSION_get_data(X509_EXTENSION *a);
void q_BASIC_CONSTRAINTS_free(BASIC_CONSTRAINTS *a);
void q_AUTHORITY_KEYID_free(AUTHORITY_KEYID *a);
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
int q_ASN1_STRING_print(BIO *a, const ASN1_STRING *b);
-#else
-int q_ASN1_STRING_print(BIO *a, ASN1_STRING *b);
-#endif
int q_X509_check_issued(X509 *a, X509 *b);
X509_NAME *q_X509_get_issuer_name(X509 *a);
X509_NAME *q_X509_get_subject_name(X509 *a);
@@ -571,6 +585,57 @@ int q_BIO_set_ex_data(BIO *b, int idx, void *data);
class QDateTime;
QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime);
+#ifndef OPENSSL_NO_TLSEXT
+
+#define q_SSL_set_tlsext_status_type(ssl, type) \
+ q_SSL_ctrl((ssl), SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE, (type), nullptr)
+
+#endif // OPENSSL_NO_TLSEXT
+
+#if QT_CONFIG(ocsp)
+
+OCSP_RESPONSE *q_d2i_OCSP_RESPONSE(OCSP_RESPONSE **a, const unsigned char **in, long len);
+Q_AUTOTEST_EXPORT int q_i2d_OCSP_RESPONSE(OCSP_RESPONSE *r, unsigned char **ppout);
+Q_AUTOTEST_EXPORT OCSP_RESPONSE *q_OCSP_response_create(int status, OCSP_BASICRESP *bs);
+Q_AUTOTEST_EXPORT void q_OCSP_RESPONSE_free(OCSP_RESPONSE *rs);
+int q_OCSP_response_status(OCSP_RESPONSE *resp);
+OCSP_BASICRESP *q_OCSP_response_get1_basic(OCSP_RESPONSE *resp);
+Q_AUTOTEST_EXPORT OCSP_SINGLERESP *q_OCSP_basic_add1_status(OCSP_BASICRESP *rsp, OCSP_CERTID *cid,
+ int status, int reason, ASN1_TIME *revtime,
+ ASN1_TIME *thisupd, ASN1_TIME *nextupd);
+Q_AUTOTEST_EXPORT int q_OCSP_basic_sign(OCSP_BASICRESP *brsp, X509 *signer, EVP_PKEY *key, const EVP_MD *dgst,
+ STACK_OF(X509) *certs, unsigned long flags);
+Q_AUTOTEST_EXPORT OCSP_BASICRESP *q_OCSP_BASICRESP_new();
+Q_AUTOTEST_EXPORT void q_OCSP_BASICRESP_free(OCSP_BASICRESP *bs);
+int q_OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs, X509_STORE *st, unsigned long flags);
+int q_OCSP_resp_count(OCSP_BASICRESP *bs);
+OCSP_SINGLERESP *q_OCSP_resp_get0(OCSP_BASICRESP *bs, int idx);
+int q_OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason, ASN1_GENERALIZEDTIME **revtime,
+ ASN1_GENERALIZEDTIME **thisupd, ASN1_GENERALIZEDTIME **nextupd);
+int q_OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd, long nsec, long maxsec);
+int q_OCSP_id_get0_info(ASN1_OCTET_STRING **piNameHash, ASN1_OBJECT **pmd, ASN1_OCTET_STRING **pikeyHash,
+ ASN1_INTEGER **pserial, OCSP_CERTID *cid);
+
+const STACK_OF(X509) *q_OCSP_resp_get0_certs(const OCSP_BASICRESP *bs);
+Q_AUTOTEST_EXPORT OCSP_CERTID *q_OCSP_cert_to_id(const EVP_MD *dgst, X509 *subject, X509 *issuer);
+Q_AUTOTEST_EXPORT void q_OCSP_CERTID_free(OCSP_CERTID *cid);
+int q_OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b);
+
+#define q_SSL_get_tlsext_status_ocsp_resp(ssl, arg) \
+ q_SSL_ctrl(ssl, SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP, 0, arg)
+
+#define q_SSL_CTX_set_tlsext_status_cb(ssl, cb) \
+ q_SSL_CTX_callback_ctrl(ssl, SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB, GenericCallbackType(cb))
+
+# define q_SSL_set_tlsext_status_ocsp_resp(ssl, arg, arglen) \
+ q_SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP, arglen, arg)
+
+#endif // ocsp
+
+
+void *q_CRYPTO_malloc(size_t num, const char *file, int line);
+#define q_OPENSSL_malloc(num) q_CRYPTO_malloc(num, "", 0)
+
QT_END_NAMESPACE
#endif
diff --git a/src/network/ssl/qsslsocket_opensslpre11.cpp b/src/network/ssl/qsslsocket_opensslpre11.cpp
index 062e03f4e6..f5aab821ea 100644
--- a/src/network/ssl/qsslsocket_opensslpre11.cpp
+++ b/src/network/ssl/qsslsocket_opensslpre11.cpp
@@ -215,8 +215,6 @@ bool QSslSocketPrivate::ensureLibraryLoaded()
QMutexLocker locker(openssl_locks()->initLock());
if (!s_libraryLoaded) {
- s_libraryLoaded = true;
-
// Initialize OpenSSL.
q_CRYPTO_set_id_callback(id_function);
q_CRYPTO_set_locking_callback(locking_function);
@@ -235,6 +233,8 @@ bool QSslSocketPrivate::ensureLibraryLoaded()
qWarning("Random number generator not seeded, disabling SSL support");
return false;
}
+
+ s_libraryLoaded = true;
}
return true;
}
@@ -251,21 +251,7 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded()
#if QT_CONFIG(library)
//load symbols needed to receive certificates from system store
-#if defined(Q_OS_WIN)
- HINSTANCE hLib = LoadLibraryW(L"Crypt32");
- if (hLib) {
- ptrCertOpenSystemStoreW = reinterpret_cast<PtrCertOpenSystemStoreW>(
- reinterpret_cast<QFunctionPointer>(GetProcAddress(hLib, "CertOpenSystemStoreW")));
- ptrCertFindCertificateInStore = reinterpret_cast<PtrCertFindCertificateInStore>(
- reinterpret_cast<QFunctionPointer>(GetProcAddress(hLib, "CertFindCertificateInStore")));
- ptrCertCloseStore = reinterpret_cast<PtrCertCloseStore>(
- reinterpret_cast<QFunctionPointer>(GetProcAddress(hLib, "CertCloseStore")));
- if (!ptrCertOpenSystemStoreW || !ptrCertFindCertificateInStore || !ptrCertCloseStore)
- qCWarning(lcSsl, "could not resolve symbols in crypt32 library"); // should never happen
- } else {
- qCWarning(lcSsl, "could not load crypt32 library"); // should never happen
- }
-#elif defined(Q_OS_QNX)
+#if defined(Q_OS_QNX)
s_loadRootCertsOnDemand = true;
#elif defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
// check whether we can enable on-demand root-cert loading (i.e. check whether the sym links are there)
diff --git a/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h b/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h
index b7bac5d2a2..46b6505346 100644
--- a/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h
+++ b/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h
@@ -89,9 +89,7 @@ void q_ERR_free_strings();
void q_EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *a);
void q_EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *a);
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
typedef _STACK STACK;
-#endif
// The typedef we use to make our pre 1.1 code look more like 1.1 (less ifdefs).
typedef STACK OPENSSL_STACK;
@@ -111,22 +109,13 @@ void q_sk_free(STACK *a);
// address of this:
#define q_OPENSSL_sk_free q_sk_free
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
void *q_sk_value(STACK *a, int b);
void q_sk_push(STACK *st, void *data);
-#else
-char *q_sk_value(STACK *a, int b);
-void q_sk_push(STACK *st, char *data);
-#endif // OPENSSL_VERSION_NUMBER >= 0x10000000L
#define q_OPENSSL_sk_value(a, b) q_sk_value(a, b)
#define q_OPENSSL_sk_push(st, data) q_sk_push(st, data)
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
SSL_CTX *q_SSL_CTX_new(const SSL_METHOD *a);
-#else
-SSL_CTX *q_SSL_CTX_new(SSL_METHOD *a);
-#endif
int q_SSL_library_init();
void q_SSL_load_error_strings();
@@ -135,49 +124,14 @@ void q_SSL_load_error_strings();
int q_SSL_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func);
#endif
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
-#ifndef OPENSSL_NO_SSL2
-const SSL_METHOD *q_SSLv2_client_method();
-#endif
-#ifndef OPENSSL_NO_SSL3_METHOD
-const SSL_METHOD *q_SSLv3_client_method();
-#endif
const SSL_METHOD *q_SSLv23_client_method();
const SSL_METHOD *q_TLSv1_client_method();
const SSL_METHOD *q_TLSv1_1_client_method();
const SSL_METHOD *q_TLSv1_2_client_method();
-#ifndef OPENSSL_NO_SSL2
-const SSL_METHOD *q_SSLv2_server_method();
-#endif
-#ifndef OPENSSL_NO_SSL3_METHOD
-const SSL_METHOD *q_SSLv3_server_method();
-#endif
const SSL_METHOD *q_SSLv23_server_method();
const SSL_METHOD *q_TLSv1_server_method();
const SSL_METHOD *q_TLSv1_1_server_method();
const SSL_METHOD *q_TLSv1_2_server_method();
-#else
-#ifndef OPENSSL_NO_SSL2
-SSL_METHOD *q_SSLv2_client_method();
-#endif
-#ifndef OPENSSL_NO_SSL3_METHOD
-SSL_METHOD *q_SSLv3_client_method();
-#endif
-SSL_METHOD *q_SSLv23_client_method();
-SSL_METHOD *q_TLSv1_client_method();
-SSL_METHOD *q_TLSv1_1_client_method();
-SSL_METHOD *q_TLSv1_2_client_method();
-#ifndef OPENSSL_NO_SSL2
-SSL_METHOD *q_SSLv2_server_method();
-#endif
-#ifndef OPENSSL_NO_SSL3_METHOD
-SSL_METHOD *q_SSLv3_server_method();
-#endif
-SSL_METHOD *q_SSLv23_server_method();
-SSL_METHOD *q_TLSv1_server_method();
-SSL_METHOD *q_TLSv1_1_server_method();
-SSL_METHOD *q_TLSv1_2_server_method();
-#endif
STACK_OF(X509) *q_X509_STORE_CTX_get_chain(X509_STORE_CTX *ctx);
@@ -218,6 +172,7 @@ DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length);
#define q_SSL_SESSION_get_ticket_lifetime_hint(s) ((s)->tlsext_tick_lifetime_hint)
#define q_RSA_bits(rsa) q_BN_num_bits((rsa)->n)
#define q_DSA_bits(dsa) q_BN_num_bits((dsa)->p)
+#define q_DH_bits(dsa) q_BN_num_bits((dh)->p)
#define q_X509_STORE_set_verify_cb(s,c) X509_STORE_set_verify_cb_func((s),(c))
char *q_CONF_get1_default_config_file();
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
index c27496ec02..daa9be23f4 100644
--- a/src/network/ssl/qsslsocket_p.h
+++ b/src/network/ssl/qsslsocket_p.h
@@ -58,6 +58,7 @@
#include <private/qtcpsocket_p.h>
#include "qsslkey.h"
#include "qsslconfiguration_p.h"
+#include "qocspresponse.h"
#ifndef QT_NO_OPENSSL
#include <private/qsslcontext_openssl_p.h>
#else
@@ -65,7 +66,7 @@ class QSslContext;
#endif
#include <QtCore/qstringlist.h>
-
+#include <QtCore/qvector.h>
#include <private/qringbuffer_p.h>
#if defined(Q_OS_MAC)
@@ -89,14 +90,6 @@ QT_BEGIN_NAMESPACE
typedef OSStatus (*PtrSecTrustCopyAnchorCertificates)(CFArrayRef*);
#endif
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
- typedef HCERTSTORE (WINAPI *PtrCertOpenSystemStoreW)(HCRYPTPROV_LEGACY, LPCWSTR);
- typedef PCCERT_CONTEXT (WINAPI *PtrCertFindCertificateInStore)(HCERTSTORE, DWORD, DWORD, DWORD, const void*, PCCERT_CONTEXT);
- typedef BOOL (WINAPI *PtrCertCloseStore)(HCERTSTORE, DWORD);
-#endif // Q_OS_WIN && !Q_OS_WINRT
-
-
-
class QSslSocketPrivate : public QTcpSocketPrivate
{
Q_DECLARE_PUBLIC(QSslSocket)
@@ -105,6 +98,7 @@ public:
virtual ~QSslSocketPrivate();
void init();
+ bool verifyProtocolSupported(const char *where);
bool initialized;
QSslSocket::SslMode mode;
@@ -155,12 +149,6 @@ public:
const QString &peerName);
Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname);
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
- static PtrCertOpenSystemStoreW ptrCertOpenSystemStoreW;
- static PtrCertFindCertificateInStore ptrCertFindCertificateInStore;
- static PtrCertCloseStore ptrCertCloseStore;
-#endif // Q_OS_WIN && !Q_OS_WINRT
-
// The socket itself, including private slots.
QTcpSocket *plainSocket;
void createPlainSocket(QIODevice::OpenMode openMode);
@@ -184,7 +172,7 @@ public:
void _q_flushWriteBuffer();
void _q_flushReadBuffer();
void _q_resumeImplementation();
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) && !QT_CONFIG(schannel)
virtual void _q_caRootLoaded(QSslCertificate,QSslCertificate) = 0;
#endif
@@ -220,9 +208,10 @@ protected:
bool verifyErrorsHaveBeenIgnored();
bool paused;
bool flushTriggered;
+ QVector<QOcspResponse> ocspResponses;
};
-#if QT_CONFIG(securetransport)
+#if QT_CONFIG(securetransport) || QT_CONFIG(schannel)
// Implemented in qsslsocket_qt.cpp
QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase);
#endif
diff --git a/src/network/ssl/qsslsocket_qt.cpp b/src/network/ssl/qsslsocket_qt.cpp
index 2c78f0f4e7..b0fb60ea76 100644
--- a/src/network/ssl/qsslsocket_qt.cpp
+++ b/src/network/ssl/qsslsocket_qt.cpp
@@ -92,14 +92,12 @@ static QByteArray _q_PKCS12_keygen(char id, const QByteArray &salt, const QStrin
QByteArray S, P;
const int sSize = v * ((salt.size() + v - 1) / v);
S.resize(sSize);
- for (int i = 0; i < sSize; ++i) {
+ for (int i = 0; i < sSize; ++i)
S[i] = salt[i % salt.size()];
- }
const int pSize = v * ((passUnicode.size() + v - 1) / v);
P.resize(pSize);
- for (int i = 0; i < pSize; ++i) {
+ for (int i = 0; i < pSize; ++i)
P[i] = passUnicode[i % passUnicode.size()];
- }
QByteArray I = S + P;
// apply hashing
@@ -117,16 +115,15 @@ static QByteArray _q_PKCS12_keygen(char id, const QByteArray &salt, const QStrin
Ai = hash.result();
}
- for (int j = 0; j < v; ++j) {
+ for (int j = 0; j < v; ++j)
B[j] = Ai[j % u];
- }
// modify I as Ij = (Ij + B + 1) modulo 2^v
for (int p = 0; p < I.size(); p += v) {
quint8 carry = 1;
for (int j = v - 1; j >= 0; --j) {
- quint16 v = quint8(I[p+j]) + quint8(B[j]) + carry;
- I[p+j] = v & 0xff;
+ quint16 v = quint8(I[p + j]) + quint8(B[j]) + carry;
+ I[p + j] = v & 0xff;
carry = (v & 0xff00) >> 8;
}
}
@@ -139,9 +136,8 @@ static QByteArray _q_PKCS12_salt()
{
QByteArray salt;
salt.resize(8);
- for (int i = 0; i < salt.size(); ++i) {
+ for (int i = 0; i < salt.size(); ++i)
salt[i] = (qrand() & 0xff);
- }
return salt;
}
@@ -203,7 +199,7 @@ static QByteArray _q_PKCS12_shroudedKeyBag(const QSslKey &key, const QString &pa
QDataStream plainStream(&plain, QIODevice::WriteOnly);
_q_PKCS12_key(key).write(plainStream);
QByteArray crypted = QSslKeyPrivate::encrypt(QSslKeyPrivate::DesEde3Cbc,
- plain, cKey, cIv);
+ plain, cKey, cIv);
QVector<QAsn1Element> items;
items << QAsn1Element::fromObjectId("1.2.840.113549.1.12.10.1.2");
@@ -246,8 +242,10 @@ static QByteArray _q_PKCS12_bag(const QList<QSslCertificate> &certs, const QSslK
items << _q_PKCS7_data(_q_PKCS12_certBag(certs[i]));
// key
- const QByteArray localKeyId = certs.first().digest(QCryptographicHash::Sha1);
- items << _q_PKCS7_data(_q_PKCS12_shroudedKeyBag(key, passPhrase, localKeyId));
+ if (!key.isNull()) {
+ const QByteArray localKeyId = certs.first().digest(QCryptographicHash::Sha1);
+ items << _q_PKCS7_data(_q_PKCS12_shroudedKeyBag(key, passPhrase, localKeyId));
+ }
// dump
QAsn1Element root = QAsn1Element::fromVector(items);
diff --git a/src/network/ssl/qsslsocket_schannel.cpp b/src/network/ssl/qsslsocket_schannel.cpp
new file mode 100644
index 0000000000..1314b432a4
--- /dev/null
+++ b/src/network/ssl/qsslsocket_schannel.cpp
@@ -0,0 +1,1994 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// #define QSSLSOCKET_DEBUG
+
+#include "qssl_p.h"
+#include "qsslsocket.h"
+#include "qsslsocket_schannel_p.h"
+#include "qsslcertificate.h"
+#include "qsslcertificateextension.h"
+#include "qsslcertificate_p.h"
+#include "qsslcipher_p.h"
+
+#include <QtCore/qscopeguard.h>
+#include <QtCore/qoperatingsystemversion.h>
+#include <QtCore/qregularexpression.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/qmutex.h>
+
+#define SECURITY_WIN32
+#include <security.h>
+#include <schnlsp.h>
+
+#if NTDDI_VERSION >= NTDDI_WINBLUE && !defined(Q_CC_MINGW)
+// ALPN = Application Layer Protocol Negotiation
+#define SUPPORTS_ALPN 1
+#endif
+
+// Not defined in MinGW
+#ifndef SECBUFFER_ALERT
+#define SECBUFFER_ALERT 17
+#endif
+#ifndef SECPKG_ATTR_APPLICATION_PROTOCOL
+#define SECPKG_ATTR_APPLICATION_PROTOCOL 35
+#endif
+
+// Another missing MinGW define
+#ifndef SEC_E_APPLICATION_PROTOCOL_MISMATCH
+#define SEC_E_APPLICATION_PROTOCOL_MISMATCH _HRESULT_TYPEDEF_(0x80090367L)
+#endif
+
+// Also not defined in MinGW.......
+#ifndef SP_PROT_TLS1_SERVER
+#define SP_PROT_TLS1_SERVER 0x00000040
+#endif
+#ifndef SP_PROT_TLS1_CLIENT
+#define SP_PROT_TLS1_CLIENT 0x00000080
+#endif
+#ifndef SP_PROT_TLS1_0_SERVER
+#define SP_PROT_TLS1_0_SERVER SP_PROT_TLS1_SERVER
+#endif
+#ifndef SP_PROT_TLS1_0_CLIENT
+#define SP_PROT_TLS1_0_CLIENT SP_PROT_TLS1_CLIENT
+#endif
+#ifndef SP_PROT_TLS1_0
+#define SP_PROT_TLS1_0 (SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_0_SERVER)
+#endif
+#ifndef SP_PROT_TLS1_1_SERVER
+#define SP_PROT_TLS1_1_SERVER 0x00000100
+#endif
+#ifndef SP_PROT_TLS1_1_CLIENT
+#define SP_PROT_TLS1_1_CLIENT 0x00000200
+#endif
+#ifndef SP_PROT_TLS1_1
+#define SP_PROT_TLS1_1 (SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_1_SERVER)
+#endif
+#ifndef SP_PROT_TLS1_2_SERVER
+#define SP_PROT_TLS1_2_SERVER 0x00000400
+#endif
+#ifndef SP_PROT_TLS1_2_CLIENT
+#define SP_PROT_TLS1_2_CLIENT 0x00000800
+#endif
+#ifndef SP_PROT_TLS1_2
+#define SP_PROT_TLS1_2 (SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_2_SERVER)
+#endif
+#ifndef SP_PROT_TLS1_3_SERVER
+#define SP_PROT_TLS1_3_SERVER 0x00001000
+#endif
+#ifndef SP_PROT_TLS1_3_CLIENT
+#define SP_PROT_TLS1_3_CLIENT 0x00002000
+#endif
+#ifndef SP_PROT_TLS1_3
+#define SP_PROT_TLS1_3 (SP_PROT_TLS1_3_CLIENT | SP_PROT_TLS1_3_SERVER)
+#endif
+
+/*
+ @future!:
+
+ - Transmitting intermediate certificates
+ - Look for a way to avoid putting intermediate certificates in the certificate store
+ - No documentation on how to send the chain
+ - A stackoverflow question on this from 3 years ago implies schannel only sends intermediate
+ certificates if it's "in the system or user certificate store".
+ - https://stackoverflow.com/q/30156584/2493610
+ - This can be done by users, but we shouldn't add any and all local intermediate
+ certs to the stores automatically.
+ - PSK support
+ - Was added in Windows 10 (it seems), documentation at time of writing is sparse/non-existent.
+ - Specifically about how to supply credentials when they're requested.
+ - Or how to recognize that they're requested in the first place.
+ - Skip certificate verification.
+ - Check if "PSK-only" is still required to do PSK _at all_ (all-around bad solution).
+ - Check if SEC_I_INCOMPLETE_CREDENTIALS is still returned for both "missing certificate" and
+ "missing PSK" when calling InitializeSecurityContext in "performHandshake".
+
+ Medium priority:
+ - Setting cipher-suites (or ALG_ID)
+ - People have survived without it in WinRT
+
+ Low priority:
+ - Possibly make RAII wrappers for SecBuffer (which I commonly create QScopeGuards for)
+
+*/
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+SecBuffer createSecBuffer(void *ptr, unsigned long length, unsigned long bufferType)
+{
+ return SecBuffer{ length, bufferType, ptr };
+}
+
+SecBuffer createSecBuffer(QByteArray &buffer, unsigned long bufferType)
+{
+ return createSecBuffer(buffer.data(), static_cast<unsigned long>(buffer.length()), bufferType);
+}
+
+QString schannelErrorToString(qint32 status)
+{
+ switch (status) {
+ case SEC_E_INSUFFICIENT_MEMORY:
+ return QSslSocket::tr("Insufficient memory");
+ case SEC_E_INTERNAL_ERROR:
+ return QSslSocket::tr("Internal error");
+ case SEC_E_INVALID_HANDLE:
+ return QSslSocket::tr("An internal handle was invalid");
+ case SEC_E_INVALID_TOKEN:
+ return QSslSocket::tr("An internal token was invalid");
+ case SEC_E_LOGON_DENIED:
+ // According to the link below we get this error when Schannel receives TLS1_ALERT_ACCESS_DENIED
+ // https://docs.microsoft.com/en-us/windows/desktop/secauthn/schannel-error-codes-for-tls-and-ssl-alerts
+ return QSslSocket::tr("Access denied");
+ case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+ return QSslSocket::tr("No authority could be contacted for authorization");
+ case SEC_E_NO_CREDENTIALS:
+ return QSslSocket::tr("No credentials");
+ case SEC_E_TARGET_UNKNOWN:
+ return QSslSocket::tr("The target is unknown or unreachable");
+ case SEC_E_UNSUPPORTED_FUNCTION:
+ return QSslSocket::tr("An unsupported function was requested");
+ case SEC_E_WRONG_PRINCIPAL:
+ // SNI error
+ return QSslSocket::tr("The hostname provided does not match the one received from the peer");
+ case SEC_E_APPLICATION_PROTOCOL_MISMATCH:
+ return QSslSocket::tr("No common protocol exists between the client and the server");
+ case SEC_E_ILLEGAL_MESSAGE:
+ return QSslSocket::tr("Unexpected or badly-formatted message received");
+ case SEC_E_ENCRYPT_FAILURE:
+ return QSslSocket::tr("The data could not be encrypted");
+ case SEC_E_ALGORITHM_MISMATCH:
+ return QSslSocket::tr("No cipher suites in common");
+ case SEC_E_UNKNOWN_CREDENTIALS:
+ // This can mean "invalid argument" in some cases...
+ return QSslSocket::tr("The credentials were not recognized / Invalid argument");
+ case SEC_E_MESSAGE_ALTERED:
+ // According to the Internet it also triggers for messages that are out of order.
+ // https://microsoft.public.platformsdk.security.narkive.com/4JAvlMvD/help-please-schannel-security-contexts-and-decryptmessage
+ return QSslSocket::tr("The message was tampered with, damaged or out of sequence.");
+ case SEC_E_OUT_OF_SEQUENCE:
+ return QSslSocket::tr("A message was received out of sequence.");
+ case SEC_E_CONTEXT_EXPIRED:
+ return QSslSocket::tr("The TLS/SSL connection has been closed");
+ default:
+ return QSslSocket::tr("Unknown error occurred: %1").arg(status);
+ }
+}
+
+DWORD toSchannelProtocol(QSsl::SslProtocol protocol)
+{
+ DWORD protocols = SP_PROT_NONE;
+ switch (protocol) {
+ case QSsl::UnknownProtocol:
+ return DWORD(-1);
+ case QSsl::DtlsV1_0:
+ case QSsl::DtlsV1_2:
+ case QSsl::DtlsV1_0OrLater:
+ case QSsl::DtlsV1_2OrLater:
+ return DWORD(-1); // Not supported at the moment (@future)
+ case QSsl::AnyProtocol:
+ protocols = SP_PROT_TLS1_0 | SP_PROT_TLS1_1 | SP_PROT_TLS1_2;
+ // @future Add TLS 1.3 when supported by Windows!
+ break;
+ case QSsl::SslV2:
+ case QSsl::SslV3:
+ return DWORD(-1); // Not supported
+ case QSsl::TlsV1SslV3:
+ protocols = SP_PROT_TLS1_0;
+ break;
+ case QSsl::TlsV1_0:
+ protocols = SP_PROT_TLS1_0;
+ break;
+ case QSsl::TlsV1_1:
+ protocols = SP_PROT_TLS1_1;
+ break;
+ case QSsl::TlsV1_2:
+ protocols = SP_PROT_TLS1_2;
+ break;
+ case QSsl::TlsV1_3:
+ if ((false)) // @future[0/1] Replace with version check once it's supported in Windows
+ protocols = SP_PROT_TLS1_3;
+ else
+ protocols = DWORD(-1);
+ break;
+ case QSsl::SecureProtocols: // TLS v1.0 and later is currently considered secure
+ case QSsl::TlsV1_0OrLater:
+ // For the "OrLater" protocols we fall through from one to the next, adding all of them
+ // in ascending order
+ protocols = SP_PROT_TLS1_0;
+ Q_FALLTHROUGH();
+ case QSsl::TlsV1_1OrLater:
+ protocols |= SP_PROT_TLS1_1;
+ Q_FALLTHROUGH();
+ case QSsl::TlsV1_2OrLater:
+ protocols |= SP_PROT_TLS1_2;
+ Q_FALLTHROUGH();
+ case QSsl::TlsV1_3OrLater:
+ if ((false)) // @future[1/1] Also replace this with a version check
+ protocols |= SP_PROT_TLS1_3;
+ else if (protocol == QSsl::TlsV1_3OrLater)
+ protocols = DWORD(-1); // if TlsV1_3OrLater was specifically chosen we should fail
+ break;
+ }
+ return protocols;
+}
+
+/*!
+ \internal
+ Used when converting the established session's \a protocol back to
+ Qt's own SslProtocol type.
+
+ Only one protocol should be passed in at a time.
+*/
+QSsl::SslProtocol toQtSslProtocol(DWORD protocol)
+{
+#define MAP_PROTOCOL(sp_protocol, q_protocol) \
+ if (protocol & sp_protocol) { \
+ Q_ASSERT(!(protocol & ~sp_protocol)); \
+ return q_protocol; \
+ }
+
+ MAP_PROTOCOL(SP_PROT_TLS1_0, QSsl::TlsV1_0)
+ MAP_PROTOCOL(SP_PROT_TLS1_1, QSsl::TlsV1_1)
+ MAP_PROTOCOL(SP_PROT_TLS1_2, QSsl::TlsV1_2)
+ MAP_PROTOCOL(SP_PROT_TLS1_3, QSsl::TlsV1_3)
+#undef MAP_PROTOCOL
+ Q_UNREACHABLE();
+ return QSsl::UnknownProtocol;
+}
+
+/*!
+ \internal
+ Used by verifyCertContext to check if a client cert is used by a server or vice versa.
+*/
+bool netscapeWrongCertType(const QList<QSslCertificateExtension> &extensions, bool isClient)
+{
+ const auto netscapeIt = std::find_if(
+ extensions.cbegin(), extensions.cend(),
+ [](const QSslCertificateExtension &extension) {
+ const auto netscapeCertType = QStringLiteral("2.16.840.1.113730.1.1");
+ return extension.oid() == netscapeCertType;
+ });
+ if (netscapeIt != extensions.cend()) {
+ const QByteArray netscapeCertTypeByte = netscapeIt->value().toByteArray();
+ int netscapeCertType = 0;
+ QDataStream dataStream(netscapeCertTypeByte);
+ dataStream >> netscapeCertType;
+ if (dataStream.status() != QDataStream::Status::Ok)
+ return true;
+ const int expectedPeerCertType = isClient ? NETSCAPE_SSL_SERVER_AUTH_CERT_TYPE
+ : NETSCAPE_SSL_CLIENT_AUTH_CERT_TYPE;
+ if ((netscapeCertType & expectedPeerCertType) == 0)
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \internal
+ Used by verifyCertContext to check the basicConstraints certificate
+ extension to see if the certificate is a certificate authority.
+ Returns false if the certificate does not have the basicConstraints
+ extension or if it is not a certificate authority.
+*/
+bool isCertificateAuthority(const QList<QSslCertificateExtension> &extensions)
+{
+ auto it = std::find_if(extensions.cbegin(), extensions.cend(),
+ [](const QSslCertificateExtension &extension) {
+ return extension.name() == QLatin1String("basicConstraints");
+ });
+ if (it != extensions.cend()) {
+ QVariantMap basicConstraints = it->value().toMap();
+ return basicConstraints.value(QLatin1String("ca"), false).toBool();
+ }
+ return false;
+}
+
+/*!
+ \internal
+ Returns true if the attributes we requested from the context/handshake have
+ been given.
+*/
+bool matchesContextRequirements(DWORD attributes, DWORD requirements,
+ QSslSocket::PeerVerifyMode verifyMode,
+ bool isClient)
+{
+#ifdef QSSLSOCKET_DEBUG
+#define DEBUG_WARN(message) qCWarning(lcSsl, message)
+#else
+#define DEBUG_WARN(message)
+#endif
+
+#define CHECK_ATTRIBUTE(attributeName) \
+ do { \
+ const DWORD req##attributeName = isClient ? ISC_REQ_##attributeName : ASC_REQ_##attributeName; \
+ const DWORD ret##attributeName = isClient ? ISC_RET_##attributeName : ASC_RET_##attributeName; \
+ if (!(requirements & req##attributeName) != !(attributes & ret##attributeName)) { \
+ DEBUG_WARN("Missing attribute \"" #attributeName "\""); \
+ return false; \
+ } \
+ } while (false)
+
+ CHECK_ATTRIBUTE(CONFIDENTIALITY);
+ CHECK_ATTRIBUTE(REPLAY_DETECT);
+ CHECK_ATTRIBUTE(SEQUENCE_DETECT);
+ CHECK_ATTRIBUTE(STREAM);
+ if (verifyMode == QSslSocket::PeerVerifyMode::VerifyPeer)
+ CHECK_ATTRIBUTE(MUTUAL_AUTH);
+
+ // This one is manual because there is no server / ASC_ version
+ if (isClient) {
+ const auto reqManualCredValidation = ISC_REQ_MANUAL_CRED_VALIDATION;
+ const auto retManualCredValidation = ISC_RET_MANUAL_CRED_VALIDATION;
+ if (!(requirements & reqManualCredValidation) != !(attributes & retManualCredValidation)) {
+ DEBUG_WARN("Missing attribute \"MANUAL_CRED_VALIDATION\"");
+ return false;
+ }
+ }
+
+ return true;
+#undef CHECK_ATTRIBUTE
+#undef DEBUG_WARN
+}
+
+template<typename Required, typename Actual>
+Required const_reinterpret_cast(Actual *p)
+{
+ return Required(p);
+}
+
+#ifdef SUPPORTS_ALPN
+bool supportsAlpn()
+{
+ return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8_1;
+}
+
+QByteArray createAlpnString(const QByteArrayList &nextAllowedProtocols)
+{
+ QByteArray alpnString;
+ if (!nextAllowedProtocols.isEmpty() && supportsAlpn()) {
+ const QByteArray names = [&nextAllowedProtocols]() {
+ QByteArray protocolString;
+ for (QByteArray proto : nextAllowedProtocols) {
+ if (proto.size() > 255) {
+ qCWarning(lcSsl) << "TLS ALPN extension" << proto
+ << "is too long and will be truncated to 255 characters.";
+ proto = proto.left(255);
+ }
+ protocolString += char(proto.length()) + proto;
+ }
+ return protocolString;
+ }();
+
+ const quint16 namesSize = names.size();
+ const quint32 alpnId = SecApplicationProtocolNegotiationExt_ALPN;
+ const quint32 totalSize = sizeof(alpnId) + sizeof(namesSize) + namesSize;
+ alpnString = QByteArray::fromRawData(reinterpret_cast<const char *>(&totalSize), sizeof(totalSize))
+ + QByteArray::fromRawData(reinterpret_cast<const char *>(&alpnId), sizeof(alpnId))
+ + QByteArray::fromRawData(reinterpret_cast<const char *>(&namesSize), sizeof(namesSize))
+ + names;
+ }
+ return alpnString;
+}
+#endif // SUPPORTS_ALPN
+} // anonymous namespace
+
+bool QSslSocketPrivate::s_loadRootCertsOnDemand = true;
+bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
+Q_GLOBAL_STATIC_WITH_ARGS(QMutex, qt_schannel_mutex, (QMutex::Recursive))
+
+void QSslSocketPrivate::ensureInitialized()
+{
+ const QMutexLocker locker(qt_schannel_mutex);
+ if (s_loadedCiphersAndCerts)
+ return;
+ s_loadedCiphersAndCerts = true;
+
+ setDefaultCaCertificates(systemCaCertificates());
+ s_loadRootCertsOnDemand = true; // setDefaultCaCertificates sets it to false, re-enable it.
+
+ resetDefaultCiphers();
+}
+
+void QSslSocketPrivate::resetDefaultCiphers()
+{
+ setDefaultSupportedCiphers(QSslSocketBackendPrivate::defaultCiphers());
+ setDefaultCiphers(QSslSocketBackendPrivate::defaultCiphers());
+}
+
+void QSslSocketPrivate::resetDefaultEllipticCurves()
+{
+ Q_UNIMPLEMENTED();
+}
+
+bool QSslSocketPrivate::supportsSsl()
+{
+ return true;
+}
+
+QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
+{
+ // Copied from qsslsocket_openssl.cpp's systemCaCertificates function.
+ QList<QSslCertificate> systemCerts;
+ auto hSystemStore = QHCertStorePointer(CertOpenSystemStore(0, L"ROOT"));
+ if (hSystemStore) {
+ PCCERT_CONTEXT pc = nullptr;
+ while ((pc = CertFindCertificateInStore(hSystemStore.get(), X509_ASN_ENCODING, 0,
+ CERT_FIND_ANY, nullptr, pc))) {
+ systemCerts.append(QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(pc));
+ }
+ }
+ return systemCerts;
+}
+
+long QSslSocketPrivate::sslLibraryVersionNumber()
+{
+ const auto os = QOperatingSystemVersion::current();
+ return (os.majorVersion() << 24) | ((os.minorVersion() & 0xFF) << 16) | (os.microVersion() & 0xFFFF);
+}
+
+QString QSslSocketPrivate::sslLibraryVersionString()
+{
+ const auto os = QOperatingSystemVersion::current();
+ return QString::fromLatin1("Secure Channel, %1 %2.%3.%4")
+ .arg(os.name(),
+ QString::number(os.majorVersion()),
+ QString::number(os.minorVersion()),
+ QString::number(os.microVersion()));
+}
+
+long QSslSocketPrivate::sslLibraryBuildVersionNumber()
+{
+ // There is no separate build version
+ return sslLibraryVersionNumber();
+}
+
+QString QSslSocketPrivate::sslLibraryBuildVersionString()
+{
+ const auto os = QOperatingSystemVersion::current();
+ return QString::fromLatin1("%1.%2.%3")
+ .arg(QString::number(os.majorVersion()),
+ QString::number(os.minorVersion()),
+ QString::number(os.microVersion()));
+}
+
+QSslSocketBackendPrivate::QSslSocketBackendPrivate()
+{
+ SecInvalidateHandle(&credentialHandle);
+ SecInvalidateHandle(&contextHandle);
+ ensureInitialized();
+}
+
+QSslSocketBackendPrivate::~QSslSocketBackendPrivate()
+{
+ closeCertificateStores();
+ deallocateContext();
+ freeCredentialsHandle();
+ CertFreeCertificateContext(localCertContext);
+}
+
+bool QSslSocketBackendPrivate::sendToken(void *token, unsigned long tokenLength, bool emitError)
+{
+ if (tokenLength == 0)
+ return true;
+ const qint64 written = plainSocket->write(static_cast<const char *>(token), tokenLength);
+ if (written != qint64(tokenLength)) {
+ // Failed to write/buffer everything or an error occurred
+ if (emitError)
+ setErrorAndEmit(plainSocket->error(), plainSocket->errorString());
+ return false;
+ }
+ return true;
+}
+
+QString QSslSocketBackendPrivate::targetName() const
+{
+ // Used for SNI extension
+ return verificationPeerName.isEmpty() ? q_func()->peerName() : verificationPeerName;
+}
+
+ULONG QSslSocketBackendPrivate::getContextRequirements()
+{
+ const bool isClient = mode == QSslSocket::SslClientMode;
+ ULONG req = 0;
+
+ req |= ISC_REQ_ALLOCATE_MEMORY; // Allocate memory for buffers automatically
+ req |= ISC_REQ_CONFIDENTIALITY; // Encrypt messages
+ req |= ISC_REQ_REPLAY_DETECT; // Detect replayed messages
+ req |= ISC_REQ_SEQUENCE_DETECT; // Detect out of sequence messages
+ req |= ISC_REQ_STREAM; // Support a stream-oriented connection
+
+ if (isClient) {
+ req |= ISC_REQ_MANUAL_CRED_VALIDATION; // Manually validate certificate
+ } else {
+ switch (configuration.peerVerifyMode) {
+ case QSslSocket::PeerVerifyMode::VerifyNone:
+ // There doesn't seem to be a way to ask for an optional client cert :-(
+ case QSslSocket::PeerVerifyMode::AutoVerifyPeer:
+ case QSslSocket::PeerVerifyMode::QueryPeer:
+ break;
+ case QSslSocket::PeerVerifyMode::VerifyPeer:
+ req |= ISC_REQ_MUTUAL_AUTH;
+ break;
+ }
+ }
+
+ return req;
+}
+
+bool QSslSocketBackendPrivate::acquireCredentialsHandle()
+{
+ Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
+
+ const bool isClient = mode == QSslSocket::SslClientMode;
+ const DWORD protocols = toSchannelProtocol(configuration.protocol);
+ if (protocols == DWORD(-1)) {
+ setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
+ QSslSocket::tr("Invalid protocol chosen"));
+ return false;
+ }
+
+ const CERT_CHAIN_CONTEXT *chainContext = nullptr;
+ auto freeCertChain = qScopeGuard([&chainContext]() {
+ if (chainContext)
+ CertFreeCertificateChain(chainContext);
+ });
+
+ DWORD certsCount = 0;
+ // Set up our certificate stores before trying to use one...
+ initializeCertificateStores();
+
+ // Check if user has specified a certificate chain but it could not be loaded.
+ // This happens if there was something wrong with the certificate chain or there was no private
+ // key.
+ if (!configuration.localCertificateChain.isEmpty() && !localCertificateStore)
+ return true; // 'true' because "tst_QSslSocket::setEmptyKey" expects us to not disconnect
+
+ if (localCertificateStore != nullptr) {
+ CERT_CHAIN_FIND_BY_ISSUER_PARA findParam;
+ ZeroMemory(&findParam, sizeof(findParam));
+ findParam.cbSize = sizeof(findParam);
+ findParam.pszUsageIdentifier = isClient ? szOID_PKIX_KP_CLIENT_AUTH : szOID_PKIX_KP_SERVER_AUTH;
+
+ // There should only be one chain in our store, so.. we grab that one.
+ chainContext = CertFindChainInStore(localCertificateStore.get(),
+ X509_ASN_ENCODING,
+ 0,
+ CERT_CHAIN_FIND_BY_ISSUER,
+ &findParam,
+ nullptr);
+ if (!chainContext) {
+ const QString message = isClient
+ ? QSslSocket::tr("The certificate provided cannot be used for a client.")
+ : QSslSocket::tr("The certificate provided cannot be used for a server.");
+ setErrorAndEmit(QAbstractSocket::SocketError::SslInvalidUserDataError, message);
+ return false;
+ }
+ Q_ASSERT(chainContext->cChain == 1);
+ Q_ASSERT(chainContext->rgpChain[0]);
+ Q_ASSERT(chainContext->rgpChain[0]->cbSize >= 1);
+ Q_ASSERT(chainContext->rgpChain[0]->rgpElement[0]);
+ Q_ASSERT(!localCertContext);
+ localCertContext = CertDuplicateCertificateContext(chainContext->rgpChain[0]
+ ->rgpElement[0]
+ ->pCertContext);
+ certsCount = 1;
+ Q_ASSERT(localCertContext);
+ }
+
+ SCHANNEL_CRED cred{
+ SCHANNEL_CRED_VERSION, // dwVersion
+ certsCount, // cCreds
+ &localCertContext, // paCred (certificate(s) containing a private key for authentication)
+ nullptr, // hRootStore
+
+ 0, // cMappers (reserved)
+ nullptr, // aphMappers (reserved)
+
+ 0, // cSupportedAlgs
+ nullptr, // palgSupportedAlgs (nullptr = system default) @future: QSslCipher-related
+
+ protocols, // grbitEnabledProtocols
+ 0, // dwMinimumCipherStrength (0 = system default)
+ 0, // dwMaximumCipherStrength (0 = system default)
+ 0, // dwSessionLifespan (0 = schannel default, 10 hours)
+ SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT
+ | SCH_CRED_NO_DEFAULT_CREDS, // dwFlags
+ 0 // dwCredFormat (must be 0)
+ };
+
+ TimeStamp expiration{};
+ auto status = AcquireCredentialsHandle(nullptr, // pszPrincipal (unused)
+ const_cast<wchar_t *>(UNISP_NAME), // pszPackage
+ isClient ? SECPKG_CRED_OUTBOUND : SECPKG_CRED_INBOUND, // fCredentialUse
+ nullptr, // pvLogonID (unused)
+ &cred, // pAuthData
+ nullptr, // pGetKeyFn (unused)
+ nullptr, // pvGetKeyArgument (unused)
+ &credentialHandle, // phCredential
+ &expiration // ptsExpir
+ );
+
+ if (status != SEC_E_OK) {
+ setErrorAndEmit(QAbstractSocket::SslInternalError, schannelErrorToString(status));
+ return false;
+ }
+ return true;
+}
+
+void QSslSocketBackendPrivate::deallocateContext()
+{
+ if (SecIsValidHandle(&contextHandle)) {
+ DeleteSecurityContext(&contextHandle);
+ SecInvalidateHandle(&contextHandle);
+ }
+}
+
+void QSslSocketBackendPrivate::freeCredentialsHandle()
+{
+ if (SecIsValidHandle(&credentialHandle)) {
+ FreeCredentialsHandle(&credentialHandle);
+ SecInvalidateHandle(&credentialHandle);
+ }
+}
+
+void QSslSocketBackendPrivate::closeCertificateStores()
+{
+ localCertificateStore.reset();
+ peerCertificateStore.reset();
+ caCertificateStore.reset();
+}
+
+bool QSslSocketBackendPrivate::createContext()
+{
+ Q_ASSERT(SecIsValidHandle(&credentialHandle));
+ Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
+ Q_ASSERT(mode == QSslSocket::SslClientMode);
+ ULONG contextReq = getContextRequirements();
+
+ SecBuffer outBuffers[3];
+ outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
+ outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
+ outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
+ auto freeBuffers = qScopeGuard([&outBuffers]() {
+ for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
+ if (outBuffers[i].pvBuffer)
+ FreeContextBuffer(outBuffers[i].pvBuffer);
+ }
+ });
+ SecBufferDesc outputBufferDesc{
+ SECBUFFER_VERSION,
+ ARRAYSIZE(outBuffers),
+ outBuffers
+ };
+
+ TimeStamp expiry;
+
+ SecBufferDesc alpnBufferDesc;
+ bool useAlpn = false;
+#ifdef SUPPORTS_ALPN
+ configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNone;
+ QByteArray alpnString = createAlpnString(configuration.nextAllowedProtocols);
+ useAlpn = !alpnString.isEmpty();
+ SecBuffer alpnBuffers[1];
+ alpnBuffers[0] = createSecBuffer(alpnString, SECBUFFER_APPLICATION_PROTOCOLS);
+ alpnBufferDesc = {
+ SECBUFFER_VERSION,
+ ARRAYSIZE(alpnBuffers),
+ alpnBuffers
+ };
+#endif
+
+ auto status = InitializeSecurityContext(&credentialHandle, // phCredential
+ nullptr, // phContext
+ const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
+ contextReq, // fContextReq
+ 0, // Reserved1
+ 0, // TargetDataRep (unused)
+ useAlpn ? &alpnBufferDesc : nullptr, // pInput
+ 0, // Reserved2
+ &contextHandle, // phNewContext
+ &outputBufferDesc, // pOutput
+ &contextAttributes, // pfContextAttr
+ &expiry // ptsExpiry
+ );
+
+ // This is the first call to InitializeSecurityContext, so theoretically "CONTINUE_NEEDED"
+ // should be the only non-error return-code here.
+ if (status != SEC_I_CONTINUE_NEEDED) {
+ setErrorAndEmit(QAbstractSocket::SslInternalError,
+ QSslSocket::tr("Error creating SSL context (%1)").arg(schannelErrorToString(status)));
+ return false;
+ }
+
+ if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
+ return false;
+ schannelState = SchannelState::PerformHandshake;
+ return true;
+}
+
+bool QSslSocketBackendPrivate::acceptContext()
+{
+ Q_ASSERT(SecIsValidHandle(&credentialHandle));
+ Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
+ Q_ASSERT(mode == QSslSocket::SslServerMode);
+ ULONG contextReq = getContextRequirements();
+
+ intermediateBuffer += plainSocket->read(16384);
+ if (intermediateBuffer.isEmpty())
+ return true; // definitely need more data..
+
+ SecBuffer inBuffers[2];
+ inBuffers[0] = createSecBuffer(intermediateBuffer, SECBUFFER_TOKEN);
+
+#ifdef SUPPORTS_ALPN
+ configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNone;
+ // The string must be alive when we call AcceptSecurityContext
+ QByteArray alpnString = createAlpnString(configuration.nextAllowedProtocols);
+ if (!alpnString.isEmpty()) {
+ inBuffers[1] = createSecBuffer(alpnString, SECBUFFER_APPLICATION_PROTOCOLS);
+ } else
+#endif
+ {
+ inBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
+ }
+
+ SecBufferDesc inputBufferDesc{
+ SECBUFFER_VERSION,
+ ARRAYSIZE(inBuffers),
+ inBuffers
+ };
+
+ SecBuffer outBuffers[3];
+ outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
+ outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
+ outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
+ auto freeBuffers = qScopeGuard([&outBuffers]() {
+ for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
+ if (outBuffers[i].pvBuffer)
+ FreeContextBuffer(outBuffers[i].pvBuffer);
+ }
+ });
+ SecBufferDesc outputBufferDesc{
+ SECBUFFER_VERSION,
+ ARRAYSIZE(outBuffers),
+ outBuffers
+ };
+
+ TimeStamp expiry;
+ auto status = AcceptSecurityContext(
+ &credentialHandle, // phCredential
+ nullptr, // phContext
+ &inputBufferDesc, // pInput
+ contextReq, // fContextReq
+ 0, // TargetDataRep (unused)
+ &contextHandle, // phNewContext
+ &outputBufferDesc, // pOutput
+ &contextAttributes, // pfContextAttr
+ &expiry // ptsTimeStamp
+ );
+
+ if (inBuffers[1].BufferType == SECBUFFER_EXTRA) {
+ // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
+ // inBuffers[1].cbBuffer indicates the amount of bytes _NOT_ processed, the rest need to
+ // be stored.
+ intermediateBuffer = intermediateBuffer.right(int(inBuffers[1].cbBuffer));
+ } else if (status != SEC_E_INCOMPLETE_MESSAGE) {
+ intermediateBuffer.clear();
+ }
+
+ if (status != SEC_I_CONTINUE_NEEDED) {
+ setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
+ QSslSocket::tr("Error creating SSL context (%1)").arg(schannelErrorToString(status)));
+ return false;
+ }
+ if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
+ return false;
+ schannelState = SchannelState::PerformHandshake;
+ return true;
+}
+
+bool QSslSocketBackendPrivate::performHandshake()
+{
+ if (plainSocket->state() == QAbstractSocket::UnconnectedState) {
+ setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
+ QSslSocket::tr("The TLS/SSL connection has been closed"));
+ return false;
+ }
+ Q_ASSERT(SecIsValidHandle(&credentialHandle));
+ Q_ASSERT(SecIsValidHandle(&contextHandle));
+ Q_ASSERT(schannelState == SchannelState::PerformHandshake);
+
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << "Bytes available from socket:" << plainSocket->bytesAvailable();
+ qCDebug(lcSsl) << "intermediateBuffer size:" << intermediateBuffer.size();
+#endif
+
+ intermediateBuffer += plainSocket->read(16384);
+ if (intermediateBuffer.isEmpty())
+ return true; // no data, will fail
+
+ SecBuffer inputBuffers[2];
+ inputBuffers[0] = createSecBuffer(intermediateBuffer, SECBUFFER_TOKEN);
+ inputBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
+ SecBufferDesc inputBufferDesc{
+ SECBUFFER_VERSION,
+ ARRAYSIZE(inputBuffers),
+ inputBuffers
+ };
+
+ SecBuffer outBuffers[3];
+ outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
+ outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
+ outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
+ auto freeBuffers = qScopeGuard([&outBuffers]() {
+ for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
+ if (outBuffers[i].pvBuffer)
+ FreeContextBuffer(outBuffers[i].pvBuffer);
+ }
+ });
+ SecBufferDesc outputBufferDesc{
+ SECBUFFER_VERSION,
+ ARRAYSIZE(outBuffers),
+ outBuffers
+ };
+
+ ULONG contextReq = getContextRequirements();
+ TimeStamp expiry;
+ auto status = InitializeSecurityContext(&credentialHandle, // phCredential
+ &contextHandle, // phContext
+ const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
+ contextReq, // fContextReq
+ 0, // Reserved1
+ 0, // TargetDataRep (unused)
+ &inputBufferDesc, // pInput
+ 0, // Reserved2
+ nullptr, // phNewContext (we already have one)
+ &outputBufferDesc, // pOutput
+ &contextAttributes, // pfContextAttr
+ &expiry // ptsExpiry
+ );
+
+ if (inputBuffers[1].BufferType == SECBUFFER_EXTRA) {
+ // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
+ // inputBuffers[1].cbBuffer indicates the amount of bytes _NOT_ processed, the rest need to
+ // be stored.
+ intermediateBuffer = intermediateBuffer.right(int(inputBuffers[1].cbBuffer));
+ } else {
+ // Clear the buffer if we weren't asked for more data
+ if (status != SEC_E_INCOMPLETE_MESSAGE)
+ intermediateBuffer.clear();
+ }
+ switch (status) {
+ case SEC_E_OK:
+ // Need to transmit a final token in the handshake if 'cbBuffer' is non-zero.
+ if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
+ return false;
+ schannelState = SchannelState::VerifyHandshake;
+ return true;
+ case SEC_I_CONTINUE_NEEDED:
+ if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
+ return false;
+ // Must call InitializeSecurityContext again later (done through continueHandshake)
+ return true;
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ // Schannel takes care of picking certificate to send (other than the one we can specify),
+ // so if we get here then that means we don't have a certificate the server accepts.
+ setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
+ QSslSocket::tr("Server did not accept any certificate we could present."));
+ return false;
+ case SEC_I_CONTEXT_EXPIRED:
+ // "The message sender has finished using the connection and has initiated a shutdown."
+ if (outBuffers[0].BufferType == SECBUFFER_TOKEN) {
+ if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
+ return false;
+ }
+ if (!shutdown) { // we did not initiate this
+ setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
+ QSslSocket::tr("The TLS/SSL connection has been closed"));
+ }
+ return true;
+ case SEC_E_INCOMPLETE_MESSAGE:
+ // Simply incomplete, wait for more data
+ return true;
+ case SEC_E_ALGORITHM_MISMATCH:
+ setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
+ QSslSocket::tr("Algorithm mismatch"));
+ shutdown = true; // skip sending the "Shutdown" alert
+ return false;
+ }
+
+ // Note: We can get here if the connection is using TLS 1.2 and the server certificate uses
+ // MD5, which is not allowed in Schannel. This causes an "invalid token" error during handshake.
+ // (If you came here investigating an error: md5 is insecure, update your certificate)
+ setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
+ QSslSocket::tr("Handshake failed: %1").arg(schannelErrorToString(status)));
+ return false;
+}
+
+bool QSslSocketBackendPrivate::verifyHandshake()
+{
+ Q_Q(QSslSocket);
+
+ const bool isClient = mode == QSslSocket::SslClientMode;
+#define CHECK_STATUS(status) \
+ if (status != SEC_E_OK) { \
+ setErrorAndEmit(QAbstractSocket::SslInternalError, \
+ QSslSocket::tr("Failed to query the TLS context: %1") \
+ .arg(schannelErrorToString(status))); \
+ return false; \
+ }
+
+ // Everything is set up, now make sure there's nothing wrong and query some attributes...
+ if (!matchesContextRequirements(contextAttributes, getContextRequirements(),
+ configuration.peerVerifyMode, isClient)) {
+ setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
+ QSslSocket::tr("Did not get the required attributes for the connection."));
+ return false;
+ }
+
+ // Get stream sizes (to know the max size of a message and the size of the header and trailer)
+ auto status = QueryContextAttributes(&contextHandle,
+ SECPKG_ATTR_STREAM_SIZES,
+ &streamSizes);
+ CHECK_STATUS(status);
+
+ // Get session cipher info
+ status = QueryContextAttributes(&contextHandle,
+ SECPKG_ATTR_CONNECTION_INFO,
+ &connectionInfo);
+ CHECK_STATUS(status);
+
+#ifdef SUPPORTS_ALPN
+ if (!configuration.nextAllowedProtocols.isEmpty() && supportsAlpn()) {
+ SecPkgContext_ApplicationProtocol alpn;
+ status = QueryContextAttributes(&contextHandle,
+ SECPKG_ATTR_APPLICATION_PROTOCOL,
+ &alpn);
+ CHECK_STATUS(status);
+ if (alpn.ProtoNegoStatus == SecApplicationProtocolNegotiationStatus_Success) {
+ QByteArray negotiatedProto = QByteArray((const char *)alpn.ProtocolId,
+ alpn.ProtocolIdSize);
+ if (!configuration.nextAllowedProtocols.contains(negotiatedProto)) {
+ setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
+ QSslSocket::tr("Unwanted protocol was negotiated"));
+ return false;
+ }
+ configuration.nextNegotiatedProtocol = negotiatedProto;
+ configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNegotiated;
+ } else {
+ configuration.nextNegotiatedProtocol = "";
+ configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationUnsupported;
+ }
+ }
+#endif // supports ALPN
+
+#undef CHECK_STATUS
+
+ // Verify certificate
+ CERT_CONTEXT *certificateContext = nullptr;
+ auto freeCertificate = qScopeGuard([&certificateContext]() {
+ if (certificateContext)
+ CertFreeCertificateContext(certificateContext);
+ });
+ status = QueryContextAttributes(&contextHandle,
+ SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+ &certificateContext);
+
+ // QueryPeer can (currently) not work in Schannel since Schannel itself doesn't have a way to
+ // ask for a certificate and then still be OK if it's not received.
+ // To work around this we don't request a certificate at all for QueryPeer.
+ // For servers AutoVerifyPeer is supposed to be treated the same as QueryPeer.
+ // This means that servers using Schannel will only request client certificate for "VerifyPeer".
+ if ((!isClient && configuration.peerVerifyMode == QSslSocket::PeerVerifyMode::VerifyPeer)
+ || (isClient && configuration.peerVerifyMode != QSslSocket::PeerVerifyMode::VerifyNone
+ && configuration.peerVerifyMode != QSslSocket::PeerVerifyMode::QueryPeer)) {
+ if (status != SEC_E_OK) {
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << "Couldn't retrieve peer certificate, status:"
+ << schannelErrorToString(status);
+#endif
+ const QSslError error{ QSslError::NoPeerCertificate };
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+
+ // verifyCertContext returns false if the user disconnected while it was checking errors.
+ if (certificateContext && sslErrors.isEmpty() && !verifyCertContext(certificateContext))
+ return false;
+
+ if (!checkSslErrors() || state != QAbstractSocket::ConnectedState) {
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << __func__ << "was unsuccessful. Paused:" << paused;
+#endif
+ // If we're paused then checkSslErrors returned false, but it's not an error
+ return paused && state == QAbstractSocket::ConnectedState;
+ }
+
+ schannelState = SchannelState::Done;
+ peerCertVerified = true;
+ return true;
+}
+
+bool QSslSocketBackendPrivate::renegotiate()
+{
+ SecBuffer outBuffers[3];
+ outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
+ outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
+ outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
+ auto freeBuffers = qScopeGuard([&outBuffers]() {
+ for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
+ if (outBuffers[i].pvBuffer)
+ FreeContextBuffer(outBuffers[i].pvBuffer);
+ }
+ });
+ SecBufferDesc outputBufferDesc{
+ SECBUFFER_VERSION,
+ ARRAYSIZE(outBuffers),
+ outBuffers
+ };
+
+ ULONG contextReq = getContextRequirements();
+ TimeStamp expiry;
+ SECURITY_STATUS status;
+ if (mode == QSslSocket::SslClientMode) {
+ status = InitializeSecurityContext(&credentialHandle, // phCredential
+ &contextHandle, // phContext
+ const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
+ contextReq, // fContextReq
+ 0, // Reserved1
+ 0, // TargetDataRep (unused)
+ nullptr, // pInput (nullptr for renegotiate)
+ 0, // Reserved2
+ nullptr, // phNewContext (we already have one)
+ &outputBufferDesc, // pOutput
+ &contextAttributes, // pfContextAttr
+ &expiry // ptsExpiry
+ );
+ } else {
+ status = AcceptSecurityContext(
+ &credentialHandle, // phCredential
+ &contextHandle, // phContext
+ nullptr, // pInput
+ contextReq, // fContextReq
+ 0, // TargetDataRep (unused)
+ nullptr, // phNewContext
+ &outputBufferDesc, // pOutput
+ &contextAttributes, // pfContextAttr,
+ &expiry // ptsTimeStamp
+ );
+ }
+ if (status == SEC_I_CONTINUE_NEEDED) {
+ schannelState = SchannelState::PerformHandshake;
+ return sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer);
+ }
+ setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
+ QSslSocket::tr("Renegotiation was unsuccessful: %1").arg(schannelErrorToString(status)));
+ return false;
+}
+
+/*!
+ \internal
+ reset the state in preparation for reuse of socket
+*/
+void QSslSocketBackendPrivate::reset()
+{
+ closeCertificateStores(); // certificate stores could've changed
+ deallocateContext();
+ freeCredentialsHandle(); // in case we already had one (@future: session resumption requires re-use)
+
+ connectionInfo = {};
+ streamSizes = {};
+
+ CertFreeCertificateContext(localCertContext);
+ localCertContext = nullptr;
+
+ contextAttributes = 0;
+ intermediateBuffer.clear();
+ schannelState = SchannelState::InitializeHandshake;
+
+ connectionEncrypted = false;
+ shutdown = false;
+ peerCertVerified = false;
+ renegotiating = false;
+}
+
+void QSslSocketBackendPrivate::startClientEncryption()
+{
+ if (connectionEncrypted)
+ return; // let's not mess up the connection...
+ reset();
+ continueHandshake();
+}
+
+void QSslSocketBackendPrivate::startServerEncryption()
+{
+ if (connectionEncrypted)
+ return; // let's not mess up the connection...
+ reset();
+ continueHandshake();
+}
+
+void QSslSocketBackendPrivate::transmit()
+{
+ Q_Q(QSslSocket);
+
+ // Can happen if called through QSslSocket::abort->QSslSocket::close->QSslSocket::flush->here
+ if (plainSocket->state() == QAbstractSocket::SocketState::UnconnectedState)
+ return;
+
+ if (schannelState != SchannelState::Done) {
+ continueHandshake();
+ return;
+ }
+
+ if (connectionEncrypted) { // encrypt data in writeBuffer and write it to plainSocket
+ qint64 totalBytesWritten = 0;
+ qint64 writeBufferSize;
+ while ((writeBufferSize = writeBuffer.size()) > 0) {
+ const int headerSize = int(streamSizes.cbHeader);
+ const int trailerSize = int(streamSizes.cbTrailer);
+ // Try to read 'cbMaximumMessage' bytes from buffer before encrypting.
+ const int size = int(std::min(writeBufferSize, qint64(streamSizes.cbMaximumMessage)));
+ QByteArray fullMessage(headerSize + trailerSize + size, Qt::Uninitialized);
+ {
+ // Use peek() here instead of read() so we don't lose data if encryption fails.
+ qint64 copied = writeBuffer.peek(fullMessage.data() + headerSize, size);
+ Q_ASSERT(copied == size);
+ }
+
+ SecBuffer inputBuffers[4]{
+ createSecBuffer(fullMessage.data(), headerSize, SECBUFFER_STREAM_HEADER),
+ createSecBuffer(fullMessage.data() + headerSize, size, SECBUFFER_DATA),
+ createSecBuffer(fullMessage.data() + headerSize + size, trailerSize, SECBUFFER_STREAM_TRAILER),
+ createSecBuffer(nullptr, 0, SECBUFFER_EMPTY)
+ };
+ SecBufferDesc message{
+ SECBUFFER_VERSION,
+ ARRAYSIZE(inputBuffers),
+ inputBuffers
+ };
+ auto status = EncryptMessage(&contextHandle, 0, &message, 0);
+ if (status != SEC_E_OK) {
+ setErrorAndEmit(QAbstractSocket::SslInternalError,
+ QSslSocket::tr("Schannel failed to encrypt data: %1")
+ .arg(schannelErrorToString(status)));
+ return;
+ }
+ // Data was encrypted successfully, so we free() what we peek()ed earlier
+ writeBuffer.free(size);
+
+ // The trailer's size is not final, so resize fullMessage to not send trailing junk
+ fullMessage.resize(inputBuffers[0].cbBuffer + inputBuffers[1].cbBuffer + inputBuffers[2].cbBuffer);
+ const qint64 bytesWritten = plainSocket->write(fullMessage);
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << "Wrote" << bytesWritten << "of total"
+ << fullMessage.length() << "bytes";
+#endif
+ if (bytesWritten >= 0) {
+ totalBytesWritten += bytesWritten;
+ } else {
+ setErrorAndEmit(plainSocket->error(), plainSocket->errorString());
+ return;
+ }
+ }
+
+ if (totalBytesWritten > 0) {
+ // Don't emit bytesWritten() recursively.
+ if (!emittedBytesWritten) {
+ emittedBytesWritten = true;
+ emit q->bytesWritten(totalBytesWritten);
+ emittedBytesWritten = false;
+ }
+ emit q->channelBytesWritten(0, totalBytesWritten);
+ }
+ }
+
+ if (connectionEncrypted) { // Decrypt data from remote
+ int totalRead = 0;
+ bool hadIncompleteData = false;
+ while (!readBufferMaxSize || buffer.size() < readBufferMaxSize) {
+ QByteArray ciphertext;
+ if (intermediateBuffer.length()) {
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << "Restoring data from intermediateBuffer:"
+ << intermediateBuffer.length() << "bytes";
+#endif
+ ciphertext.swap(intermediateBuffer);
+ }
+ int initialLength = ciphertext.length();
+ ciphertext += plainSocket->read(16384);
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << "Read" << ciphertext.length() - initialLength
+ << "encrypted bytes from the socket";
+#endif
+ if (ciphertext.length() == 0 || (hadIncompleteData && initialLength == ciphertext.length())) {
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << (hadIncompleteData ? "No new data received, leaving loop!"
+ : "Nothing to decrypt, leaving loop!");
+#endif
+ if (ciphertext.length()) // We have data, it came from intermediateBuffer, swap back
+ intermediateBuffer.swap(ciphertext);
+ break;
+ }
+ hadIncompleteData = false;
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << "Total amount of bytes to decrypt:" << ciphertext.length();
+#endif
+
+ SecBuffer dataBuffer[4]{
+ createSecBuffer(ciphertext, SECBUFFER_DATA),
+ createSecBuffer(nullptr, 0, SECBUFFER_EMPTY),
+ createSecBuffer(nullptr, 0, SECBUFFER_EMPTY),
+ createSecBuffer(nullptr, 0, SECBUFFER_EMPTY)
+ };
+ SecBufferDesc message{
+ SECBUFFER_VERSION,
+ ARRAYSIZE(dataBuffer),
+ dataBuffer
+ };
+ auto status = DecryptMessage(&contextHandle, &message, 0, nullptr);
+ if (status == SEC_E_OK || status == SEC_I_RENEGOTIATE || status == SEC_I_CONTEXT_EXPIRED) {
+ // There can still be 0 output even if it succeeds, this is fine
+ if (dataBuffer[1].cbBuffer > 0) {
+ // It is always decrypted in-place.
+ // But [0] is the STREAM_HEADER, [1] is the DATA and [2] is the STREAM_TRAILER.
+ // The pointers in all of those still point into the 'ciphertext' byte array.
+ buffer.append(static_cast<char *>(dataBuffer[1].pvBuffer),
+ dataBuffer[1].cbBuffer);
+ totalRead += dataBuffer[1].cbBuffer;
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << "Decrypted" << dataBuffer[1].cbBuffer
+ << "bytes. New read buffer size:" << buffer.size();
+#endif
+ }
+ if (dataBuffer[3].BufferType == SECBUFFER_EXTRA) {
+ // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
+ // dataBuffer[3].cbBuffer indicates the amount of bytes _NOT_ processed,
+ // the rest need to be stored.
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << "We've got excess data, moving it to the intermediate buffer:"
+ << dataBuffer[3].cbBuffer << "bytes";
+#endif
+ intermediateBuffer = ciphertext.right(int(dataBuffer[3].cbBuffer));
+ }
+ } else if (status == SEC_E_INCOMPLETE_MESSAGE) {
+ // Need more data before we can decrypt.. to the buffer it goes!
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl, "We didn't have enough data to decrypt anything, will try again!");
+#endif
+ Q_ASSERT(intermediateBuffer.isEmpty());
+ intermediateBuffer.swap(ciphertext);
+ // We try again, but if we don't get any more data then we leave
+ hadIncompleteData = true;
+ } else if (status == SEC_E_INVALID_HANDLE) {
+ // I don't think this should happen, if it does we're done...
+ qCWarning(lcSsl, "The internal SSPI handle is invalid!");
+ Q_UNREACHABLE();
+ } else if (status == SEC_E_INVALID_TOKEN) {
+ qCWarning(lcSsl, "Got SEC_E_INVALID_TOKEN!");
+ Q_UNREACHABLE(); // Happened once due to a bug, but shouldn't generally happen(?)
+ } else if (status == SEC_E_MESSAGE_ALTERED) {
+ // The message has been altered, disconnect now.
+ shutdown = true; // skips sending the shutdown alert
+ disconnectFromHost();
+ setErrorAndEmit(QAbstractSocket::SslInternalError,
+ schannelErrorToString(status));
+ break;
+ } else if (status == SEC_E_OUT_OF_SEQUENCE) {
+ // @todo: I don't know if this one is actually "fatal"..
+ // This path might never be hit as it seems this is for connection-oriented connections,
+ // while SEC_E_MESSAGE_ALTERED is for stream-oriented ones (what we use).
+ shutdown = true; // skips sending the shutdown alert
+ disconnectFromHost();
+ setErrorAndEmit(QAbstractSocket::SslInternalError,
+ schannelErrorToString(status));
+ break;
+ } else if (status == SEC_I_CONTEXT_EXPIRED) {
+ // 'remote' has initiated a shutdown
+ disconnectFromHost();
+ setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
+ schannelErrorToString(status));
+ break;
+ } else if (status == SEC_I_RENEGOTIATE) {
+ // 'remote' wants to renegotiate
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl, "The peer wants to renegotiate.");
+#endif
+ schannelState = SchannelState::Renegotiate;
+ renegotiating = true;
+ // We need to call 'continueHandshake' or else there's no guarantee it ever gets called
+ continueHandshake();
+ break;
+ }
+ }
+
+ if (totalRead) {
+ if (readyReadEmittedPointer)
+ *readyReadEmittedPointer = true;
+ emit q->readyRead();
+ emit q->channelReadyRead(0);
+ }
+ }
+}
+
+void QSslSocketBackendPrivate::sendShutdown()
+{
+ const bool isClient = mode == QSslSocket::SslClientMode;
+ DWORD shutdownToken = SCHANNEL_SHUTDOWN;
+ SecBuffer buffer = createSecBuffer(&shutdownToken, sizeof(SCHANNEL_SHUTDOWN), SECBUFFER_TOKEN);
+ SecBufferDesc token{
+ SECBUFFER_VERSION,
+ 1,
+ &buffer
+ };
+ auto status = ApplyControlToken(&contextHandle, &token);
+
+ if (status != SEC_E_OK) {
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << "Failed to apply shutdown control token:" << schannelErrorToString(status);
+#endif
+ return;
+ }
+
+ SecBuffer outBuffers[3];
+ outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
+ outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
+ outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
+ auto freeBuffers = qScopeGuard([&outBuffers]() {
+ for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
+ if (outBuffers[i].pvBuffer)
+ FreeContextBuffer(outBuffers[i].pvBuffer);
+ }
+ });
+ SecBufferDesc outputBufferDesc{
+ SECBUFFER_VERSION,
+ ARRAYSIZE(outBuffers),
+ outBuffers
+ };
+
+ ULONG contextReq = getContextRequirements();
+ TimeStamp expiry;
+ if (isClient) {
+ status = InitializeSecurityContext(&credentialHandle, // phCredential
+ &contextHandle, // phContext
+ const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
+ contextReq, // fContextReq
+ 0, // Reserved1
+ 0, // TargetDataRep (unused)
+ nullptr, // pInput
+ 0, // Reserved2
+ nullptr, // phNewContext (we already have one)
+ &outputBufferDesc, // pOutput
+ &contextAttributes, // pfContextAttr
+ &expiry // ptsExpiry
+ );
+ } else {
+ status = AcceptSecurityContext(
+ &credentialHandle, // phCredential
+ &contextHandle, // phContext
+ nullptr, // pInput
+ contextReq, // fContextReq
+ 0, // TargetDataRep (unused)
+ nullptr, // phNewContext
+ &outputBufferDesc, // pOutput
+ &contextAttributes, // pfContextAttr,
+ &expiry // ptsTimeStamp
+ );
+ }
+ if (status == SEC_E_OK || status == SEC_I_CONTEXT_EXPIRED) {
+ if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, false)) {
+ // We failed to send the shutdown message, but it's not that important since we're
+ // shutting down anyway.
+ return;
+ }
+ } else {
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << "Failed to initialize shutdown:" << schannelErrorToString(status);
+#endif
+ }
+}
+
+void QSslSocketBackendPrivate::disconnectFromHost()
+{
+ if (SecIsValidHandle(&contextHandle)) {
+ if (!shutdown) {
+ shutdown = true;
+ if (plainSocket->state() != QAbstractSocket::UnconnectedState) {
+ if (connectionEncrypted) {
+ // Read as much as possible because this is likely our last chance
+ qint64 tempMax = readBufferMaxSize;
+ readBufferMaxSize = 0;
+ transmit();
+ readBufferMaxSize = tempMax;
+ sendShutdown();
+ }
+ }
+ }
+ }
+ if (plainSocket->state() != QAbstractSocket::UnconnectedState)
+ plainSocket->disconnectFromHost();
+}
+
+void QSslSocketBackendPrivate::disconnected()
+{
+ shutdown = true;
+ connectionEncrypted = false;
+ deallocateContext();
+ freeCredentialsHandle();
+}
+
+QSslCipher QSslSocketBackendPrivate::sessionCipher() const
+{
+ if (!connectionEncrypted)
+ return QSslCipher();
+ return QSslCipher(QStringLiteral("Schannel"), sessionProtocol());
+}
+
+QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const
+{
+ if (!connectionEncrypted)
+ return QSsl::SslProtocol::UnknownProtocol;
+ return toQtSslProtocol(connectionInfo.dwProtocol);
+}
+
+void QSslSocketBackendPrivate::continueHandshake()
+{
+ Q_Q(QSslSocket);
+ const bool isServer = mode == QSslSocket::SslServerMode;
+ switch (schannelState) {
+ case SchannelState::InitializeHandshake:
+ if (!SecIsValidHandle(&credentialHandle) && !acquireCredentialsHandle()) {
+ disconnectFromHost();
+ return;
+ }
+ if (!SecIsValidHandle(&credentialHandle)) // Needed to support tst_QSslSocket::setEmptyKey
+ return;
+ if (!SecIsValidHandle(&contextHandle) && !(isServer ? acceptContext() : createContext())) {
+ disconnectFromHost();
+ return;
+ }
+ if (schannelState != SchannelState::PerformHandshake)
+ break;
+ Q_FALLTHROUGH();
+ case SchannelState::PerformHandshake:
+ if (!performHandshake()) {
+ disconnectFromHost();
+ return;
+ }
+ if (schannelState != SchannelState::VerifyHandshake)
+ break;
+ Q_FALLTHROUGH();
+ case SchannelState::VerifyHandshake:
+ // if we're in shutdown or renegotiating then we might not need to verify
+ // (since we already did)
+ if (!peerCertVerified && !verifyHandshake()) {
+ shutdown = true; // Skip sending shutdown alert
+ q->abort(); // We don't want to send buffered data
+ disconnectFromHost();
+ return;
+ }
+ if (schannelState != SchannelState::Done)
+ break;
+ Q_FALLTHROUGH();
+ case SchannelState::Done:
+ // connectionEncrypted is already true if we come here from a renegotiation
+ if (!connectionEncrypted) {
+ connectionEncrypted = true; // all is done
+ emit q->encrypted();
+ }
+ renegotiating = false;
+ if (pendingClose) {
+ pendingClose = false;
+ disconnectFromHost();
+ } else {
+ transmit();
+ }
+ break;
+ case SchannelState::Renegotiate:
+ if (!renegotiate()) {
+ disconnectFromHost();
+ return;
+ }
+ break;
+ }
+}
+
+QList<QSslCipher> QSslSocketBackendPrivate::defaultCiphers()
+{
+ QList<QSslCipher> ciphers;
+ // @temp (I hope), stolen from qsslsocket_winrt.cpp
+ const QString protocolStrings[] = { QStringLiteral("TLSv1"), QStringLiteral("TLSv1.1"),
+ QStringLiteral("TLSv1.2"), QStringLiteral("TLSv1.3") };
+ const QSsl::SslProtocol protocols[] = { QSsl::TlsV1_0, QSsl::TlsV1_1,
+ QSsl::TlsV1_2, QSsl::TlsV1_3 };
+ const int size = ARRAYSIZE(protocols);
+ Q_STATIC_ASSERT(size == ARRAYSIZE(protocolStrings));
+ ciphers.reserve(size);
+ for (int i = 0; i < size; ++i) {
+ QSslCipher cipher;
+ cipher.d->isNull = false;
+ cipher.d->name = QStringLiteral("Schannel");
+ cipher.d->protocol = protocols[i];
+ cipher.d->protocolString = protocolStrings[i];
+ ciphers.append(cipher);
+ }
+
+ return ciphers;
+}
+
+QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &certificateChain,
+ const QString &hostName)
+{
+ Q_UNUSED(certificateChain);
+ Q_UNUSED(hostName);
+
+ Q_UNIMPLEMENTED();
+ return {}; // @future implement(?)
+}
+
+bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert,
+ QList<QSslCertificate> *caCertificates,
+ const QByteArray &passPhrase)
+{
+ Q_UNUSED(device);
+ Q_UNUSED(key);
+ Q_UNUSED(cert);
+ Q_UNUSED(caCertificates);
+ Q_UNUSED(passPhrase);
+ // @future: can load into its own certificate store (encountered problems extracting key).
+ Q_UNIMPLEMENTED();
+ return false;
+}
+
+/*
+ Copied from qsslsocket_mac.cpp, which was copied from qsslsocket_openssl.cpp
+*/
+bool QSslSocketBackendPrivate::checkSslErrors()
+{
+ if (sslErrors.isEmpty())
+ return true;
+ Q_Q(QSslSocket);
+
+ emit q->sslErrors(sslErrors);
+
+ const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer
+ || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer
+ && mode == QSslSocket::SslClientMode);
+ const bool doEmitSslError = !verifyErrorsHaveBeenIgnored();
+ // check whether we need to emit an SSL handshake error
+ if (doVerifyPeer && doEmitSslError) {
+ if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) {
+ pauseSocketNotifiers(q);
+ paused = true;
+ } else {
+ setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
+ sslErrors.constFirst().errorString());
+ plainSocket->disconnectFromHost();
+ }
+ return false;
+ }
+
+ return true;
+}
+
+void QSslSocketBackendPrivate::initializeCertificateStores()
+{
+ //// helper function which turns a chain into a certificate store
+ auto createStoreFromCertificateChain = [](const QList<QSslCertificate> certChain, const QSslKey &privateKey) {
+ const wchar_t *passphrase = L"";
+ // Need to embed the private key in the certificate
+ QByteArray pkcs12 = _q_makePkcs12(certChain,
+ privateKey,
+ QString::fromWCharArray(passphrase, 0));
+ CRYPT_DATA_BLOB pfxBlob;
+ pfxBlob.cbData = DWORD(pkcs12.length());
+ pfxBlob.pbData = reinterpret_cast<unsigned char *>(pkcs12.data());
+ return QHCertStorePointer(PFXImportCertStore(&pfxBlob, passphrase, 0));
+ };
+
+ if (!configuration.localCertificateChain.isEmpty()) {
+ if (configuration.privateKey.isNull()) {
+ setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
+ QSslSocket::tr("Cannot provide a certificate with no key"));
+ return;
+ }
+ if (localCertificateStore == nullptr) {
+ localCertificateStore = createStoreFromCertificateChain(configuration.localCertificateChain,
+ configuration.privateKey);
+ if (localCertificateStore == nullptr)
+ qCWarning(lcSsl, "Failed to load certificate chain!");
+ }
+ }
+
+ if (!configuration.caCertificates.isEmpty() && !caCertificateStore) {
+ caCertificateStore = createStoreFromCertificateChain(configuration.caCertificates,
+ {}); // No private key for the CA certs
+ }
+}
+
+bool QSslSocketBackendPrivate::verifyCertContext(CERT_CONTEXT *certContext)
+{
+ Q_ASSERT(certContext);
+ Q_Q(QSslSocket);
+
+ const bool isClient = mode == QSslSocket::SslClientMode;
+
+ // Create a collection of stores so we can pass in multiple stores as additional locations to
+ // search for the certificate chain
+ auto tempCertCollection = QHCertStorePointer(CertOpenStore(CERT_STORE_PROV_COLLECTION,
+ X509_ASN_ENCODING,
+ 0,
+ CERT_STORE_CREATE_NEW_FLAG,
+ nullptr));
+ if (!tempCertCollection) {
+#ifdef QSSLSOCKET_DEBUG
+ qCWarning(lcSsl, "Failed to create certificate store collection!");
+#endif
+ return false;
+ }
+
+ if (rootCertOnDemandLoadingAllowed()) {
+ // @future(maybe): following the OpenSSL backend these certificates should be added into
+ // the Ca list, not just included during verification.
+ // That being said, it's not trivial to add the root certificates (if and only if they
+ // came from the system root store). And I don't see this mentioned in our documentation.
+ auto rootStore = QHCertStorePointer(CertOpenSystemStore(0, L"ROOT"));
+ if (!rootStore) {
+#ifdef QSSLSOCKET_DEBUG
+ qCWarning(lcSsl, "Failed to open the system root CA certificate store!");
+#endif
+ return false;
+ } else if (!CertAddStoreToCollection(tempCertCollection.get(), rootStore.get(), 0, 1)) {
+#ifdef QSSLSOCKET_DEBUG
+ qCWarning(lcSsl, "Failed to add the system root CA certificate store to the certificate store collection!");
+#endif
+ return false;
+ }
+ }
+ if (caCertificateStore) {
+ if (!CertAddStoreToCollection(tempCertCollection.get(), caCertificateStore.get(), 0, 1)) {
+#ifdef QSSLSOCKET_DEBUG
+ qCWarning(lcSsl, "Failed to add the user's CA certificate store to the certificate store collection!");
+#endif
+ return false;
+ }
+ }
+
+ if (!CertAddStoreToCollection(tempCertCollection.get(), certContext->hCertStore, 0, 0)) {
+#ifdef QSSLSOCKET_DEBUG
+ qCWarning(lcSsl, "Failed to add certificate's origin store to the certificate store collection!");
+#endif
+ return false;
+ }
+
+ CERT_CHAIN_PARA parameters;
+ ZeroMemory(&parameters, sizeof(parameters));
+ parameters.cbSize = sizeof(CERT_CHAIN_PARA);
+ parameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
+ parameters.RequestedUsage.Usage.cUsageIdentifier = 1;
+ LPSTR oid = LPSTR(isClient ? szOID_PKIX_KP_SERVER_AUTH
+ : szOID_PKIX_KP_CLIENT_AUTH);
+ parameters.RequestedUsage.Usage.rgpszUsageIdentifier = &oid;
+
+ configuration.peerCertificate.clear();
+ configuration.peerCertificateChain.clear();
+ const CERT_CHAIN_CONTEXT *chainContext = nullptr;
+ auto freeCertChain = qScopeGuard([&chainContext]() {
+ if (chainContext)
+ CertFreeCertificateChain(chainContext);
+ });
+ BOOL status = CertGetCertificateChain(nullptr, // hChainEngine, default
+ certContext, // pCertContext
+ nullptr, // pTime, 'now'
+ tempCertCollection.get(), // hAdditionalStore, additional cert store
+ &parameters, // pChainPara
+ CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, // dwFlags
+ nullptr, // reserved
+ &chainContext // ppChainContext
+ );
+ if (status == FALSE || !chainContext || chainContext->cChain == 0) {
+ QSslError error(QSslError::UnableToVerifyFirstCertificate);
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ return q->state() == QAbstractSocket::ConnectedState;
+ }
+
+ // Helper-function to get a QSslCertificate given a CERT_CHAIN_ELEMENT
+ static auto getCertificateFromChainElement = [](CERT_CHAIN_ELEMENT *element) {
+ if (!element)
+ return QSslCertificate();
+
+ const CERT_CONTEXT *certContext = element->pCertContext;
+ return QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(certContext);
+ };
+
+ // Pick a chain to use as the certificate chain, if multiple are available:
+ // According to https://docs.microsoft.com/en-gb/windows/desktop/api/wincrypt/ns-wincrypt-_cert_chain_context
+ // this seems to be the best way to get a trusted chain.
+ CERT_SIMPLE_CHAIN *chain = chainContext->rgpChain[chainContext->cChain - 1];
+
+ if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN) {
+ auto error = QSslError(QSslError::SslError::UnableToGetIssuerCertificate,
+ getCertificateFromChainElement(chain->rgpElement[chain->cElement - 1]));
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_INVALID_BASIC_CONSTRAINTS) {
+ // @Note: This is actually one of two errors:
+ // "either the certificate cannot be used to issue other certificates, or the chain path length has been exceeded."
+ // But here we are checking the chain's status, so we assume the "issuing" error cannot occur here.
+ auto error = QSslError(QSslError::PathLengthExceeded);
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ static const DWORD leftoverCertChainErrorMask = CERT_TRUST_IS_CYCLIC | CERT_TRUST_INVALID_EXTENSION
+ | CERT_TRUST_INVALID_POLICY_CONSTRAINTS | CERT_TRUST_INVALID_NAME_CONSTRAINTS
+ | CERT_TRUST_CTL_IS_NOT_TIME_VALID | CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID
+ | CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE;
+ if (chain->TrustStatus.dwErrorStatus & leftoverCertChainErrorMask) {
+ auto error = QSslError(QSslError::SslError::UnspecifiedError);
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+
+ DWORD verifyDepth = chain->cElement;
+ if (configuration.peerVerifyDepth > 0 && DWORD(configuration.peerVerifyDepth) < verifyDepth)
+ verifyDepth = DWORD(configuration.peerVerifyDepth);
+
+ for (DWORD i = 0; i < verifyDepth; i++) {
+ CERT_CHAIN_ELEMENT *element = chain->rgpElement[i];
+ QSslCertificate certificate = getCertificateFromChainElement(element);
+ const QList<QSslCertificateExtension> extensions = certificate.extensions();
+
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << "issuer:" << certificate.issuerDisplayName()
+ << "\nsubject:" << certificate.subjectDisplayName()
+ << "\nQSslCertificate info:" << certificate
+ << "\nextended error info:" << element->pwszExtendedErrorInfo
+ << "\nerror status:" << element->TrustStatus.dwErrorStatus;
+#endif
+
+ configuration.peerCertificateChain.append(certificate);
+
+ if (certificate.isBlacklisted()) {
+ const auto error = QSslError(QSslError::CertificateBlacklisted, certificate);
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+
+ LONG result = CertVerifyTimeValidity(nullptr /*== now */, element->pCertContext->pCertInfo);
+ if (result != 0) {
+ auto error = QSslError(result == -1 ? QSslError::CertificateNotYetValid
+ : QSslError::CertificateExpired,
+ certificate);
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+
+ //// Errors
+ if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_TIME_VALID) {
+ // handled right above
+ Q_ASSERT(!sslErrors.isEmpty());
+ }
+ if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED) {
+ auto error = QSslError(QSslError::CertificateRevoked, certificate);
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_SIGNATURE_VALID) {
+ auto error = QSslError(QSslError::CertificateSignatureFailed, certificate);
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+
+ // While netscape shouldn't be relevant now it defined an extension which is
+ // still in use. Schannel does not check this automatically, so we do it here.
+ // It is used to differentiate between client and server certificates.
+ if (netscapeWrongCertType(extensions, isClient))
+ element->TrustStatus.dwErrorStatus |= CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
+
+ if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_VALID_FOR_USAGE) {
+ auto error = QSslError(QSslError::InvalidPurpose, certificate);
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_UNTRUSTED_ROOT) {
+ // Override this error if we have the certificate inside our trusted CAs list.
+ const bool isTrustedRoot = configuration.caCertificates.contains(certificate);
+ if (!isTrustedRoot) {
+ auto error = QSslError(QSslError::CertificateUntrusted, certificate);
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+ static const DWORD certRevocationCheckUnavailableError = CERT_TRUST_IS_OFFLINE_REVOCATION
+ | CERT_TRUST_REVOCATION_STATUS_UNKNOWN;
+ if (element->TrustStatus.dwErrorStatus & certRevocationCheckUnavailableError) {
+ // @future(maybe): Do something with this
+ }
+
+ // Dumping ground of errors that don't fit our specific errors
+ static const DWORD leftoverCertErrorMask = CERT_TRUST_IS_CYCLIC
+ | CERT_TRUST_INVALID_EXTENSION | CERT_TRUST_INVALID_NAME_CONSTRAINTS
+ | CERT_TRUST_INVALID_POLICY_CONSTRAINTS
+ | CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT
+ | CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT
+ | CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT
+ | CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT
+ | CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT;
+ if (element->TrustStatus.dwErrorStatus & leftoverCertErrorMask) {
+ auto error = QSslError(QSslError::UnspecifiedError, certificate);
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ if (element->TrustStatus.dwErrorStatus & CERT_TRUST_INVALID_BASIC_CONSTRAINTS) {
+ auto it = std::find_if(extensions.cbegin(), extensions.cend(),
+ [](const QSslCertificateExtension &extension) {
+ return extension.name() == QLatin1String("basicConstraints");
+ });
+ if (it != extensions.cend()) {
+ // @Note: This is actually one of two errors:
+ // "either the certificate cannot be used to issue other certificates,
+ // or the chain path length has been exceeded."
+ QVariantMap basicConstraints = it->value().toMap();
+ QSslError error;
+ if (i > 0 && !basicConstraints.value(QLatin1String("ca"), false).toBool())
+ error = QSslError(QSslError::InvalidPurpose, certificate);
+ else
+ error = QSslError(QSslError::PathLengthExceeded, certificate);
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+ if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_EXPLICIT_DISTRUST) {
+ auto error = QSslError(QSslError::CertificateBlacklisted, certificate);
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+
+ if (element->TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED) {
+ // If it's self-signed *and* a CA then we can assume it's a root CA certificate
+ // and we can ignore the "self-signed" note:
+ // We check the basicConstraints certificate extension when possible, but this didn't
+ // exist for version 1, so we can only guess in that case
+ const bool isRootCertificateAuthority = isCertificateAuthority(extensions)
+ || certificate.version() == "1";
+
+ // Root certificate tends to be signed by themselves, so ignore self-signed status.
+ if (!isRootCertificateAuthority) {
+ auto error = QSslError(QSslError::SelfSignedCertificate, certificate);
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+ }
+
+ if (!configuration.peerCertificateChain.isEmpty())
+ configuration.peerCertificate = configuration.peerCertificateChain.first();
+
+ // @Note: Somewhat copied from qsslsocket_mac.cpp
+ const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer
+ || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer
+ && mode == QSslSocket::SslClientMode);
+ // Check the peer certificate itself. First try the subject's common name
+ // (CN) as a wildcard, then try all alternate subject name DNS entries the
+ // same way.
+ if (!configuration.peerCertificate.isNull()) {
+ // but only if we're a client connecting to a server
+ // if we're the server, don't check CN
+ if (mode == QSslSocket::SslClientMode) {
+ const QString peerName(verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName);
+ if (!isMatchingHostname(configuration.peerCertificate, peerName)) {
+ // No matches in common names or alternate names.
+ const QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate);
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+ } else if (doVerifyPeer) {
+ // No peer certificate presented. Report as error if the socket
+ // expected one.
+ const QSslError error(QSslError::NoPeerCertificate);
+ sslErrors += error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+
+ return true;
+}
+
+bool QSslSocketBackendPrivate::rootCertOnDemandLoadingAllowed()
+{
+ return allowRootCertOnDemandLoading && s_loadRootCertsOnDemand;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_schannel_p.h b/src/network/ssl/qsslsocket_schannel_p.h
new file mode 100644
index 0000000000..9879e2fc60
--- /dev/null
+++ b/src/network/ssl/qsslsocket_schannel_p.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSSLSOCKET_SCHANNEL_P_H
+#define QSSLSOCKET_SCHANNEL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_REQUIRE_CONFIG(schannel);
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+#include "qsslsocket_p.h"
+
+#define SECURITY_WIN32
+#include <security.h>
+#include <schnlsp.h>
+#undef SECURITY_WIN32
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+struct QHCertStoreDeleter {
+ void operator()(HCERTSTORE store)
+ {
+ CertCloseStore(store, 0);
+ }
+};
+typedef std::unique_ptr<void, QHCertStoreDeleter> QHCertStorePointer;
+
+class QSslSocketBackendPrivate final : public QSslSocketPrivate
+{
+ Q_DISABLE_COPY_MOVE(QSslSocketBackendPrivate)
+ Q_DECLARE_PUBLIC(QSslSocket)
+public:
+ QSslSocketBackendPrivate();
+ ~QSslSocketBackendPrivate();
+
+ // Platform specific functions
+ void startClientEncryption() override;
+ void startServerEncryption() override;
+ void transmit() override;
+ void disconnectFromHost() override;
+ void disconnected() override;
+ QSslCipher sessionCipher() const override;
+ QSsl::SslProtocol sessionProtocol() const override;
+ void continueHandshake() override;
+
+ static QList<QSslCipher> defaultCiphers();
+ static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain,
+ const QString &hostName);
+ static bool importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert,
+ QList<QSslCertificate> *caCertificates, const QByteArray &passPhrase);
+
+private:
+ enum class SchannelState {
+ InitializeHandshake, // create and transmit context (client)/accept context (server)
+ PerformHandshake, // get token back, process it
+ VerifyHandshake, // Verify that things are OK
+ Done, // Connection encrypted!
+ Renegotiate // Renegotiating!
+ } schannelState = SchannelState::InitializeHandshake;
+
+ void reset();
+ bool acquireCredentialsHandle();
+ ULONG getContextRequirements();
+ bool createContext(); // for clients
+ bool acceptContext(); // for server
+ bool performHandshake();
+ bool verifyHandshake();
+ bool renegotiate();
+
+ bool sendToken(void *token, unsigned long tokenLength, bool emitError = true);
+ QString targetName() const;
+
+ bool checkSslErrors();
+ void deallocateContext();
+ void freeCredentialsHandle();
+ void closeCertificateStores();
+ void sendShutdown();
+
+ void initializeCertificateStores();
+ bool verifyCertContext(CERT_CONTEXT *certContext);
+
+ bool rootCertOnDemandLoadingAllowed();
+
+ SecPkgContext_ConnectionInfo connectionInfo = {};
+ SecPkgContext_StreamSizes streamSizes = {};
+
+ CredHandle credentialHandle; // Initialized in ctor
+ CtxtHandle contextHandle; // Initialized in ctor
+
+ QByteArray intermediateBuffer; // data which is left-over or incomplete
+
+ QHCertStorePointer localCertificateStore = nullptr;
+ QHCertStorePointer peerCertificateStore = nullptr;
+ QHCertStorePointer caCertificateStore = nullptr;
+
+ const CERT_CONTEXT *localCertContext = nullptr;
+
+ ULONG contextAttributes = 0;
+
+ bool renegotiating = false;
+ bool peerCertVerified = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSSLSOCKET_SCHANNEL_P_H
diff --git a/src/network/ssl/qsslsocket_winrt.cpp b/src/network/ssl/qsslsocket_winrt.cpp
index e72edcbc52..d54ac2ad73 100644
--- a/src/network/ssl/qsslsocket_winrt.cpp
+++ b/src/network/ssl/qsslsocket_winrt.cpp
@@ -207,9 +207,9 @@ void QSslSocketPrivate::resetDefaultCiphers()
QList<QSslCipher> QSslSocketBackendPrivate::defaultCiphers()
{
QList<QSslCipher> ciphers;
- const QString protocolStrings[] = { QStringLiteral("SSLv3"), QStringLiteral("TLSv1"),
+ const QString protocolStrings[] = { QStringLiteral("TLSv1"),
QStringLiteral("TLSv1.1"), QStringLiteral("TLSv1.2") };
- const QSsl::SslProtocol protocols[] = { QSsl::SslV3, QSsl::TlsV1_0, QSsl::TlsV1_1, QSsl::TlsV1_2 };
+ const QSsl::SslProtocol protocols[] = { QSsl::TlsV1_0, QSsl::TlsV1_1, QSsl::TlsV1_2 };
const int size = static_cast<int>(ARRAYSIZE(protocols));
ciphers.reserve(size);
for (int i = 0; i < size; ++i) {
@@ -234,10 +234,14 @@ void QSslSocketBackendPrivate::startClientEncryption()
QSsl::SslProtocol protocol = q->protocol();
switch (q->protocol()) {
- case QSsl::AnyProtocol:
+ case QSsl::SslV2:
case QSsl::SslV3:
+ setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
+ QStringLiteral("unsupported protocol"));
+ return;
+ case QSsl::AnyProtocol:
case QSsl::TlsV1SslV3:
- protectionLevel = SocketProtectionLevel_Ssl; // Only use this value if weak cipher support is required
+ protectionLevel = SocketProtectionLevel_Tls10;
break;
case QSsl::TlsV1_0:
protectionLevel = SocketProtectionLevel_Tls10;
@@ -251,6 +255,8 @@ void QSslSocketBackendPrivate::startClientEncryption()
case QSsl::TlsV1_0OrLater:
case QSsl::TlsV1_1OrLater:
case QSsl::TlsV1_2OrLater:
+ case QSsl::TlsV1_3:
+ case QSsl::TlsV1_3OrLater:
// TlsV1_0OrLater, TlsV1_1OrLater and TlsV1_2OrLater are disabled on WinRT
// because there is no good way to map them to the native API.
setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri
index b5603c1258..8bb70a2aed 100644
--- a/src/network/ssl/ssl.pri
+++ b/src/network/ssl/ssl.pri
@@ -29,7 +29,9 @@ qtConfig(ssl) {
ssl/qsslsocket.h \
ssl/qsslsocket_p.h \
ssl/qsslpresharedkeyauthenticator.h \
- ssl/qsslpresharedkeyauthenticator_p.h
+ ssl/qsslpresharedkeyauthenticator_p.h \
+ ssl/qocspresponse.h \
+ ssl/qocspresponse_p.h
SOURCES += ssl/qsslconfiguration.cpp \
ssl/qsslcipher.cpp \
ssl/qssldiffiehellmanparameters.cpp \
@@ -37,7 +39,8 @@ qtConfig(ssl) {
ssl/qsslkey_p.cpp \
ssl/qsslerror.cpp \
ssl/qsslsocket.cpp \
- ssl/qsslpresharedkeyauthenticator.cpp
+ ssl/qsslpresharedkeyauthenticator.cpp \
+ ssl/qocspresponse.cpp
winrt {
HEADERS += ssl/qsslsocket_winrt_p.h
@@ -49,6 +52,19 @@ qtConfig(ssl) {
ssl/qsslellipticcurve_dummy.cpp
}
+ qtConfig(schannel) {
+ HEADERS += ssl/qsslsocket_schannel_p.h
+ SOURCES += ssl/qsslsocket_schannel.cpp \
+ ssl/qsslcertificate_schannel.cpp \
+ ssl/qsslkey_schannel.cpp \
+ ssl/qsslkey_qt.cpp \
+ ssl/qssldiffiehellmanparameters_dummy.cpp \
+ ssl/qsslellipticcurve_dummy.cpp \
+ ssl/qsslsocket_qt.cpp
+
+ LIBS_PRIVATE += "-lSecur32" "-lCrypt32" "-lbcrypt" "-lncrypt"
+ }
+
qtConfig(securetransport) {
HEADERS += ssl/qsslsocket_mac_p.h
SOURCES += ssl/qssldiffiehellmanparameters_dummy.cpp \
@@ -84,6 +100,8 @@ qtConfig(ssl) {
SOURCES += ssl/qdtls_openssl.cpp
}
+ qtConfig(ocsp): HEADERS += ssl/qocsp_p.h
+
qtConfig(opensslv11) {
HEADERS += ssl/qsslsocket_openssl11_symbols_p.h
SOURCES += ssl/qsslsocket_openssl11.cpp \