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