mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-15 21:00:05 +01:00
Add repair command and API for repairing known LUKS header problems.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
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,14 +388,19 @@ 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 {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Header detected */
|
||||
hdr->payloadOffset = ntohl(hdr->payloadOffset);
|
||||
hdr->keyBytes = ntohl(hdr->keyBytes);
|
||||
hdr->mkDigestIterations = ntohl(hdr->mkDigestIterations);
|
||||
@@ -395,12 +420,12 @@ static int _check_and_convert_hdr(const char *device,
|
||||
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(
|
||||
|
||||
39
lib/setup.c
39
lib/setup.c
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user