aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2010-08-29 12:01:32 +0000
committerJeremy Lainé <jeremy.laine@m4x.org>2010-08-29 12:01:32 +0000
commit1f3472f71ae867d9fc5e5482a355f12465c23ece (patch)
treead92ea206b084c9c2cbf5280596d6841b2c9a591 /src
parent89f37db10a84a9c74a9cdf44839316c166b8b460 (diff)
downloadqxmpp-1f3472f71ae867d9fc5e5482a355f12465c23ece.tar.gz
fix SASL authentication :
- on the client side, check the second challenge we receive - on the server side, send second challenge
Diffstat (limited to 'src')
-rw-r--r--src/QXmppIncomingClient.cpp77
-rw-r--r--src/QXmppOutgoingClient.cpp88
-rw-r--r--src/QXmppOutgoingClient.h2
-rw-r--r--src/QXmppSaslAuth.cpp134
-rw-r--r--src/QXmppSaslAuth.h70
-rw-r--r--src/QXmppUtils.cpp19
-rw-r--r--src/QXmppUtils.h3
-rw-r--r--src/src.pro2
8 files changed, 305 insertions, 90 deletions
diff --git a/src/QXmppIncomingClient.cpp b/src/QXmppIncomingClient.cpp
index f70d6edf..eb23d7bc 100644
--- a/src/QXmppIncomingClient.cpp
+++ b/src/QXmppIncomingClient.cpp
@@ -29,6 +29,7 @@
#include "QXmppBindIq.h"
#include "QXmppConstants.h"
#include "QXmppMessage.h"
+#include "QXmppSaslAuth.h"
#include "QXmppSessionIq.h"
#include "QXmppStreamFeatures.h"
#include "QXmppUtils.h"
@@ -44,7 +45,8 @@ public:
QString username;
QString resource;
QXmppPasswordChecker *passwordChecker;
- QByteArray saslNonce;
+ QXmppSaslDigestMd5 saslDigest;
+ int saslStep;
};
/// Constructs a new incoming client stream.
@@ -60,6 +62,7 @@ QXmppIncomingClient::QXmppIncomingClient(QSslSocket *socket, const QString &doma
{
d->passwordChecker = 0;
d->domain = domain;
+ d->saslStep = 0;
setObjectName("C2S-in");
setSocket(socket);
@@ -119,6 +122,7 @@ void QXmppIncomingClient::setPasswordChecker(QXmppPasswordChecker *checker)
void QXmppIncomingClient::handleStream(const QDomElement &streamElement)
{
d->idleTimer->start();
+ d->saslStep = 0;
// start stream
const QByteArray sessionId = generateStanzaHash().toAscii();
@@ -208,14 +212,13 @@ void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv)
else if (mechanism == "DIGEST-MD5")
{
// generate nonce
- QByteArray nonce(32, 'm');
- for(int n = 0; n < nonce.size(); ++n)
- nonce[n] = (char)(256.0*qrand()/(RAND_MAX+1.0));
- d->saslNonce = nonce.toBase64();
+ d->saslDigest.setNonce(QXmppSaslDigestMd5::generateNonce());
+ d->saslDigest.setRealm(d->domain.toUtf8());
+ d->saslStep = 1;
QMap<QByteArray, QByteArray> challenge;
- challenge["nonce"] = d->saslNonce;
- challenge["realm"] = d->domain.toUtf8();
+ challenge["nonce"] = d->saslDigest.nonce();
+ challenge["realm"] = d->saslDigest.realm();
challenge["qop"] = "auth";
challenge["charset"] = "utf-8";
challenge["algorithm"] = "md5-sess";
@@ -236,32 +239,45 @@ void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv)
const QByteArray raw = QByteArray::fromBase64(nodeRecv.text().toAscii());
QMap<QByteArray, QByteArray> response = parseDigestMd5(raw);
- // check credentials
- const QString username = QString::fromUtf8(response.value("username"));
- QString password;
- if (!d->passwordChecker || !d->passwordChecker->getPassword(username, password))
+ if (d->saslStep == 1)
{
- sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>");
- disconnectFromHost();
- return;
+ // check credentials
+ const QString username = QString::fromUtf8(response.value("username"));
+ QString password;
+ if (!d->passwordChecker || !d->passwordChecker->getPassword(username, password))
+ {
+ sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>");
+ disconnectFromHost();
+ return;
+ }
+ d->saslDigest.setUsername(username.toUtf8());
+ d->saslDigest.setPassword(password.toUtf8());
+ 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;
+ }
+
+ // 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 = serializeDigestMd5(challenge).toBase64();
+ sendData("<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" + data +"</challenge>");
}
- const QByteArray a1 = username.toUtf8() + ':' + d->domain.toUtf8() + ':' + password.toUtf8();
- const QByteArray remote = QByteArray::fromHex(response["response"]);
- if (remote != calculateDigestMd5(a1,
- d->saslNonce,
- response.value("nc"),
- response.value("cnonce"),
- response.value("digest-uri"),
- QByteArray()))
+ else if (d->saslStep == 2)
{
- sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>");
- disconnectFromHost();
- return;
+ // authentication succeeded
+ d->saslStep = 3;
+ sendData("<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>");
}
-
- // authentication succeeded
- d->username = username;
- sendData("<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>");
}
}
else if (ns == ns_client)
@@ -290,9 +306,10 @@ void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv)
QXmppSessionIq sessionSet;
sessionSet.parse(nodeRecv);
- QXmppSessionIq sessionResult;
+ QXmppIq sessionResult;
sessionResult.setType(QXmppIq::Result);
sessionResult.setId(sessionSet.id());
+ sessionResult.setTo(jid());
sendPacket(sessionResult);
return;
}
diff --git a/src/QXmppOutgoingClient.cpp b/src/QXmppOutgoingClient.cpp
index b4cf1281..b77b72ba 100644
--- a/src/QXmppOutgoingClient.cpp
+++ b/src/QXmppOutgoingClient.cpp
@@ -34,6 +34,7 @@
#include "QXmppOutgoingClient.h"
#include "QXmppStreamFeatures.h"
#include "QXmppNonSASLAuth.h"
+#include "QXmppSaslAuth.h"
#include "QXmppUtils.h"
// IQ types
@@ -75,7 +76,6 @@ public:
QXmppStanza::Error::Condition xmppStreamError;
// State data
- int authStep;
QString bindId;
QString sessionId;
bool sessionAvailable;
@@ -85,14 +85,18 @@ public:
QString streamVersion;
QString nonSASLAuthId;
+ // SASL
+ QXmppSaslDigestMd5 saslDigest;
+ int saslStep;
+
// Timers
QTimer *pingTimer;
QTimer *timeoutTimer;
};
QXmppOutgoingClientPrivate::QXmppOutgoingClientPrivate()
- : authStep(0),
- sessionAvailable(false)
+ : sessionAvailable(false),
+ saslStep(0)
{
}
@@ -184,7 +188,7 @@ void QXmppOutgoingClient::socketError(QAbstractSocket::SocketError ee)
void QXmppOutgoingClient::handleStart()
{
// reset authentication step
- d->authStep = 0;
+ d->saslStep = 0;
d->sessionStarted = false;
// start stream
@@ -349,14 +353,14 @@ void QXmppOutgoingClient::handleStanza(const QDomElement &nodeRecv)
else if(nodeRecv.tagName() == "challenge")
{
// TODO: Track which mechanism was used for when other SASL protocols which use challenges are supported
- d->authStep++;
- switch (d->authStep)
+ d->saslStep++;
+ switch (d->saslStep)
{
case 1 :
sendAuthDigestMD5ResponseStep1(nodeRecv.text());
break;
case 2 :
- sendAuthDigestMD5ResponseStep2();
+ sendAuthDigestMD5ResponseStep2(nodeRecv.text());
break;
default :
warning("Too many authentication steps");
@@ -715,7 +719,6 @@ void QXmppOutgoingClient::pingTimeout()
void QXmppOutgoingClient::sendAuthDigestMD5ResponseStep1(const QString& challenge)
{
QByteArray ba = QByteArray::fromBase64(challenge.toUtf8());
-
QMap<QByteArray, QByteArray> map = parseDigestMd5(ba);
if (!map.contains("nonce"))
@@ -725,38 +728,30 @@ void QXmppOutgoingClient::sendAuthDigestMD5ResponseStep1(const QString& challeng
return;
}
- const QByteArray user = configuration().user().toUtf8();
- const QByteArray passwd = configuration().passwd().toUtf8();
- const QByteArray domain = configuration().domain().toUtf8();
- const QByteArray realm = map.value("realm");
-
- QByteArray cnonce(32, 'm');
- for(int n = 0; n < cnonce.size(); ++n)
- cnonce[n] = (char)(256.0*qrand()/(RAND_MAX+1.0));
-
- // The random data can the '=' char is not valid as it is a delimiter,
- // so to be safe, base64 the nonce
- cnonce = cnonce.toBase64();
-
- const QByteArray nc = "00000001";
- const QByteArray digest_uri = "xmpp/" + domain;
- const QByteArray authzid = map.value("authzid");
- const QByteArray nonce = map.value("nonce");
- const QByteArray a1 = user + ':' + realm + ':' + passwd;
+ 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.setRealm(map.value("realm"));
+ d->saslDigest.setUsername(configuration().user().toUtf8());
+ d->saslDigest.setPassword(configuration().passwd().toUtf8());
// Build response
QMap<QByteArray, QByteArray> response;
- response["username"] = user;
- if(!realm.isEmpty())
- response["realm"] = realm;
- response["nonce"] = map["nonce"];
- response["cnonce"] = cnonce;
- response["nc"] = nc;
+ response["username"] = d->saslDigest.username();
+ if(!d->saslDigest.realm().isEmpty())
+ response["realm"] = d->saslDigest.realm();
+ response["nonce"] = d->saslDigest.nonce();
+ response["cnonce"] = d->saslDigest.cnonce();
+ response["nc"] = d->saslDigest.nc();
response["qop"] = "auth";
- response["digest-uri"] = digest_uri;
- response["response"] = calculateDigestMd5(a1, nonce, nc, cnonce, digest_uri, authzid).toHex();
- if(!authzid.isEmpty())
- response["authzid"] = authzid;
+ 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 = serializeDigestMd5(response);
@@ -765,8 +760,27 @@ void QXmppOutgoingClient::sendAuthDigestMD5ResponseStep1(const QString& challeng
sendData(packet);
}
-void QXmppOutgoingClient::sendAuthDigestMD5ResponseStep2()
+void QXmppOutgoingClient::sendAuthDigestMD5ResponseStep2(const QString &challenge)
{
+ QByteArray ba = QByteArray::fromBase64(challenge.toUtf8());
+ QMap<QByteArray, QByteArray> map = parseDigestMd5(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'/>");
}
diff --git a/src/QXmppOutgoingClient.h b/src/QXmppOutgoingClient.h
index 7371c307..5909962b 100644
--- a/src/QXmppOutgoingClient.h
+++ b/src/QXmppOutgoingClient.h
@@ -146,7 +146,7 @@ private slots:
private:
QXmppDiscoveryIq capabilities() const;
void sendAuthDigestMD5ResponseStep1(const QString& challenge);
- void sendAuthDigestMD5ResponseStep2();
+ void sendAuthDigestMD5ResponseStep2(const QString& challenge);
void sendNonSASLAuth(bool plaintext);
void sendNonSASLAuthQuery();
diff --git a/src/QXmppSaslAuth.cpp b/src/QXmppSaslAuth.cpp
new file mode 100644
index 00000000..b82c4231
--- /dev/null
+++ b/src/QXmppSaslAuth.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2008-2010 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 "QXmppSaslAuth.h"
+#include "QXmppUtils.h"
+
+QByteArray QXmppSaslDigestMd5::authzid() const
+{
+ return m_authzid;
+}
+
+void QXmppSaslDigestMd5::setAuthzid(const QByteArray &authzid)
+{
+ m_authzid = authzid;
+}
+
+QByteArray QXmppSaslDigestMd5::cnonce() const
+{
+ return m_cnonce;
+}
+
+void QXmppSaslDigestMd5::setCnonce(const QByteArray &cnonce)
+{
+ m_cnonce = cnonce;
+}
+
+QByteArray QXmppSaslDigestMd5::digestUri() const
+{
+ return m_digestUri;
+}
+
+void QXmppSaslDigestMd5::setDigestUri(const QByteArray &digestUri)
+{
+ m_digestUri = digestUri;
+}
+
+QByteArray QXmppSaslDigestMd5::nc() const
+{
+ return m_nc;
+}
+
+void QXmppSaslDigestMd5::setNc(const QByteArray &nc)
+{
+ m_nc = nc;
+}
+
+QByteArray QXmppSaslDigestMd5::nonce() const
+{
+ return m_nonce;
+}
+
+void QXmppSaslDigestMd5::setNonce(const QByteArray &nonce)
+{
+ m_nonce = nonce;
+}
+
+QByteArray QXmppSaslDigestMd5::realm() const
+{
+ return m_realm;
+}
+
+void QXmppSaslDigestMd5::setRealm(const QByteArray &realm)
+{
+ m_realm = realm;
+}
+
+QByteArray QXmppSaslDigestMd5::username() const
+{
+ return m_username;
+}
+
+void QXmppSaslDigestMd5::setUsername(const QByteArray &username)
+{
+ m_username = username;
+}
+
+void QXmppSaslDigestMd5::setPassword(const QByteArray &password)
+{
+ m_password = password;
+}
+
+QByteArray QXmppSaslDigestMd5::generateNonce()
+{
+ QByteArray nonce(32, 'm');
+ for(int n = 0; n < nonce.size(); ++n)
+ nonce[n] = (char)(256.0*qrand()/(RAND_MAX+1.0));
+
+ // The random data can the '=' char is not valid as it is a delimiter,
+ // so to be safe, base64 the nonce
+ return nonce.toBase64();
+}
+
+QByteArray QXmppSaslDigestMd5::calculateDigest(const QByteArray &A2) const
+{
+ const QByteArray a1 = m_username + ':' + m_realm + ':' + m_password;
+ QByteArray ha1 = QCryptographicHash::hash(a1, QCryptographicHash::Md5);
+ ha1 += ':' + m_nonce + ':' + m_cnonce;
+
+ if (!m_authzid.isEmpty())
+ ha1 += ':' + m_authzid;
+
+ QByteArray A1(ha1);
+ QByteArray HA1 = QCryptographicHash::hash(A1, QCryptographicHash::Md5).toHex();
+ QByteArray HA2 = QCryptographicHash::hash(A2, QCryptographicHash::Md5).toHex();
+ QByteArray KD = HA1 + ':' + m_nonce + ':' + m_nc + ':' + m_cnonce + ':'
+ + "auth" + ':' + HA2;
+ return QCryptographicHash::hash(KD, QCryptographicHash::Md5).toHex();
+}
+
diff --git a/src/QXmppSaslAuth.h b/src/QXmppSaslAuth.h
new file mode 100644
index 00000000..7e8c548e
--- /dev/null
+++ b/src/QXmppSaslAuth.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2008-2010 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.
+ *
+ */
+
+#ifndef QXMPPSASLAUTH_H
+#define QXMPPSASLAUTH_H
+
+#include <QByteArray>
+
+class QXmppSaslDigestMd5
+{
+public:
+ QByteArray authzid() const;
+ void setAuthzid(const QByteArray &cnonce);
+
+ QByteArray cnonce() const;
+ void setCnonce(const QByteArray &cnonce);
+
+ QByteArray digestUri() const;
+ void setDigestUri(const QByteArray &digestUri);
+
+ QByteArray nc() const;
+ void setNc(const QByteArray &nc);
+
+ QByteArray nonce() const;
+ void setNonce(const QByteArray &nonce);
+
+ QByteArray realm() const;
+ void setRealm(const QByteArray &realm);
+
+ QByteArray username() const;
+ void setUsername(const QByteArray &username);
+
+ void setPassword(const QByteArray &password);
+
+ QByteArray calculateDigest(const QByteArray &a2) const;
+ static QByteArray generateNonce();
+
+private:
+ QByteArray m_authzid;
+ QByteArray m_cnonce;
+ QByteArray m_digestUri;
+ QByteArray m_nc;
+ QByteArray m_nonce;
+ QByteArray m_realm;
+ QByteArray m_username;
+ QByteArray m_password;
+};
+
+#endif
diff --git a/src/QXmppUtils.cpp b/src/QXmppUtils.cpp
index ecd25e83..5879f3a8 100644
--- a/src/QXmppUtils.cpp
+++ b/src/QXmppUtils.cpp
@@ -299,25 +299,6 @@ QString unescapeString(const QString& str)
return strOut;
}
-QByteArray calculateDigestMd5(const QByteArray &a1,
- const QByteArray &nonce, const QByteArray &nc, const QByteArray &cnonce,
- const QByteArray &digest_uri, const QByteArray &authzid)
-{
- QByteArray ha1 = QCryptographicHash::hash(a1, QCryptographicHash::Md5);
- ha1 += ':' + nonce + ':' + cnonce;
-
- if (!authzid.isEmpty())
- ha1 += ':' + authzid;
-
- QByteArray A1(ha1);
- QByteArray A2 = "AUTHENTICATE:" + digest_uri;
- QByteArray HA1 = QCryptographicHash::hash(A1, QCryptographicHash::Md5).toHex();
- QByteArray HA2 = QCryptographicHash::hash(A2, QCryptographicHash::Md5).toHex();
- QByteArray KD = HA1 + ':' + nonce + ':' + nc + ':' + cnonce + ':'
- + "auth" + ':' + HA2;
- return QCryptographicHash::hash(KD, QCryptographicHash::Md5);
-}
-
QMap<QByteArray, QByteArray> parseDigestMd5(const QByteArray &ba)
{
QMap<QByteArray, QByteArray> map;
diff --git a/src/QXmppUtils.h b/src/QXmppUtils.h
index 46e53f5b..cc5e1cbf 100644
--- a/src/QXmppUtils.h
+++ b/src/QXmppUtils.h
@@ -67,9 +67,6 @@ QString escapeString(const QString& str);
QString unescapeString(const QString& str);
// Digest MD5 authentication
-QByteArray calculateDigestMd5(const QByteArray &a1,
- const QByteArray &nonce, const QByteArray &nc, const QByteArray &cnonce,
- const QByteArray &digest_uri, const QByteArray &authzid);
QMap<QByteArray, QByteArray> parseDigestMd5(const QByteArray &ba);
QByteArray serializeDigestMd5(const QMap<QByteArray, QByteArray> &map);
diff --git a/src/src.pro b/src/src.pro
index 1562a560..40c8c089 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -51,6 +51,7 @@ INSTALL_HEADERS = QXmppUtils.h \
QXmppRoster.h \
QXmppRosterIq.h \
QXmppRosterManager.h \
+ QXmppSaslAuth.h \
QXmppServer.h \
QXmppServerExtension.h \
QXmppServerPlugin.h \
@@ -103,6 +104,7 @@ SOURCES += QXmppUtils.cpp \
QXmppPresence.cpp \
QXmppRosterIq.cpp \
QXmppRosterManager.cpp \
+ QXmppSaslAuth.cpp \
QXmppServer.cpp \
QXmppServerExtension.cpp \
QXmppSessionIq.cpp \