defining SignonSessionCore as SignonDisposable and code optimization
[accounts-sso:vitalyrepins-signon.git] / src / signond / signondaemon.cpp
1 /*
2  * This file is part of signon
3  *
4  * Copyright (C) 2009-2010 Nokia Corporation.
5  *
6  * Contact: Aurel Popirtac <ext-aurel.popirtac@nokia.com>
7  * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * version 2.1 as published by the Free Software Foundation.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  */
23
24 #include <QtDebug>
25 #include <QDBusConnection>
26
27 #include "signondaemon.h"
28 #include "signond-common.h"
29 #include "signondaemonadaptor.h"
30 #include "signonidentity.h"
31 #include "signonauthsession.h"
32 #include "backupifadaptor.h"
33
34 #define IDENTITY_MAX_IDLE_TIME (60 * 5) // five minutes
35 #define AUTHSESSION_MAX_IDLE_TIME (60 * 5) // five minutes
36
37 #define SIGNON_RETURN_IF_CAM_UNAVAILABLE(_ret_arg_) do {                   \
38         if (m_pCAMManager && !m_pCAMManager->credentialsSystemOpened()) {  \
39             QDBusMessage errReply = message().createErrorReply(            \
40                     internalServerErrName,                                 \
41                     internalServerErrStr + QLatin1String("Could not access Signon Database.")); \
42             SIGNOND_BUS.send(errReply); \
43             return _ret_arg_;           \
44         }                               \
45     } while(0)
46
47 using namespace SignOn;
48
49 namespace SignonDaemonNS {
50
51     RequestCounter *RequestCounter::m_pInstance = NULL;
52
53     const QString internalServerErrName = SIGNOND_INTERNAL_SERVER_ERR_NAME;
54     const QString internalServerErrStr = SIGNOND_INTERNAL_SERVER_ERR_STR;
55
56     SignonDaemon::SignonDaemon(QObject *parent) : QObject(parent)
57     {
58         m_backup = false;
59         m_pCAMManager = CredentialsAccessManager::instance();
60     }
61
62     SignonDaemon::~SignonDaemon()
63     {
64         if (m_backup) exit(0);
65
66         SignonAuthSession::stopAllAuthSessions();
67         m_storedIdentities.clear();
68         m_unstoredIdentities.clear();
69
70         if (m_pCAMManager) {
71             m_pCAMManager->closeCredentialsSystem();
72             m_pCAMManager->deleteLater();
73         }
74
75         QDBusConnection sessionConnection = QDBusConnection::sessionBus();
76
77         sessionConnection.unregisterObject(SIGNOND_DAEMON_OBJECTPATH
78                                            + QLatin1String("/backup"));
79         sessionConnection.unregisterService(SIGNOND_SERVICE
80                                             + QLatin1String(".backup"));
81
82         if (m_backup == false)
83         {
84             sessionConnection.unregisterObject(SIGNOND_DAEMON_OBJECTPATH);
85             sessionConnection.unregisterService(SIGNOND_SERVICE);
86         }
87
88         delete RequestCounter::instance();
89     }
90
91     bool SignonDaemon::init(bool backup)
92     {
93         m_backup = backup;
94
95         /* backup dbus interface */
96         QDBusConnection sessionConnection = QDBusConnection::sessionBus();
97
98         if (!sessionConnection.isConnected()) {
99             QDBusError err = sessionConnection.lastError();
100             TRACE() << "Session connection cannot be established:" << err.errorString(err.type());
101             TRACE() << err.message();
102             return false;
103         }
104
105         QDBusConnection::RegisterOptions registerSessionOptions = QDBusConnection::ExportAdaptors;
106
107         (void)new BackupIfAdaptor(this);
108
109         if (!sessionConnection.registerObject(SIGNOND_DAEMON_OBJECTPATH
110                                               + QLatin1String("/backup"), this, registerSessionOptions)) {
111             TRACE() << "Object cannot be registered";
112             return false;
113         }
114
115         if (!sessionConnection.registerService(SIGNOND_SERVICE+QLatin1String(".backup"))) {
116             QDBusError err = sessionConnection.lastError();
117             TRACE() << "Service cannot be registered: " << err.errorString(err.type());
118             return false;
119         }
120
121         if (m_backup) {
122             TRACE() << "Signond initialized in backup mode.";
123             //skit rest of initialization in backup mode
124             return true;
125         }
126
127         /* DBus Service init */
128         QDBusConnection connection = SIGNOND_BUS;
129
130         if (!connection.isConnected()) {
131             QDBusError err = connection.lastError();
132             TRACE() << "Connection cannot be established:" << err.errorString(err.type());
133             TRACE() << err.message();
134             return false;
135         }
136
137         int envIdentityTimeout = qgetenv("SSO_IDENTITY_TIMEOUT").toInt();
138         m_identityTimeout = envIdentityTimeout > 0 ?
139             envIdentityTimeout : IDENTITY_MAX_IDLE_TIME;
140
141         int envAuthSessionTimeout = qgetenv("SSO_AUTHSESSION_TIMEOUT").toInt();
142         m_authSessionTimeout = envAuthSessionTimeout > 0 ?
143             envAuthSessionTimeout : AUTHSESSION_MAX_IDLE_TIME;
144
145         QDBusConnection::RegisterOptions registerOptions = QDBusConnection::ExportAllContents;
146
147         (void)new SignonDaemonAdaptor(this);
148         registerOptions = QDBusConnection::ExportAdaptors;
149
150         if (!connection.registerObject(SIGNOND_DAEMON_OBJECTPATH, this, registerOptions)) {
151             TRACE() << "Object cannot be registered";
152             return false;
153         }
154
155         if (!connection.registerService(SIGNOND_SERVICE)) {
156             QDBusError err = connection.lastError();
157             TRACE() << "Service cannot be registered: " << err.errorString(err.type());
158             return false;
159         }
160
161         /*
162             Secure storage init
163             TODO - make this callable only by external entity
164             (e.g. boot script or whichever process has the passphrase)
165         */
166         if (!initSecureStorage(QByteArray()))
167             qFatal("Signond: Cannot initialize credentials secure storage.");
168
169         TRACE() << "Signond SUCCESSFULLY initialized.";
170
171         // TODO - remove this
172         QTimer *requestCounterTimer = new QTimer(this);
173         requestCounterTimer->setInterval(500000);
174         connect(requestCounterTimer,
175                 SIGNAL(timeout()),
176                 this,
177                 SLOT(displayRequestsCount()));
178         requestCounterTimer->start();
179         //TODO - end
180
181         return true;
182     }
183
184     bool SignonDaemon::initSecureStorage(const QByteArray &lockCode)
185     {
186         m_pCAMManager = CredentialsAccessManager::instance();
187         CAMConfiguration config;
188
189         //Leaving encryption disabled for the moment.
190         config.m_useEncryption = false;
191         config.m_encryptionPassphrase = lockCode;
192
193         /* If auto start fails(SIM not present or has invalid data)
194            and this is called by DBUS retry init of secure storage.
195         */
196         if (!m_pCAMManager->credentialsSystemOpened()) {
197             if (calledFromDBus()) {
198                 TRACE() << "Initialize secure storage called from DBUS...";
199                 m_pCAMManager->finalize();
200             }
201
202             if (!m_pCAMManager->init(config)) {
203                 qCritical("Signond: Cannot set proper configuration of CAM");
204                 delete m_pCAMManager;
205                 m_pCAMManager = NULL;
206                 return false;
207             }
208
209             //If not encryption in use just init the storage here - unsecure
210             if (config.m_useEncryption == false) {
211                 if (!m_pCAMManager->openCredentialsSystem()) {
212                     qCritical("Signond: Cannot open CAM credentials system...");
213                     delete m_pCAMManager;
214                     m_pCAMManager = NULL;
215                     return false;
216                 }
217             }
218         } else {
219             TRACE() << "Secure storage already initialized...";
220             return false;
221         }
222
223         return true;
224     }
225
226     void SignonDaemon::displayRequestsCount()
227     {
228         TRACE() << "\n\n\nUnstored identities:" << m_unstoredIdentities.count()
229                 << "\nStored identities:" << m_storedIdentities.count()
230                 << "\nService requests:" << RequestCounter::instance()->serviceRequests()
231                 << "\nIdentity requests:" << RequestCounter::instance()->identityRequests()
232                 << "\n\n";
233     }
234
235     void SignonDaemon::unregisterIdentity(SignonIdentity *identity)
236     {
237         if (m_storedIdentities.contains(identity->id()))
238             m_storedIdentities.remove(identity->id());
239         else
240             m_unstoredIdentities.remove(identity->objectName());
241
242         identity->deleteLater();
243     }
244
245     void SignonDaemon::identityStored(SignonIdentity *identity)
246     {
247         if (m_unstoredIdentities.contains(identity->objectName())) {
248             m_unstoredIdentities.remove(identity->objectName());
249             m_storedIdentities.insert(identity->id(), identity);
250         }
251     }
252
253     void SignonDaemon::registerNewIdentity(QDBusObjectPath &objectPath)
254     {
255         RequestCounter::instance()->addServiceResquest();
256
257         TRACE() << "Registering new identity:";
258
259         SignonIdentity *identity = SignonIdentity::createIdentity(SIGNOND_NEW_IDENTITY, this);
260
261         if (identity == NULL) {
262             QDBusMessage errReply = message().createErrorReply(
263                     internalServerErrName,
264                     internalServerErrStr + QLatin1String("Could not create remote Identity object."));
265             SIGNOND_BUS.send(errReply);
266             return;
267         }
268
269         m_unstoredIdentities.insert(identity->objectName(), identity);
270
271         objectPath = QDBusObjectPath(identity->objectName());
272     }
273
274     void SignonDaemon::registerStoredIdentity(const quint32 id, QDBusObjectPath &objectPath, QList<QVariant> &identityData)
275     {
276         RequestCounter::instance()->addServiceResquest();
277
278         SIGNON_RETURN_IF_CAM_UNAVAILABLE();
279
280         TRACE() << "Registering identity:" << id;
281
282         //1st check if the existing identity is in cache
283         SignonIdentity *identity = m_storedIdentities.value(id, NULL);
284
285         //if not create it
286         if (identity == NULL)
287             identity = SignonIdentity::createIdentity(id, this);
288
289         if (identity == NULL)
290         {
291             QDBusMessage errReply = message().createErrorReply(
292                     internalServerErrName,
293                     internalServerErrStr + QLatin1String("Could not create remote Identity object."));
294             SIGNOND_BUS.send(errReply);
295             return;
296         }
297
298         bool ok;
299         SignonIdentityInfo info = identity->queryInfo(ok);
300
301         if (info.m_id == 0)
302         {
303             QDBusMessage errReply = message().createErrorReply(
304                                                             SIGNOND_IDENTITY_NOT_FOUND_ERR_NAME,
305                                                             SIGNOND_IDENTITY_NOT_FOUND_ERR_STR);
306             SIGNOND_BUS.send(errReply);
307             objectPath = QDBusObjectPath();
308             return;
309         }
310
311         //cache the identity as stored
312         m_storedIdentities.insert(identity->id(), identity);
313
314         identityData = info.toVariantList();
315
316         TRACE() << "DONE REGISTERING IDENTITY";
317         objectPath = QDBusObjectPath(identity->objectName());
318     }
319
320     QStringList SignonDaemon::queryMethods()
321     {
322         RequestCounter::instance()->addServiceResquest();
323
324         QDir pluginsDir(SIGNOND_PLUGINS_DIR);
325         //TODO: in the future remove the sym links comment
326         QStringList fileNames = pluginsDir.entryList(
327                 QStringList() << QLatin1String("*.so*"), QDir::Files | QDir::NoDotAndDotDot);
328
329         QStringList ret;
330         QString fileName;
331         foreach (fileName, fileNames) {
332             if (fileName.startsWith(QLatin1String("lib"))) {
333                 fileName = fileName.mid(3, fileName.indexOf(QLatin1String("plugin")) -3);
334                 if ((fileName.length() > 0) && !ret.contains(fileName))
335                     ret << fileName;
336             }
337         }
338
339         return ret;
340     }
341
342     QStringList SignonDaemon::queryMechanisms(const QString &method)
343     {
344         RequestCounter::instance()->addServiceResquest();
345         TRACE() << "\n\n\n Querying mechanisms\n\n";
346
347         QStringList mechs = SignonSessionCore::loadedPluginMethods(method);
348
349         if (mechs.size())
350             return mechs;
351
352         PluginProxy *plugin = PluginProxy::createNewPluginProxy(method);
353
354         if (!plugin) {
355             TRACE() << "Could not load plugin of type: " << method;
356             QDBusMessage errReply = message().createErrorReply(
357                     SIGNOND_METHOD_NOT_KNOWN_ERR_NAME,
358                     QString(SIGNOND_METHOD_NOT_KNOWN_ERR_STR
359                             + QLatin1String("Method %1 is not known or could not load specific configuration.")).arg(method));
360             SIGNOND_BUS.send(errReply);
361             return QStringList();
362         }
363
364         mechs = plugin->mechanisms();
365         delete plugin;
366
367         return mechs;
368     }
369
370
371     QList<QVariant> SignonDaemon::queryIdentities(const QMap<QString, QVariant> &filter)
372     {
373         RequestCounter::instance()->addServiceResquest();
374
375         SIGNON_RETURN_IF_CAM_UNAVAILABLE(QList<QVariant>());
376
377         TRACE() << "\n\n\n Querying identities\n\n";
378
379         CredentialsDB *db = m_pCAMManager->credentialsDB();
380         if (!db) {
381             qCritical() << Q_FUNC_INFO << m_pCAMManager->lastError();
382             return QList<QVariant>();
383         }
384
385         QMap<QString, QString> filterLocal;
386         QMapIterator<QString, QVariant> it(filter);
387         while (it.hasNext()) {
388             it.next();
389             filterLocal.insert(it.key(), it.value().toString());
390         }
391
392         QList<SignonIdentityInfo> credentials = db->credentials(filterLocal);
393
394         if (db->errorOccurred()) {
395             QDBusMessage errReply = message().createErrorReply(
396                     internalServerErrName,
397                     internalServerErrStr + QLatin1String("Querying database error occurred."));
398             SIGNOND_BUS.send(errReply);
399             return QList<QVariant>();
400         }
401
402         return SignonIdentityInfo::listToVariantList(credentials);
403     }
404
405     bool SignonDaemon::clear()
406     {
407         RequestCounter::instance()->addServiceResquest();
408
409         SIGNON_RETURN_IF_CAM_UNAVAILABLE(false);
410
411         TRACE() << "\n\n\n Clearing DB\n\n";
412         CredentialsDB *db = m_pCAMManager->credentialsDB();
413         if (!db) {
414             qCritical() << Q_FUNC_INFO << m_pCAMManager->lastError();
415             return false;
416         }
417
418         if (!db->clear()) {
419             QDBusMessage errReply = message().createErrorReply(
420                                                     SIGNOND_INTERNAL_SERVER_ERR_NAME,
421                                                     QString(SIGNOND_INTERNAL_SERVER_ERR_STR
422                                                             + QLatin1String("Database error occurred.")));
423             SIGNOND_BUS.send(errReply);
424             return false;
425         }
426         return true;
427     }
428
429     QString SignonDaemon::getAuthSessionObjectPath(const quint32 id, const QString type)
430     {
431         return SignonAuthSession::getAuthSessionObjectPath(id, type, this);
432     }
433
434     bool SignonDaemon::setDeviceLockCode(const QByteArray &newLockCode,
435                                          const QByteArray &oldLockCode)
436     {
437         TRACE() << "SignonDaemon::setDeviceLockCode()";
438         if (m_pCAMManager == NULL) {
439             /*
440              * Initialized CAM if it is not already so.
441              * If the old lock code is not provided, assume this is a 1st time call and
442              * attempt the formatting of the CAM secure storage with the new lock code.
443              * If the secure storage has already been formatted the initSecureStorage call
444              * will fail!
445              */
446             QByteArray lockCode = oldLockCode;
447             if (oldLockCode.isEmpty())
448                 lockCode = newLockCode;
449
450             if (!initSecureStorage(lockCode))
451                 return false;
452         }
453
454         if (!m_pCAMManager->setDeviceLockCodeKey(newLockCode, oldLockCode)) {
455             TRACE() << "Failed to set new device lock code.";
456             return false;
457         }
458         return true;
459     }
460
461     bool SignonDaemon::remoteLock(const QByteArray &lockCode)
462     {
463         Q_UNUSED(lockCode)
464         // TODO - implement this, research how to.
465         TRACE() << "remoteLock:   lockCode = " << lockCode;
466         return false;
467     }
468
469     /*
470      * backup/restore
471      * TODO move fixed strings into config
472      */
473
474     static QString &backupCopyFilename()
475     {
476         static QString name(QLatin1String("/home/user/.signon/signondb.bin"));
477         return name;
478     }
479
480     uchar SignonDaemon::backupStarts()
481     {
482         TRACE() << "backup";
483         if (!m_backup && m_pCAMManager->credentialsSystemOpened())
484         {
485             m_pCAMManager->closeCredentialsSystem();
486             if (m_pCAMManager->credentialsSystemOpened())
487             {
488                 qCritical() << "Cannot close credentials database";
489                 return 2;
490             }
491         }
492
493         //do backup copy
494         CAMConfiguration config;
495         QString source;
496         if (config.m_useEncryption)
497             source = config.m_dbFileSystemPath;
498         else
499             source = QDir::homePath() + QDir::separator() + config.m_dbName;
500
501         QDir target;
502         if (!target.mkpath(QLatin1String("/home/user/.signon/")))
503         {
504             qCritical() << "Cannot create target directory";
505             m_pCAMManager->openCredentialsSystem();
506             return 2;
507         }
508
509         if (!QFile::copy(source, backupCopyFilename()))
510         {
511             qCritical() << "Cannot copy database";
512             m_pCAMManager->openCredentialsSystem();
513             return 2;
514         }
515
516         if (!m_backup)
517         {
518             //mount file system back
519             if (!m_pCAMManager->openCredentialsSystem()) {
520                 qCritical() << "Cannot reopen database";
521                 return 2;
522             }
523         }
524         return 0;
525     }
526
527     uchar SignonDaemon::backupFinished()
528     {
529         TRACE() << "close";
530
531         QFile copy(backupCopyFilename());
532
533         if (copy.exists())
534             QFile::remove(backupCopyFilename());
535
536         if (m_backup)
537         {
538             //close daemon
539             TRACE() << "close daemon";
540             this->deleteLater();
541         }
542
543         return 0;
544      }
545
546     /*
547      * Does nothing but start-on-demand
548      * */
549     uchar SignonDaemon::restoreStarts()
550     {
551         TRACE();
552         return 0;
553     }
554
555     uchar SignonDaemon::restoreFinished()
556     {
557         TRACE() << "restore";
558         //restore requested
559         if (m_pCAMManager->credentialsSystemOpened())
560         {
561             //umount file system
562             if (!m_pCAMManager->closeCredentialsSystem())
563             {
564                 qCritical() << "database cannot be closed";
565                 return 2;
566             }
567         }
568
569         //do restore
570         //TODO add checking if encryption status has changed
571         CAMConfiguration config;
572         QString target;
573         if (config.m_useEncryption)
574             target = config.m_dbFileSystemPath;
575         else
576             target = QDir::homePath() + QDir::separator() + config.m_dbName;
577
578         if (!QFile::remove(target+QLatin1String(".bak")))
579         {
580             qCritical() << "Cannot remove backup copy of database";
581         }
582
583         if (!QFile::rename(target, target+QLatin1String(".bak")))
584         {
585             qCritical() << "Cannot make backup copy of database";
586         }
587
588         if (!QFile::rename(backupCopyFilename(), target))
589         {
590             qCritical() << "Cannot copy database";
591
592             if (!QFile::rename(target+QLatin1String(".bak"), target))
593                 qCritical() << "Cannot restore backup copy of database";
594
595             m_pCAMManager->openCredentialsSystem();
596             return 2;
597         }
598
599         //try to remove backup database
600         if (!QFile::remove(target+QLatin1String(".bak")))
601             qCritical() << "Cannot remove backup copy of database";
602
603         //TODO check database integrity
604         if (!m_backup)
605         {
606             //mount file system back
607              if (!m_pCAMManager->openCredentialsSystem())
608                  return 2;
609         }
610
611         return 0;
612     }
613
614     void SignonDaemon::listDBusInterfaces()
615     {
616         QDBusReply<QStringList> reply = SIGNOND_BUS.interface()->registeredServiceNames();
617         QStringList list = reply.value();
618
619         QString servicesList = QLatin1String("DBUS registered services: \n");
620         servicesList += list.join(QLatin1String("\n"));
621
622         TRACE() << "\n\n" << servicesList.toAscii().data() << "\n";
623     }
624
625 } //namespace SignonDaemonNS