Fix CredentialsAccessManager::onKeyAuthorized()
[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         //If passphrase exists (device lock code) open creds system - sync
150         if (!m_CAMConfiguration.m_encryptionPassphrase.isEmpty()) {
151
152             m_pCryptoFileSystemManager->setEncryptionKey(
153                     m_CAMConfiguration.m_encryptionPassphrase);
154             m_accessCodeFetched = true;
155
156             if (!openCredentialsSystemPriv(true)) {
157                 BLAME() << "Failed to open credentials system. Fallback to alternative methods.";
158             }
159         }
160
161         // Initialize all key managers
162         foreach (SignOn::AbstractKeyManager *keyManager, keyManagers) {
163             connect(keyManager,
164                     SIGNAL(keyInserted(const SignOn::Key)),
165                     SLOT(onKeyInserted(const SignOn::Key)));
166             connect(keyManager,
167                     SIGNAL(keyDisabled(const SignOn::Key)),
168                     SLOT(onKeyDisabled(const SignOn::Key)));
169             connect(keyManager,
170                     SIGNAL(keyRemoved(const SignOn::Key)),
171                     SLOT(onKeyRemoved(const SignOn::Key)));
172             connect(keyManager,
173                     SIGNAL(keyAuthorized(const SignOn::Key, bool)),
174                     SLOT(onKeyAuthorized(const SignOn::Key, bool)));
175             keyManager->setup();
176         }
177     }
178
179     m_isInitialized = true;
180     m_error = NoError;
181
182     TRACE() << "CredentialsAccessManager successfully initialized...";
183     return true;
184 }
185
186 void CredentialsAccessManager::addKeyManager(
187     SignOn::AbstractKeyManager *keyManager)
188 {
189     keyManagers.append(keyManager);
190 }
191
192 bool CredentialsAccessManager::openCredentialsSystemPriv(bool mountFileSystem)
193 {
194     //todo remove this variable after LUKS implementation becomes stable.
195     QString dbPath;
196
197     if (m_CAMConfiguration.m_useEncryption) {
198         dbPath = m_pCryptoFileSystemManager->fileSystemMountPath()
199             + QDir::separator()
200             + m_CAMConfiguration.m_dbName;
201
202         if (mountFileSystem) {
203             if (!fileSystemDeployed()) {
204                 if (deployCredentialsSystem()) {
205                     if (openDB(dbPath))
206                         m_systemOpened = true;
207                 }
208                 return m_systemOpened;
209             }
210
211             if (fileSystemLoaded()) {
212                 m_error = CredentialsDbAlreadyDeployed;
213                 return false;
214             }
215
216             if (!m_pCryptoFileSystemManager->mountFileSystem()) {
217                 m_error = CredentialsDbMountFailed;
218                 return false;
219             }
220         }
221     } else {
222         QFileInfo fInfo(m_CAMConfiguration.m_dbFileSystemPath);
223         QDir storageDir = fInfo.dir();
224         if (!storageDir.exists()) {
225             if (!storageDir.mkpath(storageDir.path()))
226                 BLAME() << "Could not create storage directory!!!";
227         }
228
229         dbPath = storageDir.path()
230                  + QDir::separator()
231                  + m_CAMConfiguration.m_dbName;
232     }
233
234     TRACE() << "Database name: [" << dbPath << "]";
235
236     if (openDB(dbPath)) {
237         m_systemOpened = true;
238         m_error = NoError;
239     }
240
241     return m_systemOpened;
242 }
243
244 bool CredentialsAccessManager::openCredentialsSystem()
245 {
246     RETURN_IF_NOT_INITIALIZED(false);
247
248     if (m_CAMConfiguration.m_useEncryption && !m_accessCodeFetched) {
249         m_error = AccessCodeNotReady;
250         return false;
251     }
252
253     return openCredentialsSystemPriv(true);
254 }
255
256 bool CredentialsAccessManager::closeCredentialsSystem()
257 {
258     RETURN_IF_NOT_INITIALIZED(false);
259
260     closeDB();
261
262     if (m_CAMConfiguration.m_useEncryption) {
263         if (!m_pCryptoFileSystemManager->unmountFileSystem()) {
264             m_error = CredentialsDbUnmountFailed;
265             return false;
266         }
267     }
268
269     m_error = NoError;
270     m_systemOpened = false;
271     return true;
272 }
273
274 bool CredentialsAccessManager::deleteCredentialsSystem()
275 {
276     RETURN_IF_NOT_INITIALIZED(false);
277
278     if (m_systemOpened && !closeCredentialsSystem()) {
279         /* The close operation failed: we cannot proceed */
280         return false;
281     }
282
283     m_error = NoError;
284
285     if (m_CAMConfiguration.m_useEncryption) {
286         if (!m_pCryptoFileSystemManager->deleteFileSystem())
287             m_error = CredentialsDbDeletionFailed;
288     } else {
289         QFile dbFile(m_CAMConfiguration.m_dbName);
290         if (dbFile.exists()) {
291             if (!dbFile.remove())
292                 m_error = CredentialsDbDeletionFailed;
293         }
294     }
295
296     return m_error == NoError;
297 }
298
299 bool CredentialsAccessManager::setMasterEncryptionKey(const QByteArray &newKey,
300                                                       const QByteArray &existingKey)
301 {
302     if (!m_CAMConfiguration.m_useEncryption)
303         return false;
304
305     /* Clear this for security reasons - SIM data must not be stored
306        using an deprecated master key
307     */
308     m_CAMConfiguration.m_encryptionPassphrase.clear();
309
310     if (!encryptionKeyCanMountFS(existingKey)) {
311         BLAME() << "Existing lock code check failed.";
312         return false;
313     }
314
315     if (!m_pCryptoFileSystemManager->addEncryptionKey(
316             newKey, existingKey)) {
317         BLAME() << "Failed to add new device lock code.";
318         return false;
319     }
320
321     if (!m_pCryptoFileSystemManager->removeEncryptionKey(existingKey, newKey)) {
322         BLAME() << "Failed to remove old device lock code.";
323         return false;
324     }
325
326     return true;
327 }
328
329 bool CredentialsAccessManager::lockSecureStorage(const QByteArray &lockData)
330 {
331     // TODO - implement this, research how to.
332     Q_UNUSED(lockData)
333     return false;
334 }
335
336 CredentialsDB *CredentialsAccessManager::credentialsDB() const
337 {
338     RETURN_IF_NOT_INITIALIZED(NULL);
339
340     return m_pCredentialsDB;
341 }
342
343 bool CredentialsAccessManager::deployCredentialsSystem()
344 {
345     if (m_CAMConfiguration.m_useEncryption) {
346         if (!m_pCryptoFileSystemManager->setupFileSystem()) {
347             m_error = CredentialsDbSetupFailed;
348             return false;
349         }
350     }
351     return true;
352 }
353
354 bool CredentialsAccessManager::fileSystemLoaded(bool checkForDatabase)
355 {
356     if (!m_pCryptoFileSystemManager->fileSystemMounted())
357         return false;
358
359     if (checkForDatabase
360         && !m_pCryptoFileSystemManager->fileSystemContainsFile(m_CAMConfiguration.m_dbName))
361         return false;
362
363     return true;
364 }
365
366 bool CredentialsAccessManager::fileSystemDeployed()
367 {
368     return QFile::exists(m_pCryptoFileSystemManager->fileSystemPath());
369 }
370
371 bool CredentialsAccessManager::openDB(const QString &databaseName)
372 {
373     m_pCredentialsDB = new CredentialsDB(databaseName);
374
375     if (!m_pCredentialsDB->connect()) {
376         TRACE() << SqlDatabase::errorInfo(m_pCredentialsDB->error(false));
377         m_error = CredentialsDbConnectionError;
378         return false;
379     }
380     TRACE() <<  "Database connection succeeded.";
381
382     if (!m_pCredentialsDB->hasTableStructure()) {
383         TRACE() << "Creating SQL table structure...";
384         if (!m_pCredentialsDB->createTableStructure()) {
385             TRACE() << SqlDatabase::errorInfo(m_pCredentialsDB->error());
386             m_error = CredentialsDbSqlError;
387             return false;
388         }
389     } else {
390         TRACE() << "SQL table structure already created...";
391     }
392
393     TRACE() << m_pCredentialsDB->sqlDBConfiguration();
394
395     return true;
396 }
397
398 bool CredentialsAccessManager::encryptionKeyCanMountFS(const QByteArray &key)
399 {
400     if (!fileSystemDeployed()) {
401         TRACE() << "Secure FS not deployed";
402         return false;
403     }
404
405     if (m_pCryptoFileSystemManager->encryptionKeyInUse(key)) {
406         TRACE() << "SIM data already in use.";
407         if (m_pCryptoFileSystemManager->fileSystemMounted()) {
408
409             m_pCryptoFileSystemManager->setEncryptionKey(key);
410
411             if (!credentialsSystemOpened()) {
412                 if (openCredentialsSystemPriv(false)) {
413                     TRACE() << "Credentials system opened.";
414                 } else {
415                     BLAME() << "Failed to open credentials system.";
416                 }
417             } else {
418                 TRACE() << "Credentials system already opened.";
419             }
420         }
421         return true;
422     } else {
423         return false;
424     }
425 }
426
427 void CredentialsAccessManager::closeDB()
428 {
429     if (m_pCredentialsDB) {
430         m_pCredentialsDB->disconnect();
431         delete m_pCredentialsDB;
432         m_pCredentialsDB = NULL;
433     }
434 }
435
436 void CredentialsAccessManager::onKeyInserted(const SignOn::Key key)
437 {
438     TRACE() << "Key:" << key.toHex();
439
440     if (key.isEmpty()) return;
441
442     /* The `key in use` check will attempt to mount using the new key if
443        the file system is not already mounted
444     */
445     if (encryptionKeyCanMountFS(key)) {
446         TRACE() << "SIM data already in use.";
447         authorizedKeys << key;
448         return;
449     }
450
451     /* We got here because the inserted key is totally new to the CAM.
452      * Let's see if any key manager wants to authorize it: we call
453      * authorizeKey() on each of them, and continue processing the key when
454      * the keyAuthorized() signal comes.
455      */
456     foreach (SignOn::AbstractKeyManager *keyManager, keyManagers) {
457         keyManager->authorizeKey(key);
458     }
459 }
460
461 void CredentialsAccessManager::onKeyDisabled(const SignOn::Key key)
462 {
463     TRACE() << "Key:" << key.toHex();
464
465     if (authorizedKeys.removeAll(key) == 0) {
466         TRACE() << "Key was already disabled";
467         return;
468     }
469
470     if (authorizedKeys.isEmpty()) {
471         TRACE() << "All keys removed, closing secure storage.";
472         if (credentialsSystemOpened())
473             if (!closeCredentialsSystem())
474                 BLAME() << "Error occurred while closing secure storage.";
475
476         TRACE() << "Querying for keys.";
477         foreach (SignOn::AbstractKeyManager *keyManager, keyManagers) {
478             keyManager->queryKeys();
479         }
480     }
481 }
482
483 void CredentialsAccessManager::onKeyRemoved(const SignOn::Key key)
484 {
485     TRACE() << "Key:" << key.toHex();
486
487     // Make sure the key is disabled:
488     onKeyDisabled(key);
489
490     if (!encryptionKeyCanMountFS(key)) {
491         TRACE() << "Key is not known to the CryptoManager.";
492         return;
493     }
494
495     if (authorizedKeys.isEmpty()) {
496         BLAME() << "Cannot remove key: no authorized keys";
497         return;
498     }
499
500     SignOn::Key authorizedKey = authorizedKeys.first();
501     if (!m_pCryptoFileSystemManager->removeEncryptionKey(key, authorizedKey)) {
502         BLAME() << "Failed to remove key.";
503     } else {
504         TRACE() << "Key successfully removed.";
505     }
506 }
507
508 void CredentialsAccessManager::onKeyAuthorized(const SignOn::Key key,
509                                                bool authorized)
510 {
511     TRACE() << "Key:" << key.toHex() << "Authorized:" << authorized;
512
513     if (!authorized) return;
514
515     if (encryptionKeyCanMountFS(key)) {
516         TRACE() << "Encryption key already in use.";
517         authorizedKeys << key;
518         return;
519     }
520
521     if (m_pCryptoFileSystemManager->fileSystemMounted()) {
522         /* if the secure FS is already mounted, add the new key to it */
523         if (authorizedKeys.isEmpty()) {
524             BLAME() << "No authorized keys: cannot add new key";
525             return;
526         }
527
528         SignOn::Key authorizedKey = authorizedKeys.first();
529         if (m_pCryptoFileSystemManager->addEncryptionKey(key, authorizedKey)) {
530             TRACE() << "Encryption key successfullyadded into the CryptoManager.";
531             m_pCryptoFileSystemManager->setEncryptionKey(key);
532             authorizedKeys << key;
533         } else {
534             BLAME() << "Could not store encryption key.";
535         }
536     } else if (!fileSystemDeployed()) {
537         /* if the secure FS does not exist, create it and use this new key to
538          * initialize it */
539         m_pCryptoFileSystemManager->setEncryptionKey(key);
540         m_accessCodeFetched = true;
541         if (openCredentialsSystemPriv(true)) {
542             authorizedKeys << key;
543         } else {
544             BLAME() << "Couldn't create the secure FS";
545         }
546     } else {
547         BLAME() << "Secure FS already created with another set of keys";
548     }
549 }
550