qsslsocket/qsslcontext - add ALPN (OpenSSL only)
Application-Layer Protocol Negotiation (ALPN) - is a reworked revision of Next Protocol Negotiation (NPN) we have in our OpenSSL code. Can be used as a part of HTTP2 negotiation during TLS handshake. Change-Id: I484ec528c81d4887a64749095ec292dfaec18330 Reviewed-by: Richard J. Moore <rich@kde.org>
This commit is contained in:
parent
fb6000a74f
commit
765eab5103
@ -119,7 +119,8 @@ const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1";
|
|||||||
/*!
|
/*!
|
||||||
\enum QSslConfiguration::NextProtocolNegotiationStatus
|
\enum QSslConfiguration::NextProtocolNegotiationStatus
|
||||||
|
|
||||||
Describes the status of the Next Protocol Negotiation (NPN).
|
Describes the status of the Next Protocol Negotiation (NPN) or
|
||||||
|
Application-Layer Protocol Negotiation (ALPN).
|
||||||
|
|
||||||
\value NextProtocolNegotiationNone No application protocol
|
\value NextProtocolNegotiationNone No application protocol
|
||||||
has been negotiated (yet).
|
has been negotiated (yet).
|
||||||
@ -812,8 +813,9 @@ QVector<QSslEllipticCurve> QSslConfiguration::supportedEllipticCurves()
|
|||||||
\since 5.3
|
\since 5.3
|
||||||
|
|
||||||
This function returns the protocol negotiated with the server
|
This function returns the protocol negotiated with the server
|
||||||
if the Next Protocol Negotiation (NPN) TLS extension was enabled.
|
if the Next Protocol Negotiation (NPN) or Application-Layer Protocol
|
||||||
In order for the NPN extension to be enabled, setAllowedNextProtocols()
|
Negotiation (ALPN) TLS extension was enabled.
|
||||||
|
In order for the NPN/ALPN extension to be enabled, setAllowedNextProtocols()
|
||||||
needs to be called explicitly before connecting to the server.
|
needs to be called explicitly before connecting to the server.
|
||||||
|
|
||||||
If no protocol could be negotiated or the extension was not enabled,
|
If no protocol could be negotiated or the extension was not enabled,
|
||||||
@ -830,9 +832,10 @@ QByteArray QSslConfiguration::nextNegotiatedProtocol() const
|
|||||||
\since 5.3
|
\since 5.3
|
||||||
|
|
||||||
This function sets the allowed \a protocols to be negotiated with the
|
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 Next Protocol Negotiation (NPN) or Application-Layer
|
||||||
|
Protocol Negotiation (ALPN) TLS extension; each
|
||||||
element in \a protocols must define one allowed protocol.
|
element in \a protocols must define one allowed protocol.
|
||||||
The function must be called explicitly before connecting to send the NPN
|
The function must be called explicitly before connecting to send the NPN/ALPN
|
||||||
extension in the SSL handshake.
|
extension in the SSL handshake.
|
||||||
Whether or not the negotiation succeeded can be queried through
|
Whether or not the negotiation succeeded can be queried through
|
||||||
nextProtocolNegotiationStatus().
|
nextProtocolNegotiationStatus().
|
||||||
@ -852,8 +855,8 @@ void QSslConfiguration::setAllowedNextProtocols(QList<QByteArray> protocols)
|
|||||||
\since 5.3
|
\since 5.3
|
||||||
|
|
||||||
This function returns the allowed protocols to be negotiated with the
|
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 Next Protocol Negotiation (NPN) or Application-Layer
|
||||||
by setAllowedNextProtocols().
|
Protocol Negotiation (ALPN) TLS extension, as set by setAllowedNextProtocols().
|
||||||
|
|
||||||
\sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), setAllowedNextProtocols(), QSslConfiguration::NextProtocolSpdy3_0, QSslConfiguration::NextProtocolHttp1_1
|
\sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), setAllowedNextProtocols(), QSslConfiguration::NextProtocolSpdy3_0, QSslConfiguration::NextProtocolHttp1_1
|
||||||
*/
|
*/
|
||||||
@ -865,7 +868,8 @@ QList<QByteArray> QSslConfiguration::allowedNextProtocols() const
|
|||||||
/*!
|
/*!
|
||||||
\since 5.3
|
\since 5.3
|
||||||
|
|
||||||
This function returns the status of the Next Protocol Negotiation (NPN).
|
This function returns the status of the Next Protocol Negotiation (NPN)
|
||||||
|
or Application-Layer Protocol Negotiation (ALPN).
|
||||||
If the feature has not been enabled through setAllowedNextProtocols(),
|
If the feature has not been enabled through setAllowedNextProtocols(),
|
||||||
this function returns NextProtocolNegotiationNone.
|
this function returns NextProtocolNegotiationNone.
|
||||||
The status will be set before emitting the encrypted() signal.
|
The status will be set before emitting the encrypted() signal.
|
||||||
|
@ -457,8 +457,26 @@ SSL* QSslContext::createSsl()
|
|||||||
m_npnContext.data = reinterpret_cast<unsigned char *>(m_supportedNPNVersions.data());
|
m_npnContext.data = reinterpret_cast<unsigned char *>(m_supportedNPNVersions.data());
|
||||||
m_npnContext.len = m_supportedNPNVersions.count();
|
m_npnContext.len = m_supportedNPNVersions.count();
|
||||||
m_npnContext.status = QSslConfiguration::NextProtocolNegotiationNone;
|
m_npnContext.status = QSslConfiguration::NextProtocolNegotiationNone;
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
if (q_SSLeay() >= 0x10002000L) {
|
||||||
|
// Callback's type has a parameter 'const unsigned char ** out'
|
||||||
|
// since it was introduced in 1.0.2. Internally, OpenSSL's own code
|
||||||
|
// (tests/examples) cast it to unsigned char * (since it's 'out').
|
||||||
|
// We just re-use our NPN callback and cast here:
|
||||||
|
typedef int (*alpn_callback_t) (SSL *, const unsigned char **, unsigned char *,
|
||||||
|
const unsigned char *, unsigned int, void *);
|
||||||
|
// With ALPN callback is for a server side only, for a client m_npnContext.status
|
||||||
|
// will stay in NextProtocolNegotiationNone.
|
||||||
|
q_SSL_CTX_set_alpn_select_cb(ctx, alpn_callback_t(next_proto_cb), &m_npnContext);
|
||||||
|
// Client:
|
||||||
|
q_SSL_set_alpn_protos(ssl, m_npnContext.data, m_npnContext.len);
|
||||||
|
} else {
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L ...
|
||||||
q_SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &m_npnContext);
|
q_SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &m_npnContext);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
|
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
|
||||||
|
|
||||||
return ssl;
|
return ssl;
|
||||||
|
@ -1565,7 +1565,20 @@ void QSslSocketBackendPrivate::continueHandshake()
|
|||||||
} else {
|
} else {
|
||||||
const unsigned char *proto = 0;
|
const unsigned char *proto = 0;
|
||||||
unsigned int proto_len = 0;
|
unsigned int proto_len = 0;
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
if (q_SSLeay() >= 0x10002000L) {
|
||||||
|
q_SSL_get0_alpn_selected(ssl, &proto, &proto_len);
|
||||||
|
if (proto_len && mode == QSslSocket::SslClientMode) {
|
||||||
|
// Client does not have a callback that sets it ...
|
||||||
|
configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNegotiated;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
#endif
|
||||||
q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len);
|
q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len);
|
||||||
|
}
|
||||||
|
|
||||||
if (proto_len)
|
if (proto_len)
|
||||||
configuration.nextNegotiatedProtocol = QByteArray(reinterpret_cast<const char *>(proto), proto_len);
|
configuration.nextNegotiatedProtocol = QByteArray(reinterpret_cast<const char *>(proto), proto_len);
|
||||||
else
|
else
|
||||||
|
@ -418,6 +418,18 @@ DEFINEFUNC3(void, SSL_CTX_set_next_proto_select_cb, SSL_CTX *s, s,
|
|||||||
void *arg, arg, return, DUMMYARG)
|
void *arg, arg, return, DUMMYARG)
|
||||||
DEFINEFUNC3(void, SSL_get0_next_proto_negotiated, const SSL *s, s,
|
DEFINEFUNC3(void, SSL_get0_next_proto_negotiated, const SSL *s, s,
|
||||||
const unsigned char **data, data, unsigned *len, len, return, DUMMYARG)
|
const unsigned char **data, data, unsigned *len, len, return, DUMMYARG)
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
DEFINEFUNC3(int, SSL_set_alpn_protos, SSL *s, s, const unsigned char *protos, protos,
|
||||||
|
unsigned protos_len, protos_len, return -1, return)
|
||||||
|
DEFINEFUNC3(void, SSL_CTX_set_alpn_select_cb, SSL_CTX *s, s,
|
||||||
|
int (*cb) (SSL *ssl, const 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 // OPENSSL_VERSION_NUMBER >= 0x10002000L ...
|
||||||
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
|
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
|
||||||
DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return 0, return)
|
DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return 0, return)
|
||||||
DEFINEFUNC(void, DH_free, DH *dh, dh, return, DUMMYARG)
|
DEFINEFUNC(void, DH_free, DH *dh, dh, return, DUMMYARG)
|
||||||
|
@ -558,6 +558,19 @@ void q_SSL_CTX_set_next_proto_select_cb(SSL_CTX *s,
|
|||||||
void *arg);
|
void *arg);
|
||||||
void q_SSL_get0_next_proto_negotiated(const SSL *s, const unsigned char **data,
|
void q_SSL_get0_next_proto_negotiated(const SSL *s, const unsigned char **data,
|
||||||
unsigned *len);
|
unsigned *len);
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
int q_SSL_set_alpn_protos(SSL *ssl, const unsigned char *protos,
|
||||||
|
unsigned protos_len);
|
||||||
|
void q_SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx,
|
||||||
|
int (*cb) (SSL *ssl,
|
||||||
|
const 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 *ssl, const unsigned char **data,
|
||||||
|
unsigned *len);
|
||||||
|
#endif
|
||||||
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
|
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
|
||||||
|
|
||||||
// Helper function
|
// Helper function
|
||||||
|
@ -229,6 +229,7 @@ private slots:
|
|||||||
void simplePskConnect();
|
void simplePskConnect();
|
||||||
void ephemeralServerKey_data();
|
void ephemeralServerKey_data();
|
||||||
void ephemeralServerKey();
|
void ephemeralServerKey();
|
||||||
|
void allowedProtocolNegotiation();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void exitLoop()
|
static void exitLoop()
|
||||||
@ -3375,6 +3376,45 @@ void tst_QSslSocket::ephemeralServerKey()
|
|||||||
QCOMPARE(client->sslConfiguration().ephemeralServerKey().isNull(), emptyKey);
|
QCOMPARE(client->sslConfiguration().ephemeralServerKey().isNull(), emptyKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QSslSocket::allowedProtocolNegotiation()
|
||||||
|
{
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(OPENSSL_NO_TLSEXT)
|
||||||
|
|
||||||
|
QFETCH_GLOBAL(bool, setProxy);
|
||||||
|
if (setProxy)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const QByteArray expectedNegotiated("cool-protocol");
|
||||||
|
QList<QByteArray> serverProtos;
|
||||||
|
serverProtos << expectedNegotiated << "not-so-cool-protocol";
|
||||||
|
QList<QByteArray> clientProtos;
|
||||||
|
clientProtos << "uber-cool-protocol" << expectedNegotiated << "not-so-cool-protocol";
|
||||||
|
|
||||||
|
|
||||||
|
SslServer server;
|
||||||
|
server.config.setAllowedNextProtocols(serverProtos);
|
||||||
|
QVERIFY(server.listen());
|
||||||
|
|
||||||
|
QSslSocket clientSocket;
|
||||||
|
auto configuration = clientSocket.sslConfiguration();
|
||||||
|
configuration.setAllowedNextProtocols(clientProtos);
|
||||||
|
clientSocket.setSslConfiguration(configuration);
|
||||||
|
|
||||||
|
clientSocket.connectToHostEncrypted("127.0.0.1", server.serverPort());
|
||||||
|
clientSocket.ignoreSslErrors();
|
||||||
|
|
||||||
|
QEventLoop loop;
|
||||||
|
QTimer::singleShot(5000, &loop, SLOT(quit()));
|
||||||
|
connect(&clientSocket, SIGNAL(encrypted()), &loop, SLOT(quit()));
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
QVERIFY(server.socket->sslConfiguration().nextNegotiatedProtocol() ==
|
||||||
|
clientSocket.sslConfiguration().nextNegotiatedProtocol());
|
||||||
|
QVERIFY(server.socket->sslConfiguration().nextNegotiatedProtocol() == expectedNegotiated);
|
||||||
|
|
||||||
|
#endif // OPENSSL_VERSION_NUMBER
|
||||||
|
}
|
||||||
|
|
||||||
#endif // QT_NO_OPENSSL
|
#endif // QT_NO_OPENSSL
|
||||||
|
|
||||||
#endif // QT_NO_SSL
|
#endif // QT_NO_SSL
|
||||||
|
Loading…
x
Reference in New Issue
Block a user