From fb6b4739e44220036196f370406de612007cad30 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Wed, 21 Feb 2018 14:46:01 +0100 Subject: [PATCH] Clean up keyring handling. Move all keyring functions to one place and separate LUKS2 specific code to generic handling. Also fix possible mismatch if volume key is in keyring but it is not native LUKS2 device (libarary cannot process such a device properly). --- lib/internal.h | 3 + lib/luks2/luks2.h | 9 +- lib/luks2/luks2_digest.c | 55 +++++ lib/luks2/luks2_token.c | 4 +- lib/luks2/luks2_token_keyring.c | 9 +- lib/setup.c | 392 ++++++++++++++------------------ 6 files changed, 245 insertions(+), 227 deletions(-) diff --git a/lib/internal.h b/lib/internal.h index 9bf5dbe7..4a61d104 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -191,6 +191,9 @@ int crypt_get_integrity_tag_size(struct crypt_device *cd); int crypt_key_in_keyring(struct crypt_device *cd); void crypt_set_key_in_keyring(struct crypt_device *cd, unsigned key_in_keyring); +int crypt_volume_key_load_in_keyring(struct crypt_device *cd, struct volume_key *vk); +int crypt_use_keyring_for_vk(const struct crypt_device *cd); +void crypt_drop_keyring_key(struct crypt_device *cd, const char *key_description); static inline uint64_t version(uint16_t major, uint16_t minor, uint16_t patch, uint16_t release) { diff --git a/lib/luks2/luks2.h b/lib/luks2/luks2.h index 9a988710..95a8a02d 100644 --- a/lib/luks2/luks2.h +++ b/lib/luks2/luks2.h @@ -352,11 +352,10 @@ int LUKS2_config_set_requirements(struct crypt_device *cd, struct luks2_hdr *hdr int LUKS2_unmet_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t reqs_mask, int quiet); -int crypt_use_keyring_for_vk(const struct crypt_device *cd); -int crypt_volume_key_load_in_keyring_by_keyslot(struct crypt_device *cd, struct volume_key *vk, int keyslot); -void crypt_drop_keyring_key(struct crypt_device *cd, const char *key_description); -int crypt_get_passphrase_from_keyring(const char *key_description, - char **passphrase, size_t *passphrase_len); +int LUKS2_key_description_by_segment(struct crypt_device *cd, + struct luks2_hdr *hdr, struct volume_key *vk, int segment); +int LUKS2_volume_key_load_in_keyring_by_keyslot(struct crypt_device *cd, + struct luks2_hdr *hdr, struct volume_key *vk, int keyslot); struct luks_phdr; int LUKS2_luks1_to_luks2(struct crypt_device *cd, diff --git a/lib/luks2/luks2_digest.c b/lib/luks2/luks2_digest.c index fe2661e5..dd64173f 100644 --- a/lib/luks2/luks2_digest.c +++ b/lib/luks2/luks2_digest.c @@ -341,3 +341,58 @@ void LUKS2_digests_erase_unused(struct crypt_device *cd, } } } + +/* Key description helpers */ +static char *get_key_description_by_digest(struct crypt_device *cd, int digest) +{ + char *desc, digest_str[3]; + int r; + size_t len; + + if (!crypt_get_uuid(cd)) + return NULL; + + r = snprintf(digest_str, sizeof(digest_str), "d%u", digest); + if (r < 0 || (size_t)r >= sizeof(digest_str)) + return NULL; + + /* "cryptsetup:-" + \0 */ + len = strlen(crypt_get_uuid(cd)) + strlen(digest_str) + 13; + + desc = malloc(len); + if (!desc) + return NULL; + + r = snprintf(desc, len, "%s:%s-%s", "cryptsetup", crypt_get_uuid(cd), digest_str); + if (r < 0 || (size_t)r >= len) { + free(desc); + return NULL; + } + + return desc; +} + +int LUKS2_key_description_by_segment(struct crypt_device *cd, + struct luks2_hdr *hdr, struct volume_key *vk, int segment) +{ + char *desc = get_key_description_by_digest(cd, LUKS2_digest_by_segment(cd, hdr, segment)); + int r; + + r = crypt_volume_key_set_description(vk, desc); + free(desc); + return r; +} + +int LUKS2_volume_key_load_in_keyring_by_keyslot(struct crypt_device *cd, + struct luks2_hdr *hdr, struct volume_key *vk, int keyslot) +{ + char *desc = get_key_description_by_digest(cd, LUKS2_digest_by_keyslot(cd, hdr, keyslot)); + int r; + + r = crypt_volume_key_set_description(vk, desc); + if (!r) + r = crypt_volume_key_load_in_keyring(cd, vk); + + free(desc); + return r; +} diff --git a/lib/luks2/luks2_token.c b/lib/luks2/luks2_token.c index 6bd11576..7f2f8aea 100644 --- a/lib/luks2/luks2_token.c +++ b/lib/luks2/luks2_token.c @@ -404,7 +404,7 @@ int LUKS2_token_open_and_activate(struct crypt_device *cd, keyslot = r; if ((name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && crypt_use_keyring_for_vk(cd)) - r = crypt_volume_key_load_in_keyring_by_keyslot(cd, vk, keyslot); + r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, hdr, vk, keyslot); if (r >= 0 && name) r = LUKS2_activate(cd, name, vk, flags); @@ -448,7 +448,7 @@ int LUKS2_token_open_and_activate_any(struct crypt_device *cd, keyslot = r; if (r >= 0 && (name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && crypt_use_keyring_for_vk(cd)) - r = crypt_volume_key_load_in_keyring_by_keyslot(cd, vk, keyslot); + r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, hdr, vk, keyslot); if (r >= 0 && name) r = LUKS2_activate(cd, name, vk, flags); diff --git a/lib/luks2/luks2_token_keyring.c b/lib/luks2/luks2_token_keyring.c index dde41f4b..9572d3da 100644 --- a/lib/luks2/luks2_token_keyring.c +++ b/lib/luks2/luks2_token_keyring.c @@ -31,6 +31,7 @@ static int keyring_open(struct crypt_device *cd, { json_object *jobj_token, *jobj_key; struct luks2_hdr *hdr; + int r; if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2))) return -EINVAL; @@ -41,8 +42,14 @@ static int keyring_open(struct crypt_device *cd, json_object_object_get_ex(jobj_token, "key_description", &jobj_key); - if (crypt_get_passphrase_from_keyring(json_object_get_string(jobj_key), buffer, buffer_len)) + r = keyring_get_passphrase(json_object_get_string(jobj_key), buffer, buffer_len); + if (r == -ENOTSUP) { + log_dbg("Kernel keyring features disabled."); return -EINVAL; + } else if (r < 0) { + log_dbg("keyring_get_passphrase failed (error %d)", r); + return -EINVAL; + } return 0; } diff --git a/lib/setup.c b/lib/setup.c index 127f42e6..7b1473dd 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -2020,83 +2020,6 @@ int crypt_repair(struct crypt_device *cd, return r; } -static char *crypt_get_key_description_by_digest(struct crypt_device *cd, int digest) -{ - char *desc, digest_str[3]; - int r; - size_t len; - - if (!crypt_get_uuid(cd)) - return NULL; - - r = snprintf(digest_str, sizeof(digest_str), "d%u", digest); - if (r < 0 || (size_t)r >= sizeof(digest_str)) - return NULL; - - /* "cryptsetup:-" + \0 */ - len = strlen(crypt_get_uuid(cd)) + strlen(digest_str) + 13; - - desc = malloc(len); - if (!desc) - return NULL; - - r = snprintf(desc, len, "%s:%s-%s", "cryptsetup", crypt_get_uuid(cd), digest_str); - if (r < 0 || (size_t)r >= len) { - free(desc); - return NULL; - } - - return desc; -} - -static int crypt_set_key_description_by_segment(struct crypt_device *cd, struct volume_key *vk, int segment) -{ - char *desc = crypt_get_key_description_by_digest(cd, LUKS2_digest_by_segment(cd, &cd->u.luks2.hdr, segment)); - int r; - - r = crypt_volume_key_set_description(vk, desc); - free(desc); - return r; -} - -/* internal only */ -static int crypt_volume_key_load_in_keyring(struct crypt_device *cd, struct volume_key *vk) -{ - int r; - - if (!vk || !cd) - return -EINVAL; - - if (!vk->key_description) { - log_dbg("Invalid key description"); - return -EINVAL; - } - - log_dbg("Loading key (%zu bytes) in thread keyring.", vk->keylength); - - r = keyring_add_key_in_thread_keyring(vk->key_description, vk->key, vk->keylength); - if (r) { - log_dbg("keyring_add_key_in_thread_keyring failed (error %d)", r); - log_err(cd, _("Failed to load key in kernel keyring.\n")); - } else - crypt_set_key_in_keyring(cd, 1); - - return r; -} - -int crypt_volume_key_load_in_keyring_by_keyslot(struct crypt_device *cd, struct volume_key *vk, int keyslot) -{ - char *desc = crypt_get_key_description_by_digest(cd, LUKS2_digest_by_keyslot(cd, &cd->u.luks2.hdr, keyslot)); - int r; - - r = crypt_volume_key_set_description(vk, desc); - if (!r) - r = crypt_volume_key_load_in_keyring(cd, vk); - - free(desc); - return r; -} - int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size) { struct crypt_dm_active_device dmd = {}; @@ -2136,7 +2059,12 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size) } if (crypt_key_in_keyring(cd)) { - r = crypt_set_key_description_by_segment(cd, dmd.u.crypt.vk, CRYPT_DEFAULT_SEGMENT); + if (!isLUKS2(cd->type)) { + r = -EINVAL; + goto out; + } + r = LUKS2_key_description_by_segment(cd, &cd->u.luks2.hdr, + dmd.u.crypt.vk, CRYPT_DEFAULT_SEGMENT); if (r) goto out; @@ -2336,37 +2264,6 @@ void crypt_free(struct crypt_device *cd) free(cd); } -/* internal only */ -int crypt_key_in_keyring(struct crypt_device *cd) -{ - return cd ? cd->key_in_keyring : 0; -} - -/* internal only */ -void crypt_set_key_in_keyring(struct crypt_device *cd, unsigned key_in_keyring) -{ - if (!cd) - return; - - cd->key_in_keyring = key_in_keyring; -} - -/* internal only */ -void crypt_drop_keyring_key(struct crypt_device *cd, const char *key_description) -{ - int r; - - if (!key_description) - return; - - log_dbg("Requesting keyring key for revoke and unlink."); - - r = keyring_revoke_and_unlink_key(key_description); - if (r) - log_dbg("keyring_revoke_and_unlink failed (error %d)", r); - crypt_set_key_in_keyring(cd, 0); -} - static char *crypt_get_device_key_description(const char *name) { char *tmp = NULL; @@ -2486,7 +2383,12 @@ int crypt_resume_by_passphrase(struct crypt_device *cd, keyslot = r; if (crypt_use_keyring_for_vk(cd)) { - r = crypt_volume_key_load_in_keyring_by_keyslot(cd, vk, keyslot); + if (!isLUKS2(cd->type)) { + r = -EINVAL; + goto out; + } + r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, + &cd->u.luks2.hdr, vk, keyslot); if (r < 0) goto out; } @@ -2552,7 +2454,12 @@ int crypt_resume_by_keyfile_device_offset(struct crypt_device *cd, keyslot = r; if (crypt_use_keyring_for_vk(cd)) { - r = crypt_volume_key_load_in_keyring_by_keyslot(cd, vk, keyslot); + if (!isLUKS2(cd->type)) { + r = -EINVAL; + goto out; + } + r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, + &cd->u.luks2.hdr, vk, keyslot); if (r < 0) goto out; } @@ -3017,8 +2924,10 @@ static int _activate_by_passphrase(struct crypt_device *cd, if (r >= 0) { keyslot = r; - if ((name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && crypt_use_keyring_for_vk(cd)) { - r = crypt_volume_key_load_in_keyring_by_keyslot(cd, vk, keyslot); + if ((name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && + crypt_use_keyring_for_vk(cd)) { + r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, + &cd->u.luks2.hdr, vk, keyslot); if (r < 0) goto out; flags |= CRYPT_ACTIVATE_KEYRING_KEY; @@ -3155,8 +3064,10 @@ int crypt_activate_by_keyfile_device_offset(struct crypt_device *cd, goto out; keyslot = r; - if ((name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && crypt_use_keyring_for_vk(cd)) { - r = crypt_volume_key_load_in_keyring_by_keyslot(cd, vk, keyslot); + if ((name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && + crypt_use_keyring_for_vk(cd)) { + r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, + &cd->u.luks2.hdr, vk, keyslot); if (r < 0) goto out; flags |= CRYPT_ACTIVATE_KEYRING_KEY; @@ -3292,8 +3203,10 @@ int crypt_activate_by_volume_key(struct crypt_device *cd, if (r == -EPERM || r == -ENOENT) log_err(cd, _("Volume key does not match the volume.\n")); - if (!r && (name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && crypt_use_keyring_for_vk(cd)) { - r = crypt_set_key_description_by_segment(cd, vk, CRYPT_DEFAULT_SEGMENT); + if (!r && (name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && + crypt_use_keyring_for_vk(cd)) { + r = LUKS2_key_description_by_segment(cd, + &cd->u.luks2.hdr, vk, CRYPT_DEFAULT_SEGMENT); if (!r) r = crypt_volume_key_load_in_keyring(cd, vk); if (!r) @@ -4115,44 +4028,6 @@ void *crypt_get_hdr(struct crypt_device *cd, const char *type) return NULL; } -static int kernel_keyring_support(void) -{ - static unsigned _checked = 0; - - if (!_checked) { - _kernel_keyring_supported = keyring_check(); - _checked = 1; - } - - return _kernel_keyring_supported; -} - -static int dmcrypt_keyring_bug(void) -{ - uint64_t kversion; - - if (kernel_version(&kversion)) - return 1; - return kversion < version(4,15,0,0); -} - -int crypt_use_keyring_for_vk(const struct crypt_device *cd) -{ - uint32_t dmc_flags; - - /* dm backend must be initialised */ - if (!cd || !isLUKS2(cd->type)) - return 0; - - if (!_vk_via_keyring || !kernel_keyring_support()) - return 0; - - if (dm_flags(DM_CRYPT, &dmc_flags)) - return dmcrypt_keyring_bug() ? 0 : 1; - - return (dmc_flags & DM_KERNEL_KEYRING_SUPPORTED); -} - /* * Token handling */ @@ -4308,70 +4183,6 @@ int crypt_metadata_locking(struct crypt_device *cd, int enable) return 0; } -int crypt_volume_key_keyring(struct crypt_device *cd, int enable) -{ - _vk_via_keyring = enable ? 1 : 0; - return 0; -} - -/* internal only */ -int crypt_get_passphrase_from_keyring(const char *key_description, - char **passphrase, - size_t *passphrase_len) -{ - int r; - - log_dbg("Looking for passphrase with key description: %s", key_description); - - r = keyring_get_passphrase(key_description, passphrase, passphrase_len); - if (r) { - if (r == -ENOTSUP) - log_dbg("Kernel keyring features disabled."); - else - log_dbg("keyring_get_passphrase failed (error %d)", r); - } - - return r; -} - -int crypt_activate_by_keyring(struct crypt_device *cd, - const char *name, - const char *key_description, - int keyslot, - uint32_t flags) -{ - char *passphrase; - size_t passphrase_size; - int r; - - if (!cd || !key_description) - return -EINVAL; - - log_dbg("%s volume %s [keyslot %d] using passphrase in keyring key %s.", - name ? "Activating" : "Checking", name ?: "passphrase", keyslot, key_description); - - if (!kernel_keyring_support()) { - log_err(cd, _("Kernel keyring is not supported by the kernel.\n")); - return -EINVAL; - } - - r = _activate_check_status(cd, name); - if (r < 0) - return r; - - if (crypt_get_passphrase_from_keyring(key_description, &passphrase, &passphrase_size)) { - log_err(cd, _("Failed to read passphrase from keyring key %s"), key_description); - return -EINVAL; - } - - r = _activate_by_passphrase(cd, name, keyslot, passphrase, passphrase_size, flags); - - crypt_memzero(passphrase, passphrase_size); - free(passphrase); - - return r; -} - int crypt_persistent_flags_set(struct crypt_device *cd, crypt_flags_type type, uint32_t flags) { int r; @@ -4479,6 +4290,149 @@ out: return keyslot; } +/* + * Keyring handling + */ + +static int kernel_keyring_support(void) +{ + static unsigned _checked = 0; + + if (!_checked) { + _kernel_keyring_supported = keyring_check(); + _checked = 1; + } + + return _kernel_keyring_supported; +} + +static int dmcrypt_keyring_bug(void) +{ + uint64_t kversion; + + if (kernel_version(&kversion)) + return 1; + return kversion < version(4,15,0,0); +} + +int crypt_use_keyring_for_vk(const struct crypt_device *cd) +{ + uint32_t dmc_flags; + + /* dm backend must be initialised */ + if (!cd || !isLUKS2(cd->type)) + return 0; + + if (!_vk_via_keyring || !kernel_keyring_support()) + return 0; + + if (dm_flags(DM_CRYPT, &dmc_flags)) + return dmcrypt_keyring_bug() ? 0 : 1; + + return (dmc_flags & DM_KERNEL_KEYRING_SUPPORTED); +} + +int crypt_volume_key_keyring(struct crypt_device *cd, int enable) +{ + _vk_via_keyring = enable ? 1 : 0; + return 0; +} + +/* internal only */ +int crypt_volume_key_load_in_keyring(struct crypt_device *cd, struct volume_key *vk) +{ + int r; + + if (!vk || !cd) + return -EINVAL; + + if (!vk->key_description) { + log_dbg("Invalid key description"); + return -EINVAL; + } + + log_dbg("Loading key (%zu bytes) in thread keyring.", vk->keylength); + + r = keyring_add_key_in_thread_keyring(vk->key_description, vk->key, vk->keylength); + if (r) { + log_dbg("keyring_add_key_in_thread_keyring failed (error %d)", r); + log_err(cd, _("Failed to load key in kernel keyring.\n")); + } else + crypt_set_key_in_keyring(cd, 1); + + return r; +} + +/* internal only */ +int crypt_key_in_keyring(struct crypt_device *cd) +{ + return cd ? cd->key_in_keyring : 0; +} + +/* internal only */ +void crypt_set_key_in_keyring(struct crypt_device *cd, unsigned key_in_keyring) +{ + if (!cd) + return; + + cd->key_in_keyring = key_in_keyring; +} + +/* internal only */ +void crypt_drop_keyring_key(struct crypt_device *cd, const char *key_description) +{ + int r; + + if (!key_description) + return; + + log_dbg("Requesting keyring key for revoke and unlink."); + + r = keyring_revoke_and_unlink_key(key_description); + if (r) + log_dbg("keyring_revoke_and_unlink failed (error %d)", r); + crypt_set_key_in_keyring(cd, 0); +} + +int crypt_activate_by_keyring(struct crypt_device *cd, + const char *name, + const char *key_description, + int keyslot, + uint32_t flags) +{ + char *passphrase; + size_t passphrase_size; + int r; + + if (!cd || !key_description) + return -EINVAL; + + log_dbg("%s volume %s [keyslot %d] using passphrase in keyring.", + name ? "Activating" : "Checking", name ?: "passphrase", keyslot); + + if (!kernel_keyring_support()) { + log_err(cd, _("Kernel keyring is not supported by the kernel.\n")); + return -EINVAL; + } + + r = _activate_check_status(cd, name); + if (r < 0) + return r; + + r = keyring_get_passphrase(key_description, &passphrase, &passphrase_size); + if (r < 0) { + log_err(cd, _("Failed to read passphrase from keyring (error %d)."), r); + return -EINVAL; + } + + r = _activate_by_passphrase(cd, name, keyslot, passphrase, passphrase_size, flags); + + crypt_memzero(passphrase, passphrase_size); + free(passphrase); + + return r; +} + static void __attribute__((destructor)) libcryptsetup_exit(void) { crypt_backend_destroy();