X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fcryptsetup_reencrypt.c;h=fa6ce13a883da6c6dbde469789f71fec5d27c71a;hb=a38fcafcff9ae75feda12bc84d04b2cdaa591f9f;hp=8b54e5ee147eed89cc700ed1811b6bae58a93aaa;hpb=dc067454c1cb3c6fa874da8b69bb9dde8002490b;p=platform%2Fupstream%2Fcryptsetup.git diff --git a/src/cryptsetup_reencrypt.c b/src/cryptsetup_reencrypt.c index 8b54e5e..fa6ce13 100644 --- a/src/cryptsetup_reencrypt.c +++ b/src/cryptsetup_reencrypt.c @@ -1,7 +1,8 @@ /* - * crypt_reencrypt - crypt utility for offline reencryption + * cryptsetup-reencrypt - crypt utility for offline re-encryption * * Copyright (C) 2012 Milan Broz All rights reserved. + * Copyright (C) 2012, Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -22,6 +23,8 @@ #define _LARGEFILE64_SOURCE #define _FILE_OFFSET_BITS 64 #define SECTOR_SIZE 512 +#define NO_UUID "cafecafe-cafe-cafe-cafe-cafecafeeeee" +#define MAX_BCK_SECTORS 8192 #include #include @@ -35,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -54,11 +58,14 @@ static int opt_version_mode = 0; static int opt_random = 0; static int opt_urandom = 0; static int opt_bsize = 4; +static int opt_reduce_device_size = 0; static int opt_directio = 0; static int opt_fsync = 0; static int opt_write_log = 0; static int opt_tries = 3; static int opt_key_slot = CRYPT_ANY_SLOT; +static int opt_key_size = 0; +static int opt_new = 0; static const char **action_argv; @@ -199,11 +206,13 @@ static int alignment(int fd) return alignment; } +/* Depends on the first two fields of LUKS1 header format, magic and version */ static int device_check(struct reenc_ctx *rc, header_magic set_magic) { char *buf = NULL; int r, devfd; ssize_t s; + uint16_t version; devfd = open(rc->device, O_RDWR | O_EXCL | O_DIRECT); if (devfd == -1) { @@ -234,16 +243,21 @@ static int device_check(struct reenc_ctx *rc, header_magic set_magic) return -EIO; } - if (set_magic == MAKE_UNUSABLE && !memcmp(buf, MAGIC, MAGIC_L)) { + /* Be sure that we do not process new version of header */ + memcpy((void*)&version, &buf[MAGIC_L], sizeof(uint16_t)); + version = ntohs(version); + + if (set_magic == MAKE_UNUSABLE && !memcmp(buf, MAGIC, MAGIC_L) && + version == 1) { log_verbose(_("Marking LUKS device %s unusable.\n"), rc->device); memcpy(buf, NOMAGIC, MAGIC_L); r = 0; - - } else if (set_magic == MAKE_USABLE && !memcmp(buf, NOMAGIC, MAGIC_L)) { + } else if (set_magic == MAKE_USABLE && !memcmp(buf, NOMAGIC, MAGIC_L) && + version == 1) { log_verbose(_("Marking LUKS device %s usable.\n"), rc->device); memcpy(buf, MAGIC, MAGIC_L); r = 0; - } else if (set_magic == CHECK_UNUSABLE) { + } else if (set_magic == CHECK_UNUSABLE && version == 1) { r = memcmp(buf, NOMAGIC, MAGIC_L) ? -EINVAL : 0; if (!r) rc->device_uuid = strndup(&buf[0xa8], 40); @@ -269,22 +283,41 @@ out: return r; } -static int create_empty_header(const char *new_file, const char *old_file) +static int create_empty_header(const char *new_file, const char *old_file, + uint64_t data_sector) { struct stat st; - ssize_t size; + ssize_t size = 0; int fd, r = 0; char *buf; - if (stat(old_file, &st) == -1 || - (st.st_mode & S_IFMT) != S_IFREG || - (st.st_size > 16 * 1024 * 1024)) - return -EINVAL; - size = st.st_size; + /* Never create header > 4MiB */ + if (data_sector > MAX_BCK_SECTORS) + data_sector = MAX_BCK_SECTORS; + + /* new header file of the same size as old backup */ + if (old_file) { + if (stat(old_file, &st) == -1 || + (st.st_mode & S_IFMT) != S_IFREG || + (st.st_size > 16 * 1024 * 1024)) + return -EINVAL; + size = st.st_size; + } + + /* + * if requesting key size change, try to use offset + * here can be enough space to fit new key. + */ + if (opt_key_size) + size = data_sector * SECTOR_SIZE; + + /* if reducing size, be sure we have enough space */ + if (opt_reduce_device_size) + size += (opt_reduce_device_size * SECTOR_SIZE); log_dbg("Creating empty file %s of size %lu.", new_file, (unsigned long)size); - if (!(buf = malloc(size))) + if (!size || !(buf = malloc(size))) return -ENOMEM; memset(buf, 0, size); @@ -415,7 +448,7 @@ static int open_log(struct reenc_ctx *rc) if (rc->log_fd == -1) return -EINVAL; } else { - log_dbg("Log file %s exists, restarting.", rc->log_file); + log_std(_("Log file %s exists, restarting reencryption.\n"), rc->log_file); flags = opt_directio ? O_RDWR|O_DIRECT : O_RDWR; rc->log_fd = open(rc->log_file, flags); if (rc->log_fd == -1) @@ -473,12 +506,49 @@ out: return r; } +static int create_new_header(struct reenc_ctx *rc, const char *cipher, + const char *cipher_mode, const char *uuid, + int key_size, struct crypt_params_luks1 *params) +{ + struct crypt_device *cd_new = NULL; + int i, r; + + if ((r = crypt_init(&cd_new, rc->header_file_new))) + goto out; + + if (opt_random) + crypt_set_rng_type(cd_new, CRYPT_RNG_RANDOM); + else if (opt_urandom) + crypt_set_rng_type(cd_new, CRYPT_RNG_URANDOM); + + if (opt_iteration_time) + crypt_set_iteration_time(cd_new, opt_iteration_time); + + if ((r = crypt_format(cd_new, CRYPT_LUKS1, cipher, cipher_mode, + uuid, NULL, key_size, params))) + goto out; + log_verbose(_("New LUKS header for device %s created.\n"), rc->device); + + for (i = 0; i < MAX_SLOT; i++) { + if (!rc->p[i].password) + continue; + if ((r = crypt_keyslot_add_by_volume_key(cd_new, i, + NULL, 0, rc->p[i].password, rc->p[i].passwordLen)) < 0) + goto out; + log_verbose(_("Activated keyslot %i.\n"), r); + r = 0; + } +out: + crypt_free(cd_new); + return r; +} + static int backup_luks_headers(struct reenc_ctx *rc) { - struct crypt_device *cd = NULL, *cd_new = NULL; + struct crypt_device *cd = NULL; struct crypt_params_luks1 params = {0}; char cipher [MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN]; - int i, r; + int r; log_dbg("Creating LUKS header backup for device %s.", rc->device); @@ -491,23 +561,49 @@ static int backup_luks_headers(struct reenc_ctx *rc) goto out; log_verbose(_("LUKS header backup of device %s created.\n"), rc->device); - if ((r = create_empty_header(rc->header_file_new, rc->header_file_org))) + if ((r = create_empty_header(rc->header_file_new, rc->header_file_org, + crypt_get_data_offset(cd)))) goto out; params.hash = opt_hash ?: DEFAULT_LUKS1_HASH; params.data_alignment = crypt_get_data_offset(cd); + params.data_alignment += opt_reduce_device_size; params.data_device = rc->device; - if ((r = crypt_init(&cd_new, rc->header_file_new))) - goto out; + if (opt_cipher) { + r = crypt_parse_name_and_mode(opt_cipher, cipher, NULL, cipher_mode); + if (r < 0) { + log_err(_("No known cipher specification pattern detected.\n")); + goto out; + } + } - if (opt_random) - crypt_set_rng_type(cd_new, CRYPT_RNG_RANDOM); - else if (opt_urandom) - crypt_set_rng_type(cd_new, CRYPT_RNG_URANDOM); + r = create_new_header(rc, + opt_cipher ? cipher : crypt_get_cipher(cd), + opt_cipher ? cipher_mode : crypt_get_cipher_mode(cd), + crypt_get_uuid(cd), + opt_key_size ? opt_key_size / 8 : crypt_get_volume_key_size(cd), + ¶ms); +out: + crypt_free(cd); + if (r) + log_err(_("Creation of LUKS backup headers failed.\n")); + return r; +} - if (opt_iteration_time) - crypt_set_iteration_time(cd_new, opt_iteration_time); +/* Create fake header for original device */ +static int backup_fake_header(struct reenc_ctx *rc) +{ + struct crypt_device *cd_new = NULL; + struct crypt_params_luks1 params = {0}; + char cipher [MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN]; + + int r; + + log_dbg("Creating fake (cipher_null) header for original device."); + + if (!opt_key_size) + opt_key_size = DEFAULT_LUKS1_KEYBITS; if (opt_cipher) { r = crypt_parse_name_and_mode(opt_cipher, cipher, NULL, cipher_mode); @@ -517,28 +613,41 @@ static int backup_luks_headers(struct reenc_ctx *rc) } } - if ((r = crypt_format(cd_new, CRYPT_LUKS1, - opt_cipher ? cipher : crypt_get_cipher(cd), - opt_cipher ? cipher_mode : crypt_get_cipher_mode(cd), - crypt_get_uuid(cd), - NULL, crypt_get_volume_key_size(cd), ¶ms))) + r = create_empty_header(rc->header_file_org, NULL, 0); + if (r < 0) + return r; + + params.hash = opt_hash ?: DEFAULT_LUKS1_HASH; + params.data_alignment = 0; + params.data_device = rc->device; + + r = crypt_init(&cd_new, rc->header_file_org); + if (r < 0) + return r; + + r = crypt_format(cd_new, CRYPT_LUKS1, "cipher_null", "ecb", + NO_UUID, NULL, opt_key_size / 8, ¶ms); + if (r < 0) goto out; - log_verbose(_("New LUKS header for device %s created.\n"), rc->device); - for (i = 0; i < MAX_SLOT; i++) { - if (!rc->p[i].password) - continue; - if ((r = crypt_keyslot_add_by_volume_key(cd_new, i, - NULL, 0, rc->p[i].password, rc->p[i].passwordLen)) < 0) - goto out; - log_verbose(_("Activated keyslot %i.\n"), r); - r = 0; - } + r = crypt_keyslot_add_by_volume_key(cd_new, 0, NULL, 0, + rc->p[0].password, rc->p[0].passwordLen); + if (r < 0) + goto out; + + r = create_empty_header(rc->header_file_new, rc->header_file_org, 0); + if (r < 0) + goto out; + + params.data_alignment = opt_reduce_device_size; + r = create_new_header(rc, + opt_cipher ? cipher : DEFAULT_LUKS1_CIPHER, + opt_cipher ? cipher_mode : DEFAULT_LUKS1_MODE, + NULL, + (opt_key_size ? opt_key_size : DEFAULT_LUKS1_KEYBITS) / 8, + ¶ms); out: - crypt_free(cd); crypt_free(cd_new); - if (r) - log_err(_("Creation of LUKS backup headers failed.\n")); return r; } @@ -551,8 +660,10 @@ static void remove_headers(struct reenc_ctx *rc) if (crypt_init(&cd, NULL)) return; crypt_set_log_callback(cd, _quiet_log, NULL); - (void)crypt_deactivate(cd, rc->header_file_org); - (void)crypt_deactivate(cd, rc->header_file_new); + if (*rc->header_file_org) + (void)crypt_deactivate(cd, rc->header_file_org); + if (*rc->header_file_new) + (void)crypt_deactivate(cd, rc->header_file_new); crypt_free(cd); } @@ -750,7 +861,7 @@ static int copy_data(struct reenc_ctx *rc) } /* Check size */ - if (ioctl(fd_old, BLKGETSIZE64, &rc->device_size) < 0) { + if (ioctl(fd_new, BLKGETSIZE64, &rc->device_size) < 0) { log_err(_("Cannot get device size.\n")); goto out; } @@ -794,6 +905,11 @@ static int initialize_uuid(struct reenc_ctx *rc) log_dbg("Initialising UUID."); + if (opt_new) { + rc->device_uuid = strdup(NO_UUID); + return 0; + } + /* Try to load LUKS from device */ if ((r = crypt_init(&cd, rc->device))) return r; @@ -810,11 +926,11 @@ static int initialize_uuid(struct reenc_ctx *rc) } static int init_passphrase1(struct reenc_ctx *rc, struct crypt_device *cd, - const char *msg, int slot_check) + const char *msg, int slot_to_check, int check) { int r = -EINVAL, slot, retry_count; - slot = (slot_check == CRYPT_ANY_SLOT) ? 0 : slot_check; + slot = (slot_to_check == CRYPT_ANY_SLOT) ? 0 : slot_to_check; retry_count = opt_tries ?: 1; while (retry_count--) { @@ -830,8 +946,11 @@ static int init_passphrase1(struct reenc_ctx *rc, struct crypt_device *cd, /* library uses sigint internally, until it is fixed...*/ set_int_block(1); - r = crypt_activate_by_passphrase(cd, NULL, slot_check, - rc->p[slot].password, rc->p[slot].passwordLen, 0); + if (check) + r = crypt_activate_by_passphrase(cd, NULL, slot_to_check, + rc->p[slot].password, rc->p[slot].passwordLen, 0); + else + r = slot; if (r < 0) { crypt_safe_free(rc->p[slot].password); @@ -895,6 +1014,11 @@ static int initialize_passphrase(struct reenc_ctx *rc, const char *device) log_dbg("Passhrases initialization."); + if (opt_new && !rc->in_progress) { + r = init_passphrase1(rc, cd, _("Enter new LUKS passphrase: "), 0, 0); + return r > 0 ? 0 : r; + } + if ((r = crypt_init(&cd, device)) || (r = crypt_load(cd, CRYPT_LUKS1, NULL)) || (r = crypt_set_data_device(cd, rc->device))) { @@ -905,14 +1029,15 @@ static int initialize_passphrase(struct reenc_ctx *rc, const char *device) if (opt_key_file) { r = init_keyfile(rc, cd, opt_key_slot); } else if (rc->in_progress) { - r = init_passphrase1(rc, cd, _("Enter any LUKS passphrase: "), CRYPT_ANY_SLOT); + r = init_passphrase1(rc, cd, _("Enter any LUKS passphrase: "), + CRYPT_ANY_SLOT, 1); } else for (i = 0; i < MAX_SLOT; i++) { ki = crypt_keyslot_status(cd, i); if (ki != CRYPT_SLOT_ACTIVE && ki != CRYPT_SLOT_ACTIVE_LAST) continue; snprintf(msg, sizeof(msg), _("Enter LUKS passphrase for key slot %u: "), i); - r = init_passphrase1(rc, cd, msg, i); + r = init_passphrase1(rc, cd, msg, i, 1); if (r < 0) break; } @@ -965,7 +1090,7 @@ static int initialize_context(struct reenc_ctx *rc, const char *device) } if (!rc->in_progress) { - if (1 /*opt_new */) + if (!opt_reduce_device_size) rc->reencrypt_direction = FORWARD; else { rc->reencrypt_direction = BACKWARD; @@ -988,7 +1113,7 @@ static void destroy_context(struct reenc_ctx *rc) if ((rc->reencrypt_direction == FORWARD && rc->device_offset == rc->device_size) || (rc->reencrypt_direction == BACKWARD && - rc->device_offset == 0)) { + (rc->device_offset == 0 || rc->device_offset == (uint64_t)~0))) { unlink(rc->log_file); unlink(rc->header_file_org); unlink(rc->header_file_new); @@ -1012,9 +1137,13 @@ static int run_reencrypt(const char *device) log_dbg("Running reencryption."); if (!rc.in_progress) { - if ((r = initialize_passphrase(&rc, rc.device)) || - (r = backup_luks_headers(&rc)) || - (r = device_check(&rc, MAKE_UNUSABLE))) + if (opt_new) { + if ((r = initialize_passphrase(&rc, rc.device)) || + (r = backup_fake_header(&rc))) + goto out; + } else if ((r = initialize_passphrase(&rc, rc.device)) || + (r = backup_luks_headers(&rc)) || + (r = device_check(&rc, MAKE_UNUSABLE))) goto out; } else { if ((r = initialize_passphrase(&rc, rc.header_file_new))) @@ -1050,7 +1179,12 @@ static void help(poptContext popt_context, const char *arg __attribute__((unused)), void *data __attribute__((unused))) { - usage(popt_context, EXIT_SUCCESS, NULL, NULL); + if (key->shortName == '?') { + log_std("%s %s\n", PACKAGE_REENC, PACKAGE_VERSION); + poptPrintHelp(popt_context, stdout, 0); + exit(EXIT_SUCCESS); + } else + usage(popt_context, EXIT_SUCCESS, NULL, NULL); } static void _dbg_version_and_cmd(int argc, const char **argv) @@ -1081,6 +1215,7 @@ int main(int argc, const char **argv) { "debug", '\0', POPT_ARG_NONE, &opt_debug, 0, N_("Show debug messages"), NULL }, { "block-size", 'B', POPT_ARG_INT, &opt_bsize, 0, N_("Reencryption block size"), N_("MiB") }, { "cipher", 'c', POPT_ARG_STRING, &opt_cipher, 0, N_("The cipher used to encrypt the disk (see /proc/crypto)"), NULL }, + { "key-size", 's', POPT_ARG_INT, &opt_key_size, 0, N_("The size of the encryption key"), N_("BITS") }, { "hash", 'h', POPT_ARG_STRING, &opt_hash, 0, N_("The hash used to create the encryption key from the passphrase"), NULL }, { "key-file", 'd', POPT_ARG_STRING, &opt_key_file, 0, N_("Read the key from a file."), NULL }, { "iter-time", 'i', POPT_ARG_INT, &opt_iteration_time, 0, N_("PBKDF2 iteration time for LUKS (in ms)"), N_("msecs") }, @@ -1094,6 +1229,8 @@ int main(int argc, const char **argv) { "key-slot", 'S', POPT_ARG_INT, &opt_key_slot, 0, N_("Use only this slot (others will be disabled)."), NULL }, { "keyfile-offset", '\0', POPT_ARG_LONG, &opt_keyfile_offset, 0, N_("Number of bytes to skip in keyfile"), N_("bytes") }, { "keyfile-size", 'l', POPT_ARG_LONG, &opt_keyfile_size, 0, N_("Limits the read from keyfile"), N_("bytes") }, + { "reduce-device-size",'\0', POPT_ARG_INT, &opt_reduce_device_size, 0, N_("Reduce data device size (move data offset). DANGEROUS!"), N_("SECTORS") }, + { "new", 'N', POPT_ARG_NONE,&opt_new, 0, N_("Create new header on not encrypted device."), NULL }, POPT_TABLEEND }; poptContext popt_context; @@ -1109,7 +1246,7 @@ int main(int argc, const char **argv) popt_context = poptGetContext(PACKAGE, argc, argv, popt_options, 0); poptSetOtherOptionHelp(popt_context, - N_("[OPTION...] ]")); + N_("[OPTION...] ")); while((r = poptGetNextOpt(popt_context)) > 0) ; if (r < -1) @@ -1138,11 +1275,42 @@ int main(int argc, const char **argv) usage(popt_context, EXIT_FAILURE, _("Only one of --use-[u]random options is allowed."), poptGetInvocationName(popt_context)); + if (opt_bsize < 0 || opt_key_size < 0 || opt_iteration_time < 0 || + opt_tries < 0 || opt_keyfile_offset < 0 || opt_key_size < 0 || + opt_reduce_device_size < 0) { + usage(popt_context, EXIT_FAILURE, + _("Negative number for option not permitted."), + poptGetInvocationName(popt_context)); + } + if (opt_bsize < 1 || opt_bsize > 64) usage(popt_context, EXIT_FAILURE, _("Only values between 1MiB and 64 MiB allowed for reencryption block size."), poptGetInvocationName(popt_context)); + if (opt_reduce_device_size > (64 * 1024 * 1024 / SECTOR_SIZE)) + usage(popt_context, EXIT_FAILURE, + _("Maximum device reduce size is 64 MiB."), + poptGetInvocationName(popt_context)); + + if (opt_key_size % 8) + usage(popt_context, EXIT_FAILURE, + _("Key size must be a multiple of 8 bits"), + poptGetInvocationName(popt_context)); + + if (opt_key_slot != CRYPT_ANY_SLOT && + (opt_key_slot < 0 || opt_key_slot >= crypt_keyslot_max(CRYPT_LUKS1))) + usage(popt_context, EXIT_FAILURE, _("Key slot is invalid."), + poptGetInvocationName(popt_context)); + + if (opt_random && opt_urandom) + usage(popt_context, EXIT_FAILURE, _("Only one of --use-[u]random options is allowed."), + poptGetInvocationName(popt_context)); + + if (opt_new && !opt_reduce_device_size) + usage(popt_context, EXIT_FAILURE, _("Option --new must be used together with --reduce_device_size."), + poptGetInvocationName(popt_context)); + if (opt_debug) { opt_verbose = 1; crypt_set_debug_level(-1);