summaryrefslogtreecommitdiffstats
path: root/lib/luks2/luks2_token.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/luks2/luks2_token.c')
-rw-r--r--lib/luks2/luks2_token.c295
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);
/*