summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/network/ssl/qsslconfiguration.cpp28
-rw-r--r--src/network/ssl/qsslconfiguration.h3
-rw-r--r--src/network/ssl/qsslconfiguration_p.h3
-rw-r--r--src/network/ssl/qsslcontext_openssl.cpp5
-rw-r--r--src/network/ssl/qsslsocket.cpp1
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp42
-rw-r--r--src/network/ssl/qsslsocket_openssl_p.h1
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp4
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h3
-rw-r--r--tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp154
10 files changed, 236 insertions, 8 deletions
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);
@@ -810,6 +812,32 @@ QVector<QSslEllipticCurve> QSslConfiguration::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
This function returns the protocol negotiated with the server
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<QSslEllipticCurve> &curves);
static QVector<QSslEllipticCurve> 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<QByteArray> 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<QSslEllipticCurve> 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<QSslSocketBackendPrivate *>(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
diff --git a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
index c3cb477298..f1d66be442 100644
--- a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
+++ b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
@@ -230,6 +230,7 @@ private slots:
void ephemeralServerKey_data();
void ephemeralServerKey();
void allowedProtocolNegotiation();
+ void pskServer();
#endif
static void exitLoop()
@@ -3036,8 +3037,12 @@ class PskProvider : public QObject
Q_OBJECT
public:
+ bool m_server;
+ QByteArray m_identity;
+ QByteArray m_psk;
+
explicit PskProvider(QObject *parent = 0)
- : QObject(parent)
+ : QObject(parent), m_server(false)
{
}
@@ -3056,7 +3061,11 @@ public slots:
{
QVERIFY(authenticator);
QCOMPARE(authenticator->identityHint(), PSK_SERVER_IDENTITY_HINT);
- QVERIFY(authenticator->maximumIdentityLength() > 0);
+ if (m_server)
+ QCOMPARE(authenticator->maximumIdentityLength(), 0);
+ else
+ QVERIFY(authenticator->maximumIdentityLength() > 0);
+
QVERIFY(authenticator->maximumPreSharedKeyLength() > 0);
if (!m_identity.isEmpty()) {
@@ -3069,12 +3078,61 @@ public slots:
QCOMPARE(authenticator->preSharedKey(), m_psk);
}
}
-
-private:
- QByteArray m_identity;
- QByteArray m_psk;
};
+class PskServer : public QTcpServer
+{
+ Q_OBJECT
+public:
+ PskServer()
+ : socket(0),
+ config(QSslConfiguration::defaultConfiguration()),
+ ignoreSslErrors(true),
+ peerVerifyMode(QSslSocket::AutoVerifyPeer),
+ protocol(QSsl::TlsV1_0),
+ m_pskProvider()
+ {
+ m_pskProvider.m_server = true;
+ }
+ QSslSocket *socket;
+ QSslConfiguration config;
+ bool ignoreSslErrors;
+ QSslSocket::PeerVerifyMode peerVerifyMode;
+ QSsl::SslProtocol protocol;
+ QString ciphers;
+ PskProvider m_pskProvider;
+
+protected:
+ void incomingConnection(qintptr socketDescriptor)
+ {
+ socket = new QSslSocket(this);
+ socket->setSslConfiguration(config);
+ socket->setPeerVerifyMode(peerVerifyMode);
+ socket->setProtocol(protocol);
+ if (ignoreSslErrors)
+ connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
+
+ if (!ciphers.isEmpty()) {
+ socket->setCiphers(ciphers);
+ }
+
+ QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState));
+ QVERIFY(!socket->peerAddress().isNull());
+ QVERIFY(socket->peerPort() != 0);
+ QVERIFY(!socket->localAddress().isNull());
+ QVERIFY(socket->localPort() != 0);
+
+ connect(socket, &QSslSocket::preSharedKeyAuthenticationRequired, &m_pskProvider, &PskProvider::providePsk);
+
+ socket->startServerEncryption();
+ }
+
+protected slots:
+ void ignoreErrorSlot()
+ {
+ socket->ignoreSslErrors();
+ }
+};
void tst_QSslSocket::simplePskConnect_data()
{
QTest::addColumn<PskConnectTestType>("pskTestType");
@@ -3415,6 +3473,90 @@ void tst_QSslSocket::allowedProtocolNegotiation()
#endif // OPENSSL_VERSION_NUMBER
}
+void tst_QSslSocket::pskServer()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ if (!QSslSocket::supportsSsl() || setProxy)
+ return;
+
+ QSslSocket socket;
+ this->socket = &socket;
+
+ QSignalSpy connectedSpy(&socket, SIGNAL(connected()));
+ QVERIFY(connectedSpy.isValid());
+
+ QSignalSpy disconnectedSpy(&socket, SIGNAL(disconnected()));
+ QVERIFY(disconnectedSpy.isValid());
+
+ QSignalSpy connectionEncryptedSpy(&socket, SIGNAL(encrypted()));
+ QVERIFY(connectionEncryptedSpy.isValid());
+
+ QSignalSpy pskAuthenticationRequiredSpy(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)));
+ QVERIFY(pskAuthenticationRequiredSpy.isValid());
+
+ connect(&socket, SIGNAL(connected()), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(disconnected()), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(modeChanged(QSslSocket::SslMode)), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(encrypted()), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(peerVerifyError(QSslError)), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(exitLoop()));
+
+ // force a PSK cipher w/o auth
+ socket.setCiphers(PSK_CIPHER_WITHOUT_AUTH);
+
+ PskProvider provider;
+ provider.setIdentity(PSK_CLIENT_IDENTITY);
+ provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY);
+ connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*)));
+ socket.setPeerVerifyMode(QSslSocket::VerifyNone);
+
+ PskServer server;
+ server.m_pskProvider.setIdentity(provider.m_identity);
+ server.m_pskProvider.setPreSharedKey(provider.m_psk);
+ server.config.setPreSharedKeyIdentityHint(PSK_SERVER_IDENTITY_HINT);
+ QVERIFY(server.listen());
+
+ // Start connecting
+ socket.connectToHost(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
+ enterLoop(5);
+
+ // Entered connected state
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+ QCOMPARE(socket.mode(), QSslSocket::UnencryptedMode);
+ QVERIFY(!socket.isEncrypted());
+ QCOMPARE(connectedSpy.count(), 1);
+ QCOMPARE(disconnectedSpy.count(), 0);
+
+ // Enter encrypted mode
+ socket.startClientEncryption();
+ QCOMPARE(socket.mode(), QSslSocket::SslClientMode);
+ QVERIFY(!socket.isEncrypted());
+ QCOMPARE(connectionEncryptedSpy.count(), 0);
+
+ // Start handshake.
+ enterLoop(10);
+
+ // We must get the PSK signal in all cases
+ QCOMPARE(pskAuthenticationRequiredSpy.count(), 1);
+
+ QCOMPARE(connectionEncryptedSpy.count(), 1);
+ QVERIFY(socket.isEncrypted());
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+
+ // check writing
+ socket.write("Hello from Qt TLS/PSK!");
+ QVERIFY(socket.waitForBytesWritten());
+
+ // disconnect
+ socket.disconnectFromHost();
+ enterLoop(10);
+
+ QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState);
+ QCOMPARE(disconnectedSpy.count(), 1);
+}
+
#endif // QT_NO_OPENSSL
#endif // QT_NO_SSL