signon  8.58
crypto-manager.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /*
00003  * This file is part of signon
00004  *
00005  * Copyright (C) 2009-2010 Nokia Corporation.
00006  *
00007  * Contact: Aurel Popirtac <ext-aurel.popirtac@nokia.com>
00008  * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
00009  *
00010  * This library is free software; you can redistribute it and/or
00011  * modify it under the terms of the GNU Lesser General Public License
00012  * version 2.1 as published by the Free Software Foundation.
00013  *
00014  * This library is distributed in the hope that it will be useful, but
00015  * WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00017  * Lesser General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU Lesser General Public
00020  * License along with this library; if not, write to the Free Software
00021  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
00022  * 02110-1301 USA
00023  */
00024 
00025 
00026 #include "crypto-manager.h"
00027 #include "crypto-handlers.h"
00028 #include "debug.h"
00029 #include "misc.h"
00030 
00031 #include <QFile>
00032 #include <QDir>
00033 #include <QMetaEnum>
00034 #include <QSettings>
00035 
00036 #define DEVICE_MAPPER_DIR "/dev/mapper/"
00037 #define EXT2 "ext2"
00038 #define EXT3 "ext3"
00039 #define EXT4 "ext4"
00040 
00041 const uint CryptoManager::signonMinumumDbSize = 8;
00042 const char CryptoManager::signonDefaultFileSystemName[] = "signonfs";
00043 const char CryptoManager::signonDefaultFileSystemType[] = EXT2;
00044 
00045 const QString CryptoManager::keychainFilePath() const
00046 {
00047     return m_fileSystemMountPath + QDir::separator() + QLatin1String("keychain");
00048 }
00049 
00050 void CryptoManager::addKeyToKeychain(const QByteArray &key) const
00051 {
00052     //If key is already present do not add it - backwards compat. feature
00053     if (keychainContainsKey(key)) return;
00054 
00055     QSettings keychain(keychainFilePath(), QSettings::IniFormat);
00056     int keyCount = keychain.allKeys().count();
00057     keychain.setValue(QString::number(++keyCount), QVariant(key));
00058 }
00059 
00060 void CryptoManager::removeKeyFromKeychain(const QByteArray &key) const
00061 {
00062     QSettings keychain(keychainFilePath(), QSettings::IniFormat);
00063     foreach (QString keyIt, keychain.allKeys()) {
00064         if (keychain.value(keyIt).toByteArray() == key) {
00065             keychain.remove(keyIt);
00066             break;
00067         }
00068     }
00069 }
00070 
00071 bool CryptoManager::keychainContainsKey(const QByteArray &key) const
00072 {
00073     QSettings keychain(keychainFilePath(), QSettings::IniFormat);
00074     foreach (QString keyIt, keychain.allKeys()) {
00075         if (keychain.value(keyIt).toByteArray() == key)
00076             return true;
00077     }
00078 
00079     return false;
00080 }
00081 
00082 CryptoManager::CryptoManager(QObject *parent):
00083     SignOn::AbstractCryptoManager(parent),
00084     m_fileSystemPath(QString()),
00085     m_fileSystemMapPath(QString()),
00086     m_fileSystemName(QString()),
00087     m_fileSystemMountPath(QString()),
00088     m_loopDeviceName(QString()),
00089     m_fileSystemType(Ext2),
00090     m_fileSystemSize(4)
00091 {
00092     updateMountState(Unmounted);
00093     if (!CryptsetupHandler::loadDmMod())
00094         BLAME() << "Could not load `dm_mod`!";
00095 }
00096 
00097 CryptoManager::~CryptoManager()
00098 {
00099     unmountFileSystem();
00100 }
00101 
00102 bool CryptoManager::initialize(const QVariantMap &configuration)
00103 {
00104     QString storagePath =
00105         QDir(configuration.value(QLatin1String("StoragePath")).toString()).path();
00106     if (storagePath.startsWith(QLatin1Char('~')))
00107         storagePath.replace(0, 1, QDir::homePath());
00108 
00109     QString encryptedFSPath =
00110         storagePath + QDir::separator() +
00111         QLatin1String(signonDefaultFileSystemName);
00112 
00113     bool isOk = false;
00114     quint32 storageSize =
00115         configuration.value(QLatin1String("Size")).toUInt(&isOk);
00116     if (!isOk || storageSize < signonMinumumDbSize) {
00117         storageSize = signonMinumumDbSize;
00118         TRACE() << "Less than minimum possible storage size configured."
00119             << "Setting to the minimum of:" << signonMinumumDbSize << "Mb";
00120     }
00121 
00122     QString fileSystemType =
00123         configuration.value(QLatin1String("FileSystemType")).toString();
00124 
00125     setFileSystemPath(encryptedFSPath);
00126     setFileSystemSize(storageSize);
00127     setFileSystemType(fileSystemType);
00128 
00129     checkFileSystemSetup();
00130     return true;
00131 }
00132 
00133 void CryptoManager::setFileSystemPath(const QString &path)
00134 {
00135     m_fileSystemPath = path;
00136 
00137     QFileInfo fsFileInfo(path);
00138 
00139     m_fileSystemName = fsFileInfo.fileName();
00140     m_fileSystemMapPath = QLatin1String(DEVICE_MAPPER_DIR) + m_fileSystemName;
00141     m_fileSystemMountPath = path + QLatin1String("-mnt");
00142 }
00143 
00144 bool CryptoManager::setFileSystemSize(const quint32 size)
00145 {
00146     if (size < MINUMUM_ENCRYPTED_FILE_SYSTEM_SIZE) {
00147         TRACE() << "Minumum encrypted file size is 4 Mb.";
00148         return false;
00149     }
00150     m_fileSystemSize = size;
00151     return true;
00152 }
00153 
00154 bool CryptoManager::setFileSystemType(const QString &type)
00155 {
00156     QString cmpBase = type.toLower();
00157     if (cmpBase == QLatin1String(EXT2)) {
00158         m_fileSystemType = Ext2;
00159         return true;
00160     } else if (cmpBase == QLatin1String(EXT3)) {
00161         m_fileSystemType = Ext3;
00162         return true;
00163     } else if (cmpBase == QLatin1String(EXT4)) {
00164         m_fileSystemType = Ext4;
00165         return true;
00166     }
00167     return false;
00168 }
00169 
00170 void CryptoManager::checkFileSystemSetup()
00171 {
00172     setFileSystemSetup(QFile::exists(m_fileSystemPath));
00173 }
00174 
00175 bool CryptoManager::setupFileSystem()
00176 {
00177     if (m_mountState == Mounted) {
00178         TRACE() << "Ecrypyted file system already mounted.";
00179         return false;
00180     }
00181 
00182     if (encryptionKey().isEmpty()) {
00183         TRACE() << "No access code set. Stopping mount process.";
00184         return false;
00185     }
00186 
00187     if (!CryptsetupHandler::loadDmMod()) {
00188         BLAME() << "Could not load `dm_mod`!";
00189         return false;
00190     }
00191 
00192     clearFileSystemResources();
00193 
00194     m_loopDeviceName = LosetupHandler::findAvailableDevice();
00195     if (m_loopDeviceName.isNull()) {
00196         BLAME() << "No free loop device available!";
00197         return false;
00198     }
00199 
00200     if (!PartitionHandler::createPartitionFile(m_fileSystemPath,
00201                                                m_fileSystemSize)) {
00202         BLAME() << "Could not create partition file.";
00203         unmountFileSystem();
00204         return false;
00205     }
00206     checkFileSystemSetup();
00207 
00208     if (!LosetupHandler::setupDevice(m_loopDeviceName,
00209                                      m_fileSystemPath)) {
00210         BLAME() << "Failed to setup loop device:" << m_loopDeviceName;
00211         unmountFileSystem();
00212         return false;
00213     }
00214     updateMountState(LoopSet);
00215 
00216     if (!CryptsetupHandler::formatFile(encryptionKey(), m_loopDeviceName)) {
00217         BLAME() << "Failed to LUKS format.";
00218         unmountFileSystem();
00219         return false;
00220     }
00221     updateMountState(LoopLuksFormatted);
00222 
00223     //attempt luks close, in case of a leftover.
00224     if (QFile::exists(QLatin1String(DEVICE_MAPPER_DIR) + m_fileSystemName)) {
00225         TRACE() << "Filesystem exists, closing";
00226         CryptsetupHandler::closeFile(m_fileSystemName);
00227     }
00228 
00229     if (!CryptsetupHandler::openFile(encryptionKey(),
00230                                      m_loopDeviceName,
00231                                      m_fileSystemName)) {
00232         BLAME() << "Failed to LUKS open";
00233         unmountFileSystem();
00234         return false;
00235     }
00236     updateMountState(LoopLuksOpened);
00237 
00238     if (!PartitionHandler::formatPartitionFile(m_fileSystemMapPath,
00239                                                m_fileSystemType)) {
00240         BLAME() << "Could not format mapped partition.";
00241         unmountFileSystem();
00242         return false;
00243     }
00244 
00245     if (!mountMappedDevice()) {
00246         BLAME() << "Failed to mount ecrypted file system.";
00247         unmountFileSystem();
00248         return false;
00249     }
00250 
00251     addKeyToKeychain(encryptionKey());
00252     updateMountState(Mounted);
00253     return true;
00254 }
00255 
00256 //TODO - add checking for LUKS header in case of preavious app run formatting
00257 //failure
00258 bool CryptoManager::mountFileSystem()
00259 {
00260     if (m_mountState == Mounted) {
00261         TRACE() << "Ecrypyted file system already mounted.";
00262         return false;
00263     }
00264 
00265     if (encryptionKey().isEmpty()) {
00266         TRACE() << "No access code set. Stopping mount process.";
00267         return false;
00268     }
00269 
00270     clearFileSystemResources();
00271 
00272     if (!CryptsetupHandler::loadDmMod()) {
00273         BLAME() << "Could not load `dm_mod`!";
00274         return false;
00275     }
00276 
00277     m_loopDeviceName = LosetupHandler::findAvailableDevice();
00278     if (m_loopDeviceName.isNull()) {
00279         BLAME() << "No free loop device available!";
00280         return false;
00281     }
00282 
00283     if (!LosetupHandler::setupDevice(m_loopDeviceName, m_fileSystemPath)) {
00284         BLAME() << "Failed to setup loop device:" << m_loopDeviceName;
00285         unmountFileSystem();
00286         return false;
00287     }
00288     updateMountState(LoopSet);
00289 
00290     //attempt luks close, in case of a leftover.
00291     if (QFile::exists(QLatin1String(DEVICE_MAPPER_DIR) + m_fileSystemName))
00292         CryptsetupHandler::closeFile(m_fileSystemName);
00293 
00294     if (!CryptsetupHandler::openFile(encryptionKey(),
00295                                      m_loopDeviceName,
00296                                      m_fileSystemName)) {
00297         BLAME() << "Failed to LUKS open.";
00298         unmountFileSystem();
00299         return false;
00300     }
00301     updateMountState(LoopLuksOpened);
00302 
00303     if (!mountMappedDevice()) {
00304         TRACE() << "Failed to mount ecrypted file system.";
00305         unmountFileSystem();
00306         return false;
00307     }
00308 
00309     addKeyToKeychain(encryptionKey());
00310     updateMountState(Mounted);
00311     return true;
00312 }
00313 
00314 void CryptoManager::clearFileSystemResources()
00315 {
00316     /*
00317         This method is a `just in case call` for the situations
00318         when signond closes whithout handling the unmounting of
00319         the secure storage.
00320     */
00321 
00322     TRACE() << "--- START clearing secure storage possibly used resources."
00323                " Ignore possible errors. ---";
00324 
00325     if (!unmountMappedDevice())
00326         TRACE() << "Unmounting mapped device failed.";
00327 
00328     if (QFile::exists(QLatin1String(DEVICE_MAPPER_DIR) + m_fileSystemName)) {
00329         if (!CryptsetupHandler::closeFile(m_fileSystemName))
00330             TRACE() << "Failed to LUKS close.";
00331     }
00332 
00333     /*
00334      TODO - find a way to check which loop device was previously used by
00335             signond and close that specific one, until then this will be
00336             skipped as it might close devices used by different processes.
00337 
00338     if (!LosetupHandler::releaseDevice(m_loopDeviceName)) {
00339         TRACE() << "Failed to release loop device.";
00340     */
00341 
00342     TRACE() << "--- DONE clearing secure storage possibly used resources. ---";
00343 }
00344 
00345 bool CryptoManager::unmountFileSystem()
00346 {
00347     if (m_mountState == Unmounted) {
00348         TRACE() << "Ecrypyted file system not mounted.";
00349         return true;
00350     }
00351 
00352     setFileSystemMounted(false);
00353     bool isOk = true;
00354 
00355     if ((m_mountState >= Mounted)
00356         && !unmountMappedDevice()) {
00357         TRACE() << "Failed to unmount mapped loop device.";
00358         isOk = false;
00359     } else {
00360         TRACE() << "Mapped loop device unmounted.";
00361     }
00362 
00363     if ((m_mountState >= LoopLuksOpened)
00364         && (!CryptsetupHandler::closeFile(m_fileSystemName))) {
00365         TRACE() << "Failed to LUKS close.";
00366         isOk = false;
00367     } else {
00368         TRACE() << "Luks close succeeded.";
00369     }
00370 
00371     if ((m_mountState >= LoopSet)
00372         && (!LosetupHandler::releaseDevice(m_loopDeviceName))) {
00373         TRACE() << "Failed to release loop device.";
00374         isOk = false;
00375     } else {
00376         TRACE() << "Loop device released.";
00377     }
00378 
00379     updateMountState(Unmounted);
00380     return isOk;
00381 }
00382 
00383 bool CryptoManager::deleteFileSystem()
00384 {
00385     if (m_mountState > Unmounted) {
00386         if (!unmountFileSystem())
00387             return false;
00388     }
00389 
00390     //TODO - implement effective deletion in specific handler object
00391     return false;
00392 }
00393 
00394 QString CryptoManager::fileSystemMountPath() const
00395 {
00396     return m_fileSystemMountPath;
00397 }
00398 
00399 QStringList CryptoManager::backupFiles() const
00400 {
00401     return QStringList() << m_fileSystemPath;
00402 }
00403 
00404 void CryptoManager::updateMountState(const FileSystemMountState state)
00405 {
00406     TRACE() << "Updating mount state:" << state;
00407     if (state == m_mountState) return;
00408 
00409     m_mountState = state;
00410     setFileSystemMounted(state == Mounted);
00411 }
00412 
00413 bool CryptoManager::mountMappedDevice()
00414 {
00415     //create mnt dir if not existant
00416     if (!QFile::exists(m_fileSystemMountPath)) {
00417         QDir dir;
00418         if (!dir.mkpath(m_fileSystemMountPath)) {
00419             BLAME() << "Could not create target mount dir path.";
00420             return false;
00421         }
00422 
00423         if (!setUserOwnership(m_fileSystemMountPath))
00424             TRACE() << "Failed to set User ownership for "
00425                        "the secure storage mount target.";
00426     }
00427 
00428     MountHandler::mount(m_fileSystemMapPath, m_fileSystemMountPath);
00429     return true;
00430 }
00431 
00432 bool CryptoManager::unmountMappedDevice()
00433 {
00434     return MountHandler::umount(m_fileSystemMountPath);
00435 }
00436 
00437 bool CryptoManager::addEncryptionKey(const SignOn::Key &key,
00438                                      const SignOn::Key &existingKey)
00439 {
00440     /*
00441      * TODO -- limit number of stored keys to the total available slots - 1.
00442      */
00443     if (m_mountState >= LoopLuksOpened) {
00444         if (CryptsetupHandler::addKeySlot(
00445                 m_loopDeviceName, key, existingKey)) {
00446 
00447             addKeyToKeychain(key);
00448             return true;
00449         }
00450     }
00451     TRACE() << "FAILED to occupy key slot on the encrypted file system header.";
00452     return false;
00453 }
00454 
00455 bool CryptoManager::removeEncryptionKey(const SignOn::Key &key,
00456                                         const SignOn::Key &remainingKey)
00457 {
00458     if (m_mountState >= LoopLuksOpened) {
00459         if (CryptsetupHandler::removeKeySlot(
00460             m_loopDeviceName, key, remainingKey))
00461 
00462             removeKeyFromKeychain(key);
00463             return true;
00464     }
00465     TRACE() << "FAILED to release key slot from the encrypted file system "
00466         "header.";
00467     return false;
00468 }
00469 
00470 bool CryptoManager::encryptionKeyInUse(const SignOn::Key &key)
00471 {
00472     if (fileSystemIsMounted() && (encryptionKey() == key))
00473         return true;
00474 
00475     if(!fileSystemIsMounted()) {
00476        setEncryptionKey(key);
00477        return mountFileSystem();
00478     }
00479 
00480     /* Variant that tests if the key is in the LUKS keychain
00481      * by using a file on the encrypted storage containing the keychain.
00482      */
00483     return keychainContainsKey(key);
00484 
00485     /*
00486      * Variant that tests if the key is in the LUKS keychain
00487      * by directly accessing the LUKS system
00488      *
00489      * QByteArray dummyKey("dummy");
00490      * if (addEncryptionKey(dummyKey, key)) {
00491      *     if (!removeEncryptionKey(dummyKey, key))
00492      *         BLAME() << "Could not remove dummy auxiliary key "
00493      *                    "from encrypted file system header.";
00494      *    return true;
00495      * }
00496      *
00497      * return false;
00498      */
00499 }
00500 
00501 //TODO - remove this after stable version is achieved.
00502 void CryptoManager::serializeData()
00503 {
00504     TRACE() << "m_accessCode" << encryptionKey();
00505     TRACE() << "m_fileSystemPath" << m_fileSystemPath;
00506     TRACE() << "m_fileSystemMapPath" << m_fileSystemMapPath;
00507     TRACE() << "m_fileSystemName" << m_fileSystemName;
00508     TRACE() << "m_loopDeviceName" << m_loopDeviceName;
00509     TRACE() << "m_fileSystemType" << m_fileSystemType;
00510     TRACE() << "m_fileSystemSize" << m_fileSystemSize;
00511 }
00512