diff options
| author | Jeremy Lainé <jeremy.laine@m4x.org> | 2011-04-07 18:27:51 +0000 |
|---|---|---|
| committer | Jeremy Lainé <jeremy.laine@m4x.org> | 2011-04-07 18:27:51 +0000 |
| commit | 194762951e6a59808350032f38e05cd0e5d03807 (patch) | |
| tree | c766a97835dad98838906710be77a2197099e1e9 /src/QXmppIncomingClient.cpp | |
| parent | fafebd0f90dd8b6fe311e0384898653632072539 (diff) | |
| download | qxmpp-194762951e6a59808350032f38e05cd0e5d03807.tar.gz | |
make password checking full asynchronous
Diffstat (limited to 'src/QXmppIncomingClient.cpp')
| -rw-r--r-- | src/QXmppIncomingClient.cpp | 189 |
1 files changed, 81 insertions, 108 deletions
diff --git a/src/QXmppIncomingClient.cpp b/src/QXmppIncomingClient.cpp index cdee467c..7b8ce038 100644 --- a/src/QXmppIncomingClient.cpp +++ b/src/QXmppIncomingClient.cpp @@ -21,7 +21,6 @@ * */ -#include <QCryptographicHash> #include <QDomElement> #include <QSslKey> #include <QSslSocket> @@ -30,6 +29,7 @@ #include "QXmppBindIq.h" #include "QXmppConstants.h" #include "QXmppMessage.h" +#include "QXmppPasswordChecker.h" #include "QXmppSaslAuth.h" #include "QXmppSessionIq.h" #include "QXmppStreamFeatures.h" @@ -76,7 +76,7 @@ QXmppIncomingClient::QXmppIncomingClient(QSslSocket *socket, const QString &doma d->idleTimer = new QTimer(this); d->idleTimer->setSingleShot(true); bool check = connect(d->idleTimer, SIGNAL(timeout()), - this, SLOT(slotTimeout())); + this, SLOT(onTimeout())); Q_ASSERT(check); Q_UNUSED(check); } @@ -214,33 +214,22 @@ void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv) return; } - const QString username = QString::fromUtf8(auth[1]); - const QString password = QString::fromUtf8(auth[2]); + QXmppPasswordRequest request; + request.setDomain(d->domain); + request.setUsername(QString::fromUtf8(auth[1])); + request.setPassword(QString::fromUtf8(auth[2])); if (!d->passwordChecker) { // FIXME: what type of failure? - warning(QString("Cannot authenticate '%1', no password checker").arg(username)); + warning(QString("Cannot authenticate '%1', no password checker").arg(request.username())); sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>"); disconnectFromHost(); return; } - QXmppPasswordChecker::Error error = d->passwordChecker->checkPassword(username, d->domain, password); - if (error == QXmppPasswordChecker::NoError) - { - d->username = username; - info(QString("Authentication succeeded for '%1'").arg(d->username)); - sendData("<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>"); - } else if (error == QXmppPasswordChecker::AuthorizationError) { - warning(QString("Authentication failed for '%1'").arg(username)); - sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>"); - disconnectFromHost(); - return; - } else { - warning(QString("Temporary authentication failure for '%1'").arg(username)); - sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><temporary-auth-failure/></failure>"); - disconnectFromHost(); - return; - } + QXmppPasswordReply *reply = d->passwordChecker->checkPassword(request); + reply->setParent(this); + reply->setProperty("__sasl_username", request.username()); + connect(reply, SIGNAL(finished()), this, SLOT(onPasswordReply())); } else if (mechanism == "DIGEST-MD5") { @@ -269,13 +258,13 @@ void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv) } else if (nodeRecv.tagName() == "response") { - const QByteArray raw = QByteArray::fromBase64(nodeRecv.text().toAscii()); - QMap<QByteArray, QByteArray> response = QXmppSaslDigestMd5::parseMessage(raw); - if (d->saslStep == 1) { + const QByteArray raw = QByteArray::fromBase64(nodeRecv.text().toAscii()); + QMap<QByteArray, QByteArray> saslResponse = QXmppSaslDigestMd5::parseMessage(raw); + // check credentials - const QString username = QString::fromUtf8(response.value("username")); + const QString username = QString::fromUtf8(saslResponse.value("username")); if (!d->passwordChecker) { // FIXME: what type of failure? warning(QString("Cannot authenticate '%1', no password checker").arg(username)); @@ -284,34 +273,14 @@ void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv) return; } - QByteArray secret; - QXmppPasswordChecker::Error error = d->passwordChecker->getDigest(username, d->domain, secret); - if (error == QXmppPasswordChecker::TemporaryError) { - warning(QString("Temporary authentication failure for '%1'").arg(username)); - sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><temporary-auth-failure/></failure>"); - disconnectFromHost(); - return; - } - d->saslDigest.setSecret(secret); - d->saslDigest.setDigestUri(response.value("digest-uri")); - d->saslDigest.setNc(response.value("nc")); - d->saslDigest.setCnonce(response.value("cnonce")); - if (response["response"] != d->saslDigest.calculateDigest( - QByteArray("AUTHENTICATE:") + d->saslDigest.digestUri())) - { - sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>"); - disconnectFromHost(); - return; - } + QXmppPasswordRequest request; + request.setUsername(username); + request.setDomain(d->domain); - // send new challenge - d->username = username; - d->saslStep = 2; - QMap<QByteArray, QByteArray> challenge; - challenge["rspauth"] = d->saslDigest.calculateDigest( - QByteArray(":") + d->saslDigest.digestUri()); - const QByteArray data = QXmppSaslDigestMd5::serializeMessage(challenge).toBase64(); - sendData("<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" + data +"</challenge>"); + QXmppPasswordReply *reply = d->passwordChecker->getDigest(request); + reply->setParent(this); + reply->setProperty("__sasl_raw", raw); + connect(reply, SIGNAL(finished()), this, SLOT(onDigestReply())); } else if (d->saslStep == 2) { @@ -395,71 +364,75 @@ void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv) } } -void QXmppIncomingClient::slotTimeout() +void QXmppIncomingClient::onDigestReply() { - warning(QString("Idle timeout for %1").arg(jid())); - disconnectFromHost(); -} + QXmppPasswordReply *reply = qobject_cast<QXmppPasswordReply*>(sender()); + if (!reply) + return; + reply->deleteLater(); -/// Checks that the given credentials are valid. -/// -/// The base implementation requires that you reimplement getPassword(). -/// -/// \param username -/// \param domain -/// \param password + const QMap<QByteArray, QByteArray> saslResponse = QXmppSaslDigestMd5::parseMessage(reply->property("__sasl_raw").toByteArray()); + const QString username = QString::fromUtf8(saslResponse.value("username")); + if (reply->error() == QXmppPasswordReply::TemporaryError) { + warning(QString("Temporary authentication failure for '%1'").arg(username)); + sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><temporary-auth-failure/></failure>"); + disconnectFromHost(); + return; + } -QXmppPasswordChecker::Error QXmppPasswordChecker::checkPassword(const QString &username, const QString &domain, const QString &password) -{ - QString secret; - Error error = getPassword(username, domain, secret); - if (error != NoError) - return error; + d->saslDigest.setSecret(reply->digest()); + d->saslDigest.setDigestUri(saslResponse.value("digest-uri")); + d->saslDigest.setNc(saslResponse.value("nc")); + d->saslDigest.setCnonce(saslResponse.value("cnonce")); + if (saslResponse.value("response") != d->saslDigest.calculateDigest( + QByteArray("AUTHENTICATE:") + d->saslDigest.digestUri())) + { + sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>"); + disconnectFromHost(); + return; + } - return (password == secret) ? NoError : AuthorizationError; + // send new challenge + d->username = username; + d->saslStep = 2; + QMap<QByteArray, QByteArray> challenge; + challenge["rspauth"] = d->saslDigest.calculateDigest( + QByteArray(":") + d->saslDigest.digestUri()); + const QByteArray data = QXmppSaslDigestMd5::serializeMessage(challenge).toBase64(); + sendData("<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" + data +"</challenge>"); } -/// Retrieves the MD5 digest for the given username. -/// -/// Reimplement this method if your backend natively supports -/// retrieving MD5 digests. -/// -/// \param username - -QXmppPasswordChecker::Error QXmppPasswordChecker::getDigest(const QString &username, const QString &domain, QByteArray &digest) +void QXmppIncomingClient::onPasswordReply() { - QString secret; - Error error = getPassword(username, domain, secret); - if (error != NoError) - return error; - - digest = QCryptographicHash::hash( - (username + ":" + domain + ":" + secret).toUtf8(), - QCryptographicHash::Md5); - return NoError; + QXmppPasswordReply *reply = qobject_cast<QXmppPasswordReply*>(sender()); + if (!reply) + return; + reply->deleteLater(); + + const QString username = reply->property("__sasl_username").toString(); + switch (reply->error()) { + case QXmppPasswordReply::NoError: + d->username = username; + info(QString("Authentication succeeded for '%1'").arg(username)); + sendData("<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>"); + break; + case QXmppPasswordReply::AuthorizationError: + warning(QString("Authentication failed for '%1'").arg(username)); + sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>"); + disconnectFromHost(); + break; + case QXmppPasswordReply::TemporaryError: + warning(QString("Temporary authentication failure for '%1'").arg(username)); + sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><temporary-auth-failure/></failure>"); + disconnectFromHost(); + break; + } } -/// Retrieves the password for the given username. -/// -/// The simplest way to write a password checker is to reimplement this method. -/// -/// \param username -/// \param domain -/// \param password - -QXmppPasswordChecker::Error QXmppPasswordChecker::getPassword(const QString &username, const QString &domain, QString &password) +void QXmppIncomingClient::onTimeout() { - Q_UNUSED(username); - Q_UNUSED(domain); - Q_UNUSED(password); - return TemporaryError; + warning(QString("Idle timeout for %1").arg(jid())); + disconnectFromHost(); } -/// Returns true if the getPassword() method is implemented. -/// - -bool QXmppPasswordChecker::hasGetPassword() const -{ - return false; -} |
