signon  8.57
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...
bool keysAvailable() const
The CAM manages the encryption keys collection.
bool deleteCredentialsSystem()
Deletes the credentials system.
void addSetting(const QString &key, const QVariant &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.
bool isCredentialsSystemReady() const
The creadentials system is ready when all of the subscribed key managers have successfully reported a...
#define BLAME()
Definition: debug.h:32
QString accessControlManagerName() const
Returns the name of the AccessControlManager to use.
CredentialsAccessManager(const CAMConfiguration &configuration, QObject *parent=0)
Constructs a CredentialsAccessManager object with the given parent.
QString cryptoManagerName() const
Returns the name of the CryptoManager to use.
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...
#define RETURN_IF_NOT_INITIALIZED(return_value)
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 credentialsSystemOpened() const
For convenience method.
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.
QPointer< QObject > EventSender
Dummy implementation of a manager for the credentials storage file system encryption.
~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
QString secretsStorageName() const
Returns the name of the SecretsStorage to use.
Implements a default key authorizer, which authorizes all given keys.
Configuration object for the CredentialsAccessManager - CAM.
bool openCredentialsSystem()
Opens the credentials system, creates the CreadentialsDB object; if encryption is configured this wil...
const char signonDefaultStoragePath[]
Contains helper functions related to Access Control.
QString metadataDBPath() const
Returns the path to the metadata DB.