1 /*
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>
5  *
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.
10  *
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.
15  *
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
19  *
20  */
21 
22 #include <QDebug>
23 #include <QtCrypto>
24 #include <QtPlugin>
25 #include <qcaprovider.h>
26 
27 extern "C" {
28 #include <sasl/sasl.h>
29 }
30 
31 #include <QFile>
32 #include <QList>
33 #include <QStringList>
34 
35 #define SASL_BUFSIZE 8192
36 #define SASL_APP "qca"
37 
38 using namespace QCA;
39 
40 namespace saslQCAPlugin {
41 
42 class saslProvider : public Provider
43 {
44 public:
45     saslProvider();
46     void init() override;
47     ~saslProvider() override;
48     int         qcaVersion() const override;
49     QString     name() const override;
50     QString     credit() const override;
51     QStringList features() const override;
52     Context *   createContext(const QString &type) override;
53 
54     bool    client_init;
55     bool    server_init;
56     QString appname;
57 };
58 
59 //----------------------------------------------------------------------------
60 // SASLParams
61 //----------------------------------------------------------------------------
62 
63 class SASLParams
64 {
65 public:
66     class SParams
67     {
68     public:
69         bool user, authzid, pass, realm;
70     };
71 
SASLParams()72     SASLParams()
73     {
74         reset();
75     }
76 
reset()77     void reset()
78     {
79         resetNeed();
80         resetHave();
81         foreach (char *result, results)
82             delete result;
83         results.clear();
84     }
85 
resetNeed()86     void resetNeed()
87     {
88         need.user    = false;
89         need.authzid = false;
90         need.pass    = false;
91         need.realm   = false;
92     }
93 
resetHave()94     void resetHave()
95     {
96         have.user    = false;
97         have.authzid = false;
98         have.pass    = false;
99         have.realm   = false;
100     }
101 
setUsername(const QString & s)102     void setUsername(const QString &s)
103     {
104         have.user = true;
105         user      = s;
106     }
107 
setAuthzid(const QString & s)108     void setAuthzid(const QString &s)
109     {
110         have.authzid = true;
111         authzid      = s;
112     }
113 
setPassword(const SecureArray & s)114     void setPassword(const SecureArray &s)
115     {
116         have.pass = true;
117         pass      = QString::fromUtf8(s.toByteArray());
118     }
119 
setRealm(const QString & s)120     void setRealm(const QString &s)
121     {
122         have.realm = true;
123         realm      = s;
124     }
125 
applyInteract(sasl_interact_t * needp)126     void applyInteract(sasl_interact_t *needp)
127     {
128         for (int n = 0; needp[n].id != SASL_CB_LIST_END; ++n) {
129             if (needp[n].id == SASL_CB_AUTHNAME)
130                 need.user = true; // yes, I know these
131             if (needp[n].id == SASL_CB_USER)
132                 need.authzid = true; // look backwards
133             if (needp[n].id == SASL_CB_PASS)
134                 need.pass = true;
135             if (needp[n].id == SASL_CB_GETREALM)
136                 need.realm = true;
137         }
138     }
139 
extractHave(sasl_interact_t * needp)140     void extractHave(sasl_interact_t *needp)
141     {
142         for (int n = 0; needp[n].id != SASL_CB_LIST_END; ++n) {
143             if (needp[n].id == SASL_CB_AUTHNAME && have.user)
144                 setValue(&needp[n], user);
145             if (needp[n].id == SASL_CB_USER && have.authzid)
146                 setValue(&needp[n], authzid);
147             if (needp[n].id == SASL_CB_PASS && have.pass)
148                 setValue(&needp[n], pass);
149             if (needp[n].id == SASL_CB_GETREALM && have.realm)
150                 setValue(&needp[n], realm);
151         }
152     }
153 
missingAny() const154     bool missingAny() const
155     {
156         if ((need.user && !have.user) /*|| (need.authzid && !have.authzid)*/ ||
157             (need.pass && !have.pass) /*|| (need.realm && !have.realm)*/)
158             return true;
159         return false;
160     }
161 
missing() const162     SParams missing() const
163     {
164         SParams np = need;
165         if (have.user)
166             np.user = false;
167         if (have.authzid)
168             np.authzid = false;
169         if (have.pass)
170             np.pass = false;
171         if (have.realm)
172             np.realm = false;
173         return np;
174     }
175 
setValue(sasl_interact_t * i,const QString & s)176     void setValue(sasl_interact_t *i, const QString &s)
177     {
178         if (i->result)
179             return;
180         const QByteArray cs  = s.toUtf8();
181         const int        len = cs.length();
182         char *           p   = new char[len + 1];
183         memcpy(p, cs.data(), len);
184         p[len]    = 0;
185         i->result = p;
186         i->len    = len;
187 
188         // record this
189         results.append(p);
190     }
191 
192     QList<char *> results;
193     SParams       need;
194     SParams       have;
195     QString       user, authzid, pass, realm;
196 };
197 
makeByteArray(const void * in,unsigned int len)198 static QByteArray makeByteArray(const void *in, unsigned int len)
199 {
200     QByteArray buf(len, 0);
201     memcpy(buf.data(), in, len);
202     return buf;
203 }
204 
addrString(const SASLContext::HostPort & hp)205 static QString addrString(const SASLContext::HostPort &hp)
206 {
207     return (hp.addr + QLatin1Char(';') + QString::number(hp.port));
208 }
209 
210 //----------------------------------------------------------------------------
211 // saslContext
212 //----------------------------------------------------------------------------
213 
214 class saslContext : public SASLContext
215 {
216     Q_OBJECT
217     saslProvider *g;
218 
219     // core props
220     QString service, host;
221     QString localAddr, remoteAddr;
222 
223     // security props
224     int     secflags;
225     int     ssf_min, ssf_max;
226     QString ext_authid;
227     int     ext_ssf;
228 
229     sasl_conn_t *    con;
230     sasl_interact_t *need;
231     int              maxoutbuf;
232     sasl_callback_t *callbacks;
233 
234     // state
235     bool       servermode;
236     int        step;
237     bool       in_sendFirst;
238     QByteArray in_buf;
239     QString    in_mech;
240     bool       in_useClientInit;
241     QByteArray in_clientInit;
242     QString    out_mech;
243     // bool out_useClientInit;
244     // QByteArray out_clientInit;
245     QByteArray out_buf;
246 
247     SASLParams params;
248     QString    sc_username, sc_authzid;
249     bool       ca_flag, ca_done, ca_skip;
250     int        last_r;
251 
252     int                 result_ssf;
253     Result              result_result;
254     bool                result_haveClientInit;
255     QStringList         result_mechlist;
256     SASL::AuthCondition result_authCondition;
257     QByteArray          result_to_net;
258     QByteArray          result_plain;
259     int                 result_encoded;
260 
261 private:
resetState()262     void resetState()
263     {
264         if (con) {
265             sasl_dispose(&con);
266             con = nullptr;
267         }
268         need = nullptr;
269         if (callbacks) {
270             delete callbacks;
271             callbacks = nullptr;
272         }
273 
274         localAddr   = QLatin1String("");
275         remoteAddr  = QLatin1String("");
276         maxoutbuf   = 128;
277         sc_username = QLatin1String("");
278         sc_authzid  = QLatin1String("");
279 
280         result_authCondition  = SASL::AuthFail;
281         result_haveClientInit = false;
282         result_mechlist.clear();
283         result_plain.clear();
284         result_plain.clear();
285         result_plain.clear();
286         result_ssf = 0;
287     }
288 
resetParams()289     void resetParams()
290     {
291         params.reset();
292         secflags   = 0;
293         ssf_min    = 0;
294         ssf_max    = 0;
295         ext_authid = QLatin1String("");
296         ext_ssf    = 0;
297     }
298 
setsecprops()299     bool setsecprops()
300     {
301         sasl_security_properties_t secprops;
302         secprops.min_ssf         = ssf_min;
303         secprops.max_ssf         = ssf_max;
304         secprops.maxbufsize      = SASL_BUFSIZE;
305         secprops.property_names  = nullptr;
306         secprops.property_values = nullptr;
307         secprops.security_flags  = secflags;
308         int r                    = sasl_setprop(con, SASL_SEC_PROPS, &secprops);
309         if (r != SASL_OK)
310             return false;
311 
312         if (!ext_authid.isEmpty()) {
313             const QByteArray ext_authidBA = ext_authid.toLatin1();
314             const char *     authid       = ext_authidBA.data();
315             sasl_ssf_t       ssf          = ext_ssf;
316             r                             = sasl_setprop(con, SASL_SSF_EXTERNAL, &ssf);
317             if (r != SASL_OK)
318                 return false;
319             r = sasl_setprop(con, SASL_AUTH_EXTERNAL, authid);
320             if (r != SASL_OK)
321                 return false;
322         }
323 
324         return true;
325     }
326 
setAuthCondition(int r)327     void setAuthCondition(int r)
328     {
329         // qDebug() << "authcondition: " << r;
330         SASL::AuthCondition x;
331         switch (r) {
332         // common
333         case SASL_NOMECH:
334             x = SASL::NoMechanism;
335             break;
336         case SASL_BADPROT:
337             x = SASL::BadProtocol;
338             break;
339 
340         // client
341         case SASL_BADSERV:
342             x = SASL::BadServer;
343             break;
344 
345         // server
346         case SASL_BADAUTH:
347             x = SASL::BadAuth;
348             break;
349         case SASL_NOAUTHZ:
350             x = SASL::NoAuthzid;
351             break;
352         case SASL_TOOWEAK:
353             x = SASL::TooWeak;
354             break;
355         case SASL_ENCRYPT:
356             x = SASL::NeedEncrypt;
357             break;
358         case SASL_EXPIRED:
359             x = SASL::Expired;
360             break;
361         case SASL_DISABLED:
362             x = SASL::Disabled;
363             break;
364         case SASL_NOUSER:
365             x = SASL::NoUser;
366             break;
367         case SASL_UNAVAIL:
368             x = SASL::RemoteUnavailable;
369             break;
370 
371         default:
372             x = SASL::AuthFail;
373             break;
374         }
375         result_authCondition = x;
376     }
377 
getssfparams()378     void getssfparams()
379     {
380         const void *maybe_sff;
381         if (SASL_OK == sasl_getprop(con, SASL_SSF, &maybe_sff))
382             result_ssf = *(const int *)maybe_sff;
383 
384         const void *maybe_maxoutbuf;
385         if (SASL_OK == sasl_getprop(con, SASL_MAXOUTBUF, &maybe_maxoutbuf))
386             maxoutbuf = *(const int *)maybe_maxoutbuf;
387     }
388 
scb_checkauth(sasl_conn_t *,void * context,const char * requested_user,unsigned,const char * auth_identity,unsigned,const char *,unsigned,struct propctx *)389     static int scb_checkauth(sasl_conn_t *,
390                              void *      context,
391                              const char *requested_user,
392                              unsigned,
393                              const char *auth_identity,
394                              unsigned,
395                              const char *,
396                              unsigned,
397                              struct propctx *)
398     {
399         saslContext *that = (saslContext *)context;
400         that->sc_username = QString::fromLatin1(auth_identity);  // yeah yeah, it looks
401         that->sc_authzid  = QString::fromLatin1(requested_user); // backwards, but it is right
402         that->ca_flag     = true;
403         return SASL_OK;
404     }
405 
clientTryAgain()406     void clientTryAgain()
407     {
408         result_haveClientInit = false;
409 
410         if (step == 0) {
411             const char * clientout, *m;
412             unsigned int clientoutlen;
413 
414             need               = nullptr;
415             const QString list = result_mechlist.join(QStringLiteral(" "));
416             int           r;
417             while (true) {
418                 if (need)
419                     params.extractHave(need);
420                 if (in_sendFirst)
421                     r = sasl_client_start(con, list.toLatin1().data(), &need, &clientout, &clientoutlen, &m);
422                 else
423                     r = sasl_client_start(con, list.toLatin1().data(), &need, nullptr, nullptr, &m);
424                 if (r != SASL_INTERACT)
425                     break;
426 
427                 params.applyInteract(need);
428                 if (params.missingAny()) {
429                     out_mech      = QString::fromLatin1(m);
430                     result_result = Params;
431                     return;
432                 }
433             }
434             if (r != SASL_OK && r != SASL_CONTINUE) {
435                 setAuthCondition(r);
436                 result_result = Error;
437                 return;
438             }
439 
440             out_mech = QString::fromLatin1(m);
441             if (in_sendFirst && clientout) {
442                 out_buf               = makeByteArray(clientout, clientoutlen);
443                 result_haveClientInit = true;
444             }
445 
446             ++step;
447 
448             if (r == SASL_OK) {
449                 getssfparams();
450                 result_result = Success;
451                 return;
452             }
453             result_result = Continue;
454             return;
455         } else {
456             const char * clientout;
457             unsigned int clientoutlen;
458             int          r;
459             while (true) {
460                 if (need)
461                     params.extractHave(need);
462                 // printf("sasl_client_step(con, {%s}, %d, &need, &clientout, &clientoutlen);\n", in_buf.data(),
463                 // in_buf.size());
464                 r = sasl_client_step(con, in_buf.data(), in_buf.size(), &need, &clientout, &clientoutlen);
465                 // printf("returned: %d\n", r);
466                 if (r != SASL_INTERACT)
467                     break;
468 
469                 params.applyInteract(need);
470                 if (params.missingAny()) {
471                     result_result = Params;
472                     return;
473                 }
474             }
475             if (r != SASL_OK && r != SASL_CONTINUE) {
476                 setAuthCondition(r);
477                 result_result = Error;
478                 return;
479             }
480             out_buf = makeByteArray(clientout, clientoutlen);
481             if (r == SASL_OK) {
482                 getssfparams();
483                 result_result = Success;
484                 return;
485             }
486             result_result = Continue;
487             return;
488         }
489     }
490 
serverTryAgain()491     void serverTryAgain()
492     {
493         if (step == 0) {
494             if (!ca_skip) {
495                 const char * clientin    = nullptr;
496                 unsigned int clientinlen = 0;
497                 if (in_useClientInit) {
498                     clientin    = in_clientInit.data();
499                     clientinlen = in_clientInit.size();
500                 }
501                 const char * serverout;
502                 unsigned int serveroutlen;
503                 ca_flag = false;
504                 const int r =
505                     sasl_server_start(con, in_mech.toLatin1().data(), clientin, clientinlen, &serverout, &serveroutlen);
506                 if (r != SASL_OK && r != SASL_CONTINUE) {
507                     setAuthCondition(r);
508                     result_result = Error;
509                     return;
510                 }
511                 out_buf = makeByteArray(serverout, serveroutlen);
512                 last_r  = r;
513                 if (ca_flag && !ca_done) {
514                     ca_done       = true;
515                     ca_skip       = true;
516                     result_result = AuthCheck;
517                     return;
518                 }
519             }
520             ca_skip = false;
521             ++step;
522 
523             if (last_r == SASL_OK) {
524                 getssfparams();
525                 result_result = Success;
526                 return;
527             }
528             result_result = Continue;
529             return;
530         } else {
531             if (!ca_skip) {
532                 const char * serverout;
533                 unsigned int serveroutlen;
534                 const int    r = sasl_server_step(con, in_buf.data(), in_buf.size(), &serverout, &serveroutlen);
535                 if (r != SASL_OK && r != SASL_CONTINUE) {
536                     setAuthCondition(r);
537                     result_result = Error;
538                     return;
539                 }
540                 if (r == SASL_OK)
541                     out_buf.resize(0);
542                 else
543                     out_buf = makeByteArray(serverout, serveroutlen);
544                 last_r = r;
545                 if (ca_flag && !ca_done) {
546                     ca_done       = true;
547                     ca_skip       = true;
548                     result_result = AuthCheck;
549                     return;
550                 }
551             }
552             ca_skip = false;
553             if (last_r == SASL_OK) {
554                 getssfparams();
555                 result_result = Success;
556                 return;
557             }
558             result_result = Continue;
559             return;
560         }
561     }
562 
sasl_endecode(const QByteArray & in,QByteArray * out,bool enc)563     bool sasl_endecode(const QByteArray &in, QByteArray *out, bool enc)
564     {
565         // no security
566         if (result_ssf == 0) {
567             *out = in;
568             return true;
569         }
570 
571         int at = 0;
572         out->resize(0);
573         while (true) {
574             int size = in.size() - at;
575             if (size == 0)
576                 break;
577             if (size > maxoutbuf)
578                 size = maxoutbuf;
579             const char *outbuf;
580             unsigned    len;
581             int         r;
582             if (enc)
583                 r = sasl_encode(con, in.data() + at, size, &outbuf, &len);
584             else
585                 r = sasl_decode(con, in.data() + at, size, &outbuf, &len);
586             if (r != SASL_OK)
587                 return false;
588             const int oldsize = out->size();
589             out->resize(oldsize + len);
590             memcpy(out->data() + oldsize, outbuf, len);
591             at += size;
592         }
593         return true;
594     }
595 
doResultsReady()596     void doResultsReady()
597     {
598         QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
599     }
600 
601 public:
saslContext(saslProvider * _g)602     saslContext(saslProvider *_g)
603         : SASLContext(_g)
604     {
605         result_result = Success;
606         g             = _g;
607         con           = nullptr;
608         callbacks     = nullptr;
609 
610         reset();
611     }
612 
~saslContext()613     ~saslContext() override
614     {
615         reset();
616     }
617 
clone() const618     Provider::Context *clone() const override
619     {
620         return nullptr;
621     }
622 
result() const623     Result result() const override
624     {
625         return result_result;
626     }
627 
reset()628     void reset() override
629     {
630         resetState();
631         resetParams();
632     }
633 
setup(const QString & _service,const QString & _host,const HostPort * local,const HostPort * remote,const QString & ext_id,int _ext_ssf)634     void setup(const QString & _service,
635                const QString & _host,
636                const HostPort *local,
637                const HostPort *remote,
638                const QString & ext_id,
639                int             _ext_ssf) override
640     {
641         service    = _service;
642         host       = _host;
643         localAddr  = local ? addrString(*local) : QLatin1String("");
644         remoteAddr = remote ? addrString(*remote) : QLatin1String("");
645         ext_authid = ext_id;
646         ext_ssf    = _ext_ssf;
647     }
648 
ssf() const649     int ssf() const override
650     {
651         return result_ssf;
652     }
653 
startClient(const QStringList & mechlist,bool allowClientSendFirst)654     void startClient(const QStringList &mechlist, bool allowClientSendFirst) override
655     {
656         resetState();
657 
658         in_sendFirst = allowClientSendFirst;
659 
660         if (!g->client_init) {
661             sasl_client_init(nullptr);
662             g->client_init = true;
663         }
664 
665         callbacks = new sasl_callback_t[5];
666 
667         callbacks[0].id      = SASL_CB_GETREALM;
668         callbacks[0].proc    = nullptr;
669         callbacks[0].context = nullptr;
670 
671         callbacks[1].id      = SASL_CB_USER;
672         callbacks[1].proc    = nullptr;
673         callbacks[1].context = nullptr;
674 
675         callbacks[2].id      = SASL_CB_AUTHNAME;
676         callbacks[2].proc    = nullptr;
677         callbacks[2].context = nullptr;
678 
679         callbacks[3].id      = SASL_CB_PASS;
680         callbacks[3].proc    = nullptr;
681         callbacks[3].context = nullptr;
682 
683         callbacks[4].id      = SASL_CB_LIST_END;
684         callbacks[4].proc    = nullptr;
685         callbacks[4].context = nullptr;
686 
687         result_result = Error;
688 
689         const int r = sasl_client_new(service.toLatin1().data(),
690                                       host.toLatin1().data(),
691                                       localAddr.isEmpty() ? nullptr : localAddr.toLatin1().data(),
692                                       remoteAddr.isEmpty() ? nullptr : remoteAddr.toLatin1().data(),
693                                       callbacks,
694                                       0,
695                                       &con);
696         if (r != SASL_OK) {
697             setAuthCondition(r);
698             doResultsReady();
699             return;
700         }
701 
702         if (!setsecprops()) {
703             doResultsReady();
704             return;
705         }
706 
707         result_mechlist = mechlist;
708         servermode      = false;
709         step            = 0;
710         result_result   = Success;
711         clientTryAgain();
712         doResultsReady();
713         return;
714     }
715 
716     // TODO: make use of disableServerSendLast
startServer(const QString & realm,bool disableServerSendLast)717     void startServer(const QString &realm, bool disableServerSendLast) override
718     {
719         Q_UNUSED(disableServerSendLast);
720         resetState();
721 
722         g->appname = QStringLiteral(SASL_APP);
723         if (!g->server_init) {
724             sasl_server_init(nullptr, QFile::encodeName(g->appname).constData());
725             g->server_init = true;
726         }
727 
728         callbacks = new sasl_callback_t[2];
729 
730         callbacks[0].id      = SASL_CB_PROXY_POLICY;
731         callbacks[0].proc    = (int (*)())scb_checkauth;
732         callbacks[0].context = this;
733 
734         callbacks[1].id      = SASL_CB_LIST_END;
735         callbacks[1].proc    = nullptr;
736         callbacks[1].context = nullptr;
737 
738         result_result = Error;
739 
740         int r = sasl_server_new(service.toLatin1().data(),
741                                 host.toLatin1().data(),
742                                 !realm.isEmpty() ? realm.toLatin1().data() : nullptr,
743                                 localAddr.isEmpty() ? nullptr : localAddr.toLatin1().data(),
744                                 remoteAddr.isEmpty() ? nullptr : remoteAddr.toLatin1().data(),
745                                 callbacks,
746                                 0,
747                                 &con);
748         if (r != SASL_OK) {
749             setAuthCondition(r);
750             doResultsReady();
751             return;
752         }
753 
754         if (!setsecprops()) {
755             doResultsReady();
756             return;
757         }
758 
759         const char *ml;
760         r = sasl_listmech(con, nullptr, nullptr, " ", nullptr, &ml, nullptr, nullptr);
761         if (r != SASL_OK)
762             return;
763         result_mechlist = QString::fromUtf8(ml).split(QLatin1Char(' '));
764 
765         servermode    = true;
766         step          = 0;
767         ca_done       = false;
768         ca_skip       = false;
769         result_result = Success;
770         doResultsReady();
771         return;
772     }
773 
serverFirstStep(const QString & mech,const QByteArray * clientInit)774     void serverFirstStep(const QString &mech, const QByteArray *clientInit) override
775     {
776         in_mech = mech;
777         if (clientInit) {
778             in_useClientInit = true;
779             in_clientInit    = *clientInit;
780         } else
781             in_useClientInit = false;
782         serverTryAgain();
783         doResultsReady();
784     }
785 
clientParams() const786     SASL::Params clientParams() const override
787     {
788         const SASLParams::SParams sparams = params.missing();
789         return SASL::Params(sparams.user, sparams.authzid, sparams.pass, sparams.realm);
790     }
791 
792     void
setClientParams(const QString * user,const QString * authzid,const SecureArray * pass,const QString * realm)793     setClientParams(const QString *user, const QString *authzid, const SecureArray *pass, const QString *realm) override
794     {
795         if (user)
796             params.setUsername(*user);
797         if (authzid)
798             params.setAuthzid(*authzid);
799         if (pass)
800             params.setPassword(*pass);
801         if (realm)
802             params.setRealm(*realm);
803     }
804 
username() const805     QString username() const override
806     {
807         return sc_username;
808     }
809 
authzid() const810     QString authzid() const override
811     {
812         return sc_authzid;
813     }
814 
nextStep(const QByteArray & from_net)815     void nextStep(const QByteArray &from_net) override
816     {
817         in_buf = from_net;
818         tryAgain();
819     }
820 
tryAgain()821     void tryAgain() override
822     {
823         if (servermode)
824             serverTryAgain();
825         else
826             clientTryAgain();
827         doResultsReady();
828     }
829 
mech() const830     QString mech() const override
831     {
832         if (servermode)
833             return in_mech;
834         else
835             return out_mech;
836     }
837 
mechlist() const838     QStringList mechlist() const override
839     {
840         return result_mechlist;
841     }
842 
realmlist() const843     QStringList realmlist() const override
844     {
845         // TODO
846         return QStringList();
847     }
848 
setConstraints(SASL::AuthFlags f,int minSSF,int maxSSF)849     void setConstraints(SASL::AuthFlags f, int minSSF, int maxSSF) override
850     {
851         int sf = 0;
852         if (!(f & SASL::AllowPlain))
853             sf |= SASL_SEC_NOPLAINTEXT;
854         // if( !(f & SASL::AllowActiveVulnerable) ) // TODO
855         // 	sf |= SASL_SEC_NOACTIVE;
856         // if( !(f & SASL::AllowDictVulnerable) ) // TODO
857         // 	sf |= SASL_SEC_NODICTIONARY;
858         if (!(f & SASL::AllowAnonymous))
859             sf |= SASL_SEC_NOANONYMOUS;
860         if (f & SASL::RequireForwardSecrecy)
861             sf |= SASL_SEC_FORWARD_SECRECY;
862         if (f & SASL::RequirePassCredentials)
863             sf |= SASL_SEC_PASS_CREDENTIALS;
864         if (f & SASL::RequireMutualAuth)
865             sf |= SASL_SEC_MUTUAL_AUTH;
866 
867         secflags = sf;
868         ssf_min  = minSSF;
869         ssf_max  = maxSSF;
870     }
871 
waitForResultsReady(int msecs)872     bool waitForResultsReady(int msecs) override
873     {
874         // TODO: for now, all operations block anyway
875         Q_UNUSED(msecs);
876         return true;
877     }
878 
update(const QByteArray & from_net,const QByteArray & from_app)879     void update(const QByteArray &from_net, const QByteArray &from_app) override
880     {
881         bool ok = true;
882         if (!from_app.isEmpty())
883             ok = sasl_endecode(from_app, &result_to_net, true);
884         if (ok && !from_net.isEmpty())
885             ok = sasl_endecode(from_net, &result_plain, false);
886         result_result  = ok ? Success : Error;
887         result_encoded = from_app.size();
888 
889         // printf("update (from_net=%d, to_net=%d, from_app=%d, to_app=%d)\n", from_net.size(), result_to_net.size(),
890         // from_app.size(), result_plain.size());
891 
892         doResultsReady();
893     }
894 
haveClientInit() const895     bool haveClientInit() const override
896     {
897         return result_haveClientInit;
898     }
899 
stepData() const900     QByteArray stepData() const override
901     {
902         return out_buf;
903     }
904 
to_net()905     QByteArray to_net() override
906     {
907         const QByteArray a = result_to_net;
908         result_to_net.clear();
909         return a;
910     }
911 
encoded() const912     int encoded() const override
913     {
914         return result_encoded;
915     }
916 
to_app()917     QByteArray to_app() override
918     {
919         const QByteArray a = result_plain;
920         result_plain.clear();
921         return a;
922     }
923 
authCondition() const924     SASL::AuthCondition authCondition() const override
925     {
926         return result_authCondition;
927     }
928 };
929 
930 //----------------------------------------------------------------------------
931 // saslProvider
932 //----------------------------------------------------------------------------
saslProvider()933 saslProvider::saslProvider()
934 {
935     client_init = false;
936     server_init = false;
937 }
938 
init()939 void saslProvider::init()
940 {
941 }
942 
~saslProvider()943 saslProvider::~saslProvider()
944 {
945     if (client_init || server_init)
946         sasl_done();
947 }
948 
qcaVersion() const949 int saslProvider::qcaVersion() const
950 {
951     return QCA_VERSION;
952 }
953 
name() const954 QString saslProvider::name() const
955 {
956     return QStringLiteral("qca-cyrus-sasl");
957 }
958 
credit() const959 QString saslProvider::credit() const
960 {
961     return QString(); // TODO
962 }
963 
features() const964 QStringList saslProvider::features() const
965 {
966     QStringList list;
967     list += QStringLiteral("sasl");
968 
969     return list;
970 }
971 
createContext(const QString & type)972 Provider::Context *saslProvider::createContext(const QString &type)
973 {
974     if (type == QLatin1String("sasl"))
975         return new saslContext(this);
976 
977     return nullptr;
978 }
979 
980 } // namespace saslQCAPlugin
981 
982 using namespace saslQCAPlugin;
983 
984 //----------------------------------------------------------------------------
985 // saslPlugin
986 //----------------------------------------------------------------------------
987 
988 class saslPlugin : public QObject, public QCAPlugin
989 {
990     Q_OBJECT
991     Q_PLUGIN_METADATA(IID "com.affinix.qca.Plugin/1.0")
992     Q_INTERFACES(QCAPlugin)
993 public:
createProvider()994     Provider *createProvider() override
995     {
996         return new saslProvider;
997     }
998 };
999 
1000 #include "qca-cyrus-sasl.moc"
1001