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 }