commit 1d1237391f1a82200094a053378ab00683ac5ce6 Author: Allan Sandfeld Jensen Date: Tue Feb 10 10:59:30 2015 +0100 WIP: ALPN support Change-Id: I45c509e8dca8aeaf1f509d1754cf8a5a56c8a036 diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp index 1686d9e..d1a79be 100644 --- a/src/network/ssl/qsslconfiguration.cpp +++ b/src/network/ssl/qsslconfiguration.cpp @@ -112,7 +112,8 @@ const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1"; /*! \enum QSslConfiguration::NextProtocolNegotiationStatus - Describes the status of the Next Protocol Negotiation (NPN). + Describes the status of the Application-Layer Protocol Negotiation (ALPN) + or its predecessor Next Protocol Negotiation (NPN). \value NextProtocolNegotiationNone No application protocol has been negotiated (yet). @@ -126,14 +127,14 @@ const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1"; /*! \variable QSslConfiguration::NextProtocolSpdy3_0 - \brief The value used for negotiating SPDY 3.0 during the Next - Protocol Negotiation. + \brief The value used for negotiating SPDY 3.0 during the + Application-Layer Protocol Negotiation. */ /*! \variable QSslConfiguration::NextProtocolHttp1_1 - \brief The value used for negotiating HTTP 1.1 during the Next - Protocol Negotiation. + \brief The value used for negotiating HTTP 1.1 during the + Application-Layer Protocol Negotiation */ /*! @@ -746,8 +747,9 @@ void QSslConfiguration::setEllipticCurves(const QVector &curv \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() + if the Application-Layer Protocol Negotiation (ALPN) or the + Next Protocol Negotiation (NPN) TLS extension was enabled. + In order for the ALPN or NPN extensions 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, @@ -764,10 +766,11 @@ QByteArray QSslConfiguration::nextNegotiatedProtocol() const \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 + server through the Application-Layer Protocol Negotiation (ALPN) or + 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. + The function must be called explicitly before connecting to send the ALPN + or NPN extension in the SSL handshake. Whether or not the negotiation succeeded can be queried through nextProtocolNegotiationStatus(). @@ -786,7 +789,8 @@ void QSslConfiguration::setAllowedNextProtocols(QList 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 + server through the Application-Layer Protocol Negotiation (ALPN) or + the Next Protocol Negotiation (NPN) TLS extensions, as set by setAllowedNextProtocols(). \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), setAllowedNextProtocols(), QSslConfiguration::NextProtocolSpdy3_0, QSslConfiguration::NextProtocolHttp1_1 @@ -799,7 +803,8 @@ QList QSslConfiguration::allowedNextProtocols() const /*! \since 5.3 - This function returns the status of the Next Protocol Negotiation (NPN). + This function returns the status of the Application-Layer Protocol (ALPN) or + Next Protocol (NPN) Negotiation. If the feature has not been enabled through setAllowedNextProtocols(), this function returns NextProtocolNegotiationNone. The status will be set before emitting the encrypted() signal. diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp index b21732c..6758d83 100644 --- a/src/network/ssl/qsslcontext_openssl.cpp +++ b/src/network/ssl/qsslcontext_openssl.cpp @@ -368,7 +368,7 @@ init_context: return sslContext; } -#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG) +#if (OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)) || OPENSSL_VERSION_NUMBER >= 0x1000200fL static int next_proto_cb(SSL *, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) @@ -395,7 +395,7 @@ static int next_proto_cb(SSL *, unsigned char **out, unsigned char *outlen, ctx->status = QSslConfiguration::NextProtocolNegotiationUnsupported; break; default: - qCWarning(lcSsl, "OpenSSL sent unknown NPN status"); + qCWarning(lcSsl, "OpenSSL sent unknown ALPN/NPN status"); } return SSL_TLSEXT_ERR_OK; @@ -428,13 +428,13 @@ SSL* QSslContext::createSsl() } } -#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG) +#if (OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)) || OPENSSL_VERSION_NUMBER >= 0x1000200fL QList protocols = sslConfiguration.d->nextAllowedProtocols; if (!protocols.isEmpty()) { m_supportedNPNVersions.clear(); for (int a = 0; a < protocols.count(); ++a) { if (protocols.at(a).size() > 255) { - qCWarning(lcSsl) << "TLS NPN extension" << protocols.at(a) + qCWarning(lcSsl) << "TLS NPN/ALPN extension" << protocols.at(a) << "is too long and will be truncated to 255 characters."; protocols[a] = protocols.at(a).left(255); } @@ -443,7 +443,12 @@ SSL* QSslContext::createSsl() m_npnContext.data = reinterpret_cast(m_supportedNPNVersions.data()); m_npnContext.len = m_supportedNPNVersions.count(); m_npnContext.status = QSslConfiguration::NextProtocolNegotiationNone; +#if OPENSSL_VERSION_NUMBER >= 0x1000200fL + q_SSL_CTX_set_alpn_select_cb(ctx, next_proto_cb, &m_npnContext); +#endif +#if !defined(OPENSSL_NO_NEXTPROTONEG) q_SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &m_npnContext); +#endif } #endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ... diff --git a/src/network/ssl/qsslcontext_openssl_p.h b/src/network/ssl/qsslcontext_openssl_p.h index 77c5b4e..43c7f2d 100644 --- a/src/network/ssl/qsslcontext_openssl_p.h +++ b/src/network/ssl/qsslcontext_openssl_p.h @@ -77,8 +77,9 @@ public: void setSessionASN1(const QByteArray &sessionASN1); int sessionTicketLifeTimeHint() const; -#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG) +#if (OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)) || OPENSSL_VERSION_NUMBER >= 0x1000200fL // must be public because we want to use it from an OpenSSL callback + // Handles both NPN and ALPN. struct NPNContext { NPNContext() : data(0), len(0), @@ -103,7 +104,7 @@ private: QSslError::SslError errorCode; QString errorStr; QSslConfiguration sslConfiguration; -#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG) +#if (OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)) || OPENSSL_VERSION_NUMBER >= 0x1000200fL QByteArray m_supportedNPNVersions; NPNContext m_npnContext; #endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ... diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 7cadf3e..d5e8750 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -1615,15 +1615,25 @@ void QSslSocketBackendPrivate::continueHandshake() } } -#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG) +#if (OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)) || OPENSSL_VERSION_NUMBER >= 0x1000200fL const unsigned char *proto = 0; unsigned int proto_len = 0; - q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len); +#if OPENSSL_VERSION_NUMBER >= 0x1000200fL + q_SSL_get0_alpn_selected(ssl, &proto, &proto_len); if (proto_len) configuration.nextNegotiatedProtocol = QByteArray(reinterpret_cast(proto), proto_len); - else - configuration.nextNegotiatedProtocol.clear(); configuration.nextProtocolNegotiationStatus = sslContextPointer->npnContext().status; +#endif +#if !defined(OPENSSL_NO_NEXTPROTONEG) + if (!proto_len) { + q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len); + if (proto_len) + configuration.nextNegotiatedProtocol = QByteArray(reinterpret_cast(proto), proto_len); + configuration.nextProtocolNegotiationStatus = sslContextPointer->npnContext().status; + } +#endif // !defined(OPENSSL_NO_NEXTPROTONEG) + if (!proto_len) + configuration.nextNegotiatedProtocol.clear(); #endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ... connectionEncrypted = true; diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index 61c703e..15bdac3 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -375,11 +375,13 @@ 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_NEXTPROTONEG) +#if (OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)) || OPENSSL_VERSION_NUMBER >= 0x1000200fL 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) +#endif +#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG) DEFINEFUNC3(void, SSL_CTX_set_next_proto_select_cb, SSL_CTX *s, s, int (*cb) (SSL *ssl, unsigned char **out, unsigned char *outlen, @@ -389,6 +391,16 @@ DEFINEFUNC3(void, SSL_CTX_set_next_proto_select_cb, SSL_CTX *s, s, 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 ... +#if OPENSSL_VERSION_NUMBER >= 0x1000200fL +DEFINEFUNC3(void, SSL_CTX_set_alpn_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_alpn_selected, const SSL *s, s, + const unsigned char **data, data, unsigned *len, len, return, DUMMYARG) +#endif DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return 0, return) DEFINEFUNC(void, DH_free, DH *dh, dh, return, DUMMYARG) DEFINEFUNC3(BIGNUM *, BN_bin2bn, const unsigned char *s, s, int len, len, BIGNUM *ret, ret, return 0, return) @@ -927,11 +939,17 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(SSLeay_version) RESOLVEFUNC(i2d_SSL_SESSION) RESOLVEFUNC(d2i_SSL_SESSION) -#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG) +#if (OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)) || OPENSSL_VERSION_NUMBER >= 0x1000200fL RESOLVEFUNC(SSL_select_next_proto) +#endif +#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG) RESOLVEFUNC(SSL_CTX_set_next_proto_select_cb) RESOLVEFUNC(SSL_get0_next_proto_negotiated) #endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ... +#if OPENSSL_VERSION_NUMBER >= 0x1000200fL + RESOLVEFUNC(SSL_CTX_set_alpn_select_cb) + RESOLVEFUNC(SSL_get0_alpn_selected) +#endif RESOLVEFUNC(DH_new) RESOLVEFUNC(DH_free) RESOLVEFUNC(BN_bin2bn) diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index 1141ed5..53aae70 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -510,10 +510,12 @@ 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_NEXTPROTONEG) +#if (OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)) || OPENSSL_VERSION_NUMBER >= 0x1000200fL 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); +#endif +#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG) void q_SSL_CTX_set_next_proto_select_cb(SSL_CTX *s, int (*cb) (SSL *ssl, unsigned char **out, unsigned char *outlen, @@ -523,7 +525,16 @@ void q_SSL_CTX_set_next_proto_select_cb(SSL_CTX *s, void q_SSL_get0_next_proto_negotiated(const SSL *s, const unsigned char **data, unsigned *len); #endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ... - +#if OPENSSL_VERSION_NUMBER >= 0x1000200fL +void q_SSL_CTX_set_alpn_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_alpn_selected(const SSL *s, const unsigned char **data, + unsigned *len); +#endif // Helper function class QDateTime; QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime);