summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
authorPeter Hartmann <phartmann@blackberry.com>2013-08-28 10:56:24 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-02-11 15:37:10 +0100
commit42cfb5fe4daa586f382bde6936b0ee33b5298f4d (patch)
tree8c55a9a461f9ec7d4722e6367103c8ae50982e86 /src/network
parentdf62c31807f7b0a8b9bc222b47ccc7016cfaee65 (diff)
SSL: add support for the Next Protocol Negotiation extension
... which is needed to negotiate the SPDY protocol. [ChangeLog][QtNetwork][QSslConfiguration] Added support for the Next Protocol Negotiation (NPN) TLS extension. Task-number: QTBUG-33208 Change-Id: I3c945f9b7e2d2ffb0814bfdd3e87de1dae6c20ef Reviewed-by: Allan Sandfeld Jensen <allan.jensen@digia.com>
Diffstat (limited to 'src/network')
-rw-r--r--src/network/ssl/qsslconfiguration.cpp106
-rw-r--r--src/network/ssl/qsslconfiguration.h16
-rw-r--r--src/network/ssl/qsslconfiguration_p.h8
-rw-r--r--src/network/ssl/qsslcontext.cpp60
-rw-r--r--src/network/ssl/qsslcontext_p.h20
-rw-r--r--src/network/ssl/qsslsocket.cpp4
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp9
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp20
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h15
9 files changed, 255 insertions, 3 deletions
diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp
index 3d7656262b..1e859ae6e6 100644
--- a/src/network/ssl/qsslconfiguration.cpp
+++ b/src/network/ssl/qsslconfiguration.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -52,6 +53,9 @@ const QSsl::SslOptions QSslConfigurationPrivate::defaultSslOptions = QSsl::SslOp
|QSsl::SslOptionDisableCompression
|QSsl::SslOptionDisableSessionPersistence;
+const char QSslConfiguration::NextProtocolSpdy3_0[] = "spdy/3";
+const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1";
+
/*!
\class QSslConfiguration
\brief The QSslConfiguration class holds the configuration and state of an SSL connection
@@ -113,6 +117,33 @@ const QSsl::SslOptions QSslConfigurationPrivate::defaultSslOptions = QSsl::SslOp
*/
/*!
+ \enum QSslConfiguration::NextProtocolNegotiationStatus
+
+ Describes the status of the Next Protocol Negotiation (NPN).
+
+ \value NextProtocolNegotiationNone No application protocol
+ has been negotiated (yet).
+
+ \value NextProtocolNegotiationNegotiated A next protocol
+ has been negotiated (see nextNegotiatedProtocol()).
+
+ \value NextProtocolNegotiationUnsupported The client and
+ server could not agree on a common next application protocol.
+*/
+
+/*!
+ \variable QSslConfiguration::NextProtocolSpdy3_0
+ \brief The value used for negotiating SPDY 3.0 during the Next
+ Protocol Negotiation.
+*/
+
+/*!
+ \variable QSslConfiguration::NextProtocolHttp1_1
+ \brief The value used for negotiating HTTP 1.1 during the Next
+ Protocol Negotiation.
+*/
+
+/*!
Constructs an empty SSL configuration. This configuration contains
no valid settings and the state will be empty. isNull() will
return true after this constructor is called.
@@ -185,7 +216,10 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const
d->allowRootCertOnDemandLoading == other.d->allowRootCertOnDemandLoading &&
d->sslOptions == other.d->sslOptions &&
d->sslSession == other.d->sslSession &&
- d->sslSessionTicketLifeTimeHint == other.d->sslSessionTicketLifeTimeHint;
+ d->sslSessionTicketLifeTimeHint == other.d->sslSessionTicketLifeTimeHint &&
+ d->nextAllowedProtocols == other.d->nextAllowedProtocols &&
+ d->nextNegotiatedProtocol == other.d->nextNegotiatedProtocol &&
+ d->nextProtocolNegotiationStatus == other.d->nextProtocolNegotiationStatus;
}
/*!
@@ -221,7 +255,10 @@ bool QSslConfiguration::isNull() const
d->peerCertificateChain.count() == 0 &&
d->sslOptions == QSslConfigurationPrivate::defaultSslOptions &&
d->sslSession.isNull() &&
- d->sslSessionTicketLifeTimeHint == -1);
+ d->sslSessionTicketLifeTimeHint == -1 &&
+ d->nextAllowedProtocols.isEmpty() &&
+ d->nextNegotiatedProtocol.isNull() &&
+ d->nextProtocolNegotiationStatus == QSslConfiguration::NextProtocolNegotiationNone);
}
/*!
@@ -653,6 +690,71 @@ int QSslConfiguration::sessionTicketLifeTimeHint() const
}
/*!
+ \since 5.3
+
+ This function returns the protocol negotiated with the server
+ if the Next Protocol Negotiation (NPN) TLS extension was enabled.
+ In order for the NPN extension to be enabled, setAllowedNextProtocols()
+ needs to be called explicitly before connecting to the server.
+
+ If no protocol could be negotiated or the extension was not enabled,
+ this function returns a QByteArray which is null.
+
+ \sa setAllowedNextProtocols(), nextProtocolNegotiationStatus()
+ */
+QByteArray QSslConfiguration::nextNegotiatedProtocol() const
+{
+ return d->nextNegotiatedProtocol;
+}
+
+/*!
+ \since 5.3
+
+ This function sets the allowed \a protocols to be negotiated with the
+ server through the Next Protocol Negotiation (NPN) TLS extension; each
+ element in \a protocols must define one allowed protocol.
+ The function must be called explicitly before connecting to send the NPN
+ extension in the SSL handshake.
+ Whether or not the negotiation succeeded can be queried through
+ nextProtocolNegotiationStatus().
+
+ \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), allowedNextProtocols(), QSslConfiguration::NextProtocolSpdy3_0, QSslConfiguration::NextProtocolHttp1_1
+ */
+void QSslConfiguration::setAllowedNextProtocols(QList<QByteArray> protocols)
+{
+ d->nextAllowedProtocols = protocols;
+}
+
+/*!
+ \since 5.3
+
+ This function returns the allowed protocols to be negotiated with the
+ server through the Next Protocol Negotiation (NPN) TLS extension, as set
+ by setAllowedNextProtocols().
+
+ \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), setAllowedNextProtocols(), QSslConfiguration::NextProtocolSpdy3_0, QSslConfiguration::NextProtocolHttp1_1
+ */
+QList<QByteArray> QSslConfiguration::allowedNextProtocols() const
+{
+ return d->nextAllowedProtocols;
+}
+
+/*!
+ \since 5.3
+
+ This function returns the status of the Next Protocol Negotiation (NPN).
+ If the feature has not been enabled through setAllowedNextProtocols(),
+ this function returns NextProtocolNegotiationNone.
+ The status will be set before emitting the encrypted() signal.
+
+ \sa setAllowedNextProtocols(), allowedNextProtocols(), nextNegotiatedProtocol(), QSslConfiguration::NextProtocolNegotiationStatus
+ */
+QSslConfiguration::NextProtocolNegotiationStatus QSslConfiguration::nextProtocolNegotiationStatus() const
+{
+ return d->nextProtocolNegotiationStatus;
+}
+
+/*!
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 a48eceb63e..587187ca06 100644
--- a/src/network/ssl/qsslconfiguration.h
+++ b/src/network/ssl/qsslconfiguration.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -131,6 +132,21 @@ public:
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);
+ enum NextProtocolNegotiationStatus {
+ NextProtocolNegotiationNone,
+ NextProtocolNegotiationNegotiated,
+ NextProtocolNegotiationUnsupported
+ };
+
+ void setAllowedNextProtocols(QList<QByteArray> protocols);
+ QList<QByteArray> allowedNextProtocols() const;
+
+ QByteArray nextNegotiatedProtocol() const;
+ NextProtocolNegotiationStatus nextProtocolNegotiationStatus() const;
+
+ static const char NextProtocolSpdy3_0[];
+ static const char NextProtocolHttp1_1[];
+
private:
friend class QSslSocket;
friend class QSslConfigurationPrivate;
diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h
index 71ee8d2bfe..d183c3335c 100644
--- a/src/network/ssl/qsslconfiguration_p.h
+++ b/src/network/ssl/qsslconfiguration_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -86,7 +87,8 @@ public:
allowRootCertOnDemandLoading(true),
peerSessionShared(false),
sslOptions(QSslConfigurationPrivate::defaultSslOptions),
- sslSessionTicketLifeTimeHint(-1)
+ sslSessionTicketLifeTimeHint(-1),
+ nextProtocolNegotiationStatus(QSslConfiguration::NextProtocolNegotiationNone)
{ }
QSslCertificate peerCertificate;
@@ -114,6 +116,10 @@ public:
QByteArray sslSession;
int sslSessionTicketLifeTimeHint;
+ QList<QByteArray> nextAllowedProtocols;
+ QByteArray nextNegotiatedProtocol;
+ QSslConfiguration::NextProtocolNegotiationStatus nextProtocolNegotiationStatus;
+
// 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 adf42fb79a..551804ec79 100644
--- a/src/network/ssl/qsslcontext.cpp
+++ b/src/network/ssl/qsslcontext.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -263,6 +264,45 @@ init_context:
return sslContext;
}
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+
+static int next_proto_cb(SSL *, unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen, void *arg)
+{
+ QSslContext::NPNContext *ctx = reinterpret_cast<QSslContext::NPNContext *>(arg);
+
+ // comment out to debug:
+// QList<QByteArray> supportedVersions;
+// for (unsigned int i = 0; i < inlen; ) {
+// QByteArray version(reinterpret_cast<const char *>(&in[i+1]), in[i]);
+// supportedVersions << version;
+// i += in[i] + 1;
+// }
+
+ int proto = q_SSL_select_next_proto(out, outlen, in, inlen, ctx->data, ctx->len);
+ switch (proto) {
+ case OPENSSL_NPN_UNSUPPORTED:
+ ctx->status = QSslConfiguration::NextProtocolNegotiationNone;
+ break;
+ case OPENSSL_NPN_NEGOTIATED:
+ ctx->status = QSslConfiguration::NextProtocolNegotiationNegotiated;
+ break;
+ case OPENSSL_NPN_NO_OVERLAP:
+ ctx->status = QSslConfiguration::NextProtocolNegotiationUnsupported;
+ break;
+ default:
+ qWarning("OpenSSL sent unknown NPN status");
+ }
+
+ return SSL_TLSEXT_ERR_OK;
+}
+
+QSslContext::NPNContext QSslContext::npnContext() const
+{
+ return m_npnContext;
+}
+#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
+
// Needs to be deleted by caller
SSL* QSslContext::createSsl()
{
@@ -283,6 +323,26 @@ SSL* QSslContext::createSsl()
session = 0;
}
}
+
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+ QList<QByteArray> protocols = sslConfiguration.d->nextAllowedProtocols;
+ if (!protocols.isEmpty()) {
+ m_supportedNPNVersions.clear();
+ for (int a = 0; a < protocols.count(); ++a) {
+ if (protocols.at(a).size() > 255) {
+ qWarning() << "TLS NPN extension" << protocols.at(a)
+ << "is too long and will be truncated to 255 characters.";
+ protocols[a] = protocols.at(a).left(255);
+ }
+ m_supportedNPNVersions.append(protocols.at(a).size()).append(protocols.at(a));
+ }
+ m_npnContext.data = reinterpret_cast<unsigned char *>(m_supportedNPNVersions.data());
+ m_npnContext.len = m_supportedNPNVersions.count();
+ m_npnContext.status = QSslConfiguration::NextProtocolNegotiationNone;
+ q_SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &m_npnContext);
+ }
+#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
+
return ssl;
}
diff --git a/src/network/ssl/qsslcontext_p.h b/src/network/ssl/qsslcontext_p.h
index 2b596798a6..20b27c1ce7 100644
--- a/src/network/ssl/qsslcontext_p.h
+++ b/src/network/ssl/qsslcontext_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -72,6 +73,21 @@ public:
QByteArray sessionASN1() const;
void setSessionASN1(const QByteArray &sessionASN1);
int sessionTicketLifeTimeHint() const;
+
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+ // must be public because we want to use it from an OpenSSL callback
+ struct NPNContext {
+ NPNContext() : data(0),
+ len(0),
+ status(QSslConfiguration::NextProtocolNegotiationNone)
+ { }
+ unsigned char *data;
+ unsigned short len;
+ QSslConfiguration::NextProtocolNegotiationStatus status;
+ };
+ NPNContext npnContext() const;
+#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
+
protected:
QSslContext();
@@ -84,6 +100,10 @@ private:
QSslError::SslError errorCode;
QString errorStr;
QSslConfiguration sslConfiguration;
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+ QByteArray m_supportedNPNVersions;
+ NPNContext m_npnContext;
+#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
};
#endif // QT_NO_SSL
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index e107d94cf2..6edf4efae0 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -905,6 +906,9 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration)
d->configuration.sslOptions = configuration.d->sslOptions;
d->configuration.sslSession = configuration.sessionTicket();
d->configuration.sslSessionTicketLifeTimeHint = configuration.sessionTicketLifeTimeHint();
+ d->configuration.nextAllowedProtocols = configuration.allowedNextProtocols();
+ d->configuration.nextNegotiatedProtocol = configuration.nextNegotiatedProtocol();
+ d->configuration.nextProtocolNegotiationStatus = configuration.nextProtocolNegotiationStatus();
// 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 ce6b6562b9..3421154114 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -1486,6 +1486,15 @@ void QSslSocketBackendPrivate::continueHandshake()
}
}
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+ const unsigned char *proto;
+ unsigned int proto_len;
+ q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len);
+ QByteArray nextProtocol(reinterpret_cast<const char *>(proto), proto_len);
+ configuration.nextNegotiatedProtocol = nextProtocol;
+ configuration.nextProtocolNegotiationStatus = sslContextPointer->npnContext().status;
+#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
+
connectionEncrypted = true;
emit q->encrypted();
if (autoStartHandshake && pendingClose) {
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index ddf53f18f4..79bce22b0d 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -346,6 +347,20 @@ 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)
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !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,
+ const unsigned char *client, client, unsigned int client_len, client_len,
+ return -1, return)
+DEFINEFUNC3(void, SSL_CTX_set_next_proto_select_cb, SSL_CTX *s, s,
+ int (*cb) (SSL *ssl, unsigned char **out,
+ unsigned char *outlen,
+ const unsigned char *in,
+ unsigned int inlen, void *arg), cb,
+ void *arg, arg, return, DUMMYARG)
+DEFINEFUNC3(void, SSL_get0_next_proto_negotiated, const SSL *s, s,
+ const unsigned char **data, data, unsigned *len, len, return, DUMMYARG)
+#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
#define RESOLVEFUNC(func) \
if (!(_q_##func = _q_PTR_##func(libs.first->resolve(#func))) \
@@ -815,6 +830,11 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSLeay_version)
RESOLVEFUNC(i2d_SSL_SESSION)
RESOLVEFUNC(d2i_SSL_SESSION)
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+ RESOLVEFUNC(SSL_select_next_proto)
+ RESOLVEFUNC(SSL_CTX_set_next_proto_select_cb)
+ RESOLVEFUNC(SSL_get0_next_proto_negotiated)
+#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
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 79d2aecf5e..500fe9493b 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -473,6 +474,20 @@ 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);
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+int q_SSL_select_next_proto(unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen,
+ const unsigned char *client, unsigned int client_len);
+void q_SSL_CTX_set_next_proto_select_cb(SSL_CTX *s,
+ int (*cb) (SSL *ssl, unsigned char **out,
+ unsigned char *outlen,
+ const unsigned char *in,
+ unsigned int inlen, void *arg),
+ void *arg);
+void q_SSL_get0_next_proto_negotiated(const SSL *s, const unsigned char **data,
+ unsigned *len);
+#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
+
// Helper function
class QDateTime;
QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime);