Let's encrypt datagrams
This patch adds DTLS support to QtNetwork module (and its OpenSSL back-end). DTLS over UDP is defined by RFC 6347. The new API consists of 1) QDtlsClientVerifier which checks if a client that sent us ClientHello is a real DTLS client by generating a cookie, sending a HelloVerifyRequest with this cookie attached, and then verifiying a cookie received back. To be deployed in combination with a server-side QUdpSocket. 2) QDtls - initiates and proceeds with a TLS handshake (client or server side), with certificates and/or pre-shared key (PSK), and encrypts/decrypts datagrams after the handshake has finished. This patch does not implement yet another UDP socket, instead it allows use of existing QUdpSocket(s), by adding DTLS support on top. OpenSSL back-end uses a custom BIO to make it work with QUdpSocket and give a finer control over IO operations. On the server side, demultiplexing is left to client code (could be done either by connecting QUdpSocket or by extracting address/port for an incoming datagram and then forwarding/dispatching them to the corresponding QDtls object). Task-number: QTPM-779 Change-Id: Ifcdf8586c70c3018b0c5549efc722e795f2c1c52 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io> Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>bb10
parent
48d6990e41
commit
ac583b686d
|
|
@ -0,0 +1,569 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** 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 "qsslconfiguration.h"
|
||||
#include "qudpsocket.h"
|
||||
#include "qdtls_p.h"
|
||||
#include "qssl_p.h"
|
||||
#include "qdtls.h"
|
||||
|
||||
#include "qglobal.h"
|
||||
|
||||
#if QT_CONFIG(openssl)
|
||||
#include "qdtls_openssl_p.h"
|
||||
#endif // QT_CONFIG
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
bool isDtlsProtocol(QSsl::SslProtocol protocol)
|
||||
{
|
||||
switch (protocol) {
|
||||
case QSsl::DtlsV1_0:
|
||||
case QSsl::DtlsV1_0OrLater:
|
||||
case QSsl::DtlsV1_2:
|
||||
case QSsl::DtlsV1_2OrLater:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QSslConfiguration QDtlsBasePrivate::configuration() const
|
||||
{
|
||||
auto copyPrivate = new QSslConfigurationPrivate(dtlsConfiguration);
|
||||
copyPrivate->ref.store(0); // the QSslConfiguration constructor refs up
|
||||
QSslConfiguration copy(copyPrivate);
|
||||
copyPrivate->sessionCipher = sessionCipher;
|
||||
copyPrivate->sessionProtocol = sessionProtocol;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
void QDtlsBasePrivate::setConfiguration(const QSslConfiguration &configuration)
|
||||
{
|
||||
dtlsConfiguration.localCertificateChain = configuration.localCertificateChain();
|
||||
dtlsConfiguration.privateKey = configuration.privateKey();
|
||||
dtlsConfiguration.ciphers = configuration.ciphers();
|
||||
dtlsConfiguration.ellipticCurves = configuration.ellipticCurves();
|
||||
dtlsConfiguration.preSharedKeyIdentityHint = configuration.preSharedKeyIdentityHint();
|
||||
dtlsConfiguration.dhParams = configuration.diffieHellmanParameters();
|
||||
dtlsConfiguration.caCertificates = configuration.caCertificates();
|
||||
dtlsConfiguration.peerVerifyDepth = configuration.peerVerifyDepth();
|
||||
dtlsConfiguration.peerVerifyMode = configuration.peerVerifyMode();
|
||||
Q_ASSERT(isDtlsProtocol(configuration.protocol()));
|
||||
dtlsConfiguration.protocol = configuration.protocol();
|
||||
dtlsConfiguration.sslOptions = configuration.d->sslOptions;
|
||||
dtlsConfiguration.sslSession = configuration.sessionTicket();
|
||||
dtlsConfiguration.sslSessionTicketLifeTimeHint = configuration.sessionTicketLifeTimeHint();
|
||||
dtlsConfiguration.nextAllowedProtocols = configuration.allowedNextProtocols();
|
||||
dtlsConfiguration.nextNegotiatedProtocol = configuration.nextNegotiatedProtocol();
|
||||
dtlsConfiguration.nextProtocolNegotiationStatus = configuration.nextProtocolNegotiationStatus();
|
||||
dtlsConfiguration.dtlsCookieEnabled = configuration.dtlsCookieVerificationEnabled();
|
||||
|
||||
clearDtlsError();
|
||||
}
|
||||
|
||||
bool QDtlsBasePrivate::setCookieGeneratorParameters(QCryptographicHash::Algorithm alg,
|
||||
const QByteArray &key)
|
||||
{
|
||||
if (!key.size()) {
|
||||
setDtlsError(QDtlsError::InvalidInputParameters,
|
||||
QDtls::tr("Invalid (empty) secret"));
|
||||
return false;
|
||||
}
|
||||
|
||||
clearDtlsError();
|
||||
|
||||
hashAlgorithm = alg;
|
||||
secret = key;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QDtlsClientVerifier::QDtlsClientVerifier(QObject *parent)
|
||||
#if QT_CONFIG(openssl)
|
||||
: QObject(*new QDtlsClientVerifierOpenSSL, parent)
|
||||
#endif // openssl
|
||||
{
|
||||
Q_D(QDtlsClientVerifier);
|
||||
|
||||
d->mode = QSslSocket::SslServerMode;
|
||||
// The default configuration suffices: verifier never does a full
|
||||
// handshake and upon verifying a cookie in a client hello message,
|
||||
// it reports success.
|
||||
auto conf = QSslConfiguration::defaultDtlsConfiguration();
|
||||
conf.setPeerVerifyMode(QSslSocket::VerifyNone);
|
||||
d->setConfiguration(conf);
|
||||
}
|
||||
|
||||
bool QDtlsClientVerifier::setCookieGeneratorParameters(const GeneratorParameters ¶ms)
|
||||
{
|
||||
Q_D(QDtlsClientVerifier);
|
||||
|
||||
return d->setCookieGeneratorParameters(params.hash, params.secret);
|
||||
}
|
||||
|
||||
QDtlsClientVerifier::GeneratorParameters QDtlsClientVerifier::cookieGeneratorParameters() const
|
||||
{
|
||||
Q_D(const QDtlsClientVerifier);
|
||||
|
||||
return {d->hashAlgorithm, d->secret};
|
||||
}
|
||||
|
||||
bool QDtlsClientVerifier::verifyClient(QUdpSocket *socket, const QByteArray &dgram,
|
||||
const QHostAddress &address, quint16 port)
|
||||
{
|
||||
Q_D(QDtlsClientVerifier);
|
||||
|
||||
if (!socket || address.isNull() || !dgram.size()) {
|
||||
d->setDtlsError(QDtlsError::InvalidInputParameters,
|
||||
tr("A valid UDP socket, non-empty datagram, valid address/port were expected"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (address.isBroadcast() || address.isMulticast()) {
|
||||
d->setDtlsError(QDtlsError::InvalidInputParameters,
|
||||
tr("Multicast and broadcast addresses are not supported"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return d->verifyClient(socket, dgram, address, port);
|
||||
}
|
||||
|
||||
QByteArray QDtlsClientVerifier::verifiedHello() const
|
||||
{
|
||||
Q_D(const QDtlsClientVerifier);
|
||||
|
||||
return d->verifiedClientHello;
|
||||
}
|
||||
|
||||
QDtlsError QDtlsClientVerifier::dtlsError() const
|
||||
{
|
||||
Q_D(const QDtlsClientVerifier);
|
||||
|
||||
return d->errorCode;
|
||||
}
|
||||
|
||||
QString QDtlsClientVerifier::dtlsErrorString() const
|
||||
{
|
||||
Q_D(const QDtlsBase);
|
||||
|
||||
return d->errorDescription;
|
||||
}
|
||||
|
||||
QDtls::QDtls(QSslSocket::SslMode mode, QObject *parent)
|
||||
#if QT_CONFIG(openssl)
|
||||
: QObject(*new QDtlsPrivateOpenSSL, parent)
|
||||
#endif
|
||||
{
|
||||
Q_D(QDtls);
|
||||
|
||||
d->mode = mode;
|
||||
setDtlsConfiguration(QSslConfiguration::defaultDtlsConfiguration());
|
||||
}
|
||||
|
||||
bool QDtls::setRemote(const QHostAddress &address, quint16 port,
|
||||
const QString &verificationName)
|
||||
{
|
||||
Q_D(QDtls);
|
||||
|
||||
if (d->handshakeState != HandshakeNotStarted) {
|
||||
d->setDtlsError(QDtlsError::InvalidOperation,
|
||||
tr("Cannot set remote after handshake started"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (address.isNull()) {
|
||||
d->setDtlsError(QDtlsError::InvalidInputParameters,
|
||||
tr("Invalid address"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (address.isBroadcast() || address.isMulticast()) {
|
||||
d->setDtlsError(QDtlsError::InvalidInputParameters,
|
||||
tr("Multicast and broadcast addresses are not supported"));
|
||||
return false;
|
||||
}
|
||||
|
||||
d->clearDtlsError();
|
||||
|
||||
d->remoteAddress = address;
|
||||
d->remotePort = port;
|
||||
d->peerVerificationName = verificationName;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QDtls::setPeerVerificationName(const QString &name)
|
||||
{
|
||||
Q_D(QDtls);
|
||||
|
||||
if (d->handshakeState != HandshakeNotStarted) {
|
||||
d->setDtlsError(QDtlsError::InvalidOperation,
|
||||
tr("Cannot set verification name after handshake started"));
|
||||
return false;
|
||||
}
|
||||
|
||||
d->clearDtlsError();
|
||||
d->peerVerificationName = name;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QHostAddress QDtls::remoteAddress() const
|
||||
{
|
||||
Q_D(const QDtls);
|
||||
|
||||
return d->remoteAddress;
|
||||
}
|
||||
|
||||
quint16 QDtls::remotePort() const
|
||||
{
|
||||
Q_D(const QDtlsBase);
|
||||
|
||||
return d->remotePort;
|
||||
}
|
||||
|
||||
QString QDtls::peerVerificationName() const
|
||||
{
|
||||
Q_D(const QDtls);
|
||||
|
||||
return d->peerVerificationName;
|
||||
}
|
||||
|
||||
QSslSocket::SslMode QDtls::sslMode() const
|
||||
{
|
||||
Q_D(const QDtls);
|
||||
|
||||
return d->mode;
|
||||
}
|
||||
|
||||
void QDtls::setMtuHint(quint16 mtuHint)
|
||||
{
|
||||
Q_D(QDtls);
|
||||
|
||||
d->mtuHint = mtuHint;
|
||||
}
|
||||
|
||||
quint16 QDtls::mtuHint() const
|
||||
{
|
||||
Q_D(const QDtls);
|
||||
|
||||
return d->mtuHint;
|
||||
}
|
||||
|
||||
bool QDtls::setCookieGeneratorParameters(const GeneratorParameters ¶ms)
|
||||
{
|
||||
Q_D(QDtls);
|
||||
|
||||
return d->setCookieGeneratorParameters(params.hash, params.secret);
|
||||
}
|
||||
|
||||
QDtls::GeneratorParameters QDtls::cookieGeneratorParameters() const
|
||||
{
|
||||
Q_D(const QDtls);
|
||||
|
||||
return {d->hashAlgorithm, d->secret};
|
||||
}
|
||||
|
||||
bool QDtls::setDtlsConfiguration(const QSslConfiguration &configuration)
|
||||
{
|
||||
Q_D(QDtls);
|
||||
|
||||
if (d->handshakeState != HandshakeNotStarted) {
|
||||
d->setDtlsError(QDtlsError::InvalidOperation,
|
||||
tr("Cannot set configuration after handshake started"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isDtlsProtocol(configuration.protocol())) {
|
||||
d->setConfiguration(configuration);
|
||||
return true;
|
||||
}
|
||||
|
||||
d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Unsupported protocol"));
|
||||
return false;
|
||||
}
|
||||
|
||||
QSslConfiguration QDtls::dtlsConfiguration() const
|
||||
{
|
||||
Q_D(const QDtls);
|
||||
|
||||
return d->configuration();
|
||||
}
|
||||
|
||||
QDtls::HandshakeState QDtls::handshakeState()const
|
||||
{
|
||||
Q_D(const QDtls);
|
||||
|
||||
return d->handshakeState;
|
||||
}
|
||||
|
||||
bool QDtls::doHandshake(QUdpSocket *socket, const QByteArray &dgram)
|
||||
{
|
||||
Q_D(QDtls);
|
||||
|
||||
if (d->handshakeState == HandshakeNotStarted)
|
||||
return startHandshake(socket, dgram);
|
||||
else if (d->handshakeState == HandshakeInProgress)
|
||||
return continueHandshake(socket, dgram);
|
||||
|
||||
d->setDtlsError(QDtlsError::InvalidOperation,
|
||||
tr("Cannot start/continue handshake, invalid handshake state"));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QDtls::startHandshake(QUdpSocket *socket, const QByteArray &datagram)
|
||||
{
|
||||
Q_D(QDtls);
|
||||
|
||||
if (!socket) {
|
||||
d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d->remoteAddress.isNull()) {
|
||||
d->setDtlsError(QDtlsError::InvalidOperation,
|
||||
tr("To start a handshake you must set remote address and port first"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sslMode() == QSslSocket::SslServerMode && !datagram.size()) {
|
||||
d->setDtlsError(QDtlsError::InvalidInputParameters,
|
||||
tr("To start a handshake, DTLS server requires non-empty datagram (client hello)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d->handshakeState != HandshakeNotStarted) {
|
||||
d->setDtlsError(QDtlsError::InvalidOperation,
|
||||
tr("Cannot start handshake, already done/in progress"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return d->startHandshake(socket, datagram);
|
||||
}
|
||||
|
||||
bool QDtls::handleTimeout(QUdpSocket *socket)
|
||||
{
|
||||
Q_D(QDtls);
|
||||
|
||||
if (!socket) {
|
||||
d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sslMode() == QSslSocket::SslServerMode) {
|
||||
d->setDtlsError(QDtlsError::InvalidOperation,
|
||||
tr("DTLS server connection does not have/handle timeouts"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return d->handleTimeout(socket);
|
||||
}
|
||||
|
||||
bool QDtls::continueHandshake(QUdpSocket *socket, const QByteArray &datagram)
|
||||
{
|
||||
Q_D(QDtls);
|
||||
|
||||
if (!socket || !datagram.size()) {
|
||||
d->setDtlsError(QDtlsError::InvalidInputParameters,
|
||||
tr("A valid QUdpSocket and non-empty datagram are needed to continue the handshake"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d->handshakeState != HandshakeInProgress) {
|
||||
d->setDtlsError(QDtlsError::InvalidOperation,
|
||||
tr("Cannot continue handshake, not in InProgress state"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return d->continueHandshake(socket, datagram);
|
||||
}
|
||||
|
||||
bool QDtls::resumeHandshakeAfterError(QUdpSocket *socket)
|
||||
{
|
||||
Q_D(QDtls);
|
||||
|
||||
if (!socket) {
|
||||
d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d->handshakeState != PeerVerificationFailed) {
|
||||
d->setDtlsError(QDtlsError::InvalidOperation,
|
||||
tr("Cannot resume, not in VerificationError state"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return d->resumeHandshake(socket);
|
||||
}
|
||||
|
||||
bool QDtls::abortHandshakeAfterError(QUdpSocket *socket)
|
||||
{
|
||||
Q_D(QDtls);
|
||||
|
||||
if (!socket) {
|
||||
d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d->handshakeState != PeerVerificationFailed) {
|
||||
d->setDtlsError(QDtlsError::InvalidOperation,
|
||||
tr("Not in VerificationError state, nothing to abort"));
|
||||
return false;
|
||||
}
|
||||
|
||||
d->abortHandshake(socket);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QDtls::sendShutdownAlert(QUdpSocket *socket)
|
||||
{
|
||||
Q_D(QDtls);
|
||||
|
||||
if (!socket) {
|
||||
d->setDtlsError(QDtlsError::InvalidInputParameters,
|
||||
tr("Invalid (nullptr) socket"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!d->connectionEncrypted) {
|
||||
d->setDtlsError(QDtlsError::InvalidOperation,
|
||||
tr("Cannot send shutdown alert, not encrypted"));
|
||||
return false;
|
||||
}
|
||||
|
||||
d->sendShutdownAlert(socket);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QDtls::connectionEncrypted() const
|
||||
{
|
||||
Q_D(const QDtls);
|
||||
|
||||
return d->connectionEncrypted;
|
||||
}
|
||||
|
||||
QSslCipher QDtls::sessionCipher() const
|
||||
{
|
||||
Q_D(const QDtls);
|
||||
|
||||
return d->sessionCipher;
|
||||
}
|
||||
|
||||
QSsl::SslProtocol QDtls::sessionProtocol() const
|
||||
{
|
||||
Q_D(const QDtls);
|
||||
|
||||
return d->sessionProtocol;
|
||||
}
|
||||
|
||||
qint64 QDtls::writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram)
|
||||
{
|
||||
Q_D(QDtls);
|
||||
|
||||
if (!socket) {
|
||||
d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!connectionEncrypted()) {
|
||||
d->setDtlsError(QDtlsError::InvalidOperation,
|
||||
tr("Cannot write a datagram, not in encrypted state"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return d->writeDatagramEncrypted(socket, dgram);
|
||||
}
|
||||
|
||||
QByteArray QDtls::decryptDatagram(QUdpSocket *socket, const QByteArray &dgram)
|
||||
{
|
||||
Q_D(QDtls);
|
||||
|
||||
if (!socket) {
|
||||
d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!connectionEncrypted()) {
|
||||
d->setDtlsError(QDtlsError::InvalidOperation,
|
||||
tr("Cannot read a datagram, not in encrypted state"));
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!dgram.size())
|
||||
return {};
|
||||
|
||||
return d->decryptDatagram(socket, dgram);
|
||||
}
|
||||
|
||||
QDtlsError QDtls::dtlsError() const
|
||||
{
|
||||
Q_D(const QDtls);
|
||||
|
||||
return d->errorCode;
|
||||
}
|
||||
|
||||
QString QDtls::dtlsErrorString() const
|
||||
{
|
||||
Q_D(const QDtls);
|
||||
|
||||
return d->errorDescription;
|
||||
}
|
||||
|
||||
QVector<QSslError> QDtls::peerVerificationErrors() const
|
||||
{
|
||||
Q_D(const QDtls);
|
||||
|
||||
return d->tlsErrors;
|
||||
}
|
||||
|
||||
void QDtls::ignoreVerificationErrors(const QVector<QSslError> &errorsToIgnore)
|
||||
{
|
||||
Q_D(QDtls);
|
||||
|
||||
d->tlsErrorsToIgnore = errorsToIgnore;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** 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 QDTLS_H
|
||||
#define QDTLS_H
|
||||
|
||||
#include <QtNetwork/qtnetworkglobal.h>
|
||||
|
||||
#include <QtNetwork/qsslsocket.h>
|
||||
#include <QtNetwork/qssl.h>
|
||||
|
||||
#include <QtCore/qcryptographichash.h>
|
||||
#include <QtCore/qobject.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
enum class QDtlsError : unsigned char
|
||||
{
|
||||
NoError,
|
||||
InvalidInputParameters,
|
||||
InvalidOperation,
|
||||
UnderlyingSocketError,
|
||||
RemoteClosedConnectionError,
|
||||
PeerVerificationError,
|
||||
TlsInitializationError,
|
||||
TlsFatalError,
|
||||
TlsNonFatalError
|
||||
};
|
||||
|
||||
class QHostAddress;
|
||||
class QUdpSocket;
|
||||
class QByteArray;
|
||||
class QString;
|
||||
|
||||
class QDtlsClientVerifierPrivate;
|
||||
class Q_NETWORK_EXPORT QDtlsClientVerifier : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
explicit QDtlsClientVerifier(QObject *parent = nullptr);
|
||||
|
||||
struct GeneratorParameters
|
||||
{
|
||||
GeneratorParameters() = default;
|
||||
GeneratorParameters(QCryptographicHash::Algorithm a, const QByteArray &s)
|
||||
: hash(a), secret(s)
|
||||
{
|
||||
}
|
||||
QCryptographicHash::Algorithm hash = QCryptographicHash::Sha1;
|
||||
QByteArray secret;
|
||||
};
|
||||
|
||||
bool setCookieGeneratorParameters(const GeneratorParameters ¶ms);
|
||||
GeneratorParameters cookieGeneratorParameters() const;
|
||||
|
||||
bool verifyClient(QUdpSocket *socket, const QByteArray &dgram,
|
||||
const QHostAddress &address, quint16 port);
|
||||
QByteArray verifiedHello() const;
|
||||
|
||||
QDtlsError dtlsError() const;
|
||||
QString dtlsErrorString() const;
|
||||
|
||||
private:
|
||||
|
||||
Q_DECLARE_PRIVATE(QDtlsClientVerifier)
|
||||
Q_DISABLE_COPY(QDtlsClientVerifier)
|
||||
};
|
||||
|
||||
class QSslPreSharedKeyAuthenticator;
|
||||
template<class> class QVector;
|
||||
class QSslConfiguration;
|
||||
class QSslCipher;
|
||||
class QSslError;
|
||||
|
||||
class QDtlsPrivate;
|
||||
class Q_NETWORK_EXPORT QDtls : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
enum HandshakeState
|
||||
{
|
||||
HandshakeNotStarted,
|
||||
HandshakeInProgress,
|
||||
PeerVerificationFailed,
|
||||
HandshakeComplete
|
||||
};
|
||||
|
||||
explicit QDtls(QSslSocket::SslMode mode, QObject *parent = nullptr);
|
||||
|
||||
bool setRemote(const QHostAddress &address, quint16 port,
|
||||
const QString &verificationName = {});
|
||||
bool setPeerVerificationName(const QString &name);
|
||||
QHostAddress remoteAddress() const;
|
||||
quint16 remotePort() const;
|
||||
QString peerVerificationName() const;
|
||||
QSslSocket::SslMode sslMode() const;
|
||||
|
||||
void setMtuHint(quint16 mtuHint);
|
||||
quint16 mtuHint() const;
|
||||
|
||||
using GeneratorParameters = QDtlsClientVerifier::GeneratorParameters;
|
||||
bool setCookieGeneratorParameters(const GeneratorParameters ¶ms);
|
||||
GeneratorParameters cookieGeneratorParameters() const;
|
||||
|
||||
bool setDtlsConfiguration(const QSslConfiguration &configuration);
|
||||
QSslConfiguration dtlsConfiguration() const;
|
||||
|
||||
HandshakeState handshakeState() const;
|
||||
|
||||
bool doHandshake(QUdpSocket *socket, const QByteArray &dgram = {});
|
||||
bool handleTimeout(QUdpSocket *socket);
|
||||
bool resumeHandshakeAfterError(QUdpSocket *socket);
|
||||
bool abortHandshakeAfterError(QUdpSocket *socket);
|
||||
bool sendShutdownAlert(QUdpSocket *socket);
|
||||
|
||||
bool connectionEncrypted() const;
|
||||
QSslCipher sessionCipher() const;
|
||||
QSsl::SslProtocol sessionProtocol() const;
|
||||
|
||||
qint64 writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram);
|
||||
QByteArray decryptDatagram(QUdpSocket *socket, const QByteArray &dgram);
|
||||
|
||||
QDtlsError dtlsError() const;
|
||||
QString dtlsErrorString() const;
|
||||
|
||||
QVector<QSslError> peerVerificationErrors() const;
|
||||
void ignoreVerificationErrors(const QVector<QSslError> &errorsToIgnore);
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
void pskRequired(QSslPreSharedKeyAuthenticator *authenticator);
|
||||
void handshakeTimeout();
|
||||
|
||||
private:
|
||||
|
||||
bool startHandshake(QUdpSocket *socket, const QByteArray &dgram);
|
||||
bool continueHandshake(QUdpSocket *socket, const QByteArray &dgram);
|
||||
|
||||
Q_DECLARE_PRIVATE(QDtls)
|
||||
Q_DISABLE_COPY(QDtls)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QDTLS_H
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,212 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** 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 QDTLS_OPENSSL_P_H
|
||||
#define QDTLS_OPENSSL_P_H
|
||||
|
||||
#include <private/qtnetworkglobal_p.h>
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
QT_REQUIRE_CONFIG(openssl);
|
||||
|
||||
#include <openssl/ossl_typ.h>
|
||||
|
||||
#include "qdtls_p.h"
|
||||
|
||||
#include <private/qsslcontext_openssl_p.h>
|
||||
#include <private/qsslsocket_openssl_p.h>
|
||||
|
||||
#include <QtNetwork/qsslpresharedkeyauthenticator.h>
|
||||
#include <QtNetwork/qhostaddress.h>
|
||||
|
||||
#include <QtCore/qcryptographichash.h>
|
||||
#include <QtCore/qsharedpointer.h>
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qvector.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 QDtlsPrivateOpenSSL;
|
||||
class QUdpSocket;
|
||||
|
||||
namespace dtlsopenssl
|
||||
{
|
||||
|
||||
class DtlsState
|
||||
{
|
||||
public:
|
||||
// Note, bioMethod, if allocated (i.e. OpenSSL version >= 1.1) _must_
|
||||
// outlive BIOs it was used to create. Thus the order of declarations
|
||||
// here matters.
|
||||
using BioMethod = QSharedPointer<BIO_METHOD>;
|
||||
BioMethod bioMethod;
|
||||
|
||||
using TlsContext = QSharedPointer<QSslContext>;
|
||||
TlsContext tlsContext;
|
||||
|
||||
using TlsConnection = QSharedPointer<SSL>;
|
||||
TlsConnection tlsConnection;
|
||||
|
||||
QByteArray dgram;
|
||||
|
||||
QHostAddress remoteAddress;
|
||||
quint16 remotePort = 0;
|
||||
|
||||
QVector<QSslErrorEntry> x509Errors;
|
||||
|
||||
long peeking = false;
|
||||
QUdpSocket *udpSocket = nullptr;
|
||||
bool writeSuppressed = false;
|
||||
|
||||
bool init(QDtlsBasePrivate *dtlsBase, QUdpSocket *socket,
|
||||
const QHostAddress &remote, quint16 port,
|
||||
const QByteArray &receivedMessage);
|
||||
|
||||
void reset();
|
||||
|
||||
QDtlsPrivateOpenSSL *dtlsPrivate = nullptr;
|
||||
QByteArray secret;
|
||||
|
||||
#ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
|
||||
QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha1;
|
||||
#else
|
||||
QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha256;
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
bool initTls(QDtlsBasePrivate *dtlsBase);
|
||||
bool initCtxAndConnection(QDtlsBasePrivate *dtlsBase);
|
||||
bool initBIO(QDtlsBasePrivate *dtlsBase);
|
||||
void setLinkMtu(QDtlsBasePrivate *dtlsBase);
|
||||
};
|
||||
|
||||
} // namespace dtlsopenssl
|
||||
|
||||
class QDtlsClientVerifierOpenSSL : public QDtlsClientVerifierPrivate
|
||||
{
|
||||
public:
|
||||
|
||||
QDtlsClientVerifierOpenSSL();
|
||||
|
||||
bool verifyClient(QUdpSocket *socket, const QByteArray &dgram,
|
||||
const QHostAddress &address, quint16 port) override;
|
||||
|
||||
private:
|
||||
dtlsopenssl::DtlsState dtls;
|
||||
};
|
||||
|
||||
class QDtlsPrivateOpenSSL : public QDtlsPrivate
|
||||
{
|
||||
public:
|
||||
QDtlsPrivateOpenSSL();
|
||||
|
||||
bool startHandshake(QUdpSocket *socket, const QByteArray &datagram) override;
|
||||
bool continueHandshake(QUdpSocket *socket, const QByteArray &datagram) override;
|
||||
bool resumeHandshake(QUdpSocket *socket) override;
|
||||
void abortHandshake(QUdpSocket *socket) override;
|
||||
bool handleTimeout(QUdpSocket *socket) override;
|
||||
void sendShutdownAlert(QUdpSocket *socket) override;
|
||||
|
||||
qint64 writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &datagram) override;
|
||||
QByteArray decryptDatagram(QUdpSocket *socket, const QByteArray &tlsdgram) override;
|
||||
|
||||
unsigned pskClientCallback(const char *hint, char *identity, unsigned max_identity_len,
|
||||
unsigned char *psk, unsigned max_psk_len);
|
||||
unsigned pskServerCallback(const char *identity, unsigned char *psk,
|
||||
unsigned max_psk_len);
|
||||
|
||||
private:
|
||||
|
||||
bool verifyPeer();
|
||||
void storePeerCertificates();
|
||||
bool tlsErrorsWereIgnored() const;
|
||||
void fetchNegotiatedParameters();
|
||||
void reportTimeout();
|
||||
void resetDtls();
|
||||
|
||||
QVector<QSslErrorEntry> opensslErrors;
|
||||
dtlsopenssl::DtlsState dtls;
|
||||
|
||||
// We have to externally handle timeouts since we have non-blocking
|
||||
// sockets and OpenSSL(DTLS) with non-blocking UDP sockets does not
|
||||
// know if a timeout has occurred.
|
||||
struct TimeoutHandler : QObject
|
||||
{
|
||||
TimeoutHandler() = default;
|
||||
|
||||
void start(int hintMs = 0);
|
||||
void doubleTimeout();
|
||||
void resetTimeout() {timeoutMs = 1000;}
|
||||
void stop();
|
||||
void timerEvent(QTimerEvent *event);
|
||||
|
||||
int timerId = -1;
|
||||
int timeoutMs = 1000;
|
||||
|
||||
QDtlsPrivateOpenSSL *dtlsConnection = nullptr;
|
||||
};
|
||||
|
||||
// We will initialize it 'lazily', just in case somebody wants to move
|
||||
// QDtls to another thread.
|
||||
QScopedPointer<TimeoutHandler> timeoutHandler;
|
||||
bool connectionWasShutdown = false;
|
||||
QSslPreSharedKeyAuthenticator pskAuthenticator;
|
||||
QByteArray identityHint;
|
||||
|
||||
Q_DECLARE_PUBLIC(QDtls)
|
||||
};
|
||||
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QDTLS_OPENSSL_P_H
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2017 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 QDTLS_P_H
|
||||
#define QDTLS_P_H
|
||||
|
||||
#include <private/qtnetworkglobal_p.h>
|
||||
|
||||
#include "qdtls.h"
|
||||
|
||||
#include <private/qsslconfiguration_p.h>
|
||||
#include <private/qobject_p.h>
|
||||
|
||||
#include <QtNetwork/qabstractsocket.h>
|
||||
#include <QtNetwork/qhostaddress.h>
|
||||
#include <QtNetwork/qsslsocket.h>
|
||||
#include <QtNetwork/qsslcipher.h>
|
||||
#include <QtNetwork/qssl.h>
|
||||
|
||||
#include <QtCore/qcryptographichash.h>
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qstring.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 QHostAddress;
|
||||
|
||||
class QDtlsBasePrivate : public QObjectPrivate
|
||||
{
|
||||
public:
|
||||
|
||||
void setDtlsError(QDtlsError code, const QString &description)
|
||||
{
|
||||
errorCode = code;
|
||||
errorDescription = description;
|
||||
}
|
||||
|
||||
void clearDtlsError()
|
||||
{
|
||||
errorCode = QDtlsError::NoError;
|
||||
errorDescription.clear();
|
||||
}
|
||||
|
||||
void setConfiguration(const QSslConfiguration &configuration);
|
||||
QSslConfiguration configuration() const;
|
||||
|
||||
bool setCookieGeneratorParameters(QCryptographicHash::Algorithm alg,
|
||||
const QByteArray &secret);
|
||||
|
||||
QHostAddress remoteAddress;
|
||||
quint16 remotePort = 0;
|
||||
quint16 mtuHint = 0;
|
||||
|
||||
QDtlsError errorCode = QDtlsError::NoError;
|
||||
QString errorDescription;
|
||||
QSslConfigurationPrivate dtlsConfiguration;
|
||||
QSslSocket::SslMode mode = QSslSocket::SslClientMode;
|
||||
QSslCipher sessionCipher;
|
||||
QSsl::SslProtocol sessionProtocol = QSsl::UnknownProtocol;
|
||||
QString peerVerificationName;
|
||||
QByteArray secret;
|
||||
|
||||
#ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
|
||||
QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha1;
|
||||
#else
|
||||
QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha256;
|
||||
#endif
|
||||
};
|
||||
|
||||
class QDtlsClientVerifierPrivate : public QDtlsBasePrivate
|
||||
{
|
||||
public:
|
||||
|
||||
QByteArray verifiedClientHello;
|
||||
|
||||
virtual bool verifyClient(QUdpSocket *socket, const QByteArray &dgram,
|
||||
const QHostAddress &address, quint16 port) = 0;
|
||||
};
|
||||
|
||||
class QDtlsPrivate : public QDtlsBasePrivate
|
||||
{
|
||||
public:
|
||||
|
||||
virtual bool startHandshake(QUdpSocket *socket, const QByteArray &dgram) = 0;
|
||||
virtual bool handleTimeout(QUdpSocket *socket) = 0;
|
||||
virtual bool continueHandshake(QUdpSocket *socket, const QByteArray &dgram) = 0;
|
||||
virtual bool resumeHandshake(QUdpSocket *socket) = 0;
|
||||
virtual void abortHandshake(QUdpSocket *socket) = 0;
|
||||
virtual void sendShutdownAlert(QUdpSocket *socket) = 0;
|
||||
|
||||
virtual qint64 writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram) = 0;
|
||||
virtual QByteArray decryptDatagram(QUdpSocket *socket, const QByteArray &dgram) = 0;
|
||||
|
||||
QDtls::HandshakeState handshakeState = QDtls::HandshakeNotStarted;
|
||||
|
||||
QVector<QSslError> tlsErrors;
|
||||
QVector<QSslError> tlsErrorsToIgnore;
|
||||
|
||||
bool connectionEncrypted = false;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QDTLS_P_H
|
||||
|
|
@ -73,6 +73,11 @@ class QSslKey;
|
|||
class QSslEllipticCurve;
|
||||
class QSslDiffieHellmanParameters;
|
||||
|
||||
namespace dtlsopenssl
|
||||
{
|
||||
class DtlsState;
|
||||
}
|
||||
|
||||
class QSslConfigurationPrivate;
|
||||
class Q_NETWORK_EXPORT QSslConfiguration
|
||||
{
|
||||
|
|
@ -188,6 +193,8 @@ private:
|
|||
friend class QSslConfigurationPrivate;
|
||||
friend class QSslSocketBackendPrivate;
|
||||
friend class QSslContext;
|
||||
friend class QDtlsBasePrivate;
|
||||
friend class dtlsopenssl::DtlsState;
|
||||
QSslConfiguration(QSslConfigurationPrivate *dd);
|
||||
QSharedDataPointer<QSslConfigurationPrivate> d;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -59,11 +59,24 @@ QT_BEGIN_NAMESPACE
|
|||
extern int q_X509Callback(int ok, X509_STORE_CTX *ctx);
|
||||
extern QString getErrorsFromOpenSsl();
|
||||
|
||||
// defined in qdtls_openssl.cpp:
|
||||
namespace dtlscallbacks
|
||||
{
|
||||
extern "C" int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx);
|
||||
extern "C" int q_generate_cookie_callback(SSL *ssl, unsigned char *dst,
|
||||
unsigned *cookieLength);
|
||||
extern "C" int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie,
|
||||
unsigned cookieLength);
|
||||
}
|
||||
|
||||
static inline QString msgErrorSettingEllipticCurves(const QString &why)
|
||||
{
|
||||
return QSslSocket::tr("Error when setting the elliptic curves (%1)").arg(why);
|
||||
}
|
||||
|
||||
// Defined in qsslsocket.cpp
|
||||
QList<QSslCipher> q_getDefaultDtlsCiphers();
|
||||
|
||||
// static
|
||||
void QSslContext::initSslContext(QSslContext *sslContext, QSslSocket::SslMode mode, const QSslConfiguration &configuration, bool allowRootCertOnDemandLoading)
|
||||
{
|
||||
|
|
@ -74,14 +87,25 @@ void QSslContext::initSslContext(QSslContext *sslContext, QSslSocket::SslMode mo
|
|||
|
||||
bool reinitialized = false;
|
||||
bool unsupportedProtocol = false;
|
||||
bool isDtls = false;
|
||||
init_context:
|
||||
if (sslContext->sslConfiguration.protocol() == QSsl::SslV2) {
|
||||
// SSL 2 is no longer supported, but chosen deliberately -> error
|
||||
sslContext->ctx = nullptr;
|
||||
unsupportedProtocol = true;
|
||||
} else {
|
||||
// The ssl options will actually control the supported methods
|
||||
sslContext->ctx = q_SSL_CTX_new(client ? q_TLS_client_method() : q_TLS_server_method());
|
||||
switch (sslContext->sslConfiguration.protocol()) {
|
||||
case QSsl::DtlsV1_0:
|
||||
case QSsl::DtlsV1_0OrLater:
|
||||
case QSsl::DtlsV1_2:
|
||||
case QSsl::DtlsV1_2OrLater:
|
||||
isDtls = true;
|
||||
sslContext->ctx = q_SSL_CTX_new(client ? q_DTLS_client_method() : q_DTLS_server_method());
|
||||
break;
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
if (!sslContext->ctx) {
|
||||
|
|
@ -100,8 +124,10 @@ init_context:
|
|||
return;
|
||||
}
|
||||
|
||||
long minVersion = TLS_ANY_VERSION;
|
||||
long maxVersion = TLS_ANY_VERSION;
|
||||
const long anyVersion = isDtls ? DTLS_ANY_VERSION : TLS_ANY_VERSION;
|
||||
long minVersion = anyVersion;
|
||||
long maxVersion = anyVersion;
|
||||
|
||||
switch (sslContext->sslConfiguration.protocol()) {
|
||||
// The single-protocol versions first:
|
||||
case QSsl::SslV3:
|
||||
|
|
@ -140,12 +166,21 @@ init_context:
|
|||
maxVersion = TLS_MAX_VERSION;
|
||||
break;
|
||||
case QSsl::DtlsV1_0:
|
||||
minVersion = DTLS1_VERSION;
|
||||
maxVersion = DTLS1_VERSION;
|
||||
break;
|
||||
case QSsl::DtlsV1_0OrLater:
|
||||
minVersion = DTLS1_VERSION;
|
||||
maxVersion = DTLS_MAX_VERSION;
|
||||
break;
|
||||
case QSsl::DtlsV1_2:
|
||||
minVersion = DTLS1_2_VERSION;
|
||||
maxVersion = DTLS1_2_VERSION;
|
||||
break;
|
||||
case QSsl::DtlsV1_2OrLater:
|
||||
sslContext->errorStr = QSslSocket::tr("unsupported protocol");
|
||||
sslContext->errorCode = QSslError::UnspecifiedError;
|
||||
return;
|
||||
minVersion = DTLS1_2_VERSION;
|
||||
maxVersion = DTLS_MAX_VERSION;
|
||||
break;
|
||||
case QSsl::SslV2:
|
||||
// This protocol is not supported by OpenSSL 1.1 and we handle
|
||||
// it as an error (see the code above).
|
||||
|
|
@ -155,14 +190,14 @@ init_context:
|
|||
break;
|
||||
}
|
||||
|
||||
if (minVersion != TLS_ANY_VERSION
|
||||
if (minVersion != anyVersion
|
||||
&& !q_SSL_CTX_set_min_proto_version(sslContext->ctx, minVersion)) {
|
||||
sslContext->errorStr = QSslSocket::tr("Error while setting the minimal protocol version");
|
||||
sslContext->errorCode = QSslError::UnspecifiedError;
|
||||
return;
|
||||
}
|
||||
|
||||
if (maxVersion != TLS_ANY_VERSION
|
||||
if (maxVersion != anyVersion
|
||||
&& !q_SSL_CTX_set_max_proto_version(sslContext->ctx, maxVersion)) {
|
||||
sslContext->errorStr = QSslSocket::tr("Error while setting the maximum protocol version");
|
||||
sslContext->errorCode = QSslError::UnspecifiedError;
|
||||
|
|
@ -182,7 +217,8 @@ init_context:
|
|||
bool first = true;
|
||||
QList<QSslCipher> ciphers = sslContext->sslConfiguration.ciphers();
|
||||
if (ciphers.isEmpty())
|
||||
ciphers = QSslSocketPrivate::defaultCiphers();
|
||||
ciphers = isDtls ? q_getDefaultDtlsCiphers() : QSslSocketPrivate::defaultCiphers();
|
||||
|
||||
for (const QSslCipher &cipher : qAsConst(ciphers)) {
|
||||
if (first)
|
||||
first = false;
|
||||
|
|
@ -289,7 +325,13 @@ init_context:
|
|||
if (sslContext->sslConfiguration.peerVerifyMode() == QSslSocket::VerifyNone) {
|
||||
q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_NONE, nullptr);
|
||||
} else {
|
||||
q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_PEER, q_X509Callback);
|
||||
q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_PEER,
|
||||
isDtls ? dtlscallbacks::q_X509DtlsCallback : q_X509Callback);
|
||||
}
|
||||
|
||||
if (mode == QSslSocket::SslServerMode && isDtls && configuration.dtlsCookieVerificationEnabled()) {
|
||||
q_SSL_CTX_set_cookie_generate_cb(sslContext->ctx, dtlscallbacks::q_generate_cookie_callback);
|
||||
q_SSL_CTX_set_cookie_verify_cb(sslContext->ctx, dtlscallbacks::q_verify_cookie_callback);
|
||||
}
|
||||
|
||||
// Set verification depth.
|
||||
|
|
|
|||
|
|
@ -56,11 +56,24 @@ QT_BEGIN_NAMESPACE
|
|||
extern int q_X509Callback(int ok, X509_STORE_CTX *ctx);
|
||||
extern QString getErrorsFromOpenSsl();
|
||||
|
||||
// defined in qdtls_openssl.cpp:
|
||||
namespace dtlscallbacks
|
||||
{
|
||||
extern "C" int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx);
|
||||
extern "C" int q_generate_cookie_callback(SSL *ssl, unsigned char *dst,
|
||||
unsigned *cookieLength);
|
||||
extern "C" int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie,
|
||||
unsigned cookieLength);
|
||||
}
|
||||
|
||||
static inline QString msgErrorSettingEllipticCurves(const QString &why)
|
||||
{
|
||||
return QSslSocket::tr("Error when setting the elliptic curves (%1)").arg(why);
|
||||
}
|
||||
|
||||
// Defined in qsslsocket.cpp
|
||||
QList<QSslCipher> q_getDefaultDtlsCiphers();
|
||||
|
||||
// static
|
||||
void QSslContext::initSslContext(QSslContext *sslContext, QSslSocket::SslMode mode, const QSslConfiguration &configuration, bool allowRootCertOnDemandLoading)
|
||||
{
|
||||
|
|
@ -68,17 +81,25 @@ void QSslContext::initSslContext(QSslContext *sslContext, QSslSocket::SslMode mo
|
|||
sslContext->errorCode = QSslError::NoError;
|
||||
|
||||
bool client = (mode == QSslSocket::SslClientMode);
|
||||
|
||||
bool reinitialized = false;
|
||||
bool unsupportedProtocol = false;
|
||||
bool isDtls = false;
|
||||
init_context:
|
||||
switch (sslContext->sslConfiguration.protocol()) {
|
||||
case QSsl::DtlsV1_0:
|
||||
case QSsl::DtlsV1_0OrLater:
|
||||
isDtls = true;
|
||||
sslContext->ctx = q_SSL_CTX_new(client ? q_DTLSv1_client_method() : q_DTLSv1_server_method());
|
||||
break;
|
||||
case QSsl::DtlsV1_2:
|
||||
case QSsl::DtlsV1_2OrLater:
|
||||
sslContext->ctx = 0;
|
||||
unsupportedProtocol = true;
|
||||
// OpenSSL 1.0.2 and below will probably never receive TLS 1.3, so
|
||||
// technically 1.2 or later is 1.2 and will stay so.
|
||||
isDtls = true;
|
||||
sslContext->ctx = q_SSL_CTX_new(client ? q_DTLSv1_2_client_method() : q_DTLSv1_2_server_method());
|
||||
break;
|
||||
case QSsl::DtlsV1_0OrLater:
|
||||
isDtls = true;
|
||||
sslContext->ctx = q_SSL_CTX_new(client ? q_DTLS_client_method() : q_DTLS_server_method());
|
||||
break;
|
||||
case QSsl::SslV2:
|
||||
#ifndef OPENSSL_NO_SSL2
|
||||
|
|
@ -145,6 +166,12 @@ init_context:
|
|||
break;
|
||||
}
|
||||
|
||||
if (!client && isDtls && configuration.peerVerifyMode() != QSslSocket::VerifyNone) {
|
||||
sslContext->errorStr = QSslSocket::tr("DTLS server requires a 'VerifyNone' mode with your version of OpenSSL");
|
||||
sslContext->errorCode = QSslError::UnspecifiedError;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sslContext->ctx) {
|
||||
// After stopping Flash 10 the SSL library loses its ciphers. Try re-adding them
|
||||
// by re-initializing the library.
|
||||
|
|
@ -162,6 +189,7 @@ init_context:
|
|||
}
|
||||
|
||||
// Enable bug workarounds.
|
||||
// DTLSTODO: check this setupOpenSslOptions ...
|
||||
long options = QSslSocketBackendPrivate::setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions);
|
||||
q_SSL_CTX_set_options(sslContext->ctx, options);
|
||||
|
||||
|
|
@ -177,7 +205,7 @@ init_context:
|
|||
bool first = true;
|
||||
QList<QSslCipher> ciphers = sslContext->sslConfiguration.ciphers();
|
||||
if (ciphers.isEmpty())
|
||||
ciphers = QSslSocketPrivate::defaultCiphers();
|
||||
ciphers = isDtls ? q_getDefaultDtlsCiphers() : QSslSocketPrivate::defaultCiphers();
|
||||
for (const QSslCipher &cipher : qAsConst(ciphers)) {
|
||||
if (first)
|
||||
first = false;
|
||||
|
|
@ -284,7 +312,13 @@ init_context:
|
|||
if (sslContext->sslConfiguration.peerVerifyMode() == QSslSocket::VerifyNone) {
|
||||
q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_NONE, 0);
|
||||
} else {
|
||||
q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_PEER, q_X509Callback);
|
||||
q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_PEER,
|
||||
isDtls ? dtlscallbacks::q_X509DtlsCallback : q_X509Callback);
|
||||
}
|
||||
|
||||
if (mode == QSslSocket::SslServerMode && isDtls && configuration.dtlsCookieVerificationEnabled()) {
|
||||
q_SSL_CTX_set_cookie_generate_cb(sslContext->ctx, dtlscallbacks::q_generate_cookie_callback);
|
||||
q_SSL_CTX_set_cookie_verify_cb(sslContext->ctx, CookieVerifyCallback(dtlscallbacks::q_verify_cookie_callback));
|
||||
}
|
||||
|
||||
// Set verification depth.
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ public:
|
|||
private:
|
||||
friend Q_NETWORK_EXPORT bool operator==(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs);
|
||||
friend class QSslSocketBackendPrivate;
|
||||
friend class QDtlsPrivateOpenSSL;
|
||||
|
||||
QSharedDataPointer<QSslPreSharedKeyAuthenticatorPrivate> d;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -842,7 +842,7 @@ void QSslSocketBackendPrivate::transmit()
|
|||
} while (ssl && transmitting);
|
||||
}
|
||||
|
||||
static QSslError _q_OpenSSL_to_QSslError(int errorCode, const QSslCertificate &cert)
|
||||
QSslError _q_OpenSSL_to_QSslError(int errorCode, const QSslCertificate &cert)
|
||||
{
|
||||
QSslError error;
|
||||
switch (errorCode) {
|
||||
|
|
|
|||
|
|
@ -307,6 +307,12 @@ DEFINEFUNC3(DSA *, d2i_DSAPrivateKey, DSA **a, a, unsigned char **b, b, long c,
|
|||
DEFINEFUNC3(EC_KEY *, d2i_ECPrivateKey, EC_KEY **a, a, unsigned char **b, b, long c, c, return 0, return)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
DEFINEFUNC(const SSL_METHOD *, DTLSv1_server_method, void, DUMMYARG, return nullptr, return)
|
||||
DEFINEFUNC(const SSL_METHOD *, DTLSv1_client_method, void, DUMMYARG, return nullptr, return)
|
||||
DEFINEFUNC(const SSL_METHOD *, DTLSv1_2_server_method, void, DUMMYARG, return nullptr, return)
|
||||
DEFINEFUNC(const SSL_METHOD *, DTLSv1_2_client_method, void, DUMMYARG, return nullptr, return)
|
||||
|
||||
DEFINEFUNC(char *, CONF_get1_default_config_file, DUMMYARG, DUMMYARG, return 0, return)
|
||||
DEFINEFUNC(void, OPENSSL_add_all_algorithms_noconf, void, DUMMYARG, return, DUMMYARG)
|
||||
DEFINEFUNC(void, OPENSSL_add_all_algorithms_conf, void, DUMMYARG, return, DUMMYARG)
|
||||
|
|
@ -555,7 +561,6 @@ DEFINEFUNC3(void, SSL_get0_alpn_selected, const SSL *s, s, const unsigned char *
|
|||
// DTLS:
|
||||
DEFINEFUNC2(void, SSL_CTX_set_cookie_generate_cb, SSL_CTX *ctx, ctx, CookieGenerateCallback cb, cb, return, DUMMYARG)
|
||||
DEFINEFUNC2(void, SSL_CTX_set_cookie_verify_cb, SSL_CTX *ctx, ctx, CookieVerifyCallback cb, cb, return, DUMMYARG)
|
||||
DEFINEFUNC2(BIO *, BIO_new_dgram, int fd, fd, int flag, flag, return nullptr, return)
|
||||
DEFINEFUNC(const SSL_METHOD *, DTLS_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
|
||||
DEFINEFUNC(const SSL_METHOD *, DTLS_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
|
||||
DEFINEFUNC2(void, BIO_set_flags, BIO *b, b, int flags, flags, return, DUMMYARG)
|
||||
|
|
@ -1046,6 +1051,12 @@ bool q_resolveOpenSslSymbols()
|
|||
RESOLVEFUNC(d2i_DSAPrivateKey)
|
||||
RESOLVEFUNC(d2i_RSAPrivateKey)
|
||||
#endif
|
||||
|
||||
RESOLVEFUNC(DTLSv1_server_method)
|
||||
RESOLVEFUNC(DTLSv1_client_method)
|
||||
RESOLVEFUNC(DTLSv1_2_server_method)
|
||||
RESOLVEFUNC(DTLSv1_2_client_method)
|
||||
|
||||
RESOLVEFUNC(CONF_get1_default_config_file)
|
||||
RESOLVEFUNC(OPENSSL_add_all_algorithms_noconf)
|
||||
RESOLVEFUNC(OPENSSL_add_all_algorithms_conf)
|
||||
|
|
@ -1081,7 +1092,6 @@ bool q_resolveOpenSslSymbols()
|
|||
RESOLVEFUNC(BIO_free)
|
||||
RESOLVEFUNC(BIO_new)
|
||||
RESOLVEFUNC(BIO_new_mem_buf)
|
||||
RESOLVEFUNC(BIO_new_dgram)
|
||||
RESOLVEFUNC(BIO_read)
|
||||
RESOLVEFUNC(BIO_s_mem)
|
||||
RESOLVEFUNC(BIO_write)
|
||||
|
|
|
|||
|
|
@ -541,7 +541,6 @@ typedef int (*CookieGenerateCallback)(SSL *, unsigned char *, unsigned *);
|
|||
|
||||
void q_SSL_CTX_set_cookie_generate_cb(SSL_CTX *ctx, CookieGenerateCallback cb);
|
||||
void q_SSL_CTX_set_cookie_verify_cb(SSL_CTX *ctx, CookieVerifyCallback cb);
|
||||
BIO *q_BIO_new_dgram(int fd, int close_flag);
|
||||
const SSL_METHOD *q_DTLS_server_method();
|
||||
const SSL_METHOD *q_DTLS_client_method();
|
||||
|
||||
|
|
|
|||
|
|
@ -234,4 +234,9 @@ typedef int (*CookieVerifyCallback)(SSL *, unsigned char *, unsigned);
|
|||
}
|
||||
#define q_DTLSv1_listen(ssl, peer) q_SSL_ctrl(ssl, DTLS_CTRL_LISTEN, 0, (void *)peer)
|
||||
|
||||
const SSL_METHOD *q_DTLSv1_server_method();
|
||||
const SSL_METHOD *q_DTLSv1_client_method();
|
||||
const SSL_METHOD *q_DTLSv1_2_server_method();
|
||||
const SSL_METHOD *q_DTLSv1_2_client_method();
|
||||
|
||||
#endif // QSSLSOCKET_OPENSSL_PRE11_SYMBOLS_P_H
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ qtConfig(ssl) {
|
|||
ssl/qsslcertificate.h \
|
||||
ssl/qsslcertificate_p.h \
|
||||
ssl/qsslconfiguration.h \
|
||||
ssl/qsslconfiguration_p.h \
|
||||
ssl/qsslconfiguration_p.h \
|
||||
ssl/qsslcipher.h \
|
||||
ssl/qsslcipher_p.h \
|
||||
ssl/qssldiffiehellmanparameters.h \
|
||||
|
|
@ -59,14 +59,19 @@ qtConfig(ssl) {
|
|||
qtConfig(openssl) {
|
||||
HEADERS += ssl/qsslcontext_openssl_p.h \
|
||||
ssl/qsslsocket_openssl_p.h \
|
||||
ssl/qsslsocket_openssl_symbols_p.h
|
||||
ssl/qsslsocket_openssl_symbols_p.h \
|
||||
ssl/qdtls.h \
|
||||
ssl/qdtls_p.h \
|
||||
ssl/qdtls_openssl_p.h
|
||||
SOURCES += ssl/qsslsocket_openssl_symbols.cpp \
|
||||
ssl/qssldiffiehellmanparameters_openssl.cpp \
|
||||
ssl/qsslcertificate_openssl.cpp \
|
||||
ssl/qsslellipticcurve_openssl.cpp \
|
||||
ssl/qsslkey_openssl.cpp \
|
||||
ssl/qsslsocket_openssl.cpp \
|
||||
ssl/qsslcontext_openssl.cpp
|
||||
ssl/qsslcontext_openssl.cpp \
|
||||
ssl/qdtls.cpp \
|
||||
ssl/qdtls_openssl.cpp
|
||||
|
||||
qtConfig(opensslv11) {
|
||||
HEADERS += ssl/qsslsocket_openssl11_symbols_p.h
|
||||
|
|
|
|||
Loading…
Reference in New Issue