summaryrefslogtreecommitdiffstats
path: root/src/network/ssl
diff options
context:
space:
mode:
authorPaul Wicking <paul.wicking@qt.io>2019-02-01 13:33:25 +0100
committerPaul Wicking <paul.wicking@qt.io>2019-02-01 13:33:26 +0100
commit2bc362c9fa37455afbeb56e5f1852188ede3eab4 (patch)
treeb89e3c2c082a0285e8cd91733ddbc772fe4362a5 /src/network/ssl
parent5de981d3bc3df874f9df35a6899345f4f61fa951 (diff)
parent481db443d502c8ebc169b7256cb696428cf02199 (diff)
Merge dev into 5.13
Diffstat (limited to 'src/network/ssl')
-rw-r--r--src/network/ssl/qpassworddigestor.cpp2
-rw-r--r--src/network/ssl/qsslsocket.cpp2
-rw-r--r--src/network/ssl/qsslsocket_schannel.cpp95
3 files changed, 94 insertions, 5 deletions
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/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index 88398488a5..fc9a44f896 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -2891,8 +2891,6 @@ bool QSslSocketPrivate::isMatchingHostname(const QSslCertificate &cert, const QS
if (QHostAddress(*it).isEqual(hostAddress, QHostAddress::StrictConversion))
return true;
}
-
- return false;
}
const QString lowerPeerName = QString::fromLatin1(QUrl::toAce(peerName));
diff --git a/src/network/ssl/qsslsocket_schannel.cpp b/src/network/ssl/qsslsocket_schannel.cpp
index 06bbc86469..965e9bf2f3 100644
--- a/src/network/ssl/qsslsocket_schannel.cpp
+++ b/src/network/ssl/qsslsocket_schannel.cpp
@@ -57,6 +57,11 @@
#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
@@ -391,6 +396,40 @@ 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;
@@ -684,13 +723,28 @@ bool QSslSocketBackendPrivate::createContext()
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)
- nullptr, // pInput (no input at the moment @future: alpn)
+ useAlpn ? &alpnBufferDesc : nullptr, // pInput
0, // Reserved2
&contextHandle, // phNewContext
&outputBufferDesc, // pOutput
@@ -725,7 +779,19 @@ bool QSslSocketBackendPrivate::acceptContext()
SecBuffer inBuffers[2];
inBuffers[0] = createSecBuffer(intermediateBuffer, SECBUFFER_TOKEN);
- inBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
+
+#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),
@@ -931,6 +997,31 @@ bool QSslSocketBackendPrivate::verifyHandshake()
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