signon  8.58
crypto-manager.cpp
Go to the documentation of this file.
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 <ext-aurel.popirtac@nokia.com>
8  * Contact: Alberto Mardegan <alberto.mardegan@canonical.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 "crypto-manager.h"
27 #include "crypto-handlers.h"
28 #include "debug.h"
29 #include "misc.h"
30 
31 #include <QFile>
32 #include <QDir>
33 #include <QMetaEnum>
34 #include <QSettings>
35 
36 #define DEVICE_MAPPER_DIR "/dev/mapper/"
37 #define EXT2 "ext2"
38 #define EXT3 "ext3"
39 #define EXT4 "ext4"
40 
41 const uint CryptoManager::signonMinumumDbSize = 8;
42 const char CryptoManager::signonDefaultFileSystemName[] = "signonfs";
43 const char CryptoManager::signonDefaultFileSystemType[] = EXT2;
44 
45 const QString CryptoManager::keychainFilePath() const
46 {
47  return m_fileSystemMountPath + QDir::separator() + QLatin1String("keychain");
48 }
49 
50 void CryptoManager::addKeyToKeychain(const QByteArray &key) const
51 {
52  //If key is already present do not add it - backwards compat. feature
53  if (keychainContainsKey(key)) return;
54 
55  QSettings keychain(keychainFilePath(), QSettings::IniFormat);
56  int keyCount = keychain.allKeys().count();
57  keychain.setValue(QString::number(++keyCount), QVariant(key));
58 }
59 
60 void CryptoManager::removeKeyFromKeychain(const QByteArray &key) const
61 {
62  QSettings keychain(keychainFilePath(), QSettings::IniFormat);
63  foreach (QString keyIt, keychain.allKeys()) {
64  if (keychain.value(keyIt).toByteArray() == key) {
65  keychain.remove(keyIt);
66  break;
67  }
68  }
69 }
70 
71 bool CryptoManager::keychainContainsKey(const QByteArray &key) const
72 {
73  QSettings keychain(keychainFilePath(), QSettings::IniFormat);
74  foreach (QString keyIt, keychain.allKeys()) {
75  if (keychain.value(keyIt).toByteArray() == key)
76  return true;
77  }
78 
79  return false;
80 }
81 
83  SignOn::AbstractCryptoManager(parent),
84  m_fileSystemPath(QString()),
85  m_fileSystemMapPath(QString()),
86  m_fileSystemName(QString()),
87  m_fileSystemMountPath(QString()),
88  m_loopDeviceName(QString()),
89  m_fileSystemType(Ext2),
90  m_fileSystemSize(4)
91 {
92  updateMountState(Unmounted);
94  BLAME() << "Could not load `dm_mod`!";
95 }
96 
98 {
100 }
101 
102 bool CryptoManager::initialize(const QVariantMap &configuration)
103 {
104  QString storagePath =
105  QDir(configuration.value(QLatin1String("StoragePath")).toString()).path();
106  if (storagePath.startsWith(QLatin1Char('~')))
107  storagePath.replace(0, 1, QDir::homePath());
108 
109  QString encryptedFSPath =
110  storagePath + QDir::separator() +
111  QLatin1String(signonDefaultFileSystemName);
112 
113  bool isOk = false;
114  quint32 storageSize =
115  configuration.value(QLatin1String("Size")).toUInt(&isOk);
116  if (!isOk || storageSize < signonMinumumDbSize) {
117  storageSize = signonMinumumDbSize;
118  TRACE() << "Less than minimum possible storage size configured."
119  << "Setting to the minimum of:" << signonMinumumDbSize << "Mb";
120  }
121 
122  QString fileSystemType =
123  configuration.value(QLatin1String("FileSystemType")).toString();
124 
125  setFileSystemPath(encryptedFSPath);
126  setFileSystemSize(storageSize);
127  setFileSystemType(fileSystemType);
128 
129  checkFileSystemSetup();
130  return true;
131 }
132 
133 void CryptoManager::setFileSystemPath(const QString &path)
134 {
135  m_fileSystemPath = path;
136 
137  QFileInfo fsFileInfo(path);
138 
139  m_fileSystemName = fsFileInfo.fileName();
140  m_fileSystemMapPath = QLatin1String(DEVICE_MAPPER_DIR) + m_fileSystemName;
141  m_fileSystemMountPath = path + QLatin1String("-mnt");
142 }
143 
144 bool CryptoManager::setFileSystemSize(const quint32 size)
145 {
147  TRACE() << "Minumum encrypted file size is 4 Mb.";
148  return false;
149  }
150  m_fileSystemSize = size;
151  return true;
152 }
153 
154 bool CryptoManager::setFileSystemType(const QString &type)
155 {
156  QString cmpBase = type.toLower();
157  if (cmpBase == QLatin1String(EXT2)) {
158  m_fileSystemType = Ext2;
159  return true;
160  } else if (cmpBase == QLatin1String(EXT3)) {
161  m_fileSystemType = Ext3;
162  return true;
163  } else if (cmpBase == QLatin1String(EXT4)) {
164  m_fileSystemType = Ext4;
165  return true;
166  }
167  return false;
168 }
169 
170 void CryptoManager::checkFileSystemSetup()
171 {
172  setFileSystemSetup(QFile::exists(m_fileSystemPath));
173 }
174 
176 {
177  if (m_mountState == Mounted) {
178  TRACE() << "Ecrypyted file system already mounted.";
179  return false;
180  }
181 
182  if (encryptionKey().isEmpty()) {
183  TRACE() << "No access code set. Stopping mount process.";
184  return false;
185  }
186 
188  BLAME() << "Could not load `dm_mod`!";
189  return false;
190  }
191 
192  clearFileSystemResources();
193 
194  m_loopDeviceName = LosetupHandler::findAvailableDevice();
195  if (m_loopDeviceName.isNull()) {
196  BLAME() << "No free loop device available!";
197  return false;
198  }
199 
200  if (!PartitionHandler::createPartitionFile(m_fileSystemPath,
201  m_fileSystemSize)) {
202  BLAME() << "Could not create partition file.";
204  return false;
205  }
206  checkFileSystemSetup();
207 
208  if (!LosetupHandler::setupDevice(m_loopDeviceName,
209  m_fileSystemPath)) {
210  BLAME() << "Failed to setup loop device:" << m_loopDeviceName;
212  return false;
213  }
214  updateMountState(LoopSet);
215 
216  if (!CryptsetupHandler::formatFile(encryptionKey(), m_loopDeviceName)) {
217  BLAME() << "Failed to LUKS format.";
219  return false;
220  }
221  updateMountState(LoopLuksFormatted);
222 
223  //attempt luks close, in case of a leftover.
224  if (QFile::exists(QLatin1String(DEVICE_MAPPER_DIR) + m_fileSystemName)) {
225  TRACE() << "Filesystem exists, closing";
226  CryptsetupHandler::closeFile(m_fileSystemName);
227  }
228 
229  if (!CryptsetupHandler::openFile(encryptionKey(),
230  m_loopDeviceName,
231  m_fileSystemName)) {
232  BLAME() << "Failed to LUKS open";
234  return false;
235  }
236  updateMountState(LoopLuksOpened);
237 
238  if (!PartitionHandler::formatPartitionFile(m_fileSystemMapPath,
239  m_fileSystemType)) {
240  BLAME() << "Could not format mapped partition.";
242  return false;
243  }
244 
245  if (!mountMappedDevice()) {
246  BLAME() << "Failed to mount ecrypted file system.";
248  return false;
249  }
250 
251  addKeyToKeychain(encryptionKey());
252  updateMountState(Mounted);
253  return true;
254 }
255 
256 //TODO - add checking for LUKS header in case of preavious app run formatting
257 //failure
259 {
260  if (m_mountState == Mounted) {
261  TRACE() << "Ecrypyted file system already mounted.";
262  return false;
263  }
264 
265  if (encryptionKey().isEmpty()) {
266  TRACE() << "No access code set. Stopping mount process.";
267  return false;
268  }
269 
270  clearFileSystemResources();
271 
273  BLAME() << "Could not load `dm_mod`!";
274  return false;
275  }
276 
277  m_loopDeviceName = LosetupHandler::findAvailableDevice();
278  if (m_loopDeviceName.isNull()) {
279  BLAME() << "No free loop device available!";
280  return false;
281  }
282 
283  if (!LosetupHandler::setupDevice(m_loopDeviceName, m_fileSystemPath)) {
284  BLAME() << "Failed to setup loop device:" << m_loopDeviceName;
286  return false;
287  }
288  updateMountState(LoopSet);
289 
290  //attempt luks close, in case of a leftover.
291  if (QFile::exists(QLatin1String(DEVICE_MAPPER_DIR) + m_fileSystemName))
292  CryptsetupHandler::closeFile(m_fileSystemName);
293 
294  if (!CryptsetupHandler::openFile(encryptionKey(),
295  m_loopDeviceName,
296  m_fileSystemName)) {
297  BLAME() << "Failed to LUKS open.";
299  return false;
300  }
301  updateMountState(LoopLuksOpened);
302 
303  if (!mountMappedDevice()) {
304  TRACE() << "Failed to mount ecrypted file system.";
306  return false;
307  }
308 
309  addKeyToKeychain(encryptionKey());
310  updateMountState(Mounted);
311  return true;
312 }
313 
314 void CryptoManager::clearFileSystemResources()
315 {
316  /*
317  This method is a `just in case call` for the situations
318  when signond closes whithout handling the unmounting of
319  the secure storage.
320  */
321 
322  TRACE() << "--- START clearing secure storage possibly used resources."
323  " Ignore possible errors. ---";
324 
325  if (!unmountMappedDevice())
326  TRACE() << "Unmounting mapped device failed.";
327 
328  if (QFile::exists(QLatin1String(DEVICE_MAPPER_DIR) + m_fileSystemName)) {
329  if (!CryptsetupHandler::closeFile(m_fileSystemName))
330  TRACE() << "Failed to LUKS close.";
331  }
332 
333  /*
334  TODO - find a way to check which loop device was previously used by
335  signond and close that specific one, until then this will be
336  skipped as it might close devices used by different processes.
337 
338  if (!LosetupHandler::releaseDevice(m_loopDeviceName)) {
339  TRACE() << "Failed to release loop device.";
340  */
341 
342  TRACE() << "--- DONE clearing secure storage possibly used resources. ---";
343 }
344 
346 {
347  if (m_mountState == Unmounted) {
348  TRACE() << "Ecrypyted file system not mounted.";
349  return true;
350  }
351 
352  setFileSystemMounted(false);
353  bool isOk = true;
354 
355  if ((m_mountState >= Mounted)
356  && !unmountMappedDevice()) {
357  TRACE() << "Failed to unmount mapped loop device.";
358  isOk = false;
359  } else {
360  TRACE() << "Mapped loop device unmounted.";
361  }
362 
363  if ((m_mountState >= LoopLuksOpened)
364  && (!CryptsetupHandler::closeFile(m_fileSystemName))) {
365  TRACE() << "Failed to LUKS close.";
366  isOk = false;
367  } else {
368  TRACE() << "Luks close succeeded.";
369  }
370 
371  if ((m_mountState >= LoopSet)
372  && (!LosetupHandler::releaseDevice(m_loopDeviceName))) {
373  TRACE() << "Failed to release loop device.";
374  isOk = false;
375  } else {
376  TRACE() << "Loop device released.";
377  }
378 
379  updateMountState(Unmounted);
380  return isOk;
381 }
382 
384 {
385  if (m_mountState > Unmounted) {
386  if (!unmountFileSystem())
387  return false;
388  }
389 
390  //TODO - implement effective deletion in specific handler object
391  return false;
392 }
393 
395 {
396  return m_fileSystemMountPath;
397 }
398 
399 QStringList CryptoManager::backupFiles() const
400 {
401  return QStringList() << m_fileSystemPath;
402 }
403 
404 void CryptoManager::updateMountState(const FileSystemMountState state)
405 {
406  TRACE() << "Updating mount state:" << state;
407  if (state == m_mountState) return;
408 
409  m_mountState = state;
410  setFileSystemMounted(state == Mounted);
411 }
412 
413 bool CryptoManager::mountMappedDevice()
414 {
415  //create mnt dir if not existant
416  if (!QFile::exists(m_fileSystemMountPath)) {
417  QDir dir;
418  if (!dir.mkpath(m_fileSystemMountPath)) {
419  BLAME() << "Could not create target mount dir path.";
420  return false;
421  }
422 
423  if (!setUserOwnership(m_fileSystemMountPath))
424  TRACE() << "Failed to set User ownership for "
425  "the secure storage mount target.";
426  }
427 
428  MountHandler::mount(m_fileSystemMapPath, m_fileSystemMountPath);
429  return true;
430 }
431 
432 bool CryptoManager::unmountMappedDevice()
433 {
434  return MountHandler::umount(m_fileSystemMountPath);
435 }
436 
437 bool CryptoManager::addEncryptionKey(const SignOn::Key &key,
438  const SignOn::Key &existingKey)
439 {
440  /*
441  * TODO -- limit number of stored keys to the total available slots - 1.
442  */
443  if (m_mountState >= LoopLuksOpened) {
445  m_loopDeviceName, key, existingKey)) {
446 
447  addKeyToKeychain(key);
448  return true;
449  }
450  }
451  TRACE() << "FAILED to occupy key slot on the encrypted file system header.";
452  return false;
453 }
454 
455 bool CryptoManager::removeEncryptionKey(const SignOn::Key &key,
456  const SignOn::Key &remainingKey)
457 {
458  if (m_mountState >= LoopLuksOpened) {
460  m_loopDeviceName, key, remainingKey))
461 
462  removeKeyFromKeychain(key);
463  return true;
464  }
465  TRACE() << "FAILED to release key slot from the encrypted file system "
466  "header.";
467  return false;
468 }
469 
470 bool CryptoManager::encryptionKeyInUse(const SignOn::Key &key)
471 {
472  if (fileSystemIsMounted() && (encryptionKey() == key))
473  return true;
474 
475  if(!fileSystemIsMounted()) {
476  setEncryptionKey(key);
477  return mountFileSystem();
478  }
479 
480  /* Variant that tests if the key is in the LUKS keychain
481  * by using a file on the encrypted storage containing the keychain.
482  */
483  return keychainContainsKey(key);
484 
485  /*
486  * Variant that tests if the key is in the LUKS keychain
487  * by directly accessing the LUKS system
488  *
489  * QByteArray dummyKey("dummy");
490  * if (addEncryptionKey(dummyKey, key)) {
491  * if (!removeEncryptionKey(dummyKey, key))
492  * BLAME() << "Could not remove dummy auxiliary key "
493  * "from encrypted file system header.";
494  * return true;
495  * }
496  *
497  * return false;
498  */
499 }
500 
501 //TODO - remove this after stable version is achieved.
502 void CryptoManager::serializeData()
503 {
504  TRACE() << "m_accessCode" << encryptionKey();
505  TRACE() << "m_fileSystemPath" << m_fileSystemPath;
506  TRACE() << "m_fileSystemMapPath" << m_fileSystemMapPath;
507  TRACE() << "m_fileSystemName" << m_fileSystemName;
508  TRACE() << "m_loopDeviceName" << m_loopDeviceName;
509  TRACE() << "m_fileSystemType" << m_fileSystemType;
510  TRACE() << "m_fileSystemSize" << m_fileSystemSize;
511 }
512 
static bool setupDevice(const QString &deviceName, const QString &blockDevice)
Mounts a block device to loopback device.
#define EXT3
bool deleteFileSystem()
Deletes the encrypted file system.
static bool formatPartitionFile(const QString &fileName, const quint32 fileSystemType)
Formats a file (block device) for a specific file system type (ext2,ext3,ext4)
~CryptoManager()
Destroys a CryptoManager object.
static bool createPartitionFile(const QString &fileName, const quint32 fileSize)
Creates a random data file of fileSize Mb.
#define BLAME()
Definition: debug.h:32
bool mountFileSystem()
Mounts the encrypted file system.
bool removeEncryptionKey(const SignOn::Key &key, const SignOn::Key &remainingKey)
Releases an existing used keyslot in the LUKS partition&#39;s header.
static bool formatFile(const QByteArray &key, const QString &deviceName)
Formats the file system.
#define DEVICE_MAPPER_DIR
bool initialize(const QVariantMap &configuration)
static bool loadDmMod()
Loads the dm_mod kernel module.
QStringList backupFiles() const
static bool closeFile(const QString &deviceName)
Closes the file system.
static bool umount(const QString &target)
Unmounts a block device from a specific location.
bool setUserOwnership(const QString &filePath)
Definition: misc.cpp:33
#define EXT4
CryptoManager(QObject *parent=0)
Constructs a CryptoManager object with the given parent.
static bool removeKeySlot(const QString &deviceName, const QByteArray &key, const QByteArray &remainingKey)
Removes a key ocupying an encryption header slot.
#define TRACE()
Definition: debug.h:28
bool encryptionKeyInUse(const SignOn::Key &key)
#define MINUMUM_ENCRYPTED_FILE_SYSTEM_SIZE
QString fileSystemMountPath() const
static bool addKeySlot(const QString &deviceName, const QByteArray &key, const QByteArray &existingKey)
Adds a key to a free encryption header slot.
bool addEncryptionKey(const SignOn::Key &key, const SignOn::Key &existingKey)
Adds an encryption key to one of the available keyslots of the LUKS partition&#39;s header.
bool unmountFileSystem()
Unmounts the encrypted file system.
static bool mount(const QString &source, const QString &target, const QString &fileSystemType=QLatin1String("ext2"))
Mounts a block device to a specific location.
static bool releaseDevice(const QString &deviceName)
Releases a used loopback device.
#define EXT2
static QString findAvailableDevice()
Finds an available loopback device.
static bool openFile(const QByteArray &key, const QString &deviceName, const QString &deviceMap)
Opens the file system.
bool setupFileSystem()
Sets up an encrypted file system.