+2009-09-28 Milan Broz <mbroz@redhat.com>
+ * Add luksHeaderBackup and luksHeaderRestore commands.
+
2009-09-15 Milan Broz <mbroz@redhat.com>
* Initialize crypto library before LUKS header load.
* Fix manpage to not require --size which expands to device size by default.
};
struct crypt_device;
+int crypt_confirm(struct crypt_device *cd, const char *msg);
void set_error_va(const char *fmt, va_list va);
void set_error(const char *fmt, ...);
crypt_keyslot_info crypt_keyslot_status(struct crypt_device *cd, int keyslot);
/**
+ * Backup header and keyslots to file
+ *
+ * Returns 0 on success or negative errno value otherwise.
+ *
+ * @cd - crypt device handle
+ * @requested_type - type of header to backup
+ * @backup_file - file to backup header to
+ */
+int crypt_header_backup(struct crypt_device *cd,
+ const char *requested_type,
+ const char *backup_file);
+
+/**
+ * Restore header and keyslots from backup file
+ *
+ * Returns 0 on success or negative errno value otherwise.
+ *
+ * @cd - crypt device handle
+ * @requested_type - type of header to restore
+ * @backup_file - file to restore header from
+ */
+int crypt_header_restore(struct crypt_device *cd,
+ const char *requested_type,
+ const char *backup_file);
+
+/**
* Receives last reported error
*
* @buf - buffef for message
return xyesDialog((char*)msg);
}
+int crypt_confirm(struct crypt_device *cd, const char *msg)
+{
+ if (!cd || !cd->confirm)
+ return 1;
+ else
+ return cd->confirm(msg, cd->confirm_usrptr);
+}
+
static void key_from_terminal(struct crypt_device *cd, char *msg, char **key,
unsigned int *key_len, int force_verify)
{
return r;
}
+int crypt_header_backup(struct crypt_device *cd,
+ const char *requested_type,
+ const char *backup_file)
+{
+ if ((requested_type && !isLUKS(requested_type)) || !backup_file)
+ return -EINVAL;
+
+ log_dbg("Requested header backup of device %s (%s) to "
+ "file %s.", cd->device, requested_type, backup_file);
+
+ return LUKS_hdr_backup(backup_file, cd->device, &cd->hdr, cd);
+}
+
+int crypt_header_restore(struct crypt_device *cd,
+ const char *requested_type,
+ const char *backup_file)
+{
+ if (requested_type && !isLUKS(requested_type))
+ return -EINVAL;
+
+ log_dbg("Requested header restore to device %s (%s) from "
+ "file %s.", cd->device, requested_type, backup_file);
+
+ return LUKS_hdr_restore(backup_file, cd->device, &cd->hdr, cd);
+}
+
void crypt_free(struct crypt_device *cd)
{
if (cd) {
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
return mk;
}
-int LUKS_read_phdr(const char *device,
- struct luks_phdr *hdr,
- int require_luks_device,
- struct crypt_device *ctx)
+int LUKS_hdr_backup(
+ const char *backup_file,
+ const char *device,
+ struct luks_phdr *hdr,
+ struct crypt_device *ctx)
{
- int devfd = 0, r = 0;
- unsigned int i;
- uint64_t size;
- char luksMagic[] = LUKS_MAGIC;
+ int r = 0, devfd = -1;
+ size_t buffer_size;
+ char *buffer = NULL;
+ struct stat st;
- log_dbg("Reading LUKS header of size %d from device %s",
- sizeof(struct luks_phdr), device);
+ if(stat(backup_file, &st) == 0) {
+ log_err(ctx, _("Requested file %s already exist.\n"), backup_file);
+ return -EINVAL;
+ }
- devfd = open(device,O_RDONLY | O_DIRECT | O_SYNC);
- if(-1 == devfd) {
+ r = LUKS_read_phdr(device, hdr, 0, ctx);
+ if (r)
+ return r;
+
+ buffer_size = hdr->payloadOffset << SECTOR_SHIFT;
+ buffer = safe_alloc(buffer_size);
+ if (!buffer || buffer_size < LUKS_ALIGN_KEYSLOTS) {
+ r = -ENOMEM;
+ goto out;
+ }
+
+ 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);
+ if(devfd == -1) {
+ log_err(ctx, _("Device %s is not LUKS device.\n"), device);
+ r = -EINVAL;
+ goto out;
+ }
+
+ if(read_blockwise(devfd, 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));
+
+ devfd = creat(backup_file, S_IRUSR);
+ if(devfd == -1) {
+ r = -EINVAL;
+ goto out;
+ }
+ if(write(devfd, buffer, buffer_size) < buffer_size) {
+ log_err(ctx, _("Cannot write header backup file %s.\n"), backup_file);
+ r = -EIO;
+ goto out;
+ }
+ close(devfd);
+
+ r = 0;
+out:
+ if (devfd != -1)
+ close(devfd);
+ safe_free(buffer);
+ return r;
+}
+
+int LUKS_hdr_restore(
+ const char *backup_file,
+ const char *device,
+ struct luks_phdr *hdr,
+ struct crypt_device *ctx)
+{
+ int r = 0, devfd = -1, diff_uuid = 0;
+ size_t buffer_size;
+ char *buffer = NULL, msg[200];
+ struct stat st;
+ struct luks_phdr hdr_file;
+
+ if(stat(backup_file, &st) < 0) {
+ log_err(ctx, _("Backup file %s doesn't exist.\n"), backup_file);
+ return -EINVAL;
+ }
+
+ r = LUKS_read_phdr_backup(backup_file, device, &hdr_file, 0, ctx);
+ buffer_size = hdr_file.payloadOffset << SECTOR_SHIFT;
+
+ if (r || buffer_size < LUKS_ALIGN_KEYSLOTS) {
+ log_err(ctx, _("Backup file do not contain valid LUKS header.\n"));
+ r = -EINVAL;
+ goto out;
+ }
+
+ buffer = safe_alloc(buffer_size);
+ if (!buffer) {
+ r = -ENOMEM;
+ goto out;
+ }
+
+ devfd = open(backup_file, O_RDONLY);
+ if(devfd == -1) {
+ log_err(ctx, _("Cannot open header backup file %s.\n"), backup_file);
+ r = -EINVAL;
+ goto out;
+ }
+
+ if(read(devfd, buffer, buffer_size) < buffer_size) {
+ log_err(ctx, _("Cannot read header backup file %s.\n"), backup_file);
+ r = -EIO;
+ goto out;
+ }
+ close(devfd);
+
+ r = LUKS_read_phdr(device, hdr, 0, ctx);
+ if (r == 0) {
+ log_dbg("Device %s already contains LUKS header, checking UUID and offset.", 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"));
+ r = -EINVAL;
+ goto out;
+ }
+ if (memcmp(hdr->uuid, hdr_file.uuid, UUID_STRING_L))
+ diff_uuid = 1;
+ }
+
+ if (snprintf(msg, sizeof(msg), _("Device %s %s%s"), 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) {
+ r = -ENOMEM;
+ goto out;
+ }
+
+ if (!crypt_confirm(ctx, msg)) {
+ r = -EINVAL;
+ goto out;
+ }
+
+ log_dbg("Storing backup of header (%u bytes) and keyslot area (%u bytes) to device %s.",
+ sizeof(*hdr), buffer_size - LUKS_ALIGN_KEYSLOTS, device);
+
+ devfd = open(device, O_WRONLY | O_DIRECT | O_SYNC);
+ if(devfd == -1) {
log_err(ctx, _("Cannot open device %s.\n"), device);
- return -EINVAL;
+ r = -EINVAL;
+ goto out;
}
- if(read_blockwise(devfd, hdr, sizeof(struct luks_phdr)) < sizeof(struct luks_phdr)) {
+ if(write_blockwise(devfd, buffer, buffer_size) < buffer_size) {
r = -EIO;
- } else if(memcmp(hdr->magic, luksMagic, LUKS_MAGIC_L)) { /* Check magic */
+ goto out;
+ }
+ close(devfd);
+
+ /* Be sure to reload new data */
+ r = LUKS_read_phdr(device, hdr, 0, ctx);
+out:
+ if (devfd != -1)
+ close(devfd);
+ safe_free(buffer);
+ return r;
+}
+
+static int _check_and_convert_hdr(const char *device,
+ struct luks_phdr *hdr,
+ int require_luks_device,
+ struct crypt_device *ctx)
+{
+ int r = 0;
+ unsigned int i;
+ char luksMagic[] = LUKS_MAGIC;
+
+ if(memcmp(hdr->magic, luksMagic, LUKS_MAGIC_L)) { /* Check magic */
log_dbg("LUKS header not detected.");
if (require_luks_device)
log_err(ctx, _("%s is not a LUKS device.\n"), device);
}
}
+ return r;
+}
+
+int LUKS_read_phdr_backup(const char *backup_file,
+ const char *device,
+ struct luks_phdr *hdr,
+ int require_luks_device,
+ struct crypt_device *ctx)
+{
+ int devfd = 0, r = 0;
+
+ log_dbg("Reading LUKS header of size %d from backup file %s",
+ sizeof(struct luks_phdr), backup_file);
+
+ devfd = open(backup_file, O_RDONLY);
+ if(-1 == devfd) {
+ log_err(ctx, _("Cannot open file %s.\n"), device);
+ return -EINVAL;
+ }
+
+ if(read(devfd, hdr, sizeof(struct luks_phdr)) < sizeof(struct luks_phdr))
+ r = -EIO;
+ else
+ r = _check_and_convert_hdr(backup_file, hdr, require_luks_device, ctx);
+
+ close(devfd);
+ return r;
+}
+
+int LUKS_read_phdr(const char *device,
+ struct luks_phdr *hdr,
+ int require_luks_device,
+ struct crypt_device *ctx)
+{
+ int devfd = 0, r = 0;
+ uint64_t size;
+
+ log_dbg("Reading LUKS header of size %d from device %s",
+ sizeof(struct luks_phdr), device);
+
+ devfd = open(device,O_RDONLY | O_DIRECT | O_SYNC);
+ if(-1 == devfd) {
+ log_err(ctx, _("Cannot open device %s.\n"), device);
+ return -EINVAL;
+ }
+
+ if(read_blockwise(devfd, hdr, sizeof(struct luks_phdr)) < sizeof(struct luks_phdr))
+ r = -EIO;
+ else
+ r = _check_and_convert_hdr(device, hdr, require_luks_device, ctx);
+
#ifdef BLKGETSIZE64
if (r == 0 && (ioctl(devfd, BLKGETSIZE64, &size) < 0 ||
size < (uint64_t)hdr->payloadOffset)) {
char luksMagic[] = LUKS_MAGIC;
uuid_t partitionUuid;
int currentSector;
- int alignSectors = 4096/SECTOR_SIZE;
+ int alignSectors = LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE;
if (alignPayload == 0)
alignPayload = alignSectors;
/* Actually we need only 37, but we don't want struct autoaligning to kick in */
#define UUID_STRING_L 40
+/* Offset to align kesylot area */
+#define LUKS_ALIGN_KEYSLOTS 4096
+
/* Any integer values are stored in network byte order on disk and must be
converted */
int require_luks_device,
struct crypt_device *ctx);
+int LUKS_read_phdr_backup(
+ const char *backup_file,
+ const char *device,
+ struct luks_phdr *hdr,
+ int require_luks_device,
+ struct crypt_device *ctx);
+
+int LUKS_hdr_backup(
+ const char *backup_file,
+ const char *device,
+ struct luks_phdr *hdr,
+ struct crypt_device *ctx);
+
+int LUKS_hdr_restore(
+ const char *backup_file,
+ const char *device,
+ struct luks_phdr *hdr,
+ struct crypt_device *ctx);
+
int LUKS_write_phdr(
const char *device,
struct luks_phdr *hdr,
.IP
dumps the header information of a LUKS partition. No options.
.PP
+\fIluksHeaderBackup\fR <device> --header-backup-file <file>
+.IP
+Stores binary backup of LUKS header and keyslot areas.
+
+\fBWARNING:\fR Please note that with this backup file (and old passphrase knowledge) you can decrypt data even if old passphrase was wiped from real device.
+
+Also note that anti-forensic splitter is not used during manipulation with backup file.
+.PP
+\fIluksHeaderRestore\fR <device> --header-backup-file <file>
+.IP
+
+Restores binary backup of LUKS header and keyslot areas from specified file.
+
+\fBWARNING:\fR All the keyslot areas are overwritten, only active keyslots 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
For more information about LUKS, see \fBhttp://code.google.com/p/cryptsetup/wiki/Specification\fR
static int opt_verify_passphrase = 0;
static char *opt_key_file = NULL;
static char *opt_master_key_file = NULL;
+static char *opt_header_backup_file = NULL;
static unsigned int opt_key_size = 0;
static int opt_key_slot = CRYPT_ANY_SLOT;
static uint64_t opt_size = 0;
static int action_luksDump(int arg);
static int action_luksSuspend(int arg);
static int action_luksResume(int arg);
+static int action_luksBackup(int arg);
+static int action_luksRestore(int arg);
static struct action_type {
const char *type;
{ "luksDump", action_luksDump, 0, 1, 0, 0, 1, N_("<device>"), N_("dump LUKS partition information") },
{ "luksSuspend",action_luksSuspend, 0, 1, 1, 1, 1, N_("<device>"), N_("Suspend LUKS device and wipe key (all IOs are frozen).") },
{ "luksResume", action_luksResume, 0, 1, 1, 1, 1, N_("<device>"), N_("Resume suspended LUKS device.") },
+ { "luksHeaderBackup",action_luksBackup, 0, 1, 1, 1, 1, N_("<device>"), N_("Backup LUKS device header and keyslots") },
+ { "luksHeaderRestore",action_luksRestore,0,1, 1, 1, 1, N_("<device>"), N_("Restore LUKS device header and keyslots") },
{ "luksDelKey", action_luksDelKey, 0, 2, 1, 1, 1, N_("<device> <key slot>"), N_("identical to luksKillSlot - DEPRECATED - see man page") },
{ "reload", action_create, 1, 2, 1, 1, 1, N_("<name> <device>"), N_("modify active device - DEPRECATED - see man page") },
{ NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL }
.log = cmdLineLog,
};
+static void _log(int class, const char *msg, void *usrptr)
+{
+ cmdLineLog(class, (char *)msg);
+}
+
+static int _yesDialog(const char *msg, void *usrptr)
+{
+ return yesDialog((char*)msg);
+}
+
/* End ICBs */
static void show_status(int errcode)
options.flags |= CRYPT_FLAG_READONLY;
if (opt_non_exclusive)
log_err(_("Obsolete option --non-exclusive is ignored.\n"));
+
return crypt_luksOpen(&options);
}
return r;
}
+static int action_luksBackup(int arg)
+{
+ struct crypt_device *cd = NULL;
+ int r;
+
+ if (!opt_header_backup_file) {
+ log_err(_("Option --header-bakup-file is required.\n"));
+ return -EINVAL;
+ }
+
+ if ((r = crypt_init(&cd, action_argv[0])))
+ goto out;
+
+ crypt_set_log_callback(cd, _log, NULL);
+ crypt_set_confirm_callback(cd, _yesDialog, NULL);
+
+ r = crypt_header_backup(cd, CRYPT_LUKS1, opt_header_backup_file);
+out:
+ crypt_free(cd);
+ return r;
+}
+
+static int action_luksRestore(int arg)
+{
+ struct crypt_device *cd = NULL;
+ int r = 0;
+
+ if (!opt_header_backup_file) {
+ log_err(_("Option --header-bakup-file is required.\n"));
+ return -EINVAL;
+ }
+
+ if ((r = crypt_init(&cd, action_argv[0])))
+ goto out;
+
+ crypt_set_log_callback(cd, _log, NULL);
+ crypt_set_confirm_callback(cd, _yesDialog, NULL);
+ r = crypt_header_restore(cd, CRYPT_LUKS1, opt_header_backup_file);
+out:
+ crypt_free(cd);
+ return r;
+}
+
static void usage(poptContext popt_context, int exitcode,
const char *error, const char *more)
{
{ "tries", 'T', POPT_ARG_INT, &opt_tries, 0, N_("How often the input of the passphrase canbe retried"), NULL },
{ "align-payload", '\0', POPT_ARG_INT, &opt_align_payload, 0, N_("Align payload at <n> sector boundaries - for luksFormat"), N_("SECTORS") },
{ "non-exclusive", '\0', POPT_ARG_NONE, &opt_non_exclusive, 0, N_("Allows non-exclusive access for luksOpen, WARNING see manpage."), NULL },
+ { "header-backup-file",'\0', POPT_ARG_STRING, &opt_header_backup_file,0, N_("File with LUKS header and keyslots backup."), NULL },
POPT_TABLEEND
};
poptContext popt_context;