aboutsummaryrefslogtreecommitdiff
path: root/src/base/QXmppSaslAuth.cpp
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2012-07-20 17:37:38 +0200
committerJeremy Lainé <jeremy.laine@m4x.org>2012-07-20 17:37:38 +0200
commit51eef84fd4104f19228ebd85fce89658b64464a2 (patch)
tree31db52023ae0a063714140897bcbbfb6010ba04a /src/base/QXmppSaslAuth.cpp
parentdb4fe78387247ff039602030d964e4c0d6e39f46 (diff)
downloadqxmpp-51eef84fd4104f19228ebd85fce89658b64464a2.tar.gz
make SASL code fully private
Diffstat (limited to 'src/base/QXmppSaslAuth.cpp')
-rw-r--r--src/base/QXmppSaslAuth.cpp743
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;
-}
-