* 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
uint32_t secs_per_stripes;
/* First sectors is the header itself */
- if (phdr->keyblock[keyIndex].keyMaterialOffset * SECTOR_SIZE < LUKS_ALIGN_KEYSLOTS) {
+ if (phdr->keyblock[keyIndex].keyMaterialOffset * SECTOR_SIZE < sizeof(*phdr)) {
log_dbg("Invalid offset %u in keyslot %u.",
phdr->keyblock[keyIndex].keyMaterialOffset, keyIndex);
return 1;
return -EINVAL;
}
- r = LUKS_read_phdr(device, hdr, 1, ctx);
+ r = LUKS_read_phdr(device, hdr, 1, 0, ctx);
if (r)
return r;
}
close(devfd);
- r = LUKS_read_phdr(device, hdr, 0, ctx);
+ r = LUKS_read_phdr(device, hdr, 0, 0, ctx);
if (r == 0) {
log_dbg("Device %s already contains LUKS header, checking UUID and offset.", device);
if(hdr->payloadOffset != hdr_file.payloadOffset ||
close(devfd);
/* Be sure to reload new data */
- r = LUKS_read_phdr(device, hdr, 1, ctx);
+ r = LUKS_read_phdr(device, hdr, 1, 0, ctx);
out:
if (devfd != -1)
close(devfd);
return r;
}
+/* This routine should do some just basic recovery for known problems. */
+static int _keyslot_repair(const char *device, 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,
+ "/dev/null", 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(device, 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;
log_dbg("LUKS header not detected.");
if (require_luks_device)
log_err(ctx, _("Device %s is not a valid LUKS device.\n"), 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);
- if (LUKS_check_keyslot_size(hdr, i)) {
- log_err(ctx, _("LUKS keyslot %u is invalid.\n"), i);
- // FIXME: allow header recovery
- r = -EINVAL;
- }
+ 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';
- /* 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(device, hdr, ctx);
+ else
+ log_verbose(ctx, _("No known problems detected for LUKS header.\n"));
}
return r;
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);
int LUKS_read_phdr(const char *device,
struct luks_phdr *hdr,
int require_luks_device,
+ int repair,
struct crypt_device *ctx)
{
ssize_t hdr_size = sizeof(struct luks_phdr);
int devfd = 0, r = 0;
+ if (repair && !require_luks_device)
+ return -EINVAL;
+
log_dbg("Reading LUKS header of size %d from device %s",
hdr_size, device);
if (read_blockwise(devfd, 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, hdr, require_luks_device,
+ repair, ctx);
close(devfd);
return r;
/* 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(device, hdr, 1, 0, ctx);
if (r)
log_err(ctx, _("Error re-reading LUKS header after update on device %s.\n"), device);
}
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;
return -ENOMEM;
r = crypt_random_get(ctx, hdr->keyblock[keyIndex].passwordSalt,
- LUKS_SALTSIZE, CRYPT_RND_NORMAL);
+ LUKS_SALTSIZE, CRYPT_RND_SALT);
if (r < 0)
return r;
unsigned int startOffset, endOffset, stripesLen;
int r;
- r = LUKS_read_phdr(device, hdr, 1, ctx);
+ r = LUKS_read_phdr(device, hdr, 1, 0, ctx);
if (r)
return r;
{
int r;
char *dm_cipher = NULL;
+ enum devcheck device_check;
struct crypt_dm_active_device dmd = {
.device = crypt_get_device_name(cd),
.cipher = NULL,
.flags = flags
};
- r = device_check_and_adjust(cd, dmd.device, DEV_EXCL,
- &dmd.size, &dmd.offset, &flags);
+ if (dmd.flags & CRYPT_ACTIVATE_SHARED)
+ device_check = DEV_SHARED;
+ else
+ device_check = DEV_EXCL;
+
+ r = device_check_and_adjust(cd, dmd.device, device_check,
+ &dmd.size, &dmd.offset, &dmd.flags);
if (r)
return r;