signon  8.42
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 (plugin->objectName() == m_CAMConfiguration.secretsStorageName()) {
345  SignOn::AbstractSecretsStorage *secretsStorage =
346  extension3->secretsStorage(this);
347  if (secretsStorage != 0) {
348  if (m_secretsStorage == 0) {
349  m_secretsStorage = secretsStorage;
350  extensionInUse = true;
351  } else {
352  TRACE() << "SecretsStorage already set";
353  delete secretsStorage;
354  }
355  }
356  }
357 
358  /* Instantiate this plugin's AccessControlManager only if it's the
359  * plugin requested in the config file. */
360  if (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 }