Fix some problems found by Coverity static analysis.
[platform/upstream/cryptsetup.git] / lib / luks1 / keymanage.c
index b75e645..52df159 100644 (file)
@@ -2,6 +2,7 @@
  * LUKS - Linux Unified Key Setup
  *
  * Copyright (C) 2004-2006, Clemens Fruhwirth <clemens@endorphin.org>
+ * Copyright (C) 2009-2012, Red Hat, Inc. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <linux/fs.h>
 #include <netinet/in.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -29,6 +28,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
+#include <assert.h>
 #include <uuid/uuid.h>
 
 #include "luks.h"
 #include "pbkdf.h"
 #include "internal.h"
 
-#define div_round_up(a,b) ({           \
-       typeof(a) __a = (a);          \
-       typeof(b) __b = (b);          \
-       (__a - 1) / __b + 1;        \
-})
+/* Get size of struct luks_phdr with all keyslots material space */
+static size_t LUKS_device_sectors(size_t keyLen)
+{
+       size_t keyslot_sectors, sector;
+       int i;
+
+       keyslot_sectors = AF_split_sectors(keyLen, LUKS_STRIPES);
+       sector = LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE;
+
+       for (i = 0; i < LUKS_NUMKEYS; i++) {
+               sector = size_round_up(sector, LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE);
+               sector += keyslot_sectors;
+       }
+
+       return sector;
+}
+
+int LUKS_keyslot_area(struct luks_phdr *hdr,
+       int keyslot,
+       uint64_t *offset,
+       uint64_t *length)
+{
+       if(keyslot >= LUKS_NUMKEYS || keyslot < 0)
+               return -EINVAL;
+
+       *offset = hdr->keyblock[keyslot].keyMaterialOffset * SECTOR_SIZE;
+       *length = AF_split_sectors(hdr->keyBytes, LUKS_STRIPES) * SECTOR_SIZE;
+
+       return 0;
+}
+
+static int LUKS_check_device_size(struct crypt_device *ctx, size_t keyLength)
+{
+       struct device *device = crypt_metadata_device(ctx);
+       uint64_t dev_sectors, hdr_sectors;
+
+       if (!keyLength)
+               return -EINVAL;
+
+       if(device_size(device, &dev_sectors)) {
+               log_dbg("Cannot get device size for device %s.", device_path(device));
+               return -EIO;
+       }
+
+       dev_sectors >>= SECTOR_SHIFT;
+       hdr_sectors = LUKS_device_sectors(keyLength);
+       log_dbg("Key length %u, device size %" PRIu64 " sectors, header size %"
+               PRIu64 " sectors.",keyLength, dev_sectors, hdr_sectors);
 
-static inline int round_up_modulo(int x, int m) {
-       return div_round_up(x, m) * m;
+       if (hdr_sectors > dev_sectors) {
+               log_err(ctx, _("Device %s is too small.\n"), device_path(device));
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/* Check keyslot to prevent access outside of header and keyslot area */
+static int LUKS_check_keyslot_size(const struct luks_phdr *phdr, unsigned int keyIndex)
+{
+       uint32_t secs_per_stripes;
+
+       /* First sectors is the header itself */
+       if (phdr->keyblock[keyIndex].keyMaterialOffset * SECTOR_SIZE < sizeof(*phdr)) {
+               log_dbg("Invalid offset %u in keyslot %u.",
+                       phdr->keyblock[keyIndex].keyMaterialOffset, keyIndex);
+               return 1;
+       }
+
+       /* Ignore following check for detached header where offset can be zero. */
+       if (phdr->payloadOffset == 0)
+               return 0;
+
+       if (phdr->payloadOffset <= phdr->keyblock[keyIndex].keyMaterialOffset) {
+               log_dbg("Invalid offset %u in keyslot %u (beyond data area offset %u).",
+                       phdr->keyblock[keyIndex].keyMaterialOffset, keyIndex,
+                       phdr->payloadOffset);
+               return 1;
+       }
+
+       secs_per_stripes = AF_split_sectors(phdr->keyBytes, phdr->keyblock[keyIndex].stripes);
+
+       if (phdr->payloadOffset < (phdr->keyblock[keyIndex].keyMaterialOffset + secs_per_stripes)) {
+               log_dbg("Invalid keyslot size %u (offset %u, stripes %u) in "
+                       "keyslot %u (beyond data area offset %u).",
+                       secs_per_stripes,
+                       phdr->keyblock[keyIndex].keyMaterialOffset,
+                       phdr->keyblock[keyIndex].stripes,
+                       keyIndex, phdr->payloadOffset);
+               return 1;
+       }
+
+       return 0;
+}
+
+static const char *dbg_slot_state(crypt_keyslot_info ki)
+{
+       switch(ki) {
+       case CRYPT_SLOT_INACTIVE:
+               return "INACTIVE";
+       case CRYPT_SLOT_ACTIVE:
+               return "ACTIVE";
+       case CRYPT_SLOT_ACTIVE_LAST:
+               return "ACTIVE_LAST";
+       case CRYPT_SLOT_INVALID:
+       default:
+               return "INVALID";
+       }
 }
 
 int LUKS_hdr_backup(
        const char *backup_file,
-       const char *device,
        struct luks_phdr *hdr,
        struct crypt_device *ctx)
 {
+       struct device *device = crypt_metadata_device(ctx);
        int r = 0, devfd = -1;
-       size_t buffer_size;
+       ssize_t buffer_size;
        char *buffer = NULL;
        struct stat st;
 
@@ -62,11 +162,11 @@ int LUKS_hdr_backup(
                return -EINVAL;
        }
 
-       r = LUKS_read_phdr(device, hdr, 1, ctx);
+       r = LUKS_read_phdr(hdr, 1, 0, ctx);
        if (r)
                return r;
 
-       buffer_size = hdr->payloadOffset << SECTOR_SHIFT;
+       buffer_size = LUKS_device_sectors(hdr->keyBytes) << SECTOR_SHIFT;
        buffer = crypt_safe_alloc(buffer_size);
        if (!buffer || buffer_size < LUKS_ALIGN_KEYSLOTS) {
                r = -ENOMEM;
@@ -76,21 +176,22 @@ int LUKS_hdr_backup(
        log_dbg("Storing backup of header (%u bytes) and keyslot area (%u bytes).",
                sizeof(*hdr), buffer_size - LUKS_ALIGN_KEYSLOTS);
 
-       devfd = open(device, O_RDONLY | O_DIRECT | O_SYNC);
+       devfd = open(device_path(device), O_RDONLY | O_DIRECT | O_SYNC);
        if(devfd == -1) {
-               log_err(ctx, _("Device %s is not a valid LUKS device.\n"), device);
+               log_err(ctx, _("Device %s is not a valid LUKS device.\n"), device_path(device));
                r = -EINVAL;
                goto out;
        }
 
-       if(read_blockwise(devfd, buffer, buffer_size) < buffer_size) {
+       if(read_blockwise(devfd, device_block_size(device), buffer, buffer_size) < buffer_size) {
                r = -EIO;
                goto out;
        }
        close(devfd);
 
        /* Wipe unused area, so backup cannot contain old signatures */
-       memset(buffer + sizeof(*hdr), 0, LUKS_ALIGN_KEYSLOTS - sizeof(*hdr));
+       if (hdr->keyblock[0].keyMaterialOffset * SECTOR_SIZE == LUKS_ALIGN_KEYSLOTS)
+               memset(buffer + sizeof(*hdr), 0, LUKS_ALIGN_KEYSLOTS - sizeof(*hdr));
 
        devfd = creat(backup_file, S_IRUSR);
        if(devfd == -1) {
@@ -114,12 +215,12 @@ out:
 
 int LUKS_hdr_restore(
        const char *backup_file,
-       const char *device,
        struct luks_phdr *hdr,
        struct crypt_device *ctx)
 {
+       struct device *device = crypt_metadata_device(ctx);
        int r = 0, devfd = -1, diff_uuid = 0;
-       size_t buffer_size;
+       ssize_t buffer_size = 0;
        char *buffer = NULL, msg[200];
        struct stat st;
        struct luks_phdr hdr_file;
@@ -129,11 +230,12 @@ int LUKS_hdr_restore(
                return -EINVAL;
        }
 
-       r = LUKS_read_phdr_backup(backup_file, device, &hdr_file, 0, ctx);
-       buffer_size = hdr_file.payloadOffset << SECTOR_SHIFT;
+       r = LUKS_read_phdr_backup(backup_file, &hdr_file, 0, ctx);
+       if (!r)
+               buffer_size = LUKS_device_sectors(hdr_file.keyBytes) << SECTOR_SHIFT;
 
        if (r || buffer_size < LUKS_ALIGN_KEYSLOTS) {
-               log_err(ctx, _("Backup file do not contain valid LUKS header.\n"));
+               log_err(ctx, _("Backup file doesn't contain valid LUKS header.\n"));
                r = -EINVAL;
                goto out;
        }
@@ -158,9 +260,9 @@ int LUKS_hdr_restore(
        }
        close(devfd);
 
-       r = LUKS_read_phdr(device, hdr, 0, ctx);
+       r = LUKS_read_phdr(hdr, 0, 0, ctx);
        if (r == 0) {
-               log_dbg("Device %s already contains LUKS header, checking UUID and offset.", device);
+               log_dbg("Device %s already contains LUKS header, checking UUID and offset.", device_path(device));
                if(hdr->payloadOffset != hdr_file.payloadOffset ||
                   hdr->keyBytes != hdr_file.keyBytes) {
                        log_err(ctx, _("Data offset or key size differs on device and backup, restore failed.\n"));
@@ -171,7 +273,7 @@ int LUKS_hdr_restore(
                        diff_uuid = 1;
        }
 
-       if (snprintf(msg, sizeof(msg), _("Device %s %s%s"), device,
+       if (snprintf(msg, sizeof(msg), _("Device %s %s%s"), device_path(device),
                 r ? _("does not contain LUKS header. Replacing header can destroy data on that device.") :
                     _("already contains LUKS header. Replacing header will destroy existing keyslots."),
                     diff_uuid ? _("\nWARNING: real device header has different UUID than backup!") : "") < 0) {
@@ -185,23 +287,27 @@ int LUKS_hdr_restore(
        }
 
        log_dbg("Storing backup of header (%u bytes) and keyslot area (%u bytes) to device %s.",
-               sizeof(*hdr), buffer_size - LUKS_ALIGN_KEYSLOTS, device);
+               sizeof(*hdr), buffer_size - LUKS_ALIGN_KEYSLOTS, device_path(device));
 
-       devfd = open(device, O_WRONLY | O_DIRECT | O_SYNC);
+       devfd = open(device_path(device), O_WRONLY | O_DIRECT | O_SYNC);
        if(devfd == -1) {
-               log_err(ctx, _("Cannot open device %s.\n"), device);
+               if (errno == EACCES)
+                       log_err(ctx, _("Cannot write to device %s, permission denied.\n"),
+                               device_path(device));
+               else
+                       log_err(ctx, _("Cannot open device %s.\n"), device_path(device));
                r = -EINVAL;
                goto out;
        }
 
-       if(write_blockwise(devfd, buffer, buffer_size) < buffer_size) {
+       if (write_blockwise(devfd, device_block_size(device), buffer, buffer_size) < buffer_size) {
                r = -EIO;
                goto out;
        }
        close(devfd);
 
        /* Be sure to reload new data */
-       r = LUKS_read_phdr(device, hdr, 0, ctx);
+       r = LUKS_read_phdr(hdr, 1, 0, ctx);
 out:
        if (devfd != -1)
                close(devfd);
@@ -209,9 +315,96 @@ out:
        return r;
 }
 
+/* This routine should do some just basic recovery for known problems. */
+static int _keyslot_repair(struct luks_phdr *phdr, struct crypt_device *ctx)
+{
+       struct luks_phdr temp_phdr;
+       const unsigned char *sector = (const unsigned char*)phdr;
+       struct volume_key *vk;
+       uint64_t PBKDF2_per_sec = 1;
+       int i, bad, r, need_write = 0;
+
+       if (phdr->keyBytes != 16 && phdr->keyBytes != 32) {
+               log_err(ctx, _("Non standard key size, manual repair required.\n"));
+               return -EINVAL;
+       }
+       /* cryptsetup 1.0 did not align to 4k, cannot repair this one */
+       if (phdr->keyblock[0].keyMaterialOffset < (LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE)) {
+               log_err(ctx, _("Non standard keyslots alignment, manual repair required.\n"));
+               return -EINVAL;
+       }
+
+       vk = crypt_alloc_volume_key(phdr->keyBytes, NULL);
+
+       log_verbose(ctx, _("Repairing keyslots.\n"));
+
+       log_dbg("Generating second header with the same parameters for check.");
+       /* cipherName, cipherMode, hashSpec, uuid are already null terminated */
+       /* payloadOffset - cannot check */
+       r = LUKS_generate_phdr(&temp_phdr, vk, phdr->cipherName, phdr->cipherMode,
+                              phdr->hashSpec,phdr->uuid, LUKS_STRIPES,
+                              phdr->payloadOffset, 0,
+                              1, &PBKDF2_per_sec,
+                              1, ctx);
+       if (r < 0) {
+               log_err(ctx, _("Repair failed."));
+               goto out;
+       }
+
+       for(i = 0; i < LUKS_NUMKEYS; ++i) {
+               if (phdr->keyblock[i].active == LUKS_KEY_ENABLED)  {
+                       log_dbg("Skipping repair for active keyslot %i.", i);
+                       continue;
+               }
+
+               bad = 0;
+               if (phdr->keyblock[i].keyMaterialOffset != temp_phdr.keyblock[i].keyMaterialOffset) {
+                       log_err(ctx, _("Keyslot %i: offset repaired (%u -> %u).\n"), i,
+                               (unsigned)phdr->keyblock[i].keyMaterialOffset,
+                               (unsigned)temp_phdr.keyblock[i].keyMaterialOffset);
+                       phdr->keyblock[i].keyMaterialOffset = temp_phdr.keyblock[i].keyMaterialOffset;
+                       bad = 1;
+               }
+
+               if (phdr->keyblock[i].stripes != temp_phdr.keyblock[i].stripes) {
+                       log_err(ctx, _("Keyslot %i: stripes repaired (%u -> %u).\n"), i,
+                               (unsigned)phdr->keyblock[i].stripes,
+                               (unsigned)temp_phdr.keyblock[i].stripes);
+                       phdr->keyblock[i].stripes = temp_phdr.keyblock[i].stripes;
+                       bad = 1;
+               }
+
+               /* Known case - MSDOS partition table signature */
+               if (i == 6 && sector[0x1fe] == 0x55 && sector[0x1ff] == 0xaa) {
+                       log_err(ctx, _("Keyslot %i: bogus partition signature.\n"), i);
+                       bad = 1;
+               }
+
+               if(bad) {
+                       log_err(ctx, _("Keyslot %i: salt wiped.\n"), i);
+                       phdr->keyblock[i].active = LUKS_KEY_DISABLED;
+                       memset(&phdr->keyblock[i].passwordSalt, 0x00, LUKS_SALTSIZE);
+                       phdr->keyblock[i].passwordIterations = 0;
+               }
+
+               if (bad)
+                       need_write = 1;
+       }
+
+       if (need_write) {
+               log_verbose(ctx, _("Writing LUKS header to disk.\n"));
+               r = LUKS_write_phdr(phdr, ctx);
+       }
+out:
+       crypt_free_volume_key(vk);
+       memset(&temp_phdr, 0, sizeof(temp_phdr));
+       return r;
+}
+
 static int _check_and_convert_hdr(const char *device,
                                  struct luks_phdr *hdr,
                                  int require_luks_device,
+                                 int repair,
                                  struct crypt_device *ctx)
 {
        int r = 0;
@@ -222,28 +415,46 @@ static int _check_and_convert_hdr(const char *device,
                log_dbg("LUKS header not detected.");
                if (require_luks_device)
                        log_err(ctx, _("Device %s is not a valid LUKS device.\n"), device);
-               else
-                       set_error(_("Device %s is not a valid LUKS device."), device);
-               r = -EINVAL;
+               return -EINVAL;
        } else if((hdr->version = ntohs(hdr->version)) != 1) {  /* Convert every uint16/32_t item from network byte order */
                log_err(ctx, _("Unsupported LUKS version %d.\n"), hdr->version);
-               r = -EINVAL;
-       } else if (PBKDF2_HMAC_ready(hdr->hashSpec) < 0) {
+               return -EINVAL;
+       }
+
+       hdr->hashSpec[LUKS_HASHSPEC_L - 1] = '\0';
+       if (PBKDF2_HMAC_ready(hdr->hashSpec) < 0) {
                log_err(ctx, _("Requested LUKS hash %s is not supported.\n"), hdr->hashSpec);
-               r = -EINVAL;
-       } else {
-               hdr->payloadOffset      = ntohl(hdr->payloadOffset);
-               hdr->keyBytes           = ntohl(hdr->keyBytes);
-               hdr->mkDigestIterations = ntohl(hdr->mkDigestIterations);
-
-               for(i = 0; i < LUKS_NUMKEYS; ++i) {
-                       hdr->keyblock[i].active             = ntohl(hdr->keyblock[i].active);
-                       hdr->keyblock[i].passwordIterations = ntohl(hdr->keyblock[i].passwordIterations);
-                       hdr->keyblock[i].keyMaterialOffset  = ntohl(hdr->keyblock[i].keyMaterialOffset);
-                       hdr->keyblock[i].stripes            = ntohl(hdr->keyblock[i].stripes);
+               return -EINVAL;
+       }
+
+       /* Header detected */
+       hdr->payloadOffset      = ntohl(hdr->payloadOffset);
+       hdr->keyBytes           = ntohl(hdr->keyBytes);
+       hdr->mkDigestIterations = ntohl(hdr->mkDigestIterations);
+
+       for(i = 0; i < LUKS_NUMKEYS; ++i) {
+               hdr->keyblock[i].active             = ntohl(hdr->keyblock[i].active);
+               hdr->keyblock[i].passwordIterations = ntohl(hdr->keyblock[i].passwordIterations);
+               hdr->keyblock[i].keyMaterialOffset  = ntohl(hdr->keyblock[i].keyMaterialOffset);
+               hdr->keyblock[i].stripes            = ntohl(hdr->keyblock[i].stripes);
+               if (LUKS_check_keyslot_size(hdr, i)) {
+                       log_err(ctx, _("LUKS keyslot %u is invalid.\n"), i);
+                       r = -EINVAL;
                }
        }
 
+       /* Avoid unterminated strings */
+       hdr->cipherName[LUKS_CIPHERNAME_L - 1] = '\0';
+       hdr->cipherMode[LUKS_CIPHERMODE_L - 1] = '\0';
+       hdr->uuid[UUID_STRING_L - 1] = '\0';
+
+       if (repair) {
+               if (r == -EINVAL)
+                       r = _keyslot_repair(hdr, ctx);
+               else
+                       log_verbose(ctx, _("No known problems detected for LUKS header.\n"));
+       }
+
        return r;
 }
 
@@ -262,86 +473,102 @@ static void LUKS_fix_header_compatible(struct luks_phdr *header)
 }
 
 int LUKS_read_phdr_backup(const char *backup_file,
-                         const char *device,
                          struct luks_phdr *hdr,
                          int require_luks_device,
                          struct crypt_device *ctx)
 {
+       ssize_t hdr_size = sizeof(struct luks_phdr);
        int devfd = 0, r = 0;
 
        log_dbg("Reading LUKS header of size %d from backup file %s",
-               sizeof(struct luks_phdr), backup_file);
+               (int)hdr_size, backup_file);
 
        devfd = open(backup_file, O_RDONLY);
        if(-1 == devfd) {
-               log_err(ctx, _("Cannot open file %s.\n"), device);
+               log_err(ctx, _("Cannot open file %s.\n"), backup_file);
                return -EINVAL;
        }
 
-       if(read(devfd, hdr, sizeof(struct luks_phdr)) < sizeof(struct luks_phdr))
+       if (read(devfd, hdr, hdr_size) < hdr_size)
                r = -EIO;
        else {
                LUKS_fix_header_compatible(hdr);
-               r = _check_and_convert_hdr(backup_file, hdr, require_luks_device, ctx);
+               r = _check_and_convert_hdr(backup_file, hdr,
+                                          require_luks_device, 0, ctx);
        }
 
        close(devfd);
        return r;
 }
 
-int LUKS_read_phdr(const char *device,
-                  struct luks_phdr *hdr,
+int LUKS_read_phdr(struct luks_phdr *hdr,
                   int require_luks_device,
+                  int repair,
                   struct crypt_device *ctx)
 {
+       struct device *device = crypt_metadata_device(ctx);
+       ssize_t hdr_size = sizeof(struct luks_phdr);
        int devfd = 0, r = 0;
-       uint64_t size;
+
+       /* LUKS header starts at offset 0, first keyslot on LUKS_ALIGN_KEYSLOTS */
+       assert(sizeof(struct luks_phdr) <= LUKS_ALIGN_KEYSLOTS);
+
+       /* Stripes count cannot be changed without additional code fixes yet */
+       assert(LUKS_STRIPES == 4000);
+
+       if (repair && !require_luks_device)
+               return -EINVAL;
 
        log_dbg("Reading LUKS header of size %d from device %s",
-               sizeof(struct luks_phdr), device);
+               hdr_size, device_path(device));
 
-       devfd = open(device,O_RDONLY | O_DIRECT | O_SYNC);
-       if(-1 == devfd) {
-               log_err(ctx, _("Cannot open device %s.\n"), device);
+       devfd = open(device_path(device), O_RDONLY | O_DIRECT | O_SYNC);
+       if (devfd == -1) {
+               log_err(ctx, _("Cannot open device %s.\n"), device_path(device));
                return -EINVAL;
        }
 
-       if(read_blockwise(devfd, hdr, sizeof(struct luks_phdr)) < sizeof(struct luks_phdr))
+       if (read_blockwise(devfd, device_block_size(device), hdr, hdr_size) < hdr_size)
                r = -EIO;
        else
-               r = _check_and_convert_hdr(device, hdr, require_luks_device, ctx);
+               r = _check_and_convert_hdr(device_path(device), hdr, require_luks_device,
+                                          repair, ctx);
 
-#ifdef BLKGETSIZE64
-       if (r == 0 && (ioctl(devfd, BLKGETSIZE64, &size) < 0 ||
-           size < (uint64_t)hdr->payloadOffset)) {
-               log_err(ctx, _("LUKS header detected but device %s is too small.\n"), device);
-               r = -EINVAL;
-       }
-#endif
-       close(devfd);
+       if (!r)
+               r = LUKS_check_device_size(ctx, hdr->keyBytes);
 
+       close(devfd);
        return r;
 }
 
-int LUKS_write_phdr(const char *device,
-                   struct luks_phdr *hdr,
+int LUKS_write_phdr(struct luks_phdr *hdr,
                    struct crypt_device *ctx)
 {
+       struct device *device = crypt_metadata_device(ctx);
+       ssize_t hdr_size = sizeof(struct luks_phdr);
        int devfd = 0;
        unsigned int i;
        struct luks_phdr convHdr;
        int r;
 
        log_dbg("Updating LUKS header of size %d on device %s",
-               sizeof(struct luks_phdr), device);
+               sizeof(struct luks_phdr), device_path(device));
 
-       devfd = open(device,O_RDWR | O_DIRECT | O_SYNC);
+       r = LUKS_check_device_size(ctx, hdr->keyBytes);
+       if (r)
+               return r;
+
+       devfd = open(device_path(device), O_RDWR | O_DIRECT | O_SYNC);
        if(-1 == devfd) {
-               log_err(ctx, _("Cannot open device %s.\n"), device);
+               if (errno == EACCES)
+                       log_err(ctx, _("Cannot write to device %s, permission denied.\n"),
+                               device_path(device));
+               else
+                       log_err(ctx, _("Cannot open device %s.\n"), device_path(device));
                return -EINVAL;
        }
 
-       memcpy(&convHdr, hdr, sizeof(struct luks_phdr));
+       memcpy(&convHdr, hdr, hdr_size);
        memset(&convHdr._padding, 0, sizeof(convHdr._padding));
 
        /* Convert every uint16/32_t item to network byte order */
@@ -356,16 +583,17 @@ int LUKS_write_phdr(const char *device,
                convHdr.keyblock[i].stripes            = htonl(hdr->keyblock[i].stripes);
        }
 
-       r = write_blockwise(devfd, &convHdr, sizeof(struct luks_phdr)) < sizeof(struct luks_phdr) ? -EIO : 0;
+       r = write_blockwise(devfd, device_block_size(device), &convHdr, hdr_size) < hdr_size ? -EIO : 0;
        if (r)
-               log_err(ctx, _("Error during update of LUKS header on device %s.\n"), device);
+               log_err(ctx, _("Error during update of LUKS header on device %s.\n"), device_path(device));
        close(devfd);
 
        /* Re-read header from disk to be sure that in-memory and on-disk data are the same. */
        if (!r) {
-               r = LUKS_read_phdr(device, hdr, 1, ctx);
+               r = LUKS_read_phdr(hdr, 1, 0, ctx);
                if (r)
-                       log_err(ctx, _("Error re-reading LUKS header after update on device %s.\n"), device);
+                       log_err(ctx, _("Error re-reading LUKS header after update on device %s.\n"),
+                               device_path(device));
        }
 
        return r;
@@ -394,16 +622,17 @@ int LUKS_generate_phdr(struct luks_phdr *header,
                       unsigned int alignOffset,
                       uint32_t iteration_time_ms,
                       uint64_t *PBKDF2_per_sec,
+                      int detached_metadata_device,
                       struct crypt_device *ctx)
 {
        unsigned int i=0;
-       unsigned int blocksPerStripeSet = div_round_up(vk->keylength*stripes,SECTOR_SIZE);
+       size_t blocksPerStripeSet, currentSector;
        int r;
-       char luksMagic[] = LUKS_MAGIC;
        uuid_t partitionUuid;
-       int currentSector;
+       char luksMagic[] = LUKS_MAGIC;
 
-       if (alignPayload == 0)
+       /* For separate metadata device allow zero alignment */
+       if (alignPayload == 0 && !detached_metadata_device)
                alignPayload = DEFAULT_DISK_ALIGNMENT / SECTOR_SIZE;
 
        if (PBKDF2_HMAC_ready(hashSpec) < 0) {
@@ -435,7 +664,7 @@ int LUKS_generate_phdr(struct luks_phdr *header,
                header->version, header->hashSpec ,header->cipherName, header->cipherMode,
                header->keyBytes);
 
-       r = crypt_random_get(ctx, header->mkDigestSalt, LUKS_SALTSIZE, CRYPT_RND_NORMAL);
+       r = crypt_random_get(ctx, header->mkDigestSalt, LUKS_SALTSIZE, CRYPT_RND_SALT);
        if(r < 0) {
                log_err(ctx,  _("Cannot create LUKS header: reading random salt failed.\n"));
                return r;
@@ -459,18 +688,24 @@ int LUKS_generate_phdr(struct luks_phdr *header,
                return r;
        }
 
-       currentSector = round_up_modulo(LUKS_PHDR_SIZE, LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE);
+       currentSector = LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE;
+       blocksPerStripeSet = AF_split_sectors(vk->keylength, stripes);
        for(i = 0; i < LUKS_NUMKEYS; ++i) {
                header->keyblock[i].active = LUKS_KEY_DISABLED;
                header->keyblock[i].keyMaterialOffset = currentSector;
                header->keyblock[i].stripes = stripes;
-               currentSector = round_up_modulo(currentSector + blocksPerStripeSet,
+               currentSector = size_round_up(currentSector + blocksPerStripeSet,
                                                LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE);
        }
-       currentSector = round_up_modulo(currentSector, alignPayload);
 
-       /* alignOffset - offset from natural device alignment provided by topology info */
-       header->payloadOffset = currentSector + alignOffset;
+       if (detached_metadata_device) {
+               /* for separate metadata device use alignPayload directly */
+               header->payloadOffset = alignPayload;
+       } else {
+               /* alignOffset - offset from natural device alignment provided by topology info */
+               currentSector = size_round_up(currentSector, alignPayload);
+               header->payloadOffset = currentSector + alignOffset;
+       }
 
         uuid_unparse(partitionUuid, header->uuid);
 
@@ -481,7 +716,6 @@ int LUKS_generate_phdr(struct luks_phdr *header,
 }
 
 int LUKS_hdr_uuid_set(
-       const char *device,
        struct luks_phdr *hdr,
        const char *uuid,
        struct crypt_device *ctx)
@@ -497,19 +731,19 @@ int LUKS_hdr_uuid_set(
 
        uuid_unparse(partitionUuid, hdr->uuid);
 
-       return LUKS_write_phdr(device, hdr, ctx);
+       return LUKS_write_phdr(hdr, ctx);
 }
 
-int LUKS_set_key(const char *device, unsigned int keyIndex,
+int LUKS_set_key(unsigned int keyIndex,
                 const char *password, size_t passwordLen,
                 struct luks_phdr *hdr, struct volume_key *vk,
                 uint32_t iteration_time_ms,
                 uint64_t *PBKDF2_per_sec,
                 struct crypt_device *ctx)
 {
-       char derivedKey[hdr->keyBytes];
-       char *AfKey;
-       unsigned int AFEKSize;
+       struct volume_key *derived_key;
+       char *AfKey = NULL;
+       size_t AFEKSize;
        uint64_t PBKDF2_temp;
        int r;
 
@@ -518,7 +752,8 @@ int LUKS_set_key(const char *device, unsigned int keyIndex,
                return -EINVAL;
        }
 
-       if(hdr->keyblock[keyIndex].stripes < LUKS_STRIPES) {
+       /* LUKS keyslot has always at least 4000 stripes accoding to specification */
+       if(hdr->keyblock[keyIndex].stripes < 4000) {
                log_err(ctx, _("Key slot %d material includes too few stripes. Header manipulation?\n"),
                        keyIndex);
                 return -EINVAL;
@@ -542,57 +777,64 @@ int LUKS_set_key(const char *device, unsigned int keyIndex,
 
        log_dbg("Key slot %d use %d password iterations.", keyIndex, hdr->keyblock[keyIndex].passwordIterations);
 
-       r = crypt_random_get(ctx, hdr->keyblock[keyIndex].passwordSalt,
-                      LUKS_SALTSIZE, CRYPT_RND_NORMAL);
-       if(r < 0) return r;
+       derived_key = crypt_alloc_volume_key(hdr->keyBytes, NULL);
+       if (!derived_key)
+               return -ENOMEM;
 
-//     assert((vk->keylength % TWOFISH_BLOCKSIZE) == 0); FIXME
+       r = crypt_random_get(ctx, hdr->keyblock[keyIndex].passwordSalt,
+                      LUKS_SALTSIZE, CRYPT_RND_SALT);
+       if (r < 0)
+               goto out;
 
        r = PBKDF2_HMAC(hdr->hashSpec, password,passwordLen,
                        hdr->keyblock[keyIndex].passwordSalt,LUKS_SALTSIZE,
                        hdr->keyblock[keyIndex].passwordIterations,
-                       derivedKey, hdr->keyBytes);
-       if(r < 0) return r;
+                       derived_key->key, hdr->keyBytes);
+       if (r < 0)
+               goto out;
 
        /*
-        * AF splitting, the masterkey stored in vk->key is splitted to AfMK
+        * AF splitting, the masterkey stored in vk->key is split to AfKey
         */
-       AFEKSize = hdr->keyblock[keyIndex].stripes*vk->keylength;
-       AfKey = (char *)malloc(AFEKSize);
-       if(AfKey == NULL) return -ENOMEM;
+       assert(vk->keylength == hdr->keyBytes);
+       AFEKSize = AF_split_sectors(vk->keylength, hdr->keyblock[keyIndex].stripes) * SECTOR_SIZE;
+       AfKey = crypt_safe_alloc(AFEKSize);
+       if (!AfKey) {
+               r = -ENOMEM;
+               goto out;
+       }
 
        log_dbg("Using hash %s for AF in key slot %d, %d stripes",
                hdr->hashSpec, keyIndex, hdr->keyblock[keyIndex].stripes);
        r = AF_split(vk->key,AfKey,vk->keylength,hdr->keyblock[keyIndex].stripes,hdr->hashSpec);
-       if(r < 0) goto out;
+       if (r < 0)
+               goto out;
 
-       log_dbg("Updating key slot %d [0x%04x] area on device %s.", keyIndex,
-               hdr->keyblock[keyIndex].keyMaterialOffset << 9, device);
+       log_dbg("Updating key slot %d [0x%04x] area.", keyIndex,
+               hdr->keyblock[keyIndex].keyMaterialOffset << 9);
        /* Encryption via dm */
        r = LUKS_encrypt_to_storage(AfKey,
                                    AFEKSize,
                                    hdr,
-                                   derivedKey,
-                                   hdr->keyBytes,
-                                   device,
+                                   derived_key,
                                    hdr->keyblock[keyIndex].keyMaterialOffset,
                                    ctx);
-       if(r < 0) {
-               if(!get_error())
-                       log_err(ctx, _("Failed to write to key storage.\n"));
+       if (r < 0)
                goto out;
-       }
 
        /* Mark the key as active in phdr */
        r = LUKS_keyslot_set(hdr, (int)keyIndex, 1);
-       if(r < 0) goto out;
+       if (r < 0)
+               goto out;
 
-       r = LUKS_write_phdr(device, hdr, ctx);
-       if(r < 0) goto out;
+       r = LUKS_write_phdr(hdr, ctx);
+       if (r < 0)
+               goto out;
 
        r = 0;
 out:
-       free(AfKey);
+       crypt_safe_free(AfKey);
+       crypt_free_volume_key(derived_key);
        return r;
 }
 
@@ -615,8 +857,7 @@ int LUKS_verify_volume_key(const struct luks_phdr *hdr,
 }
 
 /* Try to open a particular key slot */
-static int LUKS_open_key(const char *device,
-                 unsigned int keyIndex,
+static int LUKS_open_key(unsigned int keyIndex,
                  const char *password,
                  size_t passwordLen,
                  struct luks_phdr *hdr,
@@ -624,55 +865,60 @@ static int LUKS_open_key(const char *device,
                  struct crypt_device *ctx)
 {
        crypt_keyslot_info ki = LUKS_keyslot_info(hdr, keyIndex);
-       char derivedKey[hdr->keyBytes];
+       struct volume_key *derived_key;
        char *AfKey;
        size_t AFEKSize;
        int r;
 
-       log_dbg("Trying to open key slot %d [%d].", keyIndex, (int)ki);
+       log_dbg("Trying to open key slot %d [%s].", keyIndex,
+               dbg_slot_state(ki));
 
        if (ki < CRYPT_SLOT_ACTIVE)
                return -ENOENT;
 
-       // assert((vk->keylength % TWOFISH_BLOCKSIZE) == 0); FIXME
+       derived_key = crypt_alloc_volume_key(hdr->keyBytes, NULL);
+       if (!derived_key)
+               return -ENOMEM;
 
-       AFEKSize = hdr->keyblock[keyIndex].stripes*vk->keylength;
-       AfKey = (char *)malloc(AFEKSize);
-       if(AfKey == NULL) return -ENOMEM;
+       assert(vk->keylength == hdr->keyBytes);
+       AFEKSize = AF_split_sectors(vk->keylength, hdr->keyblock[keyIndex].stripes) * SECTOR_SIZE;
+       AfKey = crypt_safe_alloc(AFEKSize);
+       if (!AfKey) {
+               r = -ENOMEM;
+               goto out;
+       }
 
        r = PBKDF2_HMAC(hdr->hashSpec, password,passwordLen,
                        hdr->keyblock[keyIndex].passwordSalt,LUKS_SALTSIZE,
                        hdr->keyblock[keyIndex].passwordIterations,
-                       derivedKey, hdr->keyBytes);
-       if(r < 0) goto out;
+                       derived_key->key, hdr->keyBytes);
+       if (r < 0)
+               goto out;
 
        log_dbg("Reading key slot %d area.", keyIndex);
        r = LUKS_decrypt_from_storage(AfKey,
                                      AFEKSize,
                                      hdr,
-                                     derivedKey,
-                                     hdr->keyBytes,
-                                     device,
+                                     derived_key,
                                      hdr->keyblock[keyIndex].keyMaterialOffset,
                                      ctx);
-       if(r < 0) {
-               log_err(ctx, _("Failed to read from key storage.\n"));
+       if (r < 0)
                goto out;
-       }
 
        r = AF_merge(AfKey,vk->key,vk->keylength,hdr->keyblock[keyIndex].stripes,hdr->hashSpec);
-       if(r < 0) goto out;
+       if (r < 0)
+               goto out;
 
        r = LUKS_verify_volume_key(hdr, vk);
        if (!r)
                log_verbose(ctx, _("Key slot %d unlocked.\n"), keyIndex);
 out:
-       free(AfKey);
+       crypt_safe_free(AfKey);
+       crypt_free_volume_key(derived_key);
        return r;
 }
 
-int LUKS_open_key_with_hdr(const char *device,
-                          int keyIndex,
+int LUKS_open_key_with_hdr(int keyIndex,
                           const char *password,
                           size_t passwordLen,
                           struct luks_phdr *hdr,
@@ -685,12 +931,12 @@ int LUKS_open_key_with_hdr(const char *device,
        *vk = crypt_alloc_volume_key(hdr->keyBytes, NULL);
 
        if (keyIndex >= 0) {
-               r = LUKS_open_key(device, keyIndex, password, passwordLen, hdr, *vk, ctx);
+               r = LUKS_open_key(keyIndex, password, passwordLen, hdr, *vk, ctx);
                return (r < 0) ? r : keyIndex;
        }
 
        for(i = 0; i < LUKS_NUMKEYS; i++) {
-               r = LUKS_open_key(device, i, password, passwordLen, hdr, *vk, ctx);
+               r = LUKS_open_key(i, password, passwordLen, hdr, *vk, ctx);
                if(r == 0)
                        return i;
 
@@ -704,77 +950,15 @@ int LUKS_open_key_with_hdr(const char *device,
        return -EPERM;
 }
 
-/*
- * Wipe patterns according to Gutmann's Paper
- */
-
-static void wipeSpecial(char *buffer, size_t buffer_size, unsigned int turn)
-{
-        unsigned int i;
-
-        unsigned char write_modes[][3] = {
-                {"\x55\x55\x55"}, {"\xaa\xaa\xaa"}, {"\x92\x49\x24"},
-                {"\x49\x24\x92"}, {"\x24\x92\x49"}, {"\x00\x00\x00"},
-                {"\x11\x11\x11"}, {"\x22\x22\x22"}, {"\x33\x33\x33"},
-                {"\x44\x44\x44"}, {"\x55\x55\x55"}, {"\x66\x66\x66"},
-                {"\x77\x77\x77"}, {"\x88\x88\x88"}, {"\x99\x99\x99"},
-                {"\xaa\xaa\xaa"}, {"\xbb\xbb\xbb"}, {"\xcc\xcc\xcc"},
-                {"\xdd\xdd\xdd"}, {"\xee\xee\xee"}, {"\xff\xff\xff"},
-                {"\x92\x49\x24"}, {"\x49\x24\x92"}, {"\x24\x92\x49"},
-                {"\x6d\xb6\xdb"}, {"\xb6\xdb\x6d"}, {"\xdb\x6d\xb6"}
-        };
-
-        for(i = 0; i < buffer_size / 3; ++i) {
-                memcpy(buffer, write_modes[turn], 3);
-                buffer += 3;
-        }
-}
-
-static int wipe(const char *device, unsigned int from, unsigned int to)
-{
-       int devfd;
-       char *buffer;
-       unsigned int i;
-       unsigned int bufLen = (to - from) * SECTOR_SIZE;
-       int r = 0;
-
-       devfd = open(device, O_RDWR | O_DIRECT | O_SYNC);
-       if(devfd == -1)
-               return -EINVAL;
-
-       buffer = (char *) malloc(bufLen);
-       if(!buffer) {
-               close(devfd);
-               return -ENOMEM;
-       }
-
-       for(i = 0; i < 39; ++i) {
-               if     (i >=  0 && i <  5) crypt_random_get(NULL, buffer, bufLen, CRYPT_RND_NORMAL);
-               else if(i >=  5 && i < 32) wipeSpecial(buffer, bufLen, i - 5);
-               else if(i >= 32 && i < 38) crypt_random_get(NULL, buffer, bufLen, CRYPT_RND_NORMAL);
-               else if(i >= 38 && i < 39) memset(buffer, 0xFF, bufLen);
-
-               if(write_lseek_blockwise(devfd, buffer, bufLen, from * SECTOR_SIZE) < 0) {
-                       r = -EIO;
-                       break;
-               }
-       }
-
-       free(buffer);
-       close(devfd);
-
-       return r;
-}
-
-int LUKS_del_key(const char *device,
-                unsigned int keyIndex,
+int LUKS_del_key(unsigned int keyIndex,
                 struct luks_phdr *hdr,
                 struct crypt_device *ctx)
 {
-       unsigned int startOffset, endOffset, stripesLen;
+       struct device *device = crypt_metadata_device(ctx);
+       unsigned int startOffset, endOffset;
        int r;
 
-       r = LUKS_read_phdr(device, hdr, 1, ctx);
+       r = LUKS_read_phdr(hdr, 1, 0, ctx);
        if (r)
                return r;
 
@@ -787,12 +971,19 @@ int LUKS_del_key(const char *device,
 
        /* secure deletion of key material */
        startOffset = hdr->keyblock[keyIndex].keyMaterialOffset;
-       stripesLen = hdr->keyBytes * hdr->keyblock[keyIndex].stripes;
-       endOffset = startOffset + div_round_up(stripesLen, SECTOR_SIZE);
+       endOffset = startOffset + AF_split_sectors(hdr->keyBytes, hdr->keyblock[keyIndex].stripes);
 
-       r = wipe(device, startOffset, endOffset);
+       r = crypt_wipe(device, startOffset * SECTOR_SIZE,
+                      (endOffset - startOffset) * SECTOR_SIZE,
+                      CRYPT_WIPE_DISK, 0);
        if (r) {
-               log_err(ctx, _("Cannot wipe device %s.\n"), device);
+               if (r == -EACCES) {
+                       log_err(ctx, _("Cannot write to device %s, permission denied.\n"),
+                               device_path(device));
+                       r = -EINVAL;
+               } else
+                       log_err(ctx, _("Cannot wipe device %s.\n"),
+                               device_path(device));
                return r;
        }
 
@@ -800,7 +991,7 @@ int LUKS_del_key(const char *device,
        memset(&hdr->keyblock[keyIndex].passwordSalt, 0, LUKS_SALTSIZE);
        hdr->keyblock[keyIndex].passwordIterations = 0;
 
-       r = LUKS_write_phdr(device, hdr, ctx);
+       r = LUKS_write_phdr(hdr, ctx);
 
        return r;
 }
@@ -861,3 +1052,46 @@ int LUKS_keyslot_set(struct luks_phdr *hdr, int keyslot, int enable)
        log_dbg("Key slot %d was %s in LUKS header.", keyslot, enable ? "enabled" : "disabled");
        return 0;
 }
+
+int LUKS1_activate(struct crypt_device *cd,
+                  const char *name,
+                  struct volume_key *vk,
+                  uint32_t flags)
+{
+       int r;
+       char *dm_cipher = NULL;
+       enum devcheck device_check;
+       struct crypt_dm_active_device dmd = {
+               .target = DM_CRYPT,
+               .uuid   = crypt_get_uuid(cd),
+               .flags  = flags,
+               .size   = 0,
+               .data_device = crypt_data_device(cd),
+               .u.crypt = {
+                       .cipher = NULL,
+                       .vk     = vk,
+                       .offset = crypt_get_data_offset(cd),
+                       .iv_offset = 0,
+               }
+       };
+
+       if (dmd.flags & CRYPT_ACTIVATE_SHARED)
+               device_check = DEV_SHARED;
+       else
+               device_check = DEV_EXCL;
+
+       r = device_block_adjust(cd, dmd.data_device, device_check,
+                                dmd.u.crypt.offset, &dmd.size, &dmd.flags);
+       if (r)
+               return r;
+
+       r = asprintf(&dm_cipher, "%s-%s", crypt_get_cipher(cd), crypt_get_cipher_mode(cd));
+       if (r < 0)
+               return -ENOMEM;
+
+       dmd.u.crypt.cipher = dm_cipher;
+       r = dm_create_device(cd, name, CRYPT_LUKS1, &dmd, 0);
+
+       free(dm_cipher);
+       return r;
+}