summaryrefslogtreecommitdiffstats
path: root/src/network/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/ssl')
-rw-r--r--src/network/ssl/qasn1element_p.h54
-rw-r--r--src/network/ssl/qdtls.cpp1163
-rw-r--r--src/network/ssl/qdtls.h186
-rw-r--r--src/network/ssl/qdtls_openssl.cpp1462
-rw-r--r--src/network/ssl/qdtls_openssl_p.h213
-rw-r--r--src/network/ssl/qdtls_p.h155
-rw-r--r--src/network/ssl/qpassworddigestor.cpp187
-rw-r--r--src/network/ssl/qpassworddigestor.h60
-rw-r--r--src/network/ssl/qssl.cpp4
-rw-r--r--src/network/ssl/qssl.h7
-rw-r--r--src/network/ssl/qsslcertificate.cpp70
-rw-r--r--src/network/ssl/qsslcertificate.h11
-rw-r--r--src/network/ssl/qsslcertificate_openssl.cpp34
-rw-r--r--src/network/ssl/qsslcertificate_p.h4
-rw-r--r--src/network/ssl/qsslcertificate_qt.cpp6
-rw-r--r--src/network/ssl/qsslcertificate_winrt.cpp5
-rw-r--r--src/network/ssl/qsslcertificateextension.h5
-rw-r--r--src/network/ssl/qsslconfiguration.cpp62
-rw-r--r--src/network/ssl/qsslconfiguration.h15
-rw-r--r--src/network/ssl/qsslconfiguration_p.h9
-rw-r--r--src/network/ssl/qsslcontext_openssl.cpp15
-rw-r--r--src/network/ssl/qsslcontext_openssl11.cpp86
-rw-r--r--src/network/ssl/qsslcontext_opensslpre11.cpp56
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters_openssl.cpp4
-rw-r--r--src/network/ssl/qsslkey_openssl.cpp46
-rw-r--r--src/network/ssl/qsslkey_p.cpp97
-rw-r--r--src/network/ssl/qsslkey_p.h11
-rw-r--r--src/network/ssl/qsslkey_qt.cpp410
-rw-r--r--src/network/ssl/qsslpresharedkeyauthenticator.cpp19
-rw-r--r--src/network/ssl/qsslpresharedkeyauthenticator.h1
-rw-r--r--src/network/ssl/qsslsocket.cpp95
-rw-r--r--src/network/ssl/qsslsocket_mac.cpp193
-rw-r--r--src/network/ssl/qsslsocket_mac_p.h7
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp272
-rw-r--r--src/network/ssl/qsslsocket_openssl11.cpp6
-rw-r--r--src/network/ssl/qsslsocket_openssl11_symbols_p.h39
-rw-r--r--src/network/ssl/qsslsocket_openssl_p.h19
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp328
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h38
-rw-r--r--src/network/ssl/qsslsocket_opensslpre11.cpp4
-rw-r--r--src/network/ssl/qsslsocket_opensslpre11_symbols_p.h15
-rw-r--r--src/network/ssl/qwindowscarootfetcher.cpp168
-rw-r--r--src/network/ssl/qwindowscarootfetcher_p.h79
-rw-r--r--src/network/ssl/ssl.pri66
44 files changed, 5278 insertions, 508 deletions
diff --git a/src/network/ssl/qasn1element_p.h b/src/network/ssl/qasn1element_p.h
index 2c5019b4f7..2068254a95 100644
--- a/src/network/ssl/qasn1element_p.h
+++ b/src/network/ssl/qasn1element_p.h
@@ -58,10 +58,62 @@
QT_BEGIN_NAMESPACE
-#define RSA_ENCRYPTION_OID QByteArrayLiteral("1.2.840.113549.1.1.1")
+// General
+#define RSADSI_OID "1.2.840.113549."
+
+#define RSA_ENCRYPTION_OID QByteArrayLiteral(RSADSI_OID "1.1.1")
#define DSA_ENCRYPTION_OID QByteArrayLiteral("1.2.840.10040.4.1")
#define EC_ENCRYPTION_OID QByteArrayLiteral("1.2.840.10045.2.1")
+// These are mostly from the RFC for PKCS#5
+// PKCS#5: https://tools.ietf.org/html/rfc8018#appendix-B
+#define PKCS5_OID RSADSI_OID "1.5."
+// PKCS#12: https://tools.ietf.org/html/rfc7292#appendix-D)
+#define PKCS12_OID RSADSI_OID "1.12."
+
+// -PBES1
+#define PKCS5_MD2_DES_CBC_OID QByteArrayLiteral(PKCS5_OID "1") // Not (yet) implemented
+#define PKCS5_MD2_RC2_CBC_OID QByteArrayLiteral(PKCS5_OID "4") // Not (yet) implemented
+#define PKCS5_MD5_DES_CBC_OID QByteArrayLiteral(PKCS5_OID "3")
+#define PKCS5_MD5_RC2_CBC_OID QByteArrayLiteral(PKCS5_OID "6")
+#define PKCS5_SHA1_DES_CBC_OID QByteArrayLiteral(PKCS5_OID "10")
+#define PKCS5_SHA1_RC2_CBC_OID QByteArrayLiteral(PKCS5_OID "11")
+#define PKCS12_SHA1_RC4_128_OID QByteArrayLiteral(PKCS12_OID "1.1") // Not (yet) implemented
+#define PKCS12_SHA1_RC4_40_OID QByteArrayLiteral(PKCS12_OID "1.2") // Not (yet) implemented
+#define PKCS12_SHA1_3KEY_3DES_CBC_OID QByteArrayLiteral(PKCS12_OID "1.3")
+#define PKCS12_SHA1_2KEY_3DES_CBC_OID QByteArrayLiteral(PKCS12_OID "1.4")
+#define PKCS12_SHA1_RC2_128_CBC_OID QByteArrayLiteral(PKCS12_OID "1.5")
+#define PKCS12_SHA1_RC2_40_CBC_OID QByteArrayLiteral(PKCS12_OID "1.6")
+
+// -PBKDF2
+#define PKCS5_PBKDF2_ENCRYPTION_OID QByteArrayLiteral(PKCS5_OID "12")
+
+// -PBES2
+#define PKCS5_PBES2_ENCRYPTION_OID QByteArrayLiteral(PKCS5_OID "13")
+
+// Digest
+#define DIGEST_ALGORITHM_OID RSADSI_OID "2."
+// -HMAC-SHA-1
+#define HMAC_WITH_SHA1 QByteArrayLiteral(DIGEST_ALGORITHM_OID "7")
+// -HMAC-SHA-2
+#define HMAC_WITH_SHA224 QByteArrayLiteral(DIGEST_ALGORITHM_OID "8")
+#define HMAC_WITH_SHA256 QByteArrayLiteral(DIGEST_ALGORITHM_OID "9")
+#define HMAC_WITH_SHA384 QByteArrayLiteral(DIGEST_ALGORITHM_OID "10")
+#define HMAC_WITH_SHA512 QByteArrayLiteral(DIGEST_ALGORITHM_OID "11")
+#define HMAC_WITH_SHA512_224 QByteArrayLiteral(DIGEST_ALGORITHM_OID "12")
+#define HMAC_WITH_SHA512_256 QByteArrayLiteral(DIGEST_ALGORITHM_OID "13")
+
+// Encryption algorithms
+#define ENCRYPTION_ALGORITHM_OID RSADSI_OID "3."
+#define DES_CBC_ENCRYPTION_OID QByteArrayLiteral("1.3.14.3.2.7")
+#define DES_EDE3_CBC_ENCRYPTION_OID QByteArrayLiteral(ENCRYPTION_ALGORITHM_OID "7")
+#define RC2_CBC_ENCRYPTION_OID QByteArrayLiteral(ENCRYPTION_ALGORITHM_OID "2")
+#define RC5_CBC_ENCRYPTION_OID QByteArrayLiteral(ENCRYPTION_ALGORITHM_OID "9") // Not (yet) implemented
+#define AES_OID "2.16.840.1.101.3.4.1."
+#define AES128_CBC_ENCRYPTION_OID QByteArrayLiteral(AES_OID "2")
+#define AES192_CBC_ENCRYPTION_OID QByteArrayLiteral(AES_OID "22") // Not (yet) implemented
+#define AES256_CBC_ENCRYPTION_OID QByteArrayLiteral(AES_OID "42") // Not (yet) implemented
+
class Q_AUTOTEST_EXPORT QAsn1Element
{
public:
diff --git a/src/network/ssl/qdtls.cpp b/src/network/ssl/qdtls.cpp
new file mode 100644
index 0000000000..bbb22aa527
--- /dev/null
+++ b/src/network/ssl/qdtls.cpp
@@ -0,0 +1,1163 @@
+/****************************************************************************
+**
+** 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 "qdtls_openssl_p.h"
+#include "qudpsocket.h"
+#include "qdtls_p.h"
+#include "qssl_p.h"
+#include "qdtls.h"
+
+#include "qglobal.h"
+
+/*!
+ \class QDtlsClientVerifier
+ \brief This class implements server-side DTLS cookie generation and verification.
+ \since 5.12
+
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+ The QDtlsClientVerifier class implements server-side DTLS cookie generation
+ and verification. Datagram security protocols are highly susceptible to a
+ variety of Denial-of-Service attacks. According to \l {https://tools.ietf.org/html/rfc6347#section-4.2.1}{RFC 6347, section 4.2.1},
+ these are two of the more common types of attack:
+
+ \list
+ \li An attacker transmits a series of handshake initiation requests, causing
+ a server to allocate excessive resources and potentially perform expensive
+ cryptographic operations.
+ \li An attacker transmits a series of handshake initiation requests with
+ a forged source of the victim, making the server act as an amplifier.
+ Normally, the server would reply to the victim machine with a Certificate message,
+ which can be quite large, thus flooding the victim machine with datagrams.
+ \endlist
+
+ As a countermeasure to these attacks, \l {https://tools.ietf.org/html/rfc6347#section-4.2.1}{RFC 6347, section 4.2.1}
+ proposes a stateless cookie technique that a server may deploy:
+
+ \list
+ \li In response to the initial ClientHello message, the server sends a HelloVerifyRequest,
+ which contains a cookie. This cookie is a cryptographic hash and is generated using the
+ client's address, port number, and the server's secret (which is a cryptographically strong
+ pseudo-random sequence of bytes).
+ \li A reachable DTLS client is expected to reply with a new ClientHello message
+ containing this cookie.
+ \li When the server receives the ClientHello message with a cookie, it
+ generates a new cookie as described above. This new cookie is compared to the
+ one found in the ClientHello message.
+ \li In the cookies are equal, the client is considered to be real, and the
+ server can continue with a TLS handshake procedure.
+ \endlist
+
+ \note A DTLS server is not required to use DTLS cookies.
+
+ QDtlsClientVerifier is designed to work in pair with QUdpSocket, as shown in
+ the following code-excerpt:
+
+ \snippet code/src_network_ssl_qdtlscookie.cpp 0
+
+ QDtlsClientVerifier does not impose any restrictions on how the application uses
+ QUdpSocket. For example, it is possible to have a server with a single QUdpSocket
+ in state QAbstractSocket::BoundState, handling multiple DTLS clients
+ simultaneously:
+
+ \list
+ \li Testing if new clients are real DTLS-capable clients.
+ \li Completing TLS handshakes with the verified clients (see QDtls).
+ \li Decrypting datagrams coming from the connected clients (see QDtls).
+ \li Sending encrypted datagrams to the connected clients (see QDtls).
+ \endlist
+
+ This implies that QDtlsClientVerifier does not read directly from a socket,
+ instead it expects the application to read an incoming datagram, extract the
+ sender's address, and port, and then pass this data to verifyClient().
+ To send a HelloVerifyRequest message, verifyClient() can write to the QUdpSocket.
+
+ \note QDtlsClientVerifier does not take ownership of the QUdpSocket object.
+
+ By default QDtlsClientVerifier obtains its secret from a cryptographically
+ strong pseudorandom number generator.
+
+ \note The default secret is shared by all objects of the classes QDtlsClientVerifier
+ and QDtls. Since this can impose security risks, RFC 6347 recommends to change
+ the server's secret frequently. Please see \l {https://tools.ietf.org/html/rfc6347}{RFC 6347, section 4.2.1}
+ for hints about possible server implementations. Cookie generator parameters
+ can be set using the class QDtlsClientVerifier::GeneratorParameters and
+ setCookieGeneratorParameters():
+
+ \snippet code/src_network_ssl_qdtlscookie.cpp 1
+
+ The \l{secureudpserver}{DTLS server} example illustrates how to use
+ QDtlsClientVerifier in a server application.
+
+ \sa QUdpSocket, QAbstractSocket::BoundState, QDtls, verifyClient(),
+ GeneratorParameters, setCookieGeneratorParameters(), cookieGeneratorParameters(),
+ QDtls::setCookieGeneratorParameters(),
+ QDtls::cookieGeneratorParameters(),
+ QCryptographicHash::Algorithm,
+ QDtlsError, dtlsError(), dtlsErrorString()
+*/
+
+/*!
+ \class QDtlsClientVerifier::GeneratorParameters
+ \brief This class defines parameters for DTLS cookie generator.
+ \since 5.12
+
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+ An object of this class provides the parameters that QDtlsClientVerifier
+ will use to generate DTLS cookies. They include a cryptographic hash
+ algorithm and a secret.
+
+ \note An empty secret is considered to be invalid by
+ QDtlsClientVerifier::setCookieGeneratorParameters().
+
+ \sa QDtlsClientVerifier::setCookieGeneratorParameters(),
+ QDtlsClientVerifier::cookieGeneratorParameters(),
+ QDtls::setCookieGeneratorParameters(),
+ QDtls::cookieGeneratorParameters(),
+ QCryptographicHash::Algorithm
+*/
+
+/*!
+ \enum QDtlsError
+ \brief Describes errors that can be found by QDtls and QDtlsClientVerifier.
+ \relates QDtls
+ \since 5.12
+
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+ This enum describes general and TLS-specific errors that can be encountered
+ by objects of the classes QDtlsClientVerifier and QDtls.
+
+ \value NoError No error occurred, the last operation was successful.
+ \value InvalidInputParameters Input parameters provided by a caller were
+ invalid.
+ \value InvalidOperation An operation was attempted in a state that did not
+ permit it.
+ \value UnderlyingSocketError QUdpSocket::writeDatagram() failed, QUdpSocket::error()
+ and QUdpSocket::errorString() can provide more specific information.
+ \value RemoteClosedConnectionError TLS shutdown alert message was received.
+ \value PeerVerificationError Peer's identity could not be verified during the
+ TLS handshake.
+ \value TlsInitializationError An error occurred while initializing an underlying
+ TLS backend.
+ \value TlsFatalError A fatal error occurred during TLS handshake, other
+ than peer verification error or TLS initialization error.
+ \value TlsNonFatalError A failure to encrypt or decrypt a datagram, non-fatal,
+ meaning QDtls can continue working after this error.
+*/
+
+/*!
+ \class QDtls
+ \brief This class provides encryption for UDP sockets.
+ \since 5.12
+
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+ The QDtls class can be used to establish a secure connection with a network
+ peer using User Datagram Protocol (UDP). DTLS connection over essentially
+ connectionless UDP means that two peers first have to successfully complete
+ a TLS handshake by calling doHandshake(). After the handshake has completed,
+ encrypted datagrams can be sent to the peer using writeDatagramEncrypted().
+ Encrypted datagrams coming from the peer can be decrypted by decryptDatagram().
+
+ QDtls is designed to work with QUdpSocket. Since QUdpSocket can receive
+ datagrams coming from different peers, an application must implement
+ demultiplexing, forwarding datagrams coming from different peers to their
+ corresponding instances of QDtls. An association between a network peer
+ and its QDtls object can be established using the peer's address and port
+ number. Before starting a handshake, the application must set the peer's
+ address and port number using setPeer().
+
+ QDtls does not read datagrams from QUdpSocket, this is expected to be done by
+ the application, for example, in a slot attached to the QUdpSocket::readyRead()
+ signal. Then, these datagrams must be processed by QDtls.
+
+ \note QDtls does \e not take ownership of the QUdpSocket object.
+
+ Normally, several datagrams are to be received and sent by both peers during
+ the handshake phase. Upon reading datagrams, server and client must pass these
+ datagrams to doHandshake() until some error is found or handshakeState()
+ returns HandshakeComplete:
+
+ \snippet code/src_network_ssl_qdtls.cpp 0
+
+ For a server, the first call to doHandshake() requires a non-empty datagram
+ containing a ClientHello message. If the server also deploys QDtlsClientVerifier,
+ the first ClientHello message is expected to be the one verified by QDtlsClientVerifier.
+
+ In case the peer's identity cannot be validated during the handshake, the application
+ must inspect errors returned by peerVerificationErrors() and then either
+ ignore errors by calling ignoreVerificationErrors() or abort the handshake
+ by calling abortHandshake(). If errors were ignored, the handshake can be
+ resumed by calling resumeHandshake().
+
+ After the handshake has been completed, datagrams can be sent to and received
+ from the network peer securely:
+
+ \snippet code/src_network_ssl_qdtls.cpp 2
+
+ A DTLS connection may be closed using shutdown().
+
+ \snippet code/src_network_ssl_qdtls.cpp 3
+
+ \warning It's recommended to call shutdown() before destroying the client's QDtls
+ object if you are planning to re-use the same port number to connect to the
+ server later. Otherwise, the server may drop incoming ClientHello messages,
+ see \l{https://tools.ietf.org/html/rfc6347#page-25}{RFC 6347, section 4.2.8}
+ for more details and implementation hints.
+
+ If the server does not use QDtlsClientVerifier, it \e must configure its
+ QDtls objects to disable the cookie verification procedure:
+
+ \snippet code/src_network_ssl_qdtls.cpp 4
+
+ A server that uses cookie verification with non-default generator parameters
+ \e must set the same parameters for its QDtls object before starting the handshake.
+
+ \note The DTLS protocol leaves Path Maximum Transmission Unit (PMTU) discovery
+ to the application. The application may provide QDtls with the MTU using
+ setMtuHint(). This hint affects only the handshake phase, since only handshake
+ messages can be fragmented and reassembled by the DTLS. All other messages sent
+ by the application must fit into a single datagram.
+ \note DTLS-specific headers add some overhead to application data further
+ reducing the possible message size.
+ \warning A server configured to reply with HelloVerifyRequest will drop
+ all fragmented ClientHello messages, never starting a handshake.
+
+ The \l{secureudpserver}{DTLS server} and \l{secureudpclient}{DTLS client}
+ examples illustrate how to use QDtls in applications.
+
+ \sa QUdpSocket, QDtlsClientVerifier, HandshakeState, QDtlsError, QSslConfiguration
+*/
+
+/*!
+ \typedef QDtls::GeneratorParameters
+
+ This is a synonym for QDtlsClientVerifier::GeneratorParameters.
+*/
+
+/*!
+ \fn void QDtls::handshakeTimeout()
+
+ Packet loss can result in timeouts during the handshake phase. In this case
+ QDtls emits a handshakeTimeout() signal. Call handleTimeout() to retransmit
+ the handshake messages:
+
+ \snippet code/src_network_ssl_qdtls.cpp 1
+
+ \sa handleTimeout()
+*/
+
+/*!
+ \fn void QDtls::pskRequired(QSslPreSharedKeyAuthenticator *authenticator)
+
+ QDtls emits this signal when it negotiates a PSK ciphersuite, and therefore
+ a PSK authentication is then required.
+
+ When using PSK, the client must send to the server a valid identity and a
+ valid pre shared key, in order for the TLS handshake to continue.
+ Applications can provide this information in a slot connected to this
+ signal, by filling in the passed \a authenticator object according to their
+ needs.
+
+ \note Ignoring this signal, or failing to provide the required credentials,
+ will cause the handshake to fail, and therefore the connection to be aborted.
+
+ \note The \a authenticator object is owned by QDtls and must not be deleted
+ by the application.
+
+ \sa QSslPreSharedKeyAuthenticator
+*/
+
+/*!
+ \enum QDtls::HandshakeState
+ \brief Describes the current state of DTLS handshake.
+ \since 5.12
+
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+ This enum describes the current state of DTLS handshake for a QDtls
+ connection.
+
+ \value HandshakeNotStarted Nothing done yet.
+ \value HandshakeInProgress Handshake was initiated and no errors were found so far.
+ \value PeerVerificationFailed The identity of the peer can't be established.
+ \value HandshakeComplete Handshake completed successfully and encrypted connection
+ was established.
+
+ \sa QDtls::doHandshake(), QDtls::handshakeState()
+*/
+
+
+QT_BEGIN_NAMESPACE
+
+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();
+ 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();
+ dtlsConfiguration.allowRootCertOnDemandLoading = configuration.d->allowRootCertOnDemandLoading;
+
+ 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;
+}
+
+bool QDtlsBasePrivate::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;
+ }
+}
+
+static QString msgUnsupportedMulticastAddress()
+{
+ return QDtls::tr("Multicast and broadcast addresses are not supported");
+}
+
+/*!
+ Default constructs GeneratorParameters object with QCryptographicHash::Sha1
+ as its algorithm and an empty secret.
+
+ \sa QDtlsClientVerifier::setCookieGeneratorParameters(),
+ QDtlsClientVerifier::cookieGeneratorParameters(),
+ QDtls::setCookieGeneratorParameters(),
+ QDtls::cookieGeneratorParameters()
+ */
+QDtlsClientVerifier::GeneratorParameters::GeneratorParameters()
+{
+}
+
+/*!
+ Constructs GeneratorParameters object from \a algorithm and \a secret.
+
+ \sa QDtlsClientVerifier::setCookieGeneratorParameters(),
+ QDtlsClientVerifier::cookieGeneratorParameters(),
+ QDtls::setCookieGeneratorParameters(),
+ QDtls::cookieGeneratorParameters()
+ */
+QDtlsClientVerifier::GeneratorParameters::GeneratorParameters(QCryptographicHash::Algorithm algorithm, const QByteArray &secret)
+ : hash(algorithm), secret(secret)
+{
+}
+
+/*!
+ Constructs a QDtlsClientVerifier object, \a parent is passed to QObject's
+ constructor.
+*/
+QDtlsClientVerifier::QDtlsClientVerifier(QObject *parent)
+ : QObject(*new QDtlsClientVerifierOpenSSL, parent)
+{
+ 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);
+}
+
+/*!
+ Destroys the QDtlsClientVerifier object.
+*/
+QDtlsClientVerifier::~QDtlsClientVerifier()
+{
+}
+
+/*!
+ Sets the secret and the cryptographic hash algorithm from \a params. This
+ QDtlsClientVerifier will use these to generate cookies. If the new secret
+ has size zero, this function returns \c false and does not change the
+ cookie generator parameters.
+
+ \note The secret is supposed to be a cryptographically secure sequence of bytes.
+
+ \sa QDtlsClientVerifier::GeneratorParameters, cookieGeneratorParameters(),
+ QCryptographicHash::Algorithm
+*/
+bool QDtlsClientVerifier::setCookieGeneratorParameters(const GeneratorParameters &params)
+{
+ Q_D(QDtlsClientVerifier);
+
+ return d->setCookieGeneratorParameters(params.hash, params.secret);
+}
+
+/*!
+ Returns the current secret and hash algorithm used to generate cookies.
+ The default hash algorithm is QCryptographicHash::Sha256 if Qt was configured
+ to support it, QCryptographicHash::Sha1 otherwise. The default secret is
+ obtained from the backend-specific cryptographically strong pseudorandom
+ number generator.
+
+ \sa QCryptographicHash::Algorithm, QDtlsClientVerifier::GeneratorParameters,
+ setCookieGeneratorParameters()
+*/
+QDtlsClientVerifier::GeneratorParameters QDtlsClientVerifier::cookieGeneratorParameters() const
+{
+ Q_D(const QDtlsClientVerifier);
+
+ return {d->hashAlgorithm, d->secret};
+}
+
+/*!
+ \a socket must be a valid pointer, \a dgram must be a non-empty
+ datagram, \a address cannot be null, broadcast, or multicast.
+ \a port is the remote peer's port. This function returns \c true
+ if \a dgram contains a ClientHello message with a valid cookie.
+ If no matching cookie is found, verifyClient() will send a
+ HelloVerifyRequest message using \a socket and return \c false.
+
+ The following snippet shows how a server application may check for errors:
+
+ \snippet code/src_network_ssl_qdtlscookie.cpp 2
+
+ \sa QHostAddress::isNull(), QHostAddress::isBroadcast(), QHostAddress::isMulticast(),
+ setCookieGeneratorParameters(), cookieGeneratorParameters()
+*/
+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,
+ msgUnsupportedMulticastAddress());
+ return false;
+ }
+
+ return d->verifyClient(socket, dgram, address, port);
+}
+
+/*!
+ Convenience function. Returns the last ClientHello message that was successfully
+ verified, or an empty QByteArray if no verification has completed.
+
+ \sa verifyClient()
+*/
+QByteArray QDtlsClientVerifier::verifiedHello() const
+{
+ Q_D(const QDtlsClientVerifier);
+
+ return d->verifiedClientHello;
+}
+
+/*!
+ Returns the last error that occurred or QDtlsError::NoError.
+
+ \sa QDtlsError, dtlsErrorString()
+*/
+QDtlsError QDtlsClientVerifier::dtlsError() const
+{
+ Q_D(const QDtlsClientVerifier);
+
+ return d->errorCode;
+}
+
+/*!
+ Returns a textual description of the last error, or an empty string.
+
+ \sa dtlsError()
+ */
+QString QDtlsClientVerifier::dtlsErrorString() const
+{
+ Q_D(const QDtlsBase);
+
+ return d->errorDescription;
+}
+
+/*!
+ Creates a QDtls object, \a parent is passed to the QObject constructor.
+ \a mode is QSslSocket::SslServerMode for a server-side DTLS connection or
+ QSslSocket::SslClientMode for a client.
+
+ \sa sslMode(), QSslSocket::SslMode
+*/
+QDtls::QDtls(QSslSocket::SslMode mode, QObject *parent)
+ : QObject(*new QDtlsPrivateOpenSSL, parent)
+{
+ Q_D(QDtls);
+
+ d->mode = mode;
+ setDtlsConfiguration(QSslConfiguration::defaultDtlsConfiguration());
+}
+
+/*!
+ Destroys the QDtls object.
+*/
+QDtls::~QDtls()
+{
+}
+
+/*!
+ Sets the peer's address, \a port, and host name and returns \c true
+ if successful. \a address must not be null, multicast, or broadcast.
+ \a verificationName is the host name used for the certificate validation.
+
+ \sa peerAddress(), peerPort(), peerVerificationName()
+ */
+bool QDtls::setPeer(const QHostAddress &address, quint16 port,
+ const QString &verificationName)
+{
+ Q_D(QDtls);
+
+ if (d->handshakeState != HandshakeNotStarted) {
+ d->setDtlsError(QDtlsError::InvalidOperation,
+ tr("Cannot set peer 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,
+ msgUnsupportedMulticastAddress());
+ return false;
+ }
+
+ d->clearDtlsError();
+
+ d->remoteAddress = address;
+ d->remotePort = port;
+ d->peerVerificationName = verificationName;
+
+ return true;
+}
+
+/*!
+ Sets the host \a name that will be used for the certificate validation
+ and returns \c true if successful.
+
+ \note This function must be called before the handshake starts.
+
+ \sa peerVerificationName(), setPeer()
+*/
+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;
+}
+
+/*!
+ Returns the peer's address, set by setPeer(), or QHostAddress::Null.
+
+ \sa setPeer()
+*/
+QHostAddress QDtls::peerAddress() const
+{
+ Q_D(const QDtls);
+
+ return d->remoteAddress;
+}
+
+/*!
+ Returns the peer's port number, set by setPeer(), or 0.
+
+ \sa setPeer()
+*/
+quint16 QDtls::peerPort() const
+{
+ Q_D(const QDtlsBase);
+
+ return d->remotePort;
+}
+
+/*!
+ Returns the host name set by setPeer() or setPeerVerificationName().
+ The default value is an empty string.
+
+ \sa setPeerVerificationName(), setPeer()
+*/
+QString QDtls::peerVerificationName() const
+{
+ Q_D(const QDtls);
+
+ return d->peerVerificationName;
+}
+
+/*!
+ Returns QSslSocket::SslServerMode for a server-side connection and
+ QSslSocket::SslClientMode for a client.
+
+ \sa QDtls(), QSslSocket::SslMode
+*/
+QSslSocket::SslMode QDtls::sslMode() const
+{
+ Q_D(const QDtls);
+
+ return d->mode;
+}
+
+/*!
+ \a mtuHint is the maximum transmission unit (MTU), either discovered or guessed
+ by the application. The application is not required to set this value.
+
+ \sa mtuHint(), QAbstractSocket::PathMtuSocketOption
+ */
+void QDtls::setMtuHint(quint16 mtuHint)
+{
+ Q_D(QDtls);
+
+ d->mtuHint = mtuHint;
+}
+
+/*!
+ Returns the value previously set by setMtuHint(). The default value is 0.
+
+ \sa setMtuHint()
+ */
+quint16 QDtls::mtuHint() const
+{
+ Q_D(const QDtls);
+
+ return d->mtuHint;
+}
+
+/*!
+ Sets the cryptographic hash algorithm and the secret from \a params.
+ This function is only needed for a server-side QDtls connection.
+ Returns \c true if successful.
+
+ \note This function must be called before the handshake starts.
+
+ \sa cookieGeneratorParameters(), doHandshake(), QDtlsClientVerifier,
+ QDtlsClientVerifier::cookieGeneratorParameters()
+*/
+bool QDtls::setCookieGeneratorParameters(const GeneratorParameters &params)
+{
+ Q_D(QDtls);
+
+ return d->setCookieGeneratorParameters(params.hash, params.secret);
+}
+
+/*!
+ Returns the current hash algorithm and secret, either default ones or previously
+ set by a call to setCookieGeneratorParameters().
+
+ The default hash algorithm is QCryptographicHash::Sha256 if Qt was
+ configured to support it, QCryptographicHash::Sha1 otherwise. The default
+ secret is obtained from the backend-specific cryptographically strong
+ pseudorandom number generator.
+
+ \sa QDtlsClientVerifier, cookieGeneratorParameters()
+*/
+QDtls::GeneratorParameters QDtls::cookieGeneratorParameters() const
+{
+ Q_D(const QDtls);
+
+ return {d->hashAlgorithm, d->secret};
+}
+
+/*!
+ Sets the connection's TLS configuration from \a configuration
+ and returns \c true if successful.
+
+ \note This function must be called before the handshake starts.
+
+ \sa dtlsConfiguration(), doHandshake()
+*/
+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;
+ }
+
+ d->setConfiguration(configuration);
+ return true;
+}
+
+/*!
+ Returns either the default DTLS configuration or the configuration set by an
+ earlier call to setDtlsConfiguration().
+
+ \sa setDtlsConfiguration(), QSslConfiguration::defaultDtlsConfiguration()
+*/
+QSslConfiguration QDtls::dtlsConfiguration() const
+{
+ Q_D(const QDtls);
+
+ return d->configuration();
+}
+
+/*!
+ Returns the current handshake state for this QDtls.
+
+ \sa doHandshake(), QDtls::HandshakeState
+ */
+QDtls::HandshakeState QDtls::handshakeState()const
+{
+ Q_D(const QDtls);
+
+ return d->handshakeState;
+}
+
+/*!
+ Starts or continues a DTLS handshake. \a socket must be a valid pointer.
+ When starting a server-side DTLS handshake, \a dgram must contain the initial
+ ClientHello message read from QUdpSocket. This function returns \c true if
+ no error was found. Handshake state can be tested using handshakeState().
+ \c false return means some error occurred, use dtlsError() for more
+ detailed information.
+
+ \note If the identity of the peer can't be established, the error is set to
+ QDtlsError::PeerVerificationError. If you want to ignore verification errors
+ and continue connecting, you must call ignoreVerificationErrors() and then
+ resumeHandshake(). If the errors cannot be ignored, you must call
+ abortHandshake().
+
+ \snippet code/src_network_ssl_qdtls.cpp 5
+
+ \sa handshakeState(), dtlsError(), ignoreVerificationErrors(), resumeHandshake(),
+ abortHandshake()
+*/
+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;
+}
+
+/*!
+ \internal
+*/
+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 peer's 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);
+}
+
+/*!
+ If a timeout occures during the handshake, the handshakeTimeout() signal
+ is emitted. The application must call handleTimeout() to retransmit handshake
+ messages; handleTimeout() returns \c true if a timeout has occurred, false
+ otherwise. \a socket must be a valid pointer.
+
+ \sa handshakeTimeout()
+*/
+bool QDtls::handleTimeout(QUdpSocket *socket)
+{
+ Q_D(QDtls);
+
+ if (!socket) {
+ d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
+ return false;
+ }
+
+ return d->handleTimeout(socket);
+}
+
+/*!
+ \internal
+*/
+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);
+}
+
+/*!
+ If peer verification errors were ignored during the handshake,
+ resumeHandshake() resumes and completes the handshake and returns
+ \c true. \a socket must be a valid pointer. Returns \c false if
+ the handshake could not be resumed.
+
+ \sa doHandshake(), abortHandshake() peerVerificationErrors(), ignoreVerificationErrors()
+*/
+bool QDtls::resumeHandshake(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);
+}
+
+/*!
+ Aborts the ongoing handshake. Returns true if one was on-going on \a socket;
+ otherwise, sets a suitable error and returns false.
+
+ \sa doHandshake(), resumeHandshake()
+ */
+bool QDtls::abortHandshake(QUdpSocket *socket)
+{
+ Q_D(QDtls);
+
+ if (!socket) {
+ d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
+ return false;
+ }
+
+ if (d->handshakeState != PeerVerificationFailed && d->handshakeState != HandshakeInProgress) {
+ d->setDtlsError(QDtlsError::InvalidOperation,
+ tr("No handshake in progress, nothing to abort"));
+ return false;
+ }
+
+ d->abortHandshake(socket);
+ return true;
+}
+
+/*!
+ Sends an encrypted shutdown alert message and closes the DTLS connection.
+ Handshake state changes to QDtls::HandshakeNotStarted. \a socket must be a
+ valid pointer. This function returns \c true on success.
+
+ \sa doHandshake()
+ */
+bool QDtls::shutdown(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;
+}
+
+/*!
+ Returns \c true if DTLS handshake completed successfully.
+
+ \sa doHandshake(), handshakeState()
+ */
+bool QDtls::isConnectionEncrypted() const
+{
+ Q_D(const QDtls);
+
+ return d->connectionEncrypted;
+}
+
+/*!
+ Returns the cryptographic \l {QSslCipher} {cipher} used by this connection,
+ or a null cipher if the connection isn't encrypted. The cipher for the
+ session is selected during the handshake phase. The cipher is used to encrypt
+ and decrypt data.
+
+ QSslConfiguration provides functions for setting the ordered list of ciphers
+ from which the handshake phase will eventually select the session cipher.
+ This ordered list must be in place before the handshake phase begins.
+
+ \sa QSslConfiguration, setDtlsConfiguration(), dtlsConfiguration()
+*/
+QSslCipher QDtls::sessionCipher() const
+{
+ Q_D(const QDtls);
+
+ return d->sessionCipher;
+}
+
+/*!
+ Returns the DTLS protocol version used by this connection, or UnknownProtocol
+ if the connection isn't encrypted yet. The protocol for the connection is selected
+ during the handshake phase.
+
+ setDtlsConfiguration() can set the preferred version before the handshake starts.
+
+ \sa setDtlsConfiguration(), QSslConfiguration, QSslConfiguration::defaultDtlsConfiguration(),
+ QSslConfiguration::setProtocol()
+*/
+QSsl::SslProtocol QDtls::sessionProtocol() const
+{
+ Q_D(const QDtls);
+
+ return d->sessionProtocol;
+}
+
+/*!
+ Encrypts \a dgram and writes the encrypted data into \a socket. Returns the
+ number of bytes written, or -1 in case of error. The handshake must be completed
+ before writing encrypted data. \a socket must be a valid
+ pointer.
+
+ \sa doHandshake(), handshakeState(), isConnectionEncrypted(), dtlsError()
+*/
+qint64 QDtls::writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram)
+{
+ Q_D(QDtls);
+
+ if (!socket) {
+ d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
+ return -1;
+ }
+
+ if (!isConnectionEncrypted()) {
+ d->setDtlsError(QDtlsError::InvalidOperation,
+ tr("Cannot write a datagram, not in encrypted state"));
+ return -1;
+ }
+
+ return d->writeDatagramEncrypted(socket, dgram);
+}
+
+/*!
+ Decrypts \a dgram and returns its contents as plain text. The handshake must
+ be completed before datagrams can be decrypted. Depending on the type of the
+ TLS message the connection may write into \a socket, which must be a valid
+ pointer.
+*/
+QByteArray QDtls::decryptDatagram(QUdpSocket *socket, const QByteArray &dgram)
+{
+ Q_D(QDtls);
+
+ if (!socket) {
+ d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
+ return {};
+ }
+
+ if (!isConnectionEncrypted()) {
+ d->setDtlsError(QDtlsError::InvalidOperation,
+ tr("Cannot read a datagram, not in encrypted state"));
+ return {};
+ }
+
+ if (!dgram.size())
+ return {};
+
+ return d->decryptDatagram(socket, dgram);
+}
+
+/*!
+ Returns the last error encountered by the connection or QDtlsError::NoError.
+
+ \sa dtlsErrorString(), QDtlsError
+*/
+QDtlsError QDtls::dtlsError() const
+{
+ Q_D(const QDtls);
+
+ return d->errorCode;
+}
+
+/*!
+ Returns a textual description for the last error encountered by the connection
+ or empty string.
+
+ \sa dtlsError()
+*/
+QString QDtls::dtlsErrorString() const
+{
+ Q_D(const QDtls);
+
+ return d->errorDescription;
+}
+
+/*!
+ Returns errors found while establishing the identity of the peer.
+
+ If you want to continue connecting despite the errors that have occurred,
+ you must call ignoreVerificationErrors().
+*/
+QVector<QSslError> QDtls::peerVerificationErrors() const
+{
+ Q_D(const QDtls);
+
+ return d->tlsErrors;
+}
+
+/*!
+ This method tells QDtls to ignore only the errors given in \a errorsToIgnore.
+
+ If, for instance, you want to connect to a server that uses a self-signed
+ certificate, consider the following snippet:
+
+ \snippet code/src_network_ssl_qdtls.cpp 6
+
+ You can also call this function after doHandshake() encountered the
+ QDtlsError::PeerVerificationError error, and then resume the handshake by
+ calling resumeHandshake().
+
+ Later calls to this function will replace the list of errors that were
+ passed in previous calls. You can clear the list of errors you want to ignore
+ by calling this function with an empty list.
+
+ \sa doHandshake(), resumeHandshake(), QSslError
+*/
+void QDtls::ignoreVerificationErrors(const QVector<QSslError> &errorsToIgnore)
+{
+ Q_D(QDtls);
+
+ d->tlsErrorsToIgnore = errorsToIgnore;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qdtls.h b/src/network/ssl/qdtls.h
new file mode 100644
index 0000000000..8505b00d5e
--- /dev/null
+++ b/src/network/ssl/qdtls.h
@@ -0,0 +1,186 @@
+/****************************************************************************
+**
+** 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_REQUIRE_CONFIG(dtls);
+
+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);
+ ~QDtlsClientVerifier();
+
+ struct Q_NETWORK_EXPORT GeneratorParameters
+ {
+ GeneratorParameters();
+ GeneratorParameters(QCryptographicHash::Algorithm a, const QByteArray &s);
+ QCryptographicHash::Algorithm hash = QCryptographicHash::Sha1;
+ QByteArray secret;
+ };
+
+ bool setCookieGeneratorParameters(const GeneratorParameters &params);
+ 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);
+ ~QDtls();
+
+ bool setPeer(const QHostAddress &address, quint16 port,
+ const QString &verificationName = {});
+ bool setPeerVerificationName(const QString &name);
+ QHostAddress peerAddress() const;
+ quint16 peerPort() const;
+ QString peerVerificationName() const;
+ QSslSocket::SslMode sslMode() const;
+
+ void setMtuHint(quint16 mtuHint);
+ quint16 mtuHint() const;
+
+ using GeneratorParameters = QDtlsClientVerifier::GeneratorParameters;
+ bool setCookieGeneratorParameters(const GeneratorParameters &params);
+ 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 resumeHandshake(QUdpSocket *socket);
+ bool abortHandshake(QUdpSocket *socket);
+ bool shutdown(QUdpSocket *socket);
+
+ bool isConnectionEncrypted() 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
diff --git a/src/network/ssl/qdtls_openssl.cpp b/src/network/ssl/qdtls_openssl.cpp
new file mode 100644
index 0000000000..8be53df24f
--- /dev/null
+++ b/src/network/ssl/qdtls_openssl.cpp
@@ -0,0 +1,1462 @@
+/****************************************************************************
+**
+** 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 NOMINMAX
+#define NOMINMAX
+#endif // NOMINMAX
+#include "private/qnativesocketengine_p.h"
+
+#include "qsslpresharedkeyauthenticator_p.h"
+#include "qsslsocket_openssl_symbols_p.h"
+#include "qsslsocket_openssl_p.h"
+#include "qsslcertificate_p.h"
+#include "qdtls_openssl_p.h"
+#include "qudpsocket.h"
+#include "qssl_p.h"
+
+#include "qmessageauthenticationcode.h"
+#include "qcryptographichash.h"
+
+#include "qdebug.h"
+
+#include <cstring>
+#include <cstddef>
+
+QT_BEGIN_NAMESPACE
+
+#define QT_DTLS_VERBOSE 0
+
+#if QT_DTLS_VERBOSE
+
+#define qDtlsWarning(arg) qWarning(arg)
+#define qDtlsDebug(arg) qDebug(arg)
+
+#else
+
+#define qDtlsWarning(arg)
+#define qDtlsDebug(arg)
+
+#endif // QT_DTLS_VERBOSE
+
+namespace dtlsutil
+{
+
+QByteArray cookie_for_peer(SSL *ssl)
+{
+ Q_ASSERT(ssl);
+
+ // SSL_get_rbio does not increment the reference count
+ BIO *readBIO = q_SSL_get_rbio(ssl);
+ if (!readBIO) {
+ qCWarning(lcSsl, "No BIO (dgram) found in SSL object");
+ return {};
+ }
+
+ auto listener = static_cast<dtlsopenssl::DtlsState *>(q_BIO_get_app_data(readBIO));
+ if (!listener) {
+ qCWarning(lcSsl, "BIO_get_app_data returned invalid (nullptr) value");
+ return {};
+ }
+
+ const QHostAddress peerAddress(listener->remoteAddress);
+ const quint16 peerPort(listener->remotePort);
+ QByteArray peerData;
+ if (peerAddress.protocol() == QAbstractSocket::IPv6Protocol) {
+ const Q_IPV6ADDR sin6_addr(peerAddress.toIPv6Address());
+ peerData.resize(int(sizeof sin6_addr + sizeof peerPort));
+ char *dst = peerData.data();
+ std::memcpy(dst, &peerPort, sizeof peerPort);
+ dst += sizeof peerPort;
+ std::memcpy(dst, &sin6_addr, sizeof sin6_addr);
+ } else if (peerAddress.protocol() == QAbstractSocket::IPv4Protocol) {
+ const quint32 sin_addr(peerAddress.toIPv4Address());
+ peerData.resize(int(sizeof sin_addr + sizeof peerPort));
+ char *dst = peerData.data();
+ std::memcpy(dst, &peerPort, sizeof peerPort);
+ dst += sizeof peerPort;
+ std::memcpy(dst, &sin_addr, sizeof sin_addr);
+ } else {
+ Q_UNREACHABLE();
+ }
+
+ return peerData;
+}
+
+struct FallbackCookieSecret
+{
+ FallbackCookieSecret()
+ {
+ key.resize(32);
+ const int status = q_RAND_bytes(reinterpret_cast<unsigned char *>(key.data()),
+ key.size());
+ if (status <= 0)
+ key.clear();
+ }
+
+ QByteArray key;
+
+ Q_DISABLE_COPY(FallbackCookieSecret)
+};
+
+QByteArray fallbackSecret()
+{
+ static const FallbackCookieSecret generator;
+ return generator.key;
+}
+
+int next_timeoutMs(SSL *tlsConnection)
+{
+ Q_ASSERT(tlsConnection);
+ timeval timeLeft = {};
+ q_DTLSv1_get_timeout(tlsConnection, &timeLeft);
+ return timeLeft.tv_sec * 1000;
+}
+
+
+void delete_connection(SSL *ssl)
+{
+ // The 'deleter' for QSharedPointer<SSL>.
+ if (ssl)
+ q_SSL_free(ssl);
+}
+
+#if QT_CONFIG(opensslv11)
+
+void delete_BIO_ADDR(BIO_ADDR *bio)
+{
+ // A deleter for QSharedPointer<BIO_ADDR>
+ if (bio)
+ q_BIO_ADDR_free(bio);
+}
+
+void delete_bio_method(BIO_METHOD *method)
+{
+ // The 'deleter' for QSharedPointer<BIO_METHOD>.
+ if (method)
+ q_BIO_meth_free(method);
+}
+
+#endif // openssl 1.1
+
+// The 'deleter' for QScopedPointer<BIO>.
+struct bio_deleter
+{
+ static void cleanup(BIO *bio)
+ {
+ if (bio)
+ q_BIO_free(bio);
+ }
+};
+
+// The path MTU discovery is non-trivial: it's a mix of getsockopt/setsockopt
+// (IP_MTU/IP6_MTU/IP_MTU_DISCOVER) and fallback MTU values. It's not
+// supported on all platforms, worse so - imposes specific requirements on
+// underlying UDP socket etc. So for now, we either try a user-proposed MTU
+// hint or rely on our own fallback value. As a fallback mtu OpenSSL uses 576
+// for IPv4 and 1280 for IPv6 (RFC 791, RFC 2460). To KIS we use 576. This
+// rather small MTU value does not affect the size that can be read/written
+// by QDtls, only a handshake (which is allowed to fragment).
+enum class MtuGuess : long
+{
+ defaultMtu = 576
+};
+
+} // namespace dtlsutil
+
+namespace dtlscallbacks
+{
+
+extern "C" int q_generate_cookie_callback(SSL *ssl, unsigned char *dst,
+ unsigned *cookieLength)
+{
+ if (!ssl || !dst || !cookieLength) {
+ qCWarning(lcSsl,
+ "Failed to generate cookie - invalid (nullptr) parameter(s)");
+ return 0;
+ }
+
+ void *generic = q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData);
+ if (!generic) {
+ qCWarning(lcSsl, "SSL_get_ex_data returned nullptr, cannot generate cookie");
+ return 0;
+ }
+
+ *cookieLength = 0;
+
+ auto dtls = static_cast<dtlsopenssl::DtlsState *>(generic);
+ if (!dtls->secret.size())
+ return 0;
+
+ const QByteArray peerData(dtlsutil::cookie_for_peer(ssl));
+ if (!peerData.size())
+ return 0;
+
+ QMessageAuthenticationCode hmac(dtls->hashAlgorithm, dtls->secret);
+ hmac.addData(peerData);
+ const QByteArray cookie = hmac.result();
+ Q_ASSERT(cookie.size() >= 0);
+ // DTLS1_COOKIE_LENGTH is erroneously 256 bytes long, must be 255 - RFC 6347, 4.2.1.
+ *cookieLength = qMin(DTLS1_COOKIE_LENGTH - 1, cookie.size());
+ std::memcpy(dst, cookie.constData(), *cookieLength);
+
+ return 1;
+}
+
+extern "C" int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie,
+ unsigned cookieLength)
+{
+ if (!ssl || !cookie || !cookieLength) {
+ qCWarning(lcSsl, "Could not verify cookie, invalid (nullptr or zero) parameters");
+ return 0;
+ }
+
+ unsigned char newCookie[DTLS1_COOKIE_LENGTH] = {};
+ unsigned newCookieLength = 0;
+ if (q_generate_cookie_callback(ssl, newCookie, &newCookieLength) != 1)
+ return 0;
+
+ return newCookieLength == cookieLength
+ && !std::memcmp(cookie, newCookie, cookieLength);
+}
+
+extern "C" int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx)
+{
+ if (!ok) {
+ // Store the error and at which depth the error was detected.
+ SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx()));
+ if (!ssl) {
+ qCWarning(lcSsl, "X509_STORE_CTX_get_ex_data returned nullptr, handshake failure");
+ return 0;
+ }
+
+ void *generic = q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData);
+ if (!generic) {
+ qCWarning(lcSsl, "SSL_get_ex_data returned nullptr, handshake failure");
+ return 0;
+ }
+
+ auto dtls = static_cast<dtlsopenssl::DtlsState *>(generic);
+ dtls->x509Errors.append(QSslErrorEntry::fromStoreContext(ctx));
+ }
+
+ // Always return 1 (OK) to allow verification to continue. We handle the
+ // errors gracefully after collecting all errors, after verification has
+ // completed.
+ return 1;
+}
+
+extern "C" unsigned q_PSK_client_callback(SSL *ssl, const char *hint, char *identity,
+ unsigned max_identity_len, unsigned char *psk,
+ unsigned max_psk_len)
+{
+ auto *dtls = static_cast<dtlsopenssl::DtlsState *>(q_SSL_get_ex_data(ssl,
+ QSslSocketBackendPrivate::s_indexForSSLExtraData));
+ if (!dtls)
+ return 0;
+
+ Q_ASSERT(dtls->dtlsPrivate);
+ return dtls->dtlsPrivate->pskClientCallback(hint, identity, max_identity_len, psk, max_psk_len);
+}
+
+extern "C" unsigned q_PSK_server_callback(SSL *ssl, const char *identity, unsigned char *psk,
+ unsigned max_psk_len)
+{
+ auto *dtls = static_cast<dtlsopenssl::DtlsState *>(q_SSL_get_ex_data(ssl,
+ QSslSocketBackendPrivate::s_indexForSSLExtraData));
+ if (!dtls)
+ return 0;
+
+ Q_ASSERT(dtls->dtlsPrivate);
+ return dtls->dtlsPrivate->pskServerCallback(identity, psk, max_psk_len);
+}
+
+} // namespace dtlscallbacks
+
+namespace dtlsbio
+{
+
+extern "C" int q_dgram_read(BIO *bio, char *dst, int bytesToRead)
+{
+ if (!bio || !dst || bytesToRead <= 0) {
+ qCWarning(lcSsl, "invalid input parameter(s)");
+ return 0;
+ }
+
+ q_BIO_clear_retry_flags(bio);
+
+ auto dtls = static_cast<dtlsopenssl::DtlsState *>(q_BIO_get_app_data(bio));
+ // It's us who set data, if OpenSSL does too, the logic here is wrong
+ // then and we have to use BIO_set_app_data then!
+ Q_ASSERT(dtls);
+ int bytesRead = 0;
+ if (dtls->dgram.size()) {
+ bytesRead = qMin(dtls->dgram.size(), bytesToRead);
+ std::memcpy(dst, dtls->dgram.constData(), bytesRead);
+
+ if (!dtls->peeking)
+ dtls->dgram = dtls->dgram.mid(bytesRead);
+ } else {
+ bytesRead = -1;
+ }
+
+ if (bytesRead <= 0)
+ q_BIO_set_retry_read(bio);
+
+ return bytesRead;
+}
+
+extern "C" int q_dgram_write(BIO *bio, const char *src, int bytesToWrite)
+{
+ if (!bio || !src || bytesToWrite <= 0) {
+ qCWarning(lcSsl, "invalid input parameter(s)");
+ return 0;
+ }
+
+ q_BIO_clear_retry_flags(bio);
+
+ auto dtls = static_cast<dtlsopenssl::DtlsState *>(q_BIO_get_app_data(bio));
+ Q_ASSERT(dtls);
+ if (dtls->writeSuppressed) {
+ // See the comment in QDtls::startHandshake.
+ return bytesToWrite;
+ }
+
+ QUdpSocket *udpSocket = dtls->udpSocket;
+ Q_ASSERT(udpSocket);
+
+ const QByteArray dgram(QByteArray::fromRawData(src, bytesToWrite));
+ qint64 bytesWritten = -1;
+ if (udpSocket->state() == QAbstractSocket::ConnectedState) {
+ bytesWritten = udpSocket->write(dgram);
+ } else {
+ bytesWritten = udpSocket->writeDatagram(dgram, dtls->remoteAddress,
+ dtls->remotePort);
+ }
+
+ if (bytesWritten <= 0)
+ q_BIO_set_retry_write(bio);
+
+ Q_ASSERT(bytesWritten <= std::numeric_limits<int>::max());
+ return int(bytesWritten);
+}
+
+extern "C" int q_dgram_puts(BIO *bio, const char *src)
+{
+ if (!bio || !src) {
+ qCWarning(lcSsl, "invalid input parameter(s)");
+ return 0;
+ }
+
+ return q_dgram_write(bio, src, int(std::strlen(src)));
+}
+
+extern "C" long q_dgram_ctrl(BIO *bio, int cmd, long num, void *ptr)
+{
+ // This is our custom BIO_ctrl. bio.h defines a lot of BIO_CTRL_*
+ // and BIO_* constants and BIO_somename macros that expands to BIO_ctrl
+ // call with one of those constants as argument. What exactly BIO_ctrl
+ // does - depends on the 'cmd' and the type of BIO (so BIO_ctrl does
+ // not even have a single well-defined value meaning success or failure).
+ // We handle only the most generic commands - the ones documented for
+ // BIO_ctrl - and also DGRAM specific ones. And even for them - in most
+ // cases we do nothing but report a success or some non-error value.
+ // Documents also state: "Source/sink BIOs return an 0 if they do not
+ // recognize the BIO_ctrl() operation." - these are covered by 'default'
+ // label in the switch-statement below. Debug messages in the switch mean:
+ // 1) we got a command that is unexpected for dgram BIO, or:
+ // 2) we do not call any function that would lead to OpenSSL using this
+ // command.
+
+ if (!bio) {
+ qDebug(lcSsl, "invalid 'bio' parameter (nullptr)");
+ return -1;
+ }
+
+ auto dtls = static_cast<dtlsopenssl::DtlsState *>(q_BIO_get_app_data(bio));
+ Q_ASSERT(dtls);
+
+#if !QT_CONFIG(opensslv11)
+ Q_UNUSED(num)
+#endif
+
+ switch (cmd) {
+ // Let's start from the most generic ones, in the order in which they are
+ // documented (as BIO_ctrl):
+ case BIO_CTRL_RESET:
+ // BIO_reset macro.
+ // From documentation:
+ // "BIO_reset() normally returns 1 for success and 0 or -1 for failure.
+ // File BIOs are an exception, they return 0 for success and -1 for
+ // failure."
+ // We have nothing to reset and we are not file BIO.
+ return 1;
+ case BIO_C_FILE_SEEK:
+ case BIO_C_FILE_TELL:
+ qDtlsWarning("Unexpected cmd (BIO_C_FILE_SEEK/BIO_C_FILE_TELL)");
+ // These are for BIO_seek, BIO_tell. We are not a file BIO.
+ // Non-negative return value means success.
+ return 0;
+ case BIO_CTRL_FLUSH:
+ // BIO_flush, nothing to do, we do not buffer any data.
+ // 0 or -1 means error, 1 - success.
+ return 1;
+ case BIO_CTRL_EOF:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_EOF)");
+ // BIO_eof, 1 means EOF read. Makes no sense for us.
+ return 0;
+ case BIO_CTRL_SET_CLOSE:
+ // BIO_set_close with BIO_CLOSE/BIO_NOCLOSE flags. Documented as
+ // always returning 1.
+ // From the documentation:
+ // "Typically BIO_CLOSE is used in a source/sink BIO to indicate that
+ // the underlying I/O stream should be closed when the BIO is freed."
+ //
+ // QUdpSocket we work with is not BIO's business, ignoring.
+ return 1;
+ case BIO_CTRL_GET_CLOSE:
+ // BIO_get_close. No, never, see the comment above.
+ return 0;
+ case BIO_CTRL_PENDING:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_PENDING)");
+ // BIO_pending. Not used by DTLS/OpenSSL (we are not buffering).
+ return 0;
+ case BIO_CTRL_WPENDING:
+ // No, we have nothing buffered.
+ return 0;
+ // The constants below are not documented as a part BIO_ctrl documentation,
+ // but they are also not type-specific.
+ case BIO_CTRL_DUP:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DUP)");
+ // BIO_dup_state, not used by DTLS (and socket-related BIOs in general).
+ // For some very specific BIO type this 'cmd' would copy some state
+ // from 'bio' to (BIO*)'ptr'. 1 means success.
+ return 0;
+ case BIO_CTRL_SET_CALLBACK:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_SET_CALLBACK)");
+ // BIO_set_info_callback. We never call this, OpenSSL does not do this
+ // on its own (normally it's used if client code wants to have some
+ // debug information, for example, dumping handshake state via
+ // BIO_printf from SSL info_callback).
+ return 0;
+ case BIO_CTRL_GET_CALLBACK:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_GET_CALLBACK)");
+ // BIO_get_info_callback. We never call this.
+ if (ptr)
+ *static_cast<bio_info_cb **>(ptr) = nullptr;
+ return 0;
+ case BIO_CTRL_SET:
+ case BIO_CTRL_GET:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_SET/BIO_CTRL_GET)");
+ // Somewhat 'documented' as setting/getting IO type. Not used anywhere
+ // except BIO_buffer_get_num_lines (which contradics 'get IO type').
+ // Ignoring.
+ return 0;
+ // DGRAM-specific operation, we have to return some reasonable value
+ // (so far, I've encountered only peek mode switching, connect).
+ case BIO_CTRL_DGRAM_CONNECT:
+ // BIO_ctrl_dgram_connect. Not needed. Our 'dtls' already knows
+ // the peer's address/port. Report success though.
+ return 1;
+ case BIO_CTRL_DGRAM_SET_CONNECTED:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_SET_CONNECTED)");
+ // BIO_ctrl_dgram_set_connected. We never call it, OpenSSL does
+ // not call it on its own (so normally it's done by client code).
+ // Similar to BIO_CTRL_DGRAM_CONNECT, but it also informs the BIO
+ // that its UDP socket is connected. We never need it though.
+ return -1;
+ case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_SET_RECV_TIMEOUT)");
+ // Essentially setsockopt with SO_RCVTIMEO, not needed, our sockets
+ // are non-blocking.
+ return -1;
+ case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_GET_RECV_TIMEOUT)");
+ // getsockopt with SO_RCVTIMEO, not needed, our sockets are
+ // non-blocking. ptr is timeval *.
+ return -1;
+ case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_SET_SEND_TIMEOUT)");
+ // setsockopt, SO_SNDTIMEO, cannot happen.
+ return -1;
+ case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_GET_SEND_TIMEOUT)");
+ // getsockopt, SO_SNDTIMEO, cannot happen.
+ return -1;
+ case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP:
+ // BIO_dgram_recv_timedout. No, we are non-blocking.
+ return 0;
+ case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP:
+ // BIO_dgram_send_timedout. No, we are non-blocking.
+ return 0;
+ case BIO_CTRL_DGRAM_MTU_DISCOVER:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_MTU_DISCOVER)");
+ // setsockopt, IP_MTU_DISCOVER/IP6_MTU_DISCOVER, to be done
+ // in QUdpSocket instead. OpenSSL never calls it, only client
+ // code.
+ return 1;
+ case BIO_CTRL_DGRAM_QUERY_MTU:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_QUERY_MTU)");
+ // To be done in QUdpSocket instead.
+ return 1;
+ case BIO_CTRL_DGRAM_GET_FALLBACK_MTU:
+ qDtlsWarning("Unexpected command *BIO_CTRL_DGRAM_GET_FALLBACK_MTU)");
+ // Without SSL_OP_NO_QUERY_MTU set on SSL, OpenSSL can request for
+ // fallback MTU after several re-transmissions.
+ // Should never happen in our case.
+ return long(dtlsutil::MtuGuess::defaultMtu);
+ case BIO_CTRL_DGRAM_GET_MTU:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_GET_MTU)");
+ return -1;
+ case BIO_CTRL_DGRAM_SET_MTU:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_SET_MTU)");
+ // Should not happen (we don't call BIO_ctrl with this parameter)
+ // and set MTU on SSL instead.
+ return -1; // num is mtu and it's a return value meaning success.
+ case BIO_CTRL_DGRAM_MTU_EXCEEDED:
+ qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_MTU_EXCEEDED)");
+ return 0;
+ case BIO_CTRL_DGRAM_GET_PEER:
+ qDtlsDebug("BIO_CTRL_DGRAM_GET_PEER");
+ // BIO_dgram_get_peer. We do not return a real address (DTLS is not
+ // using this address), but let's pretend a success.
+ switch (dtls->remoteAddress.protocol()) {
+ case QAbstractSocket::IPv6Protocol:
+ return sizeof(sockaddr_in6);
+ case QAbstractSocket::IPv4Protocol:
+ return sizeof(sockaddr_in);
+ default:
+ return -1;
+ }
+ case BIO_CTRL_DGRAM_SET_PEER:
+ // Similar to BIO_CTRL_DGRAM_CONNECTED.
+ return 1;
+ case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:
+ // DTLSTODO: I'm not sure yet, how it's used by OpenSSL.
+ return 1;
+ case BIO_CTRL_DGRAM_SET_DONT_FRAG:
+ qDtlsDebug("BIO_CTRL_DGRAM_SET_DONT_FRAG");
+ // To be done in QUdpSocket, it's about IP_DONTFRAG etc.
+ return 1;
+ case BIO_CTRL_DGRAM_GET_MTU_OVERHEAD:
+ // AFAIK it's 28 for IPv4 and 48 for IPv6, but let's pretend it's 0
+ // so that OpenSSL does not start suddenly fragmenting the first
+ // client hello (which will result in DTLSv1_listen rejecting it).
+ return 0;
+#if QT_CONFIG(opensslv11)
+ case BIO_CTRL_DGRAM_SET_PEEK_MODE:
+ dtls->peeking = num;
+ return 1;
+#endif
+ default:;
+#if QT_DTLS_VERBOSE
+ qWarning() << "Unexpected cmd (" << cmd << ")";
+#endif
+ }
+
+ return 0;
+}
+
+extern "C" int q_dgram_create(BIO *bio)
+{
+#if QT_CONFIG(opensslv11)
+ q_BIO_set_init(bio, 1);
+#else
+ bio->init = 1;
+#endif
+ // With a custom BIO you'd normally allocate some implementation-specific
+ // data and append it to this new BIO: bio->ptr = ... (pre 1.0.2) or
+ // BIO_set_data (1.1). We don't need it and thus q_dgram_destroy below
+ // is a noop.
+ return 1;
+}
+
+extern "C" int q_dgram_destroy(BIO *bio)
+{
+ Q_UNUSED(bio)
+ return 1;
+}
+
+const char * const qdtlsMethodName = "qdtlsbio";
+
+#if !QT_CONFIG(opensslv11)
+
+/*
+typedef struct bio_method_st {
+ int type;
+ const char *name;
+ int (*bwrite) (BIO *, const char *, int);
+ int (*bread) (BIO *, char *, int);
+ int (*bputs) (BIO *, const char *);
+ int (*bgets) (BIO *, char *, int);
+ long (*ctrl) (BIO *, int, long, void *);
+ int (*create) (BIO *);
+ int (*destroy) (BIO *);
+ long (*callback_ctrl) (BIO *, int, bio_info_cb *);
+} BIO_METHOD;
+*/
+
+bio_method_st qdtlsCustomBioMethod =
+{
+ BIO_TYPE_DGRAM,
+ qdtlsMethodName,
+ q_dgram_write,
+ q_dgram_read,
+ q_dgram_puts,
+ nullptr,
+ q_dgram_ctrl,
+ q_dgram_create,
+ q_dgram_destroy,
+ nullptr
+};
+
+#endif // openssl < 1.1
+
+} // namespace dtlsbio
+
+namespace dtlsopenssl
+{
+
+bool DtlsState::init(QDtlsBasePrivate *dtlsBase, QUdpSocket *socket,
+ const QHostAddress &remote, quint16 port,
+ const QByteArray &receivedMessage)
+{
+ Q_ASSERT(dtlsBase);
+ Q_ASSERT(socket);
+
+ if (!tlsContext.data() && !initTls(dtlsBase))
+ return false;
+
+ udpSocket = socket;
+
+ setLinkMtu(dtlsBase);
+
+ dgram = receivedMessage;
+ remoteAddress = remote;
+ remotePort = port;
+
+ // SSL_get_rbio does not increment a reference count.
+ BIO *bio = q_SSL_get_rbio(tlsConnection.data());
+ Q_ASSERT(bio);
+ q_BIO_set_app_data(bio, this);
+
+ return true;
+}
+
+void DtlsState::reset()
+{
+ tlsConnection.reset();
+ tlsContext.reset();
+}
+
+bool DtlsState::initTls(QDtlsBasePrivate *dtlsBase)
+{
+ if (tlsContext.data())
+ return true;
+
+ if (!QSslSocket::supportsSsl())
+ return false;
+
+ if (!initCtxAndConnection(dtlsBase))
+ return false;
+
+ if (!initBIO(dtlsBase)) {
+ tlsConnection.reset();
+ tlsContext.reset();
+ return false;
+ }
+
+ return true;
+}
+
+static QString msgFunctionFailed(const char *function)
+{
+ //: %1: Some function
+ return QDtls::tr("%1 failed").arg(QLatin1String(function));
+}
+
+bool DtlsState::initCtxAndConnection(QDtlsBasePrivate *dtlsBase)
+{
+ Q_ASSERT(dtlsBase);
+ Q_ASSERT(QSslSocket::supportsSsl());
+
+ if (dtlsBase->mode == QSslSocket::UnencryptedMode) {
+ dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
+ QDtls::tr("Invalid SslMode, SslServerMode or SslClientMode expected"));
+ return false;
+ }
+
+ if (!QDtlsBasePrivate::isDtlsProtocol(dtlsBase->dtlsConfiguration.protocol)) {
+ dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
+ QDtls::tr("Invalid protocol version, DTLS protocol expected"));
+ return false;
+ }
+
+ // Create a deep copy of our configuration
+ auto configurationCopy = new QSslConfigurationPrivate(dtlsBase->dtlsConfiguration);
+ configurationCopy->ref.store(0); // the QSslConfiguration constructor refs up
+
+ // DTLSTODO: check we do not set something DTLS-incompatible there ...
+ TlsContext newContext(QSslContext::sharedFromConfiguration(dtlsBase->mode,
+ configurationCopy,
+ dtlsBase->dtlsConfiguration.allowRootCertOnDemandLoading));
+
+ if (newContext->error() != QSslError::NoError) {
+ dtlsBase->setDtlsError(QDtlsError::TlsInitializationError, newContext->errorString());
+ return false;
+ }
+
+ TlsConnection newConnection(newContext->createSsl(), dtlsutil::delete_connection);
+ if (!newConnection.data()) {
+ dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
+ msgFunctionFailed("SSL_new"));
+ return false;
+ }
+
+ const int set = q_SSL_set_ex_data(newConnection.data(),
+ QSslSocketBackendPrivate::s_indexForSSLExtraData,
+ this);
+
+ if (set != 1 && configurationCopy->peerVerifyMode != QSslSocket::VerifyNone) {
+ dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
+ msgFunctionFailed("SSL_set_ex_data"));
+ return false;
+ }
+
+ if (dtlsBase->mode == QSslSocket::SslServerMode) {
+ if (dtlsBase->dtlsConfiguration.dtlsCookieEnabled)
+ q_SSL_set_options(newConnection.data(), SSL_OP_COOKIE_EXCHANGE);
+ q_SSL_set_psk_server_callback(newConnection.data(), dtlscallbacks::q_PSK_server_callback);
+ } else {
+ q_SSL_set_psk_client_callback(newConnection.data(), dtlscallbacks::q_PSK_client_callback);
+ }
+
+ tlsContext.swap(newContext);
+ tlsConnection.swap(newConnection);
+
+ return true;
+}
+
+bool DtlsState::initBIO(QDtlsBasePrivate *dtlsBase)
+{
+ Q_ASSERT(dtlsBase);
+ Q_ASSERT(tlsContext.data() && tlsConnection.data());
+
+#if QT_CONFIG(opensslv11)
+ BioMethod customMethod(q_BIO_meth_new(BIO_TYPE_DGRAM, dtlsbio::qdtlsMethodName),
+ dtlsutil::delete_bio_method);
+ if (!customMethod.data()) {
+ dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
+ msgFunctionFailed("BIO_meth_new"));
+ return false;
+ }
+
+ BIO_METHOD *biom = customMethod.data();
+ q_BIO_meth_set_create(biom, dtlsbio::q_dgram_create);
+ q_BIO_meth_set_destroy(biom, dtlsbio::q_dgram_destroy);
+ q_BIO_meth_set_read(biom, dtlsbio::q_dgram_read);
+ q_BIO_meth_set_write(biom, dtlsbio::q_dgram_write);
+ q_BIO_meth_set_puts(biom, dtlsbio::q_dgram_puts);
+ q_BIO_meth_set_ctrl(biom, dtlsbio::q_dgram_ctrl);
+#else
+ BIO_METHOD *biom = &dtlsbio::qdtlsCustomBioMethod;
+#endif // openssl 1.1
+
+ QScopedPointer<BIO, dtlsutil::bio_deleter> newBio(q_BIO_new(biom));
+ BIO *bio = newBio.data();
+ if (!bio) {
+ dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
+ msgFunctionFailed("BIO_new"));
+ return false;
+ }
+
+ q_SSL_set_bio(tlsConnection.data(), bio, bio);
+ newBio.take();
+
+#if QT_CONFIG(opensslv11)
+ bioMethod.swap(customMethod);
+#endif // openssl 1.1
+
+ return true;
+}
+
+void DtlsState::setLinkMtu(QDtlsBasePrivate *dtlsBase)
+{
+ Q_ASSERT(dtlsBase);
+ Q_ASSERT(udpSocket);
+ Q_ASSERT(tlsConnection.data());
+
+ long mtu = dtlsBase->mtuHint;
+ if (!mtu) {
+ // If the underlying QUdpSocket was connected, getsockopt with
+ // IP_MTU/IP6_MTU can give us some hint:
+ bool optionFound = false;
+ if (udpSocket->state() == QAbstractSocket::ConnectedState) {
+ const QVariant val(udpSocket->socketOption(QAbstractSocket::PathMtuSocketOption));
+ if (val.isValid() && val.canConvert<int>())
+ mtu = val.toInt(&optionFound);
+ }
+
+ if (!optionFound || mtu <= 0) {
+ // OK, our own initial guess.
+ mtu = long(dtlsutil::MtuGuess::defaultMtu);
+ }
+ }
+
+ // For now, we disable this option.
+ q_SSL_set_options(tlsConnection.data(), SSL_OP_NO_QUERY_MTU);
+
+ q_DTLS_set_link_mtu(tlsConnection.data(), mtu);
+}
+
+} // namespace dtlsopenssl
+
+QDtlsClientVerifierOpenSSL::QDtlsClientVerifierOpenSSL()
+{
+ secret = dtlsutil::fallbackSecret();
+}
+
+bool QDtlsClientVerifierOpenSSL::verifyClient(QUdpSocket *socket, const QByteArray &dgram,
+ const QHostAddress &address, quint16 port)
+{
+ Q_ASSERT(socket);
+ Q_ASSERT(dgram.size());
+ Q_ASSERT(!address.isNull());
+ Q_ASSERT(port);
+
+ clearDtlsError();
+ verifiedClientHello.clear();
+
+ if (!dtls.init(this, socket, address, port, dgram))
+ return false;
+
+ dtls.secret = secret;
+ dtls.hashAlgorithm = hashAlgorithm;
+
+ Q_ASSERT(dtls.tlsConnection.data());
+#if QT_CONFIG(opensslv11)
+ QSharedPointer<BIO_ADDR> peer(q_BIO_ADDR_new(), dtlsutil::delete_BIO_ADDR);
+ if (!peer.data()) {
+ setDtlsError(QDtlsError::TlsInitializationError,
+ QDtlsClientVerifier::tr("BIO_ADDR_new failed, ignoring client hello"));
+ return false;
+ }
+
+ const int ret = q_DTLSv1_listen(dtls.tlsConnection.data(), peer.data());
+ if (ret < 0) {
+ // Since 1.1 - it's a fatal error (not so in 1.0.2 for non-blocking socket)
+ setDtlsError(QDtlsError::TlsFatalError, QSslSocketBackendPrivate::getErrorsFromOpenSsl());
+ return false;
+ }
+#else
+ qt_sockaddr peer;
+ const int ret = q_DTLSv1_listen(dtls.tlsConnection.data(), &peer);
+#endif
+ if (ret > 0) {
+ verifiedClientHello = dgram;
+ return true;
+ }
+
+ return false;
+}
+
+void QDtlsPrivateOpenSSL::TimeoutHandler::start(int hintMs)
+{
+ Q_ASSERT(timerId == -1);
+ timerId = startTimer(hintMs > 0 ? hintMs : timeoutMs, Qt::PreciseTimer);
+}
+
+void QDtlsPrivateOpenSSL::TimeoutHandler::doubleTimeout()
+{
+ if (timeoutMs * 2 < 60000)
+ timeoutMs *= 2;
+ else
+ timeoutMs = 60000;
+}
+
+void QDtlsPrivateOpenSSL::TimeoutHandler::stop()
+{
+ if (timerId != -1) {
+ killTimer(timerId);
+ timerId = -1;
+ }
+}
+
+void QDtlsPrivateOpenSSL::TimeoutHandler::timerEvent(QTimerEvent *event)
+{
+ Q_UNUSED(event)
+ Q_ASSERT(timerId != -1);
+
+ killTimer(timerId);
+ timerId = -1;
+
+ Q_ASSERT(dtlsConnection);
+ dtlsConnection->reportTimeout();
+}
+
+QDtlsPrivateOpenSSL::QDtlsPrivateOpenSSL()
+{
+ secret = dtlsutil::fallbackSecret();
+ dtls.dtlsPrivate = this;
+}
+
+bool QDtlsPrivateOpenSSL::startHandshake(QUdpSocket *socket, const QByteArray &dgram)
+{
+ Q_ASSERT(socket);
+ Q_ASSERT(handshakeState == QDtls::HandshakeNotStarted);
+
+ clearDtlsError();
+ connectionEncrypted = false;
+
+ if (!dtls.init(this, socket, remoteAddress, remotePort, dgram))
+ return false;
+
+ if (mode == QSslSocket::SslServerMode && dtlsConfiguration.dtlsCookieEnabled) {
+ dtls.secret = secret;
+ dtls.hashAlgorithm = hashAlgorithm;
+ // Let's prepare the state machine so that message sequence 1 does not
+ // surprise DTLS/OpenSSL (such a message would be disregarded as
+ // 'stale or future' in SSL_accept otherwise):
+ int result = 0;
+#if QT_CONFIG(opensslv11)
+ QSharedPointer<BIO_ADDR> peer(q_BIO_ADDR_new(), dtlsutil::delete_BIO_ADDR);
+ if (!peer.data()) {
+ setDtlsError(QDtlsError::TlsInitializationError,
+ QDtls::tr("BIO_ADD_new failed, cannot start handshake"));
+ return false;
+ }
+
+ // If it's an invalid/unexpected ClientHello, we don't want to send
+ // VerifyClientRequest - it's a job of QDtlsClientVerifier - so we
+ // suppress any attempts to write into socket:
+ dtls.writeSuppressed = true;
+ result = q_DTLSv1_listen(dtls.tlsConnection.data(), peer.data());
+ dtls.writeSuppressed = false;
+#else
+ qt_sockaddr peer;
+ result = q_DTLSv1_listen(dtls.tlsConnection.data(), &peer);
+#endif
+ if (result <= 0) {
+ setDtlsError(QDtlsError::TlsFatalError,
+ QDtls::tr("Cannot start the handshake, verified client hello expected"));
+ dtls.reset();
+ return false;
+ }
+ }
+
+ handshakeState = QDtls::HandshakeInProgress;
+ opensslErrors.clear();
+ tlsErrors.clear();
+
+ return continueHandshake(socket, dgram);
+}
+
+bool QDtlsPrivateOpenSSL::continueHandshake(QUdpSocket *socket, const QByteArray &dgram)
+{
+ Q_ASSERT(socket);
+
+ Q_ASSERT(handshakeState == QDtls::HandshakeInProgress);
+
+ clearDtlsError();
+
+ if (timeoutHandler.data())
+ timeoutHandler->stop();
+
+ if (!dtls.init(this, socket, remoteAddress, remotePort, dgram))
+ return false;
+
+ dtls.x509Errors.clear();
+
+ int result = 0;
+ if (mode == QSslSocket::SslServerMode)
+ result = q_SSL_accept(dtls.tlsConnection.data());
+ else
+ result = q_SSL_connect(dtls.tlsConnection.data());
+
+ // DTLSTODO: Investigate/test if it makes sense - QSslSocket can emit
+ // peerVerifyError at this point (and thus potentially client code
+ // will close the underlying TCP connection immediately), but we are using
+ // QUdpSocket, no connection to close, our verification callback returns 1
+ // (verified OK) and this probably means OpenSSL has already sent a reply
+ // to the server's hello/certificate.
+
+ opensslErrors << dtls.x509Errors;
+
+ if (result <= 0) {
+ const auto code = q_SSL_get_error(dtls.tlsConnection.data(), result);
+ switch (code) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ // DTLSTODO: to be tested - in principle, if it was the first call to
+ // continueHandshake and server for some reason discards the client
+ // hello message (even the verified one) - our 'this' will probably
+ // forever stay in this strange InProgress state? (the client
+ // will dully re-transmit the same hello and we discard it again?)
+ // SSL_get_state can provide more information about state
+ // machine and we can switch to NotStarted (since we have not
+ // replied with our hello ...)
+ if (!timeoutHandler.data()) {
+ timeoutHandler.reset(new TimeoutHandler);
+ timeoutHandler->dtlsConnection = this;
+ } else {
+ // Back to 1s.
+ timeoutHandler->resetTimeout();
+ }
+
+ timeoutHandler->start();
+
+ return true; // The handshake is not yet complete.
+ default:
+ storePeerCertificates();
+ setDtlsError(QDtlsError::TlsFatalError,
+ QSslSocketBackendPrivate::msgErrorsDuringHandshake());
+ dtls.reset();
+ handshakeState = QDtls::HandshakeNotStarted;
+ return false;
+ }
+ }
+
+ storePeerCertificates();
+ fetchNegotiatedParameters();
+
+ const bool doVerifyPeer = dtlsConfiguration.peerVerifyMode == QSslSocket::VerifyPeer
+ || (dtlsConfiguration.peerVerifyMode == QSslSocket::AutoVerifyPeer
+ && mode == QSslSocket::SslClientMode);
+
+ if (!doVerifyPeer || verifyPeer() || tlsErrorsWereIgnored()) {
+ connectionEncrypted = true;
+ handshakeState = QDtls::HandshakeComplete;
+ return true;
+ }
+
+ setDtlsError(QDtlsError::PeerVerificationError, QDtls::tr("Peer verification failed"));
+ handshakeState = QDtls::PeerVerificationFailed;
+ return false;
+}
+
+
+bool QDtlsPrivateOpenSSL::handleTimeout(QUdpSocket *socket)
+{
+ Q_ASSERT(socket);
+
+ Q_ASSERT(timeoutHandler.data());
+ Q_ASSERT(dtls.tlsConnection.data());
+
+ clearDtlsError();
+
+ dtls.udpSocket = socket;
+
+ if (q_DTLSv1_handle_timeout(dtls.tlsConnection.data()) > 0) {
+ timeoutHandler->doubleTimeout();
+ timeoutHandler->start();
+ } else {
+ timeoutHandler->start(dtlsutil::next_timeoutMs(dtls.tlsConnection.data()));
+ }
+
+ return true;
+}
+
+bool QDtlsPrivateOpenSSL::resumeHandshake(QUdpSocket *socket)
+{
+ Q_UNUSED(socket);
+ Q_ASSERT(socket);
+ Q_ASSERT(handshakeState == QDtls::PeerVerificationFailed);
+
+ clearDtlsError();
+
+ if (tlsErrorsWereIgnored()) {
+ handshakeState = QDtls::HandshakeComplete;
+ connectionEncrypted = true;
+ tlsErrors.clear();
+ tlsErrorsToIgnore.clear();
+ return true;
+ }
+
+ return false;
+}
+
+void QDtlsPrivateOpenSSL::abortHandshake(QUdpSocket *socket)
+{
+ Q_ASSERT(socket);
+ Q_ASSERT(handshakeState == QDtls::PeerVerificationFailed
+ || handshakeState == QDtls::HandshakeInProgress);
+
+ clearDtlsError();
+
+ if (handshakeState == QDtls::PeerVerificationFailed) {
+ // Yes, while peer verification failed, we were actually encrypted.
+ // Let's play it nice - inform our peer about connection shut down.
+ sendShutdownAlert(socket);
+ } else {
+ resetDtls();
+ }
+}
+
+void QDtlsPrivateOpenSSL::sendShutdownAlert(QUdpSocket *socket)
+{
+ Q_ASSERT(socket);
+
+ clearDtlsError();
+
+ if (connectionEncrypted && !connectionWasShutdown) {
+ dtls.udpSocket = socket;
+ Q_ASSERT(dtls.tlsConnection.data());
+ q_SSL_shutdown(dtls.tlsConnection.data());
+ }
+
+ resetDtls();
+}
+
+qint64 QDtlsPrivateOpenSSL::writeDatagramEncrypted(QUdpSocket *socket,
+ const QByteArray &dgram)
+{
+ Q_ASSERT(socket);
+ Q_ASSERT(dtls.tlsConnection.data());
+ Q_ASSERT(connectionEncrypted);
+
+ clearDtlsError();
+
+ dtls.udpSocket = socket;
+ const int written = q_SSL_write(dtls.tlsConnection.data(),
+ dgram.constData(), dgram.size());
+ if (written > 0)
+ return written;
+
+ const unsigned long errorCode = q_ERR_get_error();
+ if (!dgram.size() && errorCode == SSL_ERROR_NONE) {
+ // With OpenSSL <= 1.1 this can happen. For example, DTLS client
+ // tries to reconnect (while re-using the same address/port) -
+ // DTLS server drops a message with unexpected epoch but says - no
+ // error. We leave to client code to resolve such problems until
+ // OpenSSL provides something better.
+ return 0;
+ }
+
+ switch (errorCode) {
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ // We do not set any error/description ... a user can probably re-try
+ // sending a datagram.
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ connectionWasShutdown = true;
+ setDtlsError(QDtlsError::TlsFatalError, QDtls::tr("The DTLS connection has been closed"));
+ handshakeState = QDtls::HandshakeNotStarted;
+ dtls.reset();
+ break;
+ case SSL_ERROR_SYSCALL:
+ case SSL_ERROR_SSL:
+ default:
+ // DTLSTODO: we don't know yet what to do. Tests needed - probably,
+ // some errors can be just ignored (it's UDP, not TCP after all).
+ // Unlike QSslSocket we do not abort though.
+ QString description(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
+ if (socket->error() != QAbstractSocket::UnknownSocketError && description.isEmpty()) {
+ setDtlsError(QDtlsError::UnderlyingSocketError, socket->errorString());
+ } else {
+ setDtlsError(QDtlsError::TlsFatalError,
+ QDtls::tr("Error while writing: %1").arg(description));
+ }
+ }
+
+ return -1;
+}
+
+QByteArray QDtlsPrivateOpenSSL::decryptDatagram(QUdpSocket *socket, const QByteArray &tlsdgram)
+{
+ Q_ASSERT(socket);
+ Q_ASSERT(tlsdgram.size());
+
+ Q_ASSERT(dtls.tlsConnection.data());
+ Q_ASSERT(connectionEncrypted);
+
+ dtls.dgram = tlsdgram;
+ dtls.udpSocket = socket;
+
+ clearDtlsError();
+
+ QByteArray dgram;
+ dgram.resize(tlsdgram.size());
+ const int read = q_SSL_read(dtls.tlsConnection.data(), dgram.data(),
+ dgram.size());
+
+ if (read > 0) {
+ dgram.resize(read);
+ return dgram;
+ }
+
+ dgram.clear();
+ unsigned long errorCode = q_ERR_get_error();
+ if (errorCode == SSL_ERROR_NONE) {
+ const int shutdown = q_SSL_get_shutdown(dtls.tlsConnection.data());
+ if (shutdown & SSL_RECEIVED_SHUTDOWN)
+ errorCode = SSL_ERROR_ZERO_RETURN;
+ else
+ return dgram;
+ }
+
+ switch (errorCode) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ return dgram;
+ case SSL_ERROR_ZERO_RETURN:
+ // "The connection was shut down cleanly" ... hmm, whatever,
+ // needs testing (DTLSTODO).
+ connectionWasShutdown = true;
+ setDtlsError(QDtlsError::RemoteClosedConnectionError,
+ QDtls::tr("The DTLS connection has been shutdown"));
+ dtls.reset();
+ connectionEncrypted = false;
+ handshakeState = QDtls::HandshakeNotStarted;
+ return dgram;
+ case SSL_ERROR_SYSCALL: // some IO error
+ case SSL_ERROR_SSL: // error in the SSL library
+ // DTLSTODO: Apparently, some errors can be ignored, for example,
+ // ECONNRESET etc. This all needs a lot of testing!!!
+ default:
+ setDtlsError(QDtlsError::TlsNonFatalError,
+ QDtls::tr("Error while reading: %1")
+ .arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl()));
+ return dgram;
+ }
+}
+
+unsigned QDtlsPrivateOpenSSL::pskClientCallback(const char *hint, char *identity,
+ unsigned max_identity_len,
+ unsigned char *psk,
+ unsigned max_psk_len)
+{
+ // The code below is taken (with some modifications) from qsslsocket_openssl
+ // - alas, we cannot simply re-use it, it's in QSslSocketPrivate.
+
+ Q_Q(QDtls);
+
+ {
+ QSslPreSharedKeyAuthenticator authenticator;
+ // Fill in some read-only fields (for client code)
+ if (hint) {
+ identityHint.clear();
+ identityHint.append(hint);
+ // From the original code in QSslSocket:
+ // "it's NULL terminated, but do not include the NULL" == this fromRawData(ptr/size).
+ authenticator.d->identityHint = QByteArray::fromRawData(identityHint.constData(),
+ int(std::strlen(hint)));
+ }
+
+ authenticator.d->maximumIdentityLength = int(max_identity_len) - 1; // needs to be NULL terminated
+ authenticator.d->maximumPreSharedKeyLength = int(max_psk_len);
+
+ pskAuthenticator.swap(authenticator);
+ }
+
+ // Let the client provide the remaining bits...
+ emit q->pskRequired(&pskAuthenticator);
+
+ // No PSK set? Return now to make the handshake fail
+ if (pskAuthenticator.preSharedKey().isEmpty())
+ return 0;
+
+ // Copy data back into OpenSSL
+ const int identityLength = qMin(pskAuthenticator.identity().length(),
+ pskAuthenticator.maximumIdentityLength());
+ std::memcpy(identity, pskAuthenticator.identity().constData(), identityLength);
+ identity[identityLength] = 0;
+
+ const int pskLength = qMin(pskAuthenticator.preSharedKey().length(),
+ pskAuthenticator.maximumPreSharedKeyLength());
+ std::memcpy(psk, pskAuthenticator.preSharedKey().constData(), pskLength);
+
+ return pskLength;
+}
+
+unsigned QDtlsPrivateOpenSSL::pskServerCallback(const char *identity, unsigned char *psk,
+ unsigned max_psk_len)
+{
+ Q_Q(QDtls);
+
+ {
+ QSslPreSharedKeyAuthenticator authenticator;
+ // Fill in some read-only fields (for the user)
+ authenticator.d->identityHint = dtlsConfiguration.preSharedKeyIdentityHint;
+ authenticator.d->identity = identity;
+ authenticator.d->maximumIdentityLength = 0; // user cannot set an identity
+ authenticator.d->maximumPreSharedKeyLength = int(max_psk_len);
+
+ pskAuthenticator.swap(authenticator);
+ }
+
+ // Let the client provide the remaining bits...
+ emit q->pskRequired(&pskAuthenticator);
+
+ // No PSK set? Return now to make the handshake fail
+ if (pskAuthenticator.preSharedKey().isEmpty())
+ return 0;
+
+ // Copy data back into OpenSSL
+ const int pskLength = qMin(pskAuthenticator.preSharedKey().length(),
+ pskAuthenticator.maximumPreSharedKeyLength());
+
+ std::memcpy(psk, pskAuthenticator.preSharedKey().constData(), pskLength);
+
+ return pskLength;
+}
+
+// The definition is located in qsslsocket_openssl.cpp.
+QSslError _q_OpenSSL_to_QSslError(int errorCode, const QSslCertificate &cert);
+
+bool QDtlsPrivateOpenSSL::verifyPeer()
+{
+ // DTLSTODO: Windows-specific code for CA fetcher is not here yet.
+ QVector<QSslError> errors;
+
+ // Check the whole chain for blacklisting (including root, as we check for
+ // subjectInfo and issuer)
+ for (const QSslCertificate &cert : qAsConst(dtlsConfiguration.peerCertificateChain)) {
+ if (QSslCertificatePrivate::isBlacklisted(cert))
+ errors << QSslError(QSslError::CertificateBlacklisted, cert);
+ }
+
+ if (dtlsConfiguration.peerCertificate.isNull()) {
+ errors << QSslError(QSslError::NoPeerCertificate);
+ } else if (mode == QSslSocket::SslClientMode) {
+ // Check the peer certificate itself. First try the subject's common name
+ // (CN) as a wildcard, then try all alternate subject name DNS entries the
+ // same way.
+
+ // QSslSocket has a rather twisted logic: if verificationPeerName
+ // is empty, we call QAbstractSocket::peerName(), which returns
+ // either peerName (can be set by setPeerName) or host name
+ // (can be set as a result of connectToHost).
+ QString name = peerVerificationName;
+ if (name.isEmpty()) {
+ Q_ASSERT(dtls.udpSocket);
+ name = dtls.udpSocket->peerName();
+ }
+
+ if (!QSslSocketPrivate::isMatchingHostname(dtlsConfiguration.peerCertificate, name))
+ errors << QSslError(QSslError::HostNameMismatch, dtlsConfiguration.peerCertificate);
+ }
+
+ // Translate errors from the error list into QSslErrors
+ errors.reserve(errors.size() + opensslErrors.size());
+ for (const auto &error : qAsConst(opensslErrors)) {
+ errors << _q_OpenSSL_to_QSslError(error.code,
+ dtlsConfiguration.peerCertificateChain.value(error.depth));
+ }
+
+ tlsErrors = errors;
+ return tlsErrors.isEmpty();
+}
+
+void QDtlsPrivateOpenSSL::storePeerCertificates()
+{
+ Q_ASSERT(dtls.tlsConnection.data());
+ // Store the peer certificate and chain. For clients, the peer certificate
+ // chain includes the peer certificate; for servers, it doesn't. Both the
+ // peer certificate and the chain may be empty if the peer didn't present
+ // any certificate.
+ X509 *x509 = q_SSL_get_peer_certificate(dtls.tlsConnection.data());
+ dtlsConfiguration.peerCertificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509);
+ q_X509_free(x509);
+ if (dtlsConfiguration.peerCertificateChain.isEmpty()) {
+ auto stack = q_SSL_get_peer_cert_chain(dtls.tlsConnection.data());
+ dtlsConfiguration.peerCertificateChain = QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(stack);
+ if (!dtlsConfiguration.peerCertificate.isNull() && mode == QSslSocket::SslServerMode)
+ dtlsConfiguration.peerCertificateChain.prepend(dtlsConfiguration.peerCertificate);
+ }
+}
+
+bool QDtlsPrivateOpenSSL::tlsErrorsWereIgnored() const
+{
+ // check whether the errors we got are all in the list of expected errors
+ // (applies only if the method QDtlsConnection::ignoreTlsErrors(const
+ // QVector<QSslError> &errors) was called)
+ for (const QSslError &error : tlsErrors) {
+ if (!tlsErrorsToIgnore.contains(error))
+ return false;
+ }
+
+ return !tlsErrorsToIgnore.empty();
+}
+
+void QDtlsPrivateOpenSSL::fetchNegotiatedParameters()
+{
+ Q_ASSERT(dtls.tlsConnection.data());
+
+ const SSL_CIPHER *cipher = q_SSL_get_current_cipher(dtls.tlsConnection.data());
+ sessionCipher = cipher ? QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher)
+ : QSslCipher();
+
+ // Note: cipher's protocol version will be reported as either TLS 1.0 or
+ // TLS 1.2, that's how it's set by OpenSSL (and that's what they are?).
+
+ switch (q_SSL_version(dtls.tlsConnection.data())) {
+ case DTLS1_VERSION:
+ sessionProtocol = QSsl::DtlsV1_0;
+ break;
+ case DTLS1_2_VERSION:
+ sessionProtocol = QSsl::DtlsV1_2;
+ break;
+ default:
+ qCWarning(lcSsl, "unknown protocol version");
+ sessionProtocol = QSsl::UnknownProtocol;
+ }
+}
+
+void QDtlsPrivateOpenSSL::reportTimeout()
+{
+ Q_Q(QDtls);
+
+ emit q->handshakeTimeout();
+}
+
+void QDtlsPrivateOpenSSL::resetDtls()
+{
+ dtls.reset();
+ connectionEncrypted = false;
+ tlsErrors.clear();
+ tlsErrorsToIgnore.clear();
+ dtlsConfiguration.peerCertificate.clear();
+ dtlsConfiguration.peerCertificateChain.clear();
+ connectionWasShutdown = false;
+ handshakeState = QDtls::HandshakeNotStarted;
+ sessionCipher = {};
+ sessionProtocol = QSsl::UnknownProtocol;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qdtls_openssl_p.h b/src/network/ssl/qdtls_openssl_p.h
new file mode 100644
index 0000000000..9306fa2433
--- /dev/null
+++ b/src/network/ssl/qdtls_openssl_p.h
@@ -0,0 +1,213 @@
+/****************************************************************************
+**
+** 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>
+
+#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_REQUIRE_CONFIG(openssl);
+QT_REQUIRE_CONFIG(dtls);
+
+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
diff --git a/src/network/ssl/qdtls_p.h b/src/network/ssl/qdtls_p.h
new file mode 100644
index 0000000000..bdc001502b
--- /dev/null
+++ b/src/network/ssl/qdtls_p.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** 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_REQUIRE_CONFIG(dtls);
+
+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);
+
+ static bool isDtlsProtocol(QSsl::SslProtocol protocol);
+
+ 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
diff --git a/src/network/ssl/qpassworddigestor.cpp b/src/network/ssl/qpassworddigestor.cpp
new file mode 100644
index 0000000000..127d94e849
--- /dev/null
+++ b/src/network/ssl/qpassworddigestor.cpp
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore 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 "qpassworddigestor.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QMessageAuthenticationCode>
+#include <QtCore/QtEndian>
+
+#include <limits>
+
+QT_BEGIN_NAMESPACE
+namespace QPasswordDigestor {
+
+/*!
+ \namespace QPasswordDigestor
+ \inmodule QtNetwork
+
+ \brief The QPasswordDigestor namespace contains functions which you can use
+ to generate hashes or keys.
+*/
+
+/*!
+ \since 5.12
+
+ Returns a hash computed using the PBKDF1-algorithm as defined in
+ \l {https://tools.ietf.org/html/rfc8018#section-5.1} {RFC 8018}.
+
+ The function takes the \a data and \a salt, and then hashes it repeatedly
+ for \a iterations iterations using the specified hash \a algorithm. If the
+ resulting hash is longer than \a dkLen then it is truncated before it is
+ returned.
+
+ This function only supports SHA-1 and MD5! The max output size is 160 bits
+ (20 bytes) when using SHA-1, or 128 bits (16 bytes) when using MD5.
+ Specifying a value for \a dkLen which is greater than this results in a
+ warning and an empty QByteArray is returned. To programmatically check this
+ limit you can use \l {QCryptographicHash::hashLength}. Furthermore: the
+ \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}.
+
+ \sa deriveKeyPbkdf2, QCryptographicHash, QCryptographicHash::hashLength
+*/
+Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf1(QCryptographicHash::Algorithm algorithm,
+ const QByteArray &data, const QByteArray &salt,
+ int iterations, quint64 dkLen)
+{
+ // https://tools.ietf.org/html/rfc8018#section-5.1
+
+ if (algorithm != QCryptographicHash::Sha1
+#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
+ && algorithm != QCryptographicHash::Md5
+#endif
+ ) {
+ qWarning("The only supported algorithms for pbkdf1 are SHA-1 and MD5!");
+ return QByteArray();
+ }
+
+ if (salt.size() != 8) {
+ qWarning("The salt must be 8 bytes long!");
+ return QByteArray();
+ }
+ if (iterations < 1 || dkLen < 1)
+ return QByteArray();
+
+ if (dkLen > quint64(QCryptographicHash::hashLength(algorithm))) {
+ qWarning() << "Derived key too long:\n"
+ << algorithm << "was chosen which produces output of length"
+ << QCryptographicHash::hashLength(algorithm) << "but" << dkLen
+ << "was requested.";
+ return QByteArray();
+ }
+
+ QCryptographicHash hash(algorithm);
+ hash.addData(data);
+ hash.addData(salt);
+ QByteArray key = hash.result();
+
+ for (int i = 1; i < iterations; i++) {
+ hash.reset();
+ hash.addData(key);
+ key = hash.result();
+ }
+ return key.left(dkLen);
+}
+
+/*!
+ \since 5.12
+
+ Derive a key using the PBKDF2-algorithm as defined in
+ \l {https://tools.ietf.org/html/rfc8018#section-5.2} {RFC 8018}.
+
+ This function takes the \a data and \a salt, and then applies HMAC-X, where
+ the X is \a algorithm, repeatedly. It internally concatenates intermediate
+ results to the final output until at least \a dkLen amount of bytes have
+ been computed and it will execute HMAC-X \a iterations times each time a
+ concatenation is required. The total number of times it will execute HMAC-X
+ depends on \a iterations, \a dkLen and \a algorithm and can be calculated
+ as
+ \c{iterations * ceil(dkLen / QCryptographicHash::hashLength(algorithm))}.
+
+ \sa deriveKeyPbkdf1, QMessageAuthenticationCode, QCryptographicHash
+*/
+Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf2(QCryptographicHash::Algorithm algorithm,
+ const QByteArray &data, const QByteArray &salt,
+ int iterations, quint64 dkLen)
+{
+ // https://tools.ietf.org/html/rfc8018#section-5.2
+
+ // The RFC recommends checking that 'dkLen' is not greater than '(2^32 - 1) * hLen'
+ int hashLen = QCryptographicHash::hashLength(algorithm);
+ const quint64 maxLen = quint64(std::numeric_limits<quint32>::max() - 1) * hashLen;
+ if (dkLen > maxLen) {
+ qWarning().nospace() << "Derived key too long:\n"
+ << algorithm << " was chosen which produces output of length "
+ << maxLen << " but " << dkLen << " was requested.";
+ return QByteArray();
+ }
+
+ if (iterations < 1 || dkLen < 1)
+ return QByteArray();
+
+ QByteArray key;
+ quint32 currentIteration = 1;
+ QMessageAuthenticationCode hmac(algorithm, data);
+ QByteArray index(4, Qt::Uninitialized);
+ while (quint64(key.length()) < dkLen) {
+ hmac.addData(salt);
+
+ qToBigEndian(currentIteration, index.data());
+ hmac.addData(index);
+
+ QByteArray u = hmac.result();
+ hmac.reset();
+ QByteArray tkey = u;
+ for (int iter = 1; iter < iterations; iter++) {
+ hmac.addData(u);
+ u = hmac.result();
+ hmac.reset();
+ std::transform(tkey.cbegin(), tkey.cend(), u.cbegin(), tkey.begin(),
+ std::bit_xor<char>());
+ }
+ key += tkey;
+ currentIteration++;
+ }
+ return key.left(dkLen);
+}
+} // namespace QPasswordDigestor
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qpassworddigestor.h b/src/network/ssl/qpassworddigestor.h
new file mode 100644
index 0000000000..0f88643298
--- /dev/null
+++ b/src/network/ssl/qpassworddigestor.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore 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 QPASSWORDDIGESTOR_H
+#define QPASSWORDDIGESTOR_H
+
+#include <QtNetwork/qtnetworkglobal.h>
+#include <QtCore/QByteArray>
+#include <QtCore/QCryptographicHash>
+
+QT_BEGIN_NAMESPACE
+
+namespace QPasswordDigestor {
+Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf1(QCryptographicHash::Algorithm algorithm,
+ const QByteArray &password, const QByteArray &salt,
+ int iterations, quint64 dkLen);
+Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf2(QCryptographicHash::Algorithm algorithm,
+ const QByteArray &password, const QByteArray &salt,
+ int iterations, quint64 dkLen);
+} // namespace QPasswordDigestor
+
+QT_END_NAMESPACE
+
+#endif // QPASSWORDDIGESTOR_H
diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp
index 51779dec33..3a0983e8b5 100644
--- a/src/network/ssl/qssl.cpp
+++ b/src/network/ssl/qssl.cpp
@@ -125,6 +125,10 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl");
\value TlsV1_1OrLater TLSv1.1 and later versions. This option is not available when using the WinRT backend due to platform limitations.
\value TlsV1_2 TLSv1.2. When using the WinRT backend this option will also enable TLSv1.0 and TLSv1.1.
\value TlsV1_2OrLater TLSv1.2 and later versions. This option is not available when using the WinRT backend due to platform limitations.
+ \value DtlsV1_0 DTLSv1.0
+ \value DtlsV1_0OrLater DTLSv1.0 and later versions.
+ \value DtlsV1_2 DTLSv1.2
+ \value DtlsV1_2OrLater DTLSv1.2 and later versions.
\value UnknownProtocol The cipher's protocol cannot be determined.
\value AnyProtocol The socket understands SSLv2, SSLv3, TLSv1.0 and all
supported later versions of TLS. This value is used by QSslSocket only.
diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h
index c2a468c97c..dd268cd86d 100644
--- a/src/network/ssl/qssl.h
+++ b/src/network/ssl/qssl.h
@@ -91,6 +91,13 @@ namespace QSsl {
TlsV1_1OrLater,
TlsV1_2OrLater,
+#if QT_CONFIG(dtls) || defined(Q_CLANG_QDOC)
+ DtlsV1_0,
+ DtlsV1_0OrLater,
+ DtlsV1_2,
+ DtlsV1_2OrLater,
+#endif
+
UnknownProtocol = -1
};
diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp
index 9ddcb70b1d..d153e0b929 100644
--- a/src/network/ssl/qsslcertificate.cpp
+++ b/src/network/ssl/qsslcertificate.cpp
@@ -125,7 +125,9 @@
#include "qssl_p.h"
#include "qsslcertificate.h"
#include "qsslcertificate_p.h"
+#ifndef QT_NO_SSL
#include "qsslkey_p.h"
+#endif
#include <QtCore/qdir.h>
#include <QtCore/qdiriterator.h>
@@ -142,8 +144,12 @@ QT_BEGIN_NAMESPACE
QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format)
: d(new QSslCertificatePrivate)
{
+#ifndef QT_NO_OPENSSL
QSslSocketPrivate::ensureInitialized();
if (device && QSslSocket::supportsSsl())
+#else
+ if (device)
+#endif
d->init(device->readAll(), format);
}
@@ -156,8 +162,10 @@ QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format)
QSslCertificate::QSslCertificate(const QByteArray &data, QSsl::EncodingFormat format)
: d(new QSslCertificatePrivate)
{
+#ifndef QT_NO_OPENSSL
QSslSocketPrivate::ensureInitialized();
if (QSslSocket::supportsSsl())
+#endif
d->init(data, format);
}
@@ -557,6 +565,8 @@ QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::E
: QSslCertificatePrivate::certificatesFromDer(data);
}
+#ifndef QT_NO_SSL
+
/*!
Verifies a certificate chain. The chain to be verified is passed in the
\a certificateChain parameter. The first certificate in the list should
@@ -600,6 +610,8 @@ bool QSslCertificate::importPkcs12(QIODevice *device,
return QSslSocketBackendPrivate::importPkcs12(device, key, certificate, caCertificates, passPhrase);
}
+#endif
+
// These certificates are known to be fraudulent and were created during the comodo
// compromise. See http://www.comodo.com/Comodo-Fraud-Incident-2011-03-23.html
static const char *const certificate_blacklist[] = {
@@ -647,12 +659,12 @@ static const char *const certificate_blacklist[] = {
"27:83", "NIC Certifying Authority", // intermediate certificate from NIC India (2007)
"27:92", "NIC CA 2011", // intermediate certificate from NIC India (2011)
"27:b1", "NIC CA 2014", // intermediate certificate from NIC India (2014)
- 0
+ nullptr
};
bool QSslCertificatePrivate::isBlacklisted(const QSslCertificate &certificate)
{
- for (int a = 0; certificate_blacklist[a] != 0; a++) {
+ for (int a = 0; certificate_blacklist[a] != nullptr; a++) {
QString blacklistedCommonName = QString::fromUtf8(certificate_blacklist[(a+1)]);
if (certificate.serialNumber() == certificate_blacklist[a++] &&
(certificate.subjectInfo(QSslCertificate::CommonName).contains(blacklistedCommonName) ||
@@ -680,6 +692,56 @@ QByteArray QSslCertificatePrivate::subjectInfoToString(QSslCertificate::SubjectI
}
/*!
+ \since 5.12
+
+ Returns a name that describes the issuer. It returns the QSslCertificate::CommonName
+ if available, otherwise falls back to the first QSslCertificate::Organization or the
+ first QSslCertificate::OrganizationalUnitName.
+
+ \sa issuerInfo()
+*/
+QString QSslCertificate::issuerDisplayName() const
+{
+ QStringList names;
+ names = issuerInfo(QSslCertificate::CommonName);
+ if (!names.isEmpty())
+ return names.first();
+ names = issuerInfo(QSslCertificate::Organization);
+ if (!names.isEmpty())
+ return names.first();
+ names = issuerInfo(QSslCertificate::OrganizationalUnitName);
+ if (!names.isEmpty())
+ return names.first();
+
+ return QString();
+}
+
+/*!
+ \since 5.12
+
+ Returns a name that describes the subject. It returns the QSslCertificate::CommonName
+ if available, otherwise falls back to the first QSslCertificate::Organization or the
+ first QSslCertificate::OrganizationalUnitName.
+
+ \sa subjectInfo()
+*/
+QString QSslCertificate::subjectDisplayName() const
+{
+ QStringList names;
+ names = subjectInfo(QSslCertificate::CommonName);
+ if (!names.isEmpty())
+ return names.first();
+ names = subjectInfo(QSslCertificate::Organization);
+ if (!names.isEmpty())
+ return names.first();
+ names = subjectInfo(QSslCertificate::OrganizationalUnitName);
+ if (!names.isEmpty())
+ return names.first();
+
+ return QString();
+}
+
+/*!
\fn uint qHash(const QSslCertificate &key, uint seed)
Returns the hash value for the \a key, using \a seed to seed the calculation.
@@ -696,8 +758,8 @@ QDebug operator<<(QDebug debug, const QSslCertificate &certificate)
<< certificate.version()
<< ", " << certificate.serialNumber()
<< ", " << certificate.digest().toBase64()
- << ", " << certificate.issuerInfo(QSslCertificate::Organization)
- << ", " << certificate.subjectInfo(QSslCertificate::Organization)
+ << ", " << certificate.issuerDisplayName()
+ << ", " << certificate.subjectDisplayName()
<< ", " << certificate.subjectAlternativeNames()
#if QT_CONFIG(datestring)
<< ", " << certificate.effectiveDate()
diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h
index 6cd66fd20f..266fcdacb4 100644
--- a/src/network/ssl/qsslcertificate.h
+++ b/src/network/ssl/qsslcertificate.h
@@ -55,8 +55,6 @@
#include <QtCore/qmap.h>
#include <QtNetwork/qssl.h>
-#ifndef QT_NO_SSL
-
QT_BEGIN_NAMESPACE
class QDateTime;
@@ -122,6 +120,9 @@ public:
QStringList issuerInfo(const QByteArray &attribute) const;
QStringList subjectInfo(SubjectInfo info) const;
QStringList subjectInfo(const QByteArray &attribute) const;
+ QString issuerDisplayName() const;
+ QString subjectDisplayName() const;
+
QList<QByteArray> subjectInfoAttributes() const;
QList<QByteArray> issuerInfoAttributes() const;
#if QT_DEPRECATED_SINCE(5,0)
@@ -131,7 +132,9 @@ public:
QMultiMap<QSsl::AlternativeNameEntryType, QString> subjectAlternativeNames() const;
QDateTime effectiveDate() const;
QDateTime expiryDate() const;
+#ifndef QT_NO_SSL
QSslKey publicKey() const;
+#endif
QList<QSslCertificateExtension> extensions() const;
QByteArray toPem() const;
@@ -146,6 +149,7 @@ public:
static QList<QSslCertificate> fromData(
const QByteArray &data, QSsl::EncodingFormat format = QSsl::Pem);
+#ifndef QT_NO_SSL
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain, const QString &hostName = QString());
#else
@@ -156,6 +160,7 @@ public:
QSslKey *key, QSslCertificate *cert,
QList<QSslCertificate> *caCertificates = nullptr,
const QByteArray &passPhrase=QByteArray());
+#endif
Qt::HANDLE handle() const;
@@ -178,6 +183,4 @@ QT_END_NAMESPACE
Q_DECLARE_METATYPE(QSslCertificate)
-#endif // QT_NO_SSL
-
#endif
diff --git a/src/network/ssl/qsslcertificate_openssl.cpp b/src/network/ssl/qsslcertificate_openssl.cpp
index fce150d79d..fa87cfeaaf 100644
--- a/src/network/ssl/qsslcertificate_openssl.cpp
+++ b/src/network/ssl/qsslcertificate_openssl.cpp
@@ -44,8 +44,9 @@
#include "qsslkey_p.h"
#include "qsslcertificateextension_p.h"
+#if QT_CONFIG(thread)
#include <QtCore/private/qmutexpool_p.h>
-
+#endif
QT_BEGIN_NAMESPACE
// forward declaration
@@ -90,7 +91,9 @@ bool QSslCertificate::isSelfSigned() const
QByteArray QSslCertificate::version() const
{
+#if QT_CONFIG(thread)
QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
+#endif
if (d->versionString.isEmpty() && d->x509)
d->versionString = QByteArray::number(qlonglong(q_X509_get_version(d->x509)) + 1);
@@ -99,7 +102,9 @@ QByteArray QSslCertificate::version() const
QByteArray QSslCertificate::serialNumber() const
{
+#if QT_CONFIG(thread)
QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
+#endif
if (d->serialNumberString.isEmpty() && d->x509) {
ASN1_INTEGER *serialNumber = q_X509_get_serialNumber(d->x509);
QByteArray hexString;
@@ -116,7 +121,9 @@ QByteArray QSslCertificate::serialNumber() const
QStringList QSslCertificate::issuerInfo(SubjectInfo info) const
{
+#if QT_CONFIG(thread)
QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
+#endif
// lazy init
if (d->issuerInfo.isEmpty() && d->x509)
d->issuerInfo =
@@ -127,7 +134,9 @@ QStringList QSslCertificate::issuerInfo(SubjectInfo info) const
QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const
{
+#if QT_CONFIG(thread)
QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
+#endif
// lazy init
if (d->issuerInfo.isEmpty() && d->x509)
d->issuerInfo =
@@ -138,7 +147,9 @@ QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const
QStringList QSslCertificate::subjectInfo(SubjectInfo info) const
{
+#if QT_CONFIG(thread)
QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
+#endif
// lazy init
if (d->subjectInfo.isEmpty() && d->x509)
d->subjectInfo =
@@ -149,7 +160,9 @@ QStringList QSslCertificate::subjectInfo(SubjectInfo info) const
QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const
{
+#if QT_CONFIG(thread)
QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
+#endif
// lazy init
if (d->subjectInfo.isEmpty() && d->x509)
d->subjectInfo =
@@ -160,7 +173,9 @@ QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const
QList<QByteArray> QSslCertificate::subjectInfoAttributes() const
{
+#if QT_CONFIG(thread)
QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
+#endif
// lazy init
if (d->subjectInfo.isEmpty() && d->x509)
d->subjectInfo =
@@ -171,7 +186,9 @@ QList<QByteArray> QSslCertificate::subjectInfoAttributes() const
QList<QByteArray> QSslCertificate::issuerInfoAttributes() const
{
+#if QT_CONFIG(thread)
QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
+#endif
// lazy init
if (d->issuerInfo.isEmpty() && d->x509)
d->issuerInfo =
@@ -187,7 +204,8 @@ QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlter
if (!d->x509)
return result;
- STACK_OF(GENERAL_NAME) *altNames = (STACK_OF(GENERAL_NAME)*)q_X509_get_ext_d2i(d->x509, NID_subject_alt_name, 0, 0);
+ STACK_OF(GENERAL_NAME) *altNames = (STACK_OF(GENERAL_NAME) *)q_X509_get_ext_d2i(
+ d->x509, NID_subject_alt_name, nullptr, nullptr);
if (altNames) {
for (int i = 0; i < q_sk_GENERAL_NAME_num(altNames); ++i) {
@@ -289,7 +307,7 @@ static QVariant x509UnknownExtensionToValue(X509_EXTENSION *ext)
// If this extension can be converted
if (meth->i2v && ext_internal) {
- STACK_OF(CONF_VALUE) *val = meth->i2v(meth, ext_internal, 0);
+ STACK_OF(CONF_VALUE) *val = meth->i2v(meth, ext_internal, nullptr);
QVariantMap map;
QVariantList list;
@@ -527,7 +545,7 @@ QByteArray QSslCertificatePrivate::QByteArray_from_X509(X509 *x509, QSsl::Encodi
}
// Use i2d_X509 to convert the X509 to an array.
- int length = q_i2d_X509(x509, 0);
+ int length = q_i2d_X509(x509, nullptr);
QByteArray array;
array.resize(length);
char *data = array.data();
@@ -604,11 +622,11 @@ static QMap<QByteArray, QString> _q_mapFromX509Name(X509_NAME *name)
X509_NAME_ENTRY *e = q_X509_NAME_get_entry(name, i);
QByteArray name = QSslCertificatePrivate::asn1ObjectName(q_X509_NAME_ENTRY_get_object(e));
- unsigned char *data = 0;
+ unsigned char *data = nullptr;
int size = q_ASN1_STRING_to_UTF8(&data, q_X509_NAME_ENTRY_get_data(e));
info.insertMulti(name, QString::fromUtf8((char*)data, size));
#if QT_CONFIG(opensslv11)
- q_CRYPTO_free(data, 0, 0);
+ q_CRYPTO_free(data, nullptr, 0);
#else
q_CRYPTO_free(data);
#endif
@@ -679,7 +697,7 @@ QList<QSslCertificate> QSslCertificatePrivate::certificatesFromPem(const QByteAr
QByteArray::fromRawData(pem.data() + startPos, endPos - startPos));
const unsigned char *data = (const unsigned char *)decoded.data();
- if (X509 *x509 = q_d2i_X509(0, &data, decoded.size())) {
+ if (X509 *x509 = q_d2i_X509(nullptr, &data, decoded.size())) {
certificates << QSslCertificate_from_X509(x509);
q_X509_free(x509);
}
@@ -697,7 +715,7 @@ QList<QSslCertificate> QSslCertificatePrivate::certificatesFromDer(const QByteAr
int size = der.size();
while (size > 0 && (count == -1 || certificates.size() < count)) {
- if (X509 *x509 = q_d2i_X509(0, &data, size)) {
+ if (X509 *x509 = q_d2i_X509(nullptr, &data, size)) {
certificates << QSslCertificate_from_X509(x509);
q_X509_free(x509);
} else {
diff --git a/src/network/ssl/qsslcertificate_p.h b/src/network/ssl/qsslcertificate_p.h
index 0397845f8d..dfdceab502 100644
--- a/src/network/ssl/qsslcertificate_p.h
+++ b/src/network/ssl/qsslcertificate_p.h
@@ -55,7 +55,9 @@
// We mean it.
//
+#ifndef QT_NO_SSL
#include "qsslsocket_p.h"
+#endif
#include "qsslcertificateextension.h"
#include <QtCore/qdatetime.h>
#include <QtCore/qmap.h>
@@ -83,7 +85,9 @@ public:
QSslCertificatePrivate()
: null(true), x509(0)
{
+#ifndef QT_NO_SSL
QSslSocketPrivate::ensureInitialized();
+#endif
}
~QSslCertificatePrivate()
diff --git a/src/network/ssl/qsslcertificate_qt.cpp b/src/network/ssl/qsslcertificate_qt.cpp
index 1cc2b1f964..dfdfd529e5 100644
--- a/src/network/ssl/qsslcertificate_qt.cpp
+++ b/src/network/ssl/qsslcertificate_qt.cpp
@@ -41,8 +41,10 @@
#include "qsslcertificate_p.h"
#include "qssl_p.h"
+#ifndef QT_NO_SSL
#include "qsslkey.h"
#include "qsslkey_p.h"
+#endif
#include "qsslcertificateextension.h"
#include "qsslcertificateextension_p.h"
#include "qasn1element_p.h"
@@ -141,10 +143,11 @@ QDateTime QSslCertificate::expiryDate() const
Qt::HANDLE QSslCertificate::handle() const
{
Q_UNIMPLEMENTED();
- return 0;
+ return nullptr;
}
#endif
+#ifndef QT_NO_SSL
QSslKey QSslCertificate::publicKey() const
{
QSslKey key;
@@ -155,6 +158,7 @@ QSslKey QSslCertificate::publicKey() const
}
return key;
}
+#endif
QList<QSslCertificateExtension> QSslCertificate::extensions() const
{
diff --git a/src/network/ssl/qsslcertificate_winrt.cpp b/src/network/ssl/qsslcertificate_winrt.cpp
index eb926d217d..e601307c17 100644
--- a/src/network/ssl/qsslcertificate_winrt.cpp
+++ b/src/network/ssl/qsslcertificate_winrt.cpp
@@ -102,10 +102,11 @@ Qt::HANDLE QSslCertificate::handle() const
HRESULT hr;
ComPtr<IBuffer> buffer;
hr = g->bufferFactory->CreateFromByteArray(d->derData.length(), (BYTE *)d->derData.data(), &buffer);
- RETURN_IF_FAILED("Failed to create the certificate data buffer", return 0);
+ RETURN_IF_FAILED("Failed to create the certificate data buffer", return nullptr);
hr = g->certificateFactory->CreateCertificate(buffer.Get(), &d->certificate);
- RETURN_IF_FAILED("Failed to create the certificate handle from the data buffer", return 0);
+ RETURN_IF_FAILED("Failed to create the certificate handle from the data buffer",
+ return nullptr);
}
return d->certificate.Get();
diff --git a/src/network/ssl/qsslcertificateextension.h b/src/network/ssl/qsslcertificateextension.h
index 2ce2112687..c2910e1707 100644
--- a/src/network/ssl/qsslcertificateextension.h
+++ b/src/network/ssl/qsslcertificateextension.h
@@ -48,9 +48,6 @@
QT_BEGIN_NAMESPACE
-
-#ifndef QT_NO_SSL
-
class QSslCertificateExtensionPrivate;
class Q_NETWORK_EXPORT QSslCertificateExtension
@@ -80,8 +77,6 @@ private:
Q_DECLARE_SHARED(QSslCertificateExtension)
-#endif // QT_NO_SSL
-
QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp
index 52ddf8bb88..3f732b4646 100644
--- a/src/network/ssl/qsslconfiguration.cpp
+++ b/src/network/ssl/qsslconfiguration.cpp
@@ -227,7 +227,8 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const
d->sslSessionTicketLifeTimeHint == other.d->sslSessionTicketLifeTimeHint &&
d->nextAllowedProtocols == other.d->nextAllowedProtocols &&
d->nextNegotiatedProtocol == other.d->nextNegotiatedProtocol &&
- d->nextProtocolNegotiationStatus == other.d->nextProtocolNegotiationStatus;
+ d->nextProtocolNegotiationStatus == other.d->nextProtocolNegotiationStatus &&
+ d->dtlsCookieEnabled == other.d->dtlsCookieEnabled;
}
/*!
@@ -1034,6 +1035,65 @@ void QSslConfiguration::setDefaultConfiguration(const QSslConfiguration &configu
QSslConfigurationPrivate::setDefaultConfiguration(configuration);
}
+#if QT_CONFIG(dtls) || defined(Q_CLANG_QDOC)
+
+/*!
+ This function returns true if DTLS cookie verification was enabled on a
+ server-side socket.
+
+ \sa setDtlsCookieVerificationEnabled()
+ */
+bool QSslConfiguration::dtlsCookieVerificationEnabled() const
+{
+ return d->dtlsCookieEnabled;
+}
+
+/*!
+ This function enables DTLS cookie verification when \a enable is true.
+
+ \sa dtlsCookieVerificationEnabled()
+ */
+void QSslConfiguration::setDtlsCookieVerificationEnabled(bool enable)
+{
+ d->dtlsCookieEnabled = enable;
+}
+
+/*!
+ Returns the default DTLS configuration to be used in new DTLS
+ connections.
+
+ The default DTLS configuration consists of:
+
+ \list
+ \li no local certificate and no private key
+ \li protocol DtlsV1_2OrLater
+ \li the system's default CA certificate list
+ \li the cipher list equal to the list of the SSL libraries'
+ supported TLS 1.2 ciphers that use 128 or more secret bits
+ for the cipher.
+ \endlist
+
+ \sa setDefaultDtlsConfiguration()
+*/
+QSslConfiguration QSslConfiguration::defaultDtlsConfiguration()
+{
+ return QSslConfigurationPrivate::defaultDtlsConfiguration();
+}
+
+/*!
+ Sets the default DTLS configuration to be used in new DTLS
+ connections to be \a configuration. Existing connections are not
+ affected by this call.
+
+ \sa defaultDtlsConfiguration()
+*/
+void QSslConfiguration::setDefaultDtlsConfiguration(const QSslConfiguration &configuration)
+{
+ QSslConfigurationPrivate::setDefaultDtlsConfiguration(configuration);
+}
+
+#endif // dtls
+
/*! \internal
*/
bool QSslConfigurationPrivate::peerSessionWasShared(const QSslConfiguration &configuration) {
diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h
index fe4181d755..454ac0cee3 100644
--- a/src/network/ssl/qsslconfiguration.h
+++ b/src/network/ssl/qsslconfiguration.h
@@ -73,6 +73,11 @@ class QSslKey;
class QSslEllipticCurve;
class QSslDiffieHellmanParameters;
+namespace dtlsopenssl
+{
+class DtlsState;
+}
+
class QSslConfigurationPrivate;
class Q_NETWORK_EXPORT QSslConfiguration
{
@@ -157,6 +162,14 @@ public:
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);
+#if QT_CONFIG(dtls) || defined(Q_CLANG_QDOC)
+ bool dtlsCookieVerificationEnabled() const;
+ void setDtlsCookieVerificationEnabled(bool enable);
+
+ static QSslConfiguration defaultDtlsConfiguration();
+ static void setDefaultDtlsConfiguration(const QSslConfiguration &configuration);
+#endif // dtls
+
enum NextProtocolNegotiationStatus {
NextProtocolNegotiationNone,
NextProtocolNegotiationNegotiated,
@@ -182,6 +195,8 @@ private:
friend class QSslConfigurationPrivate;
friend class QSslSocketBackendPrivate;
friend class QSslContext;
+ friend class QDtlsBasePrivate;
+ friend class dtlsopenssl::DtlsState;
QSslConfiguration(QSslConfigurationPrivate *dd);
QSharedDataPointer<QSslConfigurationPrivate> d;
};
diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h
index 38a98239db..6c23165c6a 100644
--- a/src/network/ssl/qsslconfiguration_p.h
+++ b/src/network/ssl/qsslconfiguration_p.h
@@ -137,10 +137,19 @@ public:
QByteArray nextNegotiatedProtocol;
QSslConfiguration::NextProtocolNegotiationStatus nextProtocolNegotiationStatus;
+#if QT_CONFIG(dtls)
+ bool dtlsCookieEnabled = true;
+#else
+ const bool dtlsCookieEnabled = false;
+#endif // dtls
+
// in qsslsocket.cpp:
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);
static void deepCopyDefaultConfiguration(QSslConfigurationPrivate *config);
+
+ static QSslConfiguration defaultDtlsConfiguration();
+ static void setDefaultDtlsConfiguration(const QSslConfiguration &configuration);
};
// implemented here for inlining purposes
diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp
index 41b759364b..35cca9f01a 100644
--- a/src/network/ssl/qsslcontext_openssl.cpp
+++ b/src/network/ssl/qsslcontext_openssl.cpp
@@ -55,9 +55,9 @@ static inline QString msgErrorSettingBackendConfig(const QString &why)
}
QSslContext::QSslContext()
- : ctx(0),
- pkey(0),
- session(0),
+ : ctx(nullptr),
+ pkey(nullptr),
+ session(nullptr),
m_sessionTicketLifeTimeHint(-1)
{
}
@@ -137,7 +137,8 @@ SSL* QSslContext::createSsl()
if (!session && !sessionASN1().isEmpty()
&& !sslConfiguration.testSslOption(QSsl::SslOptionDisableSessionPersistence)) {
const unsigned char *data = reinterpret_cast<const unsigned char *>(m_sessionASN1.constData());
- session = q_d2i_SSL_SESSION(0, &data, m_sessionASN1.size()); // refcount is 1 already, set by function above
+ session = q_d2i_SSL_SESSION(
+ nullptr, &data, m_sessionASN1.size()); // refcount is 1 already, set by function above
}
if (session) {
@@ -145,7 +146,7 @@ SSL* QSslContext::createSsl()
if (!q_SSL_set_session(ssl, session)) {
qCWarning(lcSsl, "could not set SSL session");
q_SSL_SESSION_free(session);
- session = 0;
+ session = nullptr;
}
}
@@ -204,7 +205,7 @@ bool QSslContext::cacheSession(SSL* ssl)
session = q_SSL_get1_session(ssl);
if (session && !sslConfiguration.testSslOption(QSsl::SslOptionDisableSessionPersistence)) {
- int sessionSize = q_i2d_SSL_SESSION(session, 0);
+ int sessionSize = q_i2d_SSL_SESSION(session, nullptr);
if (sessionSize > 0) {
m_sessionASN1.resize(sessionSize);
unsigned char *data = reinterpret_cast<unsigned char *>(m_sessionASN1.data());
@@ -214,7 +215,7 @@ bool QSslContext::cacheSession(SSL* ssl)
}
}
- return (session != 0);
+ return (session != nullptr);
}
QByteArray QSslContext::sessionASN1() const
diff --git a/src/network/ssl/qsslcontext_openssl11.cpp b/src/network/ssl/qsslcontext_openssl11.cpp
index 4fb33cb0a4..708cb7bb0e 100644
--- a/src/network/ssl/qsslcontext_openssl11.cpp
+++ b/src/network/ssl/qsslcontext_openssl11.cpp
@@ -59,11 +59,26 @@ QT_BEGIN_NAMESPACE
extern int q_X509Callback(int ok, X509_STORE_CTX *ctx);
extern QString getErrorsFromOpenSsl();
+#if QT_CONFIG(dtls)
+// 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);
+}
+#endif // dtls
+
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 +89,27 @@ 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()) {
+#if QT_CONFIG(dtls)
+ 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;
+#endif // dtls
+ 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 +128,15 @@ init_context:
return;
}
- long minVersion = TLS_ANY_VERSION;
- long maxVersion = TLS_ANY_VERSION;
+ const long anyVersion =
+#if QT_CONFIG(dtls)
+ isDtls ? DTLS_ANY_VERSION : TLS_ANY_VERSION;
+#else
+ TLS_ANY_VERSION;
+#endif // dtls
+ long minVersion = anyVersion;
+ long maxVersion = anyVersion;
+
switch (sslContext->sslConfiguration.protocol()) {
// The single-protocol versions first:
case QSsl::SslV3:
@@ -139,6 +174,24 @@ init_context:
minVersion = TLS1_2_VERSION;
maxVersion = 0;
break;
+#if QT_CONFIG(dtls)
+ 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:
+ minVersion = DTLS1_2_VERSION;
+ maxVersion = DTLS_MAX_VERSION;
+ break;
+#endif // dtls
case QSsl::SslV2:
// This protocol is not supported by OpenSSL 1.1 and we handle
// it as an error (see the code above).
@@ -148,14 +201,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;
@@ -175,7 +228,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;
@@ -282,8 +336,19 @@ 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,
+#if QT_CONFIG(dtls)
+ isDtls ? dtlscallbacks::q_X509DtlsCallback :
+#endif // dtls
+ q_X509Callback);
+ }
+
+#if QT_CONFIG(dtls)
+ 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);
}
+#endif // dtls
// Set verification depth.
if (sslContext->sslConfiguration.peerVerifyDepth() != 0)
@@ -305,8 +370,9 @@ init_context:
if (!dhparams.isEmpty()) {
const QByteArray &params = dhparams.d->derData;
const char *ptr = params.constData();
- DH *dh = q_d2i_DHparams(NULL, reinterpret_cast<const unsigned char **>(&ptr), params.length());
- if (dh == NULL)
+ DH *dh = q_d2i_DHparams(nullptr, reinterpret_cast<const unsigned char **>(&ptr),
+ params.length());
+ if (dh == nullptr)
qFatal("q_d2i_DHparams failed to convert QSslDiffieHellmanParameters to DER form");
q_SSL_CTX_set_tmp_dh(sslContext->ctx, dh);
q_DH_free(dh);
diff --git a/src/network/ssl/qsslcontext_opensslpre11.cpp b/src/network/ssl/qsslcontext_opensslpre11.cpp
index eea821804f..c8be2ecb31 100644
--- a/src/network/ssl/qsslcontext_opensslpre11.cpp
+++ b/src/network/ssl/qsslcontext_opensslpre11.cpp
@@ -56,11 +56,26 @@ QT_BEGIN_NAMESPACE
extern int q_X509Callback(int ok, X509_STORE_CTX *ctx);
extern QString getErrorsFromOpenSsl();
+#if QT_CONFIG(dtls)
+// 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);
+}
+#endif // dtls
+
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,11 +83,28 @@ 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()) {
+#if QT_CONFIG(dtls)
+ case QSsl::DtlsV1_0:
+ 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:
+ // 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;
+#endif // dtls
case QSsl::SslV2:
#ifndef OPENSSL_NO_SSL2
sslContext->ctx = q_SSL_CTX_new(client ? q_SSLv2_client_method() : q_SSLv2_server_method());
@@ -138,6 +170,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.
@@ -155,6 +193,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);
@@ -170,7 +209,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;
@@ -277,8 +316,19 @@ 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,
+#if QT_CONFIG(dtls)
+ isDtls ? dtlscallbacks::q_X509DtlsCallback :
+#endif // dtls
+ q_X509Callback);
+ }
+
+#if QT_CONFIG(dtls)
+ 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));
}
+#endif // dtls
// Set verification depth.
if (sslContext->sslConfiguration.peerVerifyDepth() != 0)
diff --git a/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp b/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp
index 00e9be91d8..c36482329a 100644
--- a/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp
+++ b/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp
@@ -128,7 +128,7 @@ void QSslDiffieHellmanParametersPrivate::decodeDer(const QByteArray &der)
QSslSocketPrivate::ensureInitialized();
- DH *dh = q_d2i_DHparams(NULL, &data, len);
+ DH *dh = q_d2i_DHparams(nullptr, &data, len);
if (dh) {
if (isSafeDH(dh))
derData = der;
@@ -162,7 +162,7 @@ void QSslDiffieHellmanParametersPrivate::decodePem(const QByteArray &pem)
}
DH *dh = nullptr;
- q_PEM_read_bio_DHparams(bio, &dh, 0, 0);
+ q_PEM_read_bio_DHparams(bio, &dh, nullptr, nullptr);
if (dh) {
if (isSafeDH(dh)) {
diff --git a/src/network/ssl/qsslkey_openssl.cpp b/src/network/ssl/qsslkey_openssl.cpp
index 58df544a0e..9a43e67772 100644
--- a/src/network/ssl/qsslkey_openssl.cpp
+++ b/src/network/ssl/qsslkey_openssl.cpp
@@ -62,24 +62,24 @@ void QSslKeyPrivate::clear(bool deep)
if (algorithm == QSsl::Rsa && rsa) {
if (deep)
q_RSA_free(rsa);
- rsa = 0;
+ rsa = nullptr;
}
if (algorithm == QSsl::Dsa && dsa) {
if (deep)
q_DSA_free(dsa);
- dsa = 0;
+ dsa = nullptr;
}
#ifndef OPENSSL_NO_EC
if (algorithm == QSsl::Ec && ec) {
if (deep)
q_EC_KEY_free(ec);
- ec = 0;
+ ec = nullptr;
}
#endif
if (algorithm == QSsl::Opaque && opaque) {
if (deep)
q_EVP_PKEY_free(opaque);
- opaque = 0;
+ opaque = nullptr;
}
}
@@ -125,10 +125,10 @@ bool QSslKeyPrivate::fromEVP_PKEY(EVP_PKEY *pkey)
return false;
}
-void QSslKeyPrivate::decodeDer(const QByteArray &der, bool deepClear)
+void QSslKeyPrivate::decodeDer(const QByteArray &der, const QByteArray &passPhrase, bool deepClear)
{
QMap<QByteArray, QByteArray> headers;
- decodePem(pemFromDer(der, headers), QByteArray(), deepClear);
+ decodePem(pemFromDer(der, headers), passPhrase, deepClear);
}
void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
@@ -150,21 +150,21 @@ void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhra
if (algorithm == QSsl::Rsa) {
RSA *result = (type == QSsl::PublicKey)
- ? q_PEM_read_bio_RSA_PUBKEY(bio, &rsa, 0, phrase)
- : q_PEM_read_bio_RSAPrivateKey(bio, &rsa, 0, phrase);
+ ? q_PEM_read_bio_RSA_PUBKEY(bio, &rsa, nullptr, phrase)
+ : q_PEM_read_bio_RSAPrivateKey(bio, &rsa, nullptr, phrase);
if (rsa && rsa == result)
isNull = false;
} else if (algorithm == QSsl::Dsa) {
DSA *result = (type == QSsl::PublicKey)
- ? q_PEM_read_bio_DSA_PUBKEY(bio, &dsa, 0, phrase)
- : q_PEM_read_bio_DSAPrivateKey(bio, &dsa, 0, phrase);
+ ? q_PEM_read_bio_DSA_PUBKEY(bio, &dsa, nullptr, phrase)
+ : q_PEM_read_bio_DSAPrivateKey(bio, &dsa, nullptr, phrase);
if (dsa && dsa == result)
isNull = false;
#ifndef OPENSSL_NO_EC
} else if (algorithm == QSsl::Ec) {
EC_KEY *result = (type == QSsl::PublicKey)
- ? q_PEM_read_bio_EC_PUBKEY(bio, &ec, 0, phrase)
- : q_PEM_read_bio_ECPrivateKey(bio, &ec, 0, phrase);
+ ? q_PEM_read_bio_EC_PUBKEY(bio, &ec, nullptr, phrase)
+ : q_PEM_read_bio_ECPrivateKey(bio, &ec, nullptr, phrase);
if (ec && ec == result)
isNull = false;
#endif
@@ -215,8 +215,8 @@ QByteArray QSslKeyPrivate::toPem(const QByteArray &passPhrase) const
fail = true;
} else {
if (!q_PEM_write_bio_RSAPrivateKey(
- bio, rsa, cipher,
- const_cast<uchar *>((const uchar *)passPhrase.data()), passPhrase.size(), 0, 0)) {
+ bio, rsa, cipher, const_cast<uchar *>((const uchar *)passPhrase.data()),
+ passPhrase.size(), nullptr, nullptr)) {
fail = true;
}
}
@@ -226,8 +226,8 @@ QByteArray QSslKeyPrivate::toPem(const QByteArray &passPhrase) const
fail = true;
} else {
if (!q_PEM_write_bio_DSAPrivateKey(
- bio, dsa, cipher,
- const_cast<uchar *>((const uchar *)passPhrase.data()), passPhrase.size(), 0, 0)) {
+ bio, dsa, cipher, const_cast<uchar *>((const uchar *)passPhrase.data()),
+ passPhrase.size(), nullptr, nullptr)) {
fail = true;
}
}
@@ -237,9 +237,9 @@ QByteArray QSslKeyPrivate::toPem(const QByteArray &passPhrase) const
if (!q_PEM_write_bio_EC_PUBKEY(bio, ec))
fail = true;
} else {
- if (!q_PEM_write_bio_ECPrivateKey(
- bio, ec, cipher,
- const_cast<uchar *>((const uchar *)passPhrase.data()), passPhrase.size(), 0, 0)) {
+ if (!q_PEM_write_bio_ECPrivateKey(bio, ec, cipher,
+ const_cast<uchar *>((const uchar *)passPhrase.data()),
+ passPhrase.size(), nullptr, nullptr)) {
fail = true;
}
}
@@ -272,13 +272,13 @@ Qt::HANDLE QSslKeyPrivate::handle() const
return Qt::HANDLE(ec);
#endif
default:
- return Qt::HANDLE(NULL);
+ return Qt::HANDLE(nullptr);
}
}
static QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv, int enc)
{
- const EVP_CIPHER* type = 0;
+ const EVP_CIPHER* type = nullptr;
int i = 0, len = 0;
switch (cipher) {
@@ -314,10 +314,10 @@ static QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data,
q_EVP_CIPHER_CTX_init(ctx);
#endif
- q_EVP_CipherInit(ctx, type, NULL, NULL, enc);
+ q_EVP_CipherInit(ctx, type, nullptr, nullptr, enc);
q_EVP_CIPHER_CTX_set_key_length(ctx, key.size());
if (cipher == QSslKeyPrivate::Rc2Cbc)
- q_EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, 8 * key.size(), NULL);
+ q_EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, 8 * key.size(), nullptr);
#if QT_CONFIG(opensslv11)
// EVP_CipherInit in 1.1 resets the context thus making the calls above useless.
diff --git a/src/network/ssl/qsslkey_p.cpp b/src/network/ssl/qsslkey_p.cpp
index e66ec953a0..28e3e2efd8 100644
--- a/src/network/ssl/qsslkey_p.cpp
+++ b/src/network/ssl/qsslkey_p.cpp
@@ -61,6 +61,7 @@
#endif
#include "qsslsocket.h"
#include "qsslsocket_p.h"
+#include "qasn1element_p.h"
#include <QtCore/qatomic.h>
#include <QtCore/qbytearray.h>
@@ -120,6 +121,13 @@ QByteArray QSslKeyPrivate::pemHeader() const
return QByteArray();
}
+static QByteArray pkcs8Header(bool encrypted)
+{
+ return encrypted
+ ? QByteArrayLiteral("-----BEGIN ENCRYPTED PRIVATE KEY-----")
+ : QByteArrayLiteral("-----BEGIN PRIVATE KEY-----");
+}
+
/*!
\internal
*/
@@ -138,6 +146,13 @@ QByteArray QSslKeyPrivate::pemFooter() const
return QByteArray();
}
+static QByteArray pkcs8Footer(bool encrypted)
+{
+ return encrypted
+ ? QByteArrayLiteral("-----END ENCRYPTED PRIVATE KEY-----")
+ : QByteArrayLiteral("-----END PRIVATE KEY-----");
+}
+
/*!
\internal
@@ -166,8 +181,19 @@ QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der, const QMap<QByteArr
} while (it != headers.constBegin());
extra += '\n';
}
- pem.prepend(pemHeader() + '\n' + extra);
- pem.append(pemFooter() + '\n');
+
+ if (isEncryptedPkcs8(der)) {
+ pem.prepend(pkcs8Header(true) + '\n' + extra);
+ pem.append(pkcs8Footer(true) + '\n');
+#if !QT_CONFIG(openssl)
+ } else if (isPkcs8) {
+ pem.prepend(pkcs8Header(false) + '\n' + extra);
+ pem.append(pkcs8Footer(false) + '\n');
+#endif
+ } else {
+ pem.prepend(pemHeader() + '\n' + extra);
+ pem.append(pemFooter() + '\n');
+ }
return pem;
}
@@ -179,13 +205,27 @@ QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der, const QMap<QByteArr
*/
QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const
{
- const QByteArray header = pemHeader();
- const QByteArray footer = pemFooter();
+ QByteArray header = pemHeader();
+ QByteArray footer = pemFooter();
QByteArray der(pem);
- const int headerIndex = der.indexOf(header);
- const int footerIndex = der.indexOf(footer);
+ int headerIndex = der.indexOf(header);
+ int footerIndex = der.indexOf(footer, headerIndex + header.length());
+ if (type != QSsl::PublicKey) {
+ if (headerIndex == -1 || footerIndex == -1) {
+ header = pkcs8Header(true);
+ footer = pkcs8Footer(true);
+ headerIndex = der.indexOf(header);
+ footerIndex = der.indexOf(footer, headerIndex + header.length());
+ }
+ if (headerIndex == -1 || footerIndex == -1) {
+ header = pkcs8Header(false);
+ footer = pkcs8Footer(false);
+ headerIndex = der.indexOf(header);
+ footerIndex = der.indexOf(footer, headerIndex + header.length());
+ }
+ }
if (headerIndex == -1 || footerIndex == -1)
return QByteArray();
@@ -225,13 +265,47 @@ QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem, QMap<QByteArray, QB
return QByteArray::fromBase64(der); // ignores newlines
}
+bool QSslKeyPrivate::isEncryptedPkcs8(const QByteArray &der) const
+{
+ static const QVector<QByteArray> pbes1OIds {
+ // PKCS5
+ {PKCS5_MD2_DES_CBC_OID},
+ {PKCS5_MD2_RC2_CBC_OID},
+ {PKCS5_MD5_DES_CBC_OID},
+ {PKCS5_MD5_RC2_CBC_OID},
+ {PKCS5_SHA1_DES_CBC_OID},
+ {PKCS5_SHA1_RC2_CBC_OID},
+ };
+ QAsn1Element elem;
+ if (!elem.read(der) || elem.type() != QAsn1Element::SequenceType)
+ return false;
+
+ const QVector<QAsn1Element> items = elem.toVector();
+ if (items.size() != 2
+ || items[0].type() != QAsn1Element::SequenceType
+ || items[1].type() != QAsn1Element::OctetStringType) {
+ return false;
+ }
+
+ const QVector<QAsn1Element> encryptionSchemeContainer = items[0].toVector();
+ if (encryptionSchemeContainer.size() != 2
+ || encryptionSchemeContainer[0].type() != QAsn1Element::ObjectIdentifierType
+ || encryptionSchemeContainer[1].type() != QAsn1Element::SequenceType) {
+ return false;
+ }
+
+ const QByteArray encryptionScheme = encryptionSchemeContainer[0].toObjectId();
+ return encryptionScheme == PKCS5_PBES2_ENCRYPTION_OID
+ || pbes1OIds.contains(encryptionScheme)
+ || encryptionScheme.startsWith(PKCS12_OID);
+}
+
/*!
Constructs a QSslKey by decoding the string in the byte array
\a encoded using a specified \a algorithm and \a encoding format.
\a type specifies whether the key is public or private.
- If the key is encoded as PEM and encrypted, \a passPhrase is used
- to decrypt it.
+ If the key is encrypted then \a passPhrase is used to decrypt it.
After construction, use isNull() to check if \a encoded contained
a valid key.
@@ -243,7 +317,7 @@ QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm,
d->type = type;
d->algorithm = algorithm;
if (encoding == QSsl::Der)
- d->decodeDer(encoded);
+ d->decodeDer(encoded, passPhrase);
else
d->decodePem(encoded, passPhrase);
}
@@ -253,8 +327,7 @@ QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm,
\a device using a specified \a algorithm and \a encoding format.
\a type specifies whether the key is public or private.
- If the key is encoded as PEM and encrypted, \a passPhrase is used
- to decrypt it.
+ If the key is encrypted then \a passPhrase is used to decrypt it.
After construction, use isNull() to check if \a device provided
a valid key.
@@ -269,7 +342,7 @@ QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::Encoding
d->type = type;
d->algorithm = algorithm;
if (encoding == QSsl::Der)
- d->decodeDer(encoded);
+ d->decodeDer(encoded, passPhrase);
else
d->decodePem(encoded, passPhrase);
}
diff --git a/src/network/ssl/qsslkey_p.h b/src/network/ssl/qsslkey_p.h
index c93941c198..7ae2cc740b 100644
--- a/src/network/ssl/qsslkey_p.h
+++ b/src/network/ssl/qsslkey_p.h
@@ -81,9 +81,8 @@ public:
#ifndef QT_NO_OPENSSL
bool fromEVP_PKEY(EVP_PKEY *pkey);
#endif
- void decodeDer(const QByteArray &der, bool deepClear = true);
- void decodePem(const QByteArray &pem, const QByteArray &passPhrase,
- bool deepClear = true);
+ void decodeDer(const QByteArray &der, const QByteArray &passPhrase = {}, bool deepClear = true);
+ void decodePem(const QByteArray &pem, const QByteArray &passPhrase, bool deepClear = true);
QByteArray pemHeader() const;
QByteArray pemFooter() const;
QByteArray pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const;
@@ -93,6 +92,12 @@ public:
QByteArray toPem(const QByteArray &passPhrase) const;
Qt::HANDLE handle() const;
+ bool isEncryptedPkcs8(const QByteArray &der) const;
+#if !QT_CONFIG(openssl)
+ QByteArray decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase);
+ bool isPkcs8 = false;
+#endif
+
bool isNull;
QSsl::KeyType type;
QSsl::KeyAlgorithm algorithm;
diff --git a/src/network/ssl/qsslkey_qt.cpp b/src/network/ssl/qsslkey_qt.cpp
index a85fed21ed..a13275f3bb 100644
--- a/src/network/ssl/qsslkey_qt.cpp
+++ b/src/network/ssl/qsslkey_qt.cpp
@@ -43,8 +43,11 @@
#include <QtCore/qdatastream.h>
#include <QtCore/qcryptographichash.h>
+#include <QtCore/QMessageAuthenticationCode>
#include <QtCore/qrandom.h>
+#include <QtNetwork/qpassworddigestor.h>
+
QT_USE_NAMESPACE
static const quint8 bits_table[256] = {
@@ -154,15 +157,86 @@ void QSslKeyPrivate::clear(bool deep)
keyLength = -1;
}
-void QSslKeyPrivate::decodeDer(const QByteArray &der, bool deepClear)
+static int extractPkcs8KeyLength(const QVector<QAsn1Element> &items, QSslKeyPrivate *that) {
+ Q_ASSERT(items.size() == 3);
+ int keyLength;
+
+ auto getName = [](QSsl::KeyAlgorithm algorithm) {
+ switch (algorithm){
+ case QSsl::Rsa: return "RSA";
+ case QSsl::Dsa: return "DSA";
+ case QSsl::Ec: return "EC";
+ case QSsl::Opaque: return "Opaque";
+ }
+ Q_UNREACHABLE();
+ };
+
+ const QVector<QAsn1Element> pkcs8Info = items[1].toVector();
+ if (pkcs8Info.size() != 2 || pkcs8Info[0].type() != QAsn1Element::ObjectIdentifierType)
+ return -1;
+ const QByteArray value = pkcs8Info[0].toObjectId();
+ if (value == RSA_ENCRYPTION_OID) {
+ if (Q_UNLIKELY(that->algorithm != QSsl::Rsa)) {
+ // We could change the 'algorithm' of QSslKey here and continue loading, but
+ // this is not supported in the openssl back-end, so we'll fail here and give
+ // the user some feedback.
+ qWarning() << "QSslKey: Found RSA key when asked to use" << getName(that->algorithm)
+ << "\nLoading will fail.";
+ return -1;
+ }
+ // Luckily it contains the 'normal' RSA-key format inside, so we can just recurse
+ // and read the key's info.
+ that->decodeDer(items[2].value());
+ // The real info has been filled out in the call above, so return as if it was invalid
+ // to avoid overwriting the data.
+ return -1;
+ } else if (value == EC_ENCRYPTION_OID) {
+ if (Q_UNLIKELY(that->algorithm != QSsl::Ec)) {
+ // As above for RSA.
+ qWarning() << "QSslKey: Found EC key when asked to use" << getName(that->algorithm)
+ << "\nLoading will fail.";
+ return -1;
+ }
+ // I don't know where this is documented, but the elliptic-curve identifier has been
+ // moved into the "pkcs#8 wrapper", which is what we're interested in.
+ if (pkcs8Info[1].type() != QAsn1Element::ObjectIdentifierType)
+ return -1;
+ keyLength = curveBits(pkcs8Info[1].toObjectId());
+ } else if (value == DSA_ENCRYPTION_OID) {
+ if (Q_UNLIKELY(that->algorithm != QSsl::Dsa)) {
+ // As above for RSA.
+ qWarning() << "QSslKey: Found DSA when asked to use" << getName(that->algorithm)
+ << "\nLoading will fail.";
+ return -1;
+ }
+ // DSA's structure is documented here:
+ // https://www.cryptsoft.com/pkcs11doc/STANDARD/v201-95.pdf in section 11.9.
+ if (pkcs8Info[1].type() != QAsn1Element::SequenceType)
+ return -1;
+ const QVector<QAsn1Element> dsaInfo = pkcs8Info[1].toVector();
+ if (dsaInfo.size() != 3 || dsaInfo[0].type() != QAsn1Element::IntegerType)
+ return -1;
+ keyLength = numberOfBits(dsaInfo[0].value());
+ } else {
+ // in case of unexpected formats:
+ qWarning() << "QSslKey: Unsupported PKCS#8 key algorithm:" << value
+ << "\nFile a bugreport to Qt (include the line above).";
+ return -1;
+ }
+ return keyLength;
+}
+
+void QSslKeyPrivate::decodeDer(const QByteArray &der, const QByteArray &passPhrase, bool deepClear)
{
clear(deepClear);
if (der.isEmpty())
return;
+ // decryptPkcs8 decrypts if necessary or returns 'der' unaltered
+ QByteArray decryptedDer = decryptPkcs8(der, passPhrase);
QAsn1Element elem;
- if (!elem.read(der) || elem.type() != QAsn1Element::SequenceType)
+ if (!elem.read(decryptedDer) || elem.type() != QAsn1Element::SequenceType)
return;
if (type == QSsl::PublicKey) {
@@ -212,7 +286,16 @@ void QSslKeyPrivate::decodeDer(const QByteArray &der, bool deepClear)
return;
const QByteArray versionHex = items[0].value().toHex();
- if (algorithm == QSsl::Rsa) {
+ if (items.size() == 3 && items[1].type() == QAsn1Element::SequenceType
+ && items[2].type() == QAsn1Element::OctetStringType) {
+ if (versionHex != "00" && versionHex != "01")
+ return;
+ int pkcs8KeyLength = extractPkcs8KeyLength(items, this);
+ if (pkcs8KeyLength == -1)
+ return;
+ isPkcs8 = true;
+ keyLength = pkcs8KeyLength;
+ } else if (algorithm == QSsl::Rsa) {
if (versionHex != "00")
return;
if (items.size() != 9 || items[1].type() != QAsn1Element::IntegerType)
@@ -240,7 +323,7 @@ void QSslKeyPrivate::decodeDer(const QByteArray &der, bool deepClear)
}
}
- derData = der;
+ derData = decryptedDer;
isNull = false;
}
@@ -272,7 +355,7 @@ void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhra
const QByteArray key = deriveKey(cipher, passPhrase, iv);
data = decrypt(cipher, data, key, iv);
}
- decodeDer(data, deepClear);
+ decodeDer(data, passPhrase, deepClear);
}
int QSslKeyPrivate::length() const
@@ -307,3 +390,320 @@ Qt::HANDLE QSslKeyPrivate::handle() const
{
return opaque;
}
+
+// Maps OIDs to the encryption cipher they specify
+static const QMap<QByteArray, QSslKeyPrivate::Cipher> oidCipherMap {
+ {DES_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::DesCbc},
+ {DES_EDE3_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::DesEde3Cbc},
+ // {PKCS5_MD2_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc}, // No MD2
+ {PKCS5_MD5_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc},
+ {PKCS5_SHA1_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc},
+ // {PKCS5_MD2_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc}, // No MD2
+ {PKCS5_MD5_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc},
+ {PKCS5_SHA1_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc},
+ {RC2_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Rc2Cbc}
+ // {RC5_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Rc5Cbc}, // No RC5
+ // {AES128_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes128}, // no AES
+ // {AES192_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes192},
+ // {AES256_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes256}
+};
+
+struct EncryptionData
+{
+ EncryptionData() : initialized(false)
+ {}
+ EncryptionData(QSslKeyPrivate::Cipher cipher, QByteArray key, QByteArray iv)
+ : initialized(true), cipher(cipher), key(key), iv(iv)
+ {}
+ bool initialized;
+ QSslKeyPrivate::Cipher cipher;
+ QByteArray key;
+ QByteArray iv;
+};
+
+static EncryptionData readPbes2(const QVector<QAsn1Element> &element, const QByteArray &passPhrase)
+{
+ // RFC 8018: https://tools.ietf.org/html/rfc8018#section-6.2
+ /*** Scheme: ***
+ * Sequence (scheme-specific info..)
+ * Sequence (key derivation info)
+ * Object Identifier (Key derivation algorithm (e.g. PBKDF2))
+ * Sequence (salt)
+ * CHOICE (this entry can be either of the types it contains)
+ * Octet string (actual salt)
+ * Object identifier (Anything using this is deferred to a later version of PKCS #5)
+ * Integer (iteration count)
+ * Sequence (encryption algorithm info)
+ * Object identifier (identifier for the algorithm)
+ * Algorithm dependent, is covered in the switch further down
+ */
+
+ static const QMap<QByteArray, QCryptographicHash::Algorithm> pbes2OidHashFunctionMap {
+ // PBES2/PBKDF2
+ {HMAC_WITH_SHA1, QCryptographicHash::Sha1},
+ {HMAC_WITH_SHA224, QCryptographicHash::Sha224},
+ {HMAC_WITH_SHA256, QCryptographicHash::Sha256},
+ {HMAC_WITH_SHA512, QCryptographicHash::Sha512},
+ {HMAC_WITH_SHA512_224, QCryptographicHash::Sha512},
+ {HMAC_WITH_SHA512_256, QCryptographicHash::Sha512},
+ {HMAC_WITH_SHA384, QCryptographicHash::Sha384}
+ };
+
+ // Values from their respective sections here: https://tools.ietf.org/html/rfc8018#appendix-B.2
+ static const QMap<QSslKeyPrivate::Cipher, int> cipherKeyLengthMap {
+ {QSslKeyPrivate::Cipher::DesCbc, 8},
+ {QSslKeyPrivate::Cipher::DesEde3Cbc, 24},
+ // @note: variable key-length (https://tools.ietf.org/html/rfc8018#appendix-B.2.3)
+ {QSslKeyPrivate::Cipher::Rc2Cbc, 4}
+ // @todo: AES(, rc5?)
+ };
+
+ const QVector<QAsn1Element> keyDerivationContainer = element[0].toVector();
+ if (keyDerivationContainer.size() != 2
+ || keyDerivationContainer[0].type() != QAsn1Element::ObjectIdentifierType
+ || keyDerivationContainer[1].type() != QAsn1Element::SequenceType) {
+ return {};
+ }
+
+ const QByteArray keyDerivationAlgorithm = keyDerivationContainer[0].toObjectId();
+ const QVector<QAsn1Element> keyDerivationParams = keyDerivationContainer[1].toVector();
+
+ const QVector<QAsn1Element> encryptionAlgorithmContainer = element[1].toVector();
+ if (encryptionAlgorithmContainer.size() != 2
+ || encryptionAlgorithmContainer[0].type() != QAsn1Element::ObjectIdentifierType) {
+ return {};
+ }
+
+ auto iterator = oidCipherMap.constFind(encryptionAlgorithmContainer[0].toObjectId());
+ if (iterator == oidCipherMap.cend()) {
+ qWarning()
+ << "QSslKey: Unsupported encryption cipher OID:" << encryptionAlgorithmContainer[0].toObjectId()
+ << "\nFile a bugreport to Qt (include the line above).";
+ return {};
+ }
+
+ QSslKeyPrivate::Cipher cipher = *iterator;
+ QByteArray key;
+ QByteArray iv;
+ switch (cipher) {
+ case QSslKeyPrivate::Cipher::DesCbc:
+ case QSslKeyPrivate::Cipher::DesEde3Cbc:
+ // https://tools.ietf.org/html/rfc8018#appendix-B.2.1 (DES-CBC-PAD)
+ // https://tools.ietf.org/html/rfc8018#appendix-B.2.2 (DES-EDE3-CBC-PAD)
+ // @todo https://tools.ietf.org/html/rfc8018#appendix-B.2.5 (AES-CBC-PAD)
+ /*** Scheme: ***
+ * Octet string (IV)
+ */
+ if (encryptionAlgorithmContainer[1].type() != QAsn1Element::OctetStringType)
+ return {};
+
+ // @note: All AES identifiers should be able to use this branch!!
+ iv = encryptionAlgorithmContainer[1].value();
+
+ if (iv.size() != 8) // @note: AES needs 16 bytes
+ return {};
+ break;
+ case QSslKeyPrivate::Cipher::Rc2Cbc: {
+ // https://tools.ietf.org/html/rfc8018#appendix-B.2.3
+ /*** Scheme: ***
+ * Sequence (rc2 parameters)
+ * Integer (rc2 parameter version)
+ * Octet string (IV)
+ */
+ if (encryptionAlgorithmContainer[1].type() != QAsn1Element::SequenceType)
+ return {};
+ const QVector<QAsn1Element> rc2ParametersContainer = encryptionAlgorithmContainer[1].toVector();
+ if ((rc2ParametersContainer.size() != 1 && rc2ParametersContainer.size() != 2)
+ || rc2ParametersContainer.back().type() != QAsn1Element::OctetStringType) {
+ return {};
+ }
+ iv = rc2ParametersContainer.back().value();
+ if (iv.size() != 8)
+ return {};
+ break;
+ } // @todo(?): case (RC5 , AES)
+ }
+
+ if (Q_LIKELY(keyDerivationAlgorithm == PKCS5_PBKDF2_ENCRYPTION_OID)) {
+ // Definition: https://tools.ietf.org/html/rfc8018#appendix-A.2
+ QByteArray salt;
+ if (keyDerivationParams[0].type() == QAsn1Element::OctetStringType) {
+ salt = keyDerivationParams[0].value();
+ } else if (keyDerivationParams[0].type() == QAsn1Element::ObjectIdentifierType) {
+ Q_UNIMPLEMENTED();
+ /* See paragraph from https://tools.ietf.org/html/rfc8018#appendix-A.2
+ which ends with: "such facilities are deferred to a future version of PKCS #5"
+ */
+ return {};
+ } else {
+ return {};
+ }
+
+ // Iterations needed to derive the key
+ int iterationCount = keyDerivationParams[1].toInteger();
+ // Optional integer
+ int keyLength = -1;
+ int vectorPos = 2;
+ if (keyDerivationParams.size() > vectorPos
+ && keyDerivationParams[vectorPos].type() == QAsn1Element::IntegerType) {
+ keyLength = keyDerivationParams[vectorPos].toInteger(nullptr);
+ ++vectorPos;
+ } else {
+ keyLength = cipherKeyLengthMap[cipher];
+ }
+
+ // Optional algorithm identifier (default: HMAC-SHA-1)
+ QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha1;
+ if (keyDerivationParams.size() > vectorPos
+ && keyDerivationParams[vectorPos].type() == QAsn1Element::SequenceType) {
+ QVector<QAsn1Element> hashAlgorithmContainer = keyDerivationParams[vectorPos].toVector();
+ hashAlgorithm = pbes2OidHashFunctionMap[hashAlgorithmContainer.front().toObjectId()];
+ Q_ASSERT(hashAlgorithmContainer[1].type() == QAsn1Element::NullType);
+ ++vectorPos;
+ }
+ Q_ASSERT(keyDerivationParams.size() == vectorPos);
+
+ key = QPasswordDigestor::deriveKeyPbkdf2(hashAlgorithm, passPhrase, salt, iterationCount, keyLength);
+ } else {
+ qWarning()
+ << "QSslKey: Unsupported key derivation algorithm OID:" << keyDerivationAlgorithm
+ << "\nFile a bugreport to Qt (include the line above).";
+ return {};
+ }
+ return {cipher, key, iv};
+}
+
+// Maps OIDs to the hash function it specifies
+static const QMap<QByteArray, QCryptographicHash::Algorithm> pbes1OidHashFunctionMap {
+#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
+ // PKCS5
+ //{PKCS5_MD2_DES_CBC_OID, QCryptographicHash::Md2}, No MD2
+ //{PKCS5_MD2_RC2_CBC_OID, QCryptographicHash::Md2},
+ {PKCS5_MD5_DES_CBC_OID, QCryptographicHash::Md5},
+ {PKCS5_MD5_RC2_CBC_OID, QCryptographicHash::Md5},
+#endif
+ {PKCS5_SHA1_DES_CBC_OID, QCryptographicHash::Sha1},
+ {PKCS5_SHA1_RC2_CBC_OID, QCryptographicHash::Sha1},
+ // PKCS12 (unimplemented)
+ // {PKCS12_SHA1_RC4_128_OID, QCryptographicHash::Sha1}, // No RC4
+ // {PKCS12_SHA1_RC4_40_OID, QCryptographicHash::Sha1},
+ // @todo: lacking support. @note: there might be code to do this inside qsslsocket_mac...
+ // further note that more work may be required for the 3DES variations listed to be available.
+ // {PKCS12_SHA1_3KEY_3DES_CBC_OID, QCryptographicHash::Sha1},
+ // {PKCS12_SHA1_2KEY_3DES_CBC_OID, QCryptographicHash::Sha1},
+ // {PKCS12_SHA1_RC2_128_CBC_OID, QCryptographicHash::Sha1},
+ // {PKCS12_SHA1_RC2_40_CBC_OID, QCryptographicHash::Sha1}
+};
+
+
+static EncryptionData readPbes1(const QVector<QAsn1Element> &element, const QByteArray &encryptionScheme, const QByteArray &passPhrase)
+{
+ // RFC 8018: https://tools.ietf.org/html/rfc8018#section-6.1
+ // Steps refer to this section: https://tools.ietf.org/html/rfc8018#section-6.1.2
+ /*** Scheme: ***
+ * Sequence (PBE Parameter)
+ * Octet string (salt)
+ * Integer (iteration counter)
+ */
+ // Step 1
+ if (element.size() != 2
+ || element[0].type() != QAsn1Element::ElementType::OctetStringType
+ || element[1].type() != QAsn1Element::ElementType::IntegerType) {
+ return {};
+ }
+ QByteArray salt = element[0].value();
+ if (salt.size() != 8)
+ return {};
+
+ int iterationCount = element[1].toInteger();
+ if (iterationCount < 0)
+ return {};
+
+ // Step 2
+ auto iterator = pbes1OidHashFunctionMap.constFind(encryptionScheme);
+ if (iterator == pbes1OidHashFunctionMap.cend()) {
+ // Qt was compiled with ONLY_SHA1 (or it's MD2)
+ return {};
+ }
+ QCryptographicHash::Algorithm hashAlgorithm = *iterator;
+ QByteArray key = QPasswordDigestor::deriveKeyPbkdf1(hashAlgorithm, passPhrase, salt, iterationCount, 16);
+ if (key.size() != 16)
+ return {};
+
+ // Step 3
+ QByteArray iv = key.right(8); // last 8 bytes are used as IV
+ key.truncate(8); // first 8 bytes are used for the key
+
+ QSslKeyPrivate::Cipher cipher = oidCipherMap[encryptionScheme];
+#ifdef Q_OS_WINRT
+ // @todo: document this instead? find some other solution?
+ if (cipher == QSslKeyPrivate::Cipher::Rc2Cbc)
+ qWarning("PBES1 with RC2_CBC doesn't work properly on WinRT.");
+#endif
+ // Steps 4-6 are done after returning
+ return {cipher, key, iv};
+}
+
+QByteArray QSslKeyPrivate::decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase)
+{
+ // RFC 5958: https://tools.ietf.org/html/rfc5958
+ /*** Scheme: ***
+ * Sequence
+ * Sequence
+ * Object Identifier (encryption scheme (currently PBES2, PBES1, @todo PKCS12))
+ * Sequence (scheme parameters)
+ * Octet String (the encrypted data)
+ */
+ QAsn1Element elem;
+ if (!elem.read(encrypted) || elem.type() != QAsn1Element::SequenceType)
+ return encrypted;
+
+ const QVector<QAsn1Element> items = elem.toVector();
+ if (items.size() != 2
+ || items[0].type() != QAsn1Element::SequenceType
+ || items[1].type() != QAsn1Element::OctetStringType) {
+ return encrypted;
+ }
+
+ const QVector<QAsn1Element> encryptionSchemeContainer = items[0].toVector();
+
+ if (encryptionSchemeContainer.size() != 2
+ || encryptionSchemeContainer[0].type() != QAsn1Element::ObjectIdentifierType
+ || encryptionSchemeContainer[1].type() != QAsn1Element::SequenceType) {
+ return encrypted;
+ }
+
+ const QByteArray encryptionScheme = encryptionSchemeContainer[0].toObjectId();
+ const QVector<QAsn1Element> schemeParameterContainer = encryptionSchemeContainer[1].toVector();
+
+ if (schemeParameterContainer.size() != 2
+ && schemeParameterContainer[0].type() != QAsn1Element::SequenceType
+ && schemeParameterContainer[1].type() != QAsn1Element::SequenceType) {
+ return encrypted;
+ }
+
+ EncryptionData data;
+ if (encryptionScheme == PKCS5_PBES2_ENCRYPTION_OID) {
+ data = readPbes2(schemeParameterContainer, passPhrase);
+ } else if (pbes1OidHashFunctionMap.contains(encryptionScheme)) {
+ data = readPbes1(schemeParameterContainer, encryptionScheme, passPhrase);
+ } else if (encryptionScheme.startsWith(PKCS12_OID)) {
+ Q_UNIMPLEMENTED(); // this isn't some 'unknown', I know these aren't implemented
+ return encrypted;
+ } else {
+ qWarning()
+ << "QSslKey: Unsupported encryption scheme OID:" << encryptionScheme
+ << "\nFile a bugreport to Qt (include the line above).";
+ return encrypted;
+ }
+
+ if (!data.initialized) {
+ // something went wrong, return
+ return encrypted;
+ }
+
+ QByteArray decryptedKey = decrypt(data.cipher, items[1].value(), data.key, data.iv);
+ // The data is still wrapped in a octet string, so let's unwrap it
+ QAsn1Element decryptedKeyElement(QAsn1Element::ElementType::OctetStringType, decryptedKey);
+ return decryptedKeyElement.value();
+}
diff --git a/src/network/ssl/qsslpresharedkeyauthenticator.cpp b/src/network/ssl/qsslpresharedkeyauthenticator.cpp
index 8e1313493f..3bb2719026 100644
--- a/src/network/ssl/qsslpresharedkeyauthenticator.cpp
+++ b/src/network/ssl/qsslpresharedkeyauthenticator.cpp
@@ -83,28 +83,13 @@ QSslPreSharedKeyAuthenticatorPrivate::QSslPreSharedKeyAuthenticatorPrivate()
completing the PSK handshake. The client application needs to connect a
slot to the QSslSocket::preSharedKeyAuthenticationRequired() signal:
- \code
-
- connect(socket, &QSslSocket::preSharedKeyAuthenticationRequired,
- this, &AuthManager::handlePreSharedKeyAuthentication);
-
- \endcode
+ \snippet code/src_network_ssl_qsslpresharedkeyauthenticator.cpp 0
The signal carries a QSslPreSharedKeyAuthenticator object containing the
identity hint the server sent to the client, and which must be filled with the
corresponding client identity and the derived key:
- \code
-
- void AuthManager::handlePreSharedKeyAuthentication(QSslPreSharedKeyAuthenticator *authenticator)
- {
- authenticator->setIdentity("My Qt App");
-
- const QByteArray key = deriveKey(authenticator->identityHint(), passphrase);
- authenticator->setPreSharedKey(key);
- }
-
- \endcode
+ \snippet code/src_network_ssl_qsslpresharedkeyauthenticator.cpp 1
\note PSK ciphersuites are supported only when using OpenSSL 1.0.1 (or
greater) as the SSL backend.
diff --git a/src/network/ssl/qsslpresharedkeyauthenticator.h b/src/network/ssl/qsslpresharedkeyauthenticator.h
index 74ab888000..423f7731b4 100644
--- a/src/network/ssl/qsslpresharedkeyauthenticator.h
+++ b/src/network/ssl/qsslpresharedkeyauthenticator.h
@@ -78,6 +78,7 @@ public:
private:
friend Q_NETWORK_EXPORT bool operator==(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs);
friend class QSslSocketBackendPrivate;
+ friend class QDtlsPrivateOpenSSL;
QSharedDataPointer<QSslPreSharedKeyAuthenticatorPrivate> d;
};
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index ee157082ea..4a9d054c0d 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -336,12 +336,20 @@ QT_BEGIN_NAMESPACE
class QSslSocketGlobalData
{
public:
- QSslSocketGlobalData() : config(new QSslConfigurationPrivate) {}
+ QSslSocketGlobalData()
+ : config(new QSslConfigurationPrivate),
+ dtlsConfig(new QSslConfigurationPrivate)
+ {
+#if QT_CONFIG(dtls)
+ dtlsConfig->protocol = QSsl::DtlsV1_2OrLater;
+#endif // dtls
+ }
QMutex mutex;
QList<QSslCipher> supportedCiphers;
QVector<QSslEllipticCurve> supportedEllipticCurves;
QExplicitlySharedDataPointer<QSslConfigurationPrivate> config;
+ QExplicitlySharedDataPointer<QSslConfigurationPrivate> dtlsConfig;
};
Q_GLOBAL_STATIC(QSslSocketGlobalData, globalData)
@@ -371,7 +379,7 @@ QSslSocket::~QSslSocket()
qCDebug(lcSsl) << "QSslSocket::~QSslSocket(), this =" << (void *)this;
#endif
delete d->plainSocket;
- d->plainSocket = 0;
+ d->plainSocket = nullptr;
}
/*!
@@ -445,6 +453,12 @@ void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port, O
return;
}
+ if (!supportsSsl()) {
+ qCWarning(lcSsl, "QSslSocket::connectToHostEncrypted: TLS initialization failed");
+ d->setErrorAndEmit(QAbstractSocket::SslInternalError, tr("TLS initialization failed"));
+ return;
+ }
+
d->init();
d->autoStartHandshake = true;
d->initialized = true;
@@ -476,6 +490,12 @@ void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port,
return;
}
+ if (!supportsSsl()) {
+ qCWarning(lcSsl, "QSslSocket::connectToHostEncrypted: TLS initialization failed");
+ d->setErrorAndEmit(QAbstractSocket::SslInternalError, tr("TLS initialization failed"));
+ return;
+ }
+
d->init();
d->autoStartHandshake = true;
d->initialized = true;
@@ -1820,6 +1840,12 @@ void QSslSocket::startClientEncryption()
"QSslSocket::startClientEncryption: cannot start handshake when not connected");
return;
}
+
+ if (!supportsSsl()) {
+ qCWarning(lcSsl, "QSslSocket::startClientEncryption: TLS initialization failed");
+ d->setErrorAndEmit(QAbstractSocket::SslInternalError, tr("TLS initialization failed"));
+ return;
+ }
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << "QSslSocket::startClientEncryption()";
#endif
@@ -1858,6 +1884,11 @@ void QSslSocket::startServerEncryption()
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << "QSslSocket::startServerEncryption()";
#endif
+ if (!supportsSsl()) {
+ qCWarning(lcSsl, "QSslSocket::startServerEncryption: TLS initialization failed");
+ d->setErrorAndEmit(QAbstractSocket::SslInternalError, tr("TLS initialization failed"));
+ return;
+ }
d->mode = SslServerMode;
emit modeChanged(d->mode);
d->startServerEncryption();
@@ -2049,9 +2080,9 @@ QSslSocketPrivate::QSslSocketPrivate()
, connectionEncrypted(false)
, shutdown(false)
, ignoreAllSslErrors(false)
- , readyReadEmittedPointer(0)
+ , readyReadEmittedPointer(nullptr)
, allowRootCertOnDemandLoading(true)
- , plainSocket(0)
+ , plainSocket(nullptr)
, paused(false)
, flushTriggered(false)
{
@@ -2131,6 +2162,26 @@ void QSslSocketPrivate::setDefaultSupportedCiphers(const QList<QSslCipher> &ciph
/*!
\internal
*/
+void q_setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers)
+{
+ QMutexLocker locker(&globalData()->mutex);
+ globalData()->dtlsConfig.detach();
+ globalData()->dtlsConfig->ciphers = ciphers;
+}
+
+/*!
+ \internal
+*/
+QList<QSslCipher> q_getDefaultDtlsCiphers()
+{
+ QSslSocketPrivate::ensureInitialized();
+ QMutexLocker locker(&globalData()->mutex);
+ return globalData()->dtlsConfig->ciphers;
+}
+
+/*!
+ \internal
+*/
QVector<QSslEllipticCurve> QSslSocketPrivate::supportedEllipticCurves()
{
QSslSocketPrivate::ensureInitialized();
@@ -2145,6 +2196,7 @@ void QSslSocketPrivate::setDefaultSupportedEllipticCurves(const QVector<QSslElli
{
const QMutexLocker locker(&globalData()->mutex);
globalData()->config.detach();
+ globalData()->dtlsConfig.detach();
globalData()->supportedEllipticCurves = curves;
}
@@ -2167,6 +2219,8 @@ void QSslSocketPrivate::setDefaultCaCertificates(const QList<QSslCertificate> &c
QMutexLocker locker(&globalData()->mutex);
globalData()->config.detach();
globalData()->config->caCertificates = certs;
+ globalData()->dtlsConfig.detach();
+ globalData()->dtlsConfig->caCertificates = certs;
// when the certificates are set explicitly, we do not want to
// load the system certificates on demand
s_loadRootCertsOnDemand = false;
@@ -2186,6 +2240,8 @@ bool QSslSocketPrivate::addDefaultCaCertificates(const QString &path, QSsl::Enco
QMutexLocker locker(&globalData()->mutex);
globalData()->config.detach();
globalData()->config->caCertificates += certs;
+ globalData()->dtlsConfig.detach();
+ globalData()->dtlsConfig->caCertificates += certs;
return true;
}
@@ -2198,6 +2254,8 @@ void QSslSocketPrivate::addDefaultCaCertificate(const QSslCertificate &cert)
QMutexLocker locker(&globalData()->mutex);
globalData()->config.detach();
globalData()->config->caCertificates += cert;
+ globalData()->dtlsConfig.detach();
+ globalData()->dtlsConfig->caCertificates += cert;
}
/*!
@@ -2209,6 +2267,8 @@ void QSslSocketPrivate::addDefaultCaCertificates(const QList<QSslCertificate> &c
QMutexLocker locker(&globalData()->mutex);
globalData()->config.detach();
globalData()->config->caCertificates += certs;
+ globalData()->dtlsConfig.detach();
+ globalData()->dtlsConfig->caCertificates += certs;
}
/*!
@@ -2261,6 +2321,33 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri
ptr->sslOptions = global->sslOptions;
ptr->ellipticCurves = global->ellipticCurves;
ptr->backendConfig = global->backendConfig;
+#if QT_CONFIG(dtls)
+ ptr->dtlsCookieEnabled = global->dtlsCookieEnabled;
+#endif
+}
+
+/*!
+ \internal
+*/
+QSslConfiguration QSslConfigurationPrivate::defaultDtlsConfiguration()
+{
+ QSslSocketPrivate::ensureInitialized();
+ QMutexLocker locker(&globalData()->mutex);
+
+ return QSslConfiguration(globalData()->dtlsConfig.data());
+}
+
+/*!
+ \internal
+*/
+void QSslConfigurationPrivate::setDefaultDtlsConfiguration(const QSslConfiguration &configuration)
+{
+ QSslSocketPrivate::ensureInitialized();
+ QMutexLocker locker(&globalData()->mutex);
+ if (globalData()->dtlsConfig == configuration.d)
+ return; // nothing to do
+
+ globalData()->dtlsConfig = const_cast<QSslConfigurationPrivate*>(configuration.d.constData());
}
/*!
diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp
index aa0e1b0dd1..9166e0ac29 100644
--- a/src/network/ssl/qsslsocket_mac.cpp
+++ b/src/network/ssl/qsslsocket_mac.cpp
@@ -48,6 +48,7 @@
#include <QtCore/qmessageauthenticationcode.h>
#include <QtCore/qoperatingsystemversion.h>
+#include <QtCore/qscopedvaluerollback.h>
#include <QtCore/qcryptographichash.h>
#include <QtCore/qsystemdetection.h>
#include <QtCore/qdatastream.h>
@@ -242,48 +243,70 @@ static const uint8_t dhparam[] =
"\x90\x0b\x35\x64\xff\xd9\xe3\xac\xf2\xf2\xeb\x3a\x63\x02\x01\x02";
#endif
-// No ioErr on iOS/tvOS/watchOS. (defined in MacErrors.h on macOS)
-#if defined(QT_PLATFORM_UIKIT)
-# define ioErr -36
-#endif
-
-static OSStatus _q_SSLRead(QTcpSocket *plainSocket, char *data, size_t *dataLength)
+OSStatus QSslSocketBackendPrivate::ReadCallback(QSslSocketBackendPrivate *socket,
+ char *data, size_t *dataLength)
{
- Q_ASSERT(plainSocket);
+ Q_ASSERT(socket);
Q_ASSERT(data);
Q_ASSERT(dataLength);
+ QTcpSocket *plainSocket = socket->plainSocket;
+ Q_ASSERT(plainSocket);
+
+ if (socket->isHandshakeComplete()) {
+ // Check if it's a renegotiation attempt, when the handshake is complete, the
+ // session state is 'kSSLConnected':
+ SSLSessionState currentState = kSSLConnected;
+ const OSStatus result = SSLGetSessionState(socket->context, &currentState);
+ if (result != noErr) {
+ *dataLength = 0;
+ return result;
+ }
+
+ if (currentState == kSSLHandshake) {
+ // Renegotiation detected, don't allow read more yet - 'transmit'
+ // will notice this and will call 'startHandshake':
+ *dataLength = 0;
+ socket->renegotiating = true;
+ return errSSLWouldBlock;
+ }
+ }
+
const qint64 bytes = plainSocket->read(data, *dataLength);
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "read" << bytes;
#endif
if (bytes < 0) {
*dataLength = 0;
- return ioErr;
+ return errSecIO;
}
- const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : noErr;
+ const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : errSecSuccess;
*dataLength = bytes;
return err;
}
-static OSStatus _q_SSLWrite(QTcpSocket *plainSocket, const char *data, size_t *dataLength)
+OSStatus QSslSocketBackendPrivate::WriteCallback(QSslSocketBackendPrivate *socket,
+ const char *data, size_t *dataLength)
{
- Q_ASSERT(plainSocket);
+ Q_ASSERT(socket);
Q_ASSERT(data);
Q_ASSERT(dataLength);
+ QTcpSocket *plainSocket = socket->plainSocket;
+ Q_ASSERT(plainSocket);
+
const qint64 bytes = plainSocket->write(data, *dataLength);
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "write" << bytes;
#endif
if (bytes < 0) {
*dataLength = 0;
- return ioErr;
+ return errSecIO;
}
- const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : noErr;
+ const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : errSecSuccess;
*dataLength = bytes;
return err;
@@ -387,12 +410,12 @@ void QSslSocketBackendPrivate::continueHandshake()
Q_Q(QSslSocket);
connectionEncrypted = true;
-#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_NA, __IPHONE_11_0, __TVOS_11_0, __WATCHOS_4_0)
+#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13_4, __IPHONE_11_0, __TVOS_11_0, __WATCHOS_4_0)
// Unlike OpenSSL, Secure Transport does not allow to negotiate protocols via
// a callback during handshake. We can only set our list of preferred protocols
// (and send it during handshake) and then receive what our peer has sent to us.
// And here we can finally try to find a match (if any).
- if (__builtin_available(iOS 11.0, tvOS 11.0, watchOS 4.0, *)) {
+ if (__builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) {
const auto &requestedProtocols = configuration.nextAllowedProtocols;
if (const int requestedCount = requestedProtocols.size()) {
configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNone;
@@ -423,7 +446,9 @@ void QSslSocketBackendPrivate::continueHandshake()
}
#endif // QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE
- emit q->encrypted();
+ if (!renegotiating)
+ emit q->encrypted();
+
if (autoStartHandshake && pendingClose) {
pendingClose = false;
q->disconnectFromHost();
@@ -452,7 +477,7 @@ void QSslSocketBackendPrivate::disconnectFromHost()
QSslCipher QSslSocketBackendPrivate::sessionCipher() const
{
SSLCipherSuite cipher = 0;
- if (context && SSLGetNegotiatedCipher(context, &cipher) == noErr)
+ if (context && SSLGetNegotiatedCipher(context, &cipher) == errSecSuccess)
return QSslCipher_from_SSLCipherSuite(cipher);
return QSslCipher();
@@ -465,7 +490,7 @@ QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const
SSLProtocol protocol = kSSLProtocolUnknown;
const OSStatus err = SSLGetNegotiatedProtocolVersion(context, &protocol);
- if (err != noErr) {
+ if (err != errSecSuccess) {
qCWarning(lcSsl) << "SSLGetNegotiatedProtocolVersion failed:" << err;
return QSsl::UnknownProtocol;
}
@@ -521,10 +546,10 @@ void QSslSocketBackendPrivate::transmit()
if (!context || shutdown)
return;
- if (!connectionEncrypted)
+ if (!isHandshakeComplete())
startHandshake();
- if (connectionEncrypted && !writeBuffer.isEmpty()) {
+ if (isHandshakeComplete() && !writeBuffer.isEmpty()) {
qint64 totalBytesWritten = 0;
while (writeBuffer.nextDataBlockSize() > 0 && context) {
const size_t nextDataBlockSize = writeBuffer.nextDataBlockSize();
@@ -533,7 +558,7 @@ void QSslSocketBackendPrivate::transmit()
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "SSLWrite returned" << err;
#endif
- if (err != noErr && err != errSSLWouldBlock) {
+ if (err != errSecSuccess && err != errSSLWouldBlock) {
setErrorAndEmit(QAbstractSocket::SslInternalError,
QStringLiteral("SSLWrite failed: %1").arg(err));
break;
@@ -559,7 +584,7 @@ void QSslSocketBackendPrivate::transmit()
}
}
- if (connectionEncrypted) {
+ if (isHandshakeComplete()) {
QVarLengthArray<char, 4096> data;
while (context && (!readBufferMaxSize || buffer.size() < readBufferMaxSize)) {
size_t readBytes = 0;
@@ -573,12 +598,17 @@ void QSslSocketBackendPrivate::transmit()
setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
QSslSocket::tr("The TLS/SSL connection has been closed"));
break;
- } else if (err != noErr && err != errSSLWouldBlock) {
+ } else if (err != errSecSuccess && err != errSSLWouldBlock) {
setErrorAndEmit(QAbstractSocket::SslInternalError,
QStringLiteral("SSLRead failed: %1").arg(err));
break;
}
+ if (err == errSSLWouldBlock && renegotiating) {
+ startHandshake();
+ break;
+ }
+
if (readBytes) {
buffer.append(data.constData(), readBytes);
if (readyReadEmittedPointer)
@@ -861,16 +891,17 @@ bool QSslSocketBackendPrivate::initSslContext()
return false;
}
- const OSStatus err = SSLSetIOFuncs(context, reinterpret_cast<SSLReadFunc>(&_q_SSLRead),
- reinterpret_cast<SSLWriteFunc>(&_q_SSLWrite));
- if (err != noErr) {
+ const OSStatus err = SSLSetIOFuncs(context,
+ reinterpret_cast<SSLReadFunc>(&QSslSocketBackendPrivate::ReadCallback),
+ reinterpret_cast<SSLWriteFunc>(&QSslSocketBackendPrivate::WriteCallback));
+ if (err != errSecSuccess) {
destroySslContext();
setErrorAndEmit(QAbstractSocket::SslInternalError,
QStringLiteral("SSLSetIOFuncs failed: %1").arg(err));
return false;
}
- SSLSetConnection(context, plainSocket);
+ SSLSetConnection(context, this);
if (mode == QSslSocket::SslServerMode
&& !configuration.localCertificateChain.isEmpty()) {
@@ -889,8 +920,8 @@ bool QSslSocketBackendPrivate::initSslContext()
return false;
}
-#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_NA, __IPHONE_11_0, __TVOS_11_0, __WATCHOS_4_0)
- if (__builtin_available(iOS 11.0, tvOS 11.0, watchOS 4.0, *)) {
+#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13_4, __IPHONE_11_0, __TVOS_11_0, __WATCHOS_4_0)
+ if (__builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) {
const auto protocolNames = configuration.nextAllowedProtocols;
QCFType<CFMutableArrayRef> cfNames(CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks));
if (cfNames) {
@@ -922,10 +953,10 @@ bool QSslSocketBackendPrivate::initSslContext()
SSLSetPeerDomainName(context, ace.data(), ace.size());
// tell SecureTransport we handle peer verification ourselves
OSStatus err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnServerAuth, true);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnCertRequested, true);
- if (err != noErr) {
+ if (err != errSecSuccess) {
destroySslContext();
setErrorAndEmit(QSslSocket::SslInternalError,
QStringLiteral("SSLSetSessionOption failed: %1").arg(err));
@@ -936,13 +967,13 @@ bool QSslSocketBackendPrivate::initSslContext()
if (configuration.peerVerifyMode != QSslSocket::VerifyNone) {
// kAlwaysAuthenticate - always fails even if we set break on client auth.
OSStatus err = SSLSetClientSideAuthenticate(context, kTryAuthenticate);
- if (err == noErr) {
+ if (err == errSecSuccess) {
// We'd like to verify peer ourselves, otherwise handshake will
// most probably fail before we can do anything.
err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnClientAuth, true);
}
- if (err != noErr) {
+ if (err != errSecSuccess) {
destroySslContext();
setErrorAndEmit(QAbstractSocket::SslInternalError,
QStringLiteral("failed to set SSL context option in server mode: %1").arg(err));
@@ -1005,7 +1036,7 @@ bool QSslSocketBackendPrivate::setSessionCertificate(QString &errorDescription,
nullptr, nullptr);
QCFType<CFArrayRef> items;
OSStatus err = SecPKCS12Import(pkcs12, options, &items);
- if (err != noErr) {
+ if (err != errSecSuccess) {
#ifdef QSSLSOCKET_DEBUG
qCWarning(lcSsl) << plainSocket
<< QStringLiteral("SecPKCS12Import failed: %1").arg(err);
@@ -1051,7 +1082,7 @@ bool QSslSocketBackendPrivate::setSessionCertificate(QString &errorDescription,
}
err = SSLSetCertificate(context, certs);
- if (err != noErr) {
+ if (err != errSecSuccess) {
#ifdef QSSLSOCKET_DEBUG
qCWarning(lcSsl) << plainSocket
<< QStringLiteral("Cannot set certificate and key: %1").arg(err);
@@ -1079,35 +1110,35 @@ bool QSslSocketBackendPrivate::setSessionProtocol()
return false;
}
- OSStatus err = noErr;
+ OSStatus err = errSecSuccess;
if (configuration.protocol == QSsl::SslV3) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : SSLv3";
#endif
err = SSLSetProtocolVersionMin(context, kSSLProtocol3);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kSSLProtocol3);
} else if (configuration.protocol == QSsl::TlsV1_0) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.0";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kTLSProtocol1);
} else if (configuration.protocol == QSsl::TlsV1_1) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.1";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol11);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kTLSProtocol11);
} else if (configuration.protocol == QSsl::TlsV1_2) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.2";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol12);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
} else if (configuration.protocol == QSsl::AnyProtocol) {
#ifdef QSSLSOCKET_DEBUG
@@ -1115,42 +1146,42 @@ bool QSslSocketBackendPrivate::setSessionProtocol()
#endif
// kSSLProtocol3, since kSSLProtocol2 is disabled:
err = SSLSetProtocolVersionMin(context, kSSLProtocol3);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
} else if (configuration.protocol == QSsl::TlsV1SslV3) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : SSLv3 - TLSv1.2";
#endif
err = SSLSetProtocolVersionMin(context, kSSLProtocol3);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
} else if (configuration.protocol == QSsl::SecureProtocols) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1 - TLSv1.2";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
} else if (configuration.protocol == QSsl::TlsV1_0OrLater) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1 - TLSv1.2";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
} else if (configuration.protocol == QSsl::TlsV1_1OrLater) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.1 - TLSv1.2";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol11);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
} else if (configuration.protocol == QSsl::TlsV1_2OrLater) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.2";
#endif
err = SSLSetProtocolVersionMin(context, kTLSProtocol12);
- if (err == noErr)
+ if (err == errSecSuccess)
err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
} else {
#ifdef QSSLSOCKET_DEBUG
@@ -1159,7 +1190,7 @@ bool QSslSocketBackendPrivate::setSessionProtocol()
return false;
}
- return err == noErr;
+ return err == errSecSuccess;
}
bool QSslSocketBackendPrivate::canIgnoreTrustVerificationFailure() const
@@ -1204,8 +1235,8 @@ bool QSslSocketBackendPrivate::verifyPeerTrust()
QCFType<SecTrustRef> trust;
OSStatus err = SSLCopyPeerTrust(context, &trust);
- // !trust - SSLCopyPeerTrust can return noErr but null trust.
- if (err != noErr || !trust) {
+ // !trust - SSLCopyPeerTrust can return errSecSuccess but null trust.
+ if (err != errSecSuccess || !trust) {
if (!canIgnoreVerify) {
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
QStringLiteral("Failed to obtain peer trust: %1").arg(err));
@@ -1217,40 +1248,38 @@ bool QSslSocketBackendPrivate::verifyPeerTrust()
}
QList<QSslError> errors;
- // store certificates
- const int certCount = SecTrustGetCertificateCount(trust);
- // TODO: why this test depends on configuration.peerCertificateChain not being empty????
- if (configuration.peerCertificateChain.isEmpty()) {
- // Apple's docs say SetTrustEvaluate must be called before
- // SecTrustGetCertificateAtIndex, but this results
- // in 'kSecTrustResultRecoverableTrustFailure', so
- // here we just ignore 'res' (later we'll use SetAnchor etc.
- // and evaluate again).
- SecTrustResultType res = kSecTrustResultInvalid;
- err = SecTrustEvaluate(trust, &res);
- if (err != noErr) {
- // We can not ignore this, it's not even about trust verification
- // probably ...
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
- QStringLiteral("SecTrustEvaluate failed: %1").arg(err));
- plainSocket->disconnectFromHost();
- return false;
- }
- for (int i = 0; i < certCount; ++i) {
- SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, i);
- QCFType<CFDataRef> derData = SecCertificateCopyData(cert);
- configuration.peerCertificateChain << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
- }
+ // Store certificates.
+ // Apple's docs say SetTrustEvaluate must be called before
+ // SecTrustGetCertificateAtIndex, but this results
+ // in 'kSecTrustResultRecoverableTrustFailure', so
+ // here we just ignore 'res' (later we'll use SetAnchor etc.
+ // and evaluate again).
+ SecTrustResultType res = kSecTrustResultInvalid;
+ err = SecTrustEvaluate(trust, &res);
+ if (err != errSecSuccess) {
+ // We can not ignore this, it's not even about trust verification
+ // probably ...
+ setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
+ QStringLiteral("SecTrustEvaluate failed: %1").arg(err));
+ plainSocket->disconnectFromHost();
+ return false;
}
- if (certCount > 0) {
- SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, 0);
+ configuration.peerCertificate.clear();
+ configuration.peerCertificateChain.clear();
+
+ const CFIndex certCount = SecTrustGetCertificateCount(trust);
+ for (CFIndex i = 0; i < certCount; ++i) {
+ SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, i);
QCFType<CFDataRef> derData = SecCertificateCopyData(cert);
- configuration.peerCertificate = QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
+ configuration.peerCertificateChain << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
}
- // check the whole chain for blacklisting (including root, as we check for subjectInfo and issuer)
+ if (configuration.peerCertificateChain.size())
+ configuration.peerCertificate = configuration.peerCertificateChain.at(0);
+
+ // Check the whole chain for blacklisting (including root, as we check for subjectInfo and issuer):
for (const QSslCertificate &cert : qAsConst(configuration.peerCertificateChain)) {
if (QSslCertificatePrivate::isBlacklisted(cert) && !canIgnoreVerify) {
const QSslError error(QSslError::CertificateBlacklisted, cert);
@@ -1294,10 +1323,10 @@ bool QSslSocketBackendPrivate::verifyPeerTrust()
}
// verify certificate chain
- QCFType<CFMutableArrayRef> certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ QCFType<CFMutableArrayRef> certArray = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks);
for (const QSslCertificate &cert : qAsConst(configuration.caCertificates)) {
QCFType<CFDataRef> certData = cert.d->derData.toCFData();
- if (QCFType<SecCertificateRef> secRef = SecCertificateCreateWithData(NULL, certData))
+ if (QCFType<SecCertificateRef> secRef = SecCertificateCreateWithData(nullptr, certData))
CFArrayAppendValue(certArray, secRef);
else
qCWarning(lcSsl, "Failed to create SecCertificate from QSslCertificate");
@@ -1422,6 +1451,7 @@ bool QSslSocketBackendPrivate::startHandshake()
// Failure means a real error (invalid certificate, no private key, etc).
if (!setSessionCertificate(errorDescription, errorCode)) {
setErrorAndEmit(errorCode, errorDescription);
+ renegotiating = false;
return false;
} else {
// We try to resume a handshake, even if have no
@@ -1436,6 +1466,7 @@ bool QSslSocketBackendPrivate::startHandshake()
return startHandshake();
}
+ renegotiating = false;
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
QStringLiteral("SSLHandshake failed: %1").arg(err));
plainSocket->disconnectFromHost();
@@ -1445,6 +1476,7 @@ bool QSslSocketBackendPrivate::startHandshake()
// Connection aborted during handshake phase.
if (q->state() != QAbstractSocket::ConnectedState) {
qCDebug(lcSsl) << "connection aborted";
+ renegotiating = false;
return false;
}
@@ -1453,13 +1485,16 @@ bool QSslSocketBackendPrivate::startHandshake()
if (!verifySessionProtocol()) {
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, QStringLiteral("Protocol version mismatch"));
plainSocket->disconnectFromHost();
+ renegotiating = false;
return false;
}
if (verifyPeerTrust()) {
continueHandshake();
+ renegotiating = false;
return true;
} else {
+ renegotiating = false;
return false;
}
}
diff --git a/src/network/ssl/qsslsocket_mac_p.h b/src/network/ssl/qsslsocket_mac_p.h
index 34e30ebb16..e37171e56a 100644
--- a/src/network/ssl/qsslsocket_mac_p.h
+++ b/src/network/ssl/qsslsocket_mac_p.h
@@ -120,7 +120,14 @@ private:
bool checkSslErrors();
bool startHandshake();
+ bool isHandshakeComplete() const {return connectionEncrypted && !renegotiating;}
+
+ // IO callbacks:
+ static OSStatus ReadCallback(QSslSocketBackendPrivate *socket, char *data, size_t *dataLength);
+ static OSStatus WriteCallback(QSslSocketBackendPrivate *plainSocket, const char *data, size_t *dataLength);
+
QSecureTransportContext context;
+ bool renegotiating = false;
Q_DISABLE_COPY(QSslSocketBackendPrivate)
};
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 11f8a40199..37bb3e4933 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -66,6 +66,10 @@
#include "qsslpresharedkeyauthenticator.h"
#include "qsslpresharedkeyauthenticator_p.h"
+#ifdef Q_OS_WIN
+#include "qwindowscarootfetcher_p.h"
+#endif
+
#include <QtCore/qdatetime.h>
#include <QtCore/qdebug.h>
#include <QtCore/qdir.h>
@@ -77,15 +81,16 @@
#include <QtCore/qthread.h>
#include <QtCore/qurl.h>
#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qscopedvaluerollback.h>
#include <string.h>
QT_BEGIN_NAMESPACE
#if defined(Q_OS_WIN)
- PtrCertOpenSystemStoreW QSslSocketPrivate::ptrCertOpenSystemStoreW = 0;
- PtrCertFindCertificateInStore QSslSocketPrivate::ptrCertFindCertificateInStore = 0;
- PtrCertCloseStore QSslSocketPrivate::ptrCertCloseStore = 0;
+ PtrCertOpenSystemStoreW QSslSocketPrivate::ptrCertOpenSystemStoreW = nullptr;
+ PtrCertFindCertificateInStore QSslSocketPrivate::ptrCertFindCertificateInStore = nullptr;
+ PtrCertCloseStore QSslSocketPrivate::ptrCertCloseStore = nullptr;
#endif
bool QSslSocketPrivate::s_libraryLoaded = false;
@@ -99,12 +104,13 @@ int QSslSocketBackendPrivate::s_indexForSSLExtraData = -1;
QString QSslSocketBackendPrivate::getErrorsFromOpenSsl()
{
QString errorString;
+ char buf[256] = {}; // OpenSSL docs claim both 120 and 256; use the larger.
unsigned long errNum;
while ((errNum = q_ERR_get_error())) {
- if (! errorString.isEmpty())
+ if (!errorString.isEmpty())
errorString.append(QLatin1String(", "));
- const char *error = q_ERR_error_string(errNum, NULL);
- errorString.append(QString::fromLatin1(error)); // error is ascii according to man ERR_error_string
+ q_ERR_error_string_n(errNum, buf, sizeof buf);
+ errorString.append(QString::fromLatin1(buf)); // error is ascii according to man ERR_error_string
}
return errorString;
}
@@ -134,10 +140,10 @@ static unsigned int q_ssl_psk_server_callback(SSL *ssl,
} // extern "C"
QSslSocketBackendPrivate::QSslSocketBackendPrivate()
- : ssl(0),
- readBio(0),
- writeBio(0),
- session(0)
+ : ssl(nullptr),
+ readBio(nullptr),
+ writeBio(nullptr),
+ session(nullptr)
{
// Calls SSL_library_init().
ensureInitialized();
@@ -188,8 +194,7 @@ QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(const SSL_CIPHER
return ciph;
}
-// static
-inline QSslErrorEntry QSslErrorEntry::fromStoreContext(X509_STORE_CTX *ctx)
+QSslErrorEntry QSslErrorEntry::fromStoreContext(X509_STORE_CTX *ctx)
{
return {
q_X509_STORE_CTX_get_error(ctx),
@@ -242,6 +247,33 @@ int q_X509Callback(int ok, X509_STORE_CTX *ctx)
return 1;
}
+static void q_loadCiphersForConnection(SSL *connection, QList<QSslCipher> &ciphers,
+ QList<QSslCipher> &defaultCiphers)
+{
+ Q_ASSERT(connection);
+
+ STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(connection);
+ for (int i = 0; i < q_sk_SSL_CIPHER_num(supportedCiphers); ++i) {
+ if (SSL_CIPHER *cipher = q_sk_SSL_CIPHER_value(supportedCiphers, i)) {
+ QSslCipher ciph = QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher);
+ if (!ciph.isNull()) {
+ // Unconditionally exclude ADH and AECDH ciphers since they offer no MITM protection
+ if (!ciph.name().toLower().startsWith(QLatin1String("adh")) &&
+ !ciph.name().toLower().startsWith(QLatin1String("exp-adh")) &&
+ !ciph.name().toLower().startsWith(QLatin1String("aecdh"))) {
+ ciphers << ciph;
+
+ if (ciph.usedBits() >= 128)
+ defaultCiphers << ciph;
+ }
+ }
+ }
+ }
+}
+
+// Defined in qsslsocket.cpp
+void q_setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers);
+
long QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions)
{
long options;
@@ -383,7 +415,7 @@ void QSslSocketBackendPrivate::destroySslContext()
{
if (ssl) {
q_SSL_free(ssl);
- ssl = 0;
+ ssl = nullptr;
}
sslContextPointer.clear();
}
@@ -442,34 +474,38 @@ void QSslSocketPrivate::resetDefaultCiphers()
#else
SSL_CTX *myCtx = q_SSL_CTX_new(q_SSLv23_client_method());
#endif
+ // Note, we assert, not just silently return/bail out early:
+ // this should never happen and problems with OpenSSL's initialization
+ // must be caught before this (see supportsSsl()).
+ Q_ASSERT(myCtx);
SSL *mySsl = q_SSL_new(myCtx);
+ Q_ASSERT(mySsl);
QList<QSslCipher> ciphers;
QList<QSslCipher> defaultCiphers;
- STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(mySsl);
- for (int i = 0; i < q_sk_SSL_CIPHER_num(supportedCiphers); ++i) {
- if (SSL_CIPHER *cipher = q_sk_SSL_CIPHER_value(supportedCiphers, i)) {
- QSslCipher ciph = QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher);
- if (!ciph.isNull()) {
- // Unconditionally exclude ADH and AECDH ciphers since they offer no MITM protection
- if (!ciph.name().toLower().startsWith(QLatin1String("adh")) &&
- !ciph.name().toLower().startsWith(QLatin1String("exp-adh")) &&
- !ciph.name().toLower().startsWith(QLatin1String("aecdh"))) {
- ciphers << ciph;
-
- if (ciph.usedBits() >= 128)
- defaultCiphers << ciph;
- }
- }
- }
- }
+ q_loadCiphersForConnection(mySsl, ciphers, defaultCiphers);
q_SSL_CTX_free(myCtx);
q_SSL_free(mySsl);
setDefaultSupportedCiphers(ciphers);
setDefaultCiphers(defaultCiphers);
+
+#if QT_CONFIG(dtls)
+ ciphers.clear();
+ defaultCiphers.clear();
+ myCtx = q_SSL_CTX_new(q_DTLS_client_method());
+ if (myCtx) {
+ mySsl = q_SSL_new(myCtx);
+ if (mySsl) {
+ q_loadCiphersForConnection(mySsl, ciphers, defaultCiphers);
+ q_setDefaultDtlsCiphers(defaultCiphers);
+ q_SSL_free(mySsl);
+ }
+ q_SSL_CTX_free(myCtx);
+ }
+#endif // dtls
}
void QSslSocketPrivate::resetDefaultEllipticCurves()
@@ -615,6 +651,11 @@ void QSslSocketBackendPrivate::transmit()
{
Q_Q(QSslSocket);
+ using ScopedBool = QScopedValueRollback<bool>;
+
+ if (inSetAndEmitError)
+ return;
+
// If we don't have any SSL context, don't bother transmitting.
if (!ssl)
return;
@@ -642,6 +683,7 @@ void QSslSocketBackendPrivate::transmit()
break;
} else {
// ### Better error handling.
+ const ScopedBool bg(inSetAndEmitError, true);
setErrorAndEmit(QAbstractSocket::SslInternalError,
QSslSocket::tr("Unable to write data: %1").arg(
getErrorsFromOpenSsl()));
@@ -688,6 +730,7 @@ void QSslSocketBackendPrivate::transmit()
#endif
if (actualWritten < 0) {
//plain socket write fails if it was in the pending close state.
+ const ScopedBool bg(inSetAndEmitError, true);
setErrorAndEmit(plainSocket->error(), plainSocket->errorString());
return;
}
@@ -713,6 +756,7 @@ void QSslSocketBackendPrivate::transmit()
plainSocket->skip(writtenToBio);
} else {
// ### Better error handling.
+ const ScopedBool bg(inSetAndEmitError, true);
setErrorAndEmit(QAbstractSocket::SslInternalError,
QSslSocket::tr("Unable to decrypt data: %1").arg(
getErrorsFromOpenSsl()));
@@ -794,15 +838,21 @@ void QSslSocketBackendPrivate::transmit()
qCDebug(lcSsl) << "QSslSocketBackendPrivate::transmit: remote disconnect";
#endif
shutdown = true; // the other side shut down, make sure we do not send shutdown ourselves
- setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
- QSslSocket::tr("The TLS/SSL connection has been closed"));
+ {
+ const ScopedBool bg(inSetAndEmitError, true);
+ setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
+ QSslSocket::tr("The TLS/SSL connection has been closed"));
+ }
return;
case SSL_ERROR_SYSCALL: // some IO error
case SSL_ERROR_SSL: // error in the SSL library
// we do not know exactly what the error is, nor whether we can recover from it,
// so just return to prevent an endless loop in the outer "while" statement
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QSslSocket::tr("Error while reading: %1").arg(getErrorsFromOpenSsl()));
+ {
+ const ScopedBool bg(inSetAndEmitError, true);
+ setErrorAndEmit(QAbstractSocket::SslInternalError,
+ QSslSocket::tr("Error while reading: %1").arg(getErrorsFromOpenSsl()));
+ }
return;
default:
// SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: can only happen with a
@@ -810,15 +860,18 @@ void QSslSocketBackendPrivate::transmit()
// SSL_ERROR_WANT_X509_LOOKUP: can only happen with a
// SSL_CTX_set_client_cert_cb(), which we do not call.
// So this default case should never be triggered.
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QSslSocket::tr("Error while reading: %1").arg(getErrorsFromOpenSsl()));
+ {
+ const ScopedBool bg(inSetAndEmitError, true);
+ setErrorAndEmit(QAbstractSocket::SslInternalError,
+ QSslSocket::tr("Error while reading: %1").arg(getErrorsFromOpenSsl()));
+ }
break;
}
} while (ssl && readBytes > 0);
} 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) {
@@ -867,12 +920,24 @@ static QSslError _q_OpenSSL_to_QSslError(int errorCode, const QSslCertificate &c
return error;
}
+QString QSslSocketBackendPrivate::msgErrorsDuringHandshake()
+{
+ return QSslSocket::tr("Error during SSL handshake: %1")
+ .arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
+}
+
bool QSslSocketBackendPrivate::startHandshake()
{
Q_Q(QSslSocket);
// Check if the connection has been established. Get all errors from the
// verification stage.
+
+ using ScopedBool = QScopedValueRollback<bool>;
+
+ if (inSetAndEmitError)
+ return false;
+
QMutexLocker locker(&_q_sslErrorList()->mutex);
_q_sslErrorList()->errors.clear();
int result = (mode == QSslSocket::SslClientMode) ? q_SSL_connect(ssl) : q_SSL_accept(ssl);
@@ -902,12 +967,14 @@ bool QSslSocketBackendPrivate::startHandshake()
// The handshake is not yet complete.
break;
default:
- QString errorString
- = QSslSocket::tr("Error during SSL handshake: %1").arg(getErrorsFromOpenSsl());
+ QString errorString = QSslSocketBackendPrivate::msgErrorsDuringHandshake();
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << "QSslSocketBackendPrivate::startHandshake: error!" << errorString;
#endif
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, errorString);
+ {
+ const ScopedBool bg(inSetAndEmitError, true);
+ setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, errorString);
+ }
q->abort();
}
return false;
@@ -1177,119 +1244,6 @@ void QSslSocketBackendPrivate::_q_caRootLoaded(QSslCertificate cert, QSslCertifi
}
}
-class QWindowsCaRootFetcherThread : public QThread
-{
-public:
- QWindowsCaRootFetcherThread()
- {
- qRegisterMetaType<QSslCertificate>();
- setObjectName(QStringLiteral("QWindowsCaRootFetcher"));
- start();
- }
- ~QWindowsCaRootFetcherThread()
- {
- quit();
- wait(15500); // worst case, a running request can block for 15 seconds
- }
-};
-
-Q_GLOBAL_STATIC(QWindowsCaRootFetcherThread, windowsCaRootFetcherThread);
-
-QWindowsCaRootFetcher::QWindowsCaRootFetcher(const QSslCertificate &certificate, QSslSocket::SslMode sslMode)
- : cert(certificate), mode(sslMode)
-{
- moveToThread(windowsCaRootFetcherThread());
-}
-
-QWindowsCaRootFetcher::~QWindowsCaRootFetcher()
-{
-}
-
-void QWindowsCaRootFetcher::start()
-{
- QByteArray der = cert.toDer();
- PCCERT_CONTEXT wincert = CertCreateCertificateContext(X509_ASN_ENCODING, (const BYTE *)der.constData(), der.length());
- if (!wincert) {
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl, "QWindowsCaRootFetcher failed to convert certificate to windows form");
-#endif
- emit finished(cert, QSslCertificate());
- deleteLater();
- return;
- }
-
- CERT_CHAIN_PARA parameters;
- memset(&parameters, 0, sizeof(parameters));
- parameters.cbSize = sizeof(parameters);
- // set key usage constraint
- parameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
- parameters.RequestedUsage.Usage.cUsageIdentifier = 1;
- LPSTR oid = (LPSTR)(mode == QSslSocket::SslClientMode ? szOID_PKIX_KP_SERVER_AUTH : szOID_PKIX_KP_CLIENT_AUTH);
- parameters.RequestedUsage.Usage.rgpszUsageIdentifier = &oid;
-
-#ifdef QSSLSOCKET_DEBUG
- QElapsedTimer stopwatch;
- stopwatch.start();
-#endif
- PCCERT_CHAIN_CONTEXT chain;
- BOOL result = CertGetCertificateChain(
- 0, //default engine
- wincert,
- 0, //current date/time
- 0, //default store
- &parameters,
- 0, //default dwFlags
- 0, //reserved
- &chain);
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "QWindowsCaRootFetcher" << stopwatch.elapsed() << "ms to get chain";
-#endif
-
- QSslCertificate trustedRoot;
- if (result) {
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "QWindowsCaRootFetcher - examining windows chains";
- if (chain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR)
- qCDebug(lcSsl) << " - TRUSTED";
- else
- qCDebug(lcSsl) << " - NOT TRUSTED" << chain->TrustStatus.dwErrorStatus;
- if (chain->TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED)
- qCDebug(lcSsl) << " - SELF SIGNED";
- qCDebug(lcSsl) << "QSslSocketBackendPrivate::fetchCaRootForCert - dumping simple chains";
- for (unsigned int i = 0; i < chain->cChain; i++) {
- if (chain->rgpChain[i]->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR)
- qCDebug(lcSsl) << " - TRUSTED SIMPLE CHAIN" << i;
- else
- qCDebug(lcSsl) << " - UNTRUSTED SIMPLE CHAIN" << i << "reason:" << chain->rgpChain[i]->TrustStatus.dwErrorStatus;
- for (unsigned int j = 0; j < chain->rgpChain[i]->cElement; j++) {
- QSslCertificate foundCert(QByteArray((const char *)chain->rgpChain[i]->rgpElement[j]->pCertContext->pbCertEncoded
- , chain->rgpChain[i]->rgpElement[j]->pCertContext->cbCertEncoded), QSsl::Der);
- qCDebug(lcSsl) << " - " << foundCert;
- }
- }
- qCDebug(lcSsl) << " - and" << chain->cLowerQualityChainContext << "low quality chains"; //expect 0, we haven't asked for them
-#endif
-
- //based on http://msdn.microsoft.com/en-us/library/windows/desktop/aa377182%28v=vs.85%29.aspx
- //about the final chain rgpChain[cChain-1] which must begin with a trusted root to be valid
- if (chain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR
- && chain->cChain > 0) {
- const PCERT_SIMPLE_CHAIN finalChain = chain->rgpChain[chain->cChain - 1];
- // http://msdn.microsoft.com/en-us/library/windows/desktop/aa377544%28v=vs.85%29.aspx
- // rgpElement[0] is the end certificate chain element. rgpElement[cElement-1] is the self-signed "root" certificate element.
- if (finalChain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR
- && finalChain->cElement > 0) {
- trustedRoot = QSslCertificate(QByteArray((const char *)finalChain->rgpElement[finalChain->cElement - 1]->pCertContext->pbCertEncoded
- , finalChain->rgpElement[finalChain->cElement - 1]->pCertContext->cbCertEncoded), QSsl::Der);
- }
- }
- CertFreeCertificateChain(chain);
- }
- CertFreeCertificateContext(wincert);
-
- emit finished(cert, trustedRoot);
- deleteLater();
-}
#endif
void QSslSocketBackendPrivate::disconnectFromHost()
@@ -1406,7 +1360,7 @@ QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &
q_X509_STORE_set_verify_cb(certStore, q_X509Callback);
// Build the chain of intermediate certificates
- STACK_OF(X509) *intermediates = 0;
+ STACK_OF(X509) *intermediates = nullptr;
if (certificateChain.length() > 1) {
intermediates = (STACK_OF(X509) *) q_OPENSSL_sk_new_null();
@@ -1499,9 +1453,10 @@ bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device,
BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pkcs12data.constData()), pkcs12data.size());
// Create the PKCS#12 object
- PKCS12 *p12 = q_d2i_PKCS12_bio(bio, 0);
+ PKCS12 *p12 = q_d2i_PKCS12_bio(bio, nullptr);
if (!p12) {
- qCWarning(lcSsl, "Unable to read PKCS#12 structure, %s", q_ERR_error_string(q_ERR_get_error(), 0));
+ qCWarning(lcSsl, "Unable to read PKCS#12 structure, %s",
+ q_ERR_error_string(q_ERR_get_error(), nullptr));
q_BIO_free(bio);
return false;
}
@@ -1509,10 +1464,11 @@ bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device,
// Extract the data
EVP_PKEY *pkey = nullptr;
X509 *x509;
- STACK_OF(X509) *ca = 0;
+ STACK_OF(X509) *ca = nullptr;
if (!q_PKCS12_parse(p12, passPhrase.constData(), &pkey, &x509, &ca)) {
- qCWarning(lcSsl, "Unable to parse PKCS#12 structure, %s", q_ERR_error_string(q_ERR_get_error(), 0));
+ qCWarning(lcSsl, "Unable to parse PKCS#12 structure, %s",
+ q_ERR_error_string(q_ERR_get_error(), nullptr));
q_PKCS12_free(p12);
q_BIO_free(bio);
return false;
diff --git a/src/network/ssl/qsslsocket_openssl11.cpp b/src/network/ssl/qsslsocket_openssl11.cpp
index d028f5fc00..2a2667bd48 100644
--- a/src/network/ssl/qsslsocket_openssl11.cpp
+++ b/src/network/ssl/qsslsocket_openssl11.cpp
@@ -88,8 +88,6 @@ bool QSslSocketPrivate::ensureLibraryLoaded()
const QMutexLocker locker(qt_opensslInitMutex);
if (!s_libraryLoaded) {
- s_libraryLoaded = true;
-
// Initialize OpenSSL.
if (q_OPENSSL_init_ssl(0, nullptr) != 1)
return false;
@@ -105,6 +103,8 @@ bool QSslSocketPrivate::ensureLibraryLoaded()
qWarning("Random number generator not seeded, disabling SSL support");
return false;
}
+
+ s_libraryLoaded = true;
}
return true;
}
@@ -248,7 +248,7 @@ void QSslSocketBackendPrivate::continueHandshake()
// we could not agree -> be conservative and use HTTP/1.1
configuration.nextNegotiatedProtocol = QByteArrayLiteral("http/1.1");
} else {
- const unsigned char *proto = 0;
+ const unsigned char *proto = nullptr;
unsigned int proto_len = 0;
q_SSL_get0_alpn_selected(ssl, &proto, &proto_len);
diff --git a/src/network/ssl/qsslsocket_openssl11_symbols_p.h b/src/network/ssl/qsslsocket_openssl11_symbols_p.h
index ac8d46ce6d..844c3437be 100644
--- a/src/network/ssl/qsslsocket_openssl11_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl11_symbols_p.h
@@ -128,6 +128,45 @@ long q_OpenSSL_version_num();
const char *q_OpenSSL_version(int type);
unsigned long q_SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *session);
+unsigned long q_SSL_set_options(SSL *s, unsigned long op);
+
+#if QT_CONFIG(dtls)
+// Functions and types required for DTLS support:
+extern "C"
+{
+
+typedef int (*CookieVerifyCallback)(SSL *, const unsigned char *, unsigned);
+typedef int (*DgramWriteCallback) (BIO *, const char *, int);
+typedef int (*DgramReadCallback) (BIO *, char *, int);
+typedef int (*DgramPutsCallback) (BIO *, const char *);
+typedef long (*DgramCtrlCallback) (BIO *, int, long, void *);
+typedef int (*DgramCreateCallback) (BIO *);
+typedef int (*DgramDestroyCallback) (BIO *);
+
+}
+
+int q_DTLSv1_listen(SSL *s, BIO_ADDR *client);
+BIO_ADDR *q_BIO_ADDR_new();
+void q_BIO_ADDR_free(BIO_ADDR *ap);
+
+// API we need for a custom dgram BIO:
+
+BIO_METHOD *q_BIO_meth_new(int type, const char *name);
+void q_BIO_meth_free(BIO_METHOD *biom);
+int q_BIO_meth_set_write(BIO_METHOD *biom, DgramWriteCallback);
+int q_BIO_meth_set_read(BIO_METHOD *biom, DgramReadCallback);
+int q_BIO_meth_set_puts(BIO_METHOD *biom, DgramPutsCallback);
+int q_BIO_meth_set_ctrl(BIO_METHOD *biom, DgramCtrlCallback);
+int q_BIO_meth_set_create(BIO_METHOD *biom, DgramCreateCallback);
+int q_BIO_meth_set_destroy(BIO_METHOD *biom, DgramDestroyCallback);
+
+#endif // dtls
+
+void q_BIO_set_data(BIO *a, void *ptr);
+void *q_BIO_get_data(BIO *a);
+void q_BIO_set_init(BIO *a, int init);
+int q_BIO_get_shutdown(BIO *a);
+void q_BIO_set_shutdown(BIO *a, int shut);
#define q_SSL_CTX_set_min_proto_version(ctx, version) \
q_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, version, nullptr)
diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h
index 2a800cdc34..c16b9d5f76 100644
--- a/src/network/ssl/qsslsocket_openssl_p.h
+++ b/src/network/ssl/qsslsocket_openssl_p.h
@@ -131,6 +131,8 @@ public:
static int s_indexForSSLExtraData; // index used in SSL_get_ex_data to get the matching QSslSocketBackendPrivate
#endif
+ bool inSetAndEmitError = false;
+
// Platform specific functions
void startClientEncryption() override;
void startServerEncryption() override;
@@ -159,24 +161,9 @@ public:
QSslKey *key, QSslCertificate *cert,
QList<QSslCertificate> *caCertificates,
const QByteArray &passPhrase);
-};
-#ifdef Q_OS_WIN
-class QWindowsCaRootFetcher : public QObject
-{
- Q_OBJECT;
-public:
- QWindowsCaRootFetcher(const QSslCertificate &certificate, QSslSocket::SslMode sslMode);
- ~QWindowsCaRootFetcher();
-public slots:
- void start();
-signals:
- void finished(QSslCertificate brokenChain, QSslCertificate caroot);
-private:
- QSslCertificate cert;
- QSslSocket::SslMode mode;
+ static QString msgErrorsDuringHandshake();
};
-#endif
QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index 59c93677dd..5482440b98 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -63,7 +63,9 @@
# include <QtCore/qlibrary.h>
#endif
#include <QtCore/qmutex.h>
+#if QT_CONFIG(thread)
#include <private/qmutexpool_p.h>
+#endif
#include <QtCore/qdatetime.h>
#if defined(Q_OS_UNIX)
#include <QtCore/qdir.h>
@@ -141,11 +143,11 @@ void qsslSocketCannotResolveSymbolWarning(const char *functionName)
// Below are the functions first introduced in version 1.1:
-DEFINEFUNC(const unsigned char *, ASN1_STRING_get0_data, const ASN1_STRING *a, a, return 0, return)
+DEFINEFUNC(const unsigned char *, ASN1_STRING_get0_data, const ASN1_STRING *a, a, return nullptr, return)
DEFINEFUNC2(int, OPENSSL_init_ssl, uint64_t opts, opts, const OPENSSL_INIT_SETTINGS *settings, settings, return 0, return)
DEFINEFUNC2(int, OPENSSL_init_crypto, uint64_t opts, opts, const OPENSSL_INIT_SETTINGS *settings, settings, return 0, return)
-DEFINEFUNC(BIO *, BIO_new, const BIO_METHOD *a, a, return 0, return)
-DEFINEFUNC(const BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return 0, return)
+DEFINEFUNC(BIO *, BIO_new, const BIO_METHOD *a, a, return nullptr, return)
+DEFINEFUNC(const BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return nullptr, return)
DEFINEFUNC2(int, BN_is_word, BIGNUM *a, a, BN_ULONG w, w, return 0, return)
DEFINEFUNC(int, EVP_CIPHER_CTX_reset, EVP_CIPHER_CTX *c, c, return 0, return)
DEFINEFUNC(int, EVP_PKEY_base_id, EVP_PKEY *a, a, return NID_undef, return)
@@ -153,45 +155,66 @@ DEFINEFUNC(int, RSA_bits, RSA *a, a, return 0, return)
DEFINEFUNC(int, DSA_bits, DSA *a, a, return 0, return)
DEFINEFUNC(int, OPENSSL_sk_num, OPENSSL_STACK *a, a, return -1, return)
DEFINEFUNC2(void, OPENSSL_sk_pop_free, OPENSSL_STACK *a, a, void (*b)(void*), b, return, DUMMYARG)
-DEFINEFUNC(OPENSSL_STACK *, OPENSSL_sk_new_null, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(OPENSSL_STACK *, OPENSSL_sk_new_null, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC2(void, OPENSSL_sk_push, OPENSSL_STACK *a, a, void *b, b, return, DUMMYARG)
DEFINEFUNC(void, OPENSSL_sk_free, OPENSSL_STACK *a, a, return, DUMMYARG)
-DEFINEFUNC2(void *, OPENSSL_sk_value, OPENSSL_STACK *a, a, int b, b, return 0, return)
+DEFINEFUNC2(void *, OPENSSL_sk_value, OPENSSL_STACK *a, a, int b, b, return nullptr, return)
DEFINEFUNC(int, SSL_session_reused, SSL *a, a, return 0, return)
DEFINEFUNC2(unsigned long, SSL_CTX_set_options, SSL_CTX *ctx, ctx, unsigned long op, op, return 0, return)
DEFINEFUNC3(size_t, SSL_get_client_random, SSL *a, a, unsigned char *out, out, size_t outlen, outlen, return 0, return)
DEFINEFUNC3(size_t, SSL_SESSION_get_master_key, const SSL_SESSION *ses, ses, unsigned char *out, out, size_t outlen, outlen, return 0, return)
DEFINEFUNC6(int, CRYPTO_get_ex_new_index, int class_index, class_index, long argl, argl, void *argp, argp, CRYPTO_EX_new *new_func, new_func, CRYPTO_EX_dup *dup_func, dup_func, CRYPTO_EX_free *free_func, free_func, return -1, return)
+DEFINEFUNC2(unsigned long, SSL_set_options, SSL *ssl, ssl, unsigned long op, op, return 0, return)
-DEFINEFUNC(const SSL_METHOD *, TLS_method, DUMMYARG, DUMMYARG, return 0, return)
-DEFINEFUNC(const SSL_METHOD *, TLS_client_method, DUMMYARG, DUMMYARG, return 0, return)
-DEFINEFUNC(const SSL_METHOD *, TLS_server_method, DUMMYARG, DUMMYARG, return 0, return)
-DEFINEFUNC(ASN1_TIME *, X509_getm_notBefore, X509 *a, a, return 0, return)
-DEFINEFUNC(ASN1_TIME *, X509_getm_notAfter, X509 *a, a, return 0, return)
+DEFINEFUNC(const SSL_METHOD *, TLS_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const SSL_METHOD *, TLS_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const SSL_METHOD *, TLS_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(ASN1_TIME *, X509_getm_notBefore, X509 *a, a, return nullptr, return)
+DEFINEFUNC(ASN1_TIME *, X509_getm_notAfter, X509 *a, a, return nullptr, return)
DEFINEFUNC(long, X509_get_version, X509 *a, a, return -1, return)
-DEFINEFUNC(EVP_PKEY *, X509_get_pubkey, X509 *a, a, return 0, return)
+DEFINEFUNC(EVP_PKEY *, X509_get_pubkey, X509 *a, a, return nullptr, return)
DEFINEFUNC2(void, X509_STORE_set_verify_cb, X509_STORE *a, a, X509_STORE_CTX_verify_cb verify_cb, verify_cb, return, DUMMYARG)
-DEFINEFUNC(STACK_OF(X509) *, X509_STORE_CTX_get0_chain, X509_STORE_CTX *a, a, return 0, return)
+DEFINEFUNC(STACK_OF(X509) *, X509_STORE_CTX_get0_chain, X509_STORE_CTX *a, a, return nullptr, return)
DEFINEFUNC3(void, CRYPTO_free, void *str, str, const char *file, file, int line, line, return, DUMMYARG)
DEFINEFUNC(long, OpenSSL_version_num, void, DUMMYARG, return 0, return)
-DEFINEFUNC(const char *, OpenSSL_version, int a, a, return 0, return)
+DEFINEFUNC(const char *, OpenSSL_version, int a, a, return nullptr, return)
DEFINEFUNC(unsigned long, SSL_SESSION_get_ticket_lifetime_hint, const SSL_SESSION *session, session, return 0, return)
DEFINEFUNC4(void, DH_get0_pqg, const DH *dh, dh, const BIGNUM **p, p, const BIGNUM **q, q, const BIGNUM **g, g, return, DUMMYARG)
DEFINEFUNC(int, DH_bits, DH *dh, dh, return 0, return)
+#if QT_CONFIG(dtls)
+DEFINEFUNC2(int, DTLSv1_listen, SSL *s, s, BIO_ADDR *c, c, return -1, return)
+DEFINEFUNC(BIO_ADDR *, BIO_ADDR_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(void, BIO_ADDR_free, BIO_ADDR *ap, ap, return, DUMMYARG)
+DEFINEFUNC2(BIO_METHOD *, BIO_meth_new, int type, type, const char *name, name, return nullptr, return)
+DEFINEFUNC(void, BIO_meth_free, BIO_METHOD *biom, biom, return, DUMMYARG)
+DEFINEFUNC2(int, BIO_meth_set_write, BIO_METHOD *biom, biom, DgramWriteCallback write, write, return 0, return)
+DEFINEFUNC2(int, BIO_meth_set_read, BIO_METHOD *biom, biom, DgramReadCallback read, read, return 0, return)
+DEFINEFUNC2(int, BIO_meth_set_puts, BIO_METHOD *biom, biom, DgramPutsCallback puts, puts, return 0, return)
+DEFINEFUNC2(int, BIO_meth_set_ctrl, BIO_METHOD *biom, biom, DgramCtrlCallback ctrl, ctrl, return 0, return)
+DEFINEFUNC2(int, BIO_meth_set_create, BIO_METHOD *biom, biom, DgramCreateCallback crt, crt, return 0, return)
+DEFINEFUNC2(int, BIO_meth_set_destroy, BIO_METHOD *biom, biom, DgramDestroyCallback dtr, dtr, return 0, return)
+#endif // dtls
+
+DEFINEFUNC2(void, BIO_set_data, BIO *a, a, void *ptr, ptr, return, DUMMYARG)
+DEFINEFUNC(void *, BIO_get_data, BIO *a, a, return nullptr, return)
+DEFINEFUNC2(void, BIO_set_init, BIO *a, a, int init, init, return, DUMMYARG)
+DEFINEFUNC(int, BIO_get_shutdown, BIO *a, a, return -1, return)
+DEFINEFUNC2(void, BIO_set_shutdown, BIO *a, a, int shut, shut, return, DUMMYARG)
+
#else // QT_CONFIG(opensslv11)
// Functions below are either deprecated or removed in OpenSSL >= 1.1:
-DEFINEFUNC(unsigned char *, ASN1_STRING_data, ASN1_STRING *a, a, return 0, return)
+DEFINEFUNC(unsigned char *, ASN1_STRING_data, ASN1_STRING *a, a, return nullptr, return)
#ifdef SSLEAY_MACROS
-DEFINEFUNC3(void *, ASN1_dup, i2d_of_void *a, a, d2i_of_void *b, b, char *c, c, return 0, return)
+DEFINEFUNC3(void *, ASN1_dup, i2d_of_void *a, a, d2i_of_void *b, b, char *c, c, return nullptr, return)
#endif
-DEFINEFUNC2(BIO *, BIO_new_file, const char *filename, filename, const char *mode, mode, return 0, return)
+DEFINEFUNC2(BIO *, BIO_new_file, const char *filename, filename, const char *mode, mode, return nullptr, return)
DEFINEFUNC(void, ERR_clear_error, DUMMYARG, DUMMYARG, return, DUMMYARG)
-DEFINEFUNC(BIO *, BIO_new, BIO_METHOD *a, a, return 0, return)
-DEFINEFUNC(BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return 0, return)
+DEFINEFUNC(BIO *, BIO_new, BIO_METHOD *a, a, return nullptr, return)
+DEFINEFUNC(BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return nullptr, return)
DEFINEFUNC(int, CRYPTO_num_locks, DUMMYARG, DUMMYARG, return 0, return)
DEFINEFUNC(void, CRYPTO_set_locking_callback, void (*a)(int, int, const char *, int), a, return, DUMMYARG)
DEFINEFUNC(void, CRYPTO_set_id_callback, unsigned long (*a)(), a, return, DUMMYARG)
@@ -202,23 +225,23 @@ DEFINEFUNC(void, EVP_CIPHER_CTX_cleanup, EVP_CIPHER_CTX *a, a, return, DUMMYARG)
DEFINEFUNC(void, EVP_CIPHER_CTX_init, EVP_CIPHER_CTX *a, a, return, DUMMYARG)
#ifdef SSLEAY_MACROS
-DEFINEFUNC6(void *, PEM_ASN1_read_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return 0, return)
-DEFINEFUNC6(void *, PEM_ASN1_write_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return 0, return)
+DEFINEFUNC6(void *, PEM_ASN1_read_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return nullptr, return)
+DEFINEFUNC6(void *, PEM_ASN1_write_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return nullptr, return)
#endif // SSLEAY_MACROS
DEFINEFUNC(int, sk_num, STACK *a, a, return -1, return)
DEFINEFUNC2(void, sk_pop_free, STACK *a, a, void (*b)(void*), b, return, DUMMYARG)
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
-DEFINEFUNC(_STACK *, sk_new_null, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(_STACK *, sk_new_null, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC2(void, sk_push, _STACK *a, a, void *b, b, return, DUMMYARG)
DEFINEFUNC(void, sk_free, _STACK *a, a, return, DUMMYARG)
-DEFINEFUNC2(void *, sk_value, STACK *a, a, int b, b, return 0, return)
+DEFINEFUNC2(void *, sk_value, STACK *a, a, int b, b, return nullptr, return)
#else
-DEFINEFUNC(STACK *, sk_new_null, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(STACK *, sk_new_null, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC2(void, sk_push, STACK *a, a, char *b, b, return, DUMMYARG)
DEFINEFUNC(void, sk_free, STACK *a, a, return, DUMMYARG)
-DEFINEFUNC2(char *, sk_value, STACK *a, a, int b, b, return 0, return)
+DEFINEFUNC2(char *, sk_value, STACK *a, a, int b, b, return nullptr, return)
#endif // OPENSSL_VERSION_NUMBER >= 0x10000000L
DEFINEFUNC(int, SSL_library_init, void, DUMMYARG, return -1, return)
@@ -230,49 +253,49 @@ DEFINEFUNC5(int, SSL_get_ex_new_index, long argl, argl, void *argp, argp, CRYPTO
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
#ifndef OPENSSL_NO_SSL2
-DEFINEFUNC(const SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
#endif
#ifndef OPENSSL_NO_SSL3_METHOD
-DEFINEFUNC(const SSL_METHOD *, SSLv3_client_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const SSL_METHOD *, SSLv3_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
#endif
-DEFINEFUNC(const SSL_METHOD *, SSLv23_client_method, DUMMYARG, DUMMYARG, return 0, return)
-DEFINEFUNC(const SSL_METHOD *, TLSv1_client_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const SSL_METHOD *, SSLv23_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const SSL_METHOD *, TLSv1_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
-DEFINEFUNC(const SSL_METHOD *, TLSv1_1_client_method, DUMMYARG, DUMMYARG, return 0, return)
-DEFINEFUNC(const SSL_METHOD *, TLSv1_2_client_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const SSL_METHOD *, TLSv1_1_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const SSL_METHOD *, TLSv1_2_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
#endif
#ifndef OPENSSL_NO_SSL2
-DEFINEFUNC(const SSL_METHOD *, SSLv2_server_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const SSL_METHOD *, SSLv2_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
#endif
#ifndef OPENSSL_NO_SSL3_METHOD
-DEFINEFUNC(const SSL_METHOD *, SSLv3_server_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const SSL_METHOD *, SSLv3_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
#endif
-DEFINEFUNC(const SSL_METHOD *, SSLv23_server_method, DUMMYARG, DUMMYARG, return 0, return)
-DEFINEFUNC(const SSL_METHOD *, TLSv1_server_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const SSL_METHOD *, SSLv23_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const SSL_METHOD *, TLSv1_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
-DEFINEFUNC(const SSL_METHOD *, TLSv1_1_server_method, DUMMYARG, DUMMYARG, return 0, return)
-DEFINEFUNC(const SSL_METHOD *, TLSv1_2_server_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const SSL_METHOD *, TLSv1_1_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const SSL_METHOD *, TLSv1_2_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
#endif
#else
#ifndef OPENSSL_NO_SSL2
-DEFINEFUNC(SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
#endif
#ifndef OPENSSL_NO_SSL3_METHOD
-DEFINEFUNC(SSL_METHOD *, SSLv3_client_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(SSL_METHOD *, SSLv3_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
#endif
-DEFINEFUNC(SSL_METHOD *, SSLv23_client_method, DUMMYARG, DUMMYARG, return 0, return)
-DEFINEFUNC(SSL_METHOD *, TLSv1_client_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(SSL_METHOD *, SSLv23_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(SSL_METHOD *, TLSv1_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
#ifndef OPENSSL_NO_SSL2
-DEFINEFUNC(SSL_METHOD *, SSLv2_server_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(SSL_METHOD *, SSLv2_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
#endif
#ifndef OPENSSL_NO_SSL3_METHOD
-DEFINEFUNC(SSL_METHOD *, SSLv3_server_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(SSL_METHOD *, SSLv3_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
#endif
-DEFINEFUNC(SSL_METHOD *, SSLv23_server_method, DUMMYARG, DUMMYARG, return 0, return)
-DEFINEFUNC(SSL_METHOD *, TLSv1_server_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(SSL_METHOD *, SSLv23_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(SSL_METHOD *, TLSv1_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
#endif
-DEFINEFUNC(STACK_OF(X509) *, X509_STORE_CTX_get_chain, X509_STORE_CTX *a, a, return 0, return)
+DEFINEFUNC(STACK_OF(X509) *, X509_STORE_CTX_get_chain, X509_STORE_CTX *a, a, return nullptr, return)
#ifdef SSLEAY_MACROS
DEFINEFUNC2(int, i2d_DSAPrivateKey, const DSA *a, a, unsigned char **b, b, return -1, return)
@@ -280,17 +303,25 @@ DEFINEFUNC2(int, i2d_RSAPrivateKey, const RSA *a, a, unsigned char **b, b, retur
#ifndef OPENSSL_NO_EC
DEFINEFUNC2(int, i2d_ECPrivateKey, const EC_KEY *a, a, unsigned char **b, b, return -1, return)
#endif
-DEFINEFUNC3(RSA *, d2i_RSAPrivateKey, RSA **a, a, unsigned char **b, b, long c, c, return 0, return)
-DEFINEFUNC3(DSA *, d2i_DSAPrivateKey, DSA **a, a, unsigned char **b, b, long c, c, return 0, return)
+DEFINEFUNC3(RSA *, d2i_RSAPrivateKey, RSA **a, a, unsigned char **b, b, long c, c, return nullptr, return)
+DEFINEFUNC3(DSA *, d2i_DSAPrivateKey, DSA **a, a, unsigned char **b, b, long c, c, return nullptr, return)
#ifndef OPENSSL_NO_EC
-DEFINEFUNC3(EC_KEY *, d2i_ECPrivateKey, EC_KEY **a, a, unsigned char **b, b, long c, c, return 0, return)
+DEFINEFUNC3(EC_KEY *, d2i_ECPrivateKey, EC_KEY **a, a, unsigned char **b, b, long c, c, return nullptr, return)
#endif
#endif
-DEFINEFUNC(char *, CONF_get1_default_config_file, DUMMYARG, DUMMYARG, return 0, return)
+
+#if QT_CONFIG(dtls)
+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)
+#endif // dtls
+
+DEFINEFUNC(char *, CONF_get1_default_config_file, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC(void, OPENSSL_add_all_algorithms_noconf, void, DUMMYARG, return, DUMMYARG)
DEFINEFUNC(void, OPENSSL_add_all_algorithms_conf, void, DUMMYARG, return, DUMMYARG)
DEFINEFUNC(long, SSLeay, void, DUMMYARG, return 0, return)
-DEFINEFUNC(const char *, SSLeay_version, int a, a, return 0, return)
+DEFINEFUNC(const char *, SSLeay_version, int a, a, return nullptr, return)
#endif // QT_CONFIG(opensslv11)
@@ -299,22 +330,23 @@ DEFINEFUNC(int, ASN1_STRING_length, ASN1_STRING *a, a, return 0, return)
DEFINEFUNC2(int, ASN1_STRING_to_UTF8, unsigned char **a, a, ASN1_STRING *b, b, return 0, return)
DEFINEFUNC4(long, BIO_ctrl, BIO *a, a, int b, b, long c, c, void *d, d, return -1, return)
DEFINEFUNC(int, BIO_free, BIO *a, a, return 0, return)
-DEFINEFUNC2(BIO *, BIO_new_mem_buf, void *a, a, int b, b, return 0, return)
+DEFINEFUNC2(BIO *, BIO_new_mem_buf, void *a, a, int b, b, return nullptr, return)
DEFINEFUNC3(int, BIO_read, BIO *a, a, void *b, b, int c, c, return -1, return)
DEFINEFUNC3(int, BIO_write, BIO *a, a, const void *b, b, int c, c, return -1, return)
DEFINEFUNC(int, BN_num_bits, const BIGNUM *a, a, return 0, return)
DEFINEFUNC2(BN_ULONG, BN_mod_word, const BIGNUM *a, a, BN_ULONG w, w, return static_cast<BN_ULONG>(-1), return)
#ifndef OPENSSL_NO_EC
-DEFINEFUNC(const EC_GROUP*, EC_KEY_get0_group, const EC_KEY* k, k, return 0, return)
+DEFINEFUNC(const EC_GROUP*, EC_KEY_get0_group, const EC_KEY* k, k, return nullptr, return)
DEFINEFUNC(int, EC_GROUP_get_degree, const EC_GROUP* g, g, return 0, return)
#endif
-DEFINEFUNC(DSA *, DSA_new, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(DSA *, DSA_new, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC(void, DSA_free, DSA *a, a, return, DUMMYARG)
-DEFINEFUNC3(X509 *, d2i_X509, X509 **a, a, const unsigned char **b, b, long c, c, return 0, return)
-DEFINEFUNC2(char *, ERR_error_string, unsigned long a, a, char *b, b, return 0, return)
+DEFINEFUNC3(X509 *, d2i_X509, X509 **a, a, const unsigned char **b, b, long c, c, return nullptr, return)
+DEFINEFUNC2(char *, ERR_error_string, unsigned long a, a, char *b, b, return nullptr, return)
+DEFINEFUNC3(void, ERR_error_string_n, unsigned long e, e, char *b, b, size_t len, len, return, DUMMYARG)
DEFINEFUNC(unsigned long, ERR_get_error, DUMMYARG, DUMMYARG, return 0, return)
-DEFINEFUNC(EVP_CIPHER_CTX *, EVP_CIPHER_CTX_new, void, DUMMYARG, return 0, return)
+DEFINEFUNC(EVP_CIPHER_CTX *, EVP_CIPHER_CTX_new, void, DUMMYARG, return nullptr, return)
DEFINEFUNC(void, EVP_CIPHER_CTX_free, EVP_CIPHER_CTX *a, a, return, DUMMYARG)
DEFINEFUNC4(int, EVP_CIPHER_CTX_ctrl, EVP_CIPHER_CTX *ctx, ctx, int type, type, int arg, arg, void *ptr, ptr, return 0, return)
DEFINEFUNC2(int, EVP_CIPHER_CTX_set_key_length, EVP_CIPHER_CTX *ctx, ctx, int keylen, keylen, return 0, return)
@@ -323,13 +355,13 @@ DEFINEFUNC6(int, EVP_CipherInit_ex, EVP_CIPHER_CTX *ctx, ctx, const EVP_CIPHER *
DEFINEFUNC5(int, EVP_CipherUpdate, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, const unsigned char *in, in, int inl, inl, return 0, return)
DEFINEFUNC3(int, EVP_CipherFinal, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, return 0, return)
#ifndef OPENSSL_NO_DES
-DEFINEFUNC(const EVP_CIPHER *, EVP_des_cbc, DUMMYARG, DUMMYARG, return 0, return)
-DEFINEFUNC(const EVP_CIPHER *, EVP_des_ede3_cbc, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const EVP_CIPHER *, EVP_des_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const EVP_CIPHER *, EVP_des_ede3_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
#endif
#ifndef OPENSSL_NO_RC2
-DEFINEFUNC(const EVP_CIPHER *, EVP_rc2_cbc, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const EVP_CIPHER *, EVP_rc2_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
#endif
-DEFINEFUNC(const EVP_MD *, EVP_sha1, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const EVP_MD *, EVP_sha1, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC3(int, EVP_PKEY_assign, EVP_PKEY *a, a, int b, b, char *c, c, return -1, return)
DEFINEFUNC2(int, EVP_PKEY_set1_RSA, EVP_PKEY *a, a, RSA *b, b, return -1, return)
DEFINEFUNC2(int, EVP_PKEY_set1_DSA, EVP_PKEY *a, a, DSA *b, b, return -1, return)
@@ -337,16 +369,16 @@ DEFINEFUNC2(int, EVP_PKEY_set1_DSA, EVP_PKEY *a, a, DSA *b, b, return -1, return
DEFINEFUNC2(int, EVP_PKEY_set1_EC_KEY, EVP_PKEY *a, a, EC_KEY *b, b, return -1, return)
#endif
DEFINEFUNC(void, EVP_PKEY_free, EVP_PKEY *a, a, return, DUMMYARG)
-DEFINEFUNC(DSA *, EVP_PKEY_get1_DSA, EVP_PKEY *a, a, return 0, return)
-DEFINEFUNC(RSA *, EVP_PKEY_get1_RSA, EVP_PKEY *a, a, return 0, return)
+DEFINEFUNC(DSA *, EVP_PKEY_get1_DSA, EVP_PKEY *a, a, return nullptr, return)
+DEFINEFUNC(RSA *, EVP_PKEY_get1_RSA, EVP_PKEY *a, a, return nullptr, return)
#ifndef OPENSSL_NO_EC
-DEFINEFUNC(EC_KEY *, EVP_PKEY_get1_EC_KEY, EVP_PKEY *a, a, return 0, return)
+DEFINEFUNC(EC_KEY *, EVP_PKEY_get1_EC_KEY, EVP_PKEY *a, a, return nullptr, return)
#endif
-DEFINEFUNC(EVP_PKEY *, EVP_PKEY_new, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(EVP_PKEY *, EVP_PKEY_new, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC(int, EVP_PKEY_type, int a, a, return NID_undef, return)
DEFINEFUNC2(int, i2d_X509, X509 *a, a, unsigned char **b, b, return -1, return)
-DEFINEFUNC(const char *, OBJ_nid2sn, int a, a, return 0, return)
-DEFINEFUNC(const char *, OBJ_nid2ln, int a, a, return 0, return)
+DEFINEFUNC(const char *, OBJ_nid2sn, int a, a, return nullptr, return)
+DEFINEFUNC(const char *, OBJ_nid2ln, int a, a, return nullptr, return)
DEFINEFUNC(int, OBJ_sn2nid, const char *s, s, return 0, return)
DEFINEFUNC(int, OBJ_ln2nid, const char *s, s, return 0, return)
DEFINEFUNC3(int, i2t_ASN1_OBJECT, char *a, a, int b, b, ASN1_OBJECT *c, c, return -1, return)
@@ -355,24 +387,24 @@ DEFINEFUNC4(int, OBJ_obj2txt, char *a, a, int b, b, ASN1_OBJECT *c, c, int d, d,
DEFINEFUNC(int, OBJ_obj2nid, const ASN1_OBJECT *a, a, return NID_undef, return)
#ifndef SSLEAY_MACROS
-DEFINEFUNC4(EVP_PKEY *, PEM_read_bio_PrivateKey, BIO *a, a, EVP_PKEY **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
-DEFINEFUNC4(DSA *, PEM_read_bio_DSAPrivateKey, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
-DEFINEFUNC4(RSA *, PEM_read_bio_RSAPrivateKey, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
+DEFINEFUNC4(EVP_PKEY *, PEM_read_bio_PrivateKey, BIO *a, a, EVP_PKEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+DEFINEFUNC4(DSA *, PEM_read_bio_DSAPrivateKey, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+DEFINEFUNC4(RSA *, PEM_read_bio_RSAPrivateKey, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
#ifndef OPENSSL_NO_EC
-DEFINEFUNC4(EC_KEY *, PEM_read_bio_ECPrivateKey, BIO *a, a, EC_KEY **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
+DEFINEFUNC4(EC_KEY *, PEM_read_bio_ECPrivateKey, BIO *a, a, EC_KEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
#endif
-DEFINEFUNC4(DH *, PEM_read_bio_DHparams, BIO *a, a, DH **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
+DEFINEFUNC4(DH *, PEM_read_bio_DHparams, BIO *a, a, DH **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
DEFINEFUNC7(int, PEM_write_bio_DSAPrivateKey, BIO *a, a, DSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
DEFINEFUNC7(int, PEM_write_bio_RSAPrivateKey, BIO *a, a, RSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
#ifndef OPENSSL_NO_EC
DEFINEFUNC7(int, PEM_write_bio_ECPrivateKey, BIO *a, a, EC_KEY *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
#endif
#endif // !SSLEAY_MACROS
-DEFINEFUNC4(EVP_PKEY *, PEM_read_bio_PUBKEY, BIO *a, a, EVP_PKEY **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
-DEFINEFUNC4(DSA *, PEM_read_bio_DSA_PUBKEY, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
-DEFINEFUNC4(RSA *, PEM_read_bio_RSA_PUBKEY, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
+DEFINEFUNC4(EVP_PKEY *, PEM_read_bio_PUBKEY, BIO *a, a, EVP_PKEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+DEFINEFUNC4(DSA *, PEM_read_bio_DSA_PUBKEY, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
+DEFINEFUNC4(RSA *, PEM_read_bio_RSA_PUBKEY, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
#ifndef OPENSSL_NO_EC
-DEFINEFUNC4(EC_KEY *, PEM_read_bio_EC_PUBKEY, BIO *a, a, EC_KEY **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
+DEFINEFUNC4(EC_KEY *, PEM_read_bio_EC_PUBKEY, BIO *a, a, EC_KEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
#endif
DEFINEFUNC2(int, PEM_write_bio_DSA_PUBKEY, BIO *a, a, DSA *b, b, return 0, return)
DEFINEFUNC2(int, PEM_write_bio_RSA_PUBKEY, BIO *a, a, RSA *b, b, return 0, return)
@@ -381,20 +413,22 @@ DEFINEFUNC2(int, PEM_write_bio_EC_PUBKEY, BIO *a, a, EC_KEY *b, b, return 0, ret
#endif
DEFINEFUNC2(void, RAND_seed, const void *a, a, int b, b, return, DUMMYARG)
DEFINEFUNC(int, RAND_status, void, DUMMYARG, return -1, return)
-DEFINEFUNC(RSA *, RSA_new, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC2(int, RAND_bytes, unsigned char *b, b, int n, n, return 0, return)
+DEFINEFUNC(RSA *, RSA_new, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC(void, RSA_free, RSA *a, a, return, DUMMYARG)
DEFINEFUNC(int, SSL_accept, SSL *a, a, return -1, return)
DEFINEFUNC(int, SSL_clear, SSL *a, a, return -1, return)
-DEFINEFUNC3(char *, SSL_CIPHER_description, const SSL_CIPHER *a, a, char *b, b, int c, c, return 0, return)
+DEFINEFUNC3(char *, SSL_CIPHER_description, const SSL_CIPHER *a, a, char *b, b, int c, c, return nullptr, return)
DEFINEFUNC2(int, SSL_CIPHER_get_bits, const SSL_CIPHER *a, a, int *b, b, return 0, return)
+DEFINEFUNC(BIO *, SSL_get_rbio, const SSL *s, s, return nullptr, return)
DEFINEFUNC(int, SSL_connect, SSL *a, a, return -1, return)
DEFINEFUNC(int, SSL_CTX_check_private_key, const SSL_CTX *a, a, return -1, return)
DEFINEFUNC4(long, SSL_CTX_ctrl, SSL_CTX *a, a, int b, b, long c, c, void *d, d, return -1, return)
DEFINEFUNC(void, SSL_CTX_free, SSL_CTX *a, a, return, DUMMYARG)
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
-DEFINEFUNC(SSL_CTX *, SSL_CTX_new, const SSL_METHOD *a, a, return 0, return)
+DEFINEFUNC(SSL_CTX *, SSL_CTX_new, const SSL_METHOD *a, a, return nullptr, return)
#else
-DEFINEFUNC(SSL_CTX *, SSL_CTX_new, SSL_METHOD *a, a, return 0, return)
+DEFINEFUNC(SSL_CTX *, SSL_CTX_new, SSL_METHOD *a, a, return nullptr, return)
#endif
DEFINEFUNC2(int, SSL_CTX_set_cipher_list, SSL_CTX *a, a, const char *b, b, return -1, return)
DEFINEFUNC(int, SSL_CTX_set_default_verify_paths, SSL_CTX *a, a, return -1, return)
@@ -405,9 +439,9 @@ DEFINEFUNC3(int, SSL_CTX_use_certificate_file, SSL_CTX *a, a, const char *b, b,
DEFINEFUNC2(int, SSL_CTX_use_PrivateKey, SSL_CTX *a, a, EVP_PKEY *b, b, return -1, return)
DEFINEFUNC2(int, SSL_CTX_use_RSAPrivateKey, SSL_CTX *a, a, RSA *b, b, return -1, return)
DEFINEFUNC3(int, SSL_CTX_use_PrivateKey_file, SSL_CTX *a, a, const char *b, b, int c, c, return -1, return)
-DEFINEFUNC(X509_STORE *, SSL_CTX_get_cert_store, const SSL_CTX *a, a, return 0, return)
+DEFINEFUNC(X509_STORE *, SSL_CTX_get_cert_store, const SSL_CTX *a, a, return nullptr, return)
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
-DEFINEFUNC(SSL_CONF_CTX *, SSL_CONF_CTX_new, DUMMYARG, DUMMYARG, return 0, return);
+DEFINEFUNC(SSL_CONF_CTX *, SSL_CONF_CTX_new, DUMMYARG, DUMMYARG, return nullptr, return);
DEFINEFUNC(void, SSL_CONF_CTX_free, SSL_CONF_CTX *a, a, return ,return);
DEFINEFUNC2(void, SSL_CONF_CTX_set_ssl_ctx, SSL_CONF_CTX *a, a, SSL_CTX *b, b, return, return);
DEFINEFUNC2(unsigned int, SSL_CONF_CTX_set_flags, SSL_CONF_CTX *a, a, unsigned int b, b, return 0, return);
@@ -415,36 +449,37 @@ DEFINEFUNC(int, SSL_CONF_CTX_finish, SSL_CONF_CTX *a, a, return 0, return);
DEFINEFUNC3(int, SSL_CONF_cmd, SSL_CONF_CTX *a, a, const char *b, b, const char *c, c, return 0, return);
#endif
DEFINEFUNC(void, SSL_free, SSL *a, a, return, DUMMYARG)
-DEFINEFUNC(STACK_OF(SSL_CIPHER) *, SSL_get_ciphers, const SSL *a, a, return 0, return)
+DEFINEFUNC(STACK_OF(SSL_CIPHER) *, SSL_get_ciphers, const SSL *a, a, return nullptr, return)
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
-DEFINEFUNC(const SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return 0, return)
+DEFINEFUNC(const SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return nullptr, return)
#else
-DEFINEFUNC(SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return 0, return)
+DEFINEFUNC(SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return nullptr, return)
#endif
DEFINEFUNC(int, SSL_version, const SSL *a, a, return 0, return)
DEFINEFUNC2(int, SSL_get_error, SSL *a, a, int b, b, return -1, return)
-DEFINEFUNC(STACK_OF(X509) *, SSL_get_peer_cert_chain, SSL *a, a, return 0, return)
-DEFINEFUNC(X509 *, SSL_get_peer_certificate, SSL *a, a, return 0, return)
+DEFINEFUNC(STACK_OF(X509) *, SSL_get_peer_cert_chain, SSL *a, a, return nullptr, return)
+DEFINEFUNC(X509 *, SSL_get_peer_certificate, SSL *a, a, return nullptr, return)
#if OPENSSL_VERSION_NUMBER >= 0x00908000L
// 0.9.8 broke SC and BC by changing this function's signature.
DEFINEFUNC(long, SSL_get_verify_result, const SSL *a, a, return -1, return)
#else
DEFINEFUNC(long, SSL_get_verify_result, SSL *a, a, return -1, return)
#endif
-DEFINEFUNC(SSL *, SSL_new, SSL_CTX *a, a, return 0, return)
+DEFINEFUNC(SSL *, SSL_new, SSL_CTX *a, a, return nullptr, return)
DEFINEFUNC4(long, SSL_ctrl, SSL *a, a, int cmd, cmd, long larg, larg, void *parg, parg, return -1, return)
DEFINEFUNC3(int, SSL_read, SSL *a, a, void *b, b, int c, c, return -1, return)
DEFINEFUNC3(void, SSL_set_bio, SSL *a, a, BIO *b, b, BIO *c, c, return, DUMMYARG)
DEFINEFUNC(void, SSL_set_accept_state, SSL *a, a, return, DUMMYARG)
DEFINEFUNC(void, SSL_set_connect_state, SSL *a, a, return, DUMMYARG)
DEFINEFUNC(int, SSL_shutdown, SSL *a, a, return -1, return)
+DEFINEFUNC(int, SSL_get_shutdown, const SSL *ssl, ssl, return 0, return)
DEFINEFUNC2(int, SSL_set_session, SSL* to, to, SSL_SESSION *session, session, return -1, return)
DEFINEFUNC(void, SSL_SESSION_free, SSL_SESSION *ses, ses, return, DUMMYARG)
-DEFINEFUNC(SSL_SESSION*, SSL_get1_session, SSL *ssl, ssl, return 0, return)
-DEFINEFUNC(SSL_SESSION*, SSL_get_session, const SSL *ssl, ssl, return 0, return)
+DEFINEFUNC(SSL_SESSION*, SSL_get1_session, SSL *ssl, ssl, return nullptr, return)
+DEFINEFUNC(SSL_SESSION*, SSL_get_session, const SSL *ssl, ssl, return nullptr, return)
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
DEFINEFUNC3(int, SSL_set_ex_data, SSL *ssl, ssl, int idx, idx, void *arg, arg, return 0, return)
-DEFINEFUNC2(void *, SSL_get_ex_data, const SSL *ssl, ssl, int idx, idx, return NULL, return)
+DEFINEFUNC2(void *, SSL_get_ex_data, const SSL *ssl, ssl, int idx, idx, return nullptr, return)
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
DEFINEFUNC2(void, SSL_set_psk_client_callback, SSL* ssl, ssl, q_psk_client_callback_t callback, callback, return, DUMMYARG)
@@ -455,18 +490,18 @@ DEFINEFUNC3(int, SSL_write, SSL *a, a, const void *b, b, int c, c, return -1, re
DEFINEFUNC2(int, X509_cmp, X509 *a, a, X509 *b, b, return -1, return)
DEFINEFUNC4(int, X509_digest, const X509 *x509, x509, const EVP_MD *type, type, unsigned char *md, md, unsigned int *len, len, return -1, return)
#ifndef SSLEAY_MACROS
-DEFINEFUNC(X509 *, X509_dup, X509 *a, a, return 0, return)
+DEFINEFUNC(X509 *, X509_dup, X509 *a, a, return nullptr, return)
#endif
DEFINEFUNC2(void, X509_print, BIO *a, a, X509 *b, b, return, DUMMYARG);
-DEFINEFUNC(ASN1_OBJECT *, X509_EXTENSION_get_object, X509_EXTENSION *a, a, return 0, return)
+DEFINEFUNC(ASN1_OBJECT *, X509_EXTENSION_get_object, X509_EXTENSION *a, a, return nullptr, return)
DEFINEFUNC(void, X509_free, X509 *a, a, return, DUMMYARG)
-DEFINEFUNC2(X509_EXTENSION *, X509_get_ext, X509 *a, a, int b, b, return 0, return)
+DEFINEFUNC2(X509_EXTENSION *, X509_get_ext, X509 *a, a, int b, b, return nullptr, return)
DEFINEFUNC(int, X509_get_ext_count, X509 *a, a, return 0, return)
-DEFINEFUNC4(void *, X509_get_ext_d2i, X509 *a, a, int b, b, int *c, c, int *d, d, return 0, return)
-DEFINEFUNC(const X509V3_EXT_METHOD *, X509V3_EXT_get, X509_EXTENSION *a, a, return 0, return)
-DEFINEFUNC(void *, X509V3_EXT_d2i, X509_EXTENSION *a, a, return 0, return)
+DEFINEFUNC4(void *, X509_get_ext_d2i, X509 *a, a, int b, b, int *c, c, int *d, d, return nullptr, return)
+DEFINEFUNC(const X509V3_EXT_METHOD *, X509V3_EXT_get, X509_EXTENSION *a, a, return nullptr, return)
+DEFINEFUNC(void *, X509V3_EXT_d2i, X509_EXTENSION *a, a, return nullptr, return)
DEFINEFUNC(int, X509_EXTENSION_get_critical, X509_EXTENSION *a, a, return 0, return)
-DEFINEFUNC(ASN1_OCTET_STRING *, X509_EXTENSION_get_data, X509_EXTENSION *a, a, return 0, return)
+DEFINEFUNC(ASN1_OCTET_STRING *, X509_EXTENSION_get_data, X509_EXTENSION *a, a, return nullptr, return)
DEFINEFUNC(void, BASIC_CONSTRAINTS_free, BASIC_CONSTRAINTS *a, a, return, DUMMYARG)
DEFINEFUNC(void, AUTHORITY_KEYID_free, AUTHORITY_KEYID *a, a, return, DUMMYARG)
DEFINEFUNC(void, GENERAL_NAME_free, GENERAL_NAME *a, a, return, DUMMYARG)
@@ -476,28 +511,30 @@ DEFINEFUNC2(int, ASN1_STRING_print, BIO *a, a, const ASN1_STRING *b, b, return 0
DEFINEFUNC2(int, ASN1_STRING_print, BIO *a, a, ASN1_STRING *b, b, return 0, return)
#endif
DEFINEFUNC2(int, X509_check_issued, X509 *a, a, X509 *b, b, return -1, return)
-DEFINEFUNC(X509_NAME *, X509_get_issuer_name, X509 *a, a, return 0, return)
-DEFINEFUNC(X509_NAME *, X509_get_subject_name, X509 *a, a, return 0, return)
-DEFINEFUNC(ASN1_INTEGER *, X509_get_serialNumber, X509 *a, a, return 0, return)
+DEFINEFUNC(X509_NAME *, X509_get_issuer_name, X509 *a, a, return nullptr, return)
+DEFINEFUNC(X509_NAME *, X509_get_subject_name, X509 *a, a, return nullptr, return)
+DEFINEFUNC(ASN1_INTEGER *, X509_get_serialNumber, X509 *a, a, return nullptr, return)
DEFINEFUNC(int, X509_verify_cert, X509_STORE_CTX *a, a, return -1, return)
DEFINEFUNC(int, X509_NAME_entry_count, X509_NAME *a, a, return 0, return)
-DEFINEFUNC2(X509_NAME_ENTRY *, X509_NAME_get_entry, X509_NAME *a, a, int b, b, return 0, return)
-DEFINEFUNC(ASN1_STRING *, X509_NAME_ENTRY_get_data, X509_NAME_ENTRY *a, a, return 0, return)
-DEFINEFUNC(ASN1_OBJECT *, X509_NAME_ENTRY_get_object, X509_NAME_ENTRY *a, a, return 0, return)
-DEFINEFUNC(EVP_PKEY *, X509_PUBKEY_get, X509_PUBKEY *a, a, return 0, return)
+DEFINEFUNC2(X509_NAME_ENTRY *, X509_NAME_get_entry, X509_NAME *a, a, int b, b, return nullptr, return)
+DEFINEFUNC(ASN1_STRING *, X509_NAME_ENTRY_get_data, X509_NAME_ENTRY *a, a, return nullptr, return)
+DEFINEFUNC(ASN1_OBJECT *, X509_NAME_ENTRY_get_object, X509_NAME_ENTRY *a, a, return nullptr, return)
+DEFINEFUNC(EVP_PKEY *, X509_PUBKEY_get, X509_PUBKEY *a, a, return nullptr, return)
DEFINEFUNC(void, X509_STORE_free, X509_STORE *a, a, return, DUMMYARG)
-DEFINEFUNC(X509_STORE *, X509_STORE_new, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(X509_STORE *, X509_STORE_new, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC2(int, X509_STORE_add_cert, X509_STORE *a, a, X509 *b, b, return 0, return)
DEFINEFUNC(void, X509_STORE_CTX_free, X509_STORE_CTX *a, a, return, DUMMYARG)
DEFINEFUNC4(int, X509_STORE_CTX_init, X509_STORE_CTX *a, a, X509_STORE *b, b, X509 *c, c, STACK_OF(X509) *d, d, return -1, return)
DEFINEFUNC2(int, X509_STORE_CTX_set_purpose, X509_STORE_CTX *a, a, int b, b, return -1, return)
DEFINEFUNC(int, X509_STORE_CTX_get_error, X509_STORE_CTX *a, a, return -1, return)
DEFINEFUNC(int, X509_STORE_CTX_get_error_depth, X509_STORE_CTX *a, a, return -1, return)
-DEFINEFUNC(X509 *, X509_STORE_CTX_get_current_cert, X509_STORE_CTX *a, a, return 0, return)
-DEFINEFUNC(X509_STORE_CTX *, X509_STORE_CTX_new, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(X509 *, X509_STORE_CTX_get_current_cert, X509_STORE_CTX *a, a, return nullptr, return)
+DEFINEFUNC(X509_STORE_CTX *, X509_STORE_CTX_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC2(void *, X509_STORE_CTX_get_ex_data, X509_STORE_CTX *ctx, ctx, int idx, idx, return nullptr, return)
+DEFINEFUNC(int, SSL_get_ex_data_X509_STORE_CTX_idx, DUMMYARG, DUMMYARG, return -1, return)
DEFINEFUNC3(int, SSL_CTX_load_verify_locations, SSL_CTX *ctx, ctx, const char *CAfile, CAfile, const char *CApath, CApath, return 0, return)
DEFINEFUNC2(int, i2d_SSL_SESSION, SSL_SESSION *in, in, unsigned char **pp, pp, return 0, return)
-DEFINEFUNC3(SSL_SESSION *, d2i_SSL_SESSION, SSL_SESSION **a, a, const unsigned char **pp, pp, long length, length, return 0, return)
+DEFINEFUNC3(SSL_SESSION *, d2i_SSL_SESSION, SSL_SESSION **a, a, const unsigned char **pp, pp, long length, length, return nullptr, return)
#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)
DEFINEFUNC6(int, SSL_select_next_proto, unsigned char **out, out, unsigned char *outlen, outlen,
const unsigned char *in, in, unsigned int inlen, inlen,
@@ -524,15 +561,28 @@ DEFINEFUNC3(void, SSL_get0_alpn_selected, const SSL *s, s, const unsigned char *
unsigned *len, len, return, DUMMYARG)
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L ...
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
-DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return 0, return)
+
+// DTLS:
+#if QT_CONFIG(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)
+DEFINEFUNC(const SSL_METHOD *, DTLS_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(const SSL_METHOD *, DTLS_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
+#endif // dtls
+DEFINEFUNC2(void, BIO_set_flags, BIO *b, b, int flags, flags, return, DUMMYARG)
+DEFINEFUNC2(void, BIO_clear_flags, BIO *b, b, int flags, flags, return, DUMMYARG)
+DEFINEFUNC2(void *, BIO_get_ex_data, BIO *b, b, int idx, idx, return nullptr, return)
+DEFINEFUNC3(int, BIO_set_ex_data, BIO *b, b, int idx, idx, void *data, data, return -1, return)
+
+DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC(void, DH_free, DH *dh, dh, return, DUMMYARG)
-DEFINEFUNC3(DH *, d2i_DHparams, DH**a, a, const unsigned char **pp, pp, long length, length, return 0, return)
+DEFINEFUNC3(DH *, d2i_DHparams, DH**a, a, const unsigned char **pp, pp, long length, length, return nullptr, return)
DEFINEFUNC2(int, i2d_DHparams, DH *a, a, unsigned char **p, p, return -1, return)
DEFINEFUNC2(int, DH_check, DH *dh, dh, int *codes, codes, return 0, return)
-DEFINEFUNC3(BIGNUM *, BN_bin2bn, const unsigned char *s, s, int len, len, BIGNUM *ret, ret, return 0, return)
+DEFINEFUNC3(BIGNUM *, BN_bin2bn, const unsigned char *s, s, int len, len, BIGNUM *ret, ret, return nullptr, return)
#ifndef OPENSSL_NO_EC
-DEFINEFUNC(EC_KEY *, EC_KEY_dup, const EC_KEY *ec, ec, return 0, return)
-DEFINEFUNC(EC_KEY *, EC_KEY_new_by_curve_name, int nid, nid, return 0, return)
+DEFINEFUNC(EC_KEY *, EC_KEY_dup, const EC_KEY *ec, ec, return nullptr, return)
+DEFINEFUNC(EC_KEY *, EC_KEY_new_by_curve_name, int nid, nid, return nullptr, return)
DEFINEFUNC(void, EC_KEY_free, EC_KEY *ecdh, ecdh, return, DUMMYARG)
DEFINEFUNC2(size_t, EC_get_builtin_curves, EC_builtin_curve * r, r, size_t nitems, nitems, return 0, return)
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
@@ -542,7 +592,7 @@ DEFINEFUNC(int, EC_curve_nist2nid, const char *name, name, return 0, return)
DEFINEFUNC5(int, PKCS12_parse, PKCS12 *p12, p12, const char *pass, pass, EVP_PKEY **pkey, pkey, \
X509 **cert, cert, STACK_OF(X509) **ca, ca, return 1, return);
-DEFINEFUNC2(PKCS12 *, d2i_PKCS12_bio, BIO *bio, bio, PKCS12 **pkcs12, pkcs12, return 0, return);
+DEFINEFUNC2(PKCS12 *, d2i_PKCS12_bio, BIO *bio, bio, PKCS12 **pkcs12, pkcs12, return nullptr, return);
DEFINEFUNC(void, PKCS12_free, PKCS12 *pkcs12, pkcs12, return, DUMMYARG)
#define RESOLVEFUNC(func) \
@@ -626,7 +676,7 @@ static QStringList libraryPathList()
// search in .app/Contents/Frameworks
UInt32 packageType;
- CFBundleGetPackageInfo(CFBundleGetMainBundle(), &packageType, NULL);
+ CFBundleGetPackageInfo(CFBundleGetMainBundle(), &packageType, nullptr);
if (packageType == FOUR_CHAR_CODE('APPL')) {
QUrl bundleUrl = QUrl::fromCFURL(QCFType<CFURLRef>(CFBundleCopyBundleURL(CFBundleGetMainBundle())));
QUrl frameworksUrl = QUrl::fromCFURL(QCFType<CFURLRef>(CFBundleCopyPrivateFrameworksURL(CFBundleGetMainBundle())));
@@ -878,7 +928,7 @@ bool q_resolveOpenSslSymbols()
{
static bool symbolsResolved = false;
static bool triedToResolveSymbols = false;
-#ifndef QT_NO_THREAD
+#if QT_CONFIG(thread)
#if QT_CONFIG(opensslv11)
QMutexLocker locker(QMutexPool::globalInstanceGet((void *)&q_OPENSSL_init_ssl));
#else
@@ -920,6 +970,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_SESSION_get_master_key)
RESOLVEFUNC(SSL_session_reused)
RESOLVEFUNC(SSL_get_session)
+ RESOLVEFUNC(SSL_set_options)
RESOLVEFUNC(CRYPTO_get_ex_new_index)
RESOLVEFUNC(TLS_method)
RESOLVEFUNC(TLS_client_method)
@@ -946,6 +997,25 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(DH_bits)
RESOLVEFUNC(DSA_bits)
+#if QT_CONFIG(dtls)
+ RESOLVEFUNC(DTLSv1_listen)
+ RESOLVEFUNC(BIO_ADDR_new)
+ RESOLVEFUNC(BIO_ADDR_free)
+ RESOLVEFUNC(BIO_meth_new)
+ RESOLVEFUNC(BIO_meth_free)
+ RESOLVEFUNC(BIO_meth_set_write)
+ RESOLVEFUNC(BIO_meth_set_read)
+ RESOLVEFUNC(BIO_meth_set_puts)
+ RESOLVEFUNC(BIO_meth_set_ctrl)
+ RESOLVEFUNC(BIO_meth_set_create)
+ RESOLVEFUNC(BIO_meth_set_destroy)
+#endif // dtls
+
+ RESOLVEFUNC(BIO_set_data)
+ RESOLVEFUNC(BIO_get_data)
+ RESOLVEFUNC(BIO_set_init)
+ RESOLVEFUNC(BIO_get_shutdown)
+ RESOLVEFUNC(BIO_set_shutdown)
#else // !opensslv11
RESOLVEFUNC(ASN1_STRING_data)
@@ -1010,6 +1080,14 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(d2i_DSAPrivateKey)
RESOLVEFUNC(d2i_RSAPrivateKey)
#endif
+
+#if QT_CONFIG(dtls)
+ RESOLVEFUNC(DTLSv1_server_method)
+ RESOLVEFUNC(DTLSv1_client_method)
+ RESOLVEFUNC(DTLSv1_2_server_method)
+ RESOLVEFUNC(DTLSv1_2_client_method)
+#endif // dtls
+
RESOLVEFUNC(CONF_get1_default_config_file)
RESOLVEFUNC(OPENSSL_add_all_algorithms_noconf)
RESOLVEFUNC(OPENSSL_add_all_algorithms_conf)
@@ -1048,6 +1126,11 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(BIO_read)
RESOLVEFUNC(BIO_s_mem)
RESOLVEFUNC(BIO_write)
+ RESOLVEFUNC(BIO_set_flags)
+ RESOLVEFUNC(BIO_clear_flags)
+ RESOLVEFUNC(BIO_set_ex_data)
+ RESOLVEFUNC(BIO_get_ex_data)
+
#ifndef OPENSSL_NO_EC
RESOLVEFUNC(EC_KEY_get0_group)
RESOLVEFUNC(EC_GROUP_get_degree)
@@ -1060,6 +1143,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(DSA_new)
RESOLVEFUNC(DSA_free)
RESOLVEFUNC(ERR_error_string)
+ RESOLVEFUNC(ERR_error_string_n)
RESOLVEFUNC(ERR_get_error)
RESOLVEFUNC(EVP_CIPHER_CTX_new)
RESOLVEFUNC(EVP_CIPHER_CTX_free)
@@ -1127,10 +1211,12 @@ bool q_resolveOpenSslSymbols()
#endif
RESOLVEFUNC(RAND_seed)
RESOLVEFUNC(RAND_status)
+ RESOLVEFUNC(RAND_bytes)
RESOLVEFUNC(RSA_new)
RESOLVEFUNC(RSA_free)
RESOLVEFUNC(SSL_CIPHER_description)
RESOLVEFUNC(SSL_CIPHER_get_bits)
+ RESOLVEFUNC(SSL_get_rbio)
RESOLVEFUNC(SSL_CTX_check_private_key)
RESOLVEFUNC(SSL_CTX_ctrl)
RESOLVEFUNC(SSL_CTX_free)
@@ -1171,6 +1257,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_set_bio)
RESOLVEFUNC(SSL_set_connect_state)
RESOLVEFUNC(SSL_shutdown)
+ RESOLVEFUNC(SSL_get_shutdown)
RESOLVEFUNC(SSL_set_session)
RESOLVEFUNC(SSL_SESSION_free)
RESOLVEFUNC(SSL_get1_session)
@@ -1178,6 +1265,7 @@ bool q_resolveOpenSslSymbols()
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
RESOLVEFUNC(SSL_set_ex_data)
RESOLVEFUNC(SSL_get_ex_data)
+ RESOLVEFUNC(SSL_get_ex_data_X509_STORE_CTX_idx)
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
RESOLVEFUNC(SSL_set_psk_client_callback)
@@ -1201,6 +1289,8 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(X509_STORE_CTX_get_error_depth)
RESOLVEFUNC(X509_STORE_CTX_get_current_cert)
RESOLVEFUNC(X509_cmp)
+ RESOLVEFUNC(X509_STORE_CTX_get_ex_data)
+
#ifndef SSLEAY_MACROS
RESOLVEFUNC(X509_dup)
#endif
@@ -1239,6 +1329,12 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_CTX_set_alpn_select_cb)
RESOLVEFUNC(SSL_get0_alpn_selected)
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L ...
+#if QT_CONFIG(dtls)
+ RESOLVEFUNC(SSL_CTX_set_cookie_generate_cb)
+ RESOLVEFUNC(SSL_CTX_set_cookie_verify_cb)
+ RESOLVEFUNC(DTLS_server_method)
+ RESOLVEFUNC(DTLS_client_method)
+#endif // dtls
RESOLVEFUNC(DH_new)
RESOLVEFUNC(DH_free)
RESOLVEFUNC(d2i_DHparams)
diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h
index 68b519d74e..bfdfbf0efc 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -257,6 +257,7 @@ DSA *q_DSA_new();
void q_DSA_free(DSA *a);
X509 *q_d2i_X509(X509 **a, const unsigned char **b, long c);
char *q_ERR_error_string(unsigned long a, char *b);
+void q_ERR_error_string_n(unsigned long e, char *buf, size_t len);
unsigned long q_ERR_get_error();
EVP_CIPHER_CTX *q_EVP_CIPHER_CTX_new();
void q_EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *a);
@@ -331,12 +332,14 @@ int q_PEM_write_bio_EC_PUBKEY(BIO *a, EC_KEY *b);
#endif
void q_RAND_seed(const void *a, int b);
int q_RAND_status();
+int q_RAND_bytes(unsigned char *b, int n);
RSA *q_RSA_new();
void q_RSA_free(RSA *a);
int q_SSL_accept(SSL *a);
int q_SSL_clear(SSL *a);
char *q_SSL_CIPHER_description(const SSL_CIPHER *a, char *b, int c);
int q_SSL_CIPHER_get_bits(const SSL_CIPHER *a, int *b);
+BIO *q_SSL_get_rbio(const SSL *s);
int q_SSL_connect(SSL *a);
int q_SSL_CTX_check_private_key(const SSL_CTX *a);
long q_SSL_CTX_ctrl(SSL_CTX *a, int b, long c, void *d);
@@ -383,6 +386,7 @@ void q_SSL_set_bio(SSL *a, BIO *b, BIO *c);
void q_SSL_set_accept_state(SSL *a);
void q_SSL_set_connect_state(SSL *a);
int q_SSL_shutdown(SSL *a);
+int q_SSL_get_shutdown(const SSL *ssl);
int q_SSL_set_session(SSL *to, SSL_SESSION *session);
void q_SSL_SESSION_free(SSL_SESSION *ses);
SSL_SESSION *q_SSL_get1_session(SSL *ssl);
@@ -529,6 +533,40 @@ void q_SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data,
#endif
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
+#if QT_CONFIG(dtls)
+
+extern "C"
+{
+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);
+const SSL_METHOD *q_DTLS_server_method();
+const SSL_METHOD *q_DTLS_client_method();
+
+#endif // dtls
+
+void *q_X509_STORE_CTX_get_ex_data(X509_STORE_CTX *ctx, int idx);
+int q_SSL_get_ex_data_X509_STORE_CTX_idx();
+
+#if QT_CONFIG(dtls)
+#define q_DTLS_set_link_mtu(ssl, mtu) q_SSL_ctrl((ssl), DTLS_CTRL_SET_LINK_MTU, (mtu), nullptr)
+#define q_DTLSv1_get_timeout(ssl, arg) q_SSL_ctrl(ssl, DTLS_CTRL_GET_TIMEOUT, 0, arg)
+#define q_DTLSv1_handle_timeout(ssl) q_SSL_ctrl(ssl, DTLS_CTRL_HANDLE_TIMEOUT, 0, nullptr)
+#endif // dtls
+
+void q_BIO_set_flags(BIO *b, int flags);
+void q_BIO_clear_flags(BIO *b, int flags);
+void *q_BIO_get_ex_data(BIO *b, int idx);
+int q_BIO_set_ex_data(BIO *b, int idx, void *data);
+
+#define q_BIO_set_retry_read(b) q_BIO_set_flags(b, (BIO_FLAGS_READ|BIO_FLAGS_SHOULD_RETRY))
+#define q_BIO_set_retry_write(b) q_BIO_set_flags(b, (BIO_FLAGS_WRITE|BIO_FLAGS_SHOULD_RETRY))
+#define q_BIO_clear_retry_flags(b) q_BIO_clear_flags(b, (BIO_FLAGS_RWS|BIO_FLAGS_SHOULD_RETRY))
+#define q_BIO_set_app_data(s,arg) q_BIO_set_ex_data(s,0,arg)
+#define q_BIO_get_app_data(s) q_BIO_get_ex_data(s,0)
+
// Helper function
class QDateTime;
QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime);
diff --git a/src/network/ssl/qsslsocket_opensslpre11.cpp b/src/network/ssl/qsslsocket_opensslpre11.cpp
index 062e03f4e6..bc4fd9dc85 100644
--- a/src/network/ssl/qsslsocket_opensslpre11.cpp
+++ b/src/network/ssl/qsslsocket_opensslpre11.cpp
@@ -215,8 +215,6 @@ bool QSslSocketPrivate::ensureLibraryLoaded()
QMutexLocker locker(openssl_locks()->initLock());
if (!s_libraryLoaded) {
- s_libraryLoaded = true;
-
// Initialize OpenSSL.
q_CRYPTO_set_id_callback(id_function);
q_CRYPTO_set_locking_callback(locking_function);
@@ -235,6 +233,8 @@ bool QSslSocketPrivate::ensureLibraryLoaded()
qWarning("Random number generator not seeded, disabling SSL support");
return false;
}
+
+ s_libraryLoaded = true;
}
return true;
}
diff --git a/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h b/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h
index 9686d22b98..b7bac5d2a2 100644
--- a/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h
+++ b/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h
@@ -204,6 +204,7 @@ DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length);
#endif // SSLEAY_MACROS
#define q_SSL_CTX_set_options(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,(op),NULL)
+#define q_SSL_set_options(ssl,op) q_SSL_ctrl((ssl),SSL_CTRL_OPTIONS,(op),nullptr)
#define q_SKM_sk_num(type, st) ((int (*)(const STACK_OF(type) *))q_sk_num)(st)
#define q_SKM_sk_value(type, st,i) ((type * (*)(const STACK_OF(type) *, int))q_sk_value)(st, i)
#define q_X509_getm_notAfter(x) X509_get_notAfter(x)
@@ -226,5 +227,19 @@ void q_OPENSSL_add_all_algorithms_conf();
long q_SSLeay();
const char *q_SSLeay_version(int type);
+#if QT_CONFIG(dtls)
+// DTLS:
+extern "C"
+{
+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 // dtls
#endif // QSSLSOCKET_OPENSSL_PRE11_SYMBOLS_P_H
diff --git a/src/network/ssl/qwindowscarootfetcher.cpp b/src/network/ssl/qwindowscarootfetcher.cpp
new file mode 100644
index 0000000000..f83f96886c
--- /dev/null
+++ b/src/network/ssl/qwindowscarootfetcher.cpp
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** 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 "qwindowscarootfetcher_p.h"
+
+#include <QtCore/QThread>
+#include <QtGlobal>
+
+#ifdef QSSLSOCKET_DEBUG
+#include "qssl_p.h" // for debug categories
+#include <QtCore/QElapsedTimer>
+#endif
+
+#include "qsslsocket_p.h" // Transitively includes Wincrypt.h
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsCaRootFetcherThread : public QThread
+{
+public:
+ QWindowsCaRootFetcherThread()
+ {
+ qRegisterMetaType<QSslCertificate>();
+ setObjectName(QStringLiteral("QWindowsCaRootFetcher"));
+ start();
+ }
+ ~QWindowsCaRootFetcherThread()
+ {
+ quit();
+ wait(15500); // worst case, a running request can block for 15 seconds
+ }
+};
+
+Q_GLOBAL_STATIC(QWindowsCaRootFetcherThread, windowsCaRootFetcherThread);
+
+QWindowsCaRootFetcher::QWindowsCaRootFetcher(const QSslCertificate &certificate, QSslSocket::SslMode sslMode)
+ : cert(certificate), mode(sslMode)
+{
+ moveToThread(windowsCaRootFetcherThread());
+}
+
+QWindowsCaRootFetcher::~QWindowsCaRootFetcher()
+{
+}
+
+void QWindowsCaRootFetcher::start()
+{
+ QByteArray der = cert.toDer();
+ PCCERT_CONTEXT wincert = CertCreateCertificateContext(X509_ASN_ENCODING, (const BYTE *)der.constData(), der.length());
+ if (!wincert) {
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl, "QWindowsCaRootFetcher failed to convert certificate to windows form");
+#endif
+ emit finished(cert, QSslCertificate());
+ deleteLater();
+ return;
+ }
+
+ CERT_CHAIN_PARA parameters;
+ memset(&parameters, 0, sizeof(parameters));
+ parameters.cbSize = sizeof(parameters);
+ // set key usage constraint
+ parameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
+ parameters.RequestedUsage.Usage.cUsageIdentifier = 1;
+ LPSTR oid = (LPSTR)(mode == QSslSocket::SslClientMode ? szOID_PKIX_KP_SERVER_AUTH : szOID_PKIX_KP_CLIENT_AUTH);
+ parameters.RequestedUsage.Usage.rgpszUsageIdentifier = &oid;
+
+#ifdef QSSLSOCKET_DEBUG
+ QElapsedTimer stopwatch;
+ stopwatch.start();
+#endif
+ PCCERT_CHAIN_CONTEXT chain;
+ BOOL result = CertGetCertificateChain(
+ nullptr, //default engine
+ wincert,
+ nullptr, //current date/time
+ nullptr, //default store
+ &parameters,
+ 0, //default dwFlags
+ nullptr, //reserved
+ &chain);
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << "QWindowsCaRootFetcher" << stopwatch.elapsed() << "ms to get chain";
+#endif
+
+ QSslCertificate trustedRoot;
+ if (result) {
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << "QWindowsCaRootFetcher - examining windows chains";
+ if (chain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR)
+ qCDebug(lcSsl) << " - TRUSTED";
+ else
+ qCDebug(lcSsl) << " - NOT TRUSTED" << chain->TrustStatus.dwErrorStatus;
+ if (chain->TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED)
+ qCDebug(lcSsl) << " - SELF SIGNED";
+ qCDebug(lcSsl) << "QSslSocketBackendPrivate::fetchCaRootForCert - dumping simple chains";
+ for (unsigned int i = 0; i < chain->cChain; i++) {
+ if (chain->rgpChain[i]->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR)
+ qCDebug(lcSsl) << " - TRUSTED SIMPLE CHAIN" << i;
+ else
+ qCDebug(lcSsl) << " - UNTRUSTED SIMPLE CHAIN" << i << "reason:" << chain->rgpChain[i]->TrustStatus.dwErrorStatus;
+ for (unsigned int j = 0; j < chain->rgpChain[i]->cElement; j++) {
+ QSslCertificate foundCert(QByteArray((const char *)chain->rgpChain[i]->rgpElement[j]->pCertContext->pbCertEncoded
+ , chain->rgpChain[i]->rgpElement[j]->pCertContext->cbCertEncoded), QSsl::Der);
+ qCDebug(lcSsl) << " - " << foundCert;
+ }
+ }
+ qCDebug(lcSsl) << " - and" << chain->cLowerQualityChainContext << "low quality chains"; //expect 0, we haven't asked for them
+#endif
+
+ //based on http://msdn.microsoft.com/en-us/library/windows/desktop/aa377182%28v=vs.85%29.aspx
+ //about the final chain rgpChain[cChain-1] which must begin with a trusted root to be valid
+ if (chain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR
+ && chain->cChain > 0) {
+ const PCERT_SIMPLE_CHAIN finalChain = chain->rgpChain[chain->cChain - 1];
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/aa377544%28v=vs.85%29.aspx
+ // rgpElement[0] is the end certificate chain element. rgpElement[cElement-1] is the self-signed "root" certificate element.
+ if (finalChain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR
+ && finalChain->cElement > 0) {
+ trustedRoot = QSslCertificate(QByteArray((const char *)finalChain->rgpElement[finalChain->cElement - 1]->pCertContext->pbCertEncoded
+ , finalChain->rgpElement[finalChain->cElement - 1]->pCertContext->cbCertEncoded), QSsl::Der);
+ }
+ }
+ CertFreeCertificateChain(chain);
+ }
+ CertFreeCertificateContext(wincert);
+
+ emit finished(cert, trustedRoot);
+ deleteLater();
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qwindowscarootfetcher_p.h b/src/network/ssl/qwindowscarootfetcher_p.h
new file mode 100644
index 0000000000..181c309388
--- /dev/null
+++ b/src/network/ssl/qwindowscarootfetcher_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** 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 QWINDOWSCAROOTFETCHER_P_H
+#define QWINDOWSCAROOTFETCHER_P_H
+
+#include <QtCore/QtGlobal>
+#include <QtCore/QObject>
+
+#include "qsslsocket.h"
+#include "qsslcertificate.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 QWindowsCaRootFetcher : public QObject
+{
+ Q_OBJECT;
+public:
+ QWindowsCaRootFetcher(const QSslCertificate &certificate, QSslSocket::SslMode sslMode);
+ ~QWindowsCaRootFetcher();
+public slots:
+ void start();
+signals:
+ void finished(QSslCertificate brokenChain, QSslCertificate caroot);
+private:
+ QSslCertificate cert;
+ QSslSocket::SslMode mode;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSCAROOTFETCHER_P_H
diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri
index 2783effaf1..6975264038 100644
--- a/src/network/ssl/ssl.pri
+++ b/src/network/ssl/ssl.pri
@@ -1,12 +1,23 @@
# OpenSSL support; compile in QSslSocket.
+
+HEADERS += ssl/qasn1element_p.h \
+ ssl/qssl.h \
+ ssl/qssl_p.h \
+ ssl/qsslcertificate.h \
+ ssl/qsslcertificate_p.h \
+ ssl/qsslcertificateextension.h \
+ ssl/qsslcertificateextension_p.h
+
+SOURCES += ssl/qasn1element.cpp \
+ ssl/qssl.cpp \
+ ssl/qsslcertificate.cpp \
+ ssl/qsslcertificateextension.cpp
+
+!qtConfig(openssl): SOURCES += ssl/qsslcertificate_qt.cpp
+
qtConfig(ssl) {
- HEADERS += ssl/qasn1element_p.h \
- ssl/qssl.h \
- ssl/qssl_p.h \
- ssl/qsslcertificate.h \
- ssl/qsslcertificate_p.h \
- ssl/qsslconfiguration.h \
- ssl/qsslconfiguration_p.h \
+ HEADERS += ssl/qsslconfiguration.h \
+ ssl/qsslconfiguration_p.h \
ssl/qsslcipher.h \
ssl/qsslcipher_p.h \
ssl/qssldiffiehellmanparameters.h \
@@ -18,26 +29,19 @@ qtConfig(ssl) {
ssl/qsslsocket.h \
ssl/qsslsocket_p.h \
ssl/qsslpresharedkeyauthenticator.h \
- ssl/qsslpresharedkeyauthenticator_p.h \
- ssl/qsslcertificateextension.h \
- ssl/qsslcertificateextension_p.h
- SOURCES += ssl/qasn1element.cpp \
- ssl/qssl.cpp \
- ssl/qsslcertificate.cpp \
- ssl/qsslconfiguration.cpp \
+ ssl/qsslpresharedkeyauthenticator_p.h
+ SOURCES += ssl/qsslconfiguration.cpp \
ssl/qsslcipher.cpp \
ssl/qssldiffiehellmanparameters.cpp \
ssl/qsslellipticcurve.cpp \
ssl/qsslkey_p.cpp \
ssl/qsslerror.cpp \
ssl/qsslsocket.cpp \
- ssl/qsslpresharedkeyauthenticator.cpp \
- ssl/qsslcertificateextension.cpp
+ ssl/qsslpresharedkeyauthenticator.cpp
winrt {
HEADERS += ssl/qsslsocket_winrt_p.h
- SOURCES += ssl/qsslcertificate_qt.cpp \
- ssl/qsslcertificate_winrt.cpp \
+ SOURCES += ssl/qsslcertificate_winrt.cpp \
ssl/qssldiffiehellmanparameters_dummy.cpp \
ssl/qsslkey_qt.cpp \
ssl/qsslkey_winrt.cpp \
@@ -47,8 +51,7 @@ qtConfig(ssl) {
qtConfig(securetransport) {
HEADERS += ssl/qsslsocket_mac_p.h
- SOURCES += ssl/qsslcertificate_qt.cpp \
- ssl/qssldiffiehellmanparameters_dummy.cpp \
+ SOURCES += ssl/qssldiffiehellmanparameters_dummy.cpp \
ssl/qsslkey_qt.cpp \
ssl/qsslkey_mac.cpp \
ssl/qsslsocket_mac_shared.cpp \
@@ -56,6 +59,13 @@ qtConfig(ssl) {
ssl/qsslellipticcurve_dummy.cpp
}
+ qtConfig(dtls) {
+ HEADERS += ssl/qdtls.h \
+ ssl/qdtls_p.h
+
+ SOURCES += ssl/qdtls.cpp
+ }
+
qtConfig(openssl) {
HEADERS += ssl/qsslcontext_openssl_p.h \
ssl/qsslsocket_openssl_p.h \
@@ -66,7 +76,12 @@ qtConfig(ssl) {
ssl/qsslellipticcurve_openssl.cpp \
ssl/qsslkey_openssl.cpp \
ssl/qsslsocket_openssl.cpp \
- ssl/qsslcontext_openssl.cpp
+ ssl/qsslcontext_openssl.cpp \
+
+ qtConfig(dtls) {
+ HEADERS += ssl/qdtls_openssl_p.h
+ SOURCES += ssl/qdtls_openssl.cpp
+ }
qtConfig(opensslv11) {
HEADERS += ssl/qsslsocket_openssl11_symbols_p.h
@@ -95,6 +110,13 @@ qtConfig(ssl) {
QMAKE_USE_FOR_PRIVATE += openssl
else: \
QMAKE_USE_FOR_PRIVATE += openssl/nolink
- win32: LIBS_PRIVATE += -lcrypt32
+ win32 {
+ LIBS_PRIVATE += -lcrypt32
+ HEADERS += ssl/qwindowscarootfetcher_p.h
+ SOURCES += ssl/qwindowscarootfetcher.cpp
+ }
}
}
+
+HEADERS += ssl/qpassworddigestor.h
+SOURCES += ssl/qpassworddigestor.cpp