initial commit

This commit is contained in:
mikhail "synzr" 2025-12-25 01:37:49 +05:00
commit 9d20827c46
2469 changed files with 470994 additions and 0 deletions

View file

@ -0,0 +1,676 @@
/*
* simplesasl.cpp - Simple SASL implementation
* Copyright (C) 2003 Justin Karneges
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "simplesasl.h"
#include <qhostaddress.h>
#include <qstringlist.h>
#include <q3ptrlist.h>
#include <QList>
#include <qca.h>
#include <Q3CString>
#include <stdlib.h>
#include <QtCrypto>
#include <QDebug>
#ifdef YAPSI
#include <QUrl>
#include <QVariantMap>
#include <QDateTime>
#include "yaonline.h"
#endif
#define SIMPLESASL_PLAIN
namespace XMPP {
struct Prop
{
Q3CString var, val;
};
class PropList : public QList<Prop>
{
public:
PropList() : QList<Prop>()
{
}
void set(const Q3CString &var, const Q3CString &val)
{
Prop p;
p.var = var;
p.val = val;
append(p);
}
Q3CString get(const Q3CString &var)
{
for(ConstIterator it = begin(); it != end(); ++it) {
if((*it).var == var)
return (*it).val;
}
return Q3CString();
}
Q3CString toString() const
{
Q3CString str;
bool first = true;
for(ConstIterator it = begin(); it != end(); ++it) {
if(!first)
str += ',';
if ((*it).var == "realm" || (*it).var == "nonce" || (*it).var == "username" || (*it).var == "cnonce" || (*it).var == "digest-uri" || (*it).var == "authzid")
str += (*it).var + "=\"" + (*it).val + '\"';
else
str += (*it).var + "=" + (*it).val;
first = false;
}
return str;
}
bool fromString(const QByteArray &str)
{
PropList list;
int at = 0;
while(1) {
while (at < str.length() && (str[at] == ',' || str[at] == ' ' || str[at] == '\t'))
++at;
int n = str.find('=', at);
if(n == -1)
break;
Q3CString var, val;
var = str.mid(at, n-at);
at = n + 1;
if(str[at] == '\"') {
++at;
n = str.find('\"', at);
if(n == -1)
break;
val = str.mid(at, n-at);
at = n + 1;
}
else {
n = at;
while (n < str.length() && str[n] != ',' && str[n] != ' ' && str[n] != '\t')
++n;
val = str.mid(at, n-at);
at = n;
}
Prop prop;
prop.var = var;
if (var == "qop" || var == "cipher") {
int a = 0;
while (a < val.length()) {
while (a < val.length() && (val[a] == ',' || val[a] == ' ' || val[a] == '\t'))
++a;
if (a == val.length())
break;
n = a+1;
while (n < val.length() && val[n] != ',' && val[n] != ' ' && val[n] != '\t')
++n;
prop.val = val.mid(a, n-a);
list.append(prop);
a = n+1;
}
}
else {
prop.val = val;
list.append(prop);
}
if(at >= str.size() - 1 || (str[at] != ',' && str[at] != ' ' && str[at] != '\t'))
break;
}
// integrity check
if(list.varCount("nonce") != 1)
return false;
if(list.varCount("algorithm") != 1)
return false;
*this = list;
return true;
}
int varCount(const Q3CString &var)
{
int n = 0;
for(ConstIterator it = begin(); it != end(); ++it) {
if((*it).var == var)
++n;
}
return n;
}
QStringList getValues(const Q3CString &var)
{
QStringList list;
for(ConstIterator it = begin(); it != end(); ++it) {
if((*it).var == var)
list += (*it).val;
}
return list;
}
};
class SimpleSASLContext : public QCA::SASLContext
{
Q_OBJECT
public:
class ParamsMutable
{
public:
/**
User is held
*/
bool user;
/**
Authorization ID is held
*/
bool authzid;
/**
Password is held
*/
bool pass;
/**
Realm is held
*/
bool realm;
};
// core props
QString service, host;
// state
int step;
bool capable;
bool allow_plain;
#ifdef YAPSI
bool allow_xFacebookPlatform;
#endif
QByteArray out_buf, in_buf;
QString mechanism_;
QString out_mech;
ParamsMutable need;
ParamsMutable have;
QString user, authz, realm;
QCA::SecureArray pass;
Result result_;
QCA::SASL::AuthCondition authCondition_;
QByteArray result_to_net_, result_to_app_;
int encoded_;
SimpleSASLContext(QCA::Provider* p) : QCA::SASLContext(p)
{
reset();
}
~SimpleSASLContext()
{
reset();
}
void reset()
{
resetState();
capable = true;
allow_plain = false;
#ifdef YAPSI
allow_xFacebookPlatform = false;
#endif
need.user = false;
need.authzid = false;
need.pass = false;
need.realm = false;
have.user = false;
have.authzid = false;
have.pass = false;
have.realm = false;
user = QString();
authz = QString();
pass = QCA::SecureArray();
realm = QString();
}
void resetState()
{
out_mech = QString();
out_buf.resize(0);
authCondition_ = QCA::SASL::AuthFail;
}
virtual void setConstraints(QCA::SASL::AuthFlags flags, int ssfMin, int) {
if(flags & (QCA::SASL::RequireForwardSecrecy | QCA::SASL::RequirePassCredentials | QCA::SASL::RequireMutualAuth) || ssfMin > 0)
capable = false;
else
capable = true;
allow_plain = flags & QCA::SASL::AllowPlain;
#ifdef YAPSI
allow_xFacebookPlatform = flags & QCA::SASL::AllowXFacebookPlatform;
#endif
}
virtual void setup(const QString& _service, const QString& _host, const QCA::SASLContext::HostPort*, const QCA::SASLContext::HostPort*, const QString&, int) {
service = _service;
host = _host;
}
virtual void startClient(const QStringList &mechlist, bool allowClientSendFirst) {
Q_UNUSED(allowClientSendFirst);
mechanism_ = QString();
foreach(QString mech, mechlist) {
#ifdef YAPSI
if (mech == "X-FACEBOOK-PLATFORM" && allow_xFacebookPlatform) {
mechanism_ = "X-FACEBOOK-PLATFORM";
break;
}
#endif
if (mech == "DIGEST-MD5") {
mechanism_ = "DIGEST-MD5";
break;
}
#ifdef SIMPLESASL_PLAIN
if (mech == "PLAIN" && allow_plain)
mechanism_ = "PLAIN";
#endif
}
if(!capable || mechanism_.isEmpty()) {
result_ = Error;
authCondition_ = QCA::SASL::NoMechanism;
if (!capable)
qWarning("simplesasl.cpp: Not enough capabilities");
if (mechanism_.isEmpty())
qWarning("simplesasl.cpp: No mechanism available");
QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
return;
}
resetState();
result_ = Continue;
step = 0;
tryAgain();
}
virtual void nextStep(const QByteArray &from_net) {
in_buf = from_net;
tryAgain();
}
virtual void tryAgain() {
// All exits of the method must emit the ready signal
// so all exits go through a goto ready;
if(step == 0) {
out_mech = mechanism_;
#ifdef SIMPLESASL_PLAIN
// PLAIN
if (out_mech == "PLAIN") {
// First, check if we have everything
if(need.user || need.pass) {
qWarning("simplesasl.cpp: Did not receive necessary auth parameters");
result_ = Error;
goto ready;
}
if(!have.user)
need.user = true;
if(!have.pass)
need.pass = true;
if(need.user || need.pass) {
result_ = Params;
goto ready;
}
// Continue with authentication
QByteArray plain;
if (!authz.isEmpty())
plain += authz.utf8();
plain += '\0' + user.toUtf8() + '\0' + pass.toByteArray();
out_buf.resize(plain.length());
memcpy(out_buf.data(), plain.data(), out_buf.size());
}
#endif
++step;
if (out_mech == "PLAIN")
result_ = Success;
else
result_ = Continue;
}
else if(step == 1) {
#ifdef YAPSI
if (out_mech == "X-FACEBOOK-PLATFORM") {
QString fakeUrl = "http://facebook.com/?" + QString(in_buf);
QUrl url = QUrl(fakeUrl, QUrl::TolerantMode);
QVariantMap map;
map["method"] = url.queryItemValue("method");
map["nonce"] = url.queryItemValue("nonce");
map["call_id"] = QDateTime::currentDateTime().toTime_t();
xFacebookPlatformLogin(map);
return;
}
#endif
// if we still need params, then the app has failed us!
if(need.user || need.authzid || need.pass || need.realm) {
qWarning("simplesasl.cpp: Did not receive necessary auth parameters");
result_ = Error;
goto ready;
}
// see if some params are needed
if(!have.user)
need.user = true;
//if(!have.authzid)
// need.authzid = true;
if(!have.pass)
need.pass = true;
if(need.user || need.authzid || need.pass) {
result_ = Params;
goto ready;
}
// get props
QByteArray cs(in_buf);
PropList in;
if(!in.fromString(cs)) {
authCondition_ = QCA::SASL::BadProtocol;
result_ = Error;
goto ready;
}
//qDebug() << (QString("simplesasl.cpp: IN: %1").arg(QString(in.toString())));
// make a cnonce
QByteArray a(32);
for(int n = 0; n < (int)a.size(); ++n)
a[n] = (char)(256.0*rand()/(RAND_MAX+1.0));
Q3CString cnonce = QCA::Base64().arrayToString(a).toLatin1();
// make other variables
if (realm.isEmpty())
realm = QString::fromUtf8(in.get("realm"));
Q3CString nonce = in.get("nonce");
Q3CString nc = "00000001";
Q3CString uri = service.utf8() + '/' + host.utf8();
Q3CString qop = "auth";
// build 'response'
Q3CString X = user.utf8() + ':' + realm.utf8() + ':' + Q3CString(pass.toByteArray());
QByteArray Y = QCA::Hash("md5").hash(X).toByteArray();
QByteArray tmp = ':' + nonce + ':' + cnonce;
if (!authz.isEmpty())
tmp += ':' + authz.utf8();
//qDebug() << (QString(tmp));
QByteArray A1(Y + tmp);
QByteArray A2 = QByteArray("AUTHENTICATE:") + uri;
Q3CString HA1 = QCA::Hash("md5").hashToString(A1).toLatin1();
Q3CString HA2 = QCA::Hash("md5").hashToString(A2).toLatin1();
Q3CString KD = HA1 + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + HA2;
Q3CString Z = QCA::Hash("md5").hashToString(KD).toLatin1();
//qDebug() << (QString("simplesasl.cpp: A1 = %1").arg(QString(A1)).toAscii());
//qDebug() << (QString("simplesasl.cpp: A2 = %1").arg(QString(A2)).toAscii());
//qDebug() << (QString("simplesasl.cpp: KD = %1").arg(QString(KD)).toAscii());
// build output
PropList out;
out.set("username", user.utf8());
if (!realm.isEmpty())
out.set("realm", realm.utf8());
out.set("nonce", nonce);
out.set("cnonce", cnonce);
out.set("nc", nc);
//out.set("serv-type", service.utf8());
//out.set("host", host.utf8());
out.set("digest-uri", uri);
out.set("qop", qop);
out.set("response", Z);
out.set("charset", "utf-8");
if (!authz.isEmpty())
out.set("authzid", authz.utf8());
QByteArray s(out.toString());
//qDebug() << (QString("OUT: %1").arg(QString(out.toString())));
// done
out_buf.resize(s.length());
memcpy(out_buf.data(), s.data(), out_buf.size());
++step;
result_ = Continue;
}
/*else if (step == 2) {
//Commenting this out is Justin's fix for updated QCA.
out_buf.resize(0);
result_ = Continue;
++step;
}*/
else {
out_buf.resize(0);
result_ = Success;
}
ready:
QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
}
virtual void update(const QByteArray &from_net, const QByteArray &from_app) {
result_to_app_ = from_net;
result_to_net_ = from_app;
encoded_ = from_app.size();
result_ = Success;
QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
}
virtual bool waitForResultsReady(int msecs) {
// TODO: for now, all operations block anyway
Q_UNUSED(msecs);
return true;
}
virtual Result result() const {
return result_;
}
virtual QStringList mechlist() const {
return QStringList();
}
virtual QString mech() const {
return out_mech;
}
virtual bool haveClientInit() const {
return out_mech == "PLAIN";
}
virtual QByteArray stepData() const {
return out_buf;
}
virtual QByteArray to_net() {
return result_to_net_;
}
virtual int encoded() const {
return encoded_;
}
virtual QByteArray to_app() {
return result_to_app_;
}
virtual int ssf() const {
return 0;
}
virtual QCA::SASL::AuthCondition authCondition() const {
return authCondition_;
}
virtual QCA::SASL::Params clientParams() const {
return QCA::SASL::Params(need.user, need.authzid, need.pass, need.realm);
}
virtual void setClientParams(const QString *_user, const QString *_authzid, const QCA::SecureArray *_pass, const QString *_realm) {
if(_user) {
user = *_user;
need.user = false;
have.user = true;
}
if(_authzid) {
authz = *_authzid;
need.authzid = false;
have.authzid = true;
}
if(_pass) {
pass = *_pass;
need.pass = false;
have.pass = true;
}
if(_realm) {
realm = *_realm;
need.realm = false;
have.realm = true;
}
}
virtual QStringList realmlist() const
{
// TODO
return QStringList();
}
virtual QString username() const {
return QString();
}
virtual QString authzid() const {
return QString();
}
virtual QCA::Provider::Context* clone() const {
SimpleSASLContext* s = new SimpleSASLContext(provider());
// TODO: Copy all the members
return s;
}
virtual void startServer(const QString &, bool) {
result_ = QCA::SASLContext::Error;
QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
}
virtual void serverFirstStep(const QString &, const QByteArray *) {
result_ = QCA::SASLContext::Error;
QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
}
public slots:
#ifdef YAPSI
void xFacebookPlatformLogin(QVariantMap map) {
#ifdef YAPSI_ACTIVEX_SERVER
YaOnlineHelper::instance()->xFacebookPlatformLogin(map, this, "xFacebookPlatformDataReady");
#else
QString apiKey = "API_KEY";
QString sessionKey = "SESSION_KEY";
QString secret = "SECRET";
map["api_key"] = apiKey;
map["session_key"] = sessionKey;
map["v"] = "1.0";
QStringList signingList;
QStringList resultList;
QMapIterator<QString, QVariant> it(map);
while (it.hasNext()) {
it.next();
signingList += QString("%1=%2")
.arg(it.key())
.arg(it.value().toString());
resultList += QString("%1=%2")
.arg(it.key())
.arg(QUrl::toPercentEncoding(it.value().toString()).constData());
}
QString signingData = signingList.join("") + secret;
QString resultData = resultList.join("&");
QString signature = QCA::Hash("md5").hashToString(signingData.toUtf8());
resultData += "&sig=" + signature;
// qWarning("signingData = '%s'", qPrintable(signingData));
// qWarning("resultData = '%s'", qPrintable(resultData));
xFacebookPlatformDataReady(resultData);
#endif
}
void xFacebookPlatformDataReady(const QString& data) {
Q_ASSERT(out_mech == "X-FACEBOOK-PLATFORM");
out_buf = data.toUtf8();
++step;
result_ = Success;
QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
}
#endif
};
class QCASimpleSASL : public QCA::Provider
{
public:
QCASimpleSASL() {}
~QCASimpleSASL() {}
void init()
{
}
QString name() const {
return "simplesasl";
}
QStringList features() const {
return QStringList("sasl");
}
QCA::Provider::Context* createContext(const QString& cap)
{
if(cap == "sasl")
return new SimpleSASLContext(this);
return 0;
}
int qcaVersion() const
{
return QCA_VERSION;
}
};
QCA::Provider *createProviderSimpleSASL()
{
return (new QCASimpleSASL);
}
}
#include "simplesasl.moc"