Complete support for key managers.
[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 "simdatahandler.h"
29 #include "signond-common.h"
30 #include "devicelockcodehandler.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_dbName(QLatin1String(signonDefaultDbName)),
51           m_useEncryption(signonDefaultUseEncryption),
52           m_dbFileSystemPath(
53                 QLatin1String(signonDefaultStoragePath)
54                 + QDir::separator()
55                 + QLatin1String(signonDefaultFileSystemName)),
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 " << m_dbFileSystemPath << '\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 /* ---------------------- CredentialsAccessManager ---------------------- */
86
87 CredentialsAccessManager *CredentialsAccessManager::m_pInstance = NULL;
88
89 CredentialsAccessManager::CredentialsAccessManager(QObject *parent)
90         : QObject(parent),
91           m_isInitialized(false),
92           m_accessCodeFetched(false),
93           m_systemOpened(false),
94           m_error(NoError),
95           m_currentSimData(QByteArray()),
96           keyManagers(),
97           m_pCredentialsDB(NULL),
98           m_pCryptoFileSystemManager(NULL),
99           m_pSimDataHandler(NULL),
100           m_pLockCodeHandler(NULL),
101           m_CAMConfiguration(CAMConfiguration())
102 {
103 }
104
105 CredentialsAccessManager::~CredentialsAccessManager()
106 {
107     closeCredentialsSystem();
108
109     if (m_pSimDataHandler)
110         delete m_pSimDataHandler;
111     if (m_pLockCodeHandler)
112         delete m_pLockCodeHandler;
113
114     m_pInstance = NULL;
115 }
116
117 CredentialsAccessManager *CredentialsAccessManager::instance(QObject *parent)
118 {
119     if (!m_pInstance)
120         m_pInstance = new CredentialsAccessManager(parent);
121
122     return m_pInstance;
123 }
124
125 void CredentialsAccessManager::finalize()
126 {
127     if (m_systemOpened)
128         closeCredentialsSystem();
129
130     if (m_pSimDataHandler) {
131         m_pSimDataHandler->blockSignals(true);
132         delete m_pSimDataHandler;
133         m_pSimDataHandler = NULL;
134     }
135
136     if (m_pLockCodeHandler) {
137         m_pLockCodeHandler->blockSignals(true);
138         delete m_pLockCodeHandler;
139         m_pLockCodeHandler = NULL;
140     }
141
142     if (m_pCryptoFileSystemManager)
143         delete m_pCryptoFileSystemManager;
144
145     m_accessCodeFetched = false;
146     m_isInitialized = false;
147     m_error = NoError;
148 }
149
150 bool CredentialsAccessManager::init(const CAMConfiguration &camConfiguration)
151 {
152     if (m_isInitialized) {
153         TRACE() << "CAM already initialized.";
154         m_error = AlreadyInitialized;
155         return false;
156     }
157
158     m_CAMConfiguration = camConfiguration;
159
160     QBuffer config;
161     m_CAMConfiguration.serialize(&config);
162     TRACE() << "\n\nInitualizing CredentialsAccessManager with configuration: " << config.data();
163
164     if (m_CAMConfiguration.m_useEncryption) {
165         //Initialize CryptoManager
166         m_pCryptoFileSystemManager = new CryptoManager(this);
167         m_pCryptoFileSystemManager->setFileSystemPath(m_CAMConfiguration.m_dbFileSystemPath);
168         m_pCryptoFileSystemManager->setFileSystemSize(m_CAMConfiguration.m_fileSystemSize);
169         m_pCryptoFileSystemManager->setFileSystemType(m_CAMConfiguration.m_fileSystemType);
170
171         //Initialize SIM handler
172         m_pSimDataHandler = new SimDataHandler(this);
173         connect(m_pSimDataHandler,
174                 SIGNAL(simAvailable(QByteArray)),
175                 SLOT(simDataFetched(QByteArray)));
176
177         connect(m_pSimDataHandler,
178                 SIGNAL(simRemoved()),
179                 SLOT(simRemoved()));
180
181         connect(m_pSimDataHandler,
182                 SIGNAL(error()),
183                 SLOT(simError()));
184
185         if (!m_pSimDataHandler->isValid())
186             BLAME() << "AccessCodeHandler invalid, SIM data might not be available.";
187
188         //If passphrase exists (device lock code) open creds system - sync
189         if (!m_CAMConfiguration.m_encryptionPassphrase.isEmpty()) {
190
191             m_pCryptoFileSystemManager->setEncryptionKey(
192                     m_CAMConfiguration.m_encryptionPassphrase);
193             m_accessCodeFetched = true;
194
195             if (!openCredentialsSystemPriv(true)) {
196                 BLAME() << "Failed to open credentials system. Fallback to alternative methods.";
197             }
198         }
199
200         /* Query SIM.
201            1. In case of a SIM query error or if the queried SIM auth data
202            is not stored as a LUKS key, this will later on trigger the DLC query - async.
203            2. In case of the current encryption passphrase being secure (buffered DLC),
204            - signond was started by a setDeviceLockCode() call -
205            then store the SIM reply data right away using the buffered DLC as master key.
206         */
207         m_pSimDataHandler->querySim();
208
209
210         // Initialize all key managers
211         foreach (SignOn::AbstractKeyManager *keyManager, keyManagers) {
212             connect(keyManager,
213                     SIGNAL(keyInserted(const SignOn::Key)),
214                     SLOT(onKeyInserted(const SignOn::Key)));
215             connect(keyManager,
216                     SIGNAL(keyDisabled(const SignOn::Key)),
217                     SLOT(onKeyDisabled(const SignOn::Key)));
218             connect(keyManager,
219                     SIGNAL(keyRemoved(const SignOn::Key)),
220                     SLOT(onKeyRemoved(const SignOn::Key)));
221             connect(keyManager,
222                     SIGNAL(keyAuthorized(const SignOn::Key, bool)),
223                     SLOT(onKeyAuthorized(const SignOn::Key, bool)));
224             keyManager->setup();
225         }
226     }
227
228     m_isInitialized = true;
229     m_error = NoError;
230
231     TRACE() << "CredentialsAccessManager successfully initialized...";
232     return true;
233 }
234
235 void CredentialsAccessManager::addKeyManager(
236     SignOn::AbstractKeyManager *keyManager)
237 {
238     keyManagers.append(keyManager);
239 }
240
241 bool CredentialsAccessManager::openCredentialsSystemPriv(bool mountFileSystem)
242 {
243     //todo remove this variable after LUKS implementation becomes stable.
244     QString dbPath;
245
246     if (m_CAMConfiguration.m_useEncryption) {
247         dbPath = m_pCryptoFileSystemManager->fileSystemMountPath()
248             + QDir::separator()
249             + m_CAMConfiguration.m_dbName;
250
251         if (mountFileSystem) {
252             if (!fileSystemDeployed()) {
253                 if (deployCredentialsSystem()) {
254                     if (openDB(dbPath))
255                         m_systemOpened = true;
256                 }
257                 return m_systemOpened;
258             }
259
260             if (fileSystemLoaded()) {
261                 m_error = CredentialsDbAlreadyDeployed;
262                 return false;
263             }
264
265             if (!m_pCryptoFileSystemManager->mountFileSystem()) {
266                 m_error = CredentialsDbMountFailed;
267                 return false;
268             }
269         }
270     } else {
271         QFileInfo fInfo(m_CAMConfiguration.m_dbFileSystemPath);
272         QDir storageDir = fInfo.dir();
273         if (!storageDir.exists()) {
274             if (!storageDir.mkpath(storageDir.path()))
275                 BLAME() << "Could not create storage directory!!!";
276         }
277
278         dbPath = storageDir.path()
279                  + QDir::separator()
280                  + m_CAMConfiguration.m_dbName;
281     }
282
283     TRACE() << "Database name: [" << dbPath << "]";
284
285     if (openDB(dbPath)) {
286         m_systemOpened = true;
287         m_error = NoError;
288     }
289
290     if (m_pLockCodeHandler != 0) {
291         delete m_pLockCodeHandler;
292         m_pLockCodeHandler = 0;
293     }
294
295     return m_systemOpened;
296 }
297
298 bool CredentialsAccessManager::openCredentialsSystem()
299 {
300     RETURN_IF_NOT_INITIALIZED(false);
301
302     if (m_CAMConfiguration.m_useEncryption && !m_accessCodeFetched) {
303         m_error = AccessCodeNotReady;
304         return false;
305     }
306
307     return openCredentialsSystemPriv(true);
308 }
309
310 bool CredentialsAccessManager::closeCredentialsSystem()
311 {
312     RETURN_IF_NOT_INITIALIZED(false);
313
314     closeDB();
315
316     if (m_CAMConfiguration.m_useEncryption) {
317         if (!m_pCryptoFileSystemManager->unmountFileSystem()) {
318             m_error = CredentialsDbUnmountFailed;
319             return false;
320         }
321     }
322
323     m_error = NoError;
324     m_systemOpened = false;
325     return true;
326 }
327
328 bool CredentialsAccessManager::deleteCredentialsSystem()
329 {
330     RETURN_IF_NOT_INITIALIZED(false);
331
332     if (m_systemOpened && !closeCredentialsSystem()) {
333         /* The close operation failed: we cannot proceed */
334         return false;
335     }
336
337     m_error = NoError;
338
339     if (m_CAMConfiguration.m_useEncryption) {
340         if (!m_pCryptoFileSystemManager->deleteFileSystem())
341             m_error = CredentialsDbDeletionFailed;
342     } else {
343         QFile dbFile(m_CAMConfiguration.m_dbName);
344         if (dbFile.exists()) {
345             if (!dbFile.remove())
346                 m_error = CredentialsDbDeletionFailed;
347         }
348     }
349
350     return m_error == NoError;
351 }
352
353 bool CredentialsAccessManager::setMasterEncryptionKey(const QByteArray &newKey,
354                                                       const QByteArray &existingKey)
355 {
356     if (!m_CAMConfiguration.m_useEncryption)
357         return false;
358
359     /* Clear this for security reasons - SIM data must not be stored
360        using an deprecated master key
361     */
362     m_CAMConfiguration.m_encryptionPassphrase.clear();
363
364     if (!m_pCryptoFileSystemManager->encryptionKeyInUse(existingKey)) {
365         BLAME() << "Existing lock code check failed.";
366         return false;
367     }
368
369     if (!m_pCryptoFileSystemManager->addEncryptionKey(
370             newKey, existingKey)) {
371         BLAME() << "Failed to add new device lock code.";
372         return false;
373     }
374
375     if (!m_pCryptoFileSystemManager->removeEncryptionKey(existingKey, newKey)) {
376         BLAME() << "Failed to remove old device lock code.";
377         return false;
378     }
379
380     return true;
381 }
382
383 bool CredentialsAccessManager::encryptionKeyPendingAddition() const
384 {
385     return !m_currentSimData.isEmpty();
386 }
387
388 bool CredentialsAccessManager::storeEncryptionKey(const QByteArray &masterKey)
389 {
390     bool ret = false;
391     /*Add the key to a keyslot.
392       If the SIM was recognized by the system or it was just successfully added to it
393       consider it as the current encryption key.
394     */
395     if (!m_pCryptoFileSystemManager->encryptionKeyInUse(m_currentSimData)) {
396         TRACE() << "Encryption key not in use, attemptin store.";
397
398         if (m_pCryptoFileSystemManager->addEncryptionKey(m_currentSimData, masterKey)) {
399             m_pCryptoFileSystemManager->setEncryptionKey(m_currentSimData);
400             ret = true;
401         } else {
402             BLAME() << "Could not store encryption key.";
403         }
404     } else {
405         TRACE() << masterKey;
406         m_pCryptoFileSystemManager->setEncryptionKey(m_currentSimData);
407     }
408
409     m_currentSimData.clear();
410     return ret;
411 }
412
413 bool CredentialsAccessManager::lockSecureStorage(const QByteArray &lockData)
414 {
415     // TODO - implement this, research how to.
416     Q_UNUSED(lockData)
417     return false;
418 }
419
420 CredentialsDB *CredentialsAccessManager::credentialsDB() const
421 {
422     RETURN_IF_NOT_INITIALIZED(NULL);
423
424     return m_pCredentialsDB;
425 }
426
427 bool CredentialsAccessManager::deployCredentialsSystem()
428 {
429     if (m_CAMConfiguration.m_useEncryption) {
430         if (!m_pCryptoFileSystemManager->setupFileSystem()) {
431             m_error = CredentialsDbSetupFailed;
432             return false;
433         }
434     }
435     return true;
436 }
437
438 bool CredentialsAccessManager::fileSystemLoaded(bool checkForDatabase)
439 {
440     if (!m_pCryptoFileSystemManager->fileSystemMounted())
441         return false;
442
443     if (checkForDatabase
444         && !m_pCryptoFileSystemManager->fileSystemContainsFile(m_CAMConfiguration.m_dbName))
445         return false;
446
447     return true;
448 }
449
450 bool CredentialsAccessManager::fileSystemDeployed()
451 {
452     return QFile::exists(m_pCryptoFileSystemManager->fileSystemPath());
453 }
454
455 bool CredentialsAccessManager::openDB(const QString &databaseName)
456 {
457     m_pCredentialsDB = new CredentialsDB(databaseName);
458
459     if (!m_pCredentialsDB->connect()) {
460         TRACE() << SqlDatabase::errorInfo(m_pCredentialsDB->error(false));
461         m_error = CredentialsDbConnectionError;
462         return false;
463     }
464     TRACE() <<  "Database connection succeeded.";
465
466     if (!m_pCredentialsDB->hasTableStructure()) {
467         TRACE() << "Creating SQL table structure...";
468         if (!m_pCredentialsDB->createTableStructure()) {
469             TRACE() << SqlDatabase::errorInfo(m_pCredentialsDB->error());
470             m_error = CredentialsDbSqlError;
471             return false;
472         }
473     } else {
474         TRACE() << "SQL table structure already created...";
475     }
476
477     TRACE() << m_pCredentialsDB->sqlDBConfiguration();
478
479     return true;
480 }
481
482 void CredentialsAccessManager::closeDB()
483 {
484     if (m_pCredentialsDB) {
485         m_pCredentialsDB->disconnect();
486         delete m_pCredentialsDB;
487         m_pCredentialsDB = NULL;
488     }
489 }
490
491 void CredentialsAccessManager::simDataFetched(const QByteArray &simData)
492 {
493     TRACE() << "SIM data fetched.";
494
495     if (simData.isEmpty()) {
496         BLAME() << "Fetched empty access code.";
497         m_error = FailedToFetchAccessCode;
498         return;
499     }
500
501     /* The `key in use` check will attempt to mount using the simData if
502        the file system is not already mounted
503     */
504     if (m_pCryptoFileSystemManager->encryptionKeyInUse(simData)) {
505         TRACE() << "SIM data already in use.";
506         if (m_pCryptoFileSystemManager->fileSystemMounted()) {
507
508             m_pCryptoFileSystemManager->setEncryptionKey(simData);
509
510             if (!credentialsSystemOpened()) {
511                 if (openCredentialsSystemPriv(false)) {
512                     TRACE() << "Credentials system opened.";
513                 } else {
514                     BLAME() << "Failed to open credentials system.";
515                 }
516             } else {
517                 TRACE() << "Credentials system already opened.";
518             }
519         }
520         return;
521     }
522
523     /* Keep SIM data until the master key (device lock code) is available,
524        or if the current encryption passphrase is secure
525        (the daemon has been started by a setDeviceLockCode call, thus
526        the CAMConfiguration passphrase is the current DLC), then
527        store the SIM data right away.
528     */
529     m_currentSimData = simData;
530
531     if (!m_CAMConfiguration.m_encryptionPassphrase.isEmpty()) {
532         if (!storeEncryptionKey(m_CAMConfiguration.m_encryptionPassphrase))
533             BLAME() << "Failed to store SIM data as encryption key.";
534         m_CAMConfiguration.m_encryptionPassphrase.clear();
535         return;
536     }
537
538     //Query Device Lock Code here - UI dialog.
539     if (m_pLockCodeHandler == NULL)
540         m_pLockCodeHandler = new DeviceLockCodeHandler(this);
541
542     m_pLockCodeHandler->queryLockCode();
543 }
544
545 void CredentialsAccessManager::simRemoved()
546 {
547     TRACE() << "SIM removed, closing secure storage.";
548     if (credentialsSystemOpened())
549         if (!closeCredentialsSystem())
550             BLAME() << "Error occurred while closing secure storage.";
551
552     if (m_pLockCodeHandler == NULL)
553         m_pLockCodeHandler = new DeviceLockCodeHandler;
554
555     TRACE() << "Querying DLC.";
556     m_pLockCodeHandler->queryLockCode();
557 }
558
559 void CredentialsAccessManager::simError()
560 {
561     TRACE() << "SIM error occurred.";
562
563     if (!m_pSimDataHandler->isSimPresent() && !credentialsSystemOpened()) {
564         if (m_pLockCodeHandler == NULL)
565             m_pLockCodeHandler = new DeviceLockCodeHandler;
566
567         TRACE() << "Querying DLC.";
568         m_pLockCodeHandler->queryLockCode();
569     }
570 }
571
572 void CredentialsAccessManager::onKeyInserted(const SignOn::Key key)
573 {
574     TRACE() << "Key:" << key.toHex();
575
576     if (key.isEmpty()) return;
577
578     /* The `key in use` check will attempt to mount using the new key if
579        the file system is not already mounted
580     */
581     if (m_pCryptoFileSystemManager->encryptionKeyInUse(key)) {
582         TRACE() << "SIM data already in use.";
583         if (m_pCryptoFileSystemManager->fileSystemMounted()) {
584
585             m_pCryptoFileSystemManager->setEncryptionKey(key);
586
587             if (!credentialsSystemOpened()) {
588                 if (openCredentialsSystemPriv(false)) {
589                     TRACE() << "Credentials system opened.";
590                 } else {
591                     BLAME() << "Failed to open credentials system.";
592                 }
593             } else {
594                 TRACE() << "Credentials system already opened.";
595             }
596         }
597         authorizedKeys << key;
598         return;
599     }
600
601     /* We got here because the inserted key is totally new to the CAM.
602      * Let's see if any key manager wants to authorize it: we call
603      * authorizeKey() on each of them, and continue processing the key when
604      * the keyAuthorized() signal comes.
605      */
606     foreach (SignOn::AbstractKeyManager *keyManager, keyManagers) {
607         keyManager->authorizeKey(key);
608     }
609 }
610
611 void CredentialsAccessManager::onKeyDisabled(const SignOn::Key key)
612 {
613     TRACE() << "Key:" << key.toHex();
614
615     if (authorizedKeys.removeAll(key) == 0) {
616         TRACE() << "Key was already disabled";
617         return;
618     }
619
620     if (authorizedKeys.isEmpty()) {
621         TRACE() << "All keys removed, closing secure storage.";
622         if (credentialsSystemOpened())
623             if (!closeCredentialsSystem())
624                 BLAME() << "Error occurred while closing secure storage.";
625
626         TRACE() << "Querying for keys.";
627         foreach (SignOn::AbstractKeyManager *keyManager, keyManagers) {
628             keyManager->queryKeys();
629         }
630     }
631 }
632
633 void CredentialsAccessManager::onKeyRemoved(const SignOn::Key key)
634 {
635     TRACE() << "Key:" << key.toHex();
636
637     // Make sure the key is disabled:
638     onKeyDisabled(key);
639
640     if (!m_pCryptoFileSystemManager->encryptionKeyInUse(key)) {
641         TRACE() << "Key is not known to the CryptoManager.";
642         return;
643     }
644
645     if (authorizedKeys.isEmpty()) {
646         BLAME() << "Cannot remove key: no authorized keys";
647         return;
648     }
649
650     SignOn::Key authorizedKey = authorizedKeys.first();
651     if (!m_pCryptoFileSystemManager->removeEncryptionKey(key, authorizedKey)) {
652         BLAME() << "Failed to remove key.";
653     } else {
654         TRACE() << "Key successfully removed.";
655     }
656 }
657
658 void CredentialsAccessManager::onKeyAuthorized(const SignOn::Key key,
659                                                bool authorized)
660 {
661     TRACE() << "Key:" << key.toHex() << "Authorized:" << authorized;
662
663     if (!authorized) return;
664
665     if (m_pCryptoFileSystemManager->encryptionKeyInUse(key)) {
666         TRACE() << "Encryption key already in use.";
667         authorizedKeys << key;
668         return;
669     }
670
671     if (authorizedKeys.isEmpty()) {
672         BLAME() << "No authorized keys: cannot add new key";
673         return;
674     }
675
676     SignOn::Key authorizedKey = authorizedKeys.first();
677     if (m_pCryptoFileSystemManager->addEncryptionKey(key, authorizedKey)) {
678         TRACE() << "Encryption key successfullyadded into the CryptoManager.";
679         m_pCryptoFileSystemManager->setEncryptionKey(key);
680         authorizedKeys << key;
681     } else {
682         BLAME() << "Could not store encryption key.";
683     }
684 }
685