Extend LUKS2 decryption with datashift API tests.

This commit is contained in:
Ondrej Kozina
2022-06-27 15:18:35 +02:00
parent 47cb9b0ee2
commit 0009d9532e
3 changed files with 234 additions and 1 deletions

View File

@@ -414,6 +414,9 @@ static int _setup(void)
_system("dmsetup create " DEVICE_EMPTY_name " --table \"0 10000 zero\"", 1); _system("dmsetup create " DEVICE_EMPTY_name " --table \"0 10000 zero\"", 1);
_system("dmsetup create " DEVICE_ERROR_name " --table \"0 10000 error\"", 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); _system(" [ ! -e " IMAGE1 " ] && xz -dk " IMAGE1 ".xz", 1);
fd = loop_attach(&DEVICE_1, IMAGE1, 0, 0, &ro); fd = loop_attach(&DEVICE_1, IMAGE1, 0, 0, &ro);
close(fd); close(fd);
@@ -4566,7 +4569,78 @@ static void Luks2Reencryption(void)
OK_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0)); OK_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0));
OK_(crypt_keyslot_destroy(cd, 9)); OK_(crypt_keyslot_destroy(cd, 9));
OK_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0)); 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, &params2));
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(); _cleanup_dmdevices();
} }

View File

@@ -38,6 +38,7 @@ int t_dm_check_versions(void);
int t_dm_crypt_keyring_support(void); int t_dm_crypt_keyring_support(void);
int t_dm_crypt_cpu_switch_support(void); int t_dm_crypt_cpu_switch_support(void);
int t_dm_crypt_discard_support(void); int t_dm_crypt_discard_support(void);
int t_set_readahead(const char *device, unsigned value);
int fips_mode(void); 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_device_size_by_devno(dev_t devno, uint64_t *retval);
int t_get_devno(const char *dev, dev_t *devno); 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 #endif

View File

@@ -19,6 +19,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#include <assert.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <inttypes.h> #include <inttypes.h>
@@ -161,6 +162,20 @@ int t_device_size(const char *device, uint64_t *size)
return r; 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 fips_mode(void)
{ {
int fd; int fd;
@@ -207,6 +222,139 @@ int create_dmdevice_over_loop(const char *dm_name, const uint64_t size)
return r; 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 <<EOF\n%s\nEOF", dm_name, str);
if (r < 0 || (size_t)r >= 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 // Get key from kernel dm mapping table using dm-ioctl
int get_key_dm(const char *name, char *buffer, unsigned int buffer_size) int get_key_dm(const char *name, char *buffer, unsigned int buffer_size)
{ {