2
* qca-sasl.cpp - SASL plugin for QCA
3
* Copyright (C) 2003-2007 Justin Karneges <justin@affinix.com>
4
* Copyright (C) 2006 Michail Pishchagin <mblsha@gmail.com>
6
* This library is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU Lesser General Public
8
* License as published by the Free Software Foundation; either
9
* version 2.1 of the License, or (at your option) any later version.
11
* This library is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* Lesser General Public License for more details.
16
* You should have received a copy of the GNU Lesser General Public
17
* License along with this library; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24
#include <QtCore/qplugin.h>
28
#include <sasl/sasl.h>
31
#include <QHostAddress>
32
#include <QStringList>
36
#define SASL_BUFSIZE 8192
37
#define SASL_APP "qca"
41
namespace saslQCAPlugin {
43
class saslProvider : public Provider
49
int qcaVersion() const;
51
QString credit() const;
52
QStringList features() const;
53
Context *createContext(const QString &type);
60
//----------------------------------------------------------------------------
62
//----------------------------------------------------------------------------
70
bool user, authzid, pass, realm;
82
foreach(char *result, results)
103
void setUsername(const QString &s)
109
void setAuthzid(const QString &s)
115
void setPassword(const SecureArray &s)
118
pass = QString::fromUtf8(s.toByteArray());
121
void setRealm(const QString &s)
127
void applyInteract(sasl_interact_t *needp)
129
for(int n = 0; needp[n].id != SASL_CB_LIST_END; ++n) {
130
if(needp[n].id == SASL_CB_AUTHNAME)
131
need.user = true; // yes, I know these
132
if(needp[n].id == SASL_CB_USER)
133
need.authzid = true; // look backwards
134
if(needp[n].id == SASL_CB_PASS)
136
if(needp[n].id == SASL_CB_GETREALM)
141
void extractHave(sasl_interact_t *needp)
143
for(int n = 0; needp[n].id != SASL_CB_LIST_END; ++n) {
144
if(needp[n].id == SASL_CB_AUTHNAME && have.user)
145
setValue(&needp[n], user);
146
if(needp[n].id == SASL_CB_USER && have.authzid)
147
setValue(&needp[n], authzid);
148
if(needp[n].id == SASL_CB_PASS && have.pass)
149
setValue(&needp[n], pass);
150
if(needp[n].id == SASL_CB_GETREALM && have.realm)
151
setValue(&needp[n], realm);
155
bool missingAny() const
157
if((need.user && !have.user) /*|| (need.authzid && !have.authzid)*/ || (need.pass && !have.pass) /*|| (need.realm && !have.realm)*/)
162
SParams missing() const
176
void setValue(sasl_interact_t *i, const QString &s)
180
QByteArray cs = s.toUtf8();
181
int len = cs.length();
182
char *p = new char[len+1];
183
memcpy(p, cs.data(), len);
192
QList<char *> results;
195
QString user, authzid, pass, realm;
198
static QByteArray makeByteArray(const void *in, unsigned int len)
200
QByteArray buf(len, 0);
201
memcpy(buf.data(), in, len);
205
static QString addrString(const SASLContext::HostPort &hp)
207
return (hp.addr + ';' + QString::number(hp.port));
210
//----------------------------------------------------------------------------
212
//----------------------------------------------------------------------------
214
class saslContext : public SASLContext
219
QString service, host;
220
QString localAddr, remoteAddr;
224
int ssf_min, ssf_max;
229
sasl_interact_t *need;
231
sasl_callback_t *callbacks;
239
bool in_useClientInit;
240
QByteArray in_clientInit;
242
// bool out_useClientInit;
243
// QByteArray out_clientInit;
247
QString sc_username, sc_authzid;
248
bool ca_flag, ca_done, ca_skip;
252
Result result_result;
253
bool result_haveClientInit;
254
QStringList result_mechlist;
255
SASL::AuthCondition result_authCondition;
256
QByteArray result_to_net;
257
QByteArray result_plain;
278
result_authCondition = SASL::AuthFail;
279
result_haveClientInit = false;
280
result_mechlist.clear();
281
result_plain.clear();
282
result_plain.clear();
283
result_plain.clear();
299
sasl_security_properties_t secprops;
300
secprops.min_ssf = ssf_min;
301
secprops.max_ssf = ssf_max;
302
secprops.maxbufsize = SASL_BUFSIZE;
303
secprops.property_names = NULL;
304
secprops.property_values = NULL;
305
secprops.security_flags = secflags;
306
int r = sasl_setprop(con, SASL_SEC_PROPS, &secprops);
310
if(!ext_authid.isEmpty()) {
311
const char *authid = ext_authid.toLatin1().data();
312
sasl_ssf_t ssf = ext_ssf;
313
r = sasl_setprop(con, SASL_SSF_EXTERNAL, &ssf);
316
r = sasl_setprop(con, SASL_AUTH_EXTERNAL, &authid);
324
void setAuthCondition(int r)
326
//qDebug() << "authcondition: " << r;
327
SASL::AuthCondition x;
330
case SASL_NOMECH: x = SASL::NoMechanism; break;
331
case SASL_BADPROT: x = SASL::BadProtocol; break;
334
case SASL_BADSERV: x = SASL::BadServer; break;
337
case SASL_BADAUTH: x = SASL::BadAuth; break;
338
case SASL_NOAUTHZ: x = SASL::NoAuthzid; break;
339
case SASL_TOOWEAK: x = SASL::TooWeak; break;
340
case SASL_ENCRYPT: x = SASL::NeedEncrypt; break;
341
case SASL_EXPIRED: x = SASL::Expired; break;
342
case SASL_DISABLED: x = SASL::Disabled; break;
343
case SASL_NOUSER: x = SASL::NoUser; break;
344
case SASL_UNAVAIL: x = SASL::RemoteUnavailable; break;
346
default: x = SASL::AuthFail; break;
348
result_authCondition = x;
353
const void *maybe_sff;
354
if( SASL_OK == sasl_getprop( con, SASL_SSF, &maybe_sff ) )
355
result_ssf = *(const int*)maybe_sff;
357
const void *maybe_maxoutbuf;
358
if (SASL_OK == sasl_getprop( con, SASL_MAXOUTBUF, &maybe_maxoutbuf ) )
359
maxoutbuf = *(const int*)maybe_maxoutbuf;
362
static int scb_checkauth(sasl_conn_t *, void *context, const char *requested_user, unsigned, const char *auth_identity, unsigned, const char *, unsigned, struct propctx *)
364
saslContext *that = (saslContext *)context;
365
that->sc_username = auth_identity; // yeah yeah, it looks
366
that->sc_authzid = requested_user; // backwards, but it is right
367
that->ca_flag = true;
371
void clientTryAgain()
373
result_haveClientInit = false;
376
const char *clientout, *m;
377
unsigned int clientoutlen;
380
QString list = result_mechlist.join(" ");
384
params.extractHave(need);
386
r = sasl_client_start(con, list.toLatin1().data(), &need, &clientout, &clientoutlen, &m);
388
r = sasl_client_start(con, list.toLatin1().data(), &need, NULL, NULL, &m);
389
if(r != SASL_INTERACT)
392
params.applyInteract(need);
393
if(params.missingAny()) {
394
result_result = Params;
398
if(r != SASL_OK && r != SASL_CONTINUE) {
400
result_result = Error;
405
if(in_sendFirst && clientout) {
406
out_buf = makeByteArray(clientout, clientoutlen);
407
result_haveClientInit = true;
414
result_result = Success;
417
result_result = Continue;
421
const char *clientout;
422
unsigned int clientoutlen;
426
params.extractHave(need);
427
//printf("sasl_client_step(con, {%s}, %d, &need, &clientout, &clientoutlen);\n", in_buf.data(), in_buf.size());
428
r = sasl_client_step(con, in_buf.data(), in_buf.size(), &need, &clientout, &clientoutlen);
429
//printf("returned: %d\n", r);
430
if(r != SASL_INTERACT)
433
params.applyInteract(need);
434
if(params.missingAny()) {
435
result_result = Params;
439
if(r != SASL_OK && r != SASL_CONTINUE) {
441
result_result = Error;
444
out_buf = makeByteArray(clientout, clientoutlen);
447
result_result = Success;
450
result_result = Continue;
455
void serverTryAgain()
459
const char *clientin = 0;
460
unsigned int clientinlen = 0;
461
if(in_useClientInit) {
462
clientin = in_clientInit.data();
463
clientinlen = in_clientInit.size();
465
const char *serverout;
466
unsigned int serveroutlen;
468
int r = sasl_server_start(con, in_mech.toLatin1().data(), clientin, clientinlen, &serverout, &serveroutlen);
469
if(r != SASL_OK && r != SASL_CONTINUE) {
471
result_result = Error;
474
out_buf = makeByteArray(serverout, serveroutlen);
476
if(ca_flag && !ca_done) {
479
result_result = AuthCheck;
486
if(last_r == SASL_OK) {
488
result_result = Success;
491
result_result = Continue;
496
const char *serverout;
497
unsigned int serveroutlen;
498
int r = sasl_server_step(con, in_buf.data(), in_buf.size(), &serverout, &serveroutlen);
499
if(r != SASL_OK && r != SASL_CONTINUE) {
501
result_result = Error;
507
out_buf = makeByteArray(serverout, serveroutlen);
509
if(ca_flag && !ca_done) {
512
result_result = AuthCheck;
517
if(last_r == SASL_OK) {
519
result_result = Success;
522
result_result = Continue;
527
bool sasl_endecode(const QByteArray &in, QByteArray *out, bool enc)
530
if(result_ssf == 0) {
538
int size = in.size() - at;
547
r = sasl_encode(con, in.data() + at, size, &outbuf, &len);
549
r = sasl_decode(con, in.data() + at, size, &outbuf, &len);
552
int oldsize = out->size();
553
out->resize(oldsize + len);
554
memcpy(out->data() + oldsize, outbuf, len);
560
void doResultsReady()
562
QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
566
saslContext(saslProvider *_g)
569
result_result = Success;
582
virtual Provider::Context *clone() const
587
virtual Result result() const
589
return result_result;
598
virtual void setup(const QString &_service, const QString &_host, const HostPort *local, const HostPort *remote, const QString &ext_id, int _ext_ssf)
602
localAddr = local ? addrString(*local) : "";
603
remoteAddr = remote ? addrString(*remote) : "";
608
virtual int ssf() const
613
virtual void startClient(const QStringList &mechlist, bool allowClientSendFirst)
617
in_sendFirst = allowClientSendFirst;
619
if(!g->client_init) {
620
sasl_client_init(NULL);
621
g->client_init = true;
624
callbacks = new sasl_callback_t[5];
626
callbacks[0].id = SASL_CB_GETREALM;
627
callbacks[0].proc = 0;
628
callbacks[0].context = 0;
630
callbacks[1].id = SASL_CB_USER;
631
callbacks[1].proc = 0;
632
callbacks[1].context = 0;
634
callbacks[2].id = SASL_CB_AUTHNAME;
635
callbacks[2].proc = 0;
636
callbacks[2].context = 0;
638
callbacks[3].id = SASL_CB_PASS;
639
callbacks[3].proc = 0;
640
callbacks[3].context = 0;
642
callbacks[4].id = SASL_CB_LIST_END;
643
callbacks[4].proc = 0;
644
callbacks[4].context = 0;
646
result_result = Error;
648
int r = sasl_client_new(service.toLatin1().data(), host.toLatin1().data(), localAddr.isEmpty() ? 0 : localAddr.toLatin1().data(), remoteAddr.isEmpty() ? 0 : remoteAddr.toLatin1().data(), callbacks, 0, &con);
661
result_mechlist = mechlist;
664
result_result = Success;
670
// TODO: make use of disableServerSendLast
671
virtual void startServer(const QString &realm, bool disableServerSendLast)
673
Q_UNUSED(disableServerSendLast);
676
g->appname = SASL_APP;
677
if(!g->server_init) {
678
sasl_server_init(NULL, QFile::encodeName(g->appname));
679
g->server_init = true;
682
callbacks = new sasl_callback_t[2];
684
callbacks[0].id = SASL_CB_PROXY_POLICY;
685
callbacks[0].proc = (int(*)())scb_checkauth;
686
callbacks[0].context = this;
688
callbacks[1].id = SASL_CB_LIST_END;
689
callbacks[1].proc = 0;
690
callbacks[1].context = 0;
692
result_result = Error;
694
int r = sasl_server_new(service.toLatin1().data(), host.toLatin1().data(), !realm.isEmpty() ? realm.toLatin1().data() : 0, localAddr.isEmpty() ? 0 : localAddr.toLatin1().data(), remoteAddr.isEmpty() ? 0 : remoteAddr.toLatin1().data(), callbacks, 0, &con);
708
r = sasl_listmech(con, 0, 0, " ", 0, &ml, 0, 0);
711
result_mechlist = QString::fromUtf8(ml).split(' ');
717
result_result = Success;
722
virtual void serverFirstStep(const QString &mech, const QByteArray *clientInit)
726
in_useClientInit = true;
727
in_clientInit = *clientInit;
730
in_useClientInit = false;
735
virtual SASL::Params clientParams() const
737
SASLParams::SParams sparams = params.missing();
738
return SASL::Params(sparams.user, sparams.authzid, sparams.pass, sparams.realm);
741
virtual void setClientParams(const QString *user, const QString *authzid, const SecureArray *pass, const QString *realm)
744
params.setUsername(*user);
746
params.setAuthzid(*authzid);
748
params.setPassword(*pass);
750
params.setRealm(*realm);
753
virtual QString username() const
758
virtual QString authzid() const
763
virtual void nextStep(const QByteArray &from_net)
769
virtual void tryAgain()
778
virtual QString mech() const
786
virtual QStringList mechlist() const
788
return result_mechlist;
791
virtual QStringList realmlist() const
794
return QStringList();
797
virtual void setConstraints(SASL::AuthFlags f, int minSSF, int maxSSF)
800
if( !(f & SASL::AllowPlain) )
801
sf |= SASL_SEC_NOPLAINTEXT;
802
// if( !(f & SASL::AllowActiveVulnerable) ) // TODO
803
// sf |= SASL_SEC_NOACTIVE;
804
// if( !(f & SASL::AllowDictVulnerable) ) // TODO
805
// sf |= SASL_SEC_NODICTIONARY;
806
if( !(f & SASL::AllowAnonymous) )
807
sf |= SASL_SEC_NOANONYMOUS;
808
if( f & SASL::RequireForwardSecrecy )
809
sf |= SASL_SEC_FORWARD_SECRECY;
810
if( f & SASL::RequirePassCredentials )
811
sf |= SASL_SEC_PASS_CREDENTIALS;
812
if( f & SASL::RequireMutualAuth )
813
sf |= SASL_SEC_MUTUAL_AUTH;
820
virtual bool waitForResultsReady(int msecs)
822
// TODO: for now, all operations block anyway
827
virtual void update(const QByteArray &from_net, const QByteArray &from_app)
830
if(!from_app.isEmpty())
831
ok = sasl_endecode(from_app, &result_to_net, true);
833
ok = sasl_endecode(from_net, &result_plain, false);
834
result_result = ok ? Success : Error;
836
//printf("update (from_net=%d, to_net=%d, from_app=%d, to_app=%d)\n", from_net.size(), result_to_net.size(), from_app.size(), result_plain.size());
841
virtual bool haveClientInit() const
843
return result_haveClientInit;
846
virtual QByteArray stepData() const
851
virtual QByteArray to_net()
853
QByteArray a = result_to_net;
854
result_to_net.clear();
858
virtual int encoded() const
863
virtual QByteArray to_app()
865
QByteArray a = result_plain;
866
result_plain.clear();
870
virtual SASL::AuthCondition authCondition() const
872
return result_authCondition;
876
//----------------------------------------------------------------------------
878
//----------------------------------------------------------------------------
879
saslProvider::saslProvider()
885
void saslProvider::init()
889
saslProvider::~saslProvider()
891
if(client_init || server_init)
895
int saslProvider::qcaVersion() const
900
QString saslProvider::name() const
902
return "qca-cyrus-sasl";
905
QString saslProvider::credit() const
907
return QString(); // TODO
910
QStringList saslProvider::features() const
918
Provider::Context *saslProvider::createContext(const QString &type)
920
if ( type == "sasl" )
921
return new saslContext( this );
926
} // namespace saslQCAPlugin
928
using namespace saslQCAPlugin;
930
//----------------------------------------------------------------------------
932
//----------------------------------------------------------------------------
934
class saslPlugin : public QObject, public QCAPlugin
937
Q_INTERFACES(QCAPlugin)
939
virtual Provider *createProvider() { return new saslProvider; }
942
#include "qca-cyrus-sasl.moc"
944
Q_EXPORT_PLUGIN2(qca_cyrus_sasl, saslPlugin)