From 0eaac0a3a95f8a74c06d062d3aa7928cbb09d493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= Date: Wed, 23 Mar 2016 12:27:11 +0100 Subject: Add support for PSK on server side [ChangeLog][QtNetwork][QSslSocket] TLS PSK ciphers are possible in server sockets. Task-number: QTBUG-39077 Change-Id: Iaa854a6f50242deae5492f2e4759c727488995f5 Reviewed-by: Richard J. Moore --- src/network/ssl/qsslconfiguration.cpp | 28 +++++++++++++++++ src/network/ssl/qsslconfiguration.h | 3 ++ src/network/ssl/qsslconfiguration_p.h | 3 ++ src/network/ssl/qsslcontext_openssl.cpp | 5 +++ src/network/ssl/qsslsocket.cpp | 1 + src/network/ssl/qsslsocket_openssl.cpp | 42 ++++++++++++++++++++++++-- src/network/ssl/qsslsocket_openssl_p.h | 1 + src/network/ssl/qsslsocket_openssl_symbols.cpp | 4 +++ src/network/ssl/qsslsocket_openssl_symbols_p.h | 3 ++ 9 files changed, 88 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp index 1fff2c31dd..1eb253d202 100644 --- a/src/network/ssl/qsslconfiguration.cpp +++ b/src/network/ssl/qsslconfiguration.cpp @@ -210,6 +210,7 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const d->privateKey == other.d->privateKey && d->sessionCipher == other.d->sessionCipher && d->sessionProtocol == other.d->sessionProtocol && + d->preSharedKeyIdentityHint == other.d->preSharedKeyIdentityHint && d->ciphers == other.d->ciphers && d->ellipticCurves == other.d->ellipticCurves && d->caCertificates == other.d->caCertificates && @@ -260,6 +261,7 @@ bool QSslConfiguration::isNull() const d->sslOptions == QSslConfigurationPrivate::defaultSslOptions && d->sslSession.isNull() && d->sslSessionTicketLifeTimeHint == -1 && + d->preSharedKeyIdentityHint.isNull() && d->nextAllowedProtocols.isEmpty() && d->nextNegotiatedProtocol.isNull() && d->nextProtocolNegotiationStatus == QSslConfiguration::NextProtocolNegotiationNone); @@ -809,6 +811,32 @@ QVector QSslConfiguration::supportedEllipticCurves() return QSslSocketPrivate::supportedEllipticCurves(); } +/*! + \since 5.8 + + Returns the identity hint. + + \sa setPreSharedKeyIdentityHint() +*/ +QByteArray QSslConfiguration::preSharedKeyIdentityHint() const +{ + return d->preSharedKeyIdentityHint; +} + +/*! + \since 5.8 + + Sets the identity hint for a preshared key authentication. This will affect the next + initiated handshake; calling this function on an already-encrypted socket + will not affect the socket's identity hint. + + The identity hint is used in QSslSocket::SslServerMode only! +*/ +void QSslConfiguration::setPreSharedKeyIdentityHint(const QByteArray &hint) +{ + d->preSharedKeyIdentityHint = hint; +} + /*! \since 5.3 diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h index f0754d7ef5..1f39e1a06f 100644 --- a/src/network/ssl/qsslconfiguration.h +++ b/src/network/ssl/qsslconfiguration.h @@ -141,6 +141,9 @@ public: void setEllipticCurves(const QVector &curves); static QVector supportedEllipticCurves(); + QByteArray preSharedKeyIdentityHint() const; + void setPreSharedKeyIdentityHint(const QByteArray &hint); + static QSslConfiguration defaultConfiguration(); static void setDefaultConfiguration(const QSslConfiguration &configuration); diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h index 093c9d6598..113954d7d1 100644 --- a/src/network/ssl/qsslconfiguration_p.h +++ b/src/network/ssl/qsslconfiguration_p.h @@ -88,6 +88,7 @@ public: peerSessionShared(false), sslOptions(QSslConfigurationPrivate::defaultSslOptions), sslSessionTicketLifeTimeHint(-1), + preSharedKeyIdentityHint(), nextProtocolNegotiationStatus(QSslConfiguration::NextProtocolNegotiationNone) { } @@ -121,6 +122,8 @@ public: QSslKey ephemeralServerKey; + QByteArray preSharedKeyIdentityHint; + QList nextAllowedProtocols; QByteArray nextNegotiatedProtocol; QSslConfiguration::NextProtocolNegotiationStatus nextProtocolNegotiationStatus; diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp index b24fdad1f9..0db7e10409 100644 --- a/src/network/ssl/qsslcontext_openssl.cpp +++ b/src/network/ssl/qsslcontext_openssl.cpp @@ -344,6 +344,11 @@ init_context: } #endif // OPENSSL_NO_EC +#ifndef OPENSSL_NO_PSK + if (!client) + q_SSL_CTX_use_psk_identity_hint(sslContext->ctx, sslContext->sslConfiguration.preSharedKeyIdentityHint().constData()); +#endif // OPENSSL_NO_PSK + const QVector qcurves = sslContext->sslConfiguration.ellipticCurves(); if (!qcurves.isEmpty()) { #if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(OPENSSL_NO_EC) diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index bbc62c47ff..82df861859 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -915,6 +915,7 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration) d->configuration.privateKey = configuration.privateKey(); d->configuration.ciphers = configuration.ciphers(); d->configuration.ellipticCurves = configuration.ellipticCurves(); + d->configuration.preSharedKeyIdentityHint = configuration.preSharedKeyIdentityHint(); d->configuration.caCertificates = configuration.caCertificates(); d->configuration.peerVerifyDepth = configuration.peerVerifyDepth(); d->configuration.peerVerifyMode = configuration.peerVerifyMode(); diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 5c0c8674cd..5cbd2af323 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -201,6 +201,15 @@ static unsigned int q_ssl_psk_client_callback(SSL *ssl, Q_ASSERT(d); return d->tlsPskClientCallback(hint, identity, max_identity_len, psk, max_psk_len); } + +static unsigned int q_ssl_psk_server_callback(SSL *ssl, + const char *identity, + unsigned char *psk, unsigned int max_psk_len) +{ + QSslSocketBackendPrivate *d = reinterpret_cast(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData)); + Q_ASSERT(d); + return d->tlsPskServerCallback(identity, psk, max_psk_len); +} #endif } // extern "C" @@ -436,8 +445,12 @@ bool QSslSocketBackendPrivate::initSslContext() #if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK) // Set the client callback for PSK - if (q_SSLeay() >= 0x10001000L && mode == QSslSocket::SslClientMode) - q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback); + if (q_SSLeay() >= 0x10001000L) { + if (mode == QSslSocket::SslClientMode) + q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback); + else if (mode == QSslSocket::SslServerMode) + q_SSL_set_psk_server_callback(ssl, &q_ssl_psk_server_callback); + } #endif return true; @@ -1260,6 +1273,31 @@ unsigned int QSslSocketBackendPrivate::tlsPskClientCallback(const char *hint, return pskLength; } +unsigned int QSslSocketBackendPrivate::tlsPskServerCallback(const char *identity, + unsigned char *psk, unsigned int max_psk_len) +{ + QSslPreSharedKeyAuthenticator authenticator; + + // Fill in some read-only fields (for the user) + authenticator.d->identityHint = configuration.preSharedKeyIdentityHint; + authenticator.d->identity = identity; + authenticator.d->maximumIdentityLength = 0; // user cannot set an identity + authenticator.d->maximumPreSharedKeyLength = int(max_psk_len); + + // Let the client provide the remaining bits... + Q_Q(QSslSocket); + emit q->preSharedKeyAuthenticationRequired(&authenticator); + + // No PSK set? Return now to make the handshake fail + if (authenticator.preSharedKey().isEmpty()) + return 0; + + // Copy data back into OpenSSL + const int pskLength = qMin(authenticator.preSharedKey().length(), authenticator.maximumPreSharedKeyLength()); + ::memcpy(psk, authenticator.preSharedKey().constData(), pskLength); + return pskLength; +} + #ifdef Q_OS_WIN void QSslSocketBackendPrivate::fetchCaRootForCert(const QSslCertificate &cert) diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h index 0674c05d71..c6572315f0 100644 --- a/src/network/ssl/qsslsocket_openssl_p.h +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -143,6 +143,7 @@ public: bool checkSslErrors(); void storePeerCertificates(); unsigned int tlsPskClientCallback(const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len); + unsigned int tlsPskServerCallback(const char *identity, unsigned char *psk, unsigned int max_psk_len); #ifdef Q_OS_WIN void fetchCaRootForCert(const QSslCertificate &cert); void _q_caRootLoaded(QSslCertificate,QSslCertificate) Q_DECL_OVERRIDE; diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index 05b7e2da7f..f625fd3e96 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -300,6 +300,8 @@ DEFINEFUNC2(void *, SSL_get_ex_data, const SSL *ssl, ssl, int idx, idx, return N #endif #if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK) DEFINEFUNC2(void, SSL_set_psk_client_callback, SSL* ssl, ssl, q_psk_client_callback_t callback, callback, return, DUMMYARG) +DEFINEFUNC2(void, SSL_set_psk_server_callback, SSL* ssl, ssl, q_psk_server_callback_t callback, callback, return, DUMMYARG) +DEFINEFUNC2(int, SSL_CTX_use_psk_identity_hint, SSL_CTX* ctx, ctx, const char *hint, hint, return 0, return) #endif #if OPENSSL_VERSION_NUMBER >= 0x10000000L #ifndef OPENSSL_NO_SSL2 @@ -901,6 +903,8 @@ bool q_resolveOpenSslSymbols() #endif #if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK) RESOLVEFUNC(SSL_set_psk_client_callback) + RESOLVEFUNC(SSL_set_psk_server_callback) + RESOLVEFUNC(SSL_CTX_use_psk_identity_hint) #endif RESOLVEFUNC(SSL_write) #ifndef OPENSSL_NO_SSL2 diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index 5a6c934d1a..45e4380580 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -376,6 +376,9 @@ void *q_SSL_get_ex_data(const SSL *ssl, int idx); #ifndef OPENSSL_NO_PSK typedef unsigned int (*q_psk_client_callback_t)(SSL *ssl, const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len); void q_SSL_set_psk_client_callback(SSL *ssl, q_psk_client_callback_t callback); +typedef unsigned int (*q_psk_server_callback_t)(SSL *ssl, const char *identity, unsigned char *psk, unsigned int max_psk_len); +void q_SSL_set_psk_server_callback(SSL *ssl, q_psk_server_callback_t callback); +int q_SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *hint); #endif // OPENSSL_NO_PSK #if OPENSSL_VERSION_NUMBER >= 0x10000000L #ifndef OPENSSL_NO_SSL2 -- cgit v1.2.3