Schannel support
Adds support for Schannel, an SSL backend for Windows, as an alternative to OpenSSL. [ChangeLog][QtNetwork][Ssl] Added support for Schannel on Desktop Windows. To build Qt with Schannel support use '-schannel' during configure. Task-number: QTBUG-62637 Change-Id: Ic4fb8ed3657dab994f9f4a4ac5cbddc7001a0a46 Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
parent
e0567d137d
commit
7cc6f78dd4
@ -18,6 +18,7 @@
|
||||
"ocsp": "boolean",
|
||||
"sctp": "boolean",
|
||||
"securetransport": "boolean",
|
||||
"schannel": "boolean",
|
||||
"ssl": "boolean",
|
||||
"system-proxies": "boolean"
|
||||
}
|
||||
@ -243,13 +244,13 @@
|
||||
"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 && libs.openssl_headers"
|
||||
"condition": "!features.securetransport && !features.schannel && libs.openssl_headers"
|
||||
},
|
||||
"openssl-linked": {
|
||||
"label": " Qt directly linked to OpenSSL",
|
||||
"autoDetect": false,
|
||||
"enable": "input.openssl == 'linked'",
|
||||
"condition": "!features.securetransport && libs.openssl",
|
||||
"condition": "!features.securetransport && !features.schannel && libs.openssl",
|
||||
"output": [
|
||||
"privateFeature",
|
||||
{ "type": "define", "name": "QT_LINKED_OPENSSL" }
|
||||
@ -264,9 +265,18 @@
|
||||
{ "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": {
|
||||
@ -412,6 +422,11 @@ For example:
|
||||
"args": "securetransport",
|
||||
"condition": "config.darwin"
|
||||
},
|
||||
{
|
||||
"type": "feature",
|
||||
"args": "schannel",
|
||||
"condition": "config.win32 && !config.winrt"
|
||||
},
|
||||
"openssl",
|
||||
"openssl-linked",
|
||||
"opensslv11",
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
@ -95,6 +99,10 @@ public:
|
||||
#ifndef QT_NO_OPENSSL
|
||||
if (x509)
|
||||
q_X509_free(x509);
|
||||
#endif
|
||||
#if QT_CONFIG(schannel)
|
||||
if (certificateContext)
|
||||
CertFreeCertificateContext(certificateContext);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
@ -139,7 +139,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 +206,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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
62
src/network/ssl/qsslcertificate_schannel.cpp
Normal file
62
src/network/ssl/qsslcertificate_schannel.cpp
Normal file
@ -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
|
@ -587,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
|
||||
@ -602,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)
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
/*!
|
||||
|
174
src/network/ssl/qsslkey_schannel.cpp
Normal file
174
src/network/ssl/qsslkey_schannel.cpp
Normal file
@ -0,0 +1,174 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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;
|
||||
}
|
||||
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
|
@ -94,6 +94,8 @@ QSslPreSharedKeyAuthenticatorPrivate::QSslPreSharedKeyAuthenticatorPrivate()
|
||||
\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
|
||||
*/
|
||||
|
||||
|
@ -203,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
|
||||
@ -322,6 +323,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>
|
||||
|
@ -228,7 +228,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;
|
||||
|
@ -171,7 +171,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
|
||||
|
||||
@ -209,7 +209,7 @@ protected:
|
||||
bool flushTriggered;
|
||||
};
|
||||
|
||||
#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
|
||||
|
1912
src/network/ssl/qsslsocket_schannel.cpp
Normal file
1912
src/network/ssl/qsslsocket_schannel.cpp
Normal file
File diff suppressed because it is too large
Load Diff
155
src/network/ssl/qsslsocket_schannel_p.h
Normal file
155
src/network/ssl/qsslsocket_schannel_p.h
Normal file
@ -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
|
@ -49,6 +49,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 \
|
||||
|
@ -814,7 +814,7 @@ void tst_QSslCertificate::task256066toPem()
|
||||
|
||||
void tst_QSslCertificate::nulInCN()
|
||||
{
|
||||
#if defined(QT_SECURETRANSPORT) || defined(Q_OS_WINRT)
|
||||
#if defined(QT_SECURETRANSPORT) || defined(Q_OS_WINRT) || QT_CONFIG(schannel)
|
||||
QSKIP("Generic QSslCertificatePrivate fails this test");
|
||||
#endif
|
||||
QList<QSslCertificate> certList =
|
||||
@ -833,7 +833,7 @@ void tst_QSslCertificate::nulInCN()
|
||||
|
||||
void tst_QSslCertificate::nulInSan()
|
||||
{
|
||||
#if defined(QT_SECURETRANSPORT) || defined(Q_OS_WINRT)
|
||||
#if defined(QT_SECURETRANSPORT) || defined(Q_OS_WINRT) || QT_CONFIG(schannel)
|
||||
QSKIP("Generic QSslCertificatePrivate fails this test");
|
||||
#endif
|
||||
QList<QSslCertificate> certList =
|
||||
|
@ -165,9 +165,9 @@ void tst_QSslKey::createPlainTestRows(bool pemOnly)
|
||||
foreach (KeyInfo keyInfo, keyInfoList) {
|
||||
if (pemOnly && keyInfo.format != QSsl::EncodingFormat::Pem)
|
||||
continue;
|
||||
#ifdef Q_OS_WINRT
|
||||
#if defined(Q_OS_WINRT) || QT_CONFIG(schannel)
|
||||
if (keyInfo.fileInfo.fileName().contains("RC2-64"))
|
||||
continue; // WinRT treats RC2 as 128 bit
|
||||
continue; // WinRT/Schannel treats RC2 as 128 bit
|
||||
#endif
|
||||
#if !defined(QT_NO_SSL) && defined(QT_NO_OPENSSL) // generic backend
|
||||
if (keyInfo.fileInfo.fileName().contains(QRegularExpression("-aes\\d\\d\\d-")))
|
||||
@ -599,11 +599,11 @@ void tst_QSslKey::encrypt()
|
||||
QFETCH(QByteArray, cipherText);
|
||||
QByteArray iv("abcdefgh");
|
||||
|
||||
#ifdef Q_OS_WINRT
|
||||
QEXPECT_FAIL("RC2-40-CBC, length 0", "WinRT treats RC2 as 128-bit", Abort);
|
||||
QEXPECT_FAIL("RC2-40-CBC, length 8", "WinRT treats RC2 as 128-bit", Abort);
|
||||
QEXPECT_FAIL("RC2-64-CBC, length 0", "WinRT treats RC2 as 128-bit", Abort);
|
||||
QEXPECT_FAIL("RC2-64-CBC, length 8", "WinRT treats RC2 as 128-bit", Abort);
|
||||
#if defined(Q_OS_WINRT) || QT_CONFIG(schannel)
|
||||
QEXPECT_FAIL("RC2-40-CBC, length 0", "WinRT/Schannel treats RC2 as 128-bit", Abort);
|
||||
QEXPECT_FAIL("RC2-40-CBC, length 8", "WinRT/Schannel treats RC2 as 128-bit", Abort);
|
||||
QEXPECT_FAIL("RC2-64-CBC, length 0", "WinRT/Schannel treats RC2 as 128-bit", Abort);
|
||||
QEXPECT_FAIL("RC2-64-CBC, length 8", "WinRT/Schannel treats RC2 as 128-bit", Abort);
|
||||
#endif
|
||||
QByteArray encrypted = QSslKeyPrivate::encrypt(cipher, plainText, key, iv);
|
||||
QCOMPARE(encrypted, cipherText);
|
||||
|
@ -641,6 +641,10 @@ void tst_QSslSocket::sslErrors()
|
||||
QFETCH(int, port);
|
||||
|
||||
QSslSocketPtr socket = newSocket();
|
||||
#if QT_CONFIG(schannel)
|
||||
// Needs to be < 1.2 because of the old certificate and <= 1.0 because of the mail server
|
||||
socket->setProtocol(QSsl::SslProtocol::TlsV1_0);
|
||||
#endif
|
||||
QSignalSpy sslErrorsSpy(socket.data(), SIGNAL(sslErrors(QList<QSslError>)));
|
||||
QSignalSpy peerVerifyErrorSpy(socket.data(), SIGNAL(peerVerifyError(QSslError)));
|
||||
|
||||
@ -720,6 +724,9 @@ void tst_QSslSocket::connectToHostEncrypted()
|
||||
return;
|
||||
|
||||
QSslSocketPtr socket = newSocket();
|
||||
#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2
|
||||
socket->setProtocol(QSsl::SslProtocol::TlsV1_1);
|
||||
#endif
|
||||
this->socket = socket.data();
|
||||
QVERIFY(socket->addCaCertificates(testDataDir + "certs/qt-test-server-cacert.pem"));
|
||||
#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
|
||||
@ -753,6 +760,9 @@ void tst_QSslSocket::connectToHostEncryptedWithVerificationPeerName()
|
||||
return;
|
||||
|
||||
QSslSocketPtr socket = newSocket();
|
||||
#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2
|
||||
socket->setProtocol(QSsl::SslProtocol::TlsV1_1);
|
||||
#endif
|
||||
this->socket = socket.data();
|
||||
|
||||
socket->addCaCertificates(testDataDir + "certs/qt-test-server-cacert.pem");
|
||||
@ -1413,6 +1423,11 @@ void tst_QSslSocket::setLocalCertificateChain()
|
||||
loop.exec();
|
||||
|
||||
QList<QSslCertificate> chain = socket->peerCertificateChain();
|
||||
#if QT_CONFIG(schannel)
|
||||
QEXPECT_FAIL("", "Schannel cannot send intermediate certificates not "
|
||||
"located in a system certificate store",
|
||||
Abort);
|
||||
#endif
|
||||
QCOMPARE(chain.size(), 2);
|
||||
QCOMPARE(chain[0].serialNumber(), QByteArray("10:a0:ad:77:58:f6:6e:ae:46:93:a3:43:f9:59:8a:9e"));
|
||||
QCOMPARE(chain[1].serialNumber(), QByteArray("3b:eb:99:c5:ea:d8:0b:5d:0b:97:5d:4f:06:75:4b:e1"));
|
||||
@ -1480,6 +1495,9 @@ void tst_QSslSocket::setSslConfiguration()
|
||||
QSslSocketPtr socket = newSocket();
|
||||
QFETCH(QSslConfiguration, configuration);
|
||||
socket->setSslConfiguration(configuration);
|
||||
#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2
|
||||
socket->setProtocol(QSsl::SslProtocol::TlsV1_1);
|
||||
#endif
|
||||
this->socket = socket.data();
|
||||
socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
|
||||
QFETCH(bool, works);
|
||||
@ -2174,6 +2192,9 @@ void tst_QSslSocket::verifyMode()
|
||||
return;
|
||||
|
||||
QSslSocket socket;
|
||||
#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2
|
||||
socket.setProtocol(QSsl::SslProtocol::TlsV1_1);
|
||||
#endif
|
||||
QCOMPARE(socket.peerVerifyMode(), QSslSocket::AutoVerifyPeer);
|
||||
socket.setPeerVerifyMode(QSslSocket::VerifyNone);
|
||||
QCOMPARE(socket.peerVerifyMode(), QSslSocket::VerifyNone);
|
||||
@ -2474,6 +2495,9 @@ void tst_QSslSocket::abortOnSslErrors()
|
||||
void tst_QSslSocket::readFromClosedSocket()
|
||||
{
|
||||
QSslSocketPtr socket = newSocket();
|
||||
#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2
|
||||
socket->setProtocol(QSsl::SslProtocol::TlsV1_1);
|
||||
#endif
|
||||
socket->ignoreSslErrors();
|
||||
socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
|
||||
socket->ignoreSslErrors();
|
||||
@ -3222,6 +3246,11 @@ void tst_QSslSocket::verifyClientCertificate()
|
||||
return;
|
||||
|
||||
QFETCH(QSslSocket::PeerVerifyMode, peerVerifyMode);
|
||||
#if QT_CONFIG(schannel)
|
||||
if (peerVerifyMode == QSslSocket::QueryPeer || peerVerifyMode == QSslSocket::AutoVerifyPeer)
|
||||
QSKIP("Schannel doesn't tackle requesting a certificate and not receiving one.");
|
||||
#endif
|
||||
|
||||
SslServer server;
|
||||
server.addCaCertificates = testDataDir + "certs/bogus-ca.crt";
|
||||
server.ignoreSslErrors = false;
|
||||
@ -3252,6 +3281,14 @@ void tst_QSslSocket::verifyClientCertificate()
|
||||
// check server socket
|
||||
QVERIFY(server.socket);
|
||||
|
||||
#if QT_CONFIG(schannel)
|
||||
// As additional info to the QEXPECT_FAIL below:
|
||||
// This is because schannel treats it as an error (client side) if you don't have a certificate
|
||||
// when asked for one.
|
||||
QEXPECT_FAIL("NoCert:VerifyPeer",
|
||||
"The client disconnects first, which causes the event "
|
||||
"loop to quit before the server disconnects.", Continue);
|
||||
#endif
|
||||
QCOMPARE(server.socket->state(), expectedState);
|
||||
QCOMPARE(server.socket->isEncrypted(), works);
|
||||
|
||||
@ -3260,6 +3297,13 @@ void tst_QSslSocket::verifyClientCertificate()
|
||||
QVERIFY(server.socket->peerCertificateChain().isEmpty());
|
||||
} else {
|
||||
QCOMPARE(server.socket->peerCertificate(), clientCerts.first());
|
||||
#if QT_CONFIG(schannel)
|
||||
if (clientCerts.count() == 1 && server.socket->peerCertificateChain().count() == 2) {
|
||||
QEXPECT_FAIL("",
|
||||
"Schannel includes the entire chain, not just the leaf and intermediates",
|
||||
Continue);
|
||||
}
|
||||
#endif
|
||||
QCOMPARE(server.socket->peerCertificateChain(), clientCerts);
|
||||
}
|
||||
|
||||
@ -3270,7 +3314,7 @@ void tst_QSslSocket::verifyClientCertificate()
|
||||
|
||||
void tst_QSslSocket::readBufferMaxSize()
|
||||
{
|
||||
#ifdef QT_SECURETRANSPORT
|
||||
#if defined(QT_SECURETRANSPORT) || QT_CONFIG(schannel)
|
||||
// QTBUG-55170:
|
||||
// SecureTransport back-end was ignoring read-buffer
|
||||
// size limit, resulting (potentially) in a constantly
|
||||
@ -3806,6 +3850,9 @@ void tst_QSslSocket::pskServer()
|
||||
{
|
||||
#ifdef Q_OS_WINRT
|
||||
QSKIP("Server-side encryption is not implemented on WinRT.");
|
||||
#endif
|
||||
#if QT_CONFIG(schannel)
|
||||
QSKIP("Schannel does not have PSK support implemented.");
|
||||
#endif
|
||||
QFETCH_GLOBAL(bool, setProxy);
|
||||
if (!QSslSocket::supportsSsl() || setProxy)
|
||||
|
Loading…
x
Reference in New Issue
Block a user