signon  8.54
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"),
95  QLatin1String("default")).toString();
96 }
97 
99 {
100  return m_settings.value(QLatin1String("AccessControlManager"),
101  QLatin1String("default")).toString();
102 }
103 
105 {
106  return cryptoManagerName() != QLatin1String("default");
107 }
108 
110 {
111  return m_settings.value(QLatin1String("SecretsStorage"),
112  QLatin1String("default")).toString();
113 }
114 
115 void CAMConfiguration::setStoragePath(const QString &storagePath) {
116  m_storagePath = storagePath;
117  if (m_storagePath.startsWith(QLatin1Char('~')))
118  m_storagePath.replace(0, 1, QDir::homePath());
119  // CryptoSetup extensions are given the m_settings dictionary only
120  addSetting(QLatin1String("StoragePath"), m_storagePath);
121 }
122 
123 /* ---------------------- CredentialsAccessManager ---------------------- */
124 
125 CredentialsAccessManager *CredentialsAccessManager::m_pInstance = NULL;
126 
128  const CAMConfiguration &configuration,
129  QObject *parent):
130  QObject(parent),
131  m_isInitialized(false),
132  m_systemOpened(false),
133  m_error(NoError),
134  keyManagers(),
135  m_pCredentialsDB(NULL),
136  m_cryptoManager(NULL),
137  m_keyHandler(NULL),
138  m_keyAuthorizer(NULL),
139  m_secretsStorage(NULL),
140  m_CAMConfiguration(configuration),
141  m_acManager(NULL),
142  m_acManagerHelper(NULL)
143 {
144  if (!m_pInstance) {
145  m_pInstance = this;
146  } else {
147  BLAME() << "Creating a second instance of the CAM";
148  }
149 
150  m_keyHandler = new SignOn::KeyHandler(this);
151 }
152 
154 {
156 
157  m_pInstance = NULL;
158 }
159 
161 {
162  return m_pInstance;
163 }
164 
166 {
167  TRACE() << "Enter";
168 
169  if (m_systemOpened)
171 
172  // Disconnect all key managers
173  foreach (SignOn::AbstractKeyManager *keyManager, keyManagers)
174  keyManager->disconnect();
175 
176  m_isInitialized = false;
177  m_error = NoError;
178 }
179 
181 {
182  if (m_isInitialized) {
183  TRACE() << "CAM already initialized.";
184  m_error = AlreadyInitialized;
185  return false;
186  }
187 
188  QBuffer config;
189  m_CAMConfiguration.serialize(&config);
190  TRACE() << "Initializing CredentialsAccessManager with configuration: " <<
191  config.data();
192 
193  if (!createStorageDir()) {
194  BLAME() << "Failed to create storage directory.";
195  return false;
196  }
197 
198  if (m_secretsStorage == 0) {
199  QString name = m_CAMConfiguration.secretsStorageName();
200  if (name != QLatin1String("default")) {
201  BLAME() << "Couldn't load SecretsStorage:" << name;
202  }
203  TRACE() << "No SecretsStorage set, using default (dummy)";
204  m_secretsStorage = new DefaultSecretsStorage(this);
205  }
206 
207  //Initialize AccessControlManager
208  if (m_acManager == 0) {
209  QString name = m_CAMConfiguration.accessControlManagerName();
210  if (name != QLatin1String("default")) {
211  BLAME() << "Couldn't load AccessControlManager:" << name;
212  }
213  TRACE() << "No AccessControlManager set, using default (dummy)";
214  m_acManager = new SignOn::AbstractAccessControlManager(this);
215  }
216 
217  //Initialize AccessControlManagerHelper
218  if (m_acManagerHelper == 0) {
219  m_acManagerHelper = new AccessControlManagerHelper(m_acManager);
220  }
221 
222  //Initialize CryptoManager
223  if (m_cryptoManager == 0) {
224  QString name = m_CAMConfiguration.cryptoManagerName();
225  if (name != QLatin1String("default")) {
226  BLAME() << "Couldn't load CryptoManager:" << name;
227  }
228  TRACE() << "No CryptoManager set, using default (dummy)";
229  m_cryptoManager = new DefaultCryptoManager(this);
230  }
231  QObject::connect(m_cryptoManager, SIGNAL(fileSystemMounted()),
232  this, SLOT(onEncryptedFSMounted()));
233  QObject::connect(m_cryptoManager, SIGNAL(fileSystemUnmounting()),
234  this, SLOT(onEncryptedFSUnmounting()));
235  m_cryptoManager->initialize(m_CAMConfiguration.m_settings);
236 
237  /* This check is an optimization: instantiating the KeyAuthorizer is
238  * probably not harmful if useEncryption() is false, but it's certainly
239  * useless. */
240  if (m_CAMConfiguration.useEncryption()) {
241  if (m_keyAuthorizer == 0) {
242  TRACE() << "No key authorizer set, using default";
243  m_keyAuthorizer = new DefaultKeyAuthorizer(m_keyHandler, this);
244  }
245  QObject::connect(m_keyAuthorizer,
246  SIGNAL(keyAuthorizationQueried(const SignOn::Key,int)),
247  this,
248  SLOT(onKeyAuthorizationQueried(const SignOn::Key,int)));
249 
250  /* These signal connections should be done after instantiating the
251  * KeyAuthorizer, so that the KeyAuthorizer's slot will be called
252  * first (or we could connect to them in queued mode)
253  */
254  QObject::connect(m_keyHandler, SIGNAL(ready()),
255  this, SIGNAL(credentialsSystemReady()));
256  QObject::connect(m_keyHandler, SIGNAL(keyInserted(SignOn::Key)),
257  this, SLOT(onKeyInserted(SignOn::Key)));
258  QObject::connect(m_keyHandler,
259  SIGNAL(lastAuthorizedKeyRemoved(SignOn::Key)),
260  this,
261  SLOT(onLastAuthorizedKeyRemoved(SignOn::Key)));
262  QObject::connect(m_keyHandler, SIGNAL(keyRemoved(SignOn::Key)),
263  this, SLOT(onKeyRemoved(SignOn::Key)));
264  m_keyHandler->initialize(m_cryptoManager, keyManagers);
265  }
266 
267  m_isInitialized = true;
268  m_error = NoError;
269 
270  TRACE() << "CredentialsAccessManager successfully initialized...";
271  return true;
272 }
273 
275  SignOn::AbstractKeyManager *keyManager)
276 {
277  keyManagers.append(keyManager);
278 }
279 
281 {
282  bool extensionInUse = false;
283 
284  SignOn::ExtensionInterface *extension;
285  SignOn::ExtensionInterface2 *extension2;
286  SignOn::ExtensionInterface3 *extension3;
287 
288  extension3 = qobject_cast<SignOn::ExtensionInterface3 *>(plugin);
289 
290  if (extension3 != 0)
291  extension2 = extension3;
292  else
293  extension2 = qobject_cast<SignOn::ExtensionInterface2 *>(plugin);
294 
295  if (extension2 != 0)
296  extension = extension2;
297  else
298  extension = qobject_cast<SignOn::ExtensionInterface *>(plugin);
299 
300  if (extension == 0) {
301  qWarning() << "Plugin instance is not an ExtensionInterface";
302  return false;
303  }
304 
305  SignOn::AbstractKeyManager *keyManager = extension->keyManager(this);
306  if (keyManager) {
307  addKeyManager(keyManager);
308  extensionInUse = true;
309  }
310 
311  /* Check if the extension implements the new interface and provides a key
312  * authorizer. */
313  if (extension2 != 0) {
314  SignOn::AbstractKeyAuthorizer *keyAuthorizer =
315  extension2->keyAuthorizer(m_keyHandler, this);
316  if (keyAuthorizer != 0) {
317  if (m_keyAuthorizer == 0) {
318  m_keyAuthorizer = keyAuthorizer;
319  extensionInUse = true;
320  } else {
321  TRACE() << "Key authorizer already set";
322  delete keyAuthorizer;
323  }
324  }
325  }
326 
327  if (extension3 != 0) {
328  /* Instantiate this plugin's CryptoManager only if it's the plugin
329  * requested in the config file. */
330  if (plugin->objectName() == m_CAMConfiguration.cryptoManagerName()) {
331  SignOn::AbstractCryptoManager *cryptoManager =
332  extension3->cryptoManager(this);
333  if (cryptoManager != 0) {
334  if (m_cryptoManager == 0) {
335  m_cryptoManager = cryptoManager;
336  extensionInUse = true;
337  } else {
338  TRACE() << "Crypto manager already set";
339  delete cryptoManager;
340  }
341  }
342  }
343 
344  if (m_CAMConfiguration.secretsStorageName().isEmpty() ||
345  plugin->objectName() == m_CAMConfiguration.secretsStorageName()) {
346  SignOn::AbstractSecretsStorage *secretsStorage =
347  extension3->secretsStorage(this);
348  if (secretsStorage != 0) {
349  if (m_secretsStorage == 0) {
350  m_secretsStorage = secretsStorage;
351  extensionInUse = true;
352  } else {
353  TRACE() << "SecretsStorage already set";
354  delete secretsStorage;
355  }
356  }
357  }
358 
359  /* Instantiate this plugin's AccessControlManager only if it's the
360  * plugin requested in the config file. */
361  if (plugin->objectName() ==
362  m_CAMConfiguration.accessControlManagerName()) {
363  SignOn::AbstractAccessControlManager *acManager =
364  extension3->accessControlManager(this);
365  if (acManager != 0) {
366  if (m_acManager == 0) {
367  m_acManager = acManager;
368  extensionInUse = true;
369  } else {
370  TRACE() << "Access control manager already set";
371  delete acManager;
372  }
373  }
374  }
375  }
376  return extensionInUse;
377 }
378 
380 {
381  QStringList files;
382 
383  files << m_cryptoManager->backupFiles();
384  return files;
385 }
386 
387 bool CredentialsAccessManager::openSecretsDB()
388 {
389  if (!m_cryptoManager->fileSystemIsMounted()) {
390  /* Do not attempt to mount the FS; we know that it will be mounted
391  * automatically, as soon as some encryption keys are provided */
392  m_error = CredentialsDbNotMounted;
393  return false;
394  }
395 
396  QString dbPath = m_cryptoManager->fileSystemMountPath()
397  + QDir::separator()
398  + m_CAMConfiguration.m_secretsDbName;
399 
400  TRACE() << "Database name: [" << dbPath << "]";
401 
402  if (!m_pCredentialsDB->openSecretsDB(dbPath))
403  return false;
404 
405  m_error = NoError;
406  return true;
407 }
408 
409 bool CredentialsAccessManager::isSecretsDBOpen()
410 {
411  return m_pCredentialsDB->isSecretsDBOpen();
412 }
413 
414 bool CredentialsAccessManager::closeSecretsDB()
415 {
416  m_pCredentialsDB->closeSecretsDB();
417 
418  if (!m_cryptoManager->unmountFileSystem()) {
419  m_error = CredentialsDbUnmountFailed;
420  return false;
421  }
422 
423  return true;
424 }
425 
426 bool CredentialsAccessManager::createStorageDir()
427 {
428  QString dbPath = m_CAMConfiguration.metadataDBPath();
429 
430  QFileInfo fileInfo(dbPath);
431  if (!fileInfo.exists()) {
432  QDir storageDir(fileInfo.dir());
433  if (!storageDir.mkpath(storageDir.path())) {
434  BLAME() << "Could not create storage directory:" <<
435  storageDir.path();
436  m_error = CredentialsDbSetupFailed;
437  return false;
438  }
439  setUserOwnership(storageDir.path());
440  }
441  return true;
442 
443 }
444 bool CredentialsAccessManager::openMetaDataDB()
445 {
446  QString dbPath = m_CAMConfiguration.metadataDBPath();
447 
448  m_pCredentialsDB = new CredentialsDB(dbPath, m_secretsStorage);
449 
450  if (!m_pCredentialsDB->init()) {
452  return false;
453  }
454 
455  return true;
456 }
457 
458 void CredentialsAccessManager::closeMetaDataDB()
459 {
460  if (m_pCredentialsDB) {
461  delete m_pCredentialsDB;
462  m_pCredentialsDB = NULL;
463  }
464 }
465 
467 {
469 
470  if (!openMetaDataDB()) {
471  BLAME() << "Couldn't open metadata DB!";
472  return false;
473  }
474 
475  m_systemOpened = true;
476 
477  if (m_cryptoManager->fileSystemIsMounted()) {
478  if (!openSecretsDB()) {
479  BLAME() << "Failed to open secrets DB.";
480  /* Even if the secrets DB couldn't be opened, signond is still
481  * usable: that's why we return "true" anyways. */
482  }
483  } else {
484  /* The secrets DB will be opened as soon as the encrypted FS is
485  * mounted.
486  */
487  m_cryptoManager->mountFileSystem();
488  }
489 
490  return true;
491 }
492 
494 {
496 
498  return true;
499 
500  bool allClosed = true;
501  if (isSecretsDBOpen() && !closeSecretsDB())
502  allClosed = false;
503 
504  closeMetaDataDB();
505 
506  m_error = NoError;
507  m_systemOpened = false;
508  return allClosed;
509 }
510 
512 {
514 
515  if (m_systemOpened && !closeCredentialsSystem()) {
516  /* The close operation failed: we cannot proceed */
517  return false;
518  }
519 
520  BLAME() << "Not implemented";
521  return false;
522 }
523 
525 {
527 
528  return m_pCredentialsDB;
529 }
530 
532 {
533  return (m_keyHandler != 0) ? m_keyHandler->isReady() : true;
534 }
535 
536 void CredentialsAccessManager::onKeyInserted(const SignOn::Key key)
537 {
538  TRACE() << "Key inserted.";
539 
540  if (!m_keyHandler->keyIsAuthorized(key))
541  m_keyAuthorizer->queryKeyAuthorization(
542  key, AbstractKeyAuthorizer::KeyInserted);
543 }
544 
545 void CredentialsAccessManager::onLastAuthorizedKeyRemoved(const SignOn::Key key)
546 {
547  Q_UNUSED(key);
548  TRACE() << "All keys disabled. Closing secure storage.";
549  if (isSecretsDBOpen() || m_cryptoManager->fileSystemIsMounted())
550  if (!closeSecretsDB())
551  BLAME() << "Error occurred while closing secure storage.";
552 }
553 
554 void CredentialsAccessManager::onKeyRemoved(const SignOn::Key key)
555 {
556  TRACE() << "Key removed.";
557 
558  if (m_keyHandler->keyIsAuthorized(key)) {
559  if (!m_keyHandler->revokeKeyAuthorization(key)) {
560  BLAME() << "Revoking key authorization failed";
561  }
562  }
563 }
564 
565 void CredentialsAccessManager::onKeyAuthorizationQueried(const SignOn::Key key,
566  int result)
567 {
568  TRACE() << "result:" << result;
569 
570  if (result != AbstractKeyAuthorizer::Denied) {
571  KeyHandler::AuthorizeFlags flags = KeyHandler::None;
572  if (result == AbstractKeyAuthorizer::Exclusive) {
573  TRACE() << "Reformatting secure storage.";
574  flags |= KeyHandler::FormatStorage;
575  }
576 
577  if (!m_keyHandler->authorizeKey(key, flags)) {
578  BLAME() << "Authorization failed";
579  }
580  }
581 
582  replyToSecureStorageEventNotifiers();
583 }
584 
586 {
587  if (m_keyHandler == 0) return false;
588  return !m_keyHandler->insertedKeys().isEmpty();
589 }
590 
591 void CredentialsAccessManager::replyToSecureStorageEventNotifiers()
592 {
593  TRACE();
594  //Notify secure storage notifiers if any.
595  int eventType = SIGNON_SECURE_STORAGE_NOT_AVAILABLE;
596  if ((m_pCredentialsDB != 0) && m_pCredentialsDB->isSecretsDBOpen())
598 
599  // Signal objects that posted secure storage not available events
600  foreach (EventSender object, m_secureStorageEventNotifiers) {
601  if (object.isNull())
602  continue;
603 
604  SecureStorageEvent *secureStorageEvent =
605  new SecureStorageEvent((QEvent::Type)eventType);
606 
607  QCoreApplication::postEvent(
608  object.data(),
609  secureStorageEvent,
610  Qt::HighEventPriority);
611  }
612 
613  m_secureStorageEventNotifiers.clear();
614 }
615 
617 {
618  TRACE() << "Custom event received.";
619  if (event->type() != SIGNON_SECURE_STORAGE_NOT_AVAILABLE) {
620  QObject::customEvent(event);
621  return;
622  }
623 
624  SecureStorageEvent *localEvent =
625  static_cast<SecureStorageEvent *>(event);
626 
627  /* All senders of this event will receive a reply when
628  * the secure storage becomes available or an error occurs. */
629  m_secureStorageEventNotifiers.append(localEvent->m_sender);
630 
631  TRACE() << "Processing secure storage not available event.";
632  if ((localEvent == 0) || (m_pCredentialsDB == 0)) {
633  replyToSecureStorageEventNotifiers();
634  QObject::customEvent(event);
635  return;
636  }
637 
638  //Double check if the secrets DB is indeed unavailable
639  if (m_pCredentialsDB->isSecretsDBOpen()) {
640  replyToSecureStorageEventNotifiers();
641  QObject::customEvent(event);
642  return;
643  }
644 
645  SignOn::Key key; /* we don't specity any key */
646  m_keyAuthorizer->queryKeyAuthorization(key,
647  AbstractKeyAuthorizer::StorageNeeded);
648 
649  QObject::customEvent(event);
650 }
651 
652 void CredentialsAccessManager::onEncryptedFSMounted()
653 {
654  TRACE();
655  if (!credentialsSystemOpened()) return;
656 
657  if (!isSecretsDBOpen()) {
658  if (openSecretsDB()) {
659  TRACE() << "Secrets DB opened.";
660  } else {
661  BLAME() << "Failed to open secrets DB.";
662  }
663  } else {
664  BLAME() << "Secrets DB already opened?";
665  }
666 }
667 
668 void CredentialsAccessManager::onEncryptedFSUnmounting()
669 {
670  TRACE();
671  if (!credentialsSystemOpened()) return;
672 
673  if (isSecretsDBOpen()) {
674  m_pCredentialsDB->closeSecretsDB();
675  }
676 }
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)
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...
#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 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.