Don't crash if credentials system is closed twice.
[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                 storageDir.path();
251             m_error = CredentialsDbSetupFailed;
252             return false;
253         }
254     }
255
256     QString dbPath = storageDir.path()
257              + QDir::separator()
258              + m_CAMConfiguration.m_dbName;
259     m_pCredentialsDB = new CredentialsDB(dbPath);
260
261     if (!m_pCredentialsDB->init()) {
262         m_error = CredentialsDbConnectionError;
263         return false;
264     }
265
266     return true;
267 }
268
269 void CredentialsAccessManager::closeMetaDataDB()
270 {
271     if (m_pCredentialsDB) {
272         delete m_pCredentialsDB;
273         m_pCredentialsDB = NULL;
274     }
275 }
276
277 bool CredentialsAccessManager::openCredentialsSystem()
278 {
279     RETURN_IF_NOT_INITIALIZED(false);
280
281     if (!openMetaDataDB()) {
282         BLAME() << "Couldn't open metadata DB!";
283         return false;
284     }
285
286     if (!openSecretsDB()) {
287         BLAME() << "Failed to open secrets DB.";
288         /* Even if the secrets DB couldn't be opened, signond is still usable:
289          * that's why we return "true" anyways. */
290     }
291
292     m_systemOpened = true;
293     return true;
294 }
295
296 bool CredentialsAccessManager::closeCredentialsSystem()
297 {
298     RETURN_IF_NOT_INITIALIZED(false);
299
300     if (!credentialsSystemOpened())
301         return true;
302
303     if (!closeSecretsDB())
304         return false;
305     closeMetaDataDB();
306
307     m_error = NoError;
308     m_systemOpened = false;
309     return true;
310 }
311
312 bool CredentialsAccessManager::deleteCredentialsSystem()
313 {
314     RETURN_IF_NOT_INITIALIZED(false);
315
316     if (m_systemOpened && !closeCredentialsSystem()) {
317         /* The close operation failed: we cannot proceed */
318         return false;
319     }
320
321     m_error = NoError;
322
323     if (m_CAMConfiguration.m_useEncryption) {
324         if (!m_pCryptoFileSystemManager->deleteFileSystem())
325             m_error = CredentialsDbDeletionFailed;
326     } else {
327         QFile dbFile(m_CAMConfiguration.m_dbName);
328         if (dbFile.exists()) {
329             if (!dbFile.remove())
330                 m_error = CredentialsDbDeletionFailed;
331         }
332     }
333
334     return m_error == NoError;
335 }
336
337 bool CredentialsAccessManager::setMasterEncryptionKey(const QByteArray &newKey,
338                                                       const QByteArray &existingKey)
339 {
340     if (!m_CAMConfiguration.m_useEncryption)
341         return false;
342
343     /* Clear this for security reasons - SIM data must not be stored
344        using an deprecated master key
345     */
346     m_CAMConfiguration.m_encryptionPassphrase.clear();
347
348     if (!encryptionKeyCanMountFS(existingKey)) {
349         BLAME() << "Existing lock code check failed.";
350         return false;
351     }
352
353     if (!m_pCryptoFileSystemManager->addEncryptionKey(
354             newKey, existingKey)) {
355         BLAME() << "Failed to add new device lock code.";
356         return false;
357     }
358
359     if (!m_pCryptoFileSystemManager->removeEncryptionKey(existingKey, newKey)) {
360         BLAME() << "Failed to remove old device lock code.";
361         return false;
362     }
363
364     return true;
365 }
366
367 bool CredentialsAccessManager::lockSecureStorage(const QByteArray &lockData)
368 {
369     // TODO - implement this, research how to.
370     Q_UNUSED(lockData)
371     return false;
372 }
373
374 CredentialsDB *CredentialsAccessManager::credentialsDB() const
375 {
376     RETURN_IF_NOT_INITIALIZED(NULL);
377
378     return m_pCredentialsDB;
379 }
380
381 bool CredentialsAccessManager::deployCredentialsSystem()
382 {
383     if (m_CAMConfiguration.m_useEncryption) {
384         if (!m_pCryptoFileSystemManager->setupFileSystem()) {
385             m_error = CredentialsDbSetupFailed;
386             return false;
387         }
388     }
389     return true;
390 }
391
392 bool CredentialsAccessManager::fileSystemDeployed()
393 {
394     return QFile::exists(m_pCryptoFileSystemManager->fileSystemPath());
395 }
396
397 bool CredentialsAccessManager::encryptionKeyCanMountFS(const QByteArray &key)
398 {
399     if (!fileSystemDeployed()) {
400         TRACE() << "Secure FS not deployed";
401         return false;
402     }
403
404     if (m_pCryptoFileSystemManager->encryptionKeyInUse(key)) {
405         TRACE() << "SIM data already in use.";
406         if (m_pCryptoFileSystemManager->fileSystemMounted()) {
407
408             m_pCryptoFileSystemManager->setEncryptionKey(key);
409
410             if (!isSecretsDBOpen()) {
411                 if (openSecretsDB()) {
412                     TRACE() << "Credentials system opened.";
413                 } else {
414                     BLAME() << "Failed to open credentials system.";
415                 }
416             } else {
417                 TRACE() << "Credentials system already opened.";
418             }
419         }
420         return true;
421     } else {
422         return false;
423     }
424 }
425
426 void CredentialsAccessManager::onKeyInserted(const SignOn::Key key)
427 {
428     TRACE() << "Key:" << key.toHex();
429
430     if (key.isEmpty()) return;
431
432     /* The `key in use` check will attempt to mount using the new key if
433        the file system is not already mounted
434     */
435     if (encryptionKeyCanMountFS(key)) {
436         TRACE() << "SIM data already in use.";
437         authorizedKeys << key;
438         return;
439     }
440
441     /* We got here because the inserted key is totally new to the CAM.
442      * Let's see if any key manager wants to authorize it: we call
443      * authorizeKey() on each of them, and continue processing the key when
444      * the keyAuthorized() signal comes.
445      */
446     foreach (SignOn::AbstractKeyManager *keyManager, keyManagers) {
447         keyManager->authorizeKey(key);
448     }
449 }
450
451 void CredentialsAccessManager::onKeyDisabled(const SignOn::Key key)
452 {
453     TRACE() << "Key:" << key.toHex();
454
455     if (authorizedKeys.removeAll(key) == 0) {
456         TRACE() << "Key was already disabled";
457         return;
458     }
459
460     if (authorizedKeys.isEmpty()) {
461         TRACE() << "All keys removed, closing secure storage.";
462         if (isSecretsDBOpen())
463             if (!closeSecretsDB())
464                 BLAME() << "Error occurred while closing secure storage.";
465
466         TRACE() << "Querying for keys.";
467         foreach (SignOn::AbstractKeyManager *keyManager, keyManagers) {
468             keyManager->queryKeys();
469         }
470     }
471 }
472
473 void CredentialsAccessManager::onKeyRemoved(const SignOn::Key key)
474 {
475     TRACE() << "Key:" << key.toHex();
476
477     // Make sure the key is disabled:
478     onKeyDisabled(key);
479
480     if (!encryptionKeyCanMountFS(key)) {
481         TRACE() << "Key is not known to the CryptoManager.";
482         return;
483     }
484
485     if (authorizedKeys.isEmpty()) {
486         BLAME() << "Cannot remove key: no authorized keys";
487         return;
488     }
489
490     SignOn::Key authorizedKey = authorizedKeys.first();
491     if (!m_pCryptoFileSystemManager->removeEncryptionKey(key, authorizedKey)) {
492         BLAME() << "Failed to remove key.";
493     } else {
494         TRACE() << "Key successfully removed.";
495     }
496 }
497
498 void CredentialsAccessManager::onKeyAuthorized(const SignOn::Key key,
499                                                bool authorized)
500 {
501     TRACE() << "Key:" << key.toHex() << "Authorized:" << authorized;
502
503     if (!authorized) return;
504
505     if (encryptionKeyCanMountFS(key)) {
506         TRACE() << "Encryption key already in use.";
507         authorizedKeys << key;
508         return;
509     }
510
511     if (m_pCryptoFileSystemManager->fileSystemMounted()) {
512         /* if the secure FS is already mounted, add the new key to it */
513         if (authorizedKeys.isEmpty()) {
514             BLAME() << "No authorized keys: cannot add new key";
515             return;
516         }
517
518         SignOn::Key authorizedKey = authorizedKeys.first();
519         if (m_pCryptoFileSystemManager->addEncryptionKey(key, authorizedKey)) {
520             TRACE() << "Encryption key successfullyadded into the CryptoManager.";
521             m_pCryptoFileSystemManager->setEncryptionKey(key);
522             authorizedKeys << key;
523         } else {
524             BLAME() << "Could not store encryption key.";
525         }
526     } else if (!fileSystemDeployed()) {
527         /* if the secure FS does not exist, create it and use this new key to
528          * initialize it */
529         m_pCryptoFileSystemManager->setEncryptionKey(key);
530         m_accessCodeFetched = true;
531         if (openSecretsDB()) {
532             authorizedKeys << key;
533         } else {
534             BLAME() << "Couldn't create the secure FS";
535         }
536     } else {
537         BLAME() << "Secure FS already created with another set of keys";
538     }
539 }
540