diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h index 62cfaffe..d44ea069 100644 --- a/lib/libcryptsetup.h +++ b/lib/libcryptsetup.h @@ -64,6 +64,23 @@ struct crypt_device; /* crypt device handle */ */ int crypt_init(struct crypt_device **cd, const char *device); +/** + * Initialize crypt device handle with optional data device and check + * if devices exist. + * + * @param cd Returns pointer to crypt device handle + * @param device Path to the backing device or detached header. + * @param device_device Path to the data device or @e NULL. + * + * @return @e 0 on success or negative errno value otherwise. + * + * @note Note that logging is not initialized here, possible messages use + * default log function. + */ +int crypt_init_data_device(struct crypt_device **cd, + const char *device, + const char *data_device); + /** * Initialize crypt device handle from provided active device name, * and, optionally, from separate metadata (header) device @@ -1317,6 +1334,16 @@ const char *crypt_get_uuid(struct crypt_device *cd); */ const char *crypt_get_device_name(struct crypt_device *cd); +/** + * Get path to detached metadata device or @e NULL if it is not detached. + * + * @param cd crypt device handle + * + * @return path to underlaying device name + * + */ +const char *crypt_get_metadata_device_name(struct crypt_device *cd); + /** * Get device offset in 512-bytes sectors where real data starts (on underlying device). * diff --git a/lib/libcryptsetup.sym b/lib/libcryptsetup.sym index c4d2c43f..3740bde7 100644 --- a/lib/libcryptsetup.sym +++ b/lib/libcryptsetup.sym @@ -1,6 +1,7 @@ CRYPTSETUP_2.0 { global: crypt_init; + crypt_init_data_device; crypt_init_by_name; crypt_init_by_name_and_header; @@ -72,6 +73,7 @@ CRYPTSETUP_2.0 { crypt_get_iv_offset; crypt_get_volume_key_size; crypt_get_device_name; + crypt_get_metadata_device_name; crypt_get_verity_info; crypt_get_sector_size; diff --git a/lib/setup.c b/lib/setup.c index 9a38617a..c13bac5c 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -604,25 +604,11 @@ static int crypt_check_data_device_size(struct crypt_device *cd) return r; } -int crypt_set_data_device(struct crypt_device *cd, const char *device) +static int _crypt_set_data_device(struct crypt_device *cd, const char *device) { struct device *dev = NULL; int r; - if (!cd) - return -EINVAL; - - log_dbg(cd, "Setting ciphertext data device to %s.", device ?: "(none)"); - - if (!isLUKS1(cd->type) && !isLUKS2(cd->type) && !isVERITY(cd->type)) { - log_err(cd, _("This operation is not supported for this device type.")); - return -EINVAL; - } - - /* metadata device must be set */ - if (!cd->device || !device) - return -EINVAL; - r = device_alloc(cd, &dev, device); if (r < 0) return r; @@ -637,6 +623,42 @@ int crypt_set_data_device(struct crypt_device *cd, const char *device) return crypt_check_data_device_size(cd); } +int crypt_set_data_device(struct crypt_device *cd, const char *device) +{ + /* metadata device must be set */ + if (!cd || !cd->device || !device) + return -EINVAL; + + log_dbg(cd, "Setting ciphertext data device to %s.", device ?: "(none)"); + + if (!isLUKS1(cd->type) && !isLUKS2(cd->type) && !isVERITY(cd->type)) { + log_err(cd, _("This operation is not supported for this device type.")); + return -EINVAL; + } + + return _crypt_set_data_device(cd, device); +} + +int crypt_init_data_device(struct crypt_device **cd, const char *device, const char *data_device) +{ + int r; + + if (!cd) + return -EINVAL; + + r = crypt_init(cd, device); + if (r || !data_device) + return r; + + log_dbg(NULL, "Setting ciphertext data device to %s.", data_device ?: "(none)"); + r = _crypt_set_data_device(*cd, data_device); + if (r) + crypt_free(*cd); + + return r; +} + + /* internal only */ struct crypt_pbkdf_type *crypt_get_pbkdf(struct crypt_device *cd) { @@ -775,6 +797,11 @@ static int _crypt_load_tcrypt(struct crypt_device *cd, struct crypt_params_tcryp if (!params) return -EINVAL; + if (cd->metadata_device) { + log_err(cd, _("Detached metadata device is not supported for this crypt type.")); + return -EINVAL; + } + r = init_crypto(cd); if (r < 0) return r; @@ -1338,6 +1365,11 @@ static int _crypt_format_plain(struct crypt_device *cd, return -EINVAL; } + if (cd->metadata_device) { + log_err(cd, _("Detached metadata device is not supported for this crypt type.")); + return -EINVAL; + } + /* For compatibility with old params structure */ if (!sector_size) sector_size = SECTOR_SIZE; @@ -1429,14 +1461,18 @@ static int _crypt_format_luks1(struct crypt_device *cd, } if (params && params->data_device) { - cd->metadata_device = cd->device; + if (!cd->metadata_device) + cd->metadata_device = cd->device; + else + device_free(cd, cd->device); cd->device = NULL; if (device_alloc(cd, &cd->device, params->data_device) < 0) return -ENOMEM; + } + + if (params && (params->data_alignment || cd->metadata_device)) required_alignment = params->data_alignment * SECTOR_SIZE; - } else if (params && params->data_alignment) { - required_alignment = params->data_alignment * SECTOR_SIZE; - } else + else device_topology_alignment(cd, cd->device, &required_alignment, &alignment_offset, DEFAULT_DISK_ALIGNMENT); @@ -1549,14 +1585,18 @@ static int _crypt_format_luks2(struct crypt_device *cd, return r; if (params && params->data_device) { - cd->metadata_device = cd->device; + if (!cd->metadata_device) + cd->metadata_device = cd->device; + else + device_free(cd, cd->device); cd->device = NULL; if (device_alloc(cd, &cd->device, params->data_device) < 0) return -ENOMEM; + } + + if (params && (params->data_alignment || cd->metadata_device)) required_alignment = params->data_alignment * SECTOR_SIZE; - } else if (params && params->data_alignment) { - required_alignment = params->data_alignment * SECTOR_SIZE; - } else + else device_topology_alignment(cd, cd->device, &required_alignment, &alignment_offset, DEFAULT_DISK_ALIGNMENT); @@ -1683,6 +1723,11 @@ static int _crypt_format_loopaes(struct crypt_device *cd, return -EINVAL; } + if (cd->metadata_device) { + log_err(cd, _("Detached metadata device is not supported for this crypt type.")); + return -EINVAL; + } + if (!(cd->type = strdup(CRYPT_LOOPAES))) return -ENOMEM; @@ -1713,7 +1758,10 @@ static int _crypt_format_verity(struct crypt_device *cd, return -EINVAL; } - if (!params || !params->data_device) + if (!params) + return -EINVAL; + + if (!params->data_device && !cd->metadata_device) return -EINVAL; if (params->hash_type > VERITY_MAX_HASH_TYPE) { @@ -1740,9 +1788,12 @@ static int _crypt_format_verity(struct crypt_device *cd, if (!(cd->type = strdup(CRYPT_VERITY))) return -ENOMEM; - r = crypt_set_data_device(cd, params->data_device); - if (r) - return r; + if (params->data_device) { + r = crypt_set_data_device(cd, params->data_device); + if (r) + return r; + } + if (!params->data_size) { r = device_size(cd->device, &data_device_size); if (r < 0) @@ -3783,6 +3834,20 @@ const char *crypt_get_device_name(struct crypt_device *cd) return path; } +const char *crypt_get_metadata_device_name(struct crypt_device *cd) +{ + const char *path; + + if (!cd || !cd->metadata_device) + return NULL; + + path = device_block_path(cd->metadata_device); + if (!path) + path = device_path(cd->metadata_device); + + return path; +} + int crypt_get_volume_key_size(struct crypt_device *cd) { int r; diff --git a/src/cryptsetup.c b/src/cryptsetup.c index 4741c87e..59411fc9 100644 --- a/src/cryptsetup.c +++ b/src/cryptsetup.c @@ -1130,7 +1130,7 @@ static int action_open_luks(void) activated_name = opt_test_passphrase ? NULL : action_argv[1]; - if ((r = crypt_init(&cd, header_device))) + if ((r = crypt_init_data_device(&cd, header_device, data_device))) goto out; if ((r = crypt_load(cd, luksType(opt_type), NULL))) { @@ -1139,10 +1139,6 @@ static int action_open_luks(void) goto out; } - if (data_device && - (r = crypt_set_data_device(cd, data_device))) - goto out; - if (!data_device && (crypt_get_data_offset(cd) < 8)) { log_err(_("Reduced data offset is allowed only for detached LUKS header.")); r = -EINVAL; diff --git a/src/cryptsetup_reencrypt.c b/src/cryptsetup_reencrypt.c index 98e05fe2..96725042 100644 --- a/src/cryptsetup_reencrypt.c +++ b/src/cryptsetup_reencrypt.c @@ -480,9 +480,8 @@ static int activate_luks_headers(struct reenc_ctx *rc) } else return -EINVAL; - if ((r = crypt_init(&cd, rc->header_file_org)) || - (r = crypt_load(cd, CRYPT_LUKS, NULL)) || - (r = crypt_set_data_device(cd, rc->device))) + if ((r = crypt_init_data_device(&cd, rc->header_file_org, rc->device)) || + (r = crypt_load(cd, CRYPT_LUKS, NULL))) goto out; log_verbose(_("Activating temporary device using old LUKS header.")); @@ -491,9 +490,8 @@ static int activate_luks_headers(struct reenc_ctx *rc) CRYPT_ACTIVATE_READONLY|CRYPT_ACTIVATE_PRIVATE)) < 0) goto out; - if ((r = crypt_init(&cd_new, rc->header_file_new)) || - (r = crypt_load(cd_new, CRYPT_LUKS, NULL)) || - (r = crypt_set_data_device(cd_new, rc->device))) + if ((r = crypt_init_data_device(&cd_new, rc->header_file_new, rc->device)) || + (r = crypt_load(cd_new, CRYPT_LUKS, NULL))) goto out; log_verbose(_("Activating temporary device using new LUKS header.")); @@ -1348,9 +1346,8 @@ static int initialize_passphrase(struct reenc_ctx *rc, const char *device) return r > 0 ? 0 : r; } - if ((r = crypt_init(&cd, device)) || - (r = crypt_load(cd, CRYPT_LUKS, NULL)) || - (r = crypt_set_data_device(cd, rc->device))) { + if ((r = crypt_init_data_device(&cd, device, rc->device)) || + (r = crypt_load(cd, CRYPT_LUKS, NULL))) { crypt_free(cd); return r; } diff --git a/src/veritysetup.c b/src/veritysetup.c index e65ccd69..56a9df58 100644 --- a/src/veritysetup.c +++ b/src/veritysetup.c @@ -145,7 +145,7 @@ static int _activate(const char *dm_device, ssize_t hash_size; int r; - if ((r = crypt_init(&cd, hash_device))) + if ((r = crypt_init_data_device(&cd, hash_device, data_device))) goto out; if (opt_ignore_corruption) @@ -170,9 +170,6 @@ static int _activate(const char *dm_device, goto out; r = crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, ¶ms); } - if (r < 0) - goto out; - r = crypt_set_data_device(cd, data_device); if (r < 0) goto out; diff --git a/tests/api-test-2.c b/tests/api-test-2.c index 82e4d70e..6fdebab4 100644 --- a/tests/api-test-2.c +++ b/tests/api-test-2.c @@ -1022,9 +1022,21 @@ static void Luks2HeaderLoad(void) OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_OK)); OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0)); EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); + OK_(!crypt_get_metadata_device_name(cd)); + EQ_(strcmp(DMDIR H_DEVICE, crypt_get_metadata_device_name(cd)), 0); OK_(crypt_deactivate(cd, CDEVICE_1)); crypt_free(cd); + // repeat with init with two devices + OK_(crypt_init_data_device(&cd, DMDIR H_DEVICE, DMDIR L_DEVICE_OK)); + OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms)); + crypt_free(cd); + OK_(crypt_init_data_device(&cd, DMDIR H_DEVICE, DMDIR L_DEVICE_OK)); + OK_(crypt_load(cd, CRYPT_LUKS2, NULL)); + OK_(!crypt_get_metadata_device_name(cd)); + EQ_(strcmp(DMDIR H_DEVICE, crypt_get_metadata_device_name(cd)), 0); + crypt_free(cd); + // bad header: device too small (payloadOffset > device_size) OK_(crypt_init(&cd, DMDIR H_DEVICE_WRONG)); FAIL_(crypt_load(cd, CRYPT_LUKS2, NULL), "Device too small"); diff --git a/tests/api-test.c b/tests/api-test.c index c3e9ca94..08a96ee1 100644 --- a/tests/api-test.c +++ b/tests/api-test.c @@ -457,6 +457,12 @@ static void AddDevicePlain(void) OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0)); crypt_free(cd); + // init with detached header is not supported + OK_(crypt_init_data_device(&cd, DEVICE_2, DEVICE_1)); + FAIL_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, ¶ms), + "can't use plain with separate metadata device"); + crypt_free(cd); + FAIL_(crypt_init_by_name_and_header(&cd, CDEVICE_1, H_DEVICE),"can't init plain device by header device"); OK_(crypt_init_by_name(&cd, CDEVICE_1)); OK_(strcmp(cipher_mode,crypt_get_cipher_mode(cd))); @@ -474,6 +480,7 @@ static void AddDevicePlain(void) // crypt_set_data_device FAIL_(crypt_set_data_device(cd,H_DEVICE),"can't set data device for plain device"); + NULL_(crypt_get_metadata_device_name(cd)); // crypt_get_type OK_(strcmp(crypt_get_type(cd),CRYPT_PLAIN)); @@ -1142,9 +1149,21 @@ static void LuksHeaderLoad(void) OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_OK)); OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0)); EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); + OK_(!crypt_get_metadata_device_name(cd)); + EQ_(strcmp(DMDIR H_DEVICE, crypt_get_metadata_device_name(cd)), 0); OK_(crypt_deactivate(cd, CDEVICE_1)); crypt_free(cd); + // repeat with init with two devices + OK_(crypt_init_data_device(&cd, DMDIR H_DEVICE, DMDIR L_DEVICE_OK)); + OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, ¶ms)); + crypt_free(cd); + OK_(crypt_init_data_device(&cd, DMDIR H_DEVICE, DMDIR L_DEVICE_OK)); + OK_(crypt_load(cd, CRYPT_LUKS1, NULL)); + OK_(!crypt_get_metadata_device_name(cd)); + EQ_(strcmp(DMDIR H_DEVICE, crypt_get_metadata_device_name(cd)), 0); + crypt_free(cd); + // bad header: device too small (payloadOffset > device_size) OK_(crypt_init(&cd, DMDIR H_DEVICE_WRONG)); FAIL_(crypt_load(cd, CRYPT_LUKS1, NULL), "Device too small"); @@ -1493,6 +1512,12 @@ static void VerityTest(void) OK_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, ¶ms)); crypt_free(cd); + params.data_device = NULL; + OK_(crypt_init_data_device(&cd, DEVICE_2, DEVICE_EMPTY)); + OK_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, ¶ms)); + EQ_(strcmp(DEVICE_2, crypt_get_metadata_device_name(cd)), 0); + crypt_free(cd); + /* Verify */ OK_(crypt_init(&cd, DEVICE_2)); memset(¶ms, 0, sizeof(params)); @@ -1614,6 +1639,7 @@ static void TcryptTest(void) reset_log(); OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, NULL, 0, CRYPT_ACTIVATE_READONLY)); + NULL_(crypt_get_metadata_device_name(cd)); crypt_free(cd); OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, NULL)); @@ -1637,6 +1663,11 @@ static void TcryptTest(void) OK_(crypt_deactivate(cd, CDEVICE_1)); crypt_free(cd); + // init with detached header is not supported + OK_(crypt_init_data_device(&cd, tcrypt_dev2, DEVICE_2)); + FAIL_(crypt_load(cd, CRYPT_TCRYPT, ¶ms), "can't use tcrypt with separate metadata device"); + crypt_free(cd); + // Following test uses non-FIPS algorithms in the cipher chain if(_fips_mode) return;