summaryrefslogtreecommitdiffstats
path: root/src/network/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/ssl')
-rw-r--r--src/network/ssl/qssl.cpp10
-rw-r--r--src/network/ssl/qssl.h4
-rw-r--r--src/network/ssl/qsslconfiguration.cpp65
-rw-r--r--src/network/ssl/qsslconfiguration.h4
-rw-r--r--src/network/ssl/qsslconfiguration_p.h6
-rw-r--r--src/network/ssl/qsslcontext.cpp41
-rw-r--r--src/network/ssl/qsslcontext_p.h5
-rw-r--r--src/network/ssl/qsslsocket.cpp2
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp38
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp6
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h2
-rw-r--r--src/network/ssl/qsslsocket_p.h4
12 files changed, 161 insertions, 26 deletions
diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp
index 4e33001d8d..ec771e1f49 100644
--- a/src/network/ssl/qssl.cpp
+++ b/src/network/ssl/qssl.cpp
@@ -161,12 +161,20 @@ QT_BEGIN_NAMESPACE
mechanism for renegotiating the connection parameters. When enabled, this
option can allow connections for legacy servers, but it introduces the
possibility that an attacker could inject plaintext into the SSL session.
+ \value SslOptionDisableSessionSharing Disables SSL session sharing via
+ the session ID handshake attribute.
+ \value SslOptionDisableSessionPersistence Disables storing the SSL session
+ in ASN.1 format as returned by QSslConfiguration::session(). Enabling
+ this feature adds memory overhead of approximately 1K per used session
+ ticket.
By default, SslOptionDisableEmptyFragments is turned on since this causes
problems with a large number of servers. SslOptionDisableLegacyRenegotiation
is also turned on, since it introduces a security risk.
SslOptionDisableCompression is turned on to prevent the attack publicised by
- CRIME. The other options are turned off.
+ CRIME.
+ SslOptionDisableSessionPersistence is turned on to optimize memory usage.
+ The other options are turned off.
Note: Availability of above options depends on the version of the SSL
backend in use.
diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h
index 06d80965e2..21d03cb703 100644
--- a/src/network/ssl/qssl.h
+++ b/src/network/ssl/qssl.h
@@ -95,7 +95,9 @@ namespace QSsl {
SslOptionDisableSessionTickets = 0x02,
SslOptionDisableCompression = 0x04,
SslOptionDisableServerNameIndication = 0x08,
- SslOptionDisableLegacyRenegotiation = 0x10
+ SslOptionDisableLegacyRenegotiation = 0x10,
+ SslOptionDisableSessionSharing = 0x20,
+ SslOptionDisableSessionPersistence = 0x40
};
Q_DECLARE_FLAGS(SslOptions, SslOption)
}
diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp
index afbd4fac77..6cc06dfbd2 100644
--- a/src/network/ssl/qsslconfiguration.cpp
+++ b/src/network/ssl/qsslconfiguration.cpp
@@ -49,7 +49,8 @@ QT_BEGIN_NAMESPACE
const QSsl::SslOptions QSslConfigurationPrivate::defaultSslOptions = QSsl::SslOptionDisableEmptyFragments
|QSsl::SslOptionDisableLegacyRenegotiation
- |QSsl::SslOptionDisableCompression;
+ |QSsl::SslOptionDisableCompression
+ |QSsl::SslOptionDisableSessionPersistence;
/*!
\class QSslConfiguration
@@ -182,7 +183,9 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const
d->peerVerifyMode == other.d->peerVerifyMode &&
d->peerVerifyDepth == other.d->peerVerifyDepth &&
d->allowRootCertOnDemandLoading == other.d->allowRootCertOnDemandLoading &&
- d->sslOptions == other.d->sslOptions;
+ d->sslOptions == other.d->sslOptions &&
+ d->sslSession == other.d->sslSession &&
+ d->sslSessionTicketLifeTimeHint == other.d->sslSessionTicketLifeTimeHint;
}
/*!
@@ -216,7 +219,9 @@ bool QSslConfiguration::isNull() const
d->privateKey.isNull() &&
d->peerCertificate.isNull() &&
d->peerCertificateChain.count() == 0 &&
- d->sslOptions == QSslConfigurationPrivate::defaultSslOptions);
+ d->sslOptions == QSslConfigurationPrivate::defaultSslOptions &&
+ d->sslSession.isNull() &&
+ d->sslSessionTicketLifeTimeHint == -1);
}
/*!
@@ -594,6 +599,60 @@ bool QSslConfiguration::testSslOption(QSsl::SslOption option) const
}
/*!
+ \since 5.2
+
+ If QSsl::SslOptionDisableSessionPersistence was turned off, this
+ function returns the session used in the SSL handshake in ASN.1
+ format, suitable to e.g. be persisted to disk. If no session was
+ used or QSsl::SslOptionDisableSessionPersistence was not turned off,
+ this function returns an empty QByteArray.
+
+ \b{Note:} When persisting the session to disk or similar, be
+ careful not to expose the session to a potential attacker, as
+ knowledge of the session allows for eavesdropping on data
+ encrypted with the session parameters.
+
+ \sa setSession(), QSsl::SslOptionDisableSessionPersistence, setSslOption()
+ */
+QByteArray QSslConfiguration::session() const
+{
+ return d->sslSession;
+}
+
+/*!
+ \since 5.2
+
+ Sets the session to be used in an SSL handshake.
+ QSsl::SslOptionDisableSessionPersistence must be turned off
+ for this to work, and \a session must be in ASN.1 format
+ as returned by session().
+
+ \sa session(), QSsl::SslOptionDisableSessionPersistence, setSslOption()
+ */
+void QSslConfiguration::setSession(const QByteArray &session)
+{
+ d->sslSession = session;
+}
+
+/*!
+ \since 5.2
+
+ If QSsl::SslOptionDisableSessionPersistence was turned off, this
+ function returns the session ticket life time hint sent by the
+ server (which might be 0).
+ If the server did not send a session ticket (e.g. when
+ resuming a session or when the server does not support it) or
+ QSsl::SslOptionDisableSessionPersistence was not turned off,
+ this function returns -1.
+
+ \sa session(), QSsl::SslOptionDisableSessionPersistence, setSslOption()
+ */
+int QSslConfiguration::sessionTicketLifeTimeHint() const
+{
+ return d->sslSessionTicketLifeTimeHint;
+}
+
+/*!
Returns the default SSL configuration to be used in new SSL
connections.
diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h
index 0000382ed5..949ce70d4c 100644
--- a/src/network/ssl/qsslconfiguration.h
+++ b/src/network/ssl/qsslconfiguration.h
@@ -124,6 +124,10 @@ public:
void setSslOption(QSsl::SslOption option, bool on);
bool testSslOption(QSsl::SslOption option) const;
+ QByteArray session() const;
+ void setSession(const QByteArray &session);
+ int sessionTicketLifeTimeHint() const;
+
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);
diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h
index 54b7264d3d..71ee8d2bfe 100644
--- a/src/network/ssl/qsslconfiguration_p.h
+++ b/src/network/ssl/qsslconfiguration_p.h
@@ -85,7 +85,8 @@ public:
peerVerifyDepth(0),
allowRootCertOnDemandLoading(true),
peerSessionShared(false),
- sslOptions(QSslConfigurationPrivate::defaultSslOptions)
+ sslOptions(QSslConfigurationPrivate::defaultSslOptions),
+ sslSessionTicketLifeTimeHint(-1)
{ }
QSslCertificate peerCertificate;
@@ -110,6 +111,9 @@ public:
Q_AUTOTEST_EXPORT static const QSsl::SslOptions defaultSslOptions;
+ QByteArray sslSession;
+ int sslSessionTicketLifeTimeHint;
+
// in qsslsocket.cpp:
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);
diff --git a/src/network/ssl/qsslcontext.cpp b/src/network/ssl/qsslcontext.cpp
index 22ad42116b..6d281c390d 100644
--- a/src/network/ssl/qsslcontext.cpp
+++ b/src/network/ssl/qsslcontext.cpp
@@ -57,7 +57,8 @@ extern QString getErrorsFromOpenSsl();
QSslContext::QSslContext()
: ctx(0),
pkey(0),
- session(0)
+ session(0),
+ m_sessionTicketLifeTimeHint(-1)
{
}
@@ -258,6 +259,10 @@ init_context:
if (sslContext->sslConfiguration.peerVerifyDepth() != 0)
q_SSL_CTX_set_verify_depth(sslContext->ctx, sslContext->sslConfiguration.peerVerifyDepth());
+ // set persisted session if the user set it
+ if (!configuration.session().isEmpty())
+ sslContext->setSessionASN1(configuration.session());
+
return sslContext;
}
@@ -267,6 +272,12 @@ SSL* QSslContext::createSsl()
SSL* ssl = q_SSL_new(ctx);
q_SSL_clear(ssl);
+ 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
+ }
+
if (session) {
// Try to resume the last session we cached
if (!q_SSL_set_session(ssl, session)) {
@@ -292,8 +303,34 @@ bool QSslContext::cacheSession(SSL* ssl)
// cache the session the caller gave us and increase reference count
session = q_SSL_get1_session(ssl);
- return (session != NULL);
+ if (session && !sslConfiguration.testSslOption(QSsl::SslOptionDisableSessionPersistence)) {
+ int sessionSize = q_i2d_SSL_SESSION(session, 0);
+ if (sessionSize > 0) {
+ m_sessionASN1.resize(sessionSize);
+ unsigned char *data = reinterpret_cast<unsigned char *>(m_sessionASN1.data());
+ if (!q_i2d_SSL_SESSION(session, &data))
+ qWarning("could not store persistent version of SSL session");
+ m_sessionTicketLifeTimeHint = session->tlsext_tick_lifetime_hint;
+ }
+ }
+
+ return (session != 0);
+}
+
+QByteArray QSslContext::sessionASN1() const
+{
+ return m_sessionASN1;
+}
+
+void QSslContext::setSessionASN1(const QByteArray &session)
+{
+ m_sessionASN1 = session;
+}
+
+int QSslContext::sessionTicketLifeTimeHint() const
+{
+ return m_sessionTicketLifeTimeHint;
}
QSslError::SslError QSslContext::error() const
diff --git a/src/network/ssl/qsslcontext_p.h b/src/network/ssl/qsslcontext_p.h
index c8578d349e..2b596798a6 100644
--- a/src/network/ssl/qsslcontext_p.h
+++ b/src/network/ssl/qsslcontext_p.h
@@ -69,6 +69,9 @@ public:
SSL* createSsl();
bool cacheSession(SSL*); // should be called when handshake completed
+ QByteArray sessionASN1() const;
+ void setSessionASN1(const QByteArray &sessionASN1);
+ int sessionTicketLifeTimeHint() const;
protected:
QSslContext();
@@ -76,6 +79,8 @@ private:
SSL_CTX* ctx;
EVP_PKEY *pkey;
SSL_SESSION *session;
+ QByteArray m_sessionASN1;
+ int m_sessionTicketLifeTimeHint;
QSslError::SslError errorCode;
QString errorStr;
QSslConfiguration sslConfiguration;
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index f2310356df..0e7ac39d14 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -903,6 +903,8 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration)
d->configuration.peerVerifyMode = configuration.peerVerifyMode();
d->configuration.protocol = configuration.protocol();
d->configuration.sslOptions = configuration.d->sslOptions;
+ d->configuration.sslSession = configuration.session();
+ d->configuration.sslSessionTicketLifeTimeHint = configuration.sessionTicketLifeTimeHint();
// if the CA certificates were set explicitly (either via
// QSslConfiguration::setCaCertificates() or QSslSocket::setCaCertificates(),
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 30103edc29..e94df10fed 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -80,7 +80,7 @@ QT_BEGIN_NAMESPACE
#if defined(Q_OS_MACX)
#define kSecTrustSettingsDomainSystem 2 // so we do not need to include the header file
- PtrSecCertificateGetData QSslSocketPrivate::ptrSecCertificateGetData = 0;
+ PtrSecCertificateCopyData QSslSocketPrivate::ptrSecCertificateCopyData = 0;
PtrSecTrustSettingsCopyCertificates QSslSocketPrivate::ptrSecTrustSettingsCopyCertificates = 0;
PtrSecTrustCopyAnchorCertificates QSslSocketPrivate::ptrSecTrustCopyAnchorCertificates = 0;
#elif defined(Q_OS_WIN)
@@ -492,8 +492,8 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded()
#if defined(Q_OS_MACX)
QLibrary securityLib("/System/Library/Frameworks/Security.framework/Versions/Current/Security");
if (securityLib.load()) {
- ptrSecCertificateGetData = (PtrSecCertificateGetData) securityLib.resolve("SecCertificateGetData");
- if (!ptrSecCertificateGetData)
+ ptrSecCertificateCopyData = (PtrSecCertificateCopyData) securityLib.resolve("SecCertificateCopyData");
+ if (!ptrSecCertificateCopyData)
qWarning("could not resolve symbols in security library"); // should never happen
ptrSecTrustSettingsCopyCertificates = (PtrSecTrustSettingsCopyCertificates) securityLib.resolve("SecTrustSettingsCopyCertificates");
@@ -629,12 +629,11 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
CFArrayRef cfCerts;
OSStatus status = 1;
- OSStatus SecCertificateGetData (
- SecCertificateRef certificate,
- CSSM_DATA_PTR data
+ CFDataRef SecCertificateCopyData (
+ SecCertificateRef certificate
);
- if (ptrSecCertificateGetData) {
+ if (ptrSecCertificateCopyData) {
if (ptrSecTrustSettingsCopyCertificates)
status = ptrSecTrustSettingsCopyCertificates(kSecTrustSettingsDomainSystem, &cfCerts);
else if (ptrSecTrustCopyAnchorCertificates)
@@ -643,15 +642,16 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
CFIndex size = CFArrayGetCount(cfCerts);
for (CFIndex i = 0; i < size; ++i) {
SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i);
- CSSM_DATA data;
- CSSM_DATA_PTR dataPtr = &data;
- if (ptrSecCertificateGetData(cfCert, dataPtr)) {
+ CFDataRef data;
+
+ data = ptrSecCertificateCopyData(cfCert);
+
+ if (data == NULL) {
qWarning("error retrieving a CA certificate from the system store");
} else {
- int len = data.Length;
- char *rawData = reinterpret_cast<char *>(data.Data);
- QByteArray rawCert(rawData, len);
+ QByteArray rawCert = QByteArray::fromRawData((const char *)CFDataGetBytePtr(data), CFDataGetLength(data));
systemCerts.append(QSslCertificate::fromData(rawCert, QSsl::Der));
+ CFRelease(data);
}
}
CFRelease(cfCerts);
@@ -1458,9 +1458,17 @@ void QSslSocketBackendPrivate::continueHandshake()
#endif
// Cache this SSL session inside the QSslContext
- if (!(configuration.sslOptions & QSsl::SslOptionDisableSessionTickets)) {
- if (!sslContextPointer->cacheSession(ssl))
+ if (!(configuration.sslOptions & QSsl::SslOptionDisableSessionSharing)) {
+ if (!sslContextPointer->cacheSession(ssl)) {
sslContextPointer.clear(); // we could not cache the session
+ } else {
+ // Cache the session for permanent usage as well
+ if (!(configuration.sslOptions & QSsl::SslOptionDisableSessionPersistence)) {
+ if (!sslContextPointer->sessionASN1().isEmpty())
+ configuration.sslSession = sslContextPointer->sessionASN1();
+ configuration.sslSessionTicketLifeTimeHint = sslContextPointer->sessionTicketLifeTimeHint();
+ }
+ }
}
connectionEncrypted = true;
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index d2a349455e..a010075436 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -332,6 +332,8 @@ DEFINEFUNC(void, OPENSSL_add_all_algorithms_conf, void, DUMMYARG, return, DUMMYA
DEFINEFUNC3(int, SSL_CTX_load_verify_locations, SSL_CTX *ctx, ctx, const char *CAfile, CAfile, const char *CApath, CApath, return 0, return)
DEFINEFUNC(long, SSLeay, void, DUMMYARG, return 0, return)
DEFINEFUNC(const char *, SSLeay_version, int a, a, 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)
#define RESOLVEFUNC(func) \
if (!(_q_##func = _q_PTR_##func(libs.first->resolve(#func))) \
@@ -548,7 +550,7 @@ static QPair<QLibrary*, QLibrary*> loadOpenSsl()
#ifdef Q_OS_OPENBSD
libcrypto->setLoadHints(QLibrary::ExportExternalSymbolsHint);
#endif
-#ifdef SHLIB_VERSION_NUMBER
+#if defined(SHLIB_VERSION_NUMBER) && !defined(Q_OS_QNX) // on QNX, the libs are always libssl.so and libcrypto.so
// first attempt: the canonical name is libssl.so.<SHLIB_VERSION_NUMBER>
libssl->setFileNameAndVersion(QLatin1String("ssl"), QLatin1String(SHLIB_VERSION_NUMBER));
libcrypto->setFileNameAndVersion(QLatin1String("crypto"), QLatin1String(SHLIB_VERSION_NUMBER));
@@ -801,6 +803,8 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_CTX_load_verify_locations)
RESOLVEFUNC(SSLeay)
RESOLVEFUNC(SSLeay_version)
+ RESOLVEFUNC(i2d_SSL_SESSION)
+ RESOLVEFUNC(d2i_SSL_SESSION)
symbolsResolved = true;
delete libs.first;
diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h
index 2e01ee4d31..1fd98cc7fb 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -469,6 +469,8 @@ void q_OPENSSL_add_all_algorithms_conf();
int q_SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath);
long q_SSLeay();
const char *q_SSLeay_version(int type);
+int q_i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp);
+SSL_SESSION *q_d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp, long length);
// Helper function
class QDateTime;
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
index 6ce34ba06f..6281753225 100644
--- a/src/network/ssl/qsslsocket_p.h
+++ b/src/network/ssl/qsslsocket_p.h
@@ -79,7 +79,7 @@
QT_BEGIN_NAMESPACE
#if defined(Q_OS_MACX)
- typedef OSStatus (*PtrSecCertificateGetData)(SecCertificateRef, CSSM_DATA_PTR);
+ typedef CFDataRef (*PtrSecCertificateCopyData)(SecCertificateRef);
typedef OSStatus (*PtrSecTrustSettingsCopyCertificates)(int, CFArrayRef*);
typedef OSStatus (*PtrSecTrustCopyAnchorCertificates)(CFArrayRef*);
#endif
@@ -146,7 +146,7 @@ public:
static void addDefaultCaCertificates(const QList<QSslCertificate> &certs);
#if defined(Q_OS_MACX)
- static PtrSecCertificateGetData ptrSecCertificateGetData;
+ static PtrSecCertificateCopyData ptrSecCertificateCopyData;
static PtrSecTrustSettingsCopyCertificates ptrSecTrustSettingsCopyCertificates;
static PtrSecTrustCopyAnchorCertificates ptrSecTrustCopyAnchorCertificates;
#elif defined(Q_OS_WIN)