diff options
author | Peter Hartmann <phartmann@blackberry.com> | 2013-08-28 10:56:24 +0200 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-02-11 15:37:10 +0100 |
commit | 42cfb5fe4daa586f382bde6936b0ee33b5298f4d (patch) | |
tree | 8c55a9a461f9ec7d4722e6367103c8ae50982e86 /src/network/ssl | |
parent | df62c31807f7b0a8b9bc222b47ccc7016cfaee65 (diff) | |
download | qtbase-42cfb5fe4daa586f382bde6936b0ee33b5298f4d.tar.gz |
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/ssl')
-rw-r--r-- | src/network/ssl/qsslconfiguration.cpp | 106 | ||||
-rw-r--r-- | src/network/ssl/qsslconfiguration.h | 16 | ||||
-rw-r--r-- | src/network/ssl/qsslconfiguration_p.h | 8 | ||||
-rw-r--r-- | src/network/ssl/qsslcontext.cpp | 60 | ||||
-rw-r--r-- | src/network/ssl/qsslcontext_p.h | 20 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket.cpp | 4 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl.cpp | 9 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl_symbols.cpp | 20 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket_openssl_symbols_p.h | 15 |
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); |