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