CAM: Abort inititalization if directory can't be created
[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
30 #include <QFile>
31 #include <QBuffer>
32
33
34 #define RETURN_IF_NOT_INITIALIZED(return_value)                  \
35     do {                                                         \
36         if (!m_isInitialized) {                                  \
37             m_error = NotInitialized;                            \
38             TRACE() << "CredentialsAccessManager not initialized."; \
39             return return_value;                                \
40         }                                                       \
41     } while (0)
42
43 using namespace SignonDaemonNS;
44
45 /* ---------------------- CAMConfiguration ---------------------- */
46
47 CAMConfiguration::CAMConfiguration()
48         : m_dbName(QLatin1String(signonDefaultDbName)),
49           m_useEncryption(signonDefaultUseEncryption),
50           m_dbFileSystemPath(
51                 QLatin1String(signonDefaultStoragePath)
52                 + QDir::separator()
53                 + QLatin1String(signonDefaultFileSystemName)),
54           m_fileSystemType(QLatin1String(signonDefaultFileSystemType)),
55           m_fileSystemSize(signonMinumumDbSize),
56           m_encryptionPassphrase(QByteArray())
57 {}
58
59 void CAMConfiguration::serialize(QIODevice *device)
60 {
61     if (device == NULL)
62         return;
63
64     if (!device->open(QIODevice::ReadWrite)) {
65         return;
66     }
67
68     QString buffer;
69     QTextStream stream(&buffer);
70     stream << "\n\n====== Credentials Access Manager Configuration ======\n\n";
71     stream << "File system mount name " << m_dbFileSystemPath << '\n';
72     stream << "File system format: " << m_fileSystemType << '\n';
73     stream << "File system size:" << m_fileSystemSize << "megabytes\n";
74
75     const char *usingEncryption = m_useEncryption ? "true" : "false";
76     stream << "Using encryption: " << usingEncryption << '\n';
77     stream << "Credentials database name: " << m_dbName << '\n';
78     stream << "======================================================\n\n";
79     device->write(buffer.toUtf8());
80     device->close();
81 }
82
83 /* ---------------------- CredentialsAccessManager ---------------------- */
84
85 CredentialsAccessManager *CredentialsAccessManager::m_pInstance = NULL;
86
87 CredentialsAccessManager::CredentialsAccessManager(QObject *parent)
88         : QObject(parent),
89           m_isInitialized(false),
90           m_accessCodeFetched(false),
91           m_systemOpened(false),
92           m_error(NoError),
93           keyManagers(),
94           m_pCredentialsDB(NULL),
95           m_pCryptoFileSystemManager(NULL),
96           m_CAMConfiguration(CAMConfiguration())
97 {
98 }
99
100 CredentialsAccessManager::~CredentialsAccessManager()
101 {
102     closeCredentialsSystem();
103
104     m_pInstance = NULL;
105 }
106
107 CredentialsAccessManager *CredentialsAccessManager::instance(QObject *parent)
108 {
109     if (!m_pInstance)
110         m_pInstance = new CredentialsAccessManager(parent);
111
112     return m_pInstance;
113 }
114
115 void CredentialsAccessManager::finalize()
116 {
117     if (m_systemOpened)
118         closeCredentialsSystem();
119
120     if (m_pCryptoFileSystemManager)
121         delete m_pCryptoFileSystemManager;
122
123     m_accessCodeFetched = false;
124     m_isInitialized = false;
125     m_error = NoError;
126 }
127
128 bool CredentialsAccessManager::init(const CAMConfiguration &camConfiguration)
129 {
130     if (m_isInitialized) {
131         TRACE() << "CAM already initialized.";
132         m_error = AlreadyInitialized;
133         return false;
134     }
135
136     m_CAMConfiguration = camConfiguration;
137
138     QBuffer config;
139     m_CAMConfiguration.serialize(&config);
140     TRACE() << "\n\nInitualizing CredentialsAccessManager with configuration: " << config.data();
141
142     if (m_CAMConfiguration.m_useEncryption) {
143         //Initialize CryptoManager
144         m_pCryptoFileSystemManager = new CryptoManager(this);
145         m_pCryptoFileSystemManager->setFileSystemPath(m_CAMConfiguration.m_dbFileSystemPath);
146         m_pCryptoFileSystemManager->setFileSystemSize(m_CAMConfiguration.m_fileSystemSize);
147         m_pCryptoFileSystemManager->setFileSystemType(m_CAMConfiguration.m_fileSystemType);
148
149         // Initialize all key managers
150         foreach (SignOn::AbstractKeyManager *keyManager, keyManagers) {
151             connect(keyManager,
152                     SIGNAL(keyInserted(const SignOn::Key)),
153                     SLOT(onKeyInserted(const SignOn::Key)));
154             connect(keyManager,
155                     SIGNAL(keyDisabled(const SignOn::Key)),
156                     SLOT(onKeyDisabled(const SignOn::Key)));
157             connect(keyManager,
158                     SIGNAL(keyRemoved(const SignOn::Key)),
159                     SLOT(onKeyRemoved(const SignOn::Key)));
160             connect(keyManager,
161                     SIGNAL(keyAuthorized(const SignOn::Key, bool)),
162                     SLOT(onKeyAuthorized(const SignOn::Key, bool)));
163             keyManager->setup();
164         }
165     }
166
167     m_isInitialized = true;
168     m_error = NoError;
169
170     TRACE() << "CredentialsAccessManager successfully initialized...";
171     return true;
172 }
173
174 void CredentialsAccessManager::addKeyManager(
175     SignOn::AbstractKeyManager *keyManager)
176 {
177     keyManagers.append(keyManager);
178 }
179
180 bool CredentialsAccessManager::openSecretsDB()
181 {
182     //todo remove this variable after LUKS implementation becomes stable.
183     QString dbPath;
184
185     if (m_CAMConfiguration.m_useEncryption) {
186         dbPath = m_pCryptoFileSystemManager->fileSystemMountPath()
187             + QDir::separator()
188             + m_CAMConfiguration.m_dbName;
189
190         if (!fileSystemDeployed()) {
191             if (!deployCredentialsSystem())
192                 return false;
193         }
194
195         if (!m_pCryptoFileSystemManager->fileSystemMounted()) {
196             if (!m_pCryptoFileSystemManager->mountFileSystem()) {
197                 m_error = CredentialsDbMountFailed;
198                 return false;
199             }
200         }
201     } else {
202         QFileInfo fInfo(m_CAMConfiguration.m_dbFileSystemPath);
203         QDir storageDir = fInfo.dir();
204         if (!storageDir.exists()) {
205             if (!storageDir.mkpath(storageDir.path()))
206                 BLAME() << "Could not create storage directory!!!";
207         }
208
209         dbPath = storageDir.path()
210                  + QDir::separator()
211                  + m_CAMConfiguration.m_dbName
212                  + QLatin1String(".creds");
213     }
214
215     TRACE() << "Database name: [" << dbPath << "]";
216
217     if (!m_pCredentialsDB->openSecretsDB(dbPath))
218         return false;
219
220     m_error = NoError;
221     return true;
222 }
223
224 bool CredentialsAccessManager::isSecretsDBOpen()
225 {
226     return m_pCredentialsDB->isSecretsDBOpen();
227 }
228
229 bool CredentialsAccessManager::closeSecretsDB()
230 {
231     m_pCredentialsDB->closeSecretsDB();
232
233     if (m_CAMConfiguration.m_useEncryption) {
234         if (!m_pCryptoFileSystemManager->unmountFileSystem()) {
235             m_error = CredentialsDbUnmountFailed;
236             return false;
237         }
238     }
239
240     return true;
241 }
242
243 bool CredentialsAccessManager::openMetaDataDB()
244 {
245     QFileInfo fInfo(m_CAMConfiguration.m_dbFileSystemPath);
246     QDir storageDir = fInfo.dir();
247     if (!storageDir.exists()) {
248         if (!storageDir.mkpath(storageDir.path())) {
249             BLAME() << "Could not create storage directory!!!";
250             m_error = CredentialsDbSetupFailed;
251             false;
252         }
253     }
254
255     QString dbPath = storageDir.path()
256              + QDir::separator()
257              + m_CAMConfiguration.m_dbName;
258     m_pCredentialsDB = new CredentialsDB(dbPath);
259
260     if (!m_pCredentialsDB->init()) {
261         m_error = CredentialsDbConnectionError;
262         return false;
263     }
264
265     return true;
266 }
267
268 void CredentialsAccessManager::closeMetaDataDB()
269 {
270     if (m_pCredentialsDB) {
271         delete m_pCredentialsDB;
272         m_pCredentialsDB = NULL;
273     }
274 }
275
276 bool CredentialsAccessManager::openCredentialsSystem()
277 {
278     RETURN_IF_NOT_INITIALIZED(false);
279
280     if (!openMetaDataDB()) {
281         BLAME() << "Couldn't open metadata DB!";
282         return false;
283     }
284
285     if (!openSecretsDB()) {
286         BLAME() << "Failed to open secrets DB.";
287         /* Even if the secrets DB couldn't be opened, signond is still usable:
288          * that's why we return "true" anyways. */
289     }
290
291     m_systemOpened = true;
292     return true;
293 }
294
295 bool CredentialsAccessManager::closeCredentialsSystem()
296 {
297     RETURN_IF_NOT_INITIALIZED(false);
298
299     if (!closeSecretsDB())
300         return false;
301     closeMetaDataDB();
302
303     m_error = NoError;
304     m_systemOpened = false;
305     return true;
306 }
307
308 bool CredentialsAccessManager::deleteCredentialsSystem()
309 {
310     RETURN_IF_NOT_INITIALIZED(false);
311
312     if (m_systemOpened && !closeCredentialsSystem()) {
313         /* The close operation failed: we cannot proceed */
314         return false;
315     }
316
317     m_error = NoError;
318
319     if (m_CAMConfiguration.m_useEncryption) {
320         if (!m_pCryptoFileSystemManager->deleteFileSystem())
321             m_error = CredentialsDbDeletionFailed;
322     } else {
323         QFile dbFile(m_CAMConfiguration.m_dbName);
324         if (dbFile.exists()) {
325             if (!dbFile.remove())
326                 m_error = CredentialsDbDeletionFailed;
327         }
328     }
329
330     return m_error == NoError;
331 }
332
333 bool CredentialsAccessManager::setMasterEncryptionKey(const QByteArray &newKey,
334                                                       const QByteArray &existingKey)
335 {
336     if (!m_CAMConfiguration.m_useEncryption)
337         return false;
338
339     /* Clear this for security reasons - SIM data must not be stored
340        using an deprecated master key
341     */
342     m_CAMConfiguration.m_encryptionPassphrase.clear();
343
344     if (!encryptionKeyCanMountFS(existingKey)) {
345         BLAME() << "Existing lock code check failed.";
346         return false;
347     }
348
349     if (!m_pCryptoFileSystemManager->addEncryptionKey(
350             newKey, existingKey)) {
351         BLAME() << "Failed to add new device lock code.";
352         return false;
353     }
354
355     if (!m_pCryptoFileSystemManager->removeEncryptionKey(existingKey, newKey)) {
356         BLAME() << "Failed to remove old device lock code.";
357         return false;
358     }
359
360     return true;
361 }
362
363 bool CredentialsAccessManager::lockSecureStorage(const QByteArray &lockData)
364 {
365     // TODO - implement this, research how to.
366     Q_UNUSED(lockData)
367     return false;
368 }
369
370 CredentialsDB *CredentialsAccessManager::credentialsDB() const
371 {
372     RETURN_IF_NOT_INITIALIZED(NULL);
373
374     return m_pCredentialsDB;
375 }
376
377 bool CredentialsAccessManager::deployCredentialsSystem()
378 {
379     if (m_CAMConfiguration.m_useEncryption) {
380         if (!m_pCryptoFileSystemManager->setupFileSystem()) {
381             m_error = CredentialsDbSetupFailed;
382             return false;
383         }
384     }
385     return true;
386 }
387
388 bool CredentialsAccessManager::fileSystemDeployed()
389 {
390     return QFile::exists(m_pCryptoFileSystemManager->fileSystemPath());
391 }
392
393 bool CredentialsAccessManager::encryptionKeyCanMountFS(const QByteArray &key)
394 {
395     if (!fileSystemDeployed()) {
396         TRACE() << "Secure FS not deployed";
397         return false;
398     }
399
400     if (m_pCryptoFileSystemManager->encryptionKeyInUse(key)) {
401         TRACE() << "SIM data already in use.";
402         if (m_pCryptoFileSystemManager->fileSystemMounted()) {
403
404             m_pCryptoFileSystemManager->setEncryptionKey(key);
405
406             if (!isSecretsDBOpen()) {
407                 if (openSecretsDB()) {
408                     TRACE() << "Credentials system opened.";
409                 } else {
410                     BLAME() << "Failed to open credentials system.";
411                 }
412             } else {
413                 TRACE() << "Credentials system already opened.";
414             }
415         }
416         return true;
417     } else {
418         return false;
419     }
420 }
421
422 void CredentialsAccessManager::onKeyInserted(const SignOn::Key key)
423 {
424     TRACE() << "Key:" << key.toHex();
425
426     if (key.isEmpty()) return;
427
428     /* The `key in use` check will attempt to mount using the new key if
429        the file system is not already mounted
430     */
431     if (encryptionKeyCanMountFS(key)) {
432         TRACE() << "SIM data already in use.";
433         authorizedKeys << key;
434         return;
435     }
436
437     /* We got here because the inserted key is totally new to the CAM.
438      * Let's see if any key manager wants to authorize it: we call
439      * authorizeKey() on each of them, and continue processing the key when
440      * the keyAuthorized() signal comes.
441      */
442     foreach (SignOn::AbstractKeyManager *keyManager, keyManagers) {
443         keyManager->authorizeKey(key);
444     }
445 }
446
447 void CredentialsAccessManager::onKeyDisabled(const SignOn::Key key)
448 {
449     TRACE() << "Key:" << key.toHex();
450
451     if (authorizedKeys.removeAll(key) == 0) {
452         TRACE() << "Key was already disabled";
453         return;
454     }
455
456     if (authorizedKeys.isEmpty()) {
457         TRACE() << "All keys removed, closing secure storage.";
458         if (isSecretsDBOpen())
459             if (!closeSecretsDB())
460                 BLAME() << "Error occurred while closing secure storage.";
461
462         TRACE() << "Querying for keys.";
463         foreach (SignOn::AbstractKeyManager *keyManager, keyManagers) {
464             keyManager->queryKeys();
465         }
466     }
467 }
468
469 void CredentialsAccessManager::onKeyRemoved(const SignOn::Key key)
470 {
471     TRACE() << "Key:" << key.toHex();
472
473     // Make sure the key is disabled:
474     onKeyDisabled(key);
475
476     if (!encryptionKeyCanMountFS(key)) {
477         TRACE() << "Key is not known to the CryptoManager.";
478         return;
479     }
480
481     if (authorizedKeys.isEmpty()) {
482         BLAME() << "Cannot remove key: no authorized keys";
483         return;
484     }
485
486     SignOn::Key authorizedKey = authorizedKeys.first();
487     if (!m_pCryptoFileSystemManager->removeEncryptionKey(key, authorizedKey)) {
488         BLAME() << "Failed to remove key.";
489     } else {
490         TRACE() << "Key successfully removed.";
491     }
492 }
493
494 void CredentialsAccessManager::onKeyAuthorized(const SignOn::Key key,
495                                                bool authorized)
496 {
497     TRACE() << "Key:" << key.toHex() << "Authorized:" << authorized;
498
499     if (!authorized) return;
500
501     if (encryptionKeyCanMountFS(key)) {
502         TRACE() << "Encryption key already in use.";
503         authorizedKeys << key;
504         return;
505     }
506
507     if (m_pCryptoFileSystemManager->fileSystemMounted()) {
508         /* if the secure FS is already mounted, add the new key to it */
509         if (authorizedKeys.isEmpty()) {
510             BLAME() << "No authorized keys: cannot add new key";
511             return;
512         }
513
514         SignOn::Key authorizedKey = authorizedKeys.first();
515         if (m_pCryptoFileSystemManager->addEncryptionKey(key, authorizedKey)) {
516             TRACE() << "Encryption key successfullyadded into the CryptoManager.";
517             m_pCryptoFileSystemManager->setEncryptionKey(key);
518             authorizedKeys << key;
519         } else {
520             BLAME() << "Could not store encryption key.";
521         }
522     } else if (!fileSystemDeployed()) {
523         /* if the secure FS does not exist, create it and use this new key to
524          * initialize it */
525         m_pCryptoFileSystemManager->setEncryptionKey(key);
526         m_accessCodeFetched = true;
527         if (openSecretsDB()) {
528             authorizedKeys << key;
529         } else {
530             BLAME() << "Couldn't create the secure FS";
531         }
532     } else {
533         BLAME() << "Secure FS already created with another set of keys";
534     }
535 }
536