Add luksHeaderBackup and luksHeaderRestore commands and API cals.
authorMilan Broz <gmazyland@gmail.com>
Mon, 28 Sep 2009 17:45:38 +0000 (17:45 +0000)
committerMilan Broz <gmazyland@gmail.com>
Mon, 28 Sep 2009 17:45:38 +0000 (17:45 +0000)
git-svn-id: https://cryptsetup.googlecode.com/svn/trunk@114 36d66b0a-2a48-0410-832c-cd162a569da5

ChangeLog
lib/internal.h
lib/libcryptsetup.h
lib/setup.c
luks/keymanage.c
luks/luks.h
man/cryptsetup.8
src/cryptsetup.c

index e4541d0..aacf9b4 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
index 55a7bc0..b0e0868 100644 (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, ...);
index cdd30ec..9d6e128 100644 (file)
@@ -443,6 +443,32 @@ typedef enum { SLOT_INVALID, SLOT_INACTIVE, SLOT_ACTIVE, SLOT_ACTIVE_LAST } cryp
 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
index e623c6a..04b1c43 100644 (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) {
index d9e386c..6bcfdb3 100644 (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,
-                  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);
@@ -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;
 
index 046c365..a395173 100644 (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,
index 39720bd..3743d96 100644 (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
 
index 48c59e4..0365905 100644 (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;