Move database file to .signon, this will prevent using old db
[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 "accesscodehandler.h"
29 #include "signond-common.h"
30
31 #include <QThreadPool>
32 #include <QCoreApplication>
33 #include <QFile>
34 #include <QBuffer>
35
36
37 #define RETURN_IF_NOT_INITIALIZED(return_value)                  \
38     do {                                                         \
39         if (!m_isInitialized) {                                  \
40             m_error = NotInitialized;                            \
41             TRACE() << "CredentialsAccessManager not initialized."; \
42             return return_value;                                \
43         }                                                       \
44     } while (0)
45
46 using namespace SignonDaemonNS;
47
48 /* ---------------------- CAMConfiguration ---------------------- */
49
50 CAMConfiguration::CAMConfiguration()
51         : m_dbName(QLatin1String("signon.db")),
52           m_useEncryption(false), //TODO this need to be set true when encryption is ready
53           m_dbFileSystemPath(QLatin1String("/home/user/signonfs")),
54           m_fileSystemType(QLatin1String("ext2")),
55           m_fileSystemSize(4),
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           m_pCredentialsDB(NULL),
94           m_pCryptoFileSystemManager(NULL),
95           m_pAccessCodeHandler(NULL),
96           m_CAMConfiguration(CAMConfiguration())
97 {
98 }
99
100 CredentialsAccessManager::~CredentialsAccessManager()
101 {
102     closeCredentialsSystem();
103     m_pInstance = NULL;
104 }
105
106 CredentialsAccessManager *CredentialsAccessManager::instance(QObject *parent)
107 {
108     if (!m_pInstance)
109         m_pInstance = new CredentialsAccessManager(parent);
110
111     return m_pInstance;
112 }
113
114 void CredentialsAccessManager::finalize()
115 {
116     if (m_systemOpened)
117         closeCredentialsSystem();
118
119     if (m_pAccessCodeHandler) {
120         m_pAccessCodeHandler->blockSignals(true);
121         delete m_pAccessCodeHandler;
122     }
123
124     if (m_pCryptoFileSystemManager)
125         delete m_pCryptoFileSystemManager;
126
127     m_accessCodeFetched = false;
128     m_isInitialized = false;
129     m_error = NoError;
130 }
131
132 bool CredentialsAccessManager::init(const CAMConfiguration &camConfiguration)
133 {
134     if (m_isInitialized) {
135         TRACE() << "CAM already initialized.";
136         m_error = AlreadyInitialized;
137         return false;
138     }
139
140     m_CAMConfiguration = camConfiguration;
141
142     QBuffer config;
143     m_CAMConfiguration.serialize(&config);
144     TRACE() << "\n\nInitualizing CredentialsAccessManager with configuration: " << config.data();
145
146     if (m_CAMConfiguration.m_useEncryption) {
147         m_pCryptoFileSystemManager = new CryptoManager(this);
148         m_pCryptoFileSystemManager->setFileSystemPath(m_CAMConfiguration.m_dbFileSystemPath);
149         m_pCryptoFileSystemManager->setFileSystemSize(m_CAMConfiguration.m_fileSystemSize);
150         m_pCryptoFileSystemManager->setFileSystemType(m_CAMConfiguration.m_fileSystemType);
151
152         if (!m_CAMConfiguration.m_encryptionPassphrase.isEmpty()) {
153
154             m_pCryptoFileSystemManager->setEncryptionKey(
155                     m_CAMConfiguration.m_encryptionPassphrase);
156             m_accessCodeFetched = true;
157
158             if (!openCredentialsSystemPriv()) {
159                 BLAME() << "Failed to open credentials system. Fallback to alternative methods.";
160             }
161         }
162
163         m_pAccessCodeHandler = new AccessCodeHandler(this);
164         connect(m_pAccessCodeHandler,
165                 SIGNAL(simAvailable(QByteArray)),
166                 SLOT(accessCodeFetched(QByteArray)));
167
168         connect(m_pAccessCodeHandler,
169                 SIGNAL(simChanged(QByteArray)),
170                 SLOT(accessCodeFetched(QByteArray)));
171
172         //todo create handling for the sim change too.
173         if (!m_pAccessCodeHandler->isValid())
174             BLAME() << "AccessCodeHandler invalid, SIM data might not be available.";
175
176         m_pAccessCodeHandler->querySim();
177     }
178
179     m_isInitialized = true;
180     m_error = NoError;
181
182     TRACE() << "CredentialsAccessManager successfully initialized...";
183     return true;
184 }
185
186
187 bool CredentialsAccessManager::openCredentialsSystemPriv()
188 {
189     //todo remove this variable after LUKS implementation becomes stable.
190     QString dbPath;
191
192     if (m_CAMConfiguration.m_useEncryption) {
193         dbPath = m_pCryptoFileSystemManager->fileSystemMountPath()
194             + QDir::separator()
195             + m_CAMConfiguration.m_dbName;
196
197         if (!fileSystemDeployed()) {
198             if (deployCredentialsSystem()) {
199                 if (openDB(dbPath))
200                     m_systemOpened = true;
201             }
202             return m_systemOpened;
203         }
204
205         if (fileSystemLoaded()) {
206             m_error = CredentialsDbAlreadyDeployed;
207             return false;
208         }
209
210         if (!m_pCryptoFileSystemManager->mountFileSystem()) {
211             m_error = CredentialsDbMountFailed;
212             return false;
213         }
214     } else {
215
216         /*
217          *   TODO - change this so that openDB takes only the CAM configuration db name
218          *          after the LUKS system is enabled; practically remove this 'else' branch
219          */
220         dbPath = QDir::homePath() + QDir::separator()
221                  + QLatin1String(".signon");
222
223         QDir dir;
224         if (!dir.mkpath(dbPath)) return false;
225         dbPath += QDir::separator() + m_CAMConfiguration.m_dbName;
226     }
227
228     TRACE() << "Database name: [" << dbPath << "]";
229
230     if (openDB(dbPath)) {
231         m_systemOpened = true;
232         m_error = NoError;
233     }
234
235     return m_systemOpened;
236 }
237
238 bool CredentialsAccessManager::openCredentialsSystem()
239 {
240     RETURN_IF_NOT_INITIALIZED(false);
241
242     if (m_CAMConfiguration.m_useEncryption && !m_accessCodeFetched) {
243         m_error = AccessCodeNotReady;
244         return false;
245     }
246
247     return openCredentialsSystemPriv();
248 }
249
250 bool CredentialsAccessManager::closeCredentialsSystem()
251 {
252     RETURN_IF_NOT_INITIALIZED(false);
253
254     if (!closeDB())
255         return false;
256
257     if (m_CAMConfiguration.m_useEncryption) {
258         if (!m_pCryptoFileSystemManager->unmountFileSystem()) {
259             m_error = CredentialsDbUnmountFailed;
260             return false;
261         }
262     }
263
264     m_error = NoError;
265     m_systemOpened = false;
266     return true;
267 }
268
269 bool CredentialsAccessManager::deleteCredentialsSystem()
270 {
271     RETURN_IF_NOT_INITIALIZED(false);
272
273     if (m_systemOpened && !closeCredentialsSystem()) {
274         /* The close operation failed: we cannot proceed */
275         return false;
276     }
277
278     m_error = NoError;
279
280     if (m_CAMConfiguration.m_useEncryption) {
281         if (!m_pCryptoFileSystemManager->deleteFileSystem())
282             m_error = CredentialsDbDeletionFailed;
283     } else {
284         QFile dbFile(m_CAMConfiguration.m_dbName);
285         if (dbFile.exists()) {
286             if (!dbFile.remove())
287                 m_error = CredentialsDbDeletionFailed;
288         }
289     }
290
291     return m_error == NoError;
292 }
293
294 bool CredentialsAccessManager::setDeviceLockCodeKey(const QByteArray &newLockCode,
295                                                     const QByteArray &existingLockCode)
296 {
297     if (!m_CAMConfiguration.m_useEncryption)
298         return false;
299
300     if (!m_pCryptoFileSystemManager->encryptionKeyInUse(existingLockCode)) {
301         BLAME() << "Existing lock code check failed.";
302         return false;
303     }
304
305     if (!m_pCryptoFileSystemManager->addEncryptionKey(
306             newLockCode, existingLockCode)) {
307         BLAME() << "Failed to add new device lock code.";
308         return false;
309     }
310
311     if (!m_pCryptoFileSystemManager->removeEncryptionKey(existingLockCode, newLockCode)) {
312         BLAME() << "Failed to remove old device lock code.";
313         return false;
314     }
315     return true;
316 }
317
318 bool CredentialsAccessManager::lockSecureStorage(const QByteArray &lockData)
319 {
320     // TODO - implement this, research how to.
321     Q_UNUSED(lockData)
322     return false;
323 }
324
325 CredentialsDB *CredentialsAccessManager::credentialsDB() const
326 {
327     RETURN_IF_NOT_INITIALIZED(NULL);
328
329     return m_pCredentialsDB;
330 }
331
332 bool CredentialsAccessManager::deployCredentialsSystem()
333 {
334     if (m_CAMConfiguration.m_useEncryption) {
335         if (!m_pCryptoFileSystemManager->setupFileSystem()) {
336             m_error = CredentialsDbSetupFailed;
337             return false;
338         }
339     }
340     return true;
341 }
342
343 bool CredentialsAccessManager::fileSystemLoaded(bool checkForDatabase)
344 {
345     if (!m_pCryptoFileSystemManager->fileSystemMounted())
346         return false;
347
348     if (checkForDatabase
349         && !m_pCryptoFileSystemManager->fileSystemContainsFile(m_CAMConfiguration.m_dbName))
350         return false;
351
352     return true;
353 }
354
355 bool CredentialsAccessManager::fileSystemDeployed()
356 {
357     return QFile::exists(m_pCryptoFileSystemManager->fileSystemPath());
358 }
359
360 bool CredentialsAccessManager::openDB(const QString &databaseName)
361 {
362     m_pCredentialsDB = new CredentialsDB(databaseName);
363
364     if (!m_pCredentialsDB->connect()) {
365         TRACE() << SqlDatabase::errorInfo(m_pCredentialsDB->error(false));
366         m_error = CredentialsDbConnectionError;
367         return false;
368     }
369     TRACE() <<  "Database connection succeeded.";
370
371     if (!m_pCredentialsDB->hasTableStructure()) {
372         TRACE() << "Creating SQL table structure...";
373         if (!m_pCredentialsDB->createTableStructure()) {
374             TRACE() << SqlDatabase::errorInfo(m_pCredentialsDB->error());
375             m_error = CredentialsDbSqlError;
376             return false;
377         }
378     } else {
379         TRACE() << "SQL table structure already created...";
380     }
381
382     TRACE() << m_pCredentialsDB->sqlDBConfiguration();
383
384     return true;
385 }
386
387 bool CredentialsAccessManager::closeDB()
388 {
389     if (m_pCredentialsDB) {
390         m_pCredentialsDB->disconnect();
391         delete m_pCredentialsDB;
392         m_pCredentialsDB = NULL;
393     }
394     return true;
395 }
396
397 void CredentialsAccessManager::accessCodeFetched(const QByteArray &accessCode)
398 {
399     /* todo - this will be improved when missing components are available
400              a. sim random data query
401              b. device lock code UI query
402      */
403     if (accessCode.isEmpty()) {
404         BLAME() << "Fetched empty access code.";
405         m_error = FailedToFetchAccessCode;
406         return;
407     }
408
409     m_accessCodeFetched = true;
410
411     //todo - query Device Lock Code here - UI dialog.
412     QByteArray lockCode = "1234";
413
414     //add the key to a keyslot
415     if (!m_pCryptoFileSystemManager->encryptionKeyInUse(accessCode)) {
416         if (!m_pCryptoFileSystemManager->addEncryptionKey(accessCode, lockCode)) //device lock code here
417             BLAME() << "Could not store encryption key.";
418     }
419
420     m_pCryptoFileSystemManager->setEncryptionKey(accessCode);
421
422     if (!credentialsSystemOpened()) {
423         if (openCredentialsSystem()) {
424             TRACE() << "Credentials system opened.";
425         } else {
426             BLAME() << "Failed to open credentials system.";
427         }
428     }
429 }