Commit bd047d03 authored by Milan Broz's avatar Milan Broz

Add repair command and API for repairing known LUKS header problems.

parent 9511c91a
2012-03-16 Milan Broz <gmazyland@gmail.com>
* Add --keyfile-offset and --new-keyfile-offset parameters to API and CLI.
* Add repair command and crypt_repair() for known LUKS metadata problems repair.
2012-03-16 Milan Broz <mbroz@redhat.com>
* Unify password verification option.
......
......@@ -391,6 +391,20 @@ int crypt_load(struct crypt_device *cd,
const char *requested_type,
void *params);
/**
* Try to repair crypt device on-disk header if invalid
*
* @param cd crypt device handle
* @param requested_type - use @e NULL for all known
* @param params crypt type specific parameters (see @link crypt_type @endlink)
*
* @returns 0 on success or negative errno value otherwise.
*
*/
int crypt_repair(struct crypt_device *cd,
const char *requested_type,
void *params __attribute__((unused)));
/**
* Resize crypt device
*
......
......@@ -17,6 +17,7 @@ CRYPTSETUP_1.0 {
crypt_memory_lock;
crypt_format;
crypt_load;
crypt_repair;
crypt_resize;
crypt_suspend;
crypt_resume_by_passphrase;
......
......@@ -149,7 +149,7 @@ int LUKS_hdr_backup(
return -EINVAL;
}
r = LUKS_read_phdr(device, hdr, 1, ctx);
r = LUKS_read_phdr(device, hdr, 1, 0, ctx);
if (r)
return r;
......@@ -245,7 +245,7 @@ int LUKS_hdr_restore(
}
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 ||
......@@ -288,7 +288,7 @@ int LUKS_hdr_restore(
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);
......@@ -296,23 +296,35 @@ out:
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;
int i, bad, r, need_write = 0;
// FIXME check keyBytes
if (phdr->keyBytes != 16 && phdr->keyBytes != 32) {
log_err(ctx, _("Non standard key size, manual repair required.\n"));
return -EINVAL;
}
vk = crypt_alloc_volume_key(phdr->keyBytes, NULL);
// FIXME check cipher, cipher_mode, hash, uuid, payloadOffset
LUKS_generate_phdr(&temp_phdr, vk, phdr->cipherName, phdr->cipherMode,
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) {
bad = 0;
......@@ -332,7 +344,7 @@ static int _keyslot_repair(const char *device, struct luks_phdr *phdr, struct cr
bad = 1;
}
/* if enabled, do not try to fix it */
/* if enabled, do not try to wipe salt */
if (phdr->keyblock[i].active != LUKS_KEY_ENABLED) {
/* Known case - MSDOS partition table signature */
if (i == 6 && sector[0x1fe] == 0x55 && sector[0x1ff] == 0xaa) {
......@@ -347,17 +359,25 @@ static int _keyslot_repair(const char *device, struct luks_phdr *phdr, struct cr
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 LUKS_write_phdr(device, phdr, ctx);
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;
......@@ -368,39 +388,44 @@ 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);
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);
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 0
if (r == -EINVAL) {
log_err(ctx, _("Repairing keyslots.\n"));
if (repair) {
if (r == -EINVAL)
r = _keyslot_repair(device, hdr, ctx);
}
#endif
else
log_verbose(ctx, _("No known problems detected for LUKS header.\n"));
}
return r;
......@@ -442,7 +467,8 @@ int LUKS_read_phdr_backup(const char *backup_file,
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);
......@@ -452,11 +478,15 @@ int LUKS_read_phdr_backup(const char *backup_file,
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);
......@@ -469,7 +499,8 @@ int LUKS_read_phdr(const char *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;
......@@ -521,7 +552,7 @@ int LUKS_write_phdr(const char *device,
/* 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);
}
......@@ -894,7 +925,7 @@ int LUKS_del_key(const char *device,
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;
......
......@@ -95,6 +95,7 @@ int LUKS_read_phdr(
const char *device,
struct luks_phdr *hdr,
int require_luks_device,
int repair,
struct crypt_device *ctx);
int LUKS_read_phdr_backup(
......
......@@ -602,7 +602,7 @@ int crypt_set_data_device(struct crypt_device *cd, const char *device)
return crypt_check_data_device_size(cd);
}
static int _crypt_load_luks1(struct crypt_device *cd, int require_header)
static int _crypt_load_luks1(struct crypt_device *cd, int require_header, int repair)
{
struct luks_phdr hdr;
int r;
......@@ -611,7 +611,7 @@ static int _crypt_load_luks1(struct crypt_device *cd, int require_header)
if (r < 0)
return r;
r = LUKS_read_phdr(mdata_device(cd), &hdr, require_header, cd);
r = LUKS_read_phdr(mdata_device(cd), &hdr, require_header, repair, cd);
if (r < 0)
return r;
......@@ -726,7 +726,7 @@ int crypt_init_by_name_and_header(struct crypt_device **cd,
}
} else if (isLUKS((*cd)->type)) {
if (mdata_device(*cd)) {
r = _crypt_load_luks1(*cd, 0);
r = _crypt_load_luks1(*cd, 0, 0);
if (r < 0) {
log_dbg("LUKS device header does not match active device.");
free((*cd)->type);
......@@ -972,7 +972,38 @@ int crypt_load(struct crypt_device *cd,
return -EINVAL;
}
r = _crypt_load_luks1(cd, 1);
r = _crypt_load_luks1(cd, 1, 0);
if (r < 0)
return r;
/* cd->type and header must be set in context */
r = crypt_check_data_device_size(cd);
if (r < 0) {
free(cd->type);
cd->type = NULL;
}
return r;
}
int crypt_repair(struct crypt_device *cd,
const char *requested_type,
void *params __attribute__((unused)))
{
int r;
log_dbg("Trying to repair %s crypt type from device %s.",
requested_type ?: "any", mdata_device(cd) ?: "(none)");
if (!mdata_device(cd))
return -EINVAL;
if (requested_type && !isLUKS(requested_type))
return -EINVAL;
/* Load with repair */
r = _crypt_load_luks1(cd, 1, 1);
if (r < 0)
return r;
......
......@@ -175,6 +175,18 @@ form backup file are available after issuing this command.
This command allows restoring header if device do not contain LUKS header
or if the master key size and data offset in LUKS header on device match the backup file.
.PP
\fIrepair\fR <device>
.IP
Tries to repair (LUKS) device metadata if possible.
This command is useful to fix known benign LUKS metadata header corruptions.
Only basic corruptions of unused keyslot are fixable, any rewrite
of keyslot data or used keyslot or header metadata means lost of device.
\fBWARNING:\fR Always store binary copy of the original header, for
LUKS, \fIrepair\fR will not touch more than 4kB from the start of device
(visible LUKS header).
.PP
For more information about LUKS, see
\fBhttp://code.google.com/p/cryptsetup/wiki/Specification\fR
.SH loop-AES EXTENSION
......
......@@ -91,6 +91,7 @@ static int action_luksResume(int arg);
static int action_luksBackup(int arg);
static int action_luksRestore(int arg);
static int action_loopaesOpen(int arg);
static int action_luksRepair(int arg);
static struct action_type {
const char *type;
......@@ -105,6 +106,7 @@ static struct action_type {
{ "remove", action_remove, 0, 1, 1, N_("<name>"), N_("remove device") },
{ "resize", action_resize, 0, 1, 1, N_("<name>"), N_("resize active device") },
{ "status", action_status, 0, 1, 0, N_("<name>"), N_("show device status") },
{ "repair", action_luksRepair, 0, 1, 1, N_("<device>"), N_("try to repair on-disk metadata") },
{ "luksFormat", action_luksFormat, 0, 1, 1, N_("<device> [<new key file>]"), N_("formats a LUKS device") },
{ "luksOpen", action_luksOpen, 0, 2, 1, N_("<device> <name> "), N_("open LUKS device as mapping <name>") },
{ "luksAddKey", action_luksAddKey, 0, 1, 1, N_("<device> [<new key file>]"), N_("add key to LUKS device") },
......@@ -517,6 +519,32 @@ fail:
return -EINVAL;
}
static int action_luksRepair(int arg __attribute__((unused)))
{
struct crypt_device *cd = NULL;
int r;
if ((r = crypt_init(&cd, action_argv[0])))
goto out;
/* Currently only LUKS1 allows repair */
crypt_set_log_callback(cd, _quiet_log, NULL);
r = crypt_load(cd, CRYPT_LUKS1, NULL);
crypt_set_log_callback(cd, _log, NULL);
if (r == 0) {
log_verbose( _("No known problems detected for LUKS header.\n"));
goto out;
}
r = _yesDialog(_("Really try to repair LUKS device header?"),
NULL) ? 0 : -EINVAL;
if (r == 0)
r = crypt_repair(cd, CRYPT_LUKS1, NULL);
out:
crypt_free(cd);
return r;
}
static int action_luksFormat(int arg __attribute__((unused)))
{
int r = -EINVAL, keysize;
......
......@@ -495,5 +495,14 @@ $CRYPTSETUP luksSuspend $DEV_NAME --header $HEADER_IMG || fail
echo "key0" | $CRYPTSETUP luksResume $DEV_NAME --header $HEADER_IMG || fail
$CRYPTSETUP luksClose $DEV_NAME || fail
prepare "[29] Repair metadata" wipe
$CRYPTSETUP -q luksFormat -i1 $LOOPDEV $KEY1 --key-slot 0 || fail
# second sector overwrite should corrupt keyslot 6+7
dd if=/dev/urandom of=$LOOPDEV bs=512 seek=1 count=1 >/dev/null 2>&1
$CRYPTSETUP luksOpen -d $KEY1 $LOOPDEV $DEV_NAME >/dev/null 2>&1 && fail
$CRYPTSETUP -q repair $LOOPDEV >/dev/null 2>&1 || fail
$CRYPTSETUP luksOpen -d $KEY1 $LOOPDEV $DEV_NAME || fail
$CRYPTSETUP luksClose $DEV_NAME || fail
remove_mapping
exit 0
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment