summaryrefslogtreecommitdiffstats
path: root/src/network/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/ssl')
-rw-r--r--src/network/ssl/qssl.cpp2
-rw-r--r--src/network/ssl/qssl.h4
-rw-r--r--src/network/ssl/qsslcertificate.h4
-rw-r--r--src/network/ssl/qsslcertificateextension.h4
-rw-r--r--src/network/ssl/qsslcipher.cpp2
-rw-r--r--src/network/ssl/qsslcipher.h4
-rw-r--r--src/network/ssl/qsslconfiguration.cpp56
-rw-r--r--src/network/ssl/qsslconfiguration.h9
-rw-r--r--src/network/ssl/qsslconfiguration_p.h7
-rw-r--r--src/network/ssl/qsslcontext.cpp309
-rw-r--r--src/network/ssl/qsslcontext_p.h88
-rw-r--r--src/network/ssl/qsslerror.cpp4
-rw-r--r--src/network/ssl/qsslerror.h6
-rw-r--r--src/network/ssl/qsslkey.h4
-rw-r--r--src/network/ssl/qsslsocket.cpp74
-rw-r--r--src/network/ssl/qsslsocket.h7
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp307
-rw-r--r--src/network/ssl/qsslsocket_openssl_p.h5
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp48
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h4
-rw-r--r--src/network/ssl/qsslsocket_p.h11
-rw-r--r--src/network/ssl/ssl.pri6
22 files changed, 675 insertions, 290 deletions
diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp
index 6fcd977759..4e33001d8d 100644
--- a/src/network/ssl/qssl.cpp
+++ b/src/network/ssl/qssl.cpp
@@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE
\enum QSsl::KeyType
Describes the two types of keys QSslKey supports.
-
+
\value PrivateKey A private key.
\value PublicKey A public key.
*/
diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h
index 80ccfadecb..06d80965e2 100644
--- a/src/network/ssl/qssl.h
+++ b/src/network/ssl/qssl.h
@@ -46,8 +46,6 @@
#include <QtCore/qglobal.h>
#include <QtCore/QFlags>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
@@ -106,6 +104,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QSsl::SslOptions)
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif // QSSL_H
diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h
index 23f9638981..988071eb9d 100644
--- a/src/network/ssl/qsslcertificate.h
+++ b/src/network/ssl/qsslcertificate.h
@@ -56,8 +56,6 @@
#include <QtCore/qmap.h>
#include <QtNetwork/qssl.h>
-QT_BEGIN_HEADER
-
#ifndef QT_NO_SSL
QT_BEGIN_NAMESPACE
@@ -164,6 +162,4 @@ Q_DECLARE_METATYPE(QSslCertificate)
#endif // QT_NO_SSL
-QT_END_HEADER
-
#endif
diff --git a/src/network/ssl/qsslcertificateextension.h b/src/network/ssl/qsslcertificateextension.h
index ce7ff54941..080b1ccc4e 100644
--- a/src/network/ssl/qsslcertificateextension.h
+++ b/src/network/ssl/qsslcertificateextension.h
@@ -47,8 +47,6 @@
#include <QtCore/qstring.h>
#include <QtCore/qvariant.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
@@ -85,8 +83,6 @@ Q_DECLARE_SHARED(QSslCertificateExtension)
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif // QSSLCERTIFICATEEXTENSION_H
diff --git a/src/network/ssl/qsslcipher.cpp b/src/network/ssl/qsslcipher.cpp
index 46131fc1aa..b045d2eba9 100644
--- a/src/network/ssl/qsslcipher.cpp
+++ b/src/network/ssl/qsslcipher.cpp
@@ -194,7 +194,7 @@ QString QSslCipher::keyExchangeMethod() const
{
return d->keyExchangeMethod;
}
-
+
/*!
Returns the cipher's authentication method as a QString.
*/
diff --git a/src/network/ssl/qsslcipher.h b/src/network/ssl/qsslcipher.h
index 93ae15c3f5..e351d7949b 100644
--- a/src/network/ssl/qsslcipher.h
+++ b/src/network/ssl/qsslcipher.h
@@ -47,8 +47,6 @@
#include <QtCore/qscopedpointer.h>
#include <QtNetwork/qssl.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
@@ -97,7 +95,5 @@ Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslCipher &cipher);
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif
diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp
index 0ae67b3c1f..afbd4fac77 100644
--- a/src/network/ssl/qsslconfiguration.cpp
+++ b/src/network/ssl/qsslconfiguration.cpp
@@ -173,7 +173,7 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const
return true;
return d->peerCertificate == other.d->peerCertificate &&
d->peerCertificateChain == other.d->peerCertificateChain &&
- d->localCertificate == other.d->localCertificate &&
+ d->localCertificateChain == other.d->localCertificateChain &&
d->privateKey == other.d->privateKey &&
d->sessionCipher == other.d->sessionCipher &&
d->ciphers == other.d->ciphers &&
@@ -212,7 +212,7 @@ bool QSslConfiguration::isNull() const
d->allowRootCertOnDemandLoading == true &&
d->caCertificates.count() == 0 &&
d->ciphers.count() == 0 &&
- d->localCertificate.isNull() &&
+ d->localCertificateChain.isEmpty() &&
d->privateKey.isNull() &&
d->peerCertificate.isNull() &&
d->peerCertificateChain.count() == 0 &&
@@ -313,6 +313,45 @@ void QSslConfiguration::setPeerVerifyDepth(int depth)
}
/*!
+ Returns the certificate chain to be presented to the peer during
+ the SSL handshake process.
+
+ \sa localCertificate()
+ \since 5.1
+*/
+QList<QSslCertificate> QSslConfiguration::localCertificateChain() const
+{
+ return d->localCertificateChain;
+}
+
+/*!
+ Sets the certificate chain to be presented to the peer during the
+ SSL handshake to be \a localChain.
+
+ Setting the certificate chain once the connection has been
+ established has no effect.
+
+ A certificate is the means of identification used in the SSL
+ process. The local certificate is used by the remote end to verify
+ the local user's identity against its list of Certification
+ Authorities. In most cases, such as in HTTP web browsing, only
+ servers identify to the clients, so the client does not send a
+ certificate.
+
+ Unlike QSslConfiguration::setLocalCertificate() this method allows
+ you to specify any intermediate certificates required in order to
+ validate your certificate. The first item in the list must be the
+ leaf certificate.
+
+ \sa localCertificateChain()
+ \since 5.1
+ */
+void QSslConfiguration::setLocalCertificateChain(const QList<QSslCertificate> &localChain)
+{
+ d->localCertificateChain = localChain;
+}
+
+/*!
Returns the certificate to be presented to the peer during the SSL
handshake process.
@@ -320,7 +359,9 @@ void QSslConfiguration::setPeerVerifyDepth(int depth)
*/
QSslCertificate QSslConfiguration::localCertificate() const
{
- return d->localCertificate;
+ if (d->localCertificateChain.isEmpty())
+ return QSslCertificate();
+ return d->localCertificateChain[0];
}
/*!
@@ -341,7 +382,8 @@ QSslCertificate QSslConfiguration::localCertificate() const
*/
void QSslConfiguration::setLocalCertificate(const QSslCertificate &certificate)
{
- d->localCertificate = certificate;
+ d->localCertificateChain = QList<QSslCertificate>();
+ d->localCertificateChain += certificate;
}
/*!
@@ -584,4 +626,10 @@ void QSslConfiguration::setDefaultConfiguration(const QSslConfiguration &configu
QSslConfigurationPrivate::setDefaultConfiguration(configuration);
}
+/*! \internal
+*/
+bool QSslConfigurationPrivate::peerSessionWasShared(const QSslConfiguration &configuration) {
+ return configuration.d->peerSessionShared;
+ }
+
QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h
index ae6e225ce5..0000382ed5 100644
--- a/src/network/ssl/qsslconfiguration.h
+++ b/src/network/ssl/qsslconfiguration.h
@@ -61,8 +61,6 @@
#include <QtNetwork/qsslsocket.h>
#include <QtNetwork/qssl.h>
-QT_BEGIN_HEADER
-
#ifndef QT_NO_SSL
QT_BEGIN_NAMESPACE
@@ -101,6 +99,9 @@ public:
void setPeerVerifyDepth(int depth);
// Certificate & cipher configuration
+ QList<QSslCertificate> localCertificateChain() const;
+ void setLocalCertificateChain(const QList<QSslCertificate> &localChain);
+
QSslCertificate localCertificate() const;
void setLocalCertificate(const QSslCertificate &certificate);
@@ -129,6 +130,8 @@ public:
private:
friend class QSslSocket;
friend class QSslConfigurationPrivate;
+ friend class QSslSocketBackendPrivate;
+ friend class QSslContext;
QSslConfiguration(QSslConfigurationPrivate *dd);
QSharedDataPointer<QSslConfigurationPrivate> d;
};
@@ -141,6 +144,4 @@ Q_DECLARE_METATYPE(QSslConfiguration)
#endif // QT_NO_SSL
-QT_END_HEADER
-
#endif
diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h
index 30f1ed66f9..54b7264d3d 100644
--- a/src/network/ssl/qsslconfiguration_p.h
+++ b/src/network/ssl/qsslconfiguration_p.h
@@ -84,12 +84,14 @@ public:
peerVerifyMode(QSslSocket::AutoVerifyPeer),
peerVerifyDepth(0),
allowRootCertOnDemandLoading(true),
+ peerSessionShared(false),
sslOptions(QSslConfigurationPrivate::defaultSslOptions)
{ }
QSslCertificate peerCertificate;
QList<QSslCertificate> peerCertificateChain;
- QSslCertificate localCertificate;
+
+ QList<QSslCertificate> localCertificateChain;
QSslKey privateKey;
QSslCipher sessionCipher;
@@ -100,6 +102,9 @@ public:
QSslSocket::PeerVerifyMode peerVerifyMode;
int peerVerifyDepth;
bool allowRootCertOnDemandLoading;
+ bool peerSessionShared;
+
+ Q_AUTOTEST_EXPORT static bool peerSessionWasShared(const QSslConfiguration &configuration);
QSsl::SslOptions sslOptions;
diff --git a/src/network/ssl/qsslcontext.cpp b/src/network/ssl/qsslcontext.cpp
new file mode 100644
index 0000000000..22ad42116b
--- /dev/null
+++ b/src/network/ssl/qsslcontext.cpp
@@ -0,0 +1,309 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtNetwork/qsslsocket.h>
+#include <QtCore/qmutex.h>
+
+#include "private/qsslcontext_p.h"
+#include "private/qsslsocket_p.h"
+#include "private/qsslsocket_openssl_p.h"
+#include "private/qsslsocket_openssl_symbols_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// defined in qsslsocket_openssl.cpp:
+extern int q_X509Callback(int ok, X509_STORE_CTX *ctx);
+extern QString getErrorsFromOpenSsl();
+
+QSslContext::QSslContext()
+ : ctx(0),
+ pkey(0),
+ session(0)
+{
+}
+
+QSslContext::~QSslContext()
+{
+ if (ctx)
+ // This will decrement the reference count by 1 and free the context eventually when possible
+ q_SSL_CTX_free(ctx);
+
+ if (pkey)
+ q_EVP_PKEY_free(pkey);
+
+ if (session)
+ q_SSL_SESSION_free(session);
+}
+
+QSslContext* QSslContext::fromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration, bool allowRootCertOnDemandLoading)
+{
+ QSslContext *sslContext = new QSslContext();
+ sslContext->sslConfiguration = configuration;
+ sslContext->errorCode = QSslError::NoError;
+
+ bool client = (mode == QSslSocket::SslClientMode);
+
+ bool reinitialized = false;
+init_context:
+ switch (sslContext->sslConfiguration.protocol()) {
+ case QSsl::SslV2:
+#ifndef OPENSSL_NO_SSL2
+ sslContext->ctx = q_SSL_CTX_new(client ? q_SSLv2_client_method() : q_SSLv2_server_method());
+#else
+ sslContext->ctx = 0; // SSL 2 not supported by the system, but chosen deliberately -> error
+#endif
+ break;
+ case QSsl::SslV3:
+ sslContext->ctx = q_SSL_CTX_new(client ? q_SSLv3_client_method() : q_SSLv3_server_method());
+ break;
+ case QSsl::SecureProtocols: // SslV2 will be disabled below
+ case QSsl::TlsV1SslV3: // SslV2 will be disabled below
+ case QSsl::AnyProtocol:
+ default:
+ sslContext->ctx = q_SSL_CTX_new(client ? q_SSLv23_client_method() : q_SSLv23_server_method());
+ break;
+ case QSsl::TlsV1_0:
+ sslContext->ctx = q_SSL_CTX_new(client ? q_TLSv1_client_method() : q_TLSv1_server_method());
+ break;
+ case QSsl::TlsV1_1:
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ sslContext->ctx = q_SSL_CTX_new(client ? q_TLSv1_1_client_method() : q_TLSv1_1_server_method());
+#else
+ sslContext->ctx = 0; // TLS 1.1 not supported by the system, but chosen deliberately -> error
+#endif
+ break;
+ case QSsl::TlsV1_2:
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ sslContext->ctx = q_SSL_CTX_new(client ? q_TLSv1_2_client_method() : q_TLSv1_2_server_method());
+#else
+ sslContext->ctx = 0; // TLS 1.2 not supported by the system, but chosen deliberately -> error
+#endif
+ break;
+ }
+ if (!sslContext->ctx) {
+ // After stopping Flash 10 the SSL library looses its ciphers. Try re-adding them
+ // by re-initializing the library.
+ if (!reinitialized) {
+ reinitialized = true;
+ if (q_SSL_library_init() == 1)
+ goto init_context;
+ }
+
+ sslContext->errorStr = QSslSocket::tr("Error creating SSL context (%1)").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return sslContext;
+ }
+
+ // Enable bug workarounds.
+ long options = QSslSocketBackendPrivate::setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions);
+ q_SSL_CTX_set_options(sslContext->ctx, options);
+
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+ // Tell OpenSSL to release memory early
+ // http://www.openssl.org/docs/ssl/SSL_CTX_set_mode.html
+ if (q_SSLeay() >= 0x10000000L)
+ q_SSL_CTX_set_mode(sslContext->ctx, SSL_MODE_RELEASE_BUFFERS);
+#endif
+
+ // Initialize ciphers
+ QByteArray cipherString;
+ int first = true;
+ QList<QSslCipher> ciphers = sslContext->sslConfiguration.ciphers();
+ if (ciphers.isEmpty())
+ ciphers = QSslSocketPrivate::defaultCiphers();
+ foreach (const QSslCipher &cipher, ciphers) {
+ if (first)
+ first = false;
+ else
+ cipherString.append(':');
+ cipherString.append(cipher.name().toLatin1());
+ }
+
+ if (!q_SSL_CTX_set_cipher_list(sslContext->ctx, cipherString.data())) {
+ sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return sslContext;
+ }
+
+ // Add all our CAs to this store.
+ QList<QSslCertificate> expiredCerts;
+ foreach (const QSslCertificate &caCertificate, sslContext->sslConfiguration.caCertificates()) {
+ // add expired certs later, so that the
+ // valid ones are used before the expired ones
+ if (caCertificate.expiryDate() < QDateTime::currentDateTime()) {
+ expiredCerts.append(caCertificate);
+ } else {
+ q_X509_STORE_add_cert(sslContext->ctx->cert_store, (X509 *)caCertificate.handle());
+ }
+ }
+
+ // now add the expired certs
+ foreach (const QSslCertificate &caCertificate, expiredCerts) {
+ q_X509_STORE_add_cert(sslContext->ctx->cert_store, reinterpret_cast<X509 *>(caCertificate.handle()));
+ }
+
+ if (QSslSocketPrivate::s_loadRootCertsOnDemand && allowRootCertOnDemandLoading) {
+ // tell OpenSSL the directories where to look up the root certs on demand
+ QList<QByteArray> unixDirs = QSslSocketPrivate::unixRootCertDirectories();
+ for (int a = 0; a < unixDirs.count(); ++a)
+ q_SSL_CTX_load_verify_locations(sslContext->ctx, 0, unixDirs.at(a).constData());
+ }
+
+ // Register a custom callback to get all verification errors.
+ X509_STORE_set_verify_cb_func(sslContext->ctx->cert_store, q_X509Callback);
+
+ if (!sslContext->sslConfiguration.localCertificate().isNull()) {
+ // Require a private key as well.
+ if (sslContext->sslConfiguration.privateKey().isNull()) {
+ sslContext->errorStr = QSslSocket::tr("Cannot provide a certificate with no key, %1").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return sslContext;
+ }
+
+ // Load certificate
+ if (!q_SSL_CTX_use_certificate(sslContext->ctx, (X509 *)sslContext->sslConfiguration.localCertificate().handle())) {
+ sslContext->errorStr = QSslSocket::tr("Error loading local certificate, %1").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return sslContext;
+ }
+
+ if (configuration.d->privateKey.algorithm() == QSsl::Opaque) {
+ sslContext->pkey = reinterpret_cast<EVP_PKEY *>(configuration.d->privateKey.handle());
+ } else {
+ // Load private key
+ sslContext->pkey = q_EVP_PKEY_new();
+ // before we were using EVP_PKEY_assign_R* functions and did not use EVP_PKEY_free.
+ // this lead to a memory leak. Now we use the *_set1_* functions which do not
+ // take ownership of the RSA/DSA key instance because the QSslKey already has ownership.
+ if (configuration.d->privateKey.algorithm() == QSsl::Rsa)
+ q_EVP_PKEY_set1_RSA(sslContext->pkey, reinterpret_cast<RSA *>(configuration.d->privateKey.handle()));
+ else
+ q_EVP_PKEY_set1_DSA(sslContext->pkey, reinterpret_cast<DSA *>(configuration.d->privateKey.handle()));
+ }
+
+ if (!q_SSL_CTX_use_PrivateKey(sslContext->ctx, sslContext->pkey)) {
+ sslContext->errorStr = QSslSocket::tr("Error loading private key, %1").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return sslContext;
+ }
+ if (configuration.d->privateKey.algorithm() == QSsl::Opaque)
+ sslContext->pkey = 0; // Don't free the private key, it belongs to QSslKey
+
+ // Check if the certificate matches the private key.
+ if (!q_SSL_CTX_check_private_key(sslContext->ctx)) {
+ sslContext->errorStr = QSslSocket::tr("Private key does not certify public key, %1").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return sslContext;
+ }
+
+ // If we have any intermediate certificates then we need to add them to our chain
+ bool first = true;
+ foreach (const QSslCertificate &cert, configuration.d->localCertificateChain) {
+ if (first) {
+ first = false;
+ continue;
+ }
+ q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_EXTRA_CHAIN_CERT, 0,
+ q_X509_dup(reinterpret_cast<X509 *>(cert.handle())));
+ }
+ }
+
+ // Initialize peer verification.
+ 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);
+ }
+
+ // Set verification depth.
+ if (sslContext->sslConfiguration.peerVerifyDepth() != 0)
+ q_SSL_CTX_set_verify_depth(sslContext->ctx, sslContext->sslConfiguration.peerVerifyDepth());
+
+ return sslContext;
+}
+
+// Needs to be deleted by caller
+SSL* QSslContext::createSsl()
+{
+ SSL* ssl = q_SSL_new(ctx);
+ q_SSL_clear(ssl);
+
+ if (session) {
+ // Try to resume the last session we cached
+ if (!q_SSL_set_session(ssl, session)) {
+ qWarning("could not set SSL session");
+ q_SSL_SESSION_free(session);
+ session = 0;
+ }
+ }
+ return ssl;
+}
+
+// We cache exactly one session here
+bool QSslContext::cacheSession(SSL* ssl)
+{
+ // dont cache the same session again
+ if (session && session == q_SSL_get_session(ssl))
+ return true;
+
+ // decrease refcount of currently stored session
+ // (this might happen if there are several concurrent handshakes in flight)
+ if (session)
+ q_SSL_SESSION_free(session);
+
+ // cache the session the caller gave us and increase reference count
+ session = q_SSL_get1_session(ssl);
+ return (session != NULL);
+
+}
+
+QSslError::SslError QSslContext::error() const
+{
+ return errorCode;
+}
+
+QString QSslContext::errorString() const
+{
+ return errorStr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslcontext_p.h b/src/network/ssl/qsslcontext_p.h
new file mode 100644
index 0000000000..c8578d349e
--- /dev/null
+++ b/src/network/ssl/qsslcontext_p.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QSSLCONTEXT_H
+#define QSSLCONTEXT_H
+
+#include <QtCore/qvariant.h>
+#include <QtNetwork/qsslcertificate.h>
+#include <QtNetwork/qsslconfiguration.h>
+#include <openssl/ssl.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_SSL
+
+class QSslContextPrivate;
+
+class QSslContext
+{
+public:
+
+ ~QSslContext();
+
+ static QSslContext* fromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration,
+ bool allowRootCertOnDemandLoading);
+
+ QSslError::SslError error() const;
+ QString errorString() const;
+
+ SSL* createSsl();
+ bool cacheSession(SSL*); // should be called when handshake completed
+
+protected:
+ QSslContext();
+
+private:
+ SSL_CTX* ctx;
+ EVP_PKEY *pkey;
+ SSL_SESSION *session;
+ QSslError::SslError errorCode;
+ QString errorStr;
+ QSslConfiguration sslConfiguration;
+};
+
+#endif // QT_NO_SSL
+
+QT_END_NAMESPACE
+
+#endif // QSSLCONTEXT_H
diff --git a/src/network/ssl/qsslerror.cpp b/src/network/ssl/qsslerror.cpp
index d151b376d5..bf75d2e610 100644
--- a/src/network/ssl/qsslerror.cpp
+++ b/src/network/ssl/qsslerror.cpp
@@ -61,7 +61,7 @@
\enum QSslError::SslError
Describes all recognized errors that can occur during an SSL handshake.
-
+
\value NoError
\value UnableToGetIssuerCertificate
\value UnableToDecryptCertificateSignature
@@ -108,7 +108,7 @@ public:
};
/*!
- Constructs a QSslError object with no error and default certificate.
+ Constructs a QSslError object with no error and default certificate.
*/
diff --git a/src/network/ssl/qsslerror.h b/src/network/ssl/qsslerror.h
index 847a48cbc1..73549ce648 100644
--- a/src/network/ssl/qsslerror.h
+++ b/src/network/ssl/qsslerror.h
@@ -46,8 +46,6 @@
#include <QtCore/qvariant.h>
#include <QtNetwork/qsslcertificate.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
@@ -106,7 +104,7 @@ public:
SslError error() const;
QString errorString() const;
QSslCertificate certificate() const;
-
+
private:
QScopedPointer<QSslErrorPrivate> d;
};
@@ -126,6 +124,4 @@ QT_END_NAMESPACE
Q_DECLARE_METATYPE(QList<QSslError>)
#endif
-QT_END_HEADER
-
#endif
diff --git a/src/network/ssl/qsslkey.h b/src/network/ssl/qsslkey.h
index 986b1c41de..145d4a28f1 100644
--- a/src/network/ssl/qsslkey.h
+++ b/src/network/ssl/qsslkey.h
@@ -48,8 +48,6 @@
#include <QtCore/qsharedpointer.h>
#include <QtNetwork/qssl.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
@@ -110,6 +108,4 @@ Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslKey &key);
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index f0c3f40a28..a8bf8f83b8 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -267,7 +267,7 @@
/*!
\fn void QSslSocket::sslErrors(const QList<QSslError> &errors);
-
+
QSslSocket emits this signal after the SSL handshake to indicate that one
or more errors have occurred while establishing the identity of the
peer. The errors are usually an indication that QSslSocket is unable to
@@ -281,7 +281,7 @@
\a errors contains one or more errors that prevent QSslSocket from
verifying the identity of the peer.
-
+
Note: You cannot use Qt::QueuedConnection when connecting to this signal,
or calling QSslSocket::ignoreSslErrors() will have no effect.
@@ -436,7 +436,7 @@ void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port, O
\overload
In addition to the original behaviour of connectToHostEncrypted,
- this overloaded method enables the usage of a different hostname
+ this overloaded method enables the usage of a different hostname
(\a sslPeerName) for the certificate validation instead of
the one used for the TCP connection (\a hostName).
@@ -827,7 +827,7 @@ bool QSslSocket::flush()
/*!
\since 4.4
- Sets the size of QSslSocket's internal read buffer to be \a size bytes.
+ Sets the size of QSslSocket's internal read buffer to be \a size bytes.
*/
void QSslSocket::setReadBufferSize(qint64 size)
{
@@ -895,7 +895,7 @@ QSslConfiguration QSslSocket::sslConfiguration() const
void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration)
{
Q_D(QSslSocket);
- d->configuration.localCertificate = configuration.localCertificate();
+ d->configuration.localCertificateChain = configuration.localCertificateChain();
d->configuration.privateKey = configuration.privateKey();
d->configuration.ciphers = configuration.ciphers();
d->configuration.caCertificates = configuration.caCertificates();
@@ -912,6 +912,32 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration)
}
/*!
+ Sets the certificate chain to be presented to the peer during the
+ SSL handshake to be \a localChain.
+
+ \sa QSslConfiguration::setLocalCertificateChain()
+ \since 5.1
+ */
+void QSslSocket::setLocalCertificateChain(const QList<QSslCertificate> &localChain)
+{
+ Q_D(QSslSocket);
+ d->configuration.localCertificateChain = localChain;
+}
+
+/*!
+ Returns the socket's local \l {QSslCertificate} {certificate} chain,
+ or an empty list if no local certificates have been assigned.
+
+ \sa setLocalCertificateChain()
+ \since 5.1
+*/
+QList<QSslCertificate> QSslSocket::localCertificateChain() const
+{
+ Q_D(const QSslSocket);
+ return d->configuration.localCertificateChain;
+}
+
+/*!
Sets the socket's local certificate to \a certificate. The local
certificate is necessary if you need to confirm your identity to the
peer. It is used together with the private key; if you set the local
@@ -926,23 +952,24 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration)
void QSslSocket::setLocalCertificate(const QSslCertificate &certificate)
{
Q_D(QSslSocket);
- d->configuration.localCertificate = certificate;
+ d->configuration.localCertificateChain = QList<QSslCertificate>();
+ d->configuration.localCertificateChain += certificate;
}
/*!
\overload
Sets the socket's local \l {QSslCertificate} {certificate} to the
- first one found in file \a path, which is parsed according to the
+ first one found in file \a path, which is parsed according to the
specified \a format.
*/
void QSslSocket::setLocalCertificate(const QString &path,
QSsl::EncodingFormat format)
{
- Q_D(QSslSocket);
QFile file(path);
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
- d->configuration.localCertificate = QSslCertificate(file.readAll(), format);
+ setLocalCertificate(QSslCertificate(file.readAll(), format));
+
}
/*!
@@ -954,14 +981,16 @@ void QSslSocket::setLocalCertificate(const QString &path,
QSslCertificate QSslSocket::localCertificate() const
{
Q_D(const QSslSocket);
- return d->configuration.localCertificate;
+ if (d->configuration.localCertificateChain.isEmpty())
+ return QSslCertificate();
+ return d->configuration.localCertificateChain[0];
}
/*!
Returns the peer's digital certificate (i.e., the immediate
certificate of the host you are connected to), or a null
certificate, if the peer has not assigned a certificate.
-
+
The peer certificate is checked automatically during the
handshake phase, so this function is normally used to fetch
the certificate for display or for connection diagnostic
@@ -1073,7 +1102,7 @@ void QSslSocket::setPrivateKey(const QSslKey &key)
creating an SSL server socket. If you are creating an SSL client
socket, the key and local certificate are required if your client
must identify itself to an SSL server.
-
+
\sa privateKey(), setLocalCertificate()
*/
void QSslSocket::setPrivateKey(const QString &fileName, QSsl::KeyAlgorithm algorithm,
@@ -1681,7 +1710,7 @@ void QSslSocket::startClientEncryption()
subclass of QTcpServer and reimplement
QTcpServer::incomingConnection(). The returned socket descriptor
is then passed to QSslSocket::setSocketDescriptor().
-
+
\sa connectToHostEncrypted(), startClientEncryption()
*/
void QSslSocket::startServerEncryption()
@@ -2057,7 +2086,7 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri
ptr->ref.store(1);
ptr->peerCertificate = global->peerCertificate;
ptr->peerCertificateChain = global->peerCertificateChain;
- ptr->localCertificate = global->localCertificate;
+ ptr->localCertificateChain = global->localCertificateChain;
ptr->privateKey = global->privateKey;
ptr->sessionCipher = global->sessionCipher;
ptr->ciphers = global->ciphers;
@@ -2410,6 +2439,23 @@ QList<QByteArray> QSslSocketPrivate::unixRootCertDirectories()
<< "/opt/openssl/certs/"; // HP-UX
}
+/*!
+ \internal
+*/
+void QSslSocketPrivate::checkSettingSslContext(QSslSocket* socket, QSharedPointer<QSslContext> sslContext)
+{
+ if (socket->d_func()->sslContextPointer.isNull())
+ socket->d_func()->sslContextPointer = sslContext;
+}
+
+/*!
+ \internal
+*/
+QSharedPointer<QSslContext> QSslSocketPrivate::sslContext(QSslSocket *socket)
+{
+ return (socket) ? socket->d_func()->sslContextPointer : QSharedPointer<QSslContext>();
+}
+
QT_END_NAMESPACE
#include "moc_qsslsocket.cpp"
diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h
index 929e437554..d89933efda 100644
--- a/src/network/ssl/qsslsocket.h
+++ b/src/network/ssl/qsslsocket.h
@@ -50,8 +50,6 @@
# include <QtNetwork/qsslerror.h>
#endif
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
@@ -133,6 +131,9 @@ public:
void setSslConfiguration(const QSslConfiguration &config);
// Certificate & cipher accessors.
+ void setLocalCertificateChain(const QList<QSslCertificate> &localChain);
+ QList<QSslCertificate> localCertificateChain() const;
+
void setLocalCertificate(const QSslCertificate &certificate);
void setLocalCertificate(const QString &fileName, QSsl::EncodingFormat format = QSsl::Pem);
QSslCertificate localCertificate() const;
@@ -223,6 +224,4 @@ private:
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index f846bf2d8b..2b9c4b5bd2 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -55,6 +55,7 @@
****************************************************************************/
//#define QSSLSOCKET_DEBUG
+//#define QT_DECRYPT_SSL_TRAFFIC
#include "qsslsocket_openssl_p.h"
#include "qsslsocket_openssl_symbols_p.h"
@@ -156,6 +157,19 @@ private:
};
Q_GLOBAL_STATIC(QOpenSslLocks, openssl_locks)
+QString QSslSocketBackendPrivate::getErrorsFromOpenSsl()
+{
+ QString errorString;
+ unsigned long errNum;
+ while ((errNum = q_ERR_get_error())) {
+ 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
+ }
+ return errorString;
+}
+
extern "C" {
static void locking_function(int mode, int lockNumber, const char *, int)
{
@@ -175,8 +189,6 @@ static unsigned long id_function()
QSslSocketBackendPrivate::QSslSocketBackendPrivate()
: ssl(0),
- ctx(0),
- pkey(0),
readBio(0),
writeBio(0),
session(0)
@@ -240,7 +252,8 @@ struct QSslErrorList
QList<QPair<int, int> > errors;
};
Q_GLOBAL_STATIC(QSslErrorList, _q_sslErrorList)
-static int q_X509Callback(int ok, X509_STORE_CTX *ctx)
+
+int q_X509Callback(int ok, X509_STORE_CTX *ctx)
{
if (!ok) {
// Store the error and at which depth the error was detected.
@@ -311,191 +324,21 @@ bool QSslSocketBackendPrivate::initSslContext()
{
Q_Q(QSslSocket);
- // Create and initialize SSL context. Accept SSLv2, SSLv3 and TLSv1_0.
- bool client = (mode == QSslSocket::SslClientMode);
-
- bool reinitialized = false;
+ // If no external context was set (e.g. bei QHttpNetworkConnection) we will create a default context
+ if (!sslContextPointer)
+ sslContextPointer = QSharedPointer<QSslContext>(
+ QSslContext::fromConfiguration(mode, QSslConfiguration(&configuration), allowRootCertOnDemandLoading));
-init_context:
- switch (configuration.protocol) {
- case QSsl::SslV2:
-#ifndef OPENSSL_NO_SSL2
- ctx = q_SSL_CTX_new(client ? q_SSLv2_client_method() : q_SSLv2_server_method());
-#else
- ctx = 0; // SSL 2 not supported by the system, but chosen deliberately -> error
-#endif
- break;
- case QSsl::SslV3:
- ctx = q_SSL_CTX_new(client ? q_SSLv3_client_method() : q_SSLv3_server_method());
- break;
- case QSsl::SecureProtocols: // SslV2 will be disabled below
- case QSsl::TlsV1SslV3: // SslV2 will be disabled below
- case QSsl::AnyProtocol:
- default:
- ctx = q_SSL_CTX_new(client ? q_SSLv23_client_method() : q_SSLv23_server_method());
- break;
- case QSsl::TlsV1_0:
- ctx = q_SSL_CTX_new(client ? q_TLSv1_client_method() : q_TLSv1_server_method());
- break;
- case QSsl::TlsV1_1:
-#if OPENSSL_VERSION_NUMBER >= 0x10001000L
- ctx = q_SSL_CTX_new(client ? q_TLSv1_1_client_method() : q_TLSv1_1_server_method());
-#else
- ctx = 0; // TLS 1.1 not supported by the system, but chosen deliberately -> error
-#endif
- break;
- case QSsl::TlsV1_2:
-#if OPENSSL_VERSION_NUMBER >= 0x10001000L
- ctx = q_SSL_CTX_new(client ? q_TLSv1_2_client_method() : q_TLSv1_2_server_method());
-#else
- ctx = 0; // TLS 1.2 not supported by the system, but chosen deliberately -> error
-#endif
- break;
- }
- if (!ctx) {
- // After stopping Flash 10 the SSL library looses its ciphers. Try re-adding them
- // by re-initializing the library.
- if (!reinitialized) {
- reinitialized = true;
- if (q_SSL_library_init() == 1)
- goto init_context;
- }
-
- q->setErrorString(QSslSocket::tr("Error creating SSL context (%1)").arg(getErrorsFromOpenSsl()));
- q->setSocketError(QAbstractSocket::SslInternalError);
- emit q->error(QAbstractSocket::SslInternalError);
- return false;
- }
-
- // Enable bug workarounds.
- long options = setupOpenSslOptions(configuration.protocol, configuration.sslOptions);
- q_SSL_CTX_set_options(ctx, options);
-
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
- // Tell OpenSSL to release memory early
- // http://www.openssl.org/docs/ssl/SSL_CTX_set_mode.html
- if (q_SSLeay() >= 0x10000000L)
- q_SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
-#endif
-
- // Initialize ciphers
- QByteArray cipherString;
- int first = true;
- QList<QSslCipher> ciphers = configuration.ciphers;
- if (ciphers.isEmpty())
- ciphers = defaultCiphers();
- foreach (const QSslCipher &cipher, ciphers) {
- if (first)
- first = false;
- else
- cipherString.append(':');
- cipherString.append(cipher.name().toLatin1());
- }
-
- if (!q_SSL_CTX_set_cipher_list(ctx, cipherString.data())) {
- q->setErrorString(QSslSocket::tr("Invalid or empty cipher list (%1)").arg(getErrorsFromOpenSsl()));
+ if (sslContextPointer->error() != QSslError::NoError) {
+ q->setErrorString(sslContextPointer->errorString());
q->setSocketError(QAbstractSocket::SslInvalidUserDataError);
emit q->error(QAbstractSocket::SslInvalidUserDataError);
+ sslContextPointer.clear(); // deletes the QSslContext
return false;
}
- // Add all our CAs to this store.
- QList<QSslCertificate> expiredCerts;
- foreach (const QSslCertificate &caCertificate, q->caCertificates()) {
- // add expired certs later, so that the
- // valid ones are used before the expired ones
- if (caCertificate.expiryDate() < QDateTime::currentDateTime()) {
- expiredCerts.append(caCertificate);
- } else {
- q_X509_STORE_add_cert(ctx->cert_store, reinterpret_cast<X509 *>(caCertificate.handle()));
- }
- }
-
- bool addExpiredCerts = true;
-#if defined(Q_OS_MAC) && (MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5)
- //On Leopard SSL does not work if we add the expired certificates.
- if (QSysInfo::MacintoshVersion == QSysInfo::MV_10_5)
- addExpiredCerts = false;
-#endif
- // now add the expired certs
- if (addExpiredCerts) {
- foreach (const QSslCertificate &caCertificate, expiredCerts) {
- q_X509_STORE_add_cert(ctx->cert_store, reinterpret_cast<X509 *>(caCertificate.handle()));
- }
- }
-
- if (s_loadRootCertsOnDemand && allowRootCertOnDemandLoading) {
- // tell OpenSSL the directories where to look up the root certs on demand
- QList<QByteArray> unixDirs = unixRootCertDirectories();
- for (int a = 0; a < unixDirs.count(); ++a)
- q_SSL_CTX_load_verify_locations(ctx, 0, unixDirs.at(a).constData());
- }
-
- // Register a custom callback to get all verification errors.
- X509_STORE_set_verify_cb_func(ctx->cert_store, q_X509Callback);
-
- if (!configuration.localCertificate.isNull()) {
- // Require a private key as well.
- if (configuration.privateKey.isNull()) {
- q->setErrorString(QSslSocket::tr("Cannot provide a certificate with no key, %1").arg(getErrorsFromOpenSsl()));
- q->setSocketError(QAbstractSocket::SslInvalidUserDataError);
- emit q->error(QAbstractSocket::SslInvalidUserDataError);
- return false;
- }
-
- // Load certificate
- if (!q_SSL_CTX_use_certificate(ctx, reinterpret_cast<X509 *>(configuration.localCertificate.handle()))) {
- q->setErrorString(QSslSocket::tr("Error loading local certificate, %1").arg(getErrorsFromOpenSsl()));
- q->setSocketError(QAbstractSocket::SslInternalError);
- emit q->error(QAbstractSocket::SslInternalError);
- return false;
- }
-
- if (configuration.privateKey.algorithm() == QSsl::Opaque) {
- pkey = reinterpret_cast<EVP_PKEY *>(configuration.privateKey.handle());
- } else {
- // Load private key
- pkey = q_EVP_PKEY_new();
- // before we were using EVP_PKEY_assign_R* functions and did not use EVP_PKEY_free.
- // this lead to a memory leak. Now we use the *_set1_* functions which do not
- // take ownership of the RSA/DSA key instance because the QSslKey already has ownership.
- if (configuration.privateKey.algorithm() == QSsl::Rsa)
- q_EVP_PKEY_set1_RSA(pkey, reinterpret_cast<RSA *>(configuration.privateKey.handle()));
- else
- q_EVP_PKEY_set1_DSA(pkey, reinterpret_cast<DSA *>(configuration.privateKey.handle()));
- }
-
- if (!q_SSL_CTX_use_PrivateKey(ctx, pkey)) {
- q->setErrorString(QSslSocket::tr("Error loading private key, %1").arg(getErrorsFromOpenSsl()));
- q->setSocketError(QAbstractSocket::SslInternalError);
- emit q->error(QAbstractSocket::SslInternalError);
- return false;
- }
- if (configuration.privateKey.algorithm() == QSsl::Opaque)
- pkey = 0; // Don't free the private key, it belongs to QSslKey
-
- // Check if the certificate matches the private key.
- if (!q_SSL_CTX_check_private_key(ctx)) {
- q->setErrorString(QSslSocket::tr("Private key does not certify public key, %1").arg(getErrorsFromOpenSsl()));
- q->setSocketError(QAbstractSocket::SslInvalidUserDataError);
- emit q->error(QAbstractSocket::SslInvalidUserDataError);
- return false;
- }
- }
-
- // Initialize peer verification.
- if (configuration.peerVerifyMode == QSslSocket::VerifyNone) {
- q_SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, 0);
- } else {
- q_SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, q_X509Callback);
- }
-
- // Set verification depth.
- if (configuration.peerVerifyDepth != 0)
- q_SSL_CTX_set_verify_depth(ctx, configuration.peerVerifyDepth);
-
// Create and initialize SSL session
- if (!(ssl = q_SSL_new(ctx))) {
+ if (!(ssl = sslContextPointer->createSsl())) {
// ### Bad error code
q->setErrorString(QSslSocket::tr("Error creating SSL session, %1").arg(getErrorsFromOpenSsl()));
q->setSocketError(QAbstractSocket::SslInternalError);
@@ -510,7 +353,7 @@ init_context:
configuration.protocol == QSsl::TlsV1_2 ||
configuration.protocol == QSsl::SecureProtocols ||
configuration.protocol == QSsl::AnyProtocol) &&
- client && q_SSLeay() >= 0x00090806fL) {
+ mode == QSslSocket::SslClientMode && q_SSLeay() >= 0x00090806fL) {
// Set server hostname on TLS extension. RFC4366 section 3.1 requires it in ACE format.
QString tlsHostName = verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName;
if (tlsHostName.isEmpty())
@@ -527,7 +370,6 @@ init_context:
#endif
// Clear the session.
- q_SSL_clear(ssl);
errorList.clear();
// Initialize memory BIOs for encryption and decryption.
@@ -557,14 +399,7 @@ void QSslSocketBackendPrivate::destroySslContext()
q_SSL_free(ssl);
ssl = 0;
}
- if (ctx) {
- q_SSL_CTX_free(ctx);
- ctx = 0;
- }
- if (pkey) {
- q_EVP_PKEY_free(pkey);
- pkey = 0;
- }
+ sslContextPointer.clear();
}
/*!
@@ -847,10 +682,19 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
}
#elif defined(Q_OS_UNIX)
QSet<QString> certFiles;
+# ifdef Q_OS_ANDROID
+ QList<QByteArray> directories;
+ directories << qgetenv("MINISTRO_SSL_CERTS_PATH"); // Set by Ministro
+# else
QList<QByteArray> directories = unixRootCertDirectories();
+# endif
QDir currentDir;
QStringList nameFilters;
+# ifdef Q_OS_ANDROID
+ nameFilters << QLatin1String("*.der");
+#else
nameFilters << QLatin1String("*.pem") << QLatin1String("*.crt");
+# endif
currentDir.setNameFilters(nameFilters);
for (int a = 0; a < directories.count(); a++) {
currentDir.setPath(QLatin1String(directories.at(a)));
@@ -863,10 +707,16 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
}
QSetIterator<QString> it(certFiles);
while(it.hasNext()) {
- systemCerts.append(QSslCertificate::fromPath(it.next()));
+# ifdef Q_OS_ANDROID
+ systemCerts.append(QSslCertificate::fromPath(it.next(), QSsl::Der));
+# else
+ systemCerts.append(QSslCertificate::fromPath(it.next(), QSsl::Pem));
+# endif
}
+# ifndef Q_OS_ANDROID
systemCerts.append(QSslCertificate::fromPath(QLatin1String("/etc/pki/tls/certs/ca-bundle.crt"), QSsl::Pem)); // Fedora, Mandriva
systemCerts.append(QSslCertificate::fromPath(QLatin1String("/usr/local/share/certs/ca-root-nss.crt"), QSsl::Pem)); // FreeBSD's ca_root_nss
+# endif
#endif
#ifdef QSSLSOCKET_DEBUG
qDebug() << "systemCaCertificates retrieval time " << timer.elapsed() << "ms";
@@ -1102,7 +952,7 @@ void QSslSocketBackendPrivate::transmit()
break;
}
} while (ssl && readBytes > 0);
- } while (ssl && ctx && transmitting);
+ } while (ssl && transmitting);
}
static QSslError _q_OpenSSL_to_QSslError(int errorCode, const QSslCertificate &cert)
@@ -1308,7 +1158,6 @@ bool QSslSocketBackendPrivate::startHandshake()
}
}
#endif
-
if (!checkSslErrors())
return false;
} else {
@@ -1532,7 +1381,7 @@ void QSslSocketBackendPrivate::disconnected()
QSslCipher QSslSocketBackendPrivate::sessionCipher() const
{
- if (!ssl || !ctx)
+ if (!ssl)
return QSslCipher();
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
// FIXME This is fairly evil, but needed to keep source level compatibility
@@ -1552,6 +1401,49 @@ void QSslSocketBackendPrivate::continueHandshake()
if (readBufferMaxSize)
plainSocket->setReadBufferSize(readBufferMaxSize);
+ if (q_SSL_ctrl((ssl), SSL_CTRL_GET_SESSION_REUSED, 0, NULL))
+ configuration.peerSessionShared = true;
+
+#ifdef QT_DECRYPT_SSL_TRAFFIC
+ if (ssl->session && ssl->s3) {
+ const char *mk = reinterpret_cast<const char *>(ssl->session->master_key);
+ QByteArray masterKey(mk, ssl->session->master_key_length);
+ const char *random = reinterpret_cast<const char *>(ssl->s3->client_random);
+ QByteArray clientRandom(random, SSL3_RANDOM_SIZE);
+
+ // different format, needed for e.g. older Wireshark versions:
+// const char *sid = reinterpret_cast<const char *>(ssl->session->session_id);
+// QByteArray sessionID(sid, ssl->session->session_id_length);
+// QByteArray debugLineRSA("RSA Session-ID:");
+// debugLineRSA.append(sessionID.toHex().toUpper());
+// debugLineRSA.append(" Master-Key:");
+// debugLineRSA.append(masterKey.toHex().toUpper());
+// debugLineRSA.append("\n");
+
+ QByteArray debugLineClientRandom("CLIENT_RANDOM ");
+ debugLineClientRandom.append(clientRandom.toHex().toUpper());
+ debugLineClientRandom.append(" ");
+ debugLineClientRandom.append(masterKey.toHex().toUpper());
+ debugLineClientRandom.append("\n");
+
+ QString sslKeyFile = QDir::tempPath() + QLatin1String("/qt-ssl-keys");
+ QFile file(sslKeyFile);
+ if (!file.open(QIODevice::Append))
+ qWarning() << "could not open file" << sslKeyFile << "for appending";
+ if (!file.write(debugLineClientRandom))
+ qWarning() << "could not write to file" << sslKeyFile;
+ file.close();
+ } else {
+ qWarning("could not decrypt SSL traffic");
+ }
+#endif
+
+ // Cache this SSL session inside the QSslContext
+ if (!(configuration.sslOptions & QSsl::SslOptionDisableSessionTickets)) {
+ if (!sslContextPointer->cacheSession(ssl))
+ sslContextPointer.clear(); // we could not cache the session
+ }
+
connectionEncrypted = true;
emit q->encrypted();
if (autoStartHandshake && pendingClose) {
@@ -1571,19 +1463,6 @@ QList<QSslCertificate> QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates
return certificates;
}
-QString QSslSocketBackendPrivate::getErrorsFromOpenSsl()
-{
- QString errorString;
- unsigned long errNum;
- while((errNum = q_ERR_get_error())) {
- 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
- }
- return errorString;
-}
-
bool QSslSocketBackendPrivate::isMatchingHostname(const QSslCertificate &cert, const QString &peerName)
{
QStringList commonNameList = cert.subjectInfo(QSslCertificate::CommonName);
@@ -1675,17 +1554,9 @@ QList<QSslError> QSslSocketBackendPrivate::verify(QList<QSslCertificate> certifi
}
}
- bool addExpiredCerts = true;
-#if defined(Q_OS_MAC) && (MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5)
- //On Leopard SSL does not work if we add the expired certificates.
- if (QSysInfo::MacintoshVersion == QSysInfo::MV_10_5)
- addExpiredCerts = false;
-#endif
// now add the expired certs
- if (addExpiredCerts) {
- foreach (const QSslCertificate &caCertificate, expiredCerts) {
- q_X509_STORE_add_cert(certStore, reinterpret_cast<X509 *>(caCertificate.handle()));
- }
+ foreach (const QSslCertificate &caCertificate, expiredCerts) {
+ q_X509_STORE_add_cert(certStore, reinterpret_cast<X509 *>(caCertificate.handle()));
}
QMutexLocker sslErrorListMutexLocker(&_q_sslErrorList()->mutex);
diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h
index d6675197b4..c8b23e6cad 100644
--- a/src/network/ssl/qsslsocket_openssl_p.h
+++ b/src/network/ssl/qsslsocket_openssl_p.h
@@ -75,7 +75,10 @@
#if defined(OCSP_RESPONSE)
#undef OCSP_RESPONSE
#endif
+#if defined(X509_NAME)
+#undef X509_NAME
#endif
+#endif // Q_OS_WIN
#include <openssl/asn1.h>
#include <openssl/bio.h>
@@ -115,8 +118,6 @@ public:
bool initSslContext();
void destroySslContext();
SSL *ssl;
- SSL_CTX *ctx;
- EVP_PKEY *pkey;
BIO *readBio;
BIO *writeBio;
SSL_SESSION *session;
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index 9f271f863a..fed99752b0 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -67,7 +67,7 @@
#if defined(Q_OS_UNIX)
#include <QtCore/qdir.h>
#endif
-#ifdef Q_OS_LINUX
+#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID_NO_SDK)
#include <link.h>
#endif
@@ -245,6 +245,10 @@ 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)
+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)
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
#ifndef OPENSSL_NO_SSL2
DEFINEFUNC(const SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return 0, return)
@@ -379,7 +383,7 @@ static bool libGreaterThan(const QString &lhs, const QString &rhs)
return true;
}
-#ifdef Q_OS_LINUX
+#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID_NO_SDK)
static int dlIterateCallback(struct dl_phdr_info *info, size_t size, void *data)
{
if (size < sizeof (info->dlpi_addr) + sizeof (info->dlpi_name))
@@ -410,7 +414,9 @@ static QStringList libraryPathList()
paths << QLatin1String("/lib64") << QLatin1String("/usr/lib64") << QLatin1String("/usr/local/lib64");
paths << QLatin1String("/lib32") << QLatin1String("/usr/lib32") << QLatin1String("/usr/local/lib32");
-#ifdef Q_OS_LINUX
+#if defined(Q_OS_ANDROID_NO_SDK)
+ paths << QLatin1String("/system/lib");
+#elif defined(Q_OS_LINUX)
// discover paths of already loaded libraries
QSet<QString> loadedPaths;
dl_iterate_phdr(dlIterateCallback, &loadedPaths);
@@ -457,28 +463,46 @@ static QStringList findAllLibCrypto()
# endif
#ifdef Q_OS_WIN
-static QPair<QSystemLibrary*, QSystemLibrary*> loadOpenSslWin32()
+static bool tryToLoadOpenSslWin32Library(QLatin1String ssleay32LibName, QLatin1String libeay32LibName, QPair<QSystemLibrary*, QSystemLibrary*> &pair)
{
- QPair<QSystemLibrary*,QSystemLibrary*> pair;
pair.first = 0;
pair.second = 0;
- QSystemLibrary *ssleay32 = new QSystemLibrary(QLatin1String("ssleay32"));
+ QSystemLibrary *ssleay32 = new QSystemLibrary(ssleay32LibName);
if (!ssleay32->load(false)) {
- // Cannot find ssleay32.dll
delete ssleay32;
- return pair;
+ return FALSE;
}
- QSystemLibrary *libeay32 = new QSystemLibrary(QLatin1String("libeay32"));
+ QSystemLibrary *libeay32 = new QSystemLibrary(libeay32LibName);
if (!libeay32->load(false)) {
delete ssleay32;
delete libeay32;
- return pair;
+ return FALSE;
}
pair.first = ssleay32;
pair.second = libeay32;
+ return TRUE;
+}
+
+static QPair<QSystemLibrary*, QSystemLibrary*> loadOpenSslWin32()
+{
+ QPair<QSystemLibrary*,QSystemLibrary*> pair;
+ pair.first = 0;
+ pair.second = 0;
+
+ // When OpenSSL is built using MSVC then the libraries are named 'ssleay32.dll' and 'libeay32'dll'.
+ // When OpenSSL is built using GCC then different library names are used (depending on the OpenSSL version)
+ // The oldest version of a GCC-based OpenSSL which can be detected by the code below is 0.9.8g (released in 2007)
+ if (!tryToLoadOpenSslWin32Library(QLatin1String("ssleay32"), QLatin1String("libeay32"), pair)) {
+ if (!tryToLoadOpenSslWin32Library(QLatin1String("libssl-10"), QLatin1String("libcrypto-10"), pair)) {
+ if (!tryToLoadOpenSslWin32Library(QLatin1String("libssl-8"), QLatin1String("libcrypto-8"), pair)) {
+ tryToLoadOpenSslWin32Library(QLatin1String("libssl-7"), QLatin1String("libcrypto-7"), pair);
+ }
+ }
+ }
+
return pair;
}
#else
@@ -699,6 +723,10 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_set_bio)
RESOLVEFUNC(SSL_set_connect_state)
RESOLVEFUNC(SSL_shutdown)
+ RESOLVEFUNC(SSL_set_session)
+ RESOLVEFUNC(SSL_SESSION_free)
+ RESOLVEFUNC(SSL_get1_session)
+ RESOLVEFUNC(SSL_get_session)
RESOLVEFUNC(SSL_write)
#ifndef OPENSSL_NO_SSL2
RESOLVEFUNC(SSLv2_client_method)
diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h
index ccd9d3cfb9..62648e3e37 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -347,6 +347,10 @@ 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_set_session(SSL *to, SSL_SESSION *session);
+void q_SSL_SESSION_free(SSL_SESSION *ses);
+SSL_SESSION *q_SSL_get1_session(SSL *ssl);
+SSL_SESSION *q_SSL_get_session(const SSL *ssl);
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
const SSL_METHOD *q_SSLv2_client_method();
const SSL_METHOD *q_SSLv3_client_method();
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
index 851dec5840..72117353ac 100644
--- a/src/network/ssl/qsslsocket_p.h
+++ b/src/network/ssl/qsslsocket_p.h
@@ -59,6 +59,7 @@
#include <private/qtcpsocket_p.h>
#include "qsslkey.h"
#include "qsslconfiguration_p.h"
+#include <private/qsslcontext_p.h>
#include <QtCore/qstringlist.h>
@@ -114,6 +115,7 @@ public:
QSslConfigurationPrivate configuration;
QList<QSslError> sslErrors;
+ QSharedPointer<QSslContext> sslContextPointer;
// if set, this hostname is used for certificate validation instead of the hostname
// that was used for connecting to.
@@ -121,6 +123,8 @@ public:
bool allowRootCertOnDemandLoading;
+ static bool s_loadRootCertsOnDemand;
+
static bool supportsSsl();
static long sslLibraryVersionNumber();
static QString sslLibraryVersionString();
@@ -155,6 +159,9 @@ public:
void createPlainSocket(QIODevice::OpenMode openMode);
static void pauseSocketNotifiers(QSslSocket*);
static void resumeSocketNotifiers(QSslSocket*);
+ // ### The 2 methods below should be made member methods once the QSslContext class is made public
+ static void checkSettingSslContext(QSslSocket*, QSharedPointer<QSslContext>);
+ static QSharedPointer<QSslContext> sslContext(QSslSocket *socket);
bool isPaused() const;
void _q_connectedSlot();
void _q_hostFoundSlot();
@@ -170,6 +177,8 @@ public:
virtual void _q_caRootLoaded(QSslCertificate,QSslCertificate) = 0;
#endif
+ static QList<QByteArray> unixRootCertDirectories(); // used also by QSslContext
+
virtual qint64 peek(char *data, qint64 maxSize);
virtual QByteArray peek(qint64 maxSize);
@@ -192,8 +201,6 @@ private:
static bool s_loadedCiphersAndCerts;
protected:
bool verifyErrorsHaveBeenIgnored();
- static bool s_loadRootCertsOnDemand;
- static QList<QByteArray> unixRootCertDirectories();
bool paused;
};
diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri
index 8ebb4b29af..f5afb43759 100644
--- a/src/network/ssl/ssl.pri
+++ b/src/network/ssl/ssl.pri
@@ -14,7 +14,8 @@ contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) {
ssl/qsslsocket_openssl_symbols_p.h \
ssl/qsslsocket_p.h \
ssl/qsslcertificateextension.h \
- ssl/qsslcertificateextension_p.h
+ ssl/qsslcertificateextension_p.h \
+ ssl/qsslcontext_p.h
SOURCES += ssl/qssl.cpp \
ssl/qsslcertificate.cpp \
ssl/qsslconfiguration.cpp \
@@ -24,7 +25,8 @@ contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) {
ssl/qsslsocket.cpp \
ssl/qsslsocket_openssl.cpp \
ssl/qsslsocket_openssl_symbols.cpp \
- ssl/qsslcertificateextension.cpp
+ ssl/qsslcertificateextension.cpp \
+ ssl/qsslcontext.cpp
# Add optional SSL libs
# Static linking of OpenSSL with msvc: