signon
8.58
|
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