aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2012-07-20 10:59:53 +0200
committerJeremy Lainé <jeremy.laine@m4x.org>2012-07-20 10:59:53 +0200
commit15cab790071aabfc62977d590b89f54a5fa6e92d (patch)
tree4b75e1269ab983d06130340325a4ee97baf901e7
parenta609100c2a0eb19ef4b9c7a571366281ca722ac8 (diff)
downloadqxmpp-15cab790071aabfc62977d590b89f54a5fa6e92d.tar.gz
first stab at factoring out server-side SASL
-rw-r--r--src/base/QXmppSaslAuth.cpp149
-rw-r--r--src/base/QXmppSaslAuth.h28
-rw-r--r--src/base/QXmppSaslAuth_p.h49
-rw-r--r--tests/sasl.cpp68
-rw-r--r--tests/sasl.h12
-rw-r--r--tests/tests.cpp7
6 files changed, 309 insertions, 4 deletions
diff --git a/src/base/QXmppSaslAuth.cpp b/src/base/QXmppSaslAuth.cpp
index 0ae3b1df..ead1188f 100644
--- a/src/base/QXmppSaslAuth.cpp
+++ b/src/base/QXmppSaslAuth.cpp
@@ -104,7 +104,7 @@ void QXmppSaslClient::setServiceType(const QString &serviceType)
d->serviceType = serviceType;
}
-/// Returns the host.
+/// Returns the username.
QString QXmppSaslClient::username() const
{
@@ -308,6 +308,153 @@ bool QXmppSaslClientPlain::respond(const QByteArray &challenge, QByteArray &resp
}
}
+class QXmppSaslServerPrivate
+{
+public:
+ QString username;
+ QString password;
+};
+
+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);
+ }
+}
+
+/// 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;
+}
+
+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)
+{
+}
+
+QString QXmppSaslServerDigestMd5::mechanism() const
+{
+ return "DIGEST-MD5";
+}
+
+QXmppSaslServer::Response QXmppSaslServerDigestMd5::respond(const QByteArray &request, QByteArray &response)
+{
+ if (m_step == 0) {
+ // generate nonce
+ m_saslDigest.setNonce(QXmppSaslDigestMd5::generateNonce());
+ //m_saslDigest.setQop("auth");
+
+ QMap<QByteArray, QByteArray> challenge;
+ challenge["nonce"] = m_saslDigest.nonce();
+ //challenge["realm"] = m_domain.toUtf8();
+ challenge["qop"] = "auth";
+ challenge["charset"] = "utf-8";
+ challenge["algorithm"] = "md5-sess";
+
+ m_step++;
+ response = QXmppSaslDigestMd5::serializeMessage(challenge);
+ return Challenge;
+ } else {
+ 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 Succeeded;
+ } else {
+ warning("QXmppSaslServerPlain : Invalid step");
+ return Failed;
+ }
+}
+
QByteArray QXmppSaslDigestMd5::authzid() const
{
return m_authzid;
diff --git a/src/base/QXmppSaslAuth.h b/src/base/QXmppSaslAuth.h
index b6428eb8..9026d757 100644
--- a/src/base/QXmppSaslAuth.h
+++ b/src/base/QXmppSaslAuth.h
@@ -32,6 +32,7 @@
#include "QXmppLogger.h"
class QXmppSaslClientPrivate;
+class QXmppSaslServerPrivate;
class QXMPP_EXPORT QXmppSaslDigestMd5
{
@@ -105,4 +106,31 @@ private:
QXmppSaslClientPrivate *d;
};
+class QXMPP_EXPORT QXmppSaslServer : public QXmppLoggable
+{
+public:
+ enum Response {
+ Challenge = 0,
+ Succeeded = 1,
+ Failed = 2
+ };
+
+ QXmppSaslServer(QObject *parent = 0);
+ virtual ~QXmppSaslServer();
+
+ QString username() const;
+ void setUsername(const QString &username);
+
+ QString password() const;
+ void setPassword(const QString &password);
+
+ virtual QString mechanism() const = 0;
+ virtual Response respond(const QByteArray &challenge, QByteArray &response) = 0;
+
+ static QXmppSaslServer* create(const QString &mechanism, QObject *parent = 0);
+
+private:
+ QXmppSaslServerPrivate *d;
+};
+
#endif
diff --git a/src/base/QXmppSaslAuth_p.h b/src/base/QXmppSaslAuth_p.h
index 18c1829e..10a20cfb 100644
--- a/src/base/QXmppSaslAuth_p.h
+++ b/src/base/QXmppSaslAuth_p.h
@@ -81,4 +81,53 @@ private:
int m_step;
};
+class QXmppSaslServerAnonymous : public QXmppSaslServer
+{
+public:
+ QXmppSaslServerAnonymous(QObject *parent = 0);
+ QString mechanism() const;
+
+ Response respond(const QByteArray &challenge, QByteArray &response);
+
+private:
+ int m_step;
+};
+
+class QXmppSaslServerDigestMd5 : public QXmppSaslServer
+{
+public:
+ QXmppSaslServerDigestMd5(QObject *parent = 0);
+ QString mechanism() const;
+
+ Response respond(const QByteArray &challenge, QByteArray &response);
+
+private:
+ QXmppSaslDigestMd5 m_saslDigest;
+ int m_step;
+};
+
+class QXmppSaslServerFacebook : public QXmppSaslServer
+{
+public:
+ QXmppSaslServerFacebook(QObject *parent = 0);
+ QString mechanism() const;
+
+ Response respond(const QByteArray &challenge, QByteArray &response);
+
+private:
+ int m_step;
+};
+
+class QXmppSaslServerPlain : public QXmppSaslServer
+{
+public:
+ QXmppSaslServerPlain(QObject *parent = 0);
+ QString mechanism() const;
+
+ Response respond(const QByteArray &challenge, QByteArray &response);
+
+private:
+ int m_step;
+};
+
#endif
diff --git a/tests/sasl.cpp b/tests/sasl.cpp
index e2dd8486..622bccb6 100644
--- a/tests/sasl.cpp
+++ b/tests/sasl.cpp
@@ -31,6 +31,12 @@ void tst_QXmppSaslClient::testAvailableMechanisms()
QCOMPARE(QXmppSaslClient::availableMechanisms(), QStringList() << "PLAIN" << "DIGEST-MD5" << "ANONYMOUS" << "X-FACEBOOK-PLATFORM");
}
+void tst_QXmppSaslClient::testBadMechanism()
+{
+ QXmppSaslClient *client = QXmppSaslClient::create("BAD-MECH");
+ QVERIFY(client == 0);
+}
+
void tst_QXmppSaslClient::testAnonymous()
{
QXmppSaslClient *client = QXmppSaslClient::create("ANONYMOUS");
@@ -41,7 +47,7 @@ void tst_QXmppSaslClient::testAnonymous()
QByteArray response;
QVERIFY(client->respond(QByteArray(), response));
QCOMPARE(response, QByteArray());
-
+
// any further step is an error
QVERIFY(!client->respond(QByteArray(), response));
@@ -131,3 +137,63 @@ void tst_QXmppSaslClient::testPlain()
delete client;
}
+void tst_QXmppSaslServer::testBadMechanism()
+{
+ QXmppSaslServer *server = QXmppSaslServer::create("BAD-MECH");
+ QVERIFY(server == 0);
+}
+
+void tst_QXmppSaslServer::testAnonymous()
+{
+ QXmppSaslServer *server = QXmppSaslServer::create("ANONYMOUS");
+ QVERIFY(server != 0);
+ QCOMPARE(server->mechanism(), QLatin1String("ANONYMOUS"));
+
+ // initial step returns success
+ QByteArray response;
+ QCOMPARE(server->respond(QByteArray(), response), QXmppSaslServer::Succeeded);
+ QCOMPARE(response, QByteArray());
+
+ // any further step is an error
+ QCOMPARE(server->respond(QByteArray(), response), QXmppSaslServer::Failed);
+
+ delete server;
+}
+
+void tst_QXmppSaslServer::testDigestMd5()
+{
+ qsrand(0);
+
+ QXmppSaslServer *server = QXmppSaslServer::create("DIGEST-MD5");
+ QVERIFY(server != 0);
+ QCOMPARE(server->mechanism(), QLatin1String("DIGEST-MD5"));
+
+ // initial step returns challenge
+ QByteArray response;
+ QCOMPARE(server->respond(QByteArray(), response), QXmppSaslServer::Challenge);
+ QCOMPARE(response, QByteArray("algorithm=md5-sess,charset=utf-8,nonce=\"AMzVG8Oibf+sVUCPPlWLR8lZQvbbJtJB9vJd+u3c6dw=\",qop=auth"));
+
+ // any further step is an error
+ QCOMPARE(server->respond(QByteArray(), response), QXmppSaslServer::Failed);
+
+ delete server;
+}
+
+void tst_QXmppSaslServer::testPlain()
+{
+ QXmppSaslServer *server = QXmppSaslServer::create("PLAIN");
+ QVERIFY(server != 0);
+ QCOMPARE(server->mechanism(), QLatin1String("PLAIN"));
+
+ // initial step returns success
+ QByteArray response;
+ QCOMPARE(server->respond(QByteArray("\0foo\0bar", 8), response), QXmppSaslServer::Succeeded);
+ QCOMPARE(response, QByteArray());
+ QCOMPARE(server->username(), QLatin1String("foo"));
+ QCOMPARE(server->password(), QLatin1String("bar"));
+
+ // any further step is an error
+ QCOMPARE(server->respond(QByteArray(), response), QXmppSaslServer::Failed);
+
+ delete server;
+}
diff --git a/tests/sasl.h b/tests/sasl.h
index e6db2b01..b1bb7603 100644
--- a/tests/sasl.h
+++ b/tests/sasl.h
@@ -29,6 +29,7 @@ class tst_QXmppSaslClient : public QObject
private slots:
void testAvailableMechanisms();
+ void testBadMechanism();
void testAnonymous();
void testDigestMd5();
void testDigestMd5_data();
@@ -36,3 +37,14 @@ private slots:
void testPlain();
};
+class tst_QXmppSaslServer : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void testBadMechanism();
+ void testAnonymous();
+ void testDigestMd5();
+ void testPlain();
+};
+
diff --git a/tests/tests.cpp b/tests/tests.cpp
index 49a60613..acb6b145 100644
--- a/tests/tests.cpp
+++ b/tests/tests.cpp
@@ -1378,8 +1378,11 @@ int main(int argc, char *argv[])
tst_QXmppRtpPacket testRtp;
errors += QTest::qExec(&testRtp);
- tst_QXmppSaslClient testSasl;
- errors += QTest::qExec(&testSasl);
+ tst_QXmppSaslClient testSaslClient;
+ errors += QTest::qExec(&testSaslClient);
+
+ tst_QXmppSaslServer testSaslServer;
+ errors += QTest::qExec(&testSaslServer);
TestServer testServer;
errors += QTest::qExec(&testServer);