Fix iterator
[kdenetwork.git] / kopete / protocols / jabber / libiris / iris / xmpp-core / simplesasl.cpp
blob3ab573944066598e37731268aef84387442d241c
1 /*
2 * simplesasl.cpp - Simple SASL implementation
3 * Copyright (C) 2003 Justin Karneges <justin@affinix.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "simplesasl.h"
23 #include <qhostaddress.h>
24 #include <qstringlist.h>
25 #include <q3ptrlist.h>
26 #include <QList>
27 #include <qca.h>
28 #include <Q3CString>
29 #include <stdlib.h>
30 #include <QtCrypto>
31 #include <QDebug>
33 #define SIMPLESASL_PLAIN
35 namespace XMPP {
36 struct Prop
38 Q3CString var, val;
41 class PropList : public QList<Prop>
43 public:
44 PropList() : QList<Prop>()
48 void set(const Q3CString &var, const Q3CString &val)
50 Prop p;
51 p.var = var;
52 p.val = val;
53 append(p);
56 Q3CString get(const Q3CString &var)
58 for(ConstIterator it = constBegin(); it != constEnd(); ++it) {
59 if((*it).var == var)
60 return (*it).val;
62 return Q3CString();
65 Q3CString toString() const
67 Q3CString str;
68 bool first = true;
69 for(ConstIterator it = constBegin(); it != constEnd(); ++it) {
70 if(!first)
71 str += ',';
72 if ((*it).var == "realm" || (*it).var == "nonce" || (*it).var == "username" || (*it).var == "cnonce" || (*it).var == "digest-uri" || (*it).var == "authzid")
73 str += (*it).var + "=\"" + (*it).val + '\"';
74 else
75 str += (*it).var + "=" + (*it).val;
76 first = false;
78 return str;
81 bool fromString(const QByteArray &str)
83 PropList list;
84 int at = 0;
85 while(1) {
86 while (at < str.length() && (str[at] == ',' || str[at] == ' ' || str[at] == '\t'))
87 ++at;
88 int n = str.find('=', at);
89 if(n == -1)
90 break;
91 Q3CString var, val;
92 var = str.mid(at, n-at);
93 at = n + 1;
94 if(str[at] == '\"') {
95 ++at;
96 n = str.find('\"', at);
97 if(n == -1)
98 break;
99 val = str.mid(at, n-at);
100 at = n + 1;
102 else {
103 n = at;
104 while (n < str.length() && str[n] != ',' && str[n] != ' ' && str[n] != '\t')
105 ++n;
106 val = str.mid(at, n-at);
107 at = n;
109 Prop prop;
110 prop.var = var;
111 if (var == "qop" || var == "cipher") {
112 int a = 0;
113 while (a < val.length()) {
114 while (a < val.length() && (val[a] == ',' || val[a] == ' ' || val[a] == '\t'))
115 ++a;
116 if (a == val.length())
117 break;
118 n = a+1;
119 while (n < val.length() && val[n] != ',' && val[n] != ' ' && val[n] != '\t')
120 ++n;
121 prop.val = val.mid(a, n-a);
122 list.append(prop);
123 a = n+1;
126 else {
127 prop.val = val;
128 list.append(prop);
131 if(at >= str.size() - 1 || (str[at] != ',' && str[at] != ' ' && str[at] != '\t'))
132 break;
135 // integrity check
136 if(list.varCount("nonce") != 1)
137 return false;
138 if(list.varCount("algorithm") != 1)
139 return false;
140 *this = list;
141 return true;
144 int varCount(const Q3CString &var)
146 int n = 0;
147 for(ConstIterator it = constBegin(); it != constEnd(); ++it) {
148 if((*it).var == var)
149 ++n;
151 return n;
154 QStringList getValues(const Q3CString &var)
156 QStringList list;
157 for(ConstIterator it = constBegin(); it != constEnd(); ++it) {
158 if((*it).var == var)
159 list += (*it).val;
161 return list;
165 class SimpleSASLContext : public QCA::SASLContext
167 public:
168 class ParamsMutable
170 public:
172 User is held
174 bool user;
177 Authorization ID is held
179 bool authzid;
182 Password is held
184 bool pass;
187 Realm is held
189 bool realm;
191 // core props
192 QString service, host;
194 // state
195 int step;
196 bool capable;
197 bool allow_plain;
198 QByteArray out_buf, in_buf;
199 QString mechanism_;
200 QString out_mech;
202 ParamsMutable need;
203 ParamsMutable have;
204 QString user, authz, realm;
205 QCA::SecureArray pass;
206 Result result_;
207 QCA::SASL::AuthCondition authCondition_;
208 QByteArray result_to_net_, result_to_app_;
209 int encoded_;
211 SimpleSASLContext(QCA::Provider* p) : QCA::SASLContext(p)
213 reset();
216 ~SimpleSASLContext()
218 reset();
221 void reset()
223 resetState();
225 capable = true;
226 allow_plain = false;
227 need.user = false;
228 need.authzid = false;
229 need.pass = false;
230 need.realm = false;
231 have.user = false;
232 have.authzid = false;
233 have.pass = false;
234 have.realm = false;
235 user = QString();
236 authz = QString();
237 pass = QCA::SecureArray();
238 realm = QString();
241 void resetState()
243 out_mech = QString();
244 out_buf.resize(0);
245 authCondition_ = QCA::SASL::AuthFail;
248 virtual void setConstraints(QCA::SASL::AuthFlags flags, int ssfMin, int) {
249 if(flags & (QCA::SASL::RequireForwardSecrecy | QCA::SASL::RequirePassCredentials | QCA::SASL::RequireMutualAuth) || ssfMin > 0)
250 capable = false;
251 else
252 capable = true;
253 allow_plain = flags & QCA::SASL::AllowPlain;
256 virtual void setup(const QString& _service, const QString& _host, const QCA::SASLContext::HostPort*, const QCA::SASLContext::HostPort*, const QString&, int) {
257 service = _service;
258 host = _host;
261 virtual void startClient(const QStringList &mechlist, bool allowClientSendFirst) {
262 Q_UNUSED(allowClientSendFirst);
264 mechanism_ = QString();
265 foreach(QString mech, mechlist) {
266 if (mech == "DIGEST-MD5") {
267 mechanism_ = "DIGEST-MD5";
268 break;
270 #ifdef SIMPLESASL_PLAIN
271 if (mech == "PLAIN" && allow_plain)
272 mechanism_ = "PLAIN";
273 #endif
276 if(!capable || mechanism_.isEmpty()) {
277 result_ = Error;
278 authCondition_ = QCA::SASL::NoMechanism;
279 if (!capable)
280 qWarning("simplesasl.cpp: Not enough capabilities");
281 if (mechanism_.isEmpty())
282 qWarning("simplesasl.cpp: No mechanism available");
283 QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
284 return;
287 resetState();
288 result_ = Continue;
289 step = 0;
290 tryAgain();
293 virtual void nextStep(const QByteArray &from_net) {
294 in_buf = from_net;
295 tryAgain();
298 virtual void tryAgain() {
299 // All exits of the method must emit the ready signal
300 // so all exits go through a goto ready;
301 if(step == 0) {
302 out_mech = mechanism_;
304 #ifdef SIMPLESASL_PLAIN
305 // PLAIN
306 if (out_mech == "PLAIN") {
307 // First, check if we have everything
308 if(need.user || need.pass) {
309 qWarning("simplesasl.cpp: Did not receive necessary auth parameters");
310 result_ = Error;
311 goto ready;
313 if(!have.user)
314 need.user = true;
315 if(!have.pass)
316 need.pass = true;
317 if(need.user || need.pass) {
318 result_ = Params;
319 goto ready;
322 // Continue with authentication
323 QByteArray plain;
324 if (!authz.isEmpty())
325 plain += authz.utf8();
326 plain += '\0' + user.toUtf8() + '\0' + pass.toByteArray();
327 out_buf.resize(plain.length());
328 memcpy(out_buf.data(), plain.data(), out_buf.size());
330 #endif
331 ++step;
332 if (out_mech == "PLAIN")
333 result_ = Success;
334 else
335 result_ = Continue;
337 else if(step == 1) {
338 // if we still need params, then the app has failed us!
339 if(need.user || need.authzid || need.pass || need.realm) {
340 qWarning("simplesasl.cpp: Did not receive necessary auth parameters");
341 result_ = Error;
342 goto ready;
345 // see if some params are needed
346 if(!have.user)
347 need.user = true;
348 //if(!have.authzid)
349 // need.authzid = true;
350 if(!have.pass)
351 need.pass = true;
352 if(need.user || need.authzid || need.pass) {
353 result_ = Params;
354 goto ready;
357 // get props
358 QByteArray cs(in_buf);
359 PropList in;
360 if(!in.fromString(cs)) {
361 authCondition_ = QCA::SASL::BadProtocol;
362 result_ = Error;
363 goto ready;
365 //qDebug() << (QString("simplesasl.cpp: IN: %1").arg(QString(in.toString())));
367 // make a cnonce
368 QByteArray a(32);
369 for(int n = 0; n < (int)a.size(); ++n)
370 a[n] = (char)(256.0*rand()/(RAND_MAX+1.0));
371 Q3CString cnonce = QCA::Base64().arrayToString(a).latin1();
373 // make other variables
374 if (realm.isEmpty())
375 realm = QString::fromUtf8(in.get("realm"));
376 Q3CString nonce = in.get("nonce");
377 Q3CString nc = "00000001";
378 Q3CString uri = service.utf8() + '/' + host.utf8();
379 Q3CString qop = "auth";
381 // build 'response'
382 Q3CString X = user.utf8() + ':' + realm.utf8() + ':' + Q3CString(pass.toByteArray());
383 QByteArray Y = QCA::Hash("md5").hash(X).toByteArray();
384 QByteArray tmp = ':' + nonce + ':' + cnonce;
385 if (!authz.isEmpty())
386 tmp += ':' + authz.utf8();
387 //qDebug() << (QString(tmp));
389 QByteArray A1(Y + tmp);
390 QByteArray A2 = QByteArray("AUTHENTICATE:") + uri;
391 Q3CString HA1 = QCA::Hash("md5").hashToString(A1).latin1();
392 Q3CString HA2 = QCA::Hash("md5").hashToString(A2).latin1();
393 Q3CString KD = HA1 + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + HA2;
394 Q3CString Z = QCA::Hash("md5").hashToString(KD).latin1();
396 //qDebug() << (QString("simplesasl.cpp: A1 = %1").arg(QString(A1)).toAscii());
397 //qDebug() << (QString("simplesasl.cpp: A2 = %1").arg(QString(A2)).toAscii());
398 //qDebug() << (QString("simplesasl.cpp: KD = %1").arg(QString(KD)).toAscii());
400 // build output
401 PropList out;
402 out.set("username", user.utf8());
403 if (!realm.isEmpty())
404 out.set("realm", realm.utf8());
405 out.set("nonce", nonce);
406 out.set("cnonce", cnonce);
407 out.set("nc", nc);
408 //out.set("serv-type", service.utf8());
409 //out.set("host", host.utf8());
410 out.set("digest-uri", uri);
411 out.set("qop", qop);
412 out.set("response", Z);
413 out.set("charset", "utf-8");
414 if (!authz.isEmpty())
415 out.set("authzid", authz.utf8());
416 QByteArray s(out.toString());
417 //qDebug() << (QString("OUT: %1").arg(QString(out.toString())));
419 // done
420 out_buf.resize(s.length());
421 memcpy(out_buf.data(), s.data(), out_buf.size());
422 ++step;
423 result_ = Continue;
425 /*else if (step == 2) {
426 //Commenting this out is Justin's fix for updated QCA.
427 out_buf.resize(0);
428 result_ = Continue;
429 ++step;
431 else {
432 out_buf.resize(0);
433 result_ = Success;
435 ready:
436 QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
439 virtual void update(const QByteArray &from_net, const QByteArray &from_app) {
440 result_to_app_ = from_net;
441 result_to_net_ = from_app;
442 encoded_ = from_app.size();
443 result_ = Success;
444 QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
447 virtual bool waitForResultsReady(int msecs) {
449 // TODO: for now, all operations block anyway
450 Q_UNUSED(msecs);
451 return true;
454 virtual Result result() const {
455 return result_;
458 virtual QStringList mechlist() const {
459 return QStringList();
462 virtual QString mech() const {
463 return out_mech;
466 virtual bool haveClientInit() const {
467 return out_mech == "PLAIN";
470 virtual QByteArray stepData() const {
471 return out_buf;
474 virtual QByteArray to_net() {
475 return result_to_net_;
478 virtual int encoded() const {
479 return encoded_;
482 virtual QByteArray to_app() {
483 return result_to_app_;
486 virtual int ssf() const {
487 return 0;
490 virtual QCA::SASL::AuthCondition authCondition() const {
491 return authCondition_;
494 virtual QCA::SASL::Params clientParams() const {
495 return QCA::SASL::Params(need.user, need.authzid, need.pass, need.realm);
498 virtual void setClientParams(const QString *_user, const QString *_authzid, const QCA::SecureArray *_pass, const QString *_realm) {
499 if(_user) {
500 user = *_user;
501 need.user = false;
502 have.user = true;
504 if(_authzid) {
505 authz = *_authzid;
506 need.authzid = false;
507 have.authzid = true;
509 if(_pass) {
510 pass = *_pass;
511 need.pass = false;
512 have.pass = true;
514 if(_realm) {
515 realm = *_realm;
516 need.realm = false;
517 have.realm = true;
521 virtual QStringList realmlist() const
523 // TODO
524 return QStringList();
527 virtual QString username() const {
528 return QString();
531 virtual QString authzid() const {
532 return QString();
535 virtual QCA::Provider::Context* clone() const {
536 SimpleSASLContext* s = new SimpleSASLContext(provider());
537 // TODO: Copy all the members
538 return s;
541 virtual void startServer(const QString &, bool) {
542 result_ = QCA::SASLContext::Error;
543 QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
545 virtual void serverFirstStep(const QString &, const QByteArray *) {
546 result_ = QCA::SASLContext::Error;
547 QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
552 class QCASimpleSASL : public QCA::Provider
554 public:
555 QCASimpleSASL() {}
556 ~QCASimpleSASL() {}
558 void init()
562 QString name() const {
563 return "simplesasl";
566 QStringList features() const {
567 return QStringList("sasl");
570 QCA::Provider::Context* createContext(const QString& cap)
572 if(cap == "sasl")
573 return new SimpleSASLContext(this);
574 return 0;
576 int qcaVersion() const
578 return QCA_VERSION;
582 QCA::Provider *createProviderSimpleSASL()
584 return (new QCASimpleSASL);