Fix memory leaks of bug 163865.
[accounts-sso:signon.git] / tests / saslplugintest / saslplugintest.cpp
1 /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of signon
4  *
5  * Copyright (C) 2009-2010 Nokia Corporation.
6  *
7  * Contact: Aurel Popirtac <ext-aurel.popirtac@nokia.com>
8  * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * version 2.1 as published by the Free Software Foundation.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22  * 02110-1301 USA
23  */
24
25
26 #include <QtTest/QtTest>
27 #include <sasl/sasl.h>
28
29 #include "saslplugin.h"
30 #include "sasldata.h"
31 #include "saslplugin.cpp"
32
33 #include "saslplugintest.h"
34
35 using namespace SaslPluginNS;
36
37
38 #define TEST_START qDebug("\n\n\n\n ----------------- %s ----------------\n\n",  __func__);
39
40 #define TEST_DONE  qDebug("\n\n ----------------- %s DONE ----------------\n\n",  __func__);
41
42 void SaslPluginTest::initTestCase()
43 {
44     TEST_START
45     qRegisterMetaType<SignOn::SessionData>();
46     qRegisterMetaType<AuthPluginError>();
47     TEST_DONE
48 }
49
50 void SaslPluginTest::cleanupTestCase()
51 {
52     TEST_START
53
54     sasl_done();
55     TEST_DONE
56 }
57
58 //prepare each test by creating new plugin
59 void SaslPluginTest::init()
60 {
61     m_testPlugin = new SaslPlugin();
62 }
63
64 //finnish each test by deleting plugin
65 void SaslPluginTest::cleanup()
66 {
67     delete m_testPlugin;
68     m_testPlugin=NULL;
69 }
70
71 //slot for receiving result
72 void SaslPluginTest::result(const SignOn::SessionData& data)
73 {
74     qDebug() << "got result";
75     m_response = data;
76     m_loop.exit();
77 }
78
79 //slot for receiving error
80 void SaslPluginTest::pluginError(AuthPluginError error)
81 {
82     qDebug() << "got error";
83     m_error = error;
84     m_loop.exit();
85 }
86
87 //test cases
88
89 void SaslPluginTest::testPlugin()
90 {
91     TEST_START
92
93     qDebug() << "Checking plugin integrity.";
94     QVERIFY(m_testPlugin);
95
96     TEST_DONE
97 }
98
99 void SaslPluginTest::testPluginType()
100 {
101     TEST_START
102
103     qDebug() << "Checking plugin type.";
104     QCOMPARE(m_testPlugin->type(), QString("sasl"));
105
106     TEST_DONE
107 }
108
109 void SaslPluginTest::testPluginMechanisms()
110 {
111     TEST_START
112
113     qDebug() << "Checking plugin mechanisms.";
114     QStringList mechs = m_testPlugin->mechanisms();
115     QVERIFY(!mechs.isEmpty());
116     QVERIFY(mechs.contains(QString("PLAIN")));
117     qDebug() << mechs;
118
119     TEST_DONE
120 }
121
122 void SaslPluginTest::testPluginCancel()
123 {
124     TEST_START
125     //no functionality to test
126     TEST_DONE
127 }
128
129 void SaslPluginTest::testPluginProcess()
130 {
131     TEST_START
132
133     SaslData info;
134
135     QObject::connect(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)),
136                   this,  SLOT(result(const SignOn::SessionData&)),Qt::QueuedConnection);
137     QObject::connect(m_testPlugin, SIGNAL(error(AuthPluginError)),
138                   this,  SLOT(pluginError(AuthPluginError)),Qt::QueuedConnection);
139     QTimer::singleShot(10*1000, &m_loop, SLOT(quit()));
140
141     //try without mechanism
142     m_testPlugin->process(info);
143     m_loop.exec();
144
145     QVERIFY(m_error == PLUGIN_ERROR_MECHANISM_NOT_SUPPORTED);
146
147     //try without username
148     m_testPlugin->process(info, QString("ANONYMOUS"));
149     m_loop.exec();
150     QVERIFY(m_error == PLUGIN_ERROR_MISSING_DATA);
151
152     //try with wron state
153     info.setstate(PLUGIN_STATE_CONTINUE);
154     info.setUserName(QString("test"));
155     m_testPlugin->process(info, QString("ANONYMOUS"));
156     m_loop.exec();
157     QVERIFY(m_error == PLUGIN_ERROR_INVALID_STATE);
158
159     //rest of process is tested with real authentication mechanisms
160     TEST_DONE
161 }
162
163 void SaslPluginTest::testPluginChallengePlain()
164 {
165     TEST_START
166
167     SaslData info;
168
169     QObject::connect(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)),
170                   this,  SLOT(result(const SignOn::SessionData&)),Qt::QueuedConnection);
171     QObject::connect(m_testPlugin, SIGNAL(error(AuthPluginError)),
172                   this,  SLOT(pluginError(AuthPluginError)),Qt::QueuedConnection);
173     QTimer::singleShot(10*1000, &m_loop, SLOT(quit()));
174
175     info.setUserName(QString("idmtestuser"));
176     info.setSecret(QString("abc123"));
177     info.setAuthname(QString("authn"));
178
179     //create connection to server to get initial challenge
180     SaslServer* server = new SaslServer();
181     QByteArray reply;
182     server->init(QString("PLAIN"),reply);
183
184     //give challenge to plugin
185     m_testPlugin->process(info, QString("PLAIN"));
186     m_loop.exec();
187
188     //test response here
189     SaslData result =  m_response.data<SaslData>();
190     QByteArray token=result.Response();
191     token.replace('\0',':');
192     qDebug() << token;
193     QCOMPARE(result.Response(), QByteArray("idmtestuser\0authn\0abc123",11+5+6+2));
194
195    //check that authentication server is happy about answer
196     int retval=server->step(result.Response());
197    QVERIFY(retval== SASL_NOUSER);
198
199    delete server;
200
201    TEST_DONE
202 }
203
204 void SaslPluginTest::testPluginChallengeDigestMd5()
205 {
206     TEST_START
207     SaslData info;
208
209     QObject::connect(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)),
210                   this,  SLOT(result(const SignOn::SessionData&)),Qt::QueuedConnection);
211     QObject::connect(m_testPlugin, SIGNAL(error(AuthPluginError)),
212                   this,  SLOT(pluginError(AuthPluginError)),Qt::QueuedConnection);
213     QTimer::singleShot(10*1000, &m_loop, SLOT(quit()));
214
215     info.setUserName(QString("idmtestuser"));
216     info.setSecret(QString("abc123"));
217     info.setAuthname(QString("authn"));
218     info.setRealm(QString("realm"));
219     info.setService(QByteArray("sample"));
220
221     //create connection to server to get initial challenge
222     SaslServer* server = new SaslServer();
223
224     QByteArray challenge;
225     server->init(QString("DIGEST-MD5"), challenge);
226     qDebug() <<challenge;
227     info.setChallenge(challenge);
228
229     //give challenge to plugin
230     m_testPlugin->process(info, QString("DIGEST-MD5"));
231     m_loop.exec();
232
233     //test response here
234     SaslData result = m_response.data<SaslData>();
235     qDebug() << result.Response();
236
237     qDebug("verify response");
238     int retval=server->step(result.Response());
239    QVERIFY(retval== SASL_NOUSER);
240
241     delete server;
242     TEST_DONE
243 }
244
245 void SaslPluginTest::testPluginChallengeCramMd5()
246 {
247     TEST_START
248     SaslData info;
249
250     QObject::connect(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)),
251                   this,  SLOT(result(const SignOn::SessionData&)),Qt::QueuedConnection);
252     QObject::connect(m_testPlugin, SIGNAL(error(AuthPluginError)),
253                   this,  SLOT(pluginError(AuthPluginError)),Qt::QueuedConnection);
254     QTimer::singleShot(10*1000, &m_loop, SLOT(quit()));
255
256     info.setUserName(QString("idmtestuser"));
257     info.setSecret(QString("abc123"));
258     info.setAuthname(QString("authn"));
259     info.setRealm(QString("realm"));
260     info.setService(QByteArray("sample"));
261
262     //create connection to server to get initial challenge
263     SaslServer *server = new SaslServer();
264
265     QByteArray challenge;
266     int serret = server->init(QString("CRAM-MD5"), challenge); //fails sometimes
267     if (serret!=SASL_OK)
268     {
269         QSKIP("sasl server init for CRAM-MD5 failed",SkipSingle);
270     }
271
272     qDebug() <<challenge;
273     info.setChallenge(challenge);
274
275     //give challenge to plugin
276     m_testPlugin->process(info, QString("CRAM-MD5"));
277     m_loop.exec();
278
279     //test response here
280     SaslData result =  m_response.data<SaslData>();
281     qDebug() << result.Response();
282
283     qDebug("verify response");
284     int retval=server->step(result.Response()); //fails, check server impl.
285     QVERIFY(retval== SASL_NOUSER);
286
287     delete server;
288
289     TEST_DONE
290 }
291
292 //private funcs
293
294     void SaslPluginTest::testPluginsasl_callback()
295 {
296     TEST_START
297     int ret=0;
298     const char* res;
299     unsigned len;
300     ret=m_testPlugin->sasl_callback(NULL,0,&res,&len);
301     QVERIFY(ret==SASL_BADPARAM);
302     ret=m_testPlugin->sasl_callback(m_testPlugin,0,NULL,&len);
303     QVERIFY(ret==SASL_BADPARAM);
304
305     m_testPlugin->d->m_input.setUserName(QString("user"));
306
307     ret=m_testPlugin->sasl_callback(m_testPlugin,SASL_CB_USER,&res,&len);
308     QVERIFY(ret==SASL_OK);
309     QCOMPARE(QByteArray(res),QByteArray("user"));
310     QVERIFY(len == 4);
311
312     m_testPlugin->d->m_input.setAuthname(QString("auth"));
313
314     ret=m_testPlugin->sasl_callback(m_testPlugin,SASL_CB_AUTHNAME,&res,&len);
315     QVERIFY(ret==SASL_OK);
316     QCOMPARE(QByteArray(res),QByteArray("auth"));
317     QVERIFY(len == 4);
318
319     ret=m_testPlugin->sasl_callback(m_testPlugin,SASL_CB_LANGUAGE,&res,&len);
320     QVERIFY(ret==SASL_OK);
321     QVERIFY(res == NULL);
322     QVERIFY(len == 0);
323
324     ret=m_testPlugin->sasl_callback(m_testPlugin,45643,&res,&len);
325     QVERIFY(ret==SASL_BADPARAM);
326
327     TEST_DONE
328 }
329
330 void SaslPluginTest::testPluginsasl_get_realm()
331 {
332     TEST_START
333     int ret=0;
334     const char* res;
335
336     ret=m_testPlugin->sasl_get_realm(NULL, 0, NULL, NULL);
337     QVERIFY(ret==SASL_FAIL);
338     ret=m_testPlugin->sasl_get_realm(NULL, SASL_CB_GETREALM, NULL, NULL);
339     QVERIFY(ret==SASL_BADPARAM);
340     ret=m_testPlugin->sasl_get_realm(m_testPlugin, SASL_CB_GETREALM, NULL, NULL);
341     QVERIFY(ret==SASL_BADPARAM);
342
343     ret=m_testPlugin->sasl_get_realm(m_testPlugin, SASL_CB_GETREALM, NULL, &res);
344     QVERIFY(ret==SASL_OK);
345
346     m_testPlugin->d->m_input.setRealm(QString("real"));
347
348     ret=m_testPlugin->sasl_get_realm(m_testPlugin, SASL_CB_GETREALM, NULL, &res);
349     QVERIFY(ret==SASL_OK);
350     QCOMPARE(QByteArray(res),QByteArray("real"));
351
352     TEST_DONE
353 }
354
355 void SaslPluginTest::testPluginsasl_get_secret()
356 {
357     TEST_START
358     int ret=0;
359     sasl_secret_t *secret=NULL;
360
361     ret=m_testPlugin->sasl_get_secret(m_testPlugin->d->m_conn,NULL,0, &secret);
362     QVERIFY(ret==SASL_BADPARAM);
363
364     m_testPlugin->d->m_input.setSecret(QString("password"));
365
366     ret=m_testPlugin->sasl_get_secret(m_testPlugin->d->m_conn,m_testPlugin,SASL_CB_PASS, NULL);
367     QVERIFY(ret==SASL_BADPARAM);
368     ret=m_testPlugin->sasl_get_secret(m_testPlugin->d->m_conn,m_testPlugin,SASL_CB_PASS, &secret);
369     qDebug("err: %d",ret);
370     QVERIFY(ret==SASL_OK);
371     QCOMPARE(QByteArray("password"),QByteArray((const char*)secret->data,(int)secret->len));
372
373     TEST_DONE
374 }
375
376 void SaslPluginTest::testPluginsasl_log()
377 {
378     TEST_START
379     int ret=0;
380
381     ret=m_testPlugin->sasl_log(m_testPlugin, 0, NULL);
382     QVERIFY(ret==SASL_BADPARAM);
383
384     ret=m_testPlugin->sasl_log(m_testPlugin, 0, "test debug");
385     QVERIFY(ret==SASL_OK);
386
387     TEST_DONE
388 }
389
390 void SaslPluginTest::testPluginset_callbacks()
391 {
392     TEST_START
393     m_testPlugin->set_callbacks();
394     sasl_callback_t *callback;
395     callback = m_testPlugin->d->m_callbacks;
396     QVERIFY(callback != NULL);
397     QVERIFY(callback->id == SASL_CB_LOG);
398     QVERIFY(callback->context == m_testPlugin);
399     ++callback;
400     QVERIFY(callback != NULL);
401     QVERIFY(callback->id == SASL_CB_USER);
402     QVERIFY(callback->context == m_testPlugin);
403     ++callback;
404     QVERIFY(callback != NULL);
405     QVERIFY(callback->id == SASL_CB_AUTHNAME);
406     QVERIFY(callback->context == m_testPlugin);
407     ++callback;
408     QVERIFY(callback != NULL);
409     QVERIFY(callback->id == SASL_CB_PASS);
410     QVERIFY(callback->context == m_testPlugin);
411     ++callback;
412     QVERIFY(callback != NULL);
413     QVERIFY(callback->id == SASL_CB_GETREALM);
414     QVERIFY(callback->context == m_testPlugin);
415     ++callback;
416     QVERIFY(callback != NULL);
417     QVERIFY(callback->id == SASL_CB_LIST_END);
418     QVERIFY(callback->context == NULL);
419
420     TEST_DONE
421 }
422
423 void SaslPluginTest::testPlugincheck_and_fix_parameters()
424 {
425     TEST_START
426     bool ret;
427     SaslData input;
428     ret=m_testPlugin->check_and_fix_parameters(input);
429     QVERIFY(ret==false); //m_user not defined
430     QVERIFY(input.Service().isEmpty());
431     input.setUserName(QString("user"));
432
433     ret=m_testPlugin->check_and_fix_parameters(input);
434     QVERIFY(ret); //user valid
435     QCOMPARE(input.UserName(), QString("user"));
436     QCOMPARE(input.Service(), QString("default"));
437     QCOMPARE(input.Fqdn(), QString("default"));
438     QCOMPARE(input.IpLocal(), QString("127.0.0.1"));
439     QCOMPARE(input.IpRemote(), QString("127.0.0.1"));
440
441     TEST_DONE
442 }
443
444 // sasl server implementation for response verification
445
446 int sasl_log(void *context,
447             int priority,
448             const char *message)
449     {
450         Q_UNUSED(context);
451         Q_UNUSED(priority);
452         if (! message)
453             return SASL_BADPARAM;
454
455         TRACE() << message;
456         return SASL_OK;
457     }
458
459 static sasl_callback_t callbacks[] = {
460   {
461     SASL_CB_LOG, (int(*)())(&sasl_log), NULL
462   }, {
463     SASL_CB_LIST_END, NULL, NULL
464   }
465 };
466
467 void saslfail(int why, const char *what, const char *errstr)
468 {
469   qDebug() << why << what << errstr;
470 }
471
472 #define SAMPLE_SEC_BUF_SIZE (2048)
473
474 char buf[SAMPLE_SEC_BUF_SIZE];
475
476 SaslServer::SaslServer()
477 {
478     service="sample";
479     localdomain="loc";
480     userdomain="realm";
481     iplocal="127.0.0.1";
482     ipremote="127.0.0.1";
483     searchpath=".";
484     memset(&buf, 0L, SAMPLE_SEC_BUF_SIZE);
485 }
486
487 SaslServer::~SaslServer()
488 {
489     sasl_dispose(&conn);
490     //sasl_done(); //cannot be called, as plugin also runs this
491 }
492
493 int SaslServer::init(const QString& mech, QByteArray& challenge)
494 {
495   const char *ext_authid = NULL;
496
497   /* Init defaults... */
498   memset(&secprops, 0L, sizeof(secprops));
499   secprops.maxbufsize = SAMPLE_SEC_BUF_SIZE;
500   secprops.max_ssf = UINT_MAX;
501
502  result = sasl_server_init(callbacks, "sample");
503   if (result != SASL_OK)
504     saslfail(result, "Initializing libsasl", NULL);
505
506   result = sasl_server_new(service,
507                            localdomain,
508                            userdomain,
509                            iplocal,
510                            ipremote,
511                            NULL,
512                            serverlast,
513                            &conn);
514   if (result != SASL_OK)
515     saslfail(result, "Allocating sasl connection state", NULL);
516
517   if (!mech.isEmpty()) {
518     printf("Forcing use of mechanism %s\n", mech.toAscii().constData());
519     data = strdup(mech.toAscii().constData());
520     len = (unsigned) strlen(data);
521     count = 1;
522   } else {
523     puts("Generating client mechanism list...");
524     result = sasl_listmech(conn,
525                            ext_authid,
526                            NULL,
527                            " ",
528                            NULL,
529                            &data,
530                            &len,
531                            (int*)&count);
532     if (result != SASL_OK)
533       saslfail(result, "Generating client mechanism list", NULL);
534   }
535
536   printf("Sending list of %d mechanism(s)\n", count);
537
538   if(!mech.isEmpty()) {
539       free((void *)data);
540   }
541
542  puts("Waiting for client mechanism...");
543  strcpy(buf, mech.toAscii().constData());
544  len=strlen(buf);
545
546  if (!mech.isEmpty() && strcasecmp(mech.toAscii().constData(), buf))
547  {
548      qDebug("Client chose something other than the mandatory mechanism");
549      return SASL_FAIL;
550  }
551
552  if (strlen(buf) < len) {
553      qDebug(" Hmm, there's an initial response here ");
554     data = buf + strlen(buf) + 1;
555     len = len - strlen(buf) - 1;
556   } else {
557       qDebug("no initial data");
558     data = NULL;
559     len = 0;
560   }
561   qDebug("start");
562    result = sasl_server_start(conn,
563                              buf,
564                              data,
565                              len,
566                              &data,
567                              &len);
568   if (result != SASL_OK && result != SASL_CONTINUE)
569    {
570     saslfail(result, "Starting SASL negotiation", sasl_errstring(result,NULL,NULL));
571     return result;
572 }
573   if(len) {
574       qDebug("gotl data");
575 qDebug()<< data;
576   challenge=QByteArray(data,len);
577   qDebug()<< challenge;
578 }
579   return result;
580 }
581
582 int SaslServer::step(const QByteArray &response)
583 {
584     if (data) {
585       puts("Sending response...");
586     } else {
587      qDebug("No data to send--something's wrong");
588  }
589     puts("Waiting for client reply...");
590     QByteArray resp=response;
591     resp.replace('\0',':');
592
593     qDebug()<<resp;
594     qDebug()<<response;
595     qDebug()<<response.count();
596
597     for(int i=0; i<response.count(); i++) buf[i]=(char)(response.constData()[i]);
598     len=response.count();
599     buf[len]=0;
600
601     data = NULL;
602     result = sasl_server_step(conn, buf, len,
603                               &data, &len);
604     if (result != SASL_OK && result != SASL_CONTINUE)
605     {
606       saslfail(result, "Performing SASL negotiation", sasl_errstring(result,NULL,NULL));
607       return result;
608   }
609
610   if(result == SASL_CONTINUE) {
611       qDebug("continue");
612       return result;
613   }
614
615   puts("Negotiation complete");
616
617   if(serverlast&&data) {
618       printf("might need additional send:\n");
619   }
620
621   result = sasl_getprop(conn, SASL_USERNAME, (const void **)&data);
622
623 return SASL_OK;
624 }
625
626 //end test cases
627
628 QTEST_MAIN(SaslPluginTest)