diff options
| author | Jeremy Lainé <jeremy.laine@m4x.org> | 2012-07-20 17:37:38 +0200 |
|---|---|---|
| committer | Jeremy Lainé <jeremy.laine@m4x.org> | 2012-07-20 17:37:38 +0200 |
| commit | 51eef84fd4104f19228ebd85fce89658b64464a2 (patch) | |
| tree | 31db52023ae0a063714140897bcbbfb6010ba04a /src/base/QXmppSaslAuth.cpp | |
| parent | db4fe78387247ff039602030d964e4c0d6e39f46 (diff) | |
| download | qxmpp-51eef84fd4104f19228ebd85fce89658b64464a2.tar.gz | |
make SASL code fully private
Diffstat (limited to 'src/base/QXmppSaslAuth.cpp')
| -rw-r--r-- | src/base/QXmppSaslAuth.cpp | 743 |
1 files changed, 0 insertions, 743 deletions
diff --git a/src/base/QXmppSaslAuth.cpp b/src/base/QXmppSaslAuth.cpp deleted file mode 100644 index 1c15ae02..00000000 --- a/src/base/QXmppSaslAuth.cpp +++ /dev/null @@ -1,743 +0,0 @@ -/* - * Copyright (C) 2008-2012 The QXmpp developers - * - * Authors: - * Manjeet Dahiya - * Jeremy Lainé - * - * Source: - * http://code.google.com/p/qxmpp - * - * This file is a part of QXmpp library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - */ - -#include <cstdlib> - -#include <QCryptographicHash> -#include <QDomElement> -#include <QStringList> -#include <QUrl> - -#include "QXmppSaslAuth.h" -#include "QXmppSaslAuth_p.h" -#include "QXmppUtils.h" - -const char *ns_xmpp_sasl = "urn:ietf:params:xml:ns:xmpp-sasl"; - -static QByteArray forcedNonce; - -// Calculate digest response for use with XMPP/SASL. - -static QByteArray calculateDigest(const QByteArray &method, const QByteArray &digestUri, const QByteArray &secret, const QByteArray &nonce, const QByteArray &cnonce, const QByteArray &nc) -{ - const QByteArray A1 = secret + ':' + nonce + ':' + cnonce; - const QByteArray A2 = method + ':' + digestUri; - - QByteArray HA1 = QCryptographicHash::hash(A1, QCryptographicHash::Md5).toHex(); - QByteArray HA2 = QCryptographicHash::hash(A2, QCryptographicHash::Md5).toHex(); - const QByteArray KD = HA1 + ':' + nonce + ':' + nc + ':' + cnonce + ":auth:" + HA2; - return QCryptographicHash::hash(KD, QCryptographicHash::Md5).toHex(); -} - -static QByteArray generateNonce() -{ - if (!forcedNonce.isEmpty()) - return forcedNonce; - - QByteArray nonce = QXmppUtils::generateRandomBytes(32); - - // The random data can the '=' char is not valid as it is a delimiter, - // so to be safe, base64 the nonce - return nonce.toBase64(); -} - -QXmppSaslAuth::QXmppSaslAuth(const QString &mechanism, const QByteArray &value) - : QXmppSaslStanza("auth", value) - , m_mechanism(mechanism) -{ -} - -QString QXmppSaslAuth::mechanism() const -{ - return m_mechanism; -} - -void QXmppSaslAuth::setMechanism(const QString &mechanism) -{ - m_mechanism = mechanism; -} - -void QXmppSaslAuth::parse(const QDomElement &element) -{ - m_mechanism = element.attribute("mechanism"); - setValue(QByteArray::fromBase64(element.text().toAscii())); -} - -void QXmppSaslAuth::toXml(QXmlStreamWriter *writer) const -{ - writer->writeStartElement("auth"); - writer->writeAttribute("xmlns", ns_xmpp_sasl); - writer->writeAttribute("mechanism", m_mechanism); - if (!value().isEmpty()) - writer->writeCharacters(value().toBase64()); - writer->writeEndElement(); -} - -QXmppSaslChallenge::QXmppSaslChallenge(const QByteArray &value) - : QXmppSaslStanza("challenge", value) -{ -} - -QXmppSaslFailure::QXmppSaslFailure(const QString &condition) - : QXmppSaslStanza("failure") - , m_condition(condition) -{ -} - -QString QXmppSaslFailure::condition() const -{ - return m_condition; -} - -void QXmppSaslFailure::setCondition(const QString &condition) -{ - m_condition = condition; -} - -void QXmppSaslFailure::parse(const QDomElement &element) -{ - m_condition = element.firstChildElement().tagName(); -} - -void QXmppSaslFailure::toXml(QXmlStreamWriter *writer) const -{ - writer->writeStartElement("failure"); - writer->writeAttribute("xmlns", ns_xmpp_sasl); - if (!m_condition.isEmpty()) - writer->writeEmptyElement(m_condition); - writer->writeEndElement(); -} - -QXmppSaslResponse::QXmppSaslResponse(const QByteArray &value) - : QXmppSaslStanza("response", value) -{ -} - -QXmppSaslSuccess::QXmppSaslSuccess() - : QXmppSaslStanza("success") -{ -} - -QXmppSaslStanza::QXmppSaslStanza(const QString &type, const QByteArray &value) - : m_type(type) - , m_value(value) -{ -} - -QByteArray QXmppSaslStanza::value() const -{ - return m_value; -} - -void QXmppSaslStanza::setValue(const QByteArray &value) -{ - m_value = value; -} - - -void QXmppSaslStanza::parse(const QDomElement &element) -{ - m_type = element.nodeName(); - m_value = QByteArray::fromBase64(element.text().toAscii()); -} - -void QXmppSaslStanza::toXml(QXmlStreamWriter *writer) const -{ - if (!m_type.isEmpty()) { - writer->writeStartElement(m_type); - writer->writeAttribute("xmlns", ns_xmpp_sasl); - if (!m_value.isEmpty()) - writer->writeCharacters(m_value.toBase64()); - writer->writeEndElement(); - } -} - -class QXmppSaslClientPrivate -{ -public: - QString host; - QString serviceType; - QString username; - QString password; -}; - -QXmppSaslClient::QXmppSaslClient(QObject *parent) - : QXmppLoggable(parent) - , d(new QXmppSaslClientPrivate) -{ -} - -QXmppSaslClient::~QXmppSaslClient() -{ - delete d; -} - -/// Returns a list of supported mechanisms. - -QStringList QXmppSaslClient::availableMechanisms() -{ - return QStringList() << "PLAIN" << "DIGEST-MD5" << "ANONYMOUS" << "X-FACEBOOK-PLATFORM"; -} - -/// Creates an SASL client for the given mechanism. - -QXmppSaslClient* QXmppSaslClient::create(const QString &mechanism, QObject *parent) -{ - if (mechanism == "PLAIN") { - return new QXmppSaslClientPlain(parent); - } else if (mechanism == "DIGEST-MD5") { - return new QXmppSaslClientDigestMd5(parent); - } else if (mechanism == "ANONYMOUS") { - return new QXmppSaslClientAnonymous(parent); - } else if (mechanism == "X-FACEBOOK-PLATFORM") { - return new QXmppSaslClientFacebook(parent); - } else { - return 0; - } -} - -/// Returns the host. - -QString QXmppSaslClient::host() const -{ - return d->host; -} - -/// Sets the host. - -void QXmppSaslClient::setHost(const QString &host) -{ - d->host = host; -} - -/// Returns the service type, e.g. "xmpp". - -QString QXmppSaslClient::serviceType() const -{ - return d->serviceType; -} - -/// Sets the service type, e.g. "xmpp". - -void QXmppSaslClient::setServiceType(const QString &serviceType) -{ - d->serviceType = serviceType; -} - -/// Returns the username. - -QString QXmppSaslClient::username() const -{ - return d->username; -} - -/// Sets the username. - -void QXmppSaslClient::setUsername(const QString &username) -{ - d->username = username; -} - -/// Returns the password. - -QString QXmppSaslClient::password() const -{ - return d->password; -} - -/// Sets the password. - -void QXmppSaslClient::setPassword(const QString &password) -{ - d->password = password; -} - -QXmppSaslClientAnonymous::QXmppSaslClientAnonymous(QObject *parent) - : QXmppSaslClient(parent) - , m_step(0) -{ -} - -QString QXmppSaslClientAnonymous::mechanism() const -{ - return "ANONYMOUS"; -} - -bool QXmppSaslClientAnonymous::respond(const QByteArray &challenge, QByteArray &response) -{ - Q_UNUSED(challenge); - if (m_step == 0) { - response = QByteArray(); - m_step++; - return true; - } else { - warning("QXmppSaslClientAnonymous : Invalid step"); - return false; - } -} - -QXmppSaslClientDigestMd5::QXmppSaslClientDigestMd5(QObject *parent) - : QXmppSaslClient(parent) - , m_nc("00000001") - , m_step(0) -{ - m_cnonce = generateNonce(); -} - -QString QXmppSaslClientDigestMd5::mechanism() const -{ - return "DIGEST-MD5"; -} - -bool QXmppSaslClientDigestMd5::respond(const QByteArray &challenge, QByteArray &response) -{ - Q_UNUSED(challenge); - const QByteArray digestUri = QString("%1/%2").arg(serviceType(), host()).toUtf8(); - - if (m_step == 0) { - response = QByteArray(); - m_step++; - return true; - } else if (m_step == 1) { - const QMap<QByteArray, QByteArray> input = QXmppSaslDigestMd5::parseMessage(challenge); - - if (!input.contains("nonce")) { - warning("QXmppSaslClientDigestMd5 : Invalid input on step 1"); - return false; - } - - // determine realm - const QByteArray realm = input.value("realm"); - - // determine quality of protection - const QList<QByteArray> qops = input.value("qop", "auth").split(','); - if (!qops.contains("auth")) { - warning("QXmppSaslClientDigestMd5 : Invalid quality of protection"); - return false; - } - - m_nonce = input.value("nonce"); - m_secret = QCryptographicHash::hash( - username().toUtf8() + ":" + realm + ":" + password().toUtf8(), - QCryptographicHash::Md5); - - // Build response - QMap<QByteArray, QByteArray> output; - output["username"] = username().toUtf8(); - if (!realm.isEmpty()) - output["realm"] = realm; - output["nonce"] = m_nonce; - output["qop"] = "auth"; - output["cnonce"] = m_cnonce; - output["nc"] = m_nc; - output["digest-uri"] = digestUri; - output["response"] = calculateDigest("AUTHENTICATE", digestUri, m_secret, m_nonce, m_cnonce, m_nc); - output["charset"] = "utf-8"; - - response = QXmppSaslDigestMd5::serializeMessage(output); - m_step++; - return true; - } else if (m_step == 2) { - const QMap<QByteArray, QByteArray> input = QXmppSaslDigestMd5::parseMessage(challenge); - - // check new challenge - if (input.value("rspauth") != calculateDigest(QByteArray(), digestUri, m_secret, m_nonce, m_cnonce, m_nc)) { - warning("QXmppSaslClientDigestMd5 : Invalid challenge on step 2"); - return false; - } - - response = QByteArray(); - m_step++; - return true; - } else { - warning("QXmppSaslClientDigestMd5 : Invalid step"); - return false; - } -} - -QXmppSaslClientFacebook::QXmppSaslClientFacebook(QObject *parent) - : QXmppSaslClient(parent) - , m_step(0) -{ -} - -QString QXmppSaslClientFacebook::mechanism() const -{ - return "X-FACEBOOK-PLATFORM"; -} - -bool QXmppSaslClientFacebook::respond(const QByteArray &challenge, QByteArray &response) -{ - if (m_step == 0) { - // no initial response - response = QByteArray(); - m_step++; - return true; - } else if (m_step == 1) { - // parse request - QUrl requestUrl; - requestUrl.setEncodedQuery(challenge); - if (!requestUrl.hasQueryItem("method") || !requestUrl.hasQueryItem("nonce")) { - warning("QXmppSaslClientFacebook : Invalid challenge, nonce or method missing"); - return false; - } - - // build response - QUrl responseUrl; - responseUrl.addQueryItem("access_token", username()); - responseUrl.addQueryItem("api_key", password()); - responseUrl.addQueryItem("call_id", 0); - responseUrl.addQueryItem("method", requestUrl.queryItemValue("method")); - responseUrl.addQueryItem("nonce", requestUrl.queryItemValue("nonce")); - responseUrl.addQueryItem("v", "1.0"); - - response = responseUrl.encodedQuery(); - m_step++; - return true; - } else { - warning("QXmppSaslClientFacebook : Invalid step"); - return false; - } -} - -QXmppSaslClientPlain::QXmppSaslClientPlain(QObject *parent) - : QXmppSaslClient(parent) - , m_step(0) -{ -} - -QString QXmppSaslClientPlain::mechanism() const -{ - return "PLAIN"; -} - -bool QXmppSaslClientPlain::respond(const QByteArray &challenge, QByteArray &response) -{ - Q_UNUSED(challenge); - if (m_step == 0) { - response = QString('\0' + username() + '\0' + password()).toUtf8(); - m_step++; - return true; - } else { - warning("QXmppSaslClientPlain : Invalid step"); - return false; - } -} - -class QXmppSaslServerPrivate -{ -public: - QString username; - QString password; - QByteArray passwordDigest; - QString realm; -}; - -QXmppSaslServer::QXmppSaslServer(QObject *parent) - : QXmppLoggable(parent) - , d(new QXmppSaslServerPrivate) -{ -} - -QXmppSaslServer::~QXmppSaslServer() -{ - delete d; -} - -/// Creates an SASL server for the given mechanism. - -QXmppSaslServer* QXmppSaslServer::create(const QString &mechanism, QObject *parent) -{ - if (mechanism == "PLAIN") { - return new QXmppSaslServerPlain(parent); - } else if (mechanism == "DIGEST-MD5") { - return new QXmppSaslServerDigestMd5(parent); - } else if (mechanism == "ANONYMOUS") { - return new QXmppSaslServerAnonymous(parent); - } else { - return 0; - } -} - -/// Returns the username. - -QString QXmppSaslServer::username() const -{ - return d->username; -} - -/// Sets the username. - -void QXmppSaslServer::setUsername(const QString &username) -{ - d->username = username; -} - -/// Returns the password. - -QString QXmppSaslServer::password() const -{ - return d->password; -} - -/// Sets the password. - -void QXmppSaslServer::setPassword(const QString &password) -{ - d->password = password; -} - -/// Returns the password digest. - -QByteArray QXmppSaslServer::passwordDigest() const -{ - return d->passwordDigest; -} - -/// Sets the password digest. - -void QXmppSaslServer::setPasswordDigest(const QByteArray &digest) -{ - d->passwordDigest = digest; -} - -/// Returns the realm. - -QString QXmppSaslServer::realm() const -{ - return d->realm; -} - -/// Sets the realm. - -void QXmppSaslServer::setRealm(const QString &realm) -{ - d->realm = realm; -} - -QXmppSaslServerAnonymous::QXmppSaslServerAnonymous(QObject *parent) - : QXmppSaslServer(parent) - , m_step(0) -{ -} - -QString QXmppSaslServerAnonymous::mechanism() const -{ - return "ANONYMOUS"; -} - -QXmppSaslServer::Response QXmppSaslServerAnonymous::respond(const QByteArray &request, QByteArray &response) -{ - Q_UNUSED(request); - if (m_step == 0) { - m_step++; - response = QByteArray(); - return Succeeded; - } else { - warning("QXmppSaslServerAnonymous : Invalid step"); - return Failed; - } -} - -QXmppSaslServerDigestMd5::QXmppSaslServerDigestMd5(QObject *parent) - : QXmppSaslServer(parent) - , m_step(0) -{ - m_nonce = generateNonce(); -} - -QString QXmppSaslServerDigestMd5::mechanism() const -{ - return "DIGEST-MD5"; -} - -QXmppSaslServer::Response QXmppSaslServerDigestMd5::respond(const QByteArray &request, QByteArray &response) -{ - if (m_step == 0) { - QMap<QByteArray, QByteArray> output; - output["nonce"] = m_nonce; - if (!realm().isEmpty()) - output["realm"] = realm().toUtf8(); - output["qop"] = "auth"; - output["charset"] = "utf-8"; - output["algorithm"] = "md5-sess"; - - m_step++; - response = QXmppSaslDigestMd5::serializeMessage(output); - return Challenge; - } else if (m_step == 1) { - const QMap<QByteArray, QByteArray> input = QXmppSaslDigestMd5::parseMessage(request); - const QByteArray realm = input.value("realm"); - const QByteArray digestUri = input.value("digest-uri"); - - if (input.value("qop") != "auth") { - warning("QXmppSaslServerDigestMd5 : Invalid quality of protection"); - return Failed; - } - - setUsername(QString::fromUtf8(input.value("username"))); - if (password().isEmpty() && passwordDigest().isEmpty()) - return InputNeeded; - - m_nc = input.value("nc"); - m_cnonce = input.value("cnonce"); - if (!password().isEmpty()) { - m_secret = QCryptographicHash::hash( - username().toUtf8() + ":" + realm + ":" + password().toUtf8(), - QCryptographicHash::Md5); - } else { - m_secret = passwordDigest(); - } - - if (input.value("response") != calculateDigest("AUTHENTICATE", digestUri, m_secret, m_nonce, m_cnonce, m_nc)) - return Failed; - - QMap<QByteArray, QByteArray> output; - output["rspauth"] = calculateDigest(QByteArray(), digestUri, m_secret, m_nonce, m_cnonce, m_nc); - - m_step++; - response = QXmppSaslDigestMd5::serializeMessage(output); - return Challenge; - } else if (m_step == 2) { - m_step++; - response = QByteArray(); - return Succeeded; - } else { - warning("QXmppSaslServerDigestMd5 : Invalid step"); - return Failed; - } -} - -QXmppSaslServerPlain::QXmppSaslServerPlain(QObject *parent) - : QXmppSaslServer(parent) - , m_step(0) -{ -} - -QString QXmppSaslServerPlain::mechanism() const -{ - return "PLAIN"; -} - -QXmppSaslServer::Response QXmppSaslServerPlain::respond(const QByteArray &request, QByteArray &response) -{ - if (m_step == 0) { - QList<QByteArray> auth = request.split('\0'); - if (auth.size() != 3) { - warning("QXmppSaslServerPlain : Invalid input"); - return Failed; - } - setUsername(QString::fromUtf8(auth[1])); - setPassword(QString::fromUtf8(auth[2])); - - m_step++; - response = QByteArray(); - return InputNeeded; - } else { - warning("QXmppSaslServerPlain : Invalid step"); - return Failed; - } -} - -void QXmppSaslDigestMd5::setNonce(const QByteArray &nonce) -{ - forcedNonce = nonce; -} - -QMap<QByteArray, QByteArray> QXmppSaslDigestMd5::parseMessage(const QByteArray &ba) -{ - QMap<QByteArray, QByteArray> map; - int startIndex = 0; - int pos = 0; - while ((pos = ba.indexOf("=", startIndex)) >= 0) - { - // key get name and skip equals - const QByteArray key = ba.mid(startIndex, pos - startIndex).trimmed(); - pos++; - - // check whether string is quoted - if (ba.at(pos) == '"') - { - // skip opening quote - pos++; - int endPos = ba.indexOf('"', pos); - // skip quoted quotes - while (endPos >= 0 && ba.at(endPos - 1) == '\\') - endPos = ba.indexOf('"', endPos + 1); - if (endPos < 0) - { - qWarning("Unfinished quoted string"); - return map; - } - // unquote - QByteArray value = ba.mid(pos, endPos - pos); - value.replace("\\\"", "\""); - value.replace("\\\\", "\\"); - map[key] = value; - // skip closing quote and comma - startIndex = endPos + 2; - } else { - // non-quoted string - int endPos = ba.indexOf(',', pos); - if (endPos < 0) - endPos = ba.size(); - map[key] = ba.mid(pos, endPos - pos); - // skip comma - startIndex = endPos + 1; - } - } - return map; -} - -QByteArray QXmppSaslDigestMd5::serializeMessage(const QMap<QByteArray, QByteArray> &map) -{ - QByteArray ba; - foreach (const QByteArray &key, map.keys()) - { - if (!ba.isEmpty()) - ba.append(','); - ba.append(key + "="); - QByteArray value = map[key]; - const char *separators = "()<>@,;:\\\"/[]?={} \t"; - bool quote = false; - for (const char *c = separators; *c; c++) - { - if (value.contains(*c)) - { - quote = true; - break; - } - } - if (quote) - { - value.replace("\\", "\\\\"); - value.replace("\"", "\\\""); - ba.append("\"" + value + "\""); - } - else - ba.append(value); - } - return ba; -} - |
