Add luksHeaderBackup and luksHeaderRestore commands and API cals.

git-svn-id: https://cryptsetup.googlecode.com/svn/trunk@114 36d66b0a-2a48-0410-832c-cd162a569da5
This commit is contained in:
Milan Broz
2009-09-28 17:45:38 +00:00
parent f51c7b62db
commit e026f089be
8 changed files with 384 additions and 18 deletions

View File

@@ -1,3 +1,6 @@
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.

View File

@@ -48,6 +48,7 @@ struct device_infos {
};
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, ...);

View File

@@ -442,6 +442,32 @@ int crypt_get_volume_key_size(struct crypt_device *cd);
typedef enum { SLOT_INVALID, SLOT_INACTIVE, SLOT_ACTIVE, SLOT_ACTIVE_LAST } crypt_keyslot_info;
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
*

View File

@@ -424,6 +424,14 @@ static int yesDialog_wrapper(const char *msg, void *usrptr)
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)
{
@@ -1126,6 +1134,32 @@ int crypt_load(struct crypt_device *cd,
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) {

View File

@@ -23,6 +23,7 @@
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -75,28 +76,179 @@ struct luks_masterkey *LUKS_generate_masterkey(int keylength)
return mk;
}
int LUKS_read_phdr(const char *device,
int LUKS_hdr_backup(
const char *backup_file,
const char *device,
struct luks_phdr *hdr,
struct crypt_device *ctx)
{
int r = 0, devfd = -1;
size_t buffer_size;
char *buffer = NULL;
struct stat st;
if(stat(backup_file, &st) == 0) {
log_err(ctx, _("Requested file %s already exist.\n"), backup_file);
return -EINVAL;
}
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);
r = -EINVAL;
goto out;
}
if(write_blockwise(devfd, 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);
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 devfd = 0, r = 0;
int r = 0;
unsigned int i;
uint64_t size;
char luksMagic[] = LUKS_MAGIC;
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 if(memcmp(hdr->magic, luksMagic, LUKS_MAGIC_L)) { /* Check 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);
@@ -122,6 +274,57 @@ int LUKS_read_phdr(const char *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)) {
@@ -195,7 +398,7 @@ int LUKS_generate_phdr(struct luks_phdr *header,
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;

View File

@@ -38,6 +38,9 @@
/* 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 */
@@ -98,6 +101,25 @@ int LUKS_read_phdr(
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,

View File

@@ -92,6 +92,23 @@ returns true, if <device> is a LUKS partition. Otherwise, false. No options.
.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

View File

@@ -22,6 +22,7 @@ static char *opt_hash = NULL;
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;
@@ -54,6 +55,8 @@ static int action_luksUUID(int arg);
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;
@@ -81,6 +84,8 @@ static struct action_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 }
@@ -129,6 +134,16 @@ static struct interface_callbacks cmd_icb = {
.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)
@@ -377,6 +392,7 @@ static int action_luksOpen(int arg)
options.flags |= CRYPT_FLAG_READONLY;
if (opt_non_exclusive)
log_err(_("Obsolete option --non-exclusive is ignored.\n"));
return crypt_luksOpen(&options);
}
@@ -529,6 +545,49 @@ out:
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)
{
@@ -638,6 +697,7 @@ int main(int argc, char **argv)
{ "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;