QNetworkAccessManager: introduce support for TLS PSK

Expose the same kind of TLS PSK client support we already have set
in place for QSslSocket.

[ChangeLog][QtNetwork][QNetworkAccessManager] It is now possible to use
TLS PSK ciphersuites when using HTTPS (or similar protocols working over
SSL).

Change-Id: I56a048e9f4f841f886758c781af2867d18538a3e
Reviewed-by: Richard J. Moore <rich@kde.org>
This commit is contained in:
Giuseppe D'Angelo 2014-11-07 11:13:12 +01:00
parent c27e1f498f
commit 0192630f55
12 changed files with 116 additions and 0 deletions

View File

@ -147,6 +147,9 @@ void QHttpNetworkConnectionChannel::init()
QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
this, SLOT(_q_sslErrors(QList<QSslError>)),
Qt::DirectConnection);
QObject::connect(sslSocket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)),
this, SLOT(_q_preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)),
Qt::DirectConnection);
QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)),
this, SLOT(_q_encryptedBytesWritten(qint64)),
Qt::DirectConnection);
@ -1035,6 +1038,29 @@ void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
connection->d_func()->resumeConnection();
}
void QHttpNetworkConnectionChannel::_q_preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator)
{
connection->d_func()->pauseConnection();
if (pendingEncrypt && !reply)
connection->d_func()->dequeueRequest(socket);
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP) {
if (reply)
emit reply->preSharedKeyAuthenticationRequired(authenticator);
} else {
QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
for (int a = 0; a < spdyPairs.count(); ++a) {
// emit SSL errors for all replies
QHttpNetworkReply *currentReply = spdyPairs.at(a).second;
Q_ASSERT(currentReply);
emit currentReply->preSharedKeyAuthenticationRequired(authenticator);
}
}
connection->d_func()->resumeConnection();
}
void QHttpNetworkConnectionChannel::_q_encryptedBytesWritten(qint64 bytes)
{
Q_UNUSED(bytes);

View File

@ -189,6 +189,7 @@ public:
#ifndef QT_NO_SSL
void _q_encrypted(); // start sending request (https)
void _q_sslErrors(const QList<QSslError> &errors); // ssl errors from the socket
void _q_preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*); // tls-psk auth necessary
void _q_encryptedBytesWritten(qint64 bytes); // proceed sending
#endif

View File

@ -138,6 +138,7 @@ public:
Q_SIGNALS:
void encrypted();
void sslErrors(const QList<QSslError> &errors);
void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator);
#endif
Q_SIGNALS:

View File

@ -371,6 +371,8 @@ void QHttpThreadDelegate::startRequest()
#ifndef QT_NO_SSL
connect(httpReply,SIGNAL(encrypted()), this, SLOT(encryptedSlot()));
connect(httpReply,SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrorsSlot(QList<QSslError>)));
connect(httpReply,SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)),
this, SLOT(preSharedKeyAuthenticationRequiredSlot(QSslPreSharedKeyAuthenticator*)));
#endif
// In the asynchronous HTTP case we can just forward those signals
@ -675,6 +677,14 @@ void QHttpThreadDelegate::sslErrorsSlot(const QList<QSslError> &errors)
if (!specificErrors.isEmpty())
httpReply->ignoreSslErrors(specificErrors);
}
void QHttpThreadDelegate::preSharedKeyAuthenticationRequiredSlot(QSslPreSharedKeyAuthenticator *authenticator)
{
if (!httpReply)
return;
emit preSharedKeyAuthenticationRequired(authenticator);
}
#endif
void QHttpThreadDelegate::synchronousAuthenticationRequiredSlot(const QHttpNetworkRequest &request, QAuthenticator *a)

View File

@ -131,6 +131,7 @@ signals:
void encrypted();
void sslErrors(const QList<QSslError> &, bool *, QList<QSslError> *);
void sslConfigurationChanged(const QSslConfiguration);
void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *);
#endif
void downloadMetaData(QList<QPair<QByteArray,QByteArray> >, int, QString, bool,
QSharedPointer<char>, qint64, bool);
@ -161,6 +162,7 @@ protected slots:
#ifndef QT_NO_SSL
void encryptedSlot();
void sslErrorsSlot(const QList<QSslError> &errors);
void preSharedKeyAuthenticationRequiredSlot(QSslPreSharedKeyAuthenticator *authenticator);
#endif
void synchronousAuthenticationRequiredSlot(const QHttpNetworkRequest &request, QAuthenticator *);

View File

@ -416,6 +416,29 @@ static void ensureInitialized()
QNetworkReply::sslConfiguration(), QNetworkReply::ignoreSslErrors()
*/
/*!
\fn void QNetworkAccessManager::preSharedKeyAuthenticationRequired(QNetworkReply *reply, QSslPreSharedKeyAuthenticator *authenticator)
\since 5.5
This signal is emitted if the SSL/TLS handshake negotiates a PSK
ciphersuite, and therefore a PSK authentication is then required.
The \a reply object is the QNetworkReply that is negotiating
such ciphersuites.
When using PSK, the client must send to the server a valid identity and a
valid pre shared key, in order for the SSL handshake to continue.
Applications can provide this information in a slot connected to this
signal, by filling in the passed \a authenticator object according to their
needs.
\note Ignoring this signal, or failing to provide the required credentials,
will cause the handshake to fail, and therefore the connection to be aborted.
\note The \a authenticator object is owned by the reply and must not be
deleted by the application.
\sa QSslPreSharedKeyAuthenticator
*/
/*!
Constructs a QNetworkAccessManager object that is the center of
@ -434,6 +457,7 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
#ifndef QT_NO_SSL
qRegisterMetaType<QList<QSslError> >();
qRegisterMetaType<QSslConfiguration>();
qRegisterMetaType<QSslPreSharedKeyAuthenticator *>();
#endif
qRegisterMetaType<QList<QPair<QByteArray,QByteArray> > >();
#ifndef QT_NO_HTTP
@ -1328,6 +1352,18 @@ void QNetworkAccessManagerPrivate::_q_replySslErrors(const QList<QSslError> &err
#endif
}
void QNetworkAccessManagerPrivate::_q_replyPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator)
{
#ifndef QT_NO_SSL
Q_Q(QNetworkAccessManager);
QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender());
if (reply)
emit q->preSharedKeyAuthenticationRequired(reply, authenticator);
#else
Q_UNUSED(authenticator);
#endif
}
QNetworkReply *QNetworkAccessManagerPrivate::postProcess(QNetworkReply *reply)
{
Q_Q(QNetworkAccessManager);
@ -1338,6 +1374,7 @@ QNetworkReply *QNetworkAccessManagerPrivate::postProcess(QNetworkReply *reply)
* avoid getting a connection error. */
q->connect(reply, SIGNAL(encrypted()), SLOT(_q_replyEncrypted()));
q->connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(_q_replySslErrors(QList<QSslError>)));
q->connect(reply, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), SLOT(_q_replyPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)));
#endif
#ifndef QT_NO_BEARERMANAGEMENT
activeReplyCount++;

View File

@ -37,6 +37,7 @@
#include <QtCore/QObject>
#ifndef QT_NO_SSL
#include <QtNetwork/QSslConfiguration>
#include <QtNetwork/QSslPreSharedKeyAuthenticator>
#endif
QT_BEGIN_NAMESPACE
@ -145,6 +146,7 @@ Q_SIGNALS:
#ifndef QT_NO_SSL
void encrypted(QNetworkReply *reply);
void sslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
void preSharedKeyAuthenticationRequired(QNetworkReply *reply, QSslPreSharedKeyAuthenticator *authenticator);
#endif
#ifndef QT_NO_BEARERMANAGEMENT
@ -169,6 +171,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_replyFinished())
Q_PRIVATE_SLOT(d_func(), void _q_replyEncrypted())
Q_PRIVATE_SLOT(d_func(), void _q_replySslErrors(QList<QSslError>))
Q_PRIVATE_SLOT(d_func(), void _q_replyPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*))
#ifndef QT_NO_BEARERMANAGEMENT
Q_PRIVATE_SLOT(d_func(), void _q_networkSessionClosed())
Q_PRIVATE_SLOT(d_func(), void _q_networkSessionStateChanged(QNetworkSession::State))

View File

@ -91,6 +91,7 @@ public:
void _q_replyFinished();
void _q_replyEncrypted();
void _q_replySslErrors(const QList<QSslError> &errors);
void _q_replyPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator);
QNetworkReply *postProcess(QNetworkReply *reply);
void createCookieJar() const;

View File

@ -253,6 +253,28 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
sslConfiguration(), ignoreSslErrors()
*/
/*!
\fn void QNetworkReply::preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator)
\since 5.5
This signal is emitted if the SSL/TLS handshake negotiates a PSK
ciphersuite, and therefore a PSK authentication is then required.
When using PSK, the client must send to the server a valid identity and a
valid pre shared key, in order for the SSL handshake to continue.
Applications can provide this information in a slot connected to this
signal, by filling in the passed \a authenticator object according to their
needs.
\note Ignoring this signal, or failing to provide the required credentials,
will cause the handshake to fail, and therefore the connection to be aborted.
\note The \a authenticator object is owned by the reply and must not be
deleted by the application.
\sa QSslPreSharedKeyAuthenticator
*/
/*!
\fn void QNetworkReply::metaDataChanged()

View File

@ -49,6 +49,7 @@ class QVariant;
class QAuthenticator;
class QSslConfiguration;
class QSslError;
class QSslPreSharedKeyAuthenticator;
class QNetworkReplyPrivate;
class Q_NETWORK_EXPORT QNetworkReply: public QIODevice
@ -150,6 +151,7 @@ Q_SIGNALS:
#ifndef QT_NO_SSL
void encrypted();
void sslErrors(const QList<QSslError> &errors);
void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator);
#endif
void uploadProgress(qint64 bytesSent, qint64 bytesTotal);

View File

@ -844,6 +844,9 @@ void QNetworkReplyHttpImplPrivate::postRequest()
QObject::connect(delegate, SIGNAL(sslErrors(QList<QSslError>,bool*,QList<QSslError>*)),
q, SLOT(replySslErrors(QList<QSslError>,bool*,QList<QSslError>*)),
Qt::BlockingQueuedConnection);
QObject::connect(delegate, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)),
q, SLOT(replyPreSharedKeyAuthenticationRequiredSlot(QSslPreSharedKeyAuthenticator*)),
Qt::BlockingQueuedConnection);
#endif
// This signal we will use to start the request.
QObject::connect(q, SIGNAL(startHttpRequest()), delegate, SLOT(startRequest()));
@ -1263,6 +1266,12 @@ void QNetworkReplyHttpImplPrivate::replySslConfigurationChanged(const QSslConfig
// Receiving the used SSL configuration from the HTTP thread
this->sslConfiguration = sslConfiguration;
}
void QNetworkReplyHttpImplPrivate::replyPreSharedKeyAuthenticationRequiredSlot(QSslPreSharedKeyAuthenticator *authenticator)
{
Q_Q(QNetworkReplyHttpImpl);
emit q->preSharedKeyAuthenticationRequired(authenticator);
}
#endif
// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread

View File

@ -113,6 +113,7 @@ public:
Q_PRIVATE_SLOT(d_func(), void replyEncrypted())
Q_PRIVATE_SLOT(d_func(), void replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *))
Q_PRIVATE_SLOT(d_func(), void replySslConfigurationChanged(const QSslConfiguration&))
Q_PRIVATE_SLOT(d_func(), void replyPreSharedKeyAuthenticationRequiredSlot(QSslPreSharedKeyAuthenticator *))
#endif
#ifndef QT_NO_NETWORKPROXY
Q_PRIVATE_SLOT(d_func(), void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth))
@ -275,6 +276,7 @@ public:
void replyEncrypted();
void replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *);
void replySslConfigurationChanged(const QSslConfiguration&);
void replyPreSharedKeyAuthenticationRequiredSlot(QSslPreSharedKeyAuthenticator *);
#endif
#ifndef QT_NO_NETWORKPROXY
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth);