CAM: fix opening of secrets DB
[accounts-sso:vitalyrepins-signon.git] / src / signond / credentialsaccessmanager.cpp
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  *
7  * Contact: Aurel Popirtac <mailto:ext-Aurel.Popirtac@nokia.com>
8  * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * version 2.1 as published by the Free Software Foundation.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22  * 02110-1301 USA
23  */
24
25 #define SIGNON_ENABLE_UNSTABLE_APIS
26 #include "credentialsaccessmanager.h"
27
28 #include "default-key-authorizer.h"
29 #include "signond-common.h"
30
31 #include "SignOn/ExtensionInterface"
32 #include "SignOn/misc.h"
33
34 #include <QFile>
35 #include <QBuffer>
36
37
38 #define RETURN_IF_NOT_INITIALIZED(return_value)                  \
39     do {                                                         \
40         if (!m_isInitialized) {                                  \
41             m_error = NotInitialized;                            \
42             TRACE() << "CredentialsAccessManager not initialized."; \
43             return return_value;                                \
44         }                                                       \
45     } while (0)
46
47 using namespace SignonDaemonNS;
48 using namespace SignOn;
49
50 /* ---------------------- CAMConfiguration ---------------------- */
51
52 CAMConfiguration::CAMConfiguration()
53         : m_storagePath(QLatin1String(signonDefaultStoragePath)),
54           m_dbName(QLatin1String(signonDefaultDbName)),
55           m_useEncryption(signonDefaultUseEncryption),
56           m_fileSystemType(QLatin1String(signonDefaultFileSystemType)),
57           m_fileSystemSize(signonMinumumDbSize),
58           m_encryptionPassphrase(QByteArray())
59 {}
60
61 void CAMConfiguration::serialize(QIODevice *device)
62 {
63     if (device == NULL)
64         return;
65
66     if (!device->open(QIODevice::ReadWrite)) {
67         return;
68     }
69
70     QString buffer;
71     QTextStream stream(&buffer);
72     stream << "\n\n====== Credentials Access Manager Configuration ======\n\n";
73     stream << "File system mount name " << encryptedFSPath() << '\n';
74     stream << "File system format: " << m_fileSystemType << '\n';
75     stream << "File system size:" << m_fileSystemSize << "megabytes\n";
76
77     const char *usingEncryption = m_useEncryption ? "true" : "false";
78     stream << "Using encryption: " << usingEncryption << '\n';
79     stream << "Credentials database name: " << m_dbName << '\n';
80     stream << "======================================================\n\n";
81     device->write(buffer.toUtf8());
82     device->close();
83 }
84
85 QString CAMConfiguration::metadataDBPath() const
86 {
87     return m_storagePath + QDir::separator() + m_dbName;
88 }
89
90 QString CAMConfiguration::encryptedFSPath() const
91 {
92     return m_storagePath +
93         QDir::separator() +
94         QLatin1String(signonDefaultFileSystemName);
95 }
96
97 /* ---------------------- CredentialsAccessManager ---------------------- */
98
99 CredentialsAccessManager *CredentialsAccessManager::m_pInstance = NULL;
100
101 CredentialsAccessManager::CredentialsAccessManager(QObject *parent)
102         : QObject(parent),
103           m_isInitialized(false),
104           m_systemOpened(false),
105           m_error(NoError),
106           keyManagers(),
107           m_pCredentialsDB(NULL),
108           m_pCryptoFileSystemManager(NULL),
109           m_keyHandler(NULL),
110           m_keyAuthorizer(NULL),
111           m_CAMConfiguration(CAMConfiguration())
112 {
113     m_keyHandler = new SignOn::KeyHandler(this);
114 }
115
116 CredentialsAccessManager::~CredentialsAccessManager()
117 {
118     closeCredentialsSystem();
119
120     m_pInstance = NULL;
121 }
122
123 CredentialsAccessManager *CredentialsAccessManager::instance(QObject *parent)
124 {
125     if (!m_pInstance)
126         m_pInstance = new CredentialsAccessManager(parent);
127
128     return m_pInstance;
129 }
130
131 void CredentialsAccessManager::finalize()
132 {
133     if (m_systemOpened)
134         closeCredentialsSystem();
135
136     if (m_pCryptoFileSystemManager)
137         delete m_pCryptoFileSystemManager;
138
139     // Disconnect all key managers
140     foreach (SignOn::AbstractKeyManager *keyManager, keyManagers)
141         keyManager->disconnect();
142
143     m_isInitialized = false;
144     m_error = NoError;
145 }
146
147 bool CredentialsAccessManager::init(const CAMConfiguration &camConfiguration)
148 {
149     if (m_isInitialized) {
150         TRACE() << "CAM already initialized.";
151         m_error = AlreadyInitialized;
152         return false;
153     }
154
155     m_CAMConfiguration = camConfiguration;
156
157     QBuffer config;
158     m_CAMConfiguration.serialize(&config);
159     TRACE() << "\n\nInitualizing CredentialsAccessManager with configuration: " << config.data();
160
161     if (!createStorageDir()) {
162         BLAME() << "Failed to create storage directory.";
163         return false;
164     }
165
166     if (m_CAMConfiguration.m_useEncryption) {
167         //Initialize CryptoManager
168         m_pCryptoFileSystemManager = new SignOn::CryptoManager(this);
169         QObject::connect(m_pCryptoFileSystemManager, SIGNAL(fileSystemMounted()),
170                          this, SLOT(onEncryptedFSMounted()));
171         QObject::connect(m_pCryptoFileSystemManager, SIGNAL(fileSystemUnmounting()),
172                          this, SLOT(onEncryptedFSUnmounting()));
173         m_pCryptoFileSystemManager->setFileSystemPath(m_CAMConfiguration.encryptedFSPath());
174         m_pCryptoFileSystemManager->setFileSystemSize(m_CAMConfiguration.m_fileSystemSize);
175         m_pCryptoFileSystemManager->setFileSystemType(m_CAMConfiguration.m_fileSystemType);
176
177         if (m_keyAuthorizer == 0) {
178             TRACE() << "No key authorizer set, using default";
179             m_keyAuthorizer = new DefaultKeyAuthorizer(m_keyHandler, this);
180         }
181         QObject::connect(m_keyAuthorizer,
182                          SIGNAL(keyAuthorizationQueried(const SignOn::Key,int)),
183                          this,
184                          SLOT(onKeyAuthorizationQueried(const SignOn::Key,int)));
185
186         /* These signal connections should be done after instantiating the
187          * KeyAuthorizer, so that the KeyAuthorizer's slot will be called
188          * first (or we could connect to them in queued mode)
189          */
190         QObject::connect(m_keyHandler, SIGNAL(ready()),
191                          this, SIGNAL(credentialsSystemReady()));
192         QObject::connect(m_keyHandler, SIGNAL(keyInserted(SignOn::Key)),
193                          this, SLOT(onKeyInserted(SignOn::Key)));
194         QObject::connect(m_keyHandler,
195                          SIGNAL(lastAuthorizedKeyRemoved(SignOn::Key)),
196                          this,
197                          SLOT(onLastAuthorizedKeyRemoved(SignOn::Key)));
198         QObject::connect(m_keyHandler, SIGNAL(keyRemoved(SignOn::Key)),
199                          this, SLOT(onKeyRemoved(SignOn::Key)));
200         m_keyHandler->initialize(m_pCryptoFileSystemManager, keyManagers);
201     }
202
203     m_isInitialized = true;
204     m_error = NoError;
205
206     TRACE() << "CredentialsAccessManager successfully initialized...";
207     return true;
208 }
209
210 void CredentialsAccessManager::addKeyManager(
211     SignOn::AbstractKeyManager *keyManager)
212 {
213     keyManagers.append(keyManager);
214 }
215
216 bool CredentialsAccessManager::initExtension(QObject *plugin)
217 {
218     bool extensionInUse = false;
219
220     SignOn::ExtensionInterface *extension;
221     SignOn::ExtensionInterface2 *extension2;
222
223     extension2 = qobject_cast<SignOn::ExtensionInterface2 *>(plugin);
224     if (extension2 != 0)
225         extension = extension2;
226     else
227         extension = qobject_cast<SignOn::ExtensionInterface *>(plugin);
228
229     if (extension == 0) {
230         qWarning() << "Plugin instance is not an ExtensionInterface";
231         return false;
232     }
233
234     SignOn::AbstractKeyManager *keyManager = extension->keyManager(this);
235     if (keyManager) {
236         addKeyManager(keyManager);
237         extensionInUse = true;
238     }
239
240     /* Check if the extension implements the new interface and provides a key
241      * authorizer. */
242     if (extension2 != 0) {
243         SignOn::AbstractKeyAuthorizer *keyAuthorizer =
244             extension2->keyAuthorizer(m_keyHandler, this);
245         if (keyAuthorizer != 0) {
246             if (m_keyAuthorizer == 0) {
247                 m_keyAuthorizer = keyAuthorizer;
248                 extensionInUse = true;
249             } else {
250                 TRACE() << "Key authorizer already set";
251             }
252         }
253     }
254
255     return extensionInUse;
256 }
257
258 bool CredentialsAccessManager::openSecretsDB()
259 {
260     //todo remove this variable after LUKS implementation becomes stable.
261     QString dbPath;
262
263     if (m_CAMConfiguration.m_useEncryption) {
264         dbPath = m_pCryptoFileSystemManager->fileSystemMountPath()
265             + QDir::separator()
266             + m_CAMConfiguration.m_dbName;
267
268         if (!m_pCryptoFileSystemManager->fileSystemIsMounted()) {
269             /* Do not attempt to mount the FS; we know that it will be mounted
270              * automatically, as soon as some encryption keys are provided */
271             m_error = CredentialsDbNotMounted;
272             return false;
273         }
274     } else {
275         dbPath = m_CAMConfiguration.metadataDBPath() + QLatin1String(".creds");
276     }
277
278     TRACE() << "Database name: [" << dbPath << "]";
279
280     if (!m_pCredentialsDB->openSecretsDB(dbPath))
281         return false;
282
283     m_error = NoError;
284     return true;
285 }
286
287 bool CredentialsAccessManager::isSecretsDBOpen()
288 {
289     return m_pCredentialsDB->isSecretsDBOpen();
290 }
291
292 bool CredentialsAccessManager::closeSecretsDB()
293 {
294     m_pCredentialsDB->closeSecretsDB();
295
296     if (m_CAMConfiguration.m_useEncryption) {
297         if (!m_pCryptoFileSystemManager->unmountFileSystem()) {
298             m_error = CredentialsDbUnmountFailed;
299             return false;
300         }
301     }
302
303     return true;
304 }
305
306 bool CredentialsAccessManager::createStorageDir()
307 {
308     QString dbPath = m_CAMConfiguration.metadataDBPath();
309
310     QFileInfo fileInfo(dbPath);
311     if (!fileInfo.exists()) {
312         QDir storageDir(fileInfo.dir());
313         if (!storageDir.mkpath(storageDir.path())) {
314             BLAME() << "Could not create storage directory:" <<
315                 storageDir.path();
316             m_error = CredentialsDbSetupFailed;
317             return false;
318         }
319         setUserOwnership(storageDir.path());
320     }
321     return true;
322
323 }
324 bool CredentialsAccessManager::openMetaDataDB()
325 {
326     QString dbPath = m_CAMConfiguration.metadataDBPath();
327
328     m_pCredentialsDB = new CredentialsDB(dbPath);
329
330     if (!m_pCredentialsDB->init()) {
331         m_error = CredentialsDbConnectionError;
332         return false;
333     }
334
335     return true;
336 }
337
338 void CredentialsAccessManager::closeMetaDataDB()
339 {
340     if (m_pCredentialsDB) {
341         delete m_pCredentialsDB;
342         m_pCredentialsDB = NULL;
343     }
344 }
345
346 bool CredentialsAccessManager::openCredentialsSystem()
347 {
348     RETURN_IF_NOT_INITIALIZED(false);
349
350     if (!openMetaDataDB()) {
351         BLAME() << "Couldn't open metadata DB!";
352         return false;
353     }
354
355     m_systemOpened = true;
356
357     if (m_pCryptoFileSystemManager == 0 ||
358         m_pCryptoFileSystemManager->fileSystemIsMounted()) {
359         if (!openSecretsDB()) {
360             BLAME() << "Failed to open secrets DB.";
361             /* Even if the secrets DB couldn't be opened, signond is still
362              * usable: that's why we return "true" anyways. */
363         }
364     } else {
365         /* The secrets DB will be opened as soon as the encrypted FS is
366          * mounted.
367          */
368         m_pCryptoFileSystemManager->mountFileSystem();
369     }
370
371     return true;
372 }
373
374 bool CredentialsAccessManager::closeCredentialsSystem()
375 {
376     RETURN_IF_NOT_INITIALIZED(false);
377
378     if (!credentialsSystemOpened())
379         return true;
380
381     bool allClosed = true;
382     if (isSecretsDBOpen() && !closeSecretsDB())
383         allClosed = false;
384
385     closeMetaDataDB();
386
387     m_error = NoError;
388     m_systemOpened = false;
389     return allClosed;
390 }
391
392 bool CredentialsAccessManager::deleteCredentialsSystem()
393 {
394     RETURN_IF_NOT_INITIALIZED(false);
395
396     if (m_systemOpened && !closeCredentialsSystem()) {
397         /* The close operation failed: we cannot proceed */
398         return false;
399     }
400
401     m_error = NoError;
402
403     if (m_CAMConfiguration.m_useEncryption) {
404         if (!m_pCryptoFileSystemManager->deleteFileSystem())
405             m_error = CredentialsDbDeletionFailed;
406     } else {
407         QFile dbFile(m_CAMConfiguration.m_dbName);
408         if (dbFile.exists()) {
409             if (!dbFile.remove())
410                 m_error = CredentialsDbDeletionFailed;
411         }
412     }
413
414     return m_error == NoError;
415 }
416
417 CredentialsDB *CredentialsAccessManager::credentialsDB() const
418 {
419     RETURN_IF_NOT_INITIALIZED(NULL);
420
421     return m_pCredentialsDB;
422 }
423
424 bool CredentialsAccessManager::isCredentialsSystemReady() const
425 {
426     return (m_keyHandler != 0) ? m_keyHandler->isReady() : true;
427 }
428
429 void CredentialsAccessManager::onKeyInserted(const SignOn::Key key)
430 {
431     TRACE() << "Key inserted.";
432
433     if (!m_keyHandler->keyIsAuthorized(key))
434         m_keyAuthorizer->queryKeyAuthorization(
435             key, AbstractKeyAuthorizer::KeyInserted);
436 }
437
438 void CredentialsAccessManager::onLastAuthorizedKeyRemoved(const SignOn::Key key)
439 {
440     Q_UNUSED(key);
441     TRACE() << "All keys disabled. Closing secure storage.";
442     if (isSecretsDBOpen() || m_pCryptoFileSystemManager->fileSystemIsMounted())
443         if (!closeSecretsDB())
444             BLAME() << "Error occurred while closing secure storage.";
445 }
446
447 void CredentialsAccessManager::onKeyRemoved(const SignOn::Key key)
448 {
449     TRACE() << "Key removed.";
450
451     if (m_keyHandler->keyIsAuthorized(key)) {
452         if (!m_keyHandler->revokeKeyAuthorization(key)) {
453             BLAME() << "Revoking key authorization failed";
454         }
455     }
456 }
457
458 void CredentialsAccessManager::onKeyAuthorizationQueried(const SignOn::Key key,
459                                                          int result)
460 {
461     TRACE() << "result:" << result;
462
463     if (result != AbstractKeyAuthorizer::Denied) {
464         KeyHandler::AuthorizeFlags flags = KeyHandler::None;
465         if (result == AbstractKeyAuthorizer::Exclusive) {
466             TRACE() << "Reformatting secure storage.";
467             flags |= KeyHandler::FormatStorage;
468         }
469
470         if (!m_keyHandler->authorizeKey(key, flags)) {
471             BLAME() << "Authorization failed";
472         }
473     }
474
475     replyToSecureStorageEventNotifiers();
476 }
477
478 bool CredentialsAccessManager::keysAvailable() const
479 {
480     if (m_keyHandler == 0) return false;
481     return !m_keyHandler->insertedKeys().isEmpty();
482 }
483
484 void CredentialsAccessManager::replyToSecureStorageEventNotifiers()
485 {
486     TRACE();
487     //Notify secure storage notifiers if any.
488     int eventType = SIGNON_SECURE_STORAGE_NOT_AVAILABLE;
489     if ((m_pCredentialsDB != 0) && m_pCredentialsDB->isSecretsDBOpen())
490         eventType = SIGNON_SECURE_STORAGE_AVAILABLE;
491
492     // Signal objects that posted secure storage not available events
493     foreach (EventSender object, m_secureStorageEventNotifiers) {
494         if (object.isNull())
495             continue;
496
497         SecureStorageEvent *secureStorageEvent =
498             new SecureStorageEvent((QEvent::Type)eventType);
499
500         QCoreApplication::postEvent(
501             object.data(),
502             secureStorageEvent,
503             Qt::HighEventPriority);
504     }
505
506     m_secureStorageEventNotifiers.clear();
507 }
508
509 void CredentialsAccessManager::customEvent(QEvent *event)
510 {
511     TRACE() << "Custom event received.";
512     if (event->type() != SIGNON_SECURE_STORAGE_NOT_AVAILABLE) {
513         QObject::customEvent(event);
514         return;
515     }
516
517     SecureStorageEvent *localEvent =
518         static_cast<SecureStorageEvent *>(event);
519
520     /* All senders of this event will receive a reply when
521      * the secure storage becomes available or an error occurs. */
522     m_secureStorageEventNotifiers.append(localEvent->m_sender);
523
524     TRACE() << "Processing secure storage not available event.";
525     if ((localEvent == 0) || (m_pCredentialsDB == 0)) {
526         replyToSecureStorageEventNotifiers();
527         QObject::customEvent(event);
528         return;
529     }
530
531     //Double check if the secrets DB is indeed unavailable
532     if (m_pCredentialsDB->isSecretsDBOpen()) {
533         replyToSecureStorageEventNotifiers();
534         QObject::customEvent(event);
535         return;
536     }
537
538     SignOn::Key key; /* we don't specity any key */
539     m_keyAuthorizer->queryKeyAuthorization(key,
540                                            AbstractKeyAuthorizer::StorageNeeded);
541
542     QObject::customEvent(event);
543 }
544
545 void CredentialsAccessManager::onEncryptedFSMounted()
546 {
547     TRACE();
548     if (!credentialsSystemOpened()) return;
549
550     if (!isSecretsDBOpen()) {
551         if (openSecretsDB()) {
552             TRACE() << "Secrets DB opened.";
553         } else {
554             BLAME() << "Failed to open secrets DB.";
555         }
556     } else {
557         BLAME() << "Secrets DB already opened?";
558     }
559 }
560
561 void CredentialsAccessManager::onEncryptedFSUnmounting()
562 {
563     TRACE();
564     if (!credentialsSystemOpened()) return;
565
566     if (isSecretsDBOpen()) {
567         m_pCredentialsDB->closeSecretsDB();
568     }
569 }
570