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 #include <sys/mount.h> 00026 #include <errno.h> 00027 #include <fcntl.h> 00028 #include <stdlib.h> 00029 #include <libcryptsetup.h> 00030 00031 #include <QDataStream> 00032 #include <QTextStream> 00033 #include <QProcess> 00034 #include <QLatin1Char> 00035 #include <QFileInfo> 00036 #include <QDir> 00037 00038 #include "crypto-handlers.h" 00039 #include "debug.h" 00040 #include "misc.h" 00041 00042 #define SIGNON_LUKS_DEFAULT_HASH "ripemd160" 00043 00044 #define SIGNON_LUKS_CIPHER_NAME "aes" 00045 #define SIGNON_LUKS_CIPHER_MODE "xts-plain" 00046 #define SIGNON_LUKS_CIPHER \ 00047 SIGNON_LUKS_CIPHER_NAME "-" SIGNON_LUKS_CIPHER_MODE 00048 #define SIGNON_LUKS_KEY_SIZE 256 00049 #define SIGNON_LUKS_BASE_KEYSLOT 0 00050 00051 #define SIGNON_EXTERNAL_PROCESS_READ_TIMEOUT 300 00052 00053 #define KILO_BYTE_SIZE 1024 00054 #define MEGA_BYTE_SIZE (KILO_BYTE_SIZE * 1024) 00055 00056 /* ------------- SystemCommandLineCallHandler implementation -------------- */ 00057 00058 SystemCommandLineCallHandler::SystemCommandLineCallHandler() 00059 { 00060 connect(&m_process, SIGNAL(error(QProcess::ProcessError)), 00061 this, SLOT(error(QProcess::ProcessError))); 00062 } 00063 00064 SystemCommandLineCallHandler::~SystemCommandLineCallHandler() 00065 { 00066 } 00067 00068 bool SystemCommandLineCallHandler::makeCall(const QString &appPath, 00069 const QStringList &args, 00070 bool readOutput) 00071 { 00072 QString trace; 00073 QTextStream stream(&trace); 00074 stream << appPath << QLatin1Char(' ') << args.join(QLatin1String(" ")); 00075 TRACE() << trace; 00076 00077 m_process.start(appPath, args); 00078 if (!m_process.waitForStarted()) { 00079 BLAME() << "Wait for started failed"; 00080 return false; 00081 } 00082 00083 if (readOutput) { 00084 m_output.clear(); 00085 00086 if (m_process.waitForReadyRead(SIGNON_EXTERNAL_PROCESS_READ_TIMEOUT)) { 00087 if (!m_process.bytesAvailable()) { 00088 BLAME() << "Coult not read output of external process "; 00089 return false; 00090 } 00091 00092 while(m_process.bytesAvailable()) 00093 m_output += m_process.readAllStandardOutput(); 00094 } 00095 } 00096 00097 if (!m_process.waitForFinished()) { 00098 TRACE() << "Wait for finished failed"; 00099 return false; 00100 } 00101 00102 return true; 00103 } 00104 00105 void SystemCommandLineCallHandler::error(QProcess::ProcessError err) 00106 { 00107 TRACE() << "Process erorr:" << err; 00108 } 00109 00110 00111 /* ------------------ PartitionHandler implementation --------------------- */ 00112 00113 bool PartitionHandler::createPartitionFile(const QString &fileName, 00114 const quint32 fileSize) 00115 { 00116 int fd = open(fileName.toLatin1().data(), 00117 O_RDWR | O_CREAT, 00118 666); 00119 00120 if (fd < 0) { 00121 BLAME() << "FAILED to create signon secure FS partition file. ERRNO:" 00122 << errno; 00123 return false; 00124 } 00125 00126 if (ftruncate(fd, fileSize * MEGA_BYTE_SIZE) == -1) { 00127 BLAME() << "FAILED to set signon secure FS partition file size. ERRNO:" 00128 << errno; 00129 return false; 00130 } 00131 00132 if (close(fd) < 0) 00133 TRACE() << "Failed to close secure FS partition file after creation."; 00134 00135 if (!setFilePermissions(fileName, signonFilePermissions)) 00136 TRACE() << "Failed to set file permissions " 00137 "for the secure storage container."; 00138 00139 return true; 00140 } 00141 00142 bool PartitionHandler::formatPartitionFile(const QString &fileName, 00143 const quint32 fileSystemType) 00144 { 00145 QString mkfsApp = QString::fromLatin1("/sbin/mkfs.ext2"); 00146 switch (fileSystemType) { 00147 case Ext2: mkfsApp = QString::fromLatin1("/sbin/mkfs.ext2"); break; 00148 case Ext3: mkfsApp = QString::fromLatin1("/sbin/mkfs.ext3"); break; 00149 case Ext4: mkfsApp = QString::fromLatin1("/sbin/mkfs.ext4"); break; 00150 default: break; 00151 } 00152 00153 SystemCommandLineCallHandler handler; 00154 return handler.makeCall( 00155 mkfsApp, 00156 QStringList() << fileName); 00157 } 00158 00159 00160 /* --------------------- MountHandler implementation ---------------------- */ 00161 00162 bool MountHandler::mount(const QString &toMount, 00163 const QString &mountPath, 00164 const QString &fileSystemTtpe) 00165 { 00166 /* Mount a filesystem. */ 00167 return (::mount(toMount.toUtf8().constData(), 00168 mountPath.toUtf8().constData(), 00169 fileSystemTtpe.toUtf8().constData(), 00170 MS_SYNCHRONOUS | MS_NOEXEC, NULL) == 0); 00171 } 00172 00173 bool MountHandler::umount(const QString &mountPath) 00174 { 00175 /* Unmount a filesystem. */ 00176 00177 //TODO - investigate why errno is EINVAL 00178 00179 TRACE() << mountPath.toUtf8().constData(); 00180 int ret = ::umount2(mountPath.toUtf8().constData(), MNT_FORCE); 00181 TRACE() << ret; 00182 00183 switch (errno) { 00184 case EAGAIN: TRACE() << "EAGAIN"; break; 00185 case EBUSY: TRACE() << "EBUSY"; break; 00186 case EFAULT: TRACE() << "EFAULT"; break; 00187 case EINVAL: TRACE() << "EINVAL"; break; 00188 case ENAMETOOLONG: TRACE() << "ENAMETOOLONG"; break; 00189 case ENOENT: TRACE() << "ENOENT"; break; 00190 case ENOMEM: TRACE() << "ENOMEM"; break; 00191 case EPERM: TRACE() << "EPERM"; break; 00192 default: TRACE() << "umount unknown error - ignoring."; 00193 } 00194 00195 //TODO - Remove 1st, uncommend 2nd lines after the fix above. 00196 // This is tmp hack so that the tests will work. 00197 return true; 00198 //return (ret == 0); 00199 } 00200 00201 /* ----------------------- LosetupHandler implementation ----------------------- */ 00202 00203 bool LosetupHandler::setupDevice(const QString &deviceName, 00204 const QString &blockDevice) 00205 { 00206 SystemCommandLineCallHandler handler; 00207 return handler.makeCall( 00208 QLatin1String("/sbin/losetup"), 00209 QStringList() << deviceName << blockDevice); 00210 } 00211 00212 QString LosetupHandler::findAvailableDevice() 00213 { 00214 SystemCommandLineCallHandler handler; 00215 QString deviceName; 00216 bool ret = handler.makeCall( 00217 QLatin1String("/sbin/losetup"), 00218 QStringList() << QLatin1String("-f"), 00219 true); 00220 00221 deviceName = QString::fromLocal8Bit(handler.output().trimmed()); 00222 00223 if (ret) 00224 return deviceName; 00225 00226 return QString(); 00227 } 00228 00229 bool LosetupHandler::releaseDevice(const QString &deviceName) 00230 { 00231 SystemCommandLineCallHandler handler; 00232 return handler.makeCall(QLatin1String("/sbin/losetup"), 00233 QStringList() << 00234 QString::fromLatin1("-d") << deviceName); 00235 } 00236 00237 /* -------------------- CrytpsetupHandler implementation ------------------ */ 00238 00239 /* 00240 Callbacks for the interface callbacks struct in crypt_options struct. 00241 */ 00242 static int yesDialog(char *msg) 00243 { 00244 Q_UNUSED(msg); 00245 return 0; 00246 } 00247 00248 static void cmdLineLog(int type, char *msg) 00249 { 00250 switch (type) { 00251 case CRYPT_LOG_NORMAL: 00252 TRACE() << msg; 00253 break; 00254 case CRYPT_LOG_ERROR: 00255 TRACE() << "Error: " << msg; 00256 break; 00257 default: 00258 TRACE() << "Internal error on logging class for msg: " << msg; 00259 break; 00260 } 00261 } 00262 00263 static void log_wrapper(int level, const char *msg, void *usrptr) 00264 { 00265 void (*xlog)(int level, char *msg) = (void (*)(int, char*)) usrptr; 00266 xlog(level, (char *)msg); 00267 } 00268 00269 static int yesDialog_wrapper(const char *msg, void *usrptr) 00270 { 00271 int (*xyesDialog)(char *msg) = (int (*)(char*)) usrptr; 00272 return xyesDialog((char*)msg); 00273 } 00274 00275 int crypt_luksFormatBinary(struct crypt_options *options, 00276 const char *pwd, 00277 unsigned int pwdLen) 00278 { 00279 struct crypt_device *cd = NULL; 00280 struct crypt_params_luks1 cp = { 00281 options->hash, 00282 options->align_payload 00283 }; 00284 int r; 00285 00286 if ((r = crypt_init(&cd, options->device))) 00287 return -EINVAL; 00288 00289 crypt_set_log_callback(cd, log_wrapper, (void*) options->icb->log); 00290 crypt_set_confirm_callback(cd, yesDialog_wrapper, 00291 (void*) options->icb->yesDialog); 00292 00293 crypt_set_timeout(cd, options->timeout); 00294 crypt_set_password_retry(cd, options->tries); 00295 crypt_set_iterarion_time(cd, options->iteration_time ?: 1000); 00296 crypt_set_password_verify(cd, options->flags & CRYPT_FLAG_VERIFY); 00297 00298 r = crypt_format(cd, CRYPT_LUKS1, 00299 SIGNON_LUKS_CIPHER_NAME, SIGNON_LUKS_CIPHER_MODE, 00300 NULL, NULL, options->key_size, &cp); 00301 if (r < 0) 00302 goto out; 00303 00304 /* Add keyslot using internally stored volume key generated during format */ 00305 r = crypt_keyslot_add_by_volume_key(cd, options->key_slot, NULL, 0, 00306 pwd, pwdLen); 00307 out: 00308 crypt_free(cd); 00309 return (r < 0) ? r : 0; 00310 00311 } 00312 00313 bool CryptsetupHandler::formatFile(const QByteArray &key, 00314 const QString &deviceName) 00315 { 00316 struct crypt_options options; 00317 00318 options.key_size = SIGNON_LUKS_KEY_SIZE / 8; 00319 options.key_slot = SIGNON_LUKS_BASE_KEYSLOT; 00320 00321 char *localDeviceName = (char *)malloc(deviceName.length() + 1); 00322 Q_ASSERT(localDeviceName != NULL); 00323 00324 strcpy(localDeviceName, deviceName.toLatin1().constData()); 00325 options.device = localDeviceName; 00326 00327 options.cipher = SIGNON_LUKS_CIPHER; 00328 options.new_key_file = NULL; 00329 00330 char *localKey = (char *)malloc(key.length()); 00331 Q_ASSERT(localKey != NULL); 00332 memcpy(localKey, key.constData(), key.length()); 00333 00334 options.flags = 0; 00335 options.iteration_time = 1000; 00336 options.timeout = 0; 00337 options.align_payload = 0; 00338 00339 static struct interface_callbacks cmd_icb; 00340 cmd_icb.yesDialog = 0; 00341 cmd_icb.log = 0; 00342 options.icb = &cmd_icb; 00343 00344 TRACE() << "Device: [" << options.device << "]"; 00345 TRACE() << "Key size:" << key.length(); 00346 00347 int ret = crypt_luksFormatBinary(&options, localKey, key.length()); 00348 00349 if (ret != 0) 00350 TRACE() << "LUKS format API call result:" << ret << "." << error(); 00351 00352 if (localDeviceName) 00353 free(localDeviceName); 00354 00355 if (localKey) { 00356 memset(localKey, 0x00, key.length()); 00357 free(localKey); 00358 } 00359 00360 return (ret == 0); 00361 } 00362 00363 int crypt_luksOpenBinary(struct crypt_options *options, 00364 const char *pwd, unsigned int pwdLen) 00365 { 00366 struct crypt_device *cd = NULL; 00367 uint32_t flags = 0; 00368 int r; 00369 00370 if ((r = crypt_init(&cd, options->device))) 00371 return -EINVAL; 00372 00373 crypt_set_log_callback(cd, log_wrapper, (void*) options->icb->log); 00374 crypt_set_confirm_callback(cd, yesDialog_wrapper, 00375 (void*) options->icb->yesDialog); 00376 00377 crypt_set_timeout(cd, options->timeout); 00378 crypt_set_password_retry(cd, options->tries); 00379 crypt_set_iterarion_time(cd, options->iteration_time ?: 1000); 00380 crypt_set_password_verify(cd, options->flags & CRYPT_FLAG_VERIFY); 00381 00382 if ((r = crypt_load(cd, CRYPT_LUKS1, NULL))) { 00383 crypt_free(cd); 00384 return r; 00385 } 00386 00387 if (options->flags & CRYPT_FLAG_READONLY) 00388 flags |= CRYPT_ACTIVATE_READONLY; 00389 00390 if (options->flags & CRYPT_FLAG_NON_EXCLUSIVE_ACCESS) 00391 flags |= CRYPT_ACTIVATE_NO_UUID; 00392 00393 if (options->key_file) 00394 r = -1; 00395 else 00396 r = crypt_activate_by_passphrase(cd, options->name, 00397 CRYPT_ANY_SLOT, 00398 pwd, pwdLen, flags); 00399 00400 crypt_free(cd); 00401 return (r < 0) ? r : 0; 00402 } 00403 00404 bool CryptsetupHandler::openFile(const QByteArray &key, 00405 const QString &deviceName, 00406 const QString &deviceMap) 00407 { 00408 struct crypt_options options; 00409 00410 char *localDeviceMap = (char *)malloc(deviceMap.length() + 1); 00411 Q_ASSERT(localDeviceMap != NULL); 00412 strcpy(localDeviceMap, deviceMap.toLatin1().constData()); 00413 options.name = localDeviceMap; 00414 00415 char *localDeviceName = (char *)malloc(deviceName.length() + 1); 00416 Q_ASSERT(localDeviceName != NULL); 00417 strcpy(localDeviceName, deviceName.toLatin1().constData()); 00418 options.device = localDeviceName; 00419 00420 char *localKey = (char *)malloc(key.length()); 00421 Q_ASSERT(localKey != NULL); 00422 memcpy(localKey, key.constData(), key.length()); 00423 00424 options.key_file = NULL; 00425 options.timeout = 0; 00426 /* 00427 Do not change this: 00428 1) In case of failure to open, libcryptsetup code will 00429 enter infinite loop - library BUG/FEATURE. 00430 2) There is no need for multiple tries, option is intended for 00431 command line use of the utility. 00432 */ 00433 options.tries = 0; 00434 options.flags = 0; 00435 00436 static struct interface_callbacks cmd_icb; 00437 cmd_icb.yesDialog = yesDialog; 00438 cmd_icb.log = cmdLineLog; 00439 options.icb = &cmd_icb; 00440 00441 TRACE() << "Device [" << options.device << "]"; 00442 TRACE() << "Map name [" << options.name << "]"; 00443 TRACE() << "Key size:" << key.length(); 00444 00445 int ret = crypt_luksOpenBinary(&options, localKey, key.length()); 00446 00447 if (ret != 0) 00448 TRACE() << "LUKS open API call result:" << ret << "." << error() << "."; 00449 00450 if (localDeviceName) 00451 free(localDeviceName); 00452 00453 if (localDeviceMap) 00454 free(localDeviceMap); 00455 00456 if (localKey) { 00457 memset(localKey, 0x00, key.length()); 00458 free(localKey); 00459 } 00460 00461 return (ret == 0); 00462 } 00463 00464 bool CryptsetupHandler::closeFile(const QString &deviceMap) 00465 { 00466 struct crypt_options options; 00467 00468 char *localDeviceMap = (char *)malloc(deviceMap.length() + 1); 00469 Q_ASSERT(localDeviceMap != NULL); 00470 strcpy(localDeviceMap, deviceMap.toLatin1().constData()); 00471 options.name = localDeviceMap; 00472 00473 static struct interface_callbacks cmd_icb; 00474 cmd_icb.yesDialog = yesDialog; 00475 cmd_icb.log = cmdLineLog; 00476 options.icb = &cmd_icb; 00477 00478 TRACE() << "Map name [" << options.name << "]"; 00479 00480 int ret = crypt_remove_device(&options); 00481 00482 if (ret != 0) 00483 TRACE() << "Cryptsetup remove API call result:" << ret << 00484 "." << error(); 00485 00486 if (localDeviceMap) 00487 free(localDeviceMap); 00488 00489 return (ret == 0); 00490 } 00491 00492 bool CryptsetupHandler::removeFile(const QString &deviceName) 00493 { 00494 Q_UNUSED(deviceName); 00495 //todo - delete file system (wipe credentials storege) is based on this 00496 return false; 00497 } 00498 00499 int crypt_luksAddKeyBinary(struct crypt_options *options, 00500 const char *pwd, unsigned int pwdLen, 00501 const char *newPwd, unsigned int newPwdLen) 00502 { 00503 struct crypt_device *cd = NULL; 00504 int r; 00505 00506 if ((r = crypt_init(&cd, options->device))) 00507 return -EINVAL; 00508 00509 crypt_set_log_callback(cd, log_wrapper, (void*) options->icb->log); 00510 crypt_set_confirm_callback(cd, yesDialog_wrapper, 00511 (void*) options->icb->yesDialog); 00512 00513 crypt_set_timeout(cd, options->timeout); 00514 crypt_set_password_retry(cd, options->tries); 00515 crypt_set_iterarion_time(cd, options->iteration_time ?: 1000); 00516 crypt_set_password_verify(cd, options->flags & CRYPT_FLAG_VERIFY); 00517 00518 if ((r = crypt_load(cd, CRYPT_LUKS1, NULL))) { 00519 crypt_free(cd); 00520 return r; 00521 } 00522 00523 if (options->key_file || options->new_key_file) 00524 r = -1; 00525 else 00526 r = crypt_keyslot_add_by_passphrase(cd, options->key_slot, 00527 pwd, pwdLen, newPwd, newPwdLen); 00528 00529 crypt_free(cd); 00530 return (r < 0) ? r : 0; 00531 } 00532 00533 bool CryptsetupHandler::addKeySlot(const QString &deviceName, 00534 const QByteArray &key, 00535 const QByteArray &existingKey) 00536 { 00537 struct crypt_options options; 00538 00539 options.key_size = SIGNON_LUKS_KEY_SIZE / 8; 00540 options.cipher = SIGNON_LUKS_CIPHER; 00541 00542 char *localDeviceName = (char *)malloc(deviceName.length() + 1); 00543 Q_ASSERT(localDeviceName != NULL); 00544 strcpy(localDeviceName, deviceName.toLatin1().constData()); 00545 00546 options.device = localDeviceName; 00547 options.new_key_file = NULL; 00548 options.key_file = NULL; 00549 options.key_slot = -1; 00550 00551 options.flags = 0; 00552 options.iteration_time = 1000; 00553 options.timeout = 0; 00554 options.tries = 0; 00555 00556 static struct interface_callbacks cmd_icb; 00557 cmd_icb.yesDialog = yesDialog; 00558 cmd_icb.log = cmdLineLog; 00559 options.icb = &cmd_icb; 00560 00561 int ret = crypt_luksAddKeyBinary(&options, 00562 existingKey.constData(), 00563 existingKey.length(), 00564 key.constData(), key.length()); 00565 00566 if (localDeviceName) 00567 free(localDeviceName); 00568 00569 if (ret != 0) 00570 TRACE() << "Cryptsetup add key API call result:" << ret << 00571 "." << error(); 00572 00573 return (ret == 0); 00574 } 00575 00576 int crypt_luksRemoveKeyBinary(struct crypt_options *options, 00577 const char *pwdToRemove, 00578 unsigned int pwdToRemoveLen) 00579 { 00580 struct crypt_device *cd = NULL; 00581 int key_slot; 00582 int r; 00583 00584 if ((r = crypt_init(&cd, options->device))) 00585 return -EINVAL; 00586 00587 crypt_set_log_callback(cd, log_wrapper, (void*) options->icb->log); 00588 crypt_set_confirm_callback(cd, yesDialog_wrapper, 00589 (void*) options->icb->yesDialog); 00590 00591 crypt_set_timeout(cd, options->timeout); 00592 crypt_set_password_retry(cd, options->tries); 00593 crypt_set_iterarion_time(cd, options->iteration_time ?: 1000); 00594 crypt_set_password_verify(cd, options->flags & CRYPT_FLAG_VERIFY); 00595 00596 if ((r = crypt_load(cd, CRYPT_LUKS1, NULL))) { 00597 crypt_free(cd); 00598 return r; 00599 } 00600 00601 if ((key_slot = crypt_keyslot_by_passphrase(cd, NULL, pwdToRemove, 00602 pwdToRemoveLen, 0, NULL)) < 0) { 00603 r = -EPERM; 00604 goto out; 00605 } 00606 00607 r = crypt_keyslot_destroy(cd, key_slot); 00608 00609 out: 00610 crypt_free(cd); 00611 return (r < 0) ? r : 0; 00612 } 00613 00614 bool CryptsetupHandler::removeKeySlot(const QString &deviceName, 00615 const QByteArray &key, 00616 const QByteArray &remainingKey) 00617 { 00618 struct crypt_options options; 00619 00620 options.key_size = SIGNON_LUKS_KEY_SIZE / 8; 00621 options.cipher = SIGNON_LUKS_CIPHER; 00622 00623 char *localDeviceName = (char *)malloc(deviceName.length() + 1); 00624 Q_ASSERT(localDeviceName != NULL); 00625 strcpy(localDeviceName, deviceName.toLatin1().constData()); 00626 00627 options.device = localDeviceName; 00628 options.new_key_file = NULL; 00629 options.key_file = NULL; 00630 options.key_slot = -1; 00631 00632 options.flags = 0; 00633 options.timeout = 0; 00634 00635 static struct interface_callbacks cmd_icb; 00636 cmd_icb.yesDialog = yesDialog; 00637 cmd_icb.log = cmdLineLog; 00638 options.icb = &cmd_icb; 00639 00640 int ret = crypt_luksRemoveKeyBinary(&options, key.constData(), key.length()); 00641 00642 if (localDeviceName) 00643 free(localDeviceName); 00644 00645 if (ret != 0) 00646 TRACE() << "Cryptsetup remove key API call result:" << ret << 00647 "." << error(); 00648 00649 return (ret == 0); 00650 } 00651 00652 bool CryptsetupHandler::loadDmMod() 00653 { 00654 SystemCommandLineCallHandler handler; 00655 return handler.makeCall( 00656 QLatin1String("/sbin/modprobe"), 00657 QStringList() << QString::fromLatin1("dm_mod")); 00658 } 00659 00660 QString CryptsetupHandler::error() 00661 { 00662 char buf[260]; 00663 crypt_get_error(buf, 256); 00664 return QString::fromLocal8Bit(buf); 00665 } 00666