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