diff options
Diffstat (limited to 'lib/luks2/luks2_token.c')
-rw-r--r-- | lib/luks2/luks2_token.c | 295 |
1 files changed, 208 insertions, 87 deletions
diff --git a/lib/luks2/luks2_token.c b/lib/luks2/luks2_token.c index 5f65918..9c09be2 100644 --- a/lib/luks2/luks2_token.c +++ b/lib/luks2/luks2_token.c @@ -1,8 +1,8 @@ /* * LUKS - Linux Unified Key Setup v2, token handling * - * Copyright (C) 2016-2023 Red Hat, Inc. All rights reserved. - * Copyright (C) 2016-2023 Milan Broz + * Copyright (C) 2016-2024 Red Hat, Inc. All rights reserved. + * Copyright (C) 2016-2024 Milan Broz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -25,7 +25,9 @@ #include "luks2_internal.h" #if USE_EXTERNAL_TOKENS +#define TOKENS_PATH_MAX PATH_MAX static bool external_tokens_enabled = true; +static char external_tokens_path[TOKENS_PATH_MAX] = EXTERNAL_LUKS2_TOKENS_PATH; #else static bool external_tokens_enabled = false; #endif @@ -51,31 +53,37 @@ void crypt_token_external_disable(void) const char *crypt_token_external_path(void) { - return external_tokens_enabled ? EXTERNAL_LUKS2_TOKENS_PATH : NULL; +#if USE_EXTERNAL_TOKENS + return external_tokens_enabled ? external_tokens_path : NULL; +#else + return NULL; +#endif } #if USE_EXTERNAL_TOKENS -static void *token_dlvsym(struct crypt_device *cd, - void *handle, - const char *symbol, - const char *version) +int crypt_token_set_external_path(const char *path) { - char *error; - void *sym; + int r; + char tokens_path[TOKENS_PATH_MAX]; -#ifdef HAVE_DLVSYM - log_dbg(cd, "Loading symbol %s@%s.", symbol, version); - sym = dlvsym(handle, symbol, version); -#else - log_dbg(cd, "Loading default version of symbol %s.", symbol); - sym = dlsym(handle, symbol); -#endif - error = dlerror(); + if (!path) + path = EXTERNAL_LUKS2_TOKENS_PATH; + else if (*path != '/') + return -EINVAL; - if (error) - log_dbg(cd, "%s", error); + r = snprintf(tokens_path, sizeof(tokens_path), "%s", path); + if (r < 0 || (size_t)r >= sizeof(tokens_path)) + return -EINVAL; - return sym; + (void)strcpy(external_tokens_path, tokens_path); + + return 0; +} +#else +#pragma GCC diagnostic ignored "-Wunused-parameter" +int crypt_token_set_external_path(const char *path) +{ + return -ENOTSUP; } #endif @@ -98,6 +106,29 @@ static bool token_validate_v1(struct crypt_device *cd, const crypt_token_handler } #if USE_EXTERNAL_TOKENS +static void *token_dlvsym(struct crypt_device *cd, + void *handle, + const char *symbol, + const char *version) +{ + char *error; + void *sym; + +#ifdef HAVE_DLVSYM + log_dbg(cd, "Loading symbol %s@%s.", symbol, version); + sym = dlvsym(handle, symbol, version); +#else + log_dbg(cd, "Loading default version of symbol %s.", symbol); + sym = dlsym(handle, symbol); +#endif + error = dlerror(); + + if (error) + log_dbg(cd, "%s", error); + + return sym; +} + static bool token_validate_v2(struct crypt_device *cd, const struct crypt_token_handler_internal *h) { if (!h) @@ -127,12 +158,10 @@ static bool external_token_name_valid(const char *name) return true; } -#endif static int crypt_token_load_external(struct crypt_device *cd, const char *name, struct crypt_token_handler_internal *ret) { -#if USE_EXTERNAL_TOKENS struct crypt_token_handler_v2 *token; void *h; char buf[PATH_MAX]; @@ -192,11 +221,40 @@ crypt_token_load_external(struct crypt_device *cd, const char *name, struct cryp ret->version = 2; return 0; -#else +} + +void crypt_token_unload_external_all(struct crypt_device *cd) +{ + int i; + + for (i = LUKS2_TOKENS_MAX - 1; i >= 0; i--) { + if (token_handlers[i].version < 2) + continue; + + log_dbg(cd, "Unloading %s token handler.", token_handlers[i].u.v2.name); + + free(CONST_CAST(void *)token_handlers[i].u.v2.name); + + if (dlclose(CONST_CAST(void *)token_handlers[i].u.v2.dlhandle)) + log_dbg(cd, "%s", dlerror()); + } +} + +#else /* USE_EXTERNAL_TOKENS */ + +static int crypt_token_load_external(struct crypt_device *cd __attribute__((unused)), + const char *name __attribute__((unused)), + struct crypt_token_handler_internal *ret __attribute__((unused))) +{ return -ENOTSUP; -#endif } +void crypt_token_unload_external_all(struct crypt_device *cd __attribute__((unused))) +{ +} + +#endif + static int is_builtin_candidate(const char *type) { return !strncmp(type, LUKS2_BUILTIN_TOKEN_PREFIX, LUKS2_BUILTIN_TOKEN_PREFIX_LEN); @@ -243,25 +301,6 @@ int crypt_token_register(const crypt_token_handler *handler) return 0; } -void crypt_token_unload_external_all(struct crypt_device *cd) -{ -#if USE_EXTERNAL_TOKENS - int i; - - for (i = LUKS2_TOKENS_MAX - 1; i >= 0; i--) { - if (token_handlers[i].version < 2) - continue; - - log_dbg(cd, "Unloading %s token handler.", token_handlers[i].u.v2.name); - - free(CONST_CAST(void *)token_handlers[i].u.v2.name); - - if (dlclose(CONST_CAST(void *)token_handlers[i].u.v2.dlhandle)) - log_dbg(cd, "%s", dlerror()); - } -#endif -} - static const void *LUKS2_token_handler_type(struct crypt_device *cd, const char *type) { @@ -423,12 +462,12 @@ static const char *token_json_to_string(json_object *jobj_token) JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE); } -static int token_is_usable(struct luks2_hdr *hdr, json_object *jobj_token, int segment, +static int token_is_usable(struct luks2_hdr *hdr, json_object *jobj_token, int keyslot, int segment, crypt_keyslot_priority minimal_priority, bool requires_keyslot) { crypt_keyslot_priority keyslot_priority; json_object *jobj_array; - int i, keyslot, len, r = -ENOENT; + int i, slot, len, r = -ENOENT; if (!jobj_token) return -EINVAL; @@ -451,16 +490,19 @@ static int token_is_usable(struct luks2_hdr *hdr, json_object *jobj_token, int s return -ENOENT; for (i = 0; i < len; i++) { - keyslot = atoi(json_object_get_string(json_object_array_get_idx(jobj_array, i))); + slot = atoi(json_object_get_string(json_object_array_get_idx(jobj_array, i))); + + if (keyslot != CRYPT_ANY_SLOT && slot != keyslot) + continue; - keyslot_priority = LUKS2_keyslot_priority_get(hdr, keyslot); + keyslot_priority = LUKS2_keyslot_priority_get(hdr, slot); if (keyslot_priority == CRYPT_SLOT_PRIORITY_INVALID) return -EINVAL; if (keyslot_priority < minimal_priority) continue; - r = LUKS2_keyslot_for_segment(hdr, keyslot, segment); + r = LUKS2_keyslot_for_segment(hdr, slot, segment); if (r != -ENOENT) return r; } @@ -480,6 +522,7 @@ static int translate_errno(struct crypt_device *cd, int ret_val, const char *typ static int token_open(struct crypt_device *cd, struct luks2_hdr *hdr, + int keyslot, int token, json_object *jobj_token, const char *type, @@ -507,7 +550,7 @@ static int token_open(struct crypt_device *cd, return -ENOENT; } - r = token_is_usable(hdr, jobj_token, segment, priority, requires_keyslot); + r = token_is_usable(hdr, jobj_token, keyslot, segment, priority, requires_keyslot); if (r < 0) { if (r == -ENOENT) log_dbg(cd, "Token %d unusable for segment %d with desired keyslot priority %d.", @@ -569,32 +612,22 @@ static void update_return_errno(int r, int *stored) *stored = r; } -static int LUKS2_keyslot_open_by_token(struct crypt_device *cd, +static int try_token_keyslot_unlock(struct crypt_device *cd, struct luks2_hdr *hdr, + const char *type, + json_object *jobj_token_keyslots, int token, int segment, crypt_keyslot_priority priority, const char *buffer, size_t buffer_len, - struct volume_key **vk) + struct volume_key **r_vk) { + json_object *jobj; crypt_keyslot_priority keyslot_priority; - json_object *jobj_token, *jobj_token_keyslots, *jobj_type, *jobj; - unsigned int num = 0; int i, r = -ENOENT, stored_retval = -ENOENT; + unsigned int num = 0; - jobj_token = LUKS2_get_token_jobj(hdr, token); - if (!jobj_token) - return -EINVAL; - - if (!json_object_object_get_ex(jobj_token, "type", &jobj_type)) - return -EINVAL; - - json_object_object_get_ex(jobj_token, "keyslots", &jobj_token_keyslots); - if (!jobj_token_keyslots) - return -EINVAL; - - /* Try to open keyslot referenced in token */ for (i = 0; i < (int) json_object_array_length(jobj_token_keyslots) && r < 0; i++) { jobj = json_object_array_get_idx(jobj_token_keyslots, i); num = atoi(json_object_get_string(jobj)); @@ -604,8 +637,8 @@ static int LUKS2_keyslot_open_by_token(struct crypt_device *cd, if (keyslot_priority < priority) continue; log_dbg(cd, "Trying to open keyslot %u with token %d (type %s).", - num, token, json_object_get_string(jobj_type)); - r = LUKS2_keyslot_open(cd, num, segment, buffer, buffer_len, vk); + num, token, type); + r = LUKS2_keyslot_open(cd, num, segment, buffer, buffer_len, r_vk); /* short circuit on fatal error */ if (r < 0 && r != -EPERM && r != -ENOENT) return r; @@ -620,6 +653,53 @@ static int LUKS2_keyslot_open_by_token(struct crypt_device *cd, return num; } +static int LUKS2_keyslot_open_by_token(struct crypt_device *cd, + struct luks2_hdr *hdr, + int keyslot, + int token, + int segment, + crypt_keyslot_priority min_priority, + const char *buffer, + size_t buffer_len, + struct volume_key **vk) +{ + json_object *jobj_token, *jobj_token_keyslots, *jobj_type; + crypt_keyslot_priority priority = CRYPT_SLOT_PRIORITY_PREFER; + int r = -ENOENT, stored_retval = -ENOENT; + + jobj_token = LUKS2_get_token_jobj(hdr, token); + if (!jobj_token) + return -EINVAL; + + if (!json_object_object_get_ex(jobj_token, "type", &jobj_type)) + return -EINVAL; + + json_object_object_get_ex(jobj_token, "keyslots", &jobj_token_keyslots); + if (!jobj_token_keyslots) + return -EINVAL; + + /* with specific keyslot just ignore priorities and unlock */ + if (keyslot != CRYPT_ANY_SLOT) { + log_dbg(cd, "Trying to open keyslot %u with token %d (type %s).", + keyslot, token, json_object_get_string(jobj_type)); + return LUKS2_keyslot_open(cd, keyslot, segment, buffer, buffer_len, vk); + } + + /* Try to open keyslot referenced in token */ + while (priority >= min_priority) { + r = try_token_keyslot_unlock(cd, hdr, json_object_get_string(jobj_type), + jobj_token_keyslots, token, segment, + priority, buffer, buffer_len, vk); + if (r == -EINVAL || r >= 0) + return r; + if (r == -EPERM) + stored_retval = r; + priority--; + } + + return stored_retval; +} + static bool token_is_blocked(int token, uint32_t *block_list) { /* it is safe now, but have assert in case LUKS2_TOKENS_MAX grows */ @@ -640,6 +720,7 @@ static int token_open_priority(struct crypt_device *cd, struct luks2_hdr *hdr, json_object *jobj_tokens, const char *type, + int keyslot, int segment, crypt_keyslot_priority priority, const char *pin, @@ -660,9 +741,10 @@ static int token_open_priority(struct crypt_device *cd, token = atoi(slot); if (token_is_blocked(token, block_list)) continue; - r = token_open(cd, hdr, token, val, type, segment, priority, pin, pin_size, &buffer, &buffer_size, usrptr, true); + r = token_open(cd, hdr, keyslot, token, val, type, segment, priority, pin, pin_size, + &buffer, &buffer_size, usrptr, true); if (!r) { - r = LUKS2_keyslot_open_by_token(cd, hdr, token, segment, priority, + r = LUKS2_keyslot_open_by_token(cd, hdr, keyslot, token, segment, priority, buffer, buffer_size, vk); LUKS2_token_buffer_free(cd, token, buffer, buffer_size); } @@ -679,8 +761,9 @@ static int token_open_priority(struct crypt_device *cd, return *stored_retval; } -static int token_open_any(struct crypt_device *cd, struct luks2_hdr *hdr, const char *type, int segment, - const char *pin, size_t pin_size, void *usrptr, struct volume_key **vk) +static int token_open_any(struct crypt_device *cd, struct luks2_hdr *hdr, const char *type, + int keyslot, int segment, const char *pin, size_t pin_size, void *usrptr, + struct volume_key **vk) { json_object *jobj_tokens; int r, retval = -ENOENT; @@ -692,17 +775,22 @@ static int token_open_any(struct crypt_device *cd, struct luks2_hdr *hdr, const if (!type) usrptr = NULL; - r = token_open_priority(cd, hdr, jobj_tokens, type, segment, CRYPT_SLOT_PRIORITY_PREFER, + if (keyslot != CRYPT_ANY_SLOT) + return token_open_priority(cd, hdr, jobj_tokens, type, keyslot, segment, CRYPT_SLOT_PRIORITY_IGNORE, + pin, pin_size, usrptr, &retval, &blocked, vk); + + r = token_open_priority(cd, hdr, jobj_tokens, type, keyslot, segment, CRYPT_SLOT_PRIORITY_PREFER, pin, pin_size, usrptr, &retval, &blocked, vk); if (break_loop_retval(r)) return r; - return token_open_priority(cd, hdr, jobj_tokens, type, segment, CRYPT_SLOT_PRIORITY_NORMAL, + return token_open_priority(cd, hdr, jobj_tokens, type, keyslot, segment, CRYPT_SLOT_PRIORITY_NORMAL, pin, pin_size, usrptr, &retval, &blocked, vk); } int LUKS2_token_unlock_key(struct crypt_device *cd, struct luks2_hdr *hdr, + int keyslot, int token, const char *type, const char *pin, @@ -714,6 +802,7 @@ int LUKS2_token_unlock_key(struct crypt_device *cd, char *buffer; size_t buffer_size; json_object *jobj_token; + crypt_keyslot_priority min_priority; int r = -ENOENT; assert(vk); @@ -724,13 +813,27 @@ int LUKS2_token_unlock_key(struct crypt_device *cd, if (segment < 0 && segment != CRYPT_ANY_SEGMENT) return -EINVAL; + if (keyslot != CRYPT_ANY_SLOT || token != CRYPT_ANY_TOKEN) + min_priority = CRYPT_SLOT_PRIORITY_IGNORE; + else + min_priority = CRYPT_SLOT_PRIORITY_NORMAL; + + if (keyslot != CRYPT_ANY_SLOT) { + r = LUKS2_keyslot_for_segment(hdr, keyslot, segment); + if (r < 0) { + if (r == -ENOENT) + log_dbg(cd, "Keyslot %d unusable for segment %d.", keyslot, segment); + return r; + } + } + if (token >= 0 && token < LUKS2_TOKENS_MAX) { if ((jobj_token = LUKS2_get_token_jobj(hdr, token))) { - r = token_open(cd, hdr, token, jobj_token, type, segment, CRYPT_SLOT_PRIORITY_IGNORE, + r = token_open(cd, hdr, keyslot, token, jobj_token, type, segment, min_priority, pin, pin_size, &buffer, &buffer_size, usrptr, true); if (!r) { - r = LUKS2_keyslot_open_by_token(cd, hdr, token, segment, CRYPT_SLOT_PRIORITY_IGNORE, - buffer, buffer_size, vk); + r = LUKS2_keyslot_open_by_token(cd, hdr, keyslot, token, segment, + min_priority, buffer, buffer_size, vk); LUKS2_token_buffer_free(cd, token, buffer, buffer_size); } } @@ -745,7 +848,7 @@ int LUKS2_token_unlock_key(struct crypt_device *cd, * success (>= 0) or any other negative errno short-circuits token activation loop * immediately */ - r = token_open_any(cd, hdr, type, segment, pin, pin_size, usrptr, vk); + r = token_open_any(cd, hdr, type, keyslot, segment, pin, pin_size, usrptr, vk); else r = -EINVAL; @@ -754,6 +857,7 @@ int LUKS2_token_unlock_key(struct crypt_device *cd, int LUKS2_token_open_and_activate(struct crypt_device *cd, struct luks2_hdr *hdr, + int keyslot, int token, const char *name, const char *type, @@ -763,15 +867,15 @@ int LUKS2_token_open_and_activate(struct crypt_device *cd, void *usrptr) { bool use_keyring; - int keyslot, r, segment; - struct volume_key *vk = NULL; + int r, segment; + struct volume_key *p_crypt, *p_opal, *crypt_key = NULL, *opal_key = NULL, *vk = NULL; if (flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) segment = CRYPT_ANY_SEGMENT; else segment = CRYPT_DEFAULT_SEGMENT; - r = LUKS2_token_unlock_key(cd, hdr, token, type, pin, pin_size, segment, usrptr, &vk); + r = LUKS2_token_unlock_key(cd, hdr, keyslot, token, type, pin, pin_size, segment, usrptr, &vk); if (r < 0) return r; @@ -779,23 +883,39 @@ int LUKS2_token_open_and_activate(struct crypt_device *cd, keyslot = r; - if (!crypt_use_keyring_for_vk(cd)) + if (LUKS2_segment_is_hw_opal(hdr, CRYPT_DEFAULT_SEGMENT)) { + r = LUKS2_split_crypt_and_opal_keys(cd, hdr, vk, &crypt_key, &opal_key); + if (r < 0) { + crypt_free_volume_key(vk); + return r; + } + + p_crypt = crypt_key; + p_opal = opal_key ?: vk; + } else { + p_crypt = vk; + p_opal = NULL; + } + + if (!crypt_use_keyring_for_vk(cd) || !p_crypt) use_keyring = false; else use_keyring = ((name && !crypt_is_cipher_null(crypt_get_cipher(cd))) || (flags & CRYPT_ACTIVATE_KEYRING_KEY)); if (use_keyring) { - if (!(r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, hdr, vk, keyslot))) + if (!(r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, hdr, p_crypt, keyslot))) flags |= CRYPT_ACTIVATE_KEYRING_KEY; } if (r >= 0 && name) - r = LUKS2_activate(cd, name, vk, flags); + r = LUKS2_activate(cd, name, p_crypt, p_opal, flags); if (r < 0) - crypt_drop_keyring_key(cd, vk); + crypt_drop_keyring_key(cd, p_crypt); crypt_free_volume_key(vk); + crypt_free_volume_key(crypt_key); + crypt_free_volume_key(opal_key); return r < 0 ? r : keyslot; } @@ -995,8 +1115,9 @@ int LUKS2_token_unlock_passphrase(struct crypt_device *cd, if (token >= 0 && token < LUKS2_TOKENS_MAX) { if ((jobj_token = LUKS2_get_token_jobj(hdr, token))) - r = token_open(cd, hdr, token, jobj_token, type, CRYPT_ANY_SEGMENT, CRYPT_SLOT_PRIORITY_IGNORE, - pin, pin_size, &buffer, &buffer_size, usrptr, false); + r = token_open(cd, hdr, CRYPT_ANY_SLOT, token, jobj_token, type, + CRYPT_ANY_SEGMENT, CRYPT_SLOT_PRIORITY_IGNORE, pin, pin_size, + &buffer, &buffer_size, usrptr, false); } else if (token == CRYPT_ANY_TOKEN) { json_object_object_get_ex(hdr->jobj, "tokens", &jobj_tokens); @@ -1005,7 +1126,7 @@ int LUKS2_token_unlock_passphrase(struct crypt_device *cd, json_object_object_foreach(jobj_tokens, slot, val) { token = atoi(slot); - r = token_open(cd, hdr, token, val, type, CRYPT_ANY_SEGMENT, CRYPT_SLOT_PRIORITY_IGNORE, + r = token_open(cd, hdr, CRYPT_ANY_SLOT, token, val, type, CRYPT_ANY_SEGMENT, CRYPT_SLOT_PRIORITY_IGNORE, pin, pin_size, &buffer, &buffer_size, usrptr, false); /* |