From 0009d9532ee669dc8022ef1a62c72ad4961f9bb5 Mon Sep 17 00:00:00 2001 From: Ondrej Kozina Date: Mon, 27 Jun 2022 15:18:35 +0200 Subject: [PATCH] Extend LUKS2 decryption with datashift API tests. --- tests/api-test-2.c | 76 ++++++++++++++++++++++- tests/api_test.h | 11 ++++ tests/test_utils.c | 148 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+), 1 deletion(-) diff --git a/tests/api-test-2.c b/tests/api-test-2.c index cc5fcb46..b2cc69f5 100644 --- a/tests/api-test-2.c +++ b/tests/api-test-2.c @@ -414,6 +414,9 @@ static int _setup(void) _system("dmsetup create " DEVICE_EMPTY_name " --table \"0 10000 zero\"", 1); _system("dmsetup create " DEVICE_ERROR_name " --table \"0 10000 error\"", 1); + if (t_set_readahead(DEVICE_ERROR, 0)) + printf("cannot set read ahead on device %s\n", DEVICE_ERROR); + _system(" [ ! -e " IMAGE1 " ] && xz -dk " IMAGE1 ".xz", 1); fd = loop_attach(&DEVICE_1, IMAGE1, 0, 0, &ro); close(fd); @@ -4566,7 +4569,78 @@ static void Luks2Reencryption(void) OK_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0)); OK_(crypt_keyslot_destroy(cd, 9)); OK_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0)); - crypt_free(cd); + CRYPT_FREE(cd); + + _cleanup_dmdevices(); + OK_(create_dmdevice_over_loop(L_DEVICE_OK, 2 * r_header_size)); + OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size)); + + rparams = (struct crypt_params_reencrypt) { + .mode = CRYPT_REENCRYPT_DECRYPT, + .direction = CRYPT_REENCRYPT_FORWARD, + .resilience = "datashift-checksum", + .hash = "sha256", + .data_shift = r_header_size, + .flags = CRYPT_REENCRYPT_INITIALIZE_ONLY | CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT + }; + + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(set_fast_pbkdf(cd)); + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "xts-plain64", NULL, NULL, 64, NULL)); + EQ_(0, crypt_keyslot_add_by_volume_key(cd, 0, NULL, 64, PASSPHRASE, strlen(PASSPHRASE))); + OK_(crypt_header_backup(cd, CRYPT_LUKS2, BACKUP_FILE)); + CRYPT_FREE(cd); + + params2.data_device = DMDIR L_DEVICE_OK; + params2.sector_size = 512; + + /* create detached LUKS2 header (with data_offset == 0) */ + OK_(crypt_init(&cd, DMDIR H_DEVICE)); + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "xts-plain64", NULL, NULL, 64, ¶ms2)); + EQ_(crypt_get_data_offset(cd), 0); + OK_(set_fast_pbkdf(cd)); + EQ_(0, crypt_keyslot_add_by_volume_key(cd, 0, NULL, 64, PASSPHRASE, strlen(PASSPHRASE))); + CRYPT_FREE(cd); + + /* initiate LUKS2 decryption with datashift on bogus LUKS2 header (data_offset == 0) */ + OK_(crypt_init_data_device(&cd, DMDIR H_DEVICE, DMDIR L_DEVICE_OK)); + OK_(crypt_load(cd, CRYPT_LUKS2, NULL)); + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, CRYPT_ANY_SLOT, NULL, NULL, &rparams), "Illegal data offset"); + /* reencryption must not initalize */ + EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_NONE); + CRYPT_FREE(cd); + /* original data device must stay untouched */ + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_load(cd, CRYPT_LUKS2, NULL)); + EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_NONE); + CRYPT_FREE(cd); + + OK_(chmod(BACKUP_FILE, S_IRUSR|S_IWUSR)); + OK_(crypt_init_data_device(&cd, BACKUP_FILE, DMDIR L_DEVICE_OK)); + OK_(crypt_load(cd, CRYPT_LUKS2, NULL)); + + /* simulate read error at first segment beyond data offset*/ + OK_(dmdevice_error_io(L_DEVICE_OK, DMDIR L_DEVICE_OK, DEVICE_ERROR, 0, r_header_size, 8, ERR_RD)); + + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, CRYPT_ANY_SLOT, NULL, NULL, &rparams), "Could not read first data segment"); + CRYPT_FREE(cd); + + /* Device must not be in reencryption */ + OK_(crypt_init_data_device(&cd, BACKUP_FILE, DMDIR L_DEVICE_OK)); + OK_(crypt_load(cd, CRYPT_LUKS2, NULL)); + EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_NONE); + + /* simulate write error in original LUKS2 header area */ + OK_(dmdevice_error_io(L_DEVICE_OK, DMDIR L_DEVICE_OK, DEVICE_ERROR, 0, 0, 8, ERR_WR)); + + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, CRYPT_ANY_SLOT, NULL, NULL, &rparams), "Could not write first data segment"); + CRYPT_FREE(cd); + + /* Device must not be in reencryption */ + OK_(crypt_init_data_device(&cd, BACKUP_FILE, DMDIR L_DEVICE_OK)); + OK_(crypt_load(cd, CRYPT_LUKS2, NULL)); + EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_NONE); + CRYPT_FREE(cd); _cleanup_dmdevices(); } diff --git a/tests/api_test.h b/tests/api_test.h index da81c54e..cfcab604 100644 --- a/tests/api_test.h +++ b/tests/api_test.h @@ -38,6 +38,7 @@ int t_dm_check_versions(void); int t_dm_crypt_keyring_support(void); int t_dm_crypt_cpu_switch_support(void); int t_dm_crypt_discard_support(void); +int t_set_readahead(const char *device, unsigned value); int fips_mode(void); @@ -132,4 +133,14 @@ int loop_detach(const char *loop); int t_device_size_by_devno(dev_t devno, uint64_t *retval); int t_get_devno(const char *dev, dev_t *devno); +typedef enum { ERR_RD = 0, ERR_WR, ERR_RW, ERR_REMOVE } error_io_info; + +int dmdevice_error_io(const char *dm_name, + const char *dm_device, + const char *error_device, + uint64_t data_offset, + uint64_t offset, + uint64_t length, + error_io_info ei); + #endif diff --git a/tests/test_utils.c b/tests/test_utils.c index 707f36a9..c6080a5c 100644 --- a/tests/test_utils.c +++ b/tests/test_utils.c @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include #include #include @@ -161,6 +162,20 @@ int t_device_size(const char *device, uint64_t *size) return r; } +int t_set_readahead(const char *device, unsigned value) +{ + int devfd, r = 0; + + devfd = open(device, O_RDONLY); + if(devfd == -1) + return -EINVAL; + + if (ioctl(devfd, BLKRASET, value) < 0) + r = -EINVAL; + close(devfd); + return r; +} + int fips_mode(void) { int fd; @@ -207,6 +222,139 @@ int create_dmdevice_over_loop(const char *dm_name, const uint64_t size) return r; } +__attribute__((format(printf, 3, 4))) +static int _snprintf(char **r_ptr, size_t *r_remains, const char *format, ...) +{ + int len; + va_list argp; + + assert(r_remains); + assert(r_ptr); + + va_start(argp, format); + + len = vsnprintf(*r_ptr, *r_remains, format, argp); + if (len < 0 || (size_t)len >= *r_remains) + return -EINVAL; + + *r_ptr += len; + *r_remains -= len; + + va_end(argp); + + return 0; +} + +int dmdevice_error_io(const char *dm_name, + const char *dm_device, + const char *error_device, + uint64_t data_offset, + uint64_t offset, + uint64_t length, + error_io_info ei) +{ + char str[256], cmd[384]; + int r; + uint64_t dev_size; + size_t remains; + char *ptr; + + if (t_device_size(dm_device, &dev_size) < 0 || !length) + return -1; + + dev_size >>= TST_SECTOR_SHIFT; + + if (dev_size <= offset) + return -1; + + if (ei == ERR_REMOVE) { + r = snprintf(cmd, sizeof(cmd), + "dmsetup load %s --table \"0 %" PRIu64 " linear %s %" PRIu64 "\"", + dm_name, dev_size, THE_LOOP_DEV, data_offset); + if (r < 0 || (size_t)r >= sizeof(str)) + return -3; + + if ((r = _system(cmd, 1))) + return r; + + r = snprintf(cmd, sizeof(cmd), "dmsetup resume %s", dm_name); + if (r < 0 || (size_t)r >= sizeof(cmd)) + return -3; + + return _system(cmd, 1); + } + + if ((dev_size - offset) < length) { + printf("Not enough space on target device\n."); + return -2; + } + + remains = sizeof(str); + ptr = str; + + if (offset) { + r = _snprintf(&ptr, &remains, + "0 %" PRIu64 " linear %s %" PRIu64 "\n", + offset, THE_LOOP_DEV, data_offset); + if (r < 0) + return r; + } + r = _snprintf(&ptr, &remains, "%" PRIu64 " %" PRIu64 " delay ", + offset, length); + if (r < 0) + return r; + + if (ei == ERR_RW || ei == ERR_RD) { + r = _snprintf(&ptr, &remains, "%s 0 0", + error_device); + if (r < 0) + return r; + if (ei == ERR_RD) { + r = _snprintf(&ptr, &remains, " %s %" PRIu64 " 0", + THE_LOOP_DEV, data_offset + offset); + if (r < 0) + return r; + } + } else if (ei == ERR_WR) { + r = _snprintf(&ptr, &remains, "%s %" PRIu64 " 0 %s 0 0", + THE_LOOP_DEV, data_offset + offset, error_device); + if (r < 0) + return r; + } + + if (dev_size > (offset + length)) { + r = _snprintf(&ptr, &remains, + "\n%" PRIu64 " %" PRIu64 " linear %s %" PRIu64, + offset + length, dev_size - offset - length, THE_LOOP_DEV, + data_offset + offset + length); + if (r < 0) + return r; + } + + /* + * Hello darkness, my old friend... + * + * On few old distributions there's issue with + * processing multiline tables via dmsetup load --table. + * This workaround passes on all systems we run tests on. + */ + r = snprintf(cmd, sizeof(cmd), "dmsetup load %s <= sizeof(cmd)) + return -3; + + if ((r = _system(cmd, 1))) + return r; + + r = snprintf(cmd, sizeof(cmd), "dmsetup resume %s", dm_name); + if (r < 0 || (size_t)r >= sizeof(cmd)) + return -3; + + if ((r = _system(cmd, 1))) + return r; + + return t_set_readahead(dm_device, 0); +} + // Get key from kernel dm mapping table using dm-ioctl int get_key_dm(const char *name, char *buffer, unsigned int buffer_size) {