fixing backup path
[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 extern "C" {
25     #include <sys/socket.h>
26     #include <sys/stat.h>
27     #include <sys/types.h>
28 }
29
30 #include <QtDebug>
31 #include <QDir>
32 #include <QDBusConnection>
33 #include <QDBusMessage>
34 #include <QPluginLoader>
35 #include <QProcessEnvironment>
36 #include <QSocketNotifier>
37
38 #include "SignOn/misc.h"
39
40 #include "signondaemon.h"
41 #include "signond-common.h"
42 #include "signontrace.h"
43 #include "signondaemonadaptor.h"
44 #include "signonidentity.h"
45 #include "signonauthsession.h"
46 #include "accesscontrolmanager.h"
47 #include "backupifadaptor.h"
48
49 #define SIGNON_RETURN_IF_CAM_UNAVAILABLE(_ret_arg_) do {                   \
50         if (m_pCAMManager && !m_pCAMManager->credentialsSystemOpened()) {  \
51             QDBusMessage errReply = message().createErrorReply(            \
52                     internalServerErrName,                                 \
53                     internalServerErrStr + QLatin1String("Could not access Signon Database.")); \
54             SIGNOND_BUS.send(errReply); \
55             return _ret_arg_;           \
56         }                               \
57     } while(0)
58
59 #define BACKUP_DIR_NAME() \
60     (QDir::separator() + QLatin1String("backup"))
61
62 using namespace SignOn;
63
64 namespace SignonDaemonNS {
65
66 /* ---------------------- SignonDaemonConfiguration ---------------------- */
67
68 SignonDaemonConfiguration::SignonDaemonConfiguration()
69     : m_loadedFromFile(false),
70       m_camConfiguration(),
71       m_identityTimeout(300),//secs
72       m_authSessionTimeout(300)//secs
73 {}
74
75 SignonDaemonConfiguration::~SignonDaemonConfiguration()
76 {
77     TRACE();
78 }
79
80 /*
81     --- Configuration file template ---
82
83     [General]
84     UseSecureStorage=yes
85     StoragePath=~/.signon/
86     ;0 - fatal, 1 - critical(default), 2 - info/debug
87     LoggingLevel=1
88
89     [SecureStorage]
90     FileSystemName=signonfs
91     Size=8
92     FileSystemType=ext2
93
94     [AegisFS]
95     AegisPath=~/.signon/private
96
97     [ObjectTimeouts]
98     IdentityTimeout=300
99     AuthSessionTimeout=300
100  */
101 void SignonDaemonConfiguration::load()
102 {
103     //Daemon configuration file
104
105     if (QFile::exists(QLatin1String("/etc/signond.conf"))) {
106         m_loadedFromFile = true;
107
108         QSettings settings(QLatin1String("/etc/signond.conf"),
109                            QSettings::NativeFormat);
110
111         int loggingLevel =
112             settings.value(QLatin1String("LoggingLevel"), 1).toInt();
113         setLoggingLevel(loggingLevel);
114
115         QString storagePath =
116             QDir(settings.value(QLatin1String("StoragePath")).toString()).path();
117         if (storagePath.startsWith(QLatin1Char('~')))
118             storagePath.replace(0, 1, QDir::homePath());
119         m_camConfiguration.m_storagePath = storagePath;
120
121         //Secure storage
122         QString useSecureStorage =
123             settings.value(QLatin1String("UseSecureStorage")).toString();
124
125         if (!useSecureStorage.isEmpty())
126             m_camConfiguration.m_useEncryption =
127                 (useSecureStorage == QLatin1String("yes")
128                 || useSecureStorage == QLatin1String("true"));
129
130         if (m_camConfiguration.m_useEncryption) {
131             settings.beginGroup(QLatin1String("SecureStorage"));
132
133             bool isOk = false;
134             quint32 storageSize =
135                 settings.value(QLatin1String("Size")).toUInt(&isOk);
136             if (!isOk || storageSize < signonMinumumDbSize) {
137                 storageSize = signonMinumumDbSize;
138                 TRACE() << "Less than minimum possible storage size configured."
139                         << "Setting to the minimum of:" << signonMinumumDbSize << "Mb";
140             }
141             m_camConfiguration.m_fileSystemSize = storageSize;
142
143             m_camConfiguration.m_fileSystemType = settings.value(
144                 QLatin1String("FileSystemType")).toString();
145
146             settings.endGroup();
147         }
148
149         //AegisFS
150         settings.beginGroup(QLatin1String("AegisFS"));
151
152         QString aegisPath =
153             QDir(settings.value(QLatin1String("AegisPath")).toString()).path();
154         if (aegisPath.startsWith(QLatin1Char('~')))
155             aegisPath.replace(0, 1, QDir::homePath());
156         m_camConfiguration.m_aegisPath = aegisPath;
157
158         settings.endGroup();
159
160         //Timeouts
161         settings.beginGroup(QLatin1String("ObjectTimeouts"));
162
163         bool isOk = false;
164         uint aux = settings.value(QLatin1String("Identity")).toUInt(&isOk);
165         if (isOk)
166             m_identityTimeout = aux;
167
168         aux = settings.value(QLatin1String("AuthSession")).toUInt(&isOk);
169         if (isOk)
170             m_authSessionTimeout = aux;
171
172         settings.endGroup();
173
174     } else {
175         TRACE() << "/etc/signond.conf not found. Using default daemon configuration.";
176     }
177
178     m_camConfiguration.m_encryptedStoragePath = m_camConfiguration.m_storagePath
179                                                 + QDir::separator()
180                                                 + QLatin1String(signonDefaultFileSystemName)
181                                                 + QLatin1String("-mnt")
182                                                 + QDir::separator();
183
184     //Environment variables
185
186     QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
187     int value = 0;
188     bool isOk = false;
189     if (environment.contains(QLatin1String("SSO_IDENTITY_TIMEOUT"))) {
190         value = environment.value(
191             QLatin1String("SSO_IDENTITY_TIMEOUT")).toInt(&isOk);
192
193         m_identityTimeout = (value > 0) && isOk ? value : m_identityTimeout;
194     }
195
196     if (environment.contains(QLatin1String("SSO_AUTHSESSION_TIMEOUT"))) {
197         value = environment.value(
198             QLatin1String("SSO_AUTHSESSION_TIMEOUT")).toInt(&isOk);
199         m_authSessionTimeout = (value > 0) && isOk ? value : m_authSessionTimeout;
200     }
201 }
202
203 /* ---------------------- SignonDaemon ---------------------- */
204
205 const QString internalServerErrName = SIGNOND_INTERNAL_SERVER_ERR_NAME;
206 const QString internalServerErrStr = SIGNOND_INTERNAL_SERVER_ERR_STR;
207
208 static int sigFd[2];
209
210 SignonDaemon *SignonDaemon::m_instance = NULL;
211
212 SignonDaemon::SignonDaemon(QObject *parent) : QObject(parent)
213                                             , m_configuration(NULL)
214 {
215     // Files created by signond must be unreadable by "other"
216     umask(S_IROTH | S_IWOTH);
217 }
218
219 SignonDaemon::~SignonDaemon()
220 {
221     ::close(sigFd[0]);
222     ::close(sigFd[1]);
223
224     if (m_backup) {
225         exit(0);
226     }
227
228     SignonAuthSession::stopAllAuthSessions();
229     m_storedIdentities.clear();
230     m_unstoredIdentities.clear();
231
232     if (m_pCAMManager) {
233         m_pCAMManager->closeCredentialsSystem();
234         delete m_pCAMManager;
235     }
236
237     QDBusConnection sessionConnection = QDBusConnection::sessionBus();
238
239     sessionConnection.unregisterObject(SIGNOND_DAEMON_OBJECTPATH
240                                        + QLatin1String("/Backup"));
241     sessionConnection.unregisterService(SIGNOND_SERVICE
242                                         + QLatin1String(".Backup"));
243     if (m_backup == false)
244     {
245         sessionConnection.unregisterObject(SIGNOND_DAEMON_OBJECTPATH);
246         sessionConnection.unregisterService(SIGNOND_SERVICE);
247     }
248
249     delete m_configuration;
250
251     BLAME() << "signond stopped.";
252 }
253
254 void SignonDaemon::setupSignalHandlers()
255 {
256     if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigFd) != 0)
257         BLAME() << "Couldn't create HUP socketpair";
258
259     m_sigSn = new QSocketNotifier(sigFd[1], QSocketNotifier::Read, this);
260     connect(m_sigSn, SIGNAL(activated(int)),
261             this, SLOT(handleUnixSignal()));
262 }
263
264 void SignonDaemon::signalHandler(int signal)
265 {
266     ::write(sigFd[0], &signal, sizeof(signal));
267 }
268
269 void SignonDaemon::handleUnixSignal()
270 {
271     m_sigSn->setEnabled(false);
272
273     int signal;
274     ::read(sigFd[1], &signal, sizeof(signal));
275
276     TRACE() << "signal received: " << signal;
277
278     switch (signal) {
279         case SIGHUP: {
280             TRACE() << "\n\n SIGHUP \n\n";
281             //todo restart daemon
282             deleteLater();
283
284             // reset the m_instance
285             m_instance = NULL;
286             QMetaObject::invokeMethod(instance(),
287                                       "init",
288                                       Qt::QueuedConnection);
289             break;
290         }
291         case SIGTERM: {
292             TRACE() << "\n\n SIGTERM \n\n";
293             //gently stop daemon
294             deleteLater();
295             QMetaObject::invokeMethod(QCoreApplication::instance(),
296                                       "quit",
297                                       Qt::QueuedConnection);
298             break;
299         }
300         case SIGINT:  {
301             TRACE() << "\n\n SIGINT \n\n";
302             //gently stop daemon
303             deleteLater();
304             QMetaObject::invokeMethod(QCoreApplication::instance(),
305                                       "quit",
306                                       Qt::QueuedConnection);
307             break;
308         }
309         default: break;
310     }
311
312     m_sigSn->setEnabled(true);
313 }
314
315 SignonDaemon *SignonDaemon::instance()
316 {
317     if (m_instance != NULL)
318         return m_instance;
319
320     QCoreApplication *app = QCoreApplication::instance();
321
322     if (!app)
323         qFatal("SignonDaemon requires a QCoreApplication instance to be constructed first");
324
325     TRACE() << "Creating new daemon instance.";
326     m_instance = new SignonDaemon(app);
327     return m_instance;
328 }
329
330 void SignonDaemon::init()
331 {
332     if (!(m_configuration = new SignonDaemonConfiguration))
333         qWarning("SignonDaemon could not create the configuration object.");
334
335     m_configuration->load();
336
337     SIGNOND_INITIALIZE_TRACE()
338     BLAME() << "signond started.";
339 #ifdef SIGNON_AEGISFS
340     TRACE() << "aegisfs enabled.";
341 #endif
342
343     if (getuid() != 0) {
344         BLAME() << "Failed to SUID root. Secure storage will not be available.";
345     }
346
347     QCoreApplication *app = QCoreApplication::instance();
348     if (!app)
349         qFatal("SignonDaemon requires a QCoreApplication instance to be constructed first");
350
351     setupSignalHandlers();
352     m_backup = app->arguments().contains(QLatin1String("-backup"));
353     m_pCAMManager = CredentialsAccessManager::instance();
354
355     /* backup dbus interface */
356     QDBusConnection sessionConnection = QDBusConnection::sessionBus();
357
358     if (!sessionConnection.isConnected()) {
359         QDBusError err = sessionConnection.lastError();
360         TRACE() << "Session connection cannot be established:" << err.errorString(err.type());
361         TRACE() << err.message();
362
363         qFatal("SignonDaemon requires session bus to start working");
364     }
365
366     QDBusConnection::RegisterOptions registerSessionOptions = QDBusConnection::ExportAdaptors;
367
368     (void)new BackupIfAdaptor(this);
369
370     if (!sessionConnection.registerObject(SIGNOND_DAEMON_OBJECTPATH
371                                           + QLatin1String("/Backup"), this, registerSessionOptions)) {
372         TRACE() << "Object cannot be registered";
373
374         qFatal("SignonDaemon requires to register backup object");
375     }
376
377     if (!sessionConnection.registerService(SIGNOND_SERVICE+QLatin1String(".Backup"))) {
378         QDBusError err = sessionConnection.lastError();
379         TRACE() << "Service cannot be registered: " << err.errorString(err.type());
380
381         qFatal("SignonDaemon requires to register backup service");
382     }
383
384     if (m_backup) {
385         TRACE() << "Signond initialized in backup mode.";
386         //skip rest of initialization in backup mode
387         return;
388     }
389
390     /* DBus Service init */
391     QDBusConnection connection = SIGNOND_BUS;
392
393     if (!connection.isConnected()) {
394         QDBusError err = connection.lastError();
395         TRACE() << "Connection cannot be established:" << err.errorString(err.type());
396         TRACE() << err.message();
397
398         qFatal("SignonDaemon requires DBus to start working");
399     }
400
401     QDBusConnection::RegisterOptions registerOptions = QDBusConnection::ExportAllContents;
402
403     (void)new SignonDaemonAdaptor(this);
404     registerOptions = QDBusConnection::ExportAdaptors;
405
406     if (!connection.registerObject(SIGNOND_DAEMON_OBJECTPATH, this, registerOptions)) {
407         TRACE() << "Object cannot be registered";
408
409         qFatal("SignonDaemon requires to register daemon's object");
410     }
411
412     if (!connection.registerService(SIGNOND_SERVICE)) {
413         QDBusError err = connection.lastError();
414         TRACE() << "Service cannot be registered: " << err.errorString(err.type());
415
416         qFatal("SignonDaemon requires to register daemon's service");
417     }
418
419     // handle D-Bus disconnection
420     connection.connect(QString(),
421                        QLatin1String("/org/freedesktop/DBus/Local"),
422                        QLatin1String("org.freedesktop.DBus.Local"),
423                        QLatin1String("Disconnected"),
424                        this, SLOT(onDisconnected()));
425
426     initExtensions();
427
428     if (!initStorage())
429         BLAME() << "Signond: Cannot initialize credentials storage.";
430
431     Q_UNUSED(AuthCoreCache::instance(this));
432
433     TRACE() << "Signond SUCCESSFULLY initialized.";
434 }
435
436 void SignonDaemon::initExtensions()
437 {
438     /* Scan the directory containing signond extensions and attempt loading
439      * all of them.
440      */
441     QDir dir(QString::fromLatin1(SIGNON_EXTENSIONS_DIR));
442     QStringList filters(QLatin1String("lib*.so"));
443     QStringList extensionList = dir.entryList(filters, QDir::Files);
444     foreach(QString filename, extensionList)
445         initExtension(dir.filePath(filename));
446 }
447
448 void SignonDaemon::initExtension(const QString &filePath)
449 {
450     TRACE() << "Loading plugin " << filePath;
451
452     QPluginLoader pluginLoader(filePath);
453     QObject *plugin = pluginLoader.instance();
454     if (plugin == 0) {
455         qWarning() << "Couldn't load plugin:" << pluginLoader.errorString();
456         return;
457     }
458
459     /* Check whether the extension implements some useful objects; if not,
460      * unload it. */
461     bool extensionInUse = false;
462     if (m_pCAMManager->initExtension(plugin))
463         extensionInUse = true;
464
465     if (!extensionInUse) {
466         pluginLoader.unload();
467     }
468 }
469
470 bool SignonDaemon::initStorage()
471 {
472     if (!m_pCAMManager->credentialsSystemOpened()) {
473         m_pCAMManager->finalize();
474
475         if (!m_pCAMManager->init(m_configuration->camConfiguration())) {
476             qCritical("Signond: Cannot set proper configuration of CAM");
477             return false;
478         }
479
480         // If encryption is in use this will just open the metadata DB
481         if (!m_pCAMManager->openCredentialsSystem()) {
482             qCritical("Signond: Cannot open CAM credentials system...");
483             return false;
484         }
485     } else {
486         TRACE() << "Secure storage already initialized...";
487         return false;
488     }
489
490     return true;
491 }
492
493 void SignonDaemon::unregisterIdentity(SignonIdentity *identity)
494 {
495     if (m_storedIdentities.contains(identity->id()))
496         m_storedIdentities.remove(identity->id());
497     else
498         m_unstoredIdentities.remove(identity->objectName());
499
500     identity->deleteLater();
501 }
502
503 void SignonDaemon::identityStored(SignonIdentity *identity)
504 {
505     if (m_unstoredIdentities.contains(identity->objectName())) {
506         m_unstoredIdentities.remove(identity->objectName());
507         m_storedIdentities.insert(identity->id(), identity);
508     }
509 }
510
511 void SignonDaemon::registerNewIdentity(QDBusObjectPath &objectPath)
512 {
513     TRACE() << "Registering new identity:";
514
515     SignonIdentity *identity = SignonIdentity::createIdentity(SIGNOND_NEW_IDENTITY, this);
516
517     if (identity == NULL) {
518         QDBusMessage errReply = message().createErrorReply(
519                 internalServerErrName,
520                 internalServerErrStr + QLatin1String("Could not create remote Identity object."));
521         SIGNOND_BUS.send(errReply);
522         return;
523     }
524
525     m_unstoredIdentities.insert(identity->objectName(), identity);
526
527     objectPath = QDBusObjectPath(identity->objectName());
528 }
529
530 int SignonDaemon::identityTimeout() const
531 {
532     return (m_configuration == NULL ?
533                                      300 :
534                                      m_configuration->identityTimeout());
535 }
536
537 int SignonDaemon::authSessionTimeout() const
538 {
539     return (m_configuration == NULL ?
540                                      300 :
541                                      m_configuration->authSessionTimeout());
542 }
543
544 void SignonDaemon::registerStoredIdentity(const quint32 id, QDBusObjectPath &objectPath, QList<QVariant> &identityData)
545 {
546     SIGNON_RETURN_IF_CAM_UNAVAILABLE();
547
548     TRACE() << "Registering identity:" << id;
549
550     //1st check if the existing identity is in cache
551     SignonIdentity *identity = m_storedIdentities.value(id, NULL);
552
553     //if not create it
554     if (identity == NULL)
555         identity = SignonIdentity::createIdentity(id, this);
556
557     if (identity == NULL)
558     {
559         QDBusMessage errReply = message().createErrorReply(
560                 internalServerErrName,
561                 internalServerErrStr + QLatin1String("Could not create remote Identity object."));
562         SIGNOND_BUS.send(errReply);
563         return;
564     }
565
566     bool ok;
567     SignonIdentityInfo info = identity->queryInfo(ok, false);
568
569     if (info.isNew())
570     {
571         QDBusMessage errReply = message().createErrorReply(
572                                                         SIGNOND_IDENTITY_NOT_FOUND_ERR_NAME,
573                                                         SIGNOND_IDENTITY_NOT_FOUND_ERR_STR);
574         SIGNOND_BUS.send(errReply);
575         objectPath = QDBusObjectPath();
576         return;
577     }
578
579     //cache the identity as stored
580     m_storedIdentities.insert(identity->id(), identity);
581     identity->keepInUse();
582
583     identityData = info.toVariantList();
584
585     TRACE() << "DONE REGISTERING IDENTITY";
586     objectPath = QDBusObjectPath(identity->objectName());
587 }
588
589 QStringList SignonDaemon::queryMethods()
590 {
591     QDir pluginsDir(SIGNOND_PLUGINS_DIR);
592     //TODO: in the future remove the sym links comment
593     QStringList fileNames = pluginsDir.entryList(
594             QStringList() << QLatin1String("*.so*"), QDir::Files | QDir::NoDotAndDotDot);
595
596     QStringList ret;
597     QString fileName;
598     foreach (fileName, fileNames) {
599         if (fileName.startsWith(QLatin1String("lib"))) {
600             fileName = fileName.mid(3, fileName.indexOf(QLatin1String("plugin")) -3);
601             if ((fileName.length() > 0) && !ret.contains(fileName))
602                 ret << fileName;
603         }
604     }
605
606     return ret;
607 }
608
609 QStringList SignonDaemon::queryMechanisms(const QString &method)
610 {
611     TRACE() << "\n\n\n Querying mechanisms\n\n";
612
613     QStringList mechs = SignonSessionCore::loadedPluginMethods(method);
614
615     if (mechs.size())
616         return mechs;
617
618     PluginProxy *plugin = PluginProxy::createNewPluginProxy(method);
619
620     if (!plugin) {
621         TRACE() << "Could not load plugin of type: " << method;
622         QDBusMessage errReply = message().createErrorReply(
623                 SIGNOND_METHOD_NOT_KNOWN_ERR_NAME,
624                 QString(SIGNOND_METHOD_NOT_KNOWN_ERR_STR
625                         + QLatin1String("Method %1 is not known or could not load specific configuration.")).arg(method));
626         SIGNOND_BUS.send(errReply);
627         return QStringList();
628     }
629
630     mechs = plugin->mechanisms();
631     delete plugin;
632
633     return mechs;
634 }
635
636
637 QList<QVariant> SignonDaemon::queryIdentities(const QMap<QString, QVariant> &filter)
638 {
639     SIGNON_RETURN_IF_CAM_UNAVAILABLE(QList<QVariant>());
640
641     TRACE() << "\n\n\n Querying identities\n\n";
642
643     CredentialsDB *db = m_pCAMManager->credentialsDB();
644     if (!db) {
645         qCritical() << Q_FUNC_INFO << m_pCAMManager->lastError();
646         return QList<QVariant>();
647     }
648
649     QMap<QString, QString> filterLocal;
650     QMapIterator<QString, QVariant> it(filter);
651     while (it.hasNext()) {
652         it.next();
653         filterLocal.insert(it.key(), it.value().toString());
654     }
655
656     QList<SignonIdentityInfo> credentials = db->credentials(filterLocal);
657
658     if (db->errorOccurred()) {
659         QDBusMessage errReply = message().createErrorReply(
660                 internalServerErrName,
661                 internalServerErrStr + QLatin1String("Querying database error occurred."));
662         SIGNOND_BUS.send(errReply);
663         return QList<QVariant>();
664     }
665
666     return SignonIdentityInfo::listToVariantList(credentials);
667 }
668
669 bool SignonDaemon::clear()
670 {
671     SIGNON_RETURN_IF_CAM_UNAVAILABLE(false);
672
673     TRACE() << "\n\n\n Clearing DB\n\n";
674     CredentialsDB *db = m_pCAMManager->credentialsDB();
675     if (!db) {
676         qCritical() << Q_FUNC_INFO << m_pCAMManager->lastError();
677         return false;
678     }
679
680     if (!db->clear()) {
681         QDBusMessage errReply = message().createErrorReply(
682                                                 SIGNOND_INTERNAL_SERVER_ERR_NAME,
683                                                 QString(SIGNOND_INTERNAL_SERVER_ERR_STR
684                                                         + QLatin1String("Database error occurred.")));
685         SIGNOND_BUS.send(errReply);
686         return false;
687     }
688     return true;
689 }
690
691 QString SignonDaemon::getAuthSessionObjectPath(const quint32 id, const QString type)
692 {
693     bool supportsAuthMethod = false;
694     pid_t ownerPid = AccessControlManager::pidOfPeer(*this);
695     QString objectPath =
696         SignonAuthSession::getAuthSessionObjectPath(id, type, this,
697                                                     supportsAuthMethod,
698                                                     ownerPid);
699     if (objectPath.isEmpty() && !supportsAuthMethod) {
700         QDBusMessage errReply = message().createErrorReply(
701                                                 SIGNOND_METHOD_NOT_KNOWN_ERR_NAME,
702                                                 SIGNOND_METHOD_NOT_KNOWN_ERR_STR);
703         SIGNOND_BUS.send(errReply);
704         return QString();
705     }
706     return objectPath;
707 }
708
709 void SignonDaemon::eraseBackupDir() const
710 {
711     const CAMConfiguration config = m_configuration->camConfiguration();
712     QString backupRoot = config.m_storagePath + BACKUP_DIR_NAME();
713
714     QDir target(backupRoot);
715     if (!target.exists()) return;
716
717     QStringList targetEntries = target.entryList(QDir::Files);
718     foreach (QString entry, targetEntries) {
719         target.remove(entry);
720     }
721
722     target.rmdir(backupRoot);
723 }
724
725 bool SignonDaemon::copyToBackupDir(const QStringList &fileNames) const
726 {
727     const CAMConfiguration config = m_configuration->camConfiguration();
728     QString backupRoot = config.m_storagePath + BACKUP_DIR_NAME();
729
730     QDir target(backupRoot);
731     if (!target.exists() && !target.mkpath(backupRoot)) {
732         qCritical() << "Cannot create target directory";
733         return false;
734     }
735
736     setUserOwnership(backupRoot);
737
738     /* Now copy the files to be backed up */
739     bool ok = true;
740     foreach (QString fileName, fileNames) {
741         /* Remove the target file, if it exists */
742         if (target.exists(fileName))
743             target.remove(fileName);
744
745         /* Copy the source into the target directory */
746         QString source = config.m_storagePath + QDir::separator() + fileName;
747         if (!QFile::exists(source)) continue;
748
749         QString destination = backupRoot + QDir::separator() + fileName;
750         ok = QFile::copy(source, destination);
751         if (!ok) {
752             BLAME() << "Copying" << source << "to" << destination << "failed";
753             break;
754         }
755
756         setUserOwnership(destination);
757     }
758
759     return ok;
760 }
761
762 bool SignonDaemon::copyFromBackupDir(const QStringList &fileNames) const
763 {
764     const CAMConfiguration config = m_configuration->camConfiguration();
765     QString backupRoot = config.m_storagePath + BACKUP_DIR_NAME();
766
767     QDir sourceDir(backupRoot);
768     if (!sourceDir.exists()) {
769         TRACE() << "Backup directory does not exist!";
770     }
771
772     if (!sourceDir.exists(config.m_dbName)) {
773         TRACE() << "Backup does not contain DB:" << config.m_dbName;
774     }
775
776     /* Now restore the files from the backup */
777     bool ok = true;
778     QDir target(config.m_storagePath);
779     QStringList movedFiles, copiedFiles;
780     foreach (QString fileName, fileNames) {
781         /* Remove the target file, if it exists */
782         if (target.exists(fileName)) {
783             if (target.rename(fileName, fileName + QLatin1String(".bak")))
784                 movedFiles += fileName;
785         }
786
787         /* Copy the source into the target directory */
788         QString source = backupRoot + QDir::separator() + fileName;
789         if (!QFile::exists(source)) {
790             TRACE() << "Ignoring file not present in backup:" << source;
791             continue;
792         }
793
794         QString destination =
795             config.m_storagePath + QDir::separator() + fileName;
796
797         ok = QFile::copy(source, destination);
798         if (ok) {
799             copiedFiles << fileName;
800         } else {
801             qWarning() << "Copy failed for:" << source;
802             break;
803         }
804     }
805
806     if (!ok) {
807         qWarning() << "Restore failed, recovering previous DB";
808
809         foreach (QString fileName, copiedFiles) {
810             target.remove(fileName);
811         }
812
813         foreach (QString fileName, movedFiles) {
814             if (!target.rename(fileName + QLatin1String(".bak"), fileName)) {
815                 qCritical() << "Could not recover:" << fileName;
816             }
817         }
818     } else {
819         /* delete ".bak" files */
820         foreach (QString fileName, movedFiles) {
821             target.remove(fileName + QLatin1String(".bak"));
822         }
823
824     }
825     return ok;
826 }
827
828 bool SignonDaemon::createStorageFileTree(const QStringList &backupFiles) const
829 {
830     QString storageDirPath = m_configuration->camConfiguration().m_storagePath;
831     QDir storageDir(storageDirPath);
832
833     if (!storageDir.exists()) {
834         if (!storageDir.mkpath(storageDirPath)) {
835             qCritical() << "Could not create storage dir for backup.";
836             return false;
837         }
838     }
839
840     foreach (QString fileName, backupFiles) {
841         if (storageDir.exists(fileName)) continue;
842
843         QString filePath = storageDir.path() + QDir::separator() + fileName;
844         QFile file(filePath);
845         if (!file.open(QIODevice::WriteOnly)) {
846             qCritical() << "Failed to create empty file for backup:" << filePath;
847             return false;
848         } else {
849             file.close();
850         }
851     }
852
853     return true;
854 }
855
856 uchar SignonDaemon::backupStarts()
857 {
858     TRACE() << "backup";
859     const CAMConfiguration config = m_configuration->camConfiguration();
860
861     QString luksDBName = config.m_encryptedStoragePath
862                           + QDir::separator()
863                           + config.m_dbName;
864
865     if (!m_backup && m_pCAMManager->credentialsSystemOpened())
866     {
867
868         if (m_configuration->useSecureStorage()) {
869 #ifdef SIGNON_AEGISFS
870
871             QString aegisDBName = config.m_aegisPath
872                                   + QDir::separator()
873                                   + config.m_dbName;
874
875             QFile::copy(aegisDBName, luksDBName);
876 #endif
877         }
878
879         m_pCAMManager->closeCredentialsSystem();
880         if (m_pCAMManager->credentialsSystemOpened())
881         {
882             qCritical() << "Cannot close credentials database";
883             return 2;
884         }
885     }
886
887     /* do backup copy: prepare the list of files to be backed up */
888     QStringList backupFiles;
889     backupFiles << config.m_dbName;
890
891     if (m_configuration->useSecureStorage())
892         backupFiles << QLatin1String(signonDefaultFileSystemName);
893
894     /* make sure that all the backup files and storage directory exist:
895        create storage dir and empty files if not so, as backup/restore
896        operations must be consistent */
897     if (!createStorageFileTree(backupFiles)) {
898         qCritical() << "Cannot create backup file tree.";
899         return 2;
900     }
901
902     /* perform the copy */
903     eraseBackupDir();
904     if (!copyToBackupDir(backupFiles)) {
905         qCritical() << "Cannot copy database";
906         if (!m_backup)
907             m_pCAMManager->openCredentialsSystem();
908         return 2;
909     }
910
911     if (!m_backup)
912     {
913         //mount file system back
914         if (!m_pCAMManager->openCredentialsSystem()) {
915             qCritical() << "Cannot reopen database";
916             return 0;
917         }
918
919     }
920     return 0;
921 }
922
923 uchar SignonDaemon::backupFinished()
924 {
925     TRACE() << "close";
926
927     eraseBackupDir();
928
929 #ifdef SIGNON_AEGISFS
930     const CAMConfiguration config = m_configuration->camConfiguration();
931     QString luksDBName = config.m_encryptedStoragePath
932                           + QDir::separator()
933                           + config.m_dbName;
934     QFile::remove(luksDBName);
935 #endif
936
937     if (m_backup)
938     {
939         //close daemon
940         TRACE() << "close daemon";
941         this->deleteLater();
942     }
943
944     return 0;
945  }
946
947 /*
948  * Does nothing but start-on-demand
949  * */
950 uchar SignonDaemon::restoreStarts()
951 {
952     TRACE();
953     return 0;
954 }
955
956 uchar SignonDaemon::restoreFinished()
957 {
958     TRACE() << "restore";
959     //restore requested
960     if (m_pCAMManager->credentialsSystemOpened())
961     {
962         //umount file system
963         if (!m_pCAMManager->closeCredentialsSystem())
964         {
965             qCritical() << "database cannot be closed";
966             return 2;
967         }
968     }
969
970     const CAMConfiguration config = m_configuration->camConfiguration();
971
972     QStringList backupFiles;
973     backupFiles << config.m_dbName;
974     if (m_configuration->useSecureStorage())
975         backupFiles << QLatin1String(signonDefaultFileSystemName);
976
977     /* perform the copy */
978     if (!copyFromBackupDir(backupFiles)) {
979         qCritical() << "Cannot copy database";
980         m_pCAMManager->openCredentialsSystem();
981         return 2;
982     }
983
984     eraseBackupDir();
985
986     //TODO check database integrity
987     if (!m_backup)
988     {
989         //mount file system back
990          if (!m_pCAMManager->openCredentialsSystem())
991              return 2;
992 #ifdef SIGNON_AEGISFS
993         QString luksDBName = config.m_encryptedStoragePath
994                               + QDir::separator()
995                               + config.m_dbName;
996
997         QString aegisDBName = config.m_aegisPath
998                               + QDir::separator()
999                               + config.m_dbName;
1000
1001         QFile::copy(luksDBName, aegisDBName);
1002         QFile::remove(luksDBName);
1003 #endif
1004     }
1005
1006     return 0;
1007 }
1008
1009 void SignonDaemon::listDBusInterfaces()
1010 {
1011     QDBusReply<QStringList> reply = SIGNOND_BUS.interface()->registeredServiceNames();
1012     QStringList list = reply.value();
1013
1014     QString servicesList = QLatin1String("DBUS registered services: \n");
1015     servicesList += list.join(QLatin1String("\n"));
1016
1017     TRACE() << "\n\n" << servicesList.toAscii().data() << "\n";
1018 }
1019
1020 void SignonDaemon::onDisconnected()
1021 {
1022     TRACE() << "Disconnected from session bus: exiting";
1023     this->deleteLater();
1024     QMetaObject::invokeMethod(QCoreApplication::instance(),
1025                               "quit",
1026                               Qt::QueuedConnection);
1027 }
1028
1029 } //namespace SignonDaemonNS