signon  8.58
credentialsaccessmanager.cpp
Go to the documentation of this file.
1 /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of signon
4  *
5  * Copyright (C) 2009-2010 Nokia Corporation.
6  * Copyright (C) 2011 Intel Corporation.
7  *
8  * Contact: Aurel Popirtac <mailto:ext-Aurel.Popirtac@nokia.com>
9  * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
10  * Contact: Jussi Laako <jussi.laako@linux.intel.com>
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public License
14  * version 2.1 as published by the Free Software Foundation.
15  *
16  * This library is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
24  * 02110-1301 USA
25  */
26 
27 #define SIGNON_ENABLE_UNSTABLE_APIS
29 
30 #include "default-crypto-manager.h"
31 #include "default-key-authorizer.h"
33 #include "signond-common.h"
34 
35 #include "SignOn/ExtensionInterface"
36 #include "SignOn/misc.h"
37 
38 #include <QFile>
39 #include <QBuffer>
40 
41 
42 #define RETURN_IF_NOT_INITIALIZED(return_value) \
43  do { \
44  if (!m_isInitialized) { \
45  m_error = NotInitialized; \
46  TRACE() << "CredentialsAccessManager not initialized."; \
47  return return_value; \
48  } \
49  } while (0)
50 
51 using namespace SignonDaemonNS;
52 using namespace SignOn;
53 
54 /* ---------------------- CAMConfiguration ---------------------- */
55 
57  m_dbName(QLatin1String(signonDefaultDbName)),
58  m_secretsDbName(QLatin1String(signonDefaultSecretsDbName)),
59  m_encryptionPassphrase(QByteArray())
60 {
62 }
63 
64 void CAMConfiguration::serialize(QIODevice *device)
65 {
66  if (device == NULL)
67  return;
68 
69  if (!device->open(QIODevice::ReadWrite)) {
70  return;
71  }
72 
73  QString buffer;
74  QTextStream stream(&buffer);
75  stream << "\n\n====== Credentials Access Manager Configuration ======\n\n";
76  const char *usingEncryption = useEncryption() ? "true" : "false";
77  stream << "Using encryption: " << usingEncryption << '\n';
78  stream << "Metadata DB path: " << metadataDBPath() << '\n';
79  stream << "Cryptomanager name: " << cryptoManagerName() << '\n';
80  stream << "ACL manager name: " << accessControlManagerName() << '\n';
81  stream << "Secrets storage name: " << secretsStorageName() << '\n';
82  stream << "======================================================\n\n";
83  device->write(buffer.toUtf8());
84  device->close();
85 }
86 
88 {
89  return m_storagePath + QDir::separator() + m_dbName;
90 }
91 
93 {
94  return m_settings.value(QLatin1String("CryptoManager")).toString();
95 }
96 
98 {
99  return m_settings.value(QLatin1String("AccessControlManager")).toString();
100 }
101 
103 {
104  return cryptoManagerName() != QLatin1String("default");
105 }
106 
108 {
109  return m_settings.value(QLatin1String("SecretsStorage")).toString();
110 }
111 
112 void CAMConfiguration::setStoragePath(const QString &storagePath) {
113  m_storagePath = storagePath;
114  if (m_storagePath.startsWith(QLatin1Char('~')))
115  m_storagePath.replace(0, 1, QDir::homePath());
116  // CryptoSetup extensions are given the m_settings dictionary only
117  addSetting(QLatin1String("StoragePath"), m_storagePath);
118 }
119 
120 /* ---------------------- CredentialsAccessManager ---------------------- */
121 
122 CredentialsAccessManager *CredentialsAccessManager::m_pInstance = NULL;
123 
125  const CAMConfiguration &configuration,
126  QObject *parent):
127  QObject(parent),
128  m_isInitialized(false),
129  m_systemOpened(false),
130  m_error(NoError),
131  keyManagers(),
132  m_pCredentialsDB(NULL),
133  m_cryptoManager(NULL),
134  m_keyHandler(NULL),
135  m_keyAuthorizer(NULL),
136  m_secretsStorage(NULL),
137  m_CAMConfiguration(configuration),
138  m_acManager(NULL),
139  m_acManagerHelper(NULL)
140 {
141  if (!m_pInstance) {
142  m_pInstance = this;
143  } else {
144  BLAME() << "Creating a second instance of the CAM";
145  }
146 
147  m_keyHandler = new SignOn::KeyHandler(this);
148 }
149 
151 {
153 
154  m_pInstance = NULL;
155 }
156 
158 {
159  return m_pInstance;
160 }
161 
163 {
164  TRACE() << "Enter";
165 
166  if (m_systemOpened)
168 
169  // Disconnect all key managers
170  foreach (SignOn::AbstractKeyManager *keyManager, keyManagers)
171  keyManager->disconnect();
172 
173  m_isInitialized = false;
174  m_error = NoError;
175 }
176 
178 {
179  if (m_isInitialized) {
180  TRACE() << "CAM already initialized.";
181  m_error = AlreadyInitialized;
182  return false;
183  }
184 
185  QBuffer config;
186  m_CAMConfiguration.serialize(&config);
187  TRACE() << "Initializing CredentialsAccessManager with configuration: " <<
188  config.data();
189 
190  if (!createStorageDir()) {
191  BLAME() << "Failed to create storage directory.";
192  return false;
193  }
194 
195  if (m_secretsStorage == 0) {
196  QString name = m_CAMConfiguration.secretsStorageName();
197  if (!name.isEmpty() && name != QLatin1String("default")) {
198  BLAME() << "Couldn't load SecretsStorage:" << name;
199  }
200  TRACE() << "No SecretsStorage set, using default (dummy)";
201  m_secretsStorage = new DefaultSecretsStorage(this);
202  }
203 
204  //Initialize AccessControlManager
205  if (m_acManager == 0) {
206  QString name = m_CAMConfiguration.accessControlManagerName();
207  if (!name.isEmpty() && name != QLatin1String("default")) {
208  BLAME() << "Couldn't load AccessControlManager:" << name;
209  }
210  TRACE() << "No AccessControlManager set, using default (dummy)";
211  m_acManager = new SignOn::AbstractAccessControlManager(this);
212  }
213 
214  //Initialize AccessControlManagerHelper
215  if (m_acManagerHelper == 0) {
216  m_acManagerHelper = new AccessControlManagerHelper(m_acManager);
217  }
218 
219  //Initialize CryptoManager
220  if (m_cryptoManager == 0) {
221  QString name = m_CAMConfiguration.cryptoManagerName();
222  if (!name.isEmpty() && name != QLatin1String("default")) {
223  BLAME() << "Couldn't load CryptoManager:" << name;
224  }
225  TRACE() << "No CryptoManager set, using default (dummy)";
226  m_cryptoManager = new DefaultCryptoManager(this);
227  }
228  QObject::connect(m_cryptoManager, SIGNAL(fileSystemMounted()),
229  this, SLOT(onEncryptedFSMounted()));
230  QObject::connect(m_cryptoManager, SIGNAL(fileSystemUnmounting()),
231  this, SLOT(onEncryptedFSUnmounting()));
232  m_cryptoManager->initialize(m_CAMConfiguration.m_settings);
233 
234  /* This check is an optimization: instantiating the KeyAuthorizer is
235  * probably not harmful if useEncryption() is false, but it's certainly
236  * useless. */
237  if (m_CAMConfiguration.useEncryption()) {
238  if (m_keyAuthorizer == 0) {
239  TRACE() << "No key authorizer set, using default";
240  m_keyAuthorizer = new DefaultKeyAuthorizer(m_keyHandler, this);
241  }
242  QObject::connect(m_keyAuthorizer,
243  SIGNAL(keyAuthorizationQueried(const SignOn::Key,int)),
244  this,
245  SLOT(onKeyAuthorizationQueried(const SignOn::Key,int)));
246 
247  /* These signal connections should be done after instantiating the
248  * KeyAuthorizer, so that the KeyAuthorizer's slot will be called
249  * first (or we could connect to them in queued mode)
250  */
251  QObject::connect(m_keyHandler, SIGNAL(ready()),
252  this, SIGNAL(credentialsSystemReady()));
253  QObject::connect(m_keyHandler, SIGNAL(keyInserted(SignOn::Key)),
254  this, SLOT(onKeyInserted(SignOn::Key)));
255  QObject::connect(m_keyHandler,
256  SIGNAL(lastAuthorizedKeyRemoved(SignOn::Key)),
257  this,
258  SLOT(onLastAuthorizedKeyRemoved(SignOn::Key)));
259  QObject::connect(m_keyHandler, SIGNAL(keyRemoved(SignOn::Key)),
260  this, SLOT(onKeyRemoved(SignOn::Key)));
261  m_keyHandler->initialize(m_cryptoManager, keyManagers);
262  }
263 
264  m_isInitialized = true;
265  m_error = NoError;
266 
267  TRACE() << "CredentialsAccessManager successfully initialized...";
268  return true;
269 }
270 
272  SignOn::AbstractKeyManager *keyManager)
273 {
274  keyManagers.append(keyManager);
275 }
276 
278 {
279  bool extensionInUse = false;
280 
281  SignOn::ExtensionInterface *extension;
282  SignOn::ExtensionInterface2 *extension2;
283  SignOn::ExtensionInterface3 *extension3;
284 
285  extension3 = qobject_cast<SignOn::ExtensionInterface3 *>(plugin);
286 
287  if (extension3 != 0)
288  extension2 = extension3;
289  else
290  extension2 = qobject_cast<SignOn::ExtensionInterface2 *>(plugin);
291 
292  if (extension2 != 0)
293  extension = extension2;
294  else
295  extension = qobject_cast<SignOn::ExtensionInterface *>(plugin);
296 
297  if (extension == 0) {
298  qWarning() << "Plugin instance is not an ExtensionInterface";
299  return false;
300  }
301 
302  SignOn::AbstractKeyManager *keyManager = extension->keyManager(this);
303  if (keyManager) {
304  addKeyManager(keyManager);
305  extensionInUse = true;
306  }
307 
308  /* Check if the extension implements the new interface and provides a key
309  * authorizer. */
310  if (extension2 != 0) {
311  SignOn::AbstractKeyAuthorizer *keyAuthorizer =
312  extension2->keyAuthorizer(m_keyHandler, this);
313  if (keyAuthorizer != 0) {
314  if (m_keyAuthorizer == 0) {
315  m_keyAuthorizer = keyAuthorizer;
316  extensionInUse = true;
317  } else {
318  TRACE() << "Key authorizer already set";
319  delete keyAuthorizer;
320  }
321  }
322  }
323 
324  if (extension3 != 0) {
325  /* Instantiate this plugin's CryptoManager only if it's the plugin
326  * requested in the config file. */
327  if (m_CAMConfiguration.cryptoManagerName().isEmpty() ||
328  plugin->objectName() == m_CAMConfiguration.cryptoManagerName()) {
329  SignOn::AbstractCryptoManager *cryptoManager =
330  extension3->cryptoManager(this);
331  if (cryptoManager != 0) {
332  if (m_cryptoManager == 0) {
333  m_cryptoManager = cryptoManager;
334  extensionInUse = true;
335  } else {
336  TRACE() << "Crypto manager already set";
337  delete cryptoManager;
338  }
339  }
340  }
341 
342  if (m_CAMConfiguration.secretsStorageName().isEmpty() ||
343  plugin->objectName() == m_CAMConfiguration.secretsStorageName()) {
344  SignOn::AbstractSecretsStorage *secretsStorage =
345  extension3->secretsStorage(this);
346  if (secretsStorage != 0) {
347  if (m_secretsStorage == 0) {
348  m_secretsStorage = secretsStorage;
349  extensionInUse = true;
350  } else {
351  TRACE() << "SecretsStorage already set";
352  delete secretsStorage;
353  }
354  }
355  }
356 
357  /* Instantiate this plugin's AccessControlManager only if it's the
358  * plugin requested in the config file. */
359  if (m_CAMConfiguration.accessControlManagerName().isEmpty() ||
360  plugin->objectName() ==
361  m_CAMConfiguration.accessControlManagerName()) {
362  SignOn::AbstractAccessControlManager *acManager =
363  extension3->accessControlManager(this);
364  if (acManager != 0) {
365  if (m_acManager == 0) {
366  m_acManager = acManager;
367  extensionInUse = true;
368  } else {
369  TRACE() << "Access control manager already set";
370  delete acManager;
371  }
372  }
373  }
374  }
375  return extensionInUse;
376 }
377 
379 {
380  QStringList files;
381 
382  files << m_cryptoManager->backupFiles();
383  return files;
384 }
385 
386 bool CredentialsAccessManager::openSecretsDB()
387 {
388  if (!m_cryptoManager->fileSystemIsMounted()) {
389  /* Do not attempt to mount the FS; we know that it will be mounted
390  * automatically, as soon as some encryption keys are provided */
391  m_error = CredentialsDbNotMounted;
392  return false;
393  }
394 
395  QString dbPath = m_cryptoManager->fileSystemMountPath()
396  + QDir::separator()
397  + m_CAMConfiguration.m_secretsDbName;
398 
399  TRACE() << "Database name: [" << dbPath << "]";
400 
401  if (!m_pCredentialsDB->openSecretsDB(dbPath))
402  return false;
403 
404  m_error = NoError;
405  return true;
406 }
407 
408 bool CredentialsAccessManager::isSecretsDBOpen()
409 {
410  return m_pCredentialsDB->isSecretsDBOpen();
411 }
412 
413 bool CredentialsAccessManager::closeSecretsDB()
414 {
415  m_pCredentialsDB->closeSecretsDB();
416 
417  if (!m_cryptoManager->unmountFileSystem()) {
418  m_error = CredentialsDbUnmountFailed;
419  return false;
420  }
421 
422  return true;
423 }
424 
425 bool CredentialsAccessManager::createStorageDir()
426 {
427  QString dbPath = m_CAMConfiguration.metadataDBPath();
428 
429  QFileInfo fileInfo(dbPath);
430  if (!fileInfo.exists()) {
431  QDir storageDir(fileInfo.dir());
432  if (!storageDir.mkpath(storageDir.path())) {
433  BLAME() << "Could not create storage directory:" <<
434  storageDir.path();
435  m_error = CredentialsDbSetupFailed;
436  return false;
437  }
438  setUserOwnership(storageDir.path());
439  }
440  return true;
441 
442 }
443 bool CredentialsAccessManager::openMetaDataDB()
444 {
445  QString dbPath = m_CAMConfiguration.metadataDBPath();
446 
447  m_pCredentialsDB = new CredentialsDB(dbPath, m_secretsStorage);
448 
449  if (!m_pCredentialsDB->init()) {
451  return false;
452  }
453 
454  return true;
455 }
456 
457 void CredentialsAccessManager::closeMetaDataDB()
458 {
459  if (m_pCredentialsDB) {
460  delete m_pCredentialsDB;
461  m_pCredentialsDB = NULL;
462  }
463 }
464 
466 {
468 
469  if (!openMetaDataDB()) {
470  BLAME() << "Couldn't open metadata DB!";
471  return false;
472  }
473 
474  m_systemOpened = true;
475 
476  if (m_cryptoManager->fileSystemIsMounted()) {
477  if (!openSecretsDB()) {
478  BLAME() << "Failed to open secrets DB.";
479  /* Even if the secrets DB couldn't be opened, signond is still
480  * usable: that's why we return "true" anyways. */
481  }
482  } else {
483  /* The secrets DB will be opened as soon as the encrypted FS is
484  * mounted.
485  */
486  m_cryptoManager->mountFileSystem();
487  }
488 
489  return true;
490 }
491 
493 {
495 
497  return true;
498 
499  bool allClosed = true;
500  if (isSecretsDBOpen() && !closeSecretsDB())
501  allClosed = false;
502 
503  closeMetaDataDB();
504 
505  m_error = NoError;
506  m_systemOpened = false;
507  return allClosed;
508 }
509 
511 {
513 
514  if (m_systemOpened && !closeCredentialsSystem()) {
515  /* The close operation failed: we cannot proceed */
516  return false;
517  }
518 
519  BLAME() << "Not implemented";
520  return false;
521 }
522 
524 {
526 
527  return m_pCredentialsDB;
528 }
529 
531 {
532  return (m_keyHandler != 0) ? m_keyHandler->isReady() : true;
533 }
534 
535 void CredentialsAccessManager::onKeyInserted(const SignOn::Key key)
536 {
537  TRACE() << "Key inserted.";
538 
539  if (!m_keyHandler->keyIsAuthorized(key))
540  m_keyAuthorizer->queryKeyAuthorization(
541  key, AbstractKeyAuthorizer::KeyInserted);
542 }
543 
544 void CredentialsAccessManager::onLastAuthorizedKeyRemoved(const SignOn::Key key)
545 {
546  Q_UNUSED(key);
547  TRACE() << "All keys disabled. Closing secure storage.";
548  if (isSecretsDBOpen() || m_cryptoManager->fileSystemIsMounted())
549  if (!closeSecretsDB())
550  BLAME() << "Error occurred while closing secure storage.";
551 }
552 
553 void CredentialsAccessManager::onKeyRemoved(const SignOn::Key key)
554 {
555  TRACE() << "Key removed.";
556 
557  if (m_keyHandler->keyIsAuthorized(key)) {
558  if (!m_keyHandler->revokeKeyAuthorization(key)) {
559  BLAME() << "Revoking key authorization failed";
560  }
561  }
562 }
563 
564 void CredentialsAccessManager::onKeyAuthorizationQueried(const SignOn::Key key,
565  int result)
566 {
567  TRACE() << "result:" << result;
568 
569  if (result != AbstractKeyAuthorizer::Denied) {
570  KeyHandler::AuthorizeFlags flags = KeyHandler::None;
571  if (result == AbstractKeyAuthorizer::Exclusive) {
572  TRACE() << "Reformatting secure storage.";
573  flags |= KeyHandler::FormatStorage;
574  }
575 
576  if (!m_keyHandler->authorizeKey(key, flags)) {
577  BLAME() << "Authorization failed";
578  }
579  }
580 
581  replyToSecureStorageEventNotifiers();
582 }
583 
585 {
586  if (m_keyHandler == 0) return false;
587  return !m_keyHandler->insertedKeys().isEmpty();
588 }
589 
590 void CredentialsAccessManager::replyToSecureStorageEventNotifiers()
591 {
592  TRACE();
593  //Notify secure storage notifiers if any.
594  int eventType = SIGNON_SECURE_STORAGE_NOT_AVAILABLE;
595  if ((m_pCredentialsDB != 0) && m_pCredentialsDB->isSecretsDBOpen())
597 
598  // Signal objects that posted secure storage not available events
599  foreach (EventSender object, m_secureStorageEventNotifiers) {
600  if (object.isNull())
601  continue;
602 
603  SecureStorageEvent *secureStorageEvent =
604  new SecureStorageEvent((QEvent::Type)eventType);
605 
606  QCoreApplication::postEvent(
607  object.data(),
608  secureStorageEvent,
609  Qt::HighEventPriority);
610  }
611 
612  m_secureStorageEventNotifiers.clear();
613 }
614 
616 {
617  TRACE() << "Custom event received.";
618  if (event->type() != SIGNON_SECURE_STORAGE_NOT_AVAILABLE) {
619  QObject::customEvent(event);
620  return;
621  }
622 
623  SecureStorageEvent *localEvent =
624  static_cast<SecureStorageEvent *>(event);
625 
626  /* All senders of this event will receive a reply when
627  * the secure storage becomes available or an error occurs. */
628  m_secureStorageEventNotifiers.append(localEvent->m_sender);
629 
630  TRACE() << "Processing secure storage not available event.";
631  if ((localEvent == 0) || (m_pCredentialsDB == 0)) {
632  replyToSecureStorageEventNotifiers();
633  QObject::customEvent(event);
634  return;
635  }
636 
637  //Double check if the secrets DB is indeed unavailable
638  if (m_pCredentialsDB->isSecretsDBOpen()) {
639  replyToSecureStorageEventNotifiers();
640  QObject::customEvent(event);
641  return;
642  }
643 
644  SignOn::Key key; /* we don't specity any key */
645  m_keyAuthorizer->queryKeyAuthorization(key,
646  AbstractKeyAuthorizer::StorageNeeded);
647 
648  QObject::customEvent(event);
649 }
650 
651 void CredentialsAccessManager::onEncryptedFSMounted()
652 {
653  TRACE();
654  if (!credentialsSystemOpened()) return;
655 
656  if (!isSecretsDBOpen()) {
657  if (openSecretsDB()) {
658  TRACE() << "Secrets DB opened.";
659  } else {
660  BLAME() << "Failed to open secrets DB.";
661  }
662  } else {
663  BLAME() << "Secrets DB already opened?";
664  }
665 }
666 
667 void CredentialsAccessManager::onEncryptedFSUnmounting()
668 {
669  TRACE();
670  if (!credentialsSystemOpened()) return;
671 
672  if (isSecretsDBOpen()) {
673  m_pCredentialsDB->closeSecretsDB();
674  }
675 }
CAMConfiguration()
Constructs a CAMConfiguration object with the default configuration - encryption in use...
QString metadataDBPath() const
Returns the path to the metadata DB.
bool deleteCredentialsSystem()
Deletes the credentials system.
void addSetting(const QString &key, const QVariant &value)
#define RETURN_IF_NOT_INITIALIZED(return_value)
void serialize(QIODevice *device)
Serializes the CAMConfiguration object as string to a specific IODevice.
bool closeCredentialsSystem()
Closes the credentials system.
QString m_dbName
The database file name.
bool initExtension(QObject *object)
Initializes know objects from an extension plugin.
#define BLAME()
Definition: debug.h:32
bool credentialsSystemOpened() const
For convenience method.
CredentialsAccessManager(const CAMConfiguration &configuration, QObject *parent=0)
Constructs a CredentialsAccessManager object with the given parent.
QString m_secretsDbName
The credentials database file name.
Any object in the signon framework that needs the CredentialsAccessManager - CAM - secure storage in ...
void addKeyManager(SignOn::AbstractKeyManager *keyManager)
Adds a key manager.
const char signonDefaultDbName[]
void setStoragePath(const QString &storagePath)
static CredentialsAccessManager * instance()
Returns CAM instance.
QString m_storagePath
The base directory for storage.
#define SIGNON_SECURE_STORAGE_AVAILABLE
The CAM will reply with an event of this type when the secure storage access will be successfully res...
Main singleton and manager object of the credentials database system.
#define SIGNON_SECURE_STORAGE_NOT_AVAILABLE
Use this event type to signal the CAM when the secure storage is not available.
bool isCredentialsSystemReady() const
The creadentials system is ready when all of the subscribed key managers have successfully reported a...
bool setUserOwnership(const QString &filePath)
Definition: misc.cpp:33
void credentialsSystemReady()
Is emitted when the credentials system becomes ready.
bool openSecretsDB(const QString &secretsDbName)
This method will open the DB file containing the user secrets.
bool init()
Initializes the CAM instance.
QString secretsStorageName() const
Returns the name of the SecretsStorage to use.
QPointer< QObject > EventSender
Dummy implementation of a manager for the credentials storage file system encryption.
bool keysAvailable() const
The CAM manages the encryption keys collection.
~CredentialsAccessManager()
Destroys a CredentialsAccessManager.
Definition of the CredentialsAccessManager object.
#define TRACE()
Definition: debug.h:28
void finalize()
Finalizes the CAM instance, this could include, closing the credentials system and resetting the conf...
SQLite-based implementation of the AbstractSecretsStorage interface.
const char signonDefaultSecretsDbName[]
Manages the credentials I/O.
Definition: credentialsdb.h:66
Implements a default key authorizer, which authorizes all given keys.
QString accessControlManagerName() const
Returns the name of the AccessControlManager to use.
Configuration object for the CredentialsAccessManager - CAM.
bool openCredentialsSystem()
Opens the credentials system, creates the CreadentialsDB object; if encryption is configured this wil...
QString cryptoManagerName() const
Returns the name of the CryptoManager to use.
const char signonDefaultStoragePath[]
Contains helper functions related to Access Control.