You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/kopete/protocols/jabber/libiris/iris/xmpp-core/simplesasl.cpp

512 lines
9.7 KiB

/*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "simplesasl.h"
#include <tqhostaddress.h>
#include <tqstringlist.h>
#include <tqptrlist.h>
#include <tqvaluelist.h>
#include <qca.h>
#include <stdlib.h>
#include "base64.h"
namespace XMPP
{
struct Prop
{
TQCString var, val;
};
class PropList : public TQValueList<Prop>
{
public:
PropList() : TQValueList<Prop>()
{
}
void set(const TQCString &var, const TQCString &val)
{
Prop p;
p.var = var;
p.val = val;
append(p);
}
TQCString get(const TQCString &var)
{
for(ConstIterator it = begin(); it != end(); ++it) {
if((*it).var == var)
return (*it).val;
}
return TQCString();
}
TQCString toString() const
{
TQCString str;
bool first = true;
for(ConstIterator it = begin(); it != end(); ++it) {
if(!first)
str += ',';
str += (*it).var + "=\"" + (*it).val + '\"';
first = false;
}
return str;
}
bool fromString(const TQCString &str)
{
PropList list;
int at = 0;
while(1) {
int n = str.find('=', at);
if(n == -1)
break;
TQCString 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 = str.find(',', at);
if(n != -1) {
val = str.mid(at, n-at);
at = n;
}
else {
val = str.mid(at);
at = str.length()-1;
}
}
Prop prop;
prop.var = var;
prop.val = val;
list.append(prop);
if(str[at] != ',')
break;
++at;
}
// integrity check
if(list.varCount("nonce") != 1)
return false;
if(list.varCount("algorithm") != 1)
return false;
*this = list;
return true;
}
int varCount(const TQCString &var)
{
int n = 0;
for(ConstIterator it = begin(); it != end(); ++it) {
if((*it).var == var)
++n;
}
return n;
}
TQStringList getValues(const TQCString &var)
{
TQStringList list;
for(ConstIterator it = begin(); it != end(); ++it) {
if((*it).var == var)
list += (*it).val;
}
return list;
}
};
class SimpleSASLContext : public TQCA_SASLContext
{
public:
// core props
TQString service, host;
// state
int step;
TQByteArray in_buf;
TQString out_mech;
TQByteArray out_buf;
bool capable;
bool allow_plain;
int err;
TQCA_SASLNeedParams need;
TQCA_SASLNeedParams have;
TQString user, authz, pass, realm;
SimpleSASLContext()
{
reset();
}
~SimpleSASLContext()
{
reset();
}
void reset()
{
resetState();
resetParams();
}
void resetState()
{
out_mech = TQString();
out_buf.resize(0);
err = -1;
}
void resetParams()
{
capable = true;
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 = TQString();
authz = TQString();
pass = TQString();
realm = TQString();
}
void setCoreProps(const TQString &_service, const TQString &_host, TQCA_SASLHostPort *, TQCA_SASLHostPort *)
{
service = _service;
host = _host;
}
void setSecurityProps(bool noPlain, bool, bool, bool, bool reqForward, bool reqCreds, bool reqMutual, int ssfMin, int, const TQString &, int)
{
if(reqForward || reqCreds || reqMutual || ssfMin > 0)
capable = false;
else
capable = true;
allow_plain = !noPlain;
}
int security() const
{
return 0;
}
int errorCond() const
{
return err;
}
bool clientStart(const TQStringList &mechlist)
{
bool haveMech = false;
resetState();
step = 0;
for(TQStringList::ConstIterator it = mechlist.begin(); it != mechlist.end(); ++it) {
if((*it) == "PLAIN" && allow_plain) {
out_mech = "PLAIN";
haveMech = true;
break;
}
if((*it) == "DIGEST-MD5") {
out_mech = "DIGEST-MD5";
haveMech = true;
break;
}
}
if(!capable || !haveMech) {
err = TQCA::SASL::NoMech;
return false;
}
return true;
}
int clientFirstStep(bool)
{
return clientTryAgain();
}
bool serverStart(const TQString &, TQStringList *, const TQString &)
{
return false;
}
int serverFirstStep(const TQString &, const TQByteArray *)
{
return Error;
}
TQCA_SASLNeedParams clientParamsNeeded() const
{
return need;
}
void setClientParams(const TQString *_user, const TQString *_authzid, const TQString *_pass, const TQString *_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;
}
}
TQString username() const
{
return TQString();
}
TQString authzid() const
{
return TQString();
}
int nextStep(const TQByteArray &in)
{
in_buf = in.copy();
return tryAgain();
}
int tryAgain()
{
return clientTryAgain();
}
TQString mech() const
{
return out_mech;
}
const TQByteArray *clientInit() const
{
return out_mech == "PLAIN" ? &out_buf : 0;
}
TQByteArray result() const
{
return out_buf;
}
int clientTryAgain()
{
if( out_mech == "PLAIN" ) {
if(step == 0) {
// First, check if we have everything
if(need.user || need.pass) {
err = -1;
return Error;
}
if(!have.user) {
need.user = true;
}
if(!have.pass) {
need.pass = true;
}
if(need.user || need.pass) {
return NeedParams;
}
TQCString authz_ = authz.utf8();
TQCString user_ = user.utf8();
TQCString pass_ = pass.utf8();
int l = 0;
out_buf.resize(authz_.length() + 1 + user_.length() + 1 + pass_.length());
memcpy(&out_buf[l], authz_.data(), authz_.length());
l += authz_.length();
out_buf[l] = '\0';
l += 1;
memcpy(&out_buf[l], user_.data(), user_.length());
l += user_.length();
out_buf[l] = '\0';
l += 1;
memcpy(&out_buf[l], pass_.data(), pass_.length());
++step;
return Continue;
}
out_buf.resize(0);
return Success;
}
if( out_mech == "DIGEST-MD5" ) {
if(step == 0) {
++step;
return Continue;
}
else if(step == 1) {
// if we still need params, then the app has failed us!
if(need.user || need.authzid || need.pass || need.realm) {
err = -1;
return Error;
}
// 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)
return NeedParams;
// get props
TQCString cs(in_buf.data(), in_buf.size()+1);
PropList in;
if(!in.fromString(cs)) {
err = TQCA::SASL::BadProto;
return Error;
}
// make a cnonce
TQByteArray a(32);
for(int n = 0; n < (int)a.size(); ++n)
a[n] = (char)(256.0*rand()/(RAND_MAX+1.0));
TQCString cnonce = Base64::arrayToString(a).latin1();
// make other variables
realm = host;
TQCString nonce = in.get("nonce");
TQCString nc = "00000001";
TQCString uri = service.utf8() + '/' + host.utf8();
TQCString qop = "auth";
// build 'response'
TQCString X = user.utf8() + ':' + realm.utf8() + ':' + pass.utf8();
TQByteArray Y = TQCA::MD5::hash(X);
TQCString tmp = TQCString(":") + nonce + ':' + cnonce + ':' + authz.utf8();
TQByteArray A1(Y.size() + tmp.length());
memcpy(A1.data(), Y.data(), Y.size());
memcpy(A1.data() + Y.size(), tmp.data(), tmp.length());
TQCString A2 = "AUTHENTICATE:" + uri;
TQCString HA1 = TQCA::MD5::hashToString(A1).latin1();
TQCString HA2 = TQCA::MD5::hashToString(A2).latin1();
TQCString KD = HA1 + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + HA2;
TQCString Z = TQCA::MD5::hashToString(KD).latin1();
// build output
PropList out;
out.set("username", user.utf8());
out.set("realm", host.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");
out.set("authzid", authz.utf8());
TQCString s = out.toString();
// done
out_buf.resize(s.length());
memcpy(out_buf.data(), s.data(), out_buf.size());
++step;
return Continue;
}
out_buf.resize(0);
return Success;
}
err = TQCA::SASL::NoMech;
return Error;
}
bool encode(const TQByteArray &a, TQByteArray *b)
{
*b = a.copy();
return true;
}
bool decode(const TQByteArray &a, TQByteArray *b)
{
*b = a.copy();
return true;
}
};
class TQCASimpleSASL : public TQCAProvider
{
public:
TQCASimpleSASL() {}
~TQCASimpleSASL() {}
void init()
{
}
int qcaVersion() const
{
return TQCA_PLUGIN_VERSION;
}
int capabilities() const
{
return TQCA::CAP_SASL;
}
void *context(int cap)
{
if(cap == TQCA::CAP_SASL)
return new SimpleSASLContext;
return 0;
}
};
TQCAProvider *createProviderSimpleSASL()
{
return (new TQCASimpleSASL);
}
}