signon  8.42
crypto-handlers.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 #include <sys/mount.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <stdlib.h>
29 #include <libcryptsetup.h>
30 
31 #include <QDataStream>
32 #include <QTextStream>
33 #include <QProcess>
34 #include <QLatin1Char>
35 #include <QFileInfo>
36 #include <QDir>
37 
38 #include "crypto-handlers.h"
39 #include "debug.h"
40 #include "misc.h"
41 
42 #define SIGNON_LUKS_DEFAULT_HASH "ripemd160"
43 
44 #define SIGNON_LUKS_CIPHER_NAME "aes"
45 #define SIGNON_LUKS_CIPHER_MODE "xts-plain"
46 #define SIGNON_LUKS_CIPHER \
47  SIGNON_LUKS_CIPHER_NAME "-" SIGNON_LUKS_CIPHER_MODE
48 #define SIGNON_LUKS_KEY_SIZE 256
49 #define SIGNON_LUKS_BASE_KEYSLOT 0
50 
51 #define SIGNON_EXTERNAL_PROCESS_READ_TIMEOUT 300
52 
53 #define KILO_BYTE_SIZE 1024
54 #define MEGA_BYTE_SIZE (KILO_BYTE_SIZE * 1024)
55 
56 /* ------------- SystemCommandLineCallHandler implementation -------------- */
57 
59 {
60  connect(&m_process, SIGNAL(error(QProcess::ProcessError)),
61  this, SLOT(error(QProcess::ProcessError)));
62 }
63 
65 {
66 }
67 
68 bool SystemCommandLineCallHandler::makeCall(const QString &appPath,
69  const QStringList &args,
70  bool readOutput)
71 {
72  QString trace;
73  QTextStream stream(&trace);
74  stream << appPath << QLatin1Char(' ') << args.join(QLatin1String(" "));
75  TRACE() << trace;
76 
77  m_process.start(appPath, args);
78  if (!m_process.waitForStarted()) {
79  BLAME() << "Wait for started failed";
80  return false;
81  }
82 
83  if (readOutput) {
84  m_output.clear();
85 
86  if (m_process.waitForReadyRead(SIGNON_EXTERNAL_PROCESS_READ_TIMEOUT)) {
87  if (!m_process.bytesAvailable()) {
88  BLAME() << "Coult not read output of external process ";
89  return false;
90  }
91 
92  while(m_process.bytesAvailable())
93  m_output += m_process.readAllStandardOutput();
94  }
95  }
96 
97  if (!m_process.waitForFinished()) {
98  TRACE() << "Wait for finished failed";
99  return false;
100  }
101 
102  return true;
103 }
104 
105 void SystemCommandLineCallHandler::error(QProcess::ProcessError err)
106 {
107  TRACE() << "Process erorr:" << err;
108 }
109 
110 
111 /* ------------------ PartitionHandler implementation --------------------- */
112 
113 bool PartitionHandler::createPartitionFile(const QString &fileName,
114  const quint32 fileSize)
115 {
116  int fd = open(fileName.toLatin1().data(),
117  O_RDWR | O_CREAT,
118  666);
119 
120  if (fd < 0) {
121  BLAME() << "FAILED to create signon secure FS partition file. ERRNO:"
122  << errno;
123  return false;
124  }
125 
126  if (ftruncate(fd, fileSize * MEGA_BYTE_SIZE) == -1) {
127  BLAME() << "FAILED to set signon secure FS partition file size. ERRNO:"
128  << errno;
129  return false;
130  }
131 
132  if (close(fd) < 0)
133  TRACE() << "Failed to close secure FS partition file after creation.";
134 
136  TRACE() << "Failed to set file permissions "
137  "for the secure storage container.";
138 
139  return true;
140 }
141 
142 bool PartitionHandler::formatPartitionFile(const QString &fileName,
143  const quint32 fileSystemType)
144 {
145  QString mkfsApp = QString::fromLatin1("/sbin/mkfs.ext2");
146  switch (fileSystemType) {
147  case Ext2: mkfsApp = QString::fromLatin1("/sbin/mkfs.ext2"); break;
148  case Ext3: mkfsApp = QString::fromLatin1("/sbin/mkfs.ext3"); break;
149  case Ext4: mkfsApp = QString::fromLatin1("/sbin/mkfs.ext4"); break;
150  default: break;
151  }
152 
154  return handler.makeCall(
155  mkfsApp,
156  QStringList() << fileName);
157 }
158 
159 
160 /* --------------------- MountHandler implementation ---------------------- */
161 
162 bool MountHandler::mount(const QString &toMount,
163  const QString &mountPath,
164  const QString &fileSystemTtpe)
165 {
166  /* Mount a filesystem. */
167  return (::mount(toMount.toUtf8().constData(),
168  mountPath.toUtf8().constData(),
169  fileSystemTtpe.toUtf8().constData(),
170  MS_SYNCHRONOUS | MS_NOEXEC, NULL) == 0);
171 }
172 
173 bool MountHandler::umount(const QString &mountPath)
174 {
175  /* Unmount a filesystem. */
176 
177  //TODO - investigate why errno is EINVAL
178 
179  TRACE() << mountPath.toUtf8().constData();
180  int ret = ::umount2(mountPath.toUtf8().constData(), MNT_FORCE);
181  TRACE() << ret;
182 
183  switch (errno) {
184  case EAGAIN: TRACE() << "EAGAIN"; break;
185  case EBUSY: TRACE() << "EBUSY"; break;
186  case EFAULT: TRACE() << "EFAULT"; break;
187  case EINVAL: TRACE() << "EINVAL"; break;
188  case ENAMETOOLONG: TRACE() << "ENAMETOOLONG"; break;
189  case ENOENT: TRACE() << "ENOENT"; break;
190  case ENOMEM: TRACE() << "ENOMEM"; break;
191  case EPERM: TRACE() << "EPERM"; break;
192  default: TRACE() << "umount unknown error - ignoring.";
193  }
194 
195  //TODO - Remove 1st, uncommend 2nd lines after the fix above.
196  // This is tmp hack so that the tests will work.
197  return true;
198  //return (ret == 0);
199 }
200 
201 /* ----------------------- LosetupHandler implementation ----------------------- */
202 
203 bool LosetupHandler::setupDevice(const QString &deviceName,
204  const QString &blockDevice)
205 {
207  return handler.makeCall(
208  QLatin1String("/sbin/losetup"),
209  QStringList() << deviceName << blockDevice);
210 }
211 
213 {
215  QString deviceName;
216  bool ret = handler.makeCall(
217  QLatin1String("/sbin/losetup"),
218  QStringList() << QLatin1String("-f"),
219  true);
220 
221  deviceName = QString::fromLocal8Bit(handler.output().trimmed());
222 
223  if (ret)
224  return deviceName;
225 
226  return QString();
227 }
228 
229 bool LosetupHandler::releaseDevice(const QString &deviceName)
230 {
232  return handler.makeCall(QLatin1String("/sbin/losetup"),
233  QStringList() <<
234  QString::fromLatin1("-d") << deviceName);
235 }
236 
237 /* -------------------- CrytpsetupHandler implementation ------------------ */
238 
239 /*
240  Callbacks for the interface callbacks struct in crypt_options struct.
241 */
242 static int yesDialog(char *msg)
243 {
244  Q_UNUSED(msg);
245  return 0;
246 }
247 
248 static void cmdLineLog(int type, char *msg)
249 {
250  switch (type) {
251  case CRYPT_LOG_NORMAL:
252  TRACE() << msg;
253  break;
254  case CRYPT_LOG_ERROR:
255  TRACE() << "Error: " << msg;
256  break;
257  default:
258  TRACE() << "Internal error on logging class for msg: " << msg;
259  break;
260  }
261 }
262 
263 static void log_wrapper(int level, const char *msg, void *usrptr)
264 {
265  void (*xlog)(int level, char *msg) = (void (*)(int, char*)) usrptr;
266  xlog(level, (char *)msg);
267 }
268 
269 static int yesDialog_wrapper(const char *msg, void *usrptr)
270 {
271  int (*xyesDialog)(char *msg) = (int (*)(char*)) usrptr;
272  return xyesDialog((char*)msg);
273 }
274 
275 int crypt_luksFormatBinary(struct crypt_options *options,
276  const char *pwd,
277  unsigned int pwdLen)
278 {
279  struct crypt_device *cd = NULL;
280  struct crypt_params_luks1 cp = {
281  options->hash,
282  options->align_payload
283  };
284  int r;
285 
286  if ((r = crypt_init(&cd, options->device)))
287  return -EINVAL;
288 
289  crypt_set_log_callback(cd, log_wrapper, (void*) options->icb->log);
290  crypt_set_confirm_callback(cd, yesDialog_wrapper,
291  (void*) options->icb->yesDialog);
292 
293  crypt_set_timeout(cd, options->timeout);
294  crypt_set_password_retry(cd, options->tries);
295  crypt_set_iterarion_time(cd, options->iteration_time ?: 1000);
296  crypt_set_password_verify(cd, options->flags & CRYPT_FLAG_VERIFY);
297 
298  r = crypt_format(cd, CRYPT_LUKS1,
300  NULL, NULL, options->key_size, &cp);
301  if (r < 0)
302  goto out;
303 
304  /* Add keyslot using internally stored volume key generated during format */
305  r = crypt_keyslot_add_by_volume_key(cd, options->key_slot, NULL, 0,
306  pwd, pwdLen);
307 out:
308  crypt_free(cd);
309  return (r < 0) ? r : 0;
310 
311 }
312 
313 bool CryptsetupHandler::formatFile(const QByteArray &key,
314  const QString &deviceName)
315 {
316  struct crypt_options options;
317 
318  options.key_size = SIGNON_LUKS_KEY_SIZE / 8;
319  options.key_slot = SIGNON_LUKS_BASE_KEYSLOT;
320 
321  char *localDeviceName = (char *)malloc(deviceName.length() + 1);
322  Q_ASSERT(localDeviceName != NULL);
323 
324  strcpy(localDeviceName, deviceName.toLatin1().constData());
325  options.device = localDeviceName;
326 
327  options.cipher = SIGNON_LUKS_CIPHER;
328  options.new_key_file = NULL;
329 
330  char *localKey = (char *)malloc(key.length());
331  Q_ASSERT(localKey != NULL);
332  memcpy(localKey, key.constData(), key.length());
333 
334  options.flags = 0;
335  options.iteration_time = 1000;
336  options.timeout = 0;
337  options.align_payload = 0;
338 
339  static struct interface_callbacks cmd_icb;
340  cmd_icb.yesDialog = 0;
341  cmd_icb.log = 0;
342  options.icb = &cmd_icb;
343 
344  TRACE() << "Device: [" << options.device << "]";
345  TRACE() << "Key size:" << key.length();
346 
347  int ret = crypt_luksFormatBinary(&options, localKey, key.length());
348 
349  if (ret != 0)
350  TRACE() << "LUKS format API call result:" << ret << "." << error();
351 
352  if (localDeviceName)
353  free(localDeviceName);
354 
355  if (localKey) {
356  memset(localKey, 0x00, key.length());
357  free(localKey);
358  }
359 
360  return (ret == 0);
361 }
362 
363 int crypt_luksOpenBinary(struct crypt_options *options,
364  const char *pwd, unsigned int pwdLen)
365 {
366  struct crypt_device *cd = NULL;
367  uint32_t flags = 0;
368  int r;
369 
370  if ((r = crypt_init(&cd, options->device)))
371  return -EINVAL;
372 
373  crypt_set_log_callback(cd, log_wrapper, (void*) options->icb->log);
374  crypt_set_confirm_callback(cd, yesDialog_wrapper,
375  (void*) options->icb->yesDialog);
376 
377  crypt_set_timeout(cd, options->timeout);
378  crypt_set_password_retry(cd, options->tries);
379  crypt_set_iterarion_time(cd, options->iteration_time ?: 1000);
380  crypt_set_password_verify(cd, options->flags & CRYPT_FLAG_VERIFY);
381 
382  if ((r = crypt_load(cd, CRYPT_LUKS1, NULL))) {
383  crypt_free(cd);
384  return r;
385  }
386 
387  if (options->flags & CRYPT_FLAG_READONLY)
388  flags |= CRYPT_ACTIVATE_READONLY;
389 
390  if (options->flags & CRYPT_FLAG_NON_EXCLUSIVE_ACCESS)
391  flags |= CRYPT_ACTIVATE_NO_UUID;
392 
393  if (options->key_file)
394  r = -1;
395  else
396  r = crypt_activate_by_passphrase(cd, options->name,
397  CRYPT_ANY_SLOT,
398  pwd, pwdLen, flags);
399 
400  crypt_free(cd);
401  return (r < 0) ? r : 0;
402 }
403 
404 bool CryptsetupHandler::openFile(const QByteArray &key,
405  const QString &deviceName,
406  const QString &deviceMap)
407 {
408  struct crypt_options options;
409 
410  char *localDeviceMap = (char *)malloc(deviceMap.length() + 1);
411  Q_ASSERT(localDeviceMap != NULL);
412  strcpy(localDeviceMap, deviceMap.toLatin1().constData());
413  options.name = localDeviceMap;
414 
415  char *localDeviceName = (char *)malloc(deviceName.length() + 1);
416  Q_ASSERT(localDeviceName != NULL);
417  strcpy(localDeviceName, deviceName.toLatin1().constData());
418  options.device = localDeviceName;
419 
420  char *localKey = (char *)malloc(key.length());
421  Q_ASSERT(localKey != NULL);
422  memcpy(localKey, key.constData(), key.length());
423 
424  options.key_file = NULL;
425  options.timeout = 0;
426  /*
427  Do not change this:
428  1) In case of failure to open, libcryptsetup code will
429  enter infinite loop - library BUG/FEATURE.
430  2) There is no need for multiple tries, option is intended for
431  command line use of the utility.
432  */
433  options.tries = 0;
434  options.flags = 0;
435 
436  static struct interface_callbacks cmd_icb;
437  cmd_icb.yesDialog = yesDialog;
438  cmd_icb.log = cmdLineLog;
439  options.icb = &cmd_icb;
440 
441  TRACE() << "Device [" << options.device << "]";
442  TRACE() << "Map name [" << options.name << "]";
443  TRACE() << "Key size:" << key.length();
444 
445  int ret = crypt_luksOpenBinary(&options, localKey, key.length());
446 
447  if (ret != 0)
448  TRACE() << "LUKS open API call result:" << ret << "." << error() << ".";
449 
450  if (localDeviceName)
451  free(localDeviceName);
452 
453  if (localDeviceMap)
454  free(localDeviceMap);
455 
456  if (localKey) {
457  memset(localKey, 0x00, key.length());
458  free(localKey);
459  }
460 
461  return (ret == 0);
462 }
463 
464 bool CryptsetupHandler::closeFile(const QString &deviceMap)
465 {
466  struct crypt_options options;
467 
468  char *localDeviceMap = (char *)malloc(deviceMap.length() + 1);
469  Q_ASSERT(localDeviceMap != NULL);
470  strcpy(localDeviceMap, deviceMap.toLatin1().constData());
471  options.name = localDeviceMap;
472 
473  static struct interface_callbacks cmd_icb;
474  cmd_icb.yesDialog = yesDialog;
475  cmd_icb.log = cmdLineLog;
476  options.icb = &cmd_icb;
477 
478  TRACE() << "Map name [" << options.name << "]";
479 
480  int ret = crypt_remove_device(&options);
481 
482  if (ret != 0)
483  TRACE() << "Cryptsetup remove API call result:" << ret <<
484  "." << error();
485 
486  if (localDeviceMap)
487  free(localDeviceMap);
488 
489  return (ret == 0);
490 }
491 
492 bool CryptsetupHandler::removeFile(const QString &deviceName)
493 {
494  Q_UNUSED(deviceName);
495  //todo - delete file system (wipe credentials storege) is based on this
496  return false;
497 }
498 
499 int crypt_luksAddKeyBinary(struct crypt_options *options,
500  const char *pwd, unsigned int pwdLen,
501  const char *newPwd, unsigned int newPwdLen)
502 {
503  struct crypt_device *cd = NULL;
504  int r;
505 
506  if ((r = crypt_init(&cd, options->device)))
507  return -EINVAL;
508 
509  crypt_set_log_callback(cd, log_wrapper, (void*) options->icb->log);
510  crypt_set_confirm_callback(cd, yesDialog_wrapper,
511  (void*) options->icb->yesDialog);
512 
513  crypt_set_timeout(cd, options->timeout);
514  crypt_set_password_retry(cd, options->tries);
515  crypt_set_iterarion_time(cd, options->iteration_time ?: 1000);
516  crypt_set_password_verify(cd, options->flags & CRYPT_FLAG_VERIFY);
517 
518  if ((r = crypt_load(cd, CRYPT_LUKS1, NULL))) {
519  crypt_free(cd);
520  return r;
521  }
522 
523  if (options->key_file || options->new_key_file)
524  r = -1;
525  else
526  r = crypt_keyslot_add_by_passphrase(cd, options->key_slot,
527  pwd, pwdLen, newPwd, newPwdLen);
528 
529  crypt_free(cd);
530  return (r < 0) ? r : 0;
531 }
532 
533 bool CryptsetupHandler::addKeySlot(const QString &deviceName,
534  const QByteArray &key,
535  const QByteArray &existingKey)
536 {
537  struct crypt_options options;
538 
539  options.key_size = SIGNON_LUKS_KEY_SIZE / 8;
540  options.cipher = SIGNON_LUKS_CIPHER;
541 
542  char *localDeviceName = (char *)malloc(deviceName.length() + 1);
543  Q_ASSERT(localDeviceName != NULL);
544  strcpy(localDeviceName, deviceName.toLatin1().constData());
545 
546  options.device = localDeviceName;
547  options.new_key_file = NULL;
548  options.key_file = NULL;
549  options.key_slot = -1;
550 
551  options.flags = 0;
552  options.iteration_time = 1000;
553  options.timeout = 0;
554  options.tries = 0;
555 
556  static struct interface_callbacks cmd_icb;
557  cmd_icb.yesDialog = yesDialog;
558  cmd_icb.log = cmdLineLog;
559  options.icb = &cmd_icb;
560 
561  int ret = crypt_luksAddKeyBinary(&options,
562  existingKey.constData(),
563  existingKey.length(),
564  key.constData(), key.length());
565 
566  if (localDeviceName)
567  free(localDeviceName);
568 
569  if (ret != 0)
570  TRACE() << "Cryptsetup add key API call result:" << ret <<
571  "." << error();
572 
573  return (ret == 0);
574 }
575 
576 int crypt_luksRemoveKeyBinary(struct crypt_options *options,
577  const char *pwdToRemove,
578  unsigned int pwdToRemoveLen)
579 {
580  struct crypt_device *cd = NULL;
581  int key_slot;
582  int r;
583 
584  if ((r = crypt_init(&cd, options->device)))
585  return -EINVAL;
586 
587  crypt_set_log_callback(cd, log_wrapper, (void*) options->icb->log);
588  crypt_set_confirm_callback(cd, yesDialog_wrapper,
589  (void*) options->icb->yesDialog);
590 
591  crypt_set_timeout(cd, options->timeout);
592  crypt_set_password_retry(cd, options->tries);
593  crypt_set_iterarion_time(cd, options->iteration_time ?: 1000);
594  crypt_set_password_verify(cd, options->flags & CRYPT_FLAG_VERIFY);
595 
596  if ((r = crypt_load(cd, CRYPT_LUKS1, NULL))) {
597  crypt_free(cd);
598  return r;
599  }
600 
601  if ((key_slot = crypt_keyslot_by_passphrase(cd, NULL, pwdToRemove,
602  pwdToRemoveLen, 0, NULL)) < 0) {
603  r = -EPERM;
604  goto out;
605  }
606 
607  r = crypt_keyslot_destroy(cd, key_slot);
608 
609 out:
610  crypt_free(cd);
611  return (r < 0) ? r : 0;
612 }
613 
614 bool CryptsetupHandler::removeKeySlot(const QString &deviceName,
615  const QByteArray &key,
616  const QByteArray &remainingKey)
617 {
618  struct crypt_options options;
619 
620  options.key_size = SIGNON_LUKS_KEY_SIZE / 8;
621  options.cipher = SIGNON_LUKS_CIPHER;
622 
623  char *localDeviceName = (char *)malloc(deviceName.length() + 1);
624  Q_ASSERT(localDeviceName != NULL);
625  strcpy(localDeviceName, deviceName.toLatin1().constData());
626 
627  options.device = localDeviceName;
628  options.new_key_file = NULL;
629  options.key_file = NULL;
630  options.key_slot = -1;
631 
632  options.flags = 0;
633  options.timeout = 0;
634 
635  static struct interface_callbacks cmd_icb;
636  cmd_icb.yesDialog = yesDialog;
637  cmd_icb.log = cmdLineLog;
638  options.icb = &cmd_icb;
639 
640  int ret = crypt_luksRemoveKeyBinary(&options, key.constData(), key.length());
641 
642  if (localDeviceName)
643  free(localDeviceName);
644 
645  if (ret != 0)
646  TRACE() << "Cryptsetup remove key API call result:" << ret <<
647  "." << error();
648 
649  return (ret == 0);
650 }
651 
653 {
655  return handler.makeCall(
656  QLatin1String("/sbin/modprobe"),
657  QStringList() << QString::fromLatin1("dm_mod"));
658 }
659 
661 {
662  char buf[260];
663  crypt_get_error(buf, 256);
664  return QString::fromLocal8Bit(buf);
665 }
666