From bd26defd9bdddf8619a8d6ce1c6cb90b28c27d88 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Wed, 15 Oct 2014 15:13:47 +0200 Subject: QSslSocket: introduce support for TLS PSK (client side) [ChangeLog][QtNetwork][QSslSocket] It is now possible to use TLS PSK ciphersuites in client sockets. Task-number: QTBUG-39077 Change-Id: I5523a2be33d46230c6f4106c322fab8a5afa37b4 Reviewed-by: Richard J. Moore --- src/network/ssl/qsslsocket_openssl.cpp | 68 ++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) (limited to 'src/network/ssl/qsslsocket_openssl.cpp') diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index bf348f6f9f..509db38672 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -58,6 +58,8 @@ #include "qsslcipher_p.h" #include "qsslkey_p.h" #include "qsslellipticcurve.h" +#include "qsslpresharedkeyauthenticator.h" +#include "qsslpresharedkeyauthenticator_p.h" #include #include @@ -72,6 +74,8 @@ #include #include // for loading the security lib for the CA store +#include + QT_BEGIN_NAMESPACE #if defined(Q_OS_MACX) @@ -89,6 +93,10 @@ bool QSslSocketPrivate::s_libraryLoaded = false; bool QSslSocketPrivate::s_loadedCiphersAndCerts = false; bool QSslSocketPrivate::s_loadRootCertsOnDemand = false; +#if OPENSSL_VERSION_NUMBER >= 0x10001000L +int QSslSocketBackendPrivate::s_indexForSSLExtraData = -1; +#endif + /* \internal From OpenSSL's thread(3) manual page: @@ -181,6 +189,18 @@ static unsigned long id_function() { return (quintptr)QThread::currentThreadId(); } + +#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK) +static unsigned int q_ssl_psk_client_callback(SSL *ssl, + const char *hint, + char *identity, unsigned int max_identity_len, + 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->tlsPskClientCallback(hint, identity, max_identity_len, psk, max_psk_len); +} +#endif } // extern "C" QSslSocketBackendPrivate::QSslSocketBackendPrivate() @@ -390,6 +410,18 @@ bool QSslSocketBackendPrivate::initSslContext() else q_SSL_set_accept_state(ssl); +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + // Save a pointer to this object into the SSL structure. + if (q_SSLeay() >= 0x10001000L) + q_SSL_set_ex_data(ssl, s_indexForSSLExtraData, this); +#endif + +#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); +#endif + return true; } @@ -443,6 +475,11 @@ bool QSslSocketPrivate::ensureLibraryLoaded() q_SSL_load_error_strings(); q_OpenSSL_add_all_algorithms(); +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + if (q_SSLeay() >= 0x10001000L) + QSslSocketBackendPrivate::s_indexForSSLExtraData = q_SSL_get_ex_new_index(0L, NULL, NULL, NULL, NULL); +#endif + // Initialize OpenSSL's random seed. if (!q_RAND_status()) { struct { @@ -1262,6 +1299,37 @@ bool QSslSocketBackendPrivate::checkSslErrors() return true; } +unsigned int QSslSocketBackendPrivate::tlsPskClientCallback(const char *hint, + char *identity, unsigned int max_identity_len, + unsigned char *psk, unsigned int max_psk_len) +{ + QSslPreSharedKeyAuthenticator authenticator; + + // Fill in some read-only fields (for the user) + if (hint) + authenticator.d->identityHint = QByteArray::fromRawData(hint, int(::strlen(hint))); // it's NUL terminated, but do not include the NUL + + authenticator.d->maximumIdentityLength = int(max_identity_len) - 1; // needs to be NUL terminated + 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 identityLength = qMin(authenticator.identity().length(), authenticator.maximumIdentityLength()); + ::memcpy(identity, authenticator.identity().constData(), identityLength); + identity[identityLength] = 0; + + 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) -- cgit v1.2.3