diff options
| author | Jeremy Lainé <jeremy.laine@m4x.org> | 2012-07-19 18:17:44 +0200 |
|---|---|---|
| committer | Jeremy Lainé <jeremy.laine@m4x.org> | 2012-07-19 18:17:44 +0200 |
| commit | f317c82ffdc9eced9bc4318dbea7287e6d65ce09 (patch) | |
| tree | 102eceb8aefa10b34427b29aa9159b29250e8827 /src/client/QXmppOutgoingClient.cpp | |
| parent | 7d42e6e47c4022783264cc039dfc6f6f4523df13 (diff) | |
move SASL specifics out of QXmppOutgoingClient
Diffstat (limited to 'src/client/QXmppOutgoingClient.cpp')
| -rw-r--r-- | src/client/QXmppOutgoingClient.cpp | 173 |
1 files changed, 44 insertions, 129 deletions
diff --git a/src/client/QXmppOutgoingClient.cpp b/src/client/QXmppOutgoingClient.cpp index 0d0da95a..9abd85a4 100644 --- a/src/client/QXmppOutgoingClient.cpp +++ b/src/client/QXmppOutgoingClient.cpp @@ -80,9 +80,7 @@ public: // Authentication QString nonSASLAuthId; - QXmppSaslDigestMd5 saslDigest; - int saslDigestStep; - QString saslMechanism; + QXmppSaslClient *saslClient; // Timers QTimer *pingTimer; @@ -91,7 +89,7 @@ public: QXmppOutgoingClientPrivate::QXmppOutgoingClientPrivate() : sessionAvailable(false) - , saslDigestStep(0) + , saslClient(0) { } @@ -244,8 +242,10 @@ void QXmppOutgoingClient::handleStart() d->streamVersion.clear(); // reset authentication step - d->saslDigestStep = 0; - d->saslMechanism = QString(); + if (d->saslClient) { + delete d->saslClient; + d->saslClient = 0; + } // reset session information d->bindId.clear(); @@ -343,6 +343,7 @@ void QXmppOutgoingClient::handleStanza(const QDomElement &nodeRecv) // determine SASL Authentication mechanism to use QStringList commonMechanisms; + QString usedMechanism; foreach (const QString &mechanism, features.authMechanisms()) { if (supportedMechanisms.contains(mechanism)) commonMechanisms << mechanism; @@ -353,22 +354,40 @@ void QXmppOutgoingClient::handleStanza(const QDomElement &nodeRecv) return; } else if (!commonMechanisms.contains(preferredMechanism)) { info(QString("Desired SASL Auth mechanism '%1' is not available, selecting first available one").arg(preferredMechanism)); - d->saslMechanism = commonMechanisms.first(); + usedMechanism = commonMechanisms.first(); } else { - d->saslMechanism = preferredMechanism; + usedMechanism = preferredMechanism; } - // send SASL Authentication request - if (d->saslMechanism == "PLAIN") { - QString userPass('\0' + configuration().user() + - '\0' + configuration().password()); - QByteArray data = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>"; - data += userPass.toUtf8().toBase64(); - data += "</auth>"; - sendData(data); + d->saslClient = QXmppSaslClient::create(usedMechanism); + if (!d->saslClient) { + warning("SASL mechanism negotiation failed"); + disconnectFromHost(); + return; + } + info(QString("SASL mechanism '%1' selected").arg(d->saslClient->mechanism())); + d->saslClient->setServer(configuration().domain()); + if (d->saslClient->mechanism() == "X-FACEBOOK-PLATFORM") { + d->saslClient->setUsername(configuration().facebookAppId()); + d->saslClient->setPassword(configuration().facebookAccessToken()); } else { - sendData("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='" + d->saslMechanism.toLatin1() + "'/>"); + d->saslClient->setUsername(configuration().user()); + d->saslClient->setPassword(configuration().password()); + } + + // send SASL auth request + QByteArray response; + if (!d->saslClient->respond(QByteArray(), response)) { + warning("SASL initial response failed"); + disconnectFromHost(); + return; } + QByteArray data = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='"+ d->saslClient->mechanism().toLatin1() + "'"; + if (response.isEmpty()) + data += "/>"; + else + data += ">" + response.toBase64() + "</auth>"; + sendData(data); } // check whether bind is available @@ -404,6 +423,10 @@ void QXmppOutgoingClient::handleStanza(const QDomElement &nodeRecv) } else if(ns == ns_sasl) { + if (!d->saslClient) { + warning("SASL stanza received, but no mechanism selected"); + return; + } if(nodeRecv.tagName() == "success") { debug("Authenticated"); @@ -411,25 +434,11 @@ void QXmppOutgoingClient::handleStanza(const QDomElement &nodeRecv) } else if(nodeRecv.tagName() == "challenge") { - if (d->saslMechanism == "DIGEST-MD5") { - d->saslDigestStep++; - switch (d->saslDigestStep) - { - case 1 : - sendAuthDigestMD5ResponseStep1(nodeRecv.text()); - break; - case 2 : - sendAuthDigestMD5ResponseStep2(nodeRecv.text()); - break; - default : - warning("Too many authentication steps"); - disconnectFromHost(); - break; - } - } else if (d->saslMechanism == "X-FACEBOOK-PLATFORM") { - sendAuthXFacebookResponse(nodeRecv.text()); + QByteArray response; + if (d->saslClient->respond(QByteArray::fromBase64(nodeRecv.text().toAscii()), response)) { + sendData("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" + response.toBase64() + "</response>"); } else { - warning("Unexpected SASL challenge"); + warning("Could not respond to SASL challenge"); disconnectFromHost(); } } @@ -632,100 +641,6 @@ void QXmppOutgoingClient::pingTimeout() emit error(QXmppClient::KeepAliveError); } -// challenge is BASE64 encoded string -void QXmppOutgoingClient::sendAuthDigestMD5ResponseStep1(const QString& challenge) -{ - QByteArray ba = QByteArray::fromBase64(challenge.toAscii()); - QMap<QByteArray, QByteArray> map = QXmppSaslDigestMd5::parseMessage(ba); - - if (!map.contains("nonce")) - { - warning("sendAuthDigestMD5ResponseStep1: Invalid input"); - disconnectFromHost(); - return; - } - - d->saslDigest.setAuthzid(map.value("authzid")); - d->saslDigest.setCnonce(QXmppSaslDigestMd5::generateNonce()); - d->saslDigest.setDigestUri(QString("xmpp/%1").arg(configuration().domain()).toUtf8()); - d->saslDigest.setNc("00000001"); - d->saslDigest.setNonce(map.value("nonce")); - d->saslDigest.setQop("auth"); - d->saslDigest.setSecret(QCryptographicHash::hash( - configuration().user().toUtf8() + ":" + map.value("realm") + ":" + configuration().password().toUtf8(), - QCryptographicHash::Md5)); - - // Build response - QMap<QByteArray, QByteArray> response; - response["username"] = configuration().user().toUtf8(); - if (map.contains("realm")) - response["realm"] = map.value("realm"); - response["nonce"] = d->saslDigest.nonce(); - response["cnonce"] = d->saslDigest.cnonce(); - response["nc"] = d->saslDigest.nc(); - response["qop"] = d->saslDigest.qop(); - response["digest-uri"] = d->saslDigest.digestUri(); - response["response"] = d->saslDigest.calculateDigest( - QByteArray("AUTHENTICATE:") + d->saslDigest.digestUri()); - - if(!d->saslDigest.authzid().isEmpty()) - response["authzid"] = d->saslDigest.authzid(); - response["charset"] = "utf-8"; - - const QByteArray data = QXmppSaslDigestMd5::serializeMessage(response); - QByteArray packet = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" - + data.toBase64() + "</response>"; - sendData(packet); -} - -void QXmppOutgoingClient::sendAuthDigestMD5ResponseStep2(const QString &challenge) -{ - QByteArray ba = QByteArray::fromBase64(challenge.toAscii()); - QMap<QByteArray, QByteArray> map = QXmppSaslDigestMd5::parseMessage(ba); - - if (!map.contains("rspauth")) - { - warning("sendAuthDigestMD5ResponseStep2: Invalid input"); - disconnectFromHost(); - return; - } - - // check new challenge - if (map["rspauth"] != - d->saslDigest.calculateDigest(QByteArray(":") + d->saslDigest.digestUri())) - { - warning("sendAuthDigestMD5ResponseStep2: Bad challenge"); - disconnectFromHost(); - return; - } - - sendData("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>"); -} - -void QXmppOutgoingClient::sendAuthXFacebookResponse(const QString& challenge) -{ - // parse request - QUrl request; - request.setEncodedQuery(QByteArray::fromBase64(challenge.toAscii())); - if (!request.hasQueryItem("method") || !request.hasQueryItem("nonce")) { - warning("sendAuthXFacebookResponse: Invalid input"); - disconnectFromHost(); - return; - } - - // build response - QUrl response; - response.addQueryItem("access_token", configuration().facebookAccessToken()); - response.addQueryItem("api_key", configuration().facebookAppId()); - response.addQueryItem("call_id", 0); - response.addQueryItem("method", request.queryItemValue("method")); - response.addQueryItem("nonce", request.queryItemValue("nonce")); - response.addQueryItem("v", "1.0"); - - sendData("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" - + response.encodedQuery().toBase64() + "</response>"); -} - void QXmppOutgoingClient::sendNonSASLAuth(bool plainText) { QXmppNonSASLAuthIq authQuery; |
