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