blob: 1ed9e2bb86bed6fb3b40fd19c5486c9c9d6fa9e7 [file] [log] [blame]
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <p11-kit/p11-kit.h>
#include <p11-kit/uri.h>
#include "cryptsetup-token-util.h"
#include "escape.h"
#include "hexdecoct.h"
#include "json.h"
#include "luks2-pkcs11.h"
#include "memory-util.h"
#include "pkcs11-util.h"
#include "time-util.h"
struct luks2_pkcs11_callback_data {
struct crypt_device *cd;
const char *pin;
size_t pin_size;
void *encrypted_key;
size_t encrypted_key_size;
void *decrypted_key;
size_t decrypted_key_size;
};
static int luks2_pkcs11_callback(
CK_FUNCTION_LIST *m,
CK_SESSION_HANDLE session,
CK_SLOT_ID slot_id,
const CK_SLOT_INFO *slot_info,
const CK_TOKEN_INFO *token_info,
P11KitUri *uri,
void *userdata) {
CK_OBJECT_HANDLE object;
CK_RV rv;
CK_TOKEN_INFO updated_token_info;
int r;
_cleanup_free_ char *token_label = NULL;
struct luks2_pkcs11_callback_data *data = ASSERT_PTR(userdata);
assert(m);
assert(slot_info);
assert(token_info);
assert(uri);
token_label = pkcs11_token_label(token_info);
if (!token_label)
return -ENOMEM;
/* Called for every token matching our URI */
r = pkcs11_token_login_by_pin(m, session, token_info, token_label, data->pin, data->pin_size);
if (r == -ENOLCK) {
/* Referesh the token info, so that we can prompt knowing the new flags if they changed. */
rv = m->C_GetTokenInfo(slot_id, &updated_token_info);
if (rv != CKR_OK) {
crypt_log_error(data->cd,
"Failed to acquire updated security token information for slot %lu: %s",
slot_id, p11_kit_strerror(rv));
return -EIO;
}
token_info = &updated_token_info;
r = -ENOANO;
}
if (r == -ENOANO) {
if (FLAGS_SET(token_info->flags, CKF_USER_PIN_FINAL_TRY))
crypt_log_error(data->cd, "Please enter correct PIN for security token "
"'%s' in order to unlock it (final try).", token_label);
else if (FLAGS_SET(token_info->flags, CKF_USER_PIN_COUNT_LOW))
crypt_log_error(data->cd, "PIN has been entered incorrectly previously, "
"please enter correct PIN for security token '%s' in order to unlock it.",
token_label);
}
if (r == -EPERM) /* pin is locked, but map it to -ENOANO anyway */
r = -ENOANO;
if (r < 0)
return r;
r = pkcs11_token_find_private_key(m, session, uri, &object);
if (r < 0)
return r;
r = pkcs11_token_decrypt_data(
m,
session,
object,
data->encrypted_key,
data->encrypted_key_size,
&data->decrypted_key,
&data->decrypted_key_size);
if (r < 0)
return r;
return 0;
}
static void luks2_pkcs11_callback_data_release(struct luks2_pkcs11_callback_data *data) {
erase_and_free(data->decrypted_key);
}
static int acquire_luks2_key_by_pin(
struct crypt_device *cd,
const char *pkcs11_uri,
const void *pin,
size_t pin_size,
void *encrypted_key,
size_t encrypted_key_size,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size) {
int r;
_cleanup_(luks2_pkcs11_callback_data_release) struct luks2_pkcs11_callback_data data = {
.cd = cd,
.pin = pin,
.pin_size = pin_size,
.encrypted_key = encrypted_key,
.encrypted_key_size = encrypted_key_size,
};
assert(pkcs11_uri);
assert(encrypted_key);
assert(ret_decrypted_key);
assert(ret_decrypted_key_size);
r = pkcs11_find_token(pkcs11_uri, luks2_pkcs11_callback, &data);
if (r < 0)
return r;
*ret_decrypted_key = TAKE_PTR(data.decrypted_key);
*ret_decrypted_key_size = data.decrypted_key_size;
return 0;
}
/* called from within systemd utilities */
static int acquire_luks2_key_systemd(
const char *pkcs11_uri,
systemd_pkcs11_plugin_params *params,
void *encrypted_key,
size_t encrypted_key_size,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size) {
int r;
_cleanup_(pkcs11_crypt_device_callback_data_release) pkcs11_crypt_device_callback_data data = {
.encrypted_key = encrypted_key,
.encrypted_key_size = encrypted_key_size,
.free_encrypted_key = false
};
assert(pkcs11_uri);
assert(encrypted_key);
assert(ret_decrypted_key);
assert(ret_decrypted_key_size);
assert(params);
data.friendly_name = params->friendly_name;
data.headless = params->headless;
data.until = params->until;
/* The functions called here log about all errors, except for EAGAIN which means "token not found right now" */
r = pkcs11_find_token(pkcs11_uri, pkcs11_crypt_device_callback, &data);
if (r < 0)
return r;
*ret_decrypted_key = TAKE_PTR(data.decrypted_key);
*ret_decrypted_key_size = data.decrypted_key_size;
return 0;
}
int acquire_luks2_key(
struct crypt_device *cd,
const char *json,
void *userdata,
const void *pin,
size_t pin_size,
char **ret_password,
size_t *ret_password_size) {
int r;
size_t decrypted_key_size, encrypted_key_size;
_cleanup_(erase_and_freep) void *decrypted_key = NULL;
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
_cleanup_free_ char *pkcs11_uri = NULL;
_cleanup_free_ void *encrypted_key = NULL;
systemd_pkcs11_plugin_params *pkcs11_params = userdata;
ssize_t base64_encoded_size;
assert(json);
assert(ret_password);
assert(ret_password_size);
r = parse_luks2_pkcs11_data(cd, json, &pkcs11_uri, &encrypted_key, &encrypted_key_size);
if (r < 0)
return r;
if (pkcs11_params && pin)
crypt_log_verbose(cd, "PIN parameter ignored in interactive mode.");
if (pkcs11_params) /* systemd based activation with interactive pin query callbacks */
r = acquire_luks2_key_systemd(
pkcs11_uri,
pkcs11_params,
encrypted_key, encrypted_key_size,
&decrypted_key, &decrypted_key_size);
else /* default activation that provides single PIN if needed */
r = acquire_luks2_key_by_pin(
cd, pkcs11_uri, pin, pin_size,
encrypted_key, encrypted_key_size,
&decrypted_key, &decrypted_key_size);
if (r < 0)
return r;
base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
if (base64_encoded_size < 0)
return crypt_log_error_errno(cd, (int) base64_encoded_size, "Can not base64 encode key: %m");
*ret_password = TAKE_PTR(base64_encoded);
*ret_password_size = base64_encoded_size;
return 0;
}
int parse_luks2_pkcs11_data(
struct crypt_device *cd,
const char *json,
char **ret_uri,
void **ret_encrypted_key,
size_t *ret_encrypted_key_size) {
int r;
size_t key_size;
_cleanup_free_ char *uri = NULL;
_cleanup_free_ void *key = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
JsonVariant *w;
assert(json);
assert(ret_uri);
assert(ret_encrypted_key);
assert(ret_encrypted_key_size);
r = json_parse(json, 0, &v, NULL, NULL);
if (r < 0)
return r;
w = json_variant_by_key(v, "pkcs11-uri");
if (!w)
return -EINVAL;
uri = strdup(json_variant_string(w));
if (!uri)
return -ENOMEM;
w = json_variant_by_key(v, "pkcs11-key");
if (!w)
return -EINVAL;
r = unbase64mem(json_variant_string(w), SIZE_MAX, &key, &key_size);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Failed to decode base64 encoded key: %m.");
*ret_uri = TAKE_PTR(uri);
*ret_encrypted_key = TAKE_PTR(key);
*ret_encrypted_key_size = key_size;
return 0;
}