diff --git a/lib/internal.h b/lib/internal.h index a85bf57d..db7c158c 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -64,6 +64,7 @@ struct volume_key *crypt_generate_volume_key(struct crypt_device *cd, size_t key void crypt_free_volume_key(struct volume_key *vk); int crypt_volume_key_set_description(struct volume_key *key, const char *key_description, key_type_t keyring); +int crypt_volume_key_set_description_by_name(struct volume_key *vk, const char *key_name); void crypt_volume_key_set_id(struct volume_key *vk, int id); int crypt_volume_key_get_id(const struct volume_key *vk); void crypt_volume_key_add_next(struct volume_key **vks, struct volume_key *vk); diff --git a/lib/setup.c b/lib/setup.c index 685d199f..15716741 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -5336,7 +5336,14 @@ int crypt_activate_by_keyslot_context(struct crypt_device *cd, } else if (isTCRYPT(cd->type)) { r = 0; } else if (name && isPLAIN(cd->type)) { - if (kc->get_passphrase && kc->type != CRYPT_KC_TYPE_TOKEN) { + if (kc->type == CRYPT_KC_TYPE_VK_KEYRING) { + vk = crypt_alloc_volume_key(cd->u.plain.key_size, NULL); + if (!vk) + return -ENOMEM; + r = crypt_volume_key_set_description_by_name(vk, kc->u.vk_kr.key_description); + if (r < 0) + log_err(cd, _("Cannot use keyring key %s."), kc->u.vk_kr.key_description); + } else if (kc->get_passphrase && kc->type != CRYPT_KC_TYPE_TOKEN) { r = kc->get_passphrase(cd, kc, &passphrase, &passphrase_size); if (r < 0) return r; @@ -7295,7 +7302,10 @@ int crypt_use_keyring_for_vk(struct crypt_device *cd) uint32_t dmc_flags; /* dm backend must be initialized */ - if (!cd || !isLUKS2(cd->type)) + if (!cd) + return 0; + + if (!isPLAIN(cd->type) && !isLUKS2(cd->type)) return 0; if (!_vk_via_keyring || !kernel_keyring_support()) diff --git a/lib/utils_keyring.c b/lib/utils_keyring.c index 363fdf50..41295dfc 100644 --- a/lib/utils_keyring.c +++ b/lib/utils_keyring.c @@ -277,6 +277,36 @@ const char *key_type_name(key_type_t type) return NULL; } +key_type_t keyring_type_and_name(const char *key_name, const char **name) +{ + char type[16], *name_tmp; + size_t type_len; + + if (!key_name || key_name[0] != '%') + return INVALID_KEY; + + key_name++; + if (!*key_name || *key_name == ':') + return INVALID_KEY; + + name_tmp = strchr(key_name, ':'); + if (!name_tmp) + return INVALID_KEY; + name_tmp++; + + type_len = name_tmp - key_name - 1; + if (type_len >= sizeof(type) - 1) + return INVALID_KEY; + + memcpy(type, key_name, type_len); + type[type_len] = '\0'; + + if (name) + *name = name_tmp; + + return key_type_by_name(type); +} + key_serial_t keyring_find_key_id_by_name(const char *key_name) { key_serial_t id = 0; @@ -425,6 +455,11 @@ const char *key_type_name(key_type_t type) return NULL; } +key_type_t keyring_type_and_name(const char *key_name, const char **name) +{ + return INVALID_KEY; +} + key_serial_t keyring_find_key_id_by_name(const char *key_name) { return 0; diff --git a/lib/utils_keyring.h b/lib/utils_keyring.h index 33d8f221..fbbb2795 100644 --- a/lib/utils_keyring.h +++ b/lib/utils_keyring.h @@ -21,6 +21,7 @@ typedef enum { LOGON_KEY = 0, USER_KEY, BIG_KEY, TRUSTED_KEY, ENCRYPTED_KEY, INV const char *key_type_name(key_type_t ktype); key_type_t key_type_by_name(const char *name); +key_type_t keyring_type_and_name(const char *key_name, const char **name); key_serial_t keyring_find_key_id_by_name(const char *key_name); key_serial_t keyring_find_keyring_id_by_name(const char *keyring_name); diff --git a/lib/volumekey.c b/lib/volumekey.c index 796d3b01..e4da1f88 100644 --- a/lib/volumekey.c +++ b/lib/volumekey.c @@ -57,6 +57,17 @@ int crypt_volume_key_set_description(struct volume_key *vk, return 0; } +int crypt_volume_key_set_description_by_name(struct volume_key *vk, const char *key_name) +{ + const char *key_description = NULL; + key_type_t keyring = keyring_type_and_name(key_name, &key_description); + + if (keyring == INVALID_KEY) + return -EINVAL; + + return crypt_volume_key_set_description(vk, key_description, keyring); +} + void crypt_volume_key_set_id(struct volume_key *vk, int id) { if (vk && id >= 0) diff --git a/man/cryptsetup-open.8.adoc b/man/cryptsetup-open.8.adoc index 73a5dc56..4086b631 100644 --- a/man/cryptsetup-open.8.adoc +++ b/man/cryptsetup-open.8.adoc @@ -42,18 +42,35 @@ create (*OBSOLETE syntax*) Opens (creates a mapping with) backed by device . *WARNING:* You should always specify options *--cipher*, *--key-size* and -(if no keyfile is used) then also *--hash* to avoid incompatibility as +(if no keyfile or keyring is used) then also *--hash* to avoid incompatibility as default values can be different in older cryptsetup versions. + +The plain format also allows retrieving a volume key from a kernel keyring +specified by *--volume-key-keyring*. Key in kernel keyring must be configured +before issuing cryptsetup commands, as cryptsetup does not upload any keys to +the keyring in plain mode. For subsequent commands (like resize), the user must +ensure that the key in the keyring is unchanged. Otherwise, reloading the key +can cause data corruption after an unexpected key change. + ** can be [--hash, --cipher, --verify-passphrase, --sector-size, --key-file, --keyfile-size, --keyfile-offset, --key-size, --offset, --skip, --device-size, --size, --readonly, --shared, --allow-discards, ---refresh, --timeout, --verify-passphrase, --iv-large-sectors]. +--refresh, --timeout, --verify-passphrase, --iv-large-sectors, --volume-key-keyring]. -Example: 'cryptsetup open --type plain --cipher aes-cbc-essiv:sha256 --key-size 256 --hash sha256 /dev/sda10 e1' maps the raw -encrypted device /dev/sda10 to the mapped (decrypted) device -/dev/mapper/e1, which can then be mounted, fsck-ed or have a filesystem -created on it. +*EXAMPLES:* + +To map the encrypted device /dev/sda10 to the decrypted device /dev/mapper/e1, you can use + +*cryptsetup open --type plain --cipher aes-cbc-essiv:sha256 --key-size 256 --hash sha256 /dev/sda10 e1* + +The decrypted device can then be used as a normal block device to mount a filesystem. + +To map a device with a volume key in the preconfigured trusted or encrypted keyring, you need to specify +keyring with the key and remove hash specification, for example, to use *%trusted:mykey*: + +*cryptsetup open --type plain /dev/sda10 e1 --volume-key-keyring=%trusted:mykey --cipher aes-xts-plain64 --key-size 256* + +Note that the key size must match the preconfigured key in the keyring. === LUKS *open * + diff --git a/src/cryptsetup.c b/src/cryptsetup.c index b898a28a..5cad7957 100644 --- a/src/cryptsetup.c +++ b/src/cryptsetup.c @@ -224,7 +224,8 @@ static int action_open_plain(void) .offset = ARG_UINT64(OPT_OFFSET_ID), .sector_size = ARG_UINT32(OPT_SECTOR_SIZE_ID) ?: SECTOR_SIZE }; - char *password = NULL; + struct crypt_keyslot_context *kc = NULL; + char *password = NULL, *vk_description_activation = NULL; const char *activated_name = NULL; size_t passwordLen, key_size_max, signatures = 0, key_size = (ARG_UINT32(OPT_KEY_SIZE_ID) ?: DEFAULT_PLAIN_KEYBITS) / 8; @@ -249,12 +250,12 @@ static int action_open_plain(void) cipher, cipher_mode, key_size * 8); compat_warning = true; } - if (!ARG_SET(OPT_HASH_ID) && !ARG_SET(OPT_KEY_FILE_ID)) { + if (!ARG_SET(OPT_HASH_ID) && !ARG_SET(OPT_KEY_FILE_ID) && !ARG_SET(OPT_VOLUME_KEY_KEYRING_ID)) { log_err(_("WARNING: Using default options for hash (%s) that could be incompatible with older versions."), params.hash); compat_warning = true; } if (compat_warning) - log_err(_("For plain mode, always use options --cipher, --key-size and if no keyfile is used, then also --hash.")); + log_err(_("For plain mode, always use options --cipher, --key-size and if no keyfile or keyring is used, then also --hash.")); /* FIXME: temporary hack, no hashing for keyfiles in plain mode */ if (ARG_SET(OPT_KEY_FILE_ID) && !tools_is_stdin(ARG_STR(OPT_KEY_FILE_ID))) { @@ -264,6 +265,12 @@ static int action_open_plain(void) "in plain mode with keyfile specified.\n")); } + if (ARG_SET(OPT_VOLUME_KEY_KEYRING_ID)) { + r = tools_parse_vk_description(ARG_STR(OPT_VOLUME_KEY_KEYRING_ID), &vk_description_activation); + if (r < 0) + goto out; + } + if (params.hash && !strcmp(params.hash, "plain")) params.hash = NULL; @@ -349,7 +356,14 @@ static int action_open_plain(void) set_activation_flags(&activate_flags); - if (!tools_is_stdin(ARG_STR(OPT_KEY_FILE_ID))) { + if (ARG_SET(OPT_VOLUME_KEY_KEYRING_ID)) { + r = crypt_keyslot_context_init_by_vk_in_keyring(cd, vk_description_activation, &kc); + if (r < 0) + goto out; + + r = crypt_activate_by_keyslot_context(cd, activated_name, CRYPT_ANY_SLOT, + kc, CRYPT_ANY_SLOT, NULL, activate_flags | CRYPT_ACTIVATE_KEYRING_KEY); + } else if (!tools_is_stdin(ARG_STR(OPT_KEY_FILE_ID))) { /* If no hash, key is read directly, read size is always key_size * (possible --keyfile_size is ignored. * If hash is specified, --keyfile_size is applied. @@ -372,6 +386,8 @@ static int action_open_plain(void) CRYPT_ANY_SLOT, password, passwordLen, activate_flags); } out: + free(vk_description_activation); + crypt_keyslot_context_free(kc); crypt_free(cd); crypt_free(cd1); crypt_safe_free(password); @@ -3336,7 +3352,7 @@ static const char *verify_tcryptdump(void) return NULL; } -static const char * verify_open(void) +static const char *verify_open(void) { if (ARG_SET(OPT_PERSISTENT_ID) && ARG_SET(OPT_TEST_PASSPHRASE_ID)) return _("Option --persistent is not allowed with --test-passphrase."); @@ -3378,6 +3394,10 @@ static const char * verify_open(void) if (ARG_SET(OPT_UNBOUND_ID) && !ARG_SET(OPT_TEST_PASSPHRASE_ID)) return _("Option --unbound cannot be used without --test-passphrase."); + if (ARG_SET(OPT_VOLUME_KEY_KEYRING_ID) && (ARG_SET(OPT_HASH_ID) || + ARG_SET(OPT_VOLUME_KEY_FILE_ID)) && !strcmp_or_null(device_type, "plain")) + return _("Option --volume-key-keyring cannot be combined with --hash or --volume-key-file."); + /* "open --type tcrypt" and "tcryptDump" checks are identical */ return verify_tcryptdump(); }