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
|
||||
|
||||
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
|
||||
has been negotiated (yet).
|
||||
@ -812,8 +813,9 @@ QVector<QSslEllipticCurve> QSslConfiguration::supportedEllipticCurves()
|
||||
\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 Next Protocol Negotiation (NPN) or Application-Layer Protocol
|
||||
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.
|
||||
|
||||
If no protocol could be negotiated or the extension was not enabled,
|
||||
@ -830,9 +832,10 @@ 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 Next Protocol Negotiation (NPN) or Application-Layer
|
||||
Protocol Negotiation (ALPN) TLS extension; each
|
||||
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.
|
||||
Whether or not the negotiation succeeded can be queried through
|
||||
nextProtocolNegotiationStatus().
|
||||
@ -852,8 +855,8 @@ void QSslConfiguration::setAllowedNextProtocols(QList<QByteArray> 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().
|
||||
server through the Next Protocol Negotiation (NPN) or Application-Layer
|
||||
Protocol Negotiation (ALPN) TLS extension, as set by setAllowedNextProtocols().
|
||||
|
||||
\sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), setAllowedNextProtocols(), QSslConfiguration::NextProtocolSpdy3_0, QSslConfiguration::NextProtocolHttp1_1
|
||||
*/
|
||||
@ -865,7 +868,8 @@ QList<QByteArray> QSslConfiguration::allowedNextProtocols() const
|
||||
/*!
|
||||
\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(),
|
||||
this function returns NextProtocolNegotiationNone.
|
||||
The status will be set before emitting the encrypted() signal.
|
||||
|
@ -457,7 +457,25 @@ SSL* QSslContext::createSsl()
|
||||
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);
|
||||
#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);
|
||||
}
|
||||
}
|
||||
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
|
||||
|
||||
|
@ -1565,7 +1565,20 @@ void QSslSocketBackendPrivate::continueHandshake()
|
||||
} else {
|
||||
const unsigned char *proto = 0;
|
||||
unsigned int proto_len = 0;
|
||||
q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len);
|
||||
#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);
|
||||
}
|
||||
|
||||
if (proto_len)
|
||||
configuration.nextNegotiatedProtocol = QByteArray(reinterpret_cast<const char *>(proto), proto_len);
|
||||
else
|
||||
|
@ -418,6 +418,18 @@ DEFINEFUNC3(void, SSL_CTX_set_next_proto_select_cb, SSL_CTX *s, s,
|
||||
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)
|
||||
#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 ...
|
||||
DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return 0, return)
|
||||
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 q_SSL_get0_next_proto_negotiated(const SSL *s, const unsigned char **data,
|
||||
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 ...
|
||||
|
||||
// Helper function
|
||||
|
@ -229,6 +229,7 @@ private slots:
|
||||
void simplePskConnect();
|
||||
void ephemeralServerKey_data();
|
||||
void ephemeralServerKey();
|
||||
void allowedProtocolNegotiation();
|
||||
#endif
|
||||
|
||||
static void exitLoop()
|
||||
@ -3375,6 +3376,45 @@ void tst_QSslSocket::ephemeralServerKey()
|
||||
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_SSL
|
||||
|
Loading…
x
Reference in New Issue
Block a user