/* * 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 #include #include #include #include #include #include "base64.h" namespace XMPP { struct Prop { TQCString var, val; }; class PropList : public TQValueList { public: PropList() : TQValueList() { } 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); } }