mirror of
https://github.com/Dasharo/systemd.git
synced 2026-03-06 15:02:31 -08:00
homed: add support for authenticating with fido2 hmac-secret tokens
This commit is contained in:
@@ -2157,7 +2157,8 @@ if conf.get('ENABLE_HOMED') == 1
|
||||
libcrypt,
|
||||
libopenssl,
|
||||
libfdisk,
|
||||
libp11kit],
|
||||
libp11kit,
|
||||
libfido2],
|
||||
install_rpath : rootlibexecdir,
|
||||
install : true,
|
||||
install_dir : rootlibexecdir)
|
||||
|
||||
@@ -331,8 +331,18 @@ static int handle_generic_user_record_error(
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set PKCS#11 protected authentication path permitted flag: %m");
|
||||
|
||||
} else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED)) {
|
||||
|
||||
log_notice("%s%sAuthentication requires presence verification on security token.",
|
||||
emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
|
||||
emoji_enabled() ? " " : "");
|
||||
|
||||
r = user_record_set_fido2_user_presence_permitted(hr, true);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set FIDO2 user presence permitted flag: %m");
|
||||
|
||||
} else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_LOCKED))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Security token PIN is locked, please unlock security token PIN first.");
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)");
|
||||
|
||||
else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) {
|
||||
|
||||
|
||||
@@ -457,6 +457,10 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
|
||||
return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PIN_NEEDED, "PIN for security token required.");
|
||||
case -ERFKILL:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED, "Security token requires protected authentication path.");
|
||||
case -EMEDIUMTYPE:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED, "Security token requires user presence.");
|
||||
case -ENOSTR:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_TOKEN_ACTION_TIMEOUT, "Token action timeout. (User was supposed to verify presence or similar, by interacting with the token, and didn't do that in time.)");
|
||||
case -EOWNERDEAD:
|
||||
return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PIN_LOCKED, "PIN of security token locked.");
|
||||
case -ENOLCK:
|
||||
@@ -1357,7 +1361,13 @@ static int user_record_extend_with_binding(UserRecord *hr, UserRecord *with_bind
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int home_update_internal(Home *h, const char *verb, UserRecord *hr, UserRecord *secret, sd_bus_error *error) {
|
||||
static int home_update_internal(
|
||||
Home *h,
|
||||
const char *verb,
|
||||
UserRecord *hr,
|
||||
UserRecord *secret,
|
||||
sd_bus_error *error) {
|
||||
|
||||
_cleanup_(user_record_unrefp) UserRecord *new_hr = NULL, *saved_secret = NULL, *signed_hr = NULL;
|
||||
int r, c;
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ int home_prepare_cifs(
|
||||
|
||||
int home_activate_cifs(
|
||||
UserRecord *h,
|
||||
char ***pkcs11_decrypted_passwords,
|
||||
PasswordCache *cache,
|
||||
UserRecord **ret_home) {
|
||||
|
||||
_cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
|
||||
@@ -120,7 +120,7 @@ int home_activate_cifs(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = home_refresh(h, &setup, NULL, pkcs11_decrypted_passwords, NULL, &new_home);
|
||||
r = home_refresh(h, &setup, NULL, cache, NULL, &new_home);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
|
||||
int home_prepare_cifs(UserRecord *h, bool already_activated, HomeSetup *setup);
|
||||
|
||||
int home_activate_cifs(UserRecord *h, char ***pkcs11_decrypted_passwords, UserRecord **ret_home);
|
||||
int home_activate_cifs(UserRecord *h, PasswordCache *cache, UserRecord **ret_home);
|
||||
|
||||
int home_create_cifs(UserRecord *h, UserRecord **ret_home);
|
||||
|
||||
@@ -26,7 +26,7 @@ int home_prepare_directory(UserRecord *h, bool already_activated, HomeSetup *set
|
||||
|
||||
int home_activate_directory(
|
||||
UserRecord *h,
|
||||
char ***pkcs11_decrypted_passwords,
|
||||
PasswordCache *cache,
|
||||
UserRecord **ret_home) {
|
||||
|
||||
_cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *header_home = NULL;
|
||||
@@ -44,11 +44,11 @@ int home_activate_directory(
|
||||
assert_se(hdo = user_record_home_directory(h));
|
||||
hd = strdupa(hdo);
|
||||
|
||||
r = home_prepare(h, false, pkcs11_decrypted_passwords, &setup, &header_home);
|
||||
r = home_prepare(h, false, cache, &setup, &header_home);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = home_refresh(h, &setup, header_home, pkcs11_decrypted_passwords, NULL, &new_home);
|
||||
r = home_refresh(h, &setup, header_home, cache, NULL, &new_home);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -193,7 +193,7 @@ int home_create_directory_or_subvolume(UserRecord *h, UserRecord **ret_home) {
|
||||
int home_resize_directory(
|
||||
UserRecord *h,
|
||||
bool already_activated,
|
||||
char ***pkcs11_decrypted_passwords,
|
||||
PasswordCache *cache,
|
||||
HomeSetup *setup,
|
||||
UserRecord **ret_home) {
|
||||
|
||||
@@ -205,11 +205,11 @@ int home_resize_directory(
|
||||
assert(ret_home);
|
||||
assert(IN_SET(user_record_storage(h), USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT));
|
||||
|
||||
r = home_prepare(h, already_activated, pkcs11_decrypted_passwords, setup, NULL);
|
||||
r = home_prepare(h, already_activated, cache, setup, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = home_load_embedded_identity(h, setup->root_fd, NULL, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, pkcs11_decrypted_passwords, &embedded_home, &new_home);
|
||||
r = home_load_embedded_identity(h, setup->root_fd, NULL, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, cache, &embedded_home, &new_home);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -5,6 +5,6 @@
|
||||
#include "user-record.h"
|
||||
|
||||
int home_prepare_directory(UserRecord *h, bool already_activated, HomeSetup *setup);
|
||||
int home_activate_directory(UserRecord *h, char ***pkcs11_decrypted_passwords, UserRecord **ret_home);
|
||||
int home_activate_directory(UserRecord *h, PasswordCache *cache, UserRecord **ret_home);
|
||||
int home_create_directory_or_subvolume(UserRecord *h, UserRecord **ret_home);
|
||||
int home_resize_directory(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_home);
|
||||
int home_resize_directory(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_home);
|
||||
|
||||
197
src/home/homework-fido2.c
Normal file
197
src/home/homework-fido2.c
Normal file
@@ -0,0 +1,197 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <fido.h>
|
||||
|
||||
#include "hexdecoct.h"
|
||||
#include "homework-fido2.h"
|
||||
#include "strv.h"
|
||||
|
||||
static int fido2_use_specific_token(
|
||||
const char *path,
|
||||
UserRecord *h,
|
||||
UserRecord *secret,
|
||||
const Fido2HmacSalt *salt,
|
||||
char **ret) {
|
||||
|
||||
_cleanup_(fido_cbor_info_free) fido_cbor_info_t *di = NULL;
|
||||
_cleanup_(fido_assert_free) fido_assert_t *a = NULL;
|
||||
_cleanup_(fido_dev_free) fido_dev_t *d = NULL;
|
||||
bool found_extension = false;
|
||||
size_t n, hmac_size;
|
||||
const void *hmac;
|
||||
char **e;
|
||||
int r;
|
||||
|
||||
d = fido_dev_new();
|
||||
if (!d)
|
||||
return log_oom();
|
||||
|
||||
r = fido_dev_open(d, path);
|
||||
if (r != FIDO_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Failed to open FIDO2 device %s: %s", path, fido_strerr(r));
|
||||
|
||||
if (!fido_dev_is_fido2(d))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
|
||||
"Specified device %s is not a FIDO2 device.", path);
|
||||
|
||||
di = fido_cbor_info_new();
|
||||
if (!di)
|
||||
return log_oom();
|
||||
|
||||
r = fido_dev_get_cbor_info(d, di);
|
||||
if (r != FIDO_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Failed to get CBOR device info for %s: %s", path, fido_strerr(r));
|
||||
|
||||
e = fido_cbor_info_extensions_ptr(di);
|
||||
n = fido_cbor_info_extensions_len(di);
|
||||
|
||||
for (size_t i = 0; i < n; i++)
|
||||
if (streq(e[i], "hmac-secret")) {
|
||||
found_extension = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found_extension)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
|
||||
"Specified device %s is a FIDO2 device, but does not support the required HMAC-SECRET extension.", path);
|
||||
|
||||
a = fido_assert_new();
|
||||
if (!a)
|
||||
return log_oom();
|
||||
|
||||
r = fido_assert_set_extensions(a, FIDO_EXT_HMAC_SECRET);
|
||||
if (r != FIDO_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", fido_strerr(r));
|
||||
|
||||
r = fido_assert_set_hmac_salt(a, salt->salt, salt->salt_size);
|
||||
if (r != FIDO_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Failed to set salt on FIDO2 assertion: %s", fido_strerr(r));
|
||||
|
||||
r = fido_assert_set_rp(a, "io.systemd.home");
|
||||
if (r != FIDO_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Failed to set FIDO2 assertion ID: %s", fido_strerr(r));
|
||||
|
||||
r = fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32);
|
||||
if (r != FIDO_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Failed to set FIDO2 assertion client data hash: %s", fido_strerr(r));
|
||||
|
||||
r = fido_assert_allow_cred(a, salt->credential.id, salt->credential.size);
|
||||
if (r != FIDO_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Failed to add FIDO2 assertion credential ID: %s", fido_strerr(r));
|
||||
|
||||
r = fido_assert_set_up(a, h->fido2_user_presence_permitted <= 0 ? FIDO_OPT_FALSE : FIDO_OPT_TRUE);
|
||||
if (r != FIDO_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Failed to set FIDO2 assertion user presence: %s", fido_strerr(r));
|
||||
|
||||
log_info("Asking FIDO2 token for authentication.");
|
||||
|
||||
r = fido_dev_get_assert(d, a, NULL); /* try without pin first */
|
||||
if (r == FIDO_ERR_PIN_REQUIRED) {
|
||||
char **i;
|
||||
|
||||
/* OK, we needed a pin, try with all pins in turn */
|
||||
STRV_FOREACH(i, secret->token_pin) {
|
||||
r = fido_dev_get_assert(d, a, *i);
|
||||
if (r != FIDO_ERR_PIN_INVALID)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (r) {
|
||||
case FIDO_OK:
|
||||
break;
|
||||
case FIDO_ERR_NO_CREDENTIALS:
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADSLT),
|
||||
"Wrong security token; needed credentials not present on token.");
|
||||
case FIDO_ERR_PIN_REQUIRED:
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOANO),
|
||||
"Security token requires PIN.");
|
||||
case FIDO_ERR_PIN_AUTH_BLOCKED:
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
|
||||
"PIN of security token is blocked, please remove/reinsert token.");
|
||||
case FIDO_ERR_PIN_INVALID:
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOLCK),
|
||||
"PIN of security token incorrect.");
|
||||
case FIDO_ERR_UP_REQUIRED:
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE),
|
||||
"User presence required.");
|
||||
case FIDO_ERR_ACTION_TIMEOUT:
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
|
||||
"Token action timeout. (User didn't interact with token quickly enough.)");
|
||||
default:
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Failed to ask token for assertion: %s", fido_strerr(r));
|
||||
}
|
||||
|
||||
hmac = fido_assert_hmac_secret_ptr(a, 0);
|
||||
if (!hmac)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve HMAC secret.");
|
||||
|
||||
hmac_size = fido_assert_hmac_secret_len(a, 0);
|
||||
|
||||
r = base64mem(hmac, hmac_size, ret);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to base64 encode HMAC secret: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fido2_use_token(UserRecord *h, UserRecord *secret, const Fido2HmacSalt *salt, char **ret) {
|
||||
size_t allocated = 64, found = 0;
|
||||
fido_dev_info_t *di = NULL;
|
||||
int r;
|
||||
|
||||
di = fido_dev_info_new(allocated);
|
||||
if (!di)
|
||||
return log_oom();
|
||||
|
||||
r = fido_dev_info_manifest(di, allocated, &found);
|
||||
if (r == FIDO_ERR_INTERNAL) {
|
||||
/* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
|
||||
r = log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "Got FIDO_ERR_INTERNAL, assuming no devices.");
|
||||
goto finish;
|
||||
}
|
||||
if (r != FIDO_OK) {
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", fido_strerr(r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < found; i++) {
|
||||
const fido_dev_info_t *entry;
|
||||
const char *path;
|
||||
|
||||
entry = fido_dev_info_ptr(di, i);
|
||||
if (!entry) {
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Failed to get device information for FIDO device %zu.", i);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
path = fido_dev_info_path(entry);
|
||||
if (!path) {
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Failed to query FIDO device path.");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = fido2_use_specific_token(path, h, secret, salt, ret);
|
||||
if (!IN_SET(r,
|
||||
-EBADSLT, /* device doesn't understand our credential hash */
|
||||
-ENODEV /* device is not a FIDO2 device with HMAC-SECRET */))
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = -EAGAIN;
|
||||
|
||||
finish:
|
||||
fido_dev_info_free(&di, allocated);
|
||||
return r;
|
||||
}
|
||||
6
src/home/homework-fido2.h
Normal file
6
src/home/homework-fido2.h
Normal file
@@ -0,0 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include "user-record.h"
|
||||
|
||||
int fido2_use_token(UserRecord *h, UserRecord *secret, const Fido2HmacSalt *salt, char **ret);
|
||||
@@ -208,7 +208,7 @@ static int fscrypt_slot_try_many(
|
||||
}
|
||||
|
||||
static int fscrypt_setup(
|
||||
char **pkcs11_decrypted_passwords,
|
||||
const PasswordCache *cache,
|
||||
char **password,
|
||||
HomeSetup *setup,
|
||||
void **ret_volume_key,
|
||||
@@ -230,6 +230,7 @@ static int fscrypt_setup(
|
||||
_cleanup_free_ char *value = NULL;
|
||||
size_t salt_size, encrypted_size;
|
||||
const char *nr, *e;
|
||||
char **list;
|
||||
int n;
|
||||
|
||||
/* Check if this xattr has the format 'trusted.fscrypt_slot<nr>' where '<nr>' is a 32bit unsigned integer */
|
||||
@@ -256,19 +257,17 @@ static int fscrypt_setup(
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to decode encrypted key of %s: %m", xa);
|
||||
|
||||
r = fscrypt_slot_try_many(
|
||||
pkcs11_decrypted_passwords,
|
||||
salt, salt_size,
|
||||
encrypted, encrypted_size,
|
||||
setup->fscrypt_key_descriptor,
|
||||
ret_volume_key, ret_volume_key_size);
|
||||
if (r == -ENOANO)
|
||||
r = -ENOANO;
|
||||
FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, password) {
|
||||
r = fscrypt_slot_try_many(
|
||||
password,
|
||||
list,
|
||||
salt, salt_size,
|
||||
encrypted, encrypted_size,
|
||||
setup->fscrypt_key_descriptor,
|
||||
ret_volume_key, ret_volume_key_size);
|
||||
if (r != -ENOANO)
|
||||
break;
|
||||
}
|
||||
if (r < 0) {
|
||||
if (r != -ENOANO)
|
||||
return r;
|
||||
@@ -282,7 +281,7 @@ static int fscrypt_setup(
|
||||
int home_prepare_fscrypt(
|
||||
UserRecord *h,
|
||||
bool already_activated,
|
||||
char ***pkcs11_decrypted_passwords,
|
||||
PasswordCache *cache,
|
||||
HomeSetup *setup) {
|
||||
|
||||
_cleanup_(erase_and_freep) void *volume_key = NULL;
|
||||
@@ -314,7 +313,7 @@ int home_prepare_fscrypt(
|
||||
memcpy(setup->fscrypt_key_descriptor, policy.master_key_descriptor, FS_KEY_DESCRIPTOR_SIZE);
|
||||
|
||||
r = fscrypt_setup(
|
||||
pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
|
||||
cache,
|
||||
h->password,
|
||||
setup,
|
||||
&volume_key,
|
||||
@@ -584,7 +583,7 @@ int home_create_fscrypt(
|
||||
int home_passwd_fscrypt(
|
||||
UserRecord *h,
|
||||
HomeSetup *setup,
|
||||
char **pkcs11_decrypted_passwords, /* the passwords acquired via PKCS#11 security tokens */
|
||||
PasswordCache *cache, /* the passwords acquired via PKCS#11/FIDO2 security tokens */
|
||||
char **effective_passwords /* new passwords */) {
|
||||
|
||||
_cleanup_(erase_and_freep) void *volume_key = NULL;
|
||||
@@ -600,7 +599,7 @@ int home_passwd_fscrypt(
|
||||
assert(setup);
|
||||
|
||||
r = fscrypt_setup(
|
||||
pkcs11_decrypted_passwords,
|
||||
cache,
|
||||
h->password,
|
||||
setup,
|
||||
&volume_key,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "homework.h"
|
||||
#include "user-record.h"
|
||||
|
||||
int home_prepare_fscrypt(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup);
|
||||
int home_prepare_fscrypt(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup);
|
||||
int home_create_fscrypt(UserRecord *h, char **effective_passwords, UserRecord **ret_home);
|
||||
|
||||
int home_passwd_fscrypt(UserRecord *h, HomeSetup *setup, char **pkcs11_decrypted_passwords, char **effective_passwords);
|
||||
int home_passwd_fscrypt(UserRecord *h, HomeSetup *setup, PasswordCache *cache, char **effective_passwords);
|
||||
|
||||
@@ -216,7 +216,7 @@ static int luks_setup(
|
||||
const char *cipher_mode,
|
||||
uint64_t volume_key_size,
|
||||
char **passwords,
|
||||
char **pkcs11_decrypted_passwords,
|
||||
const PasswordCache *cache,
|
||||
bool discard,
|
||||
struct crypt_device **ret,
|
||||
sd_id128_t *ret_found_uuid,
|
||||
@@ -227,6 +227,7 @@ static int luks_setup(
|
||||
_cleanup_(erase_and_freep) void *vk = NULL;
|
||||
sd_id128_t p;
|
||||
size_t vks;
|
||||
char **list;
|
||||
int r;
|
||||
|
||||
assert(node);
|
||||
@@ -278,12 +279,14 @@ static int luks_setup(
|
||||
if (!vk)
|
||||
return log_oom();
|
||||
|
||||
r = luks_try_passwords(cd, pkcs11_decrypted_passwords, vk, &vks);
|
||||
if (r == -ENOKEY) {
|
||||
r = luks_try_passwords(cd, passwords, vk, &vks);
|
||||
if (r == -ENOKEY)
|
||||
return log_error_errno(r, "No valid password for LUKS superblock.");
|
||||
r = -ENOKEY;
|
||||
FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, passwords) {
|
||||
r = luks_try_passwords(cd, list, vk, &vks);
|
||||
if (r != -ENOKEY)
|
||||
break;
|
||||
}
|
||||
if (r == -ENOKEY)
|
||||
return log_error_errno(r, "No valid password for LUKS superblock.");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to unlocks LUKS superblock: %m");
|
||||
|
||||
@@ -312,7 +315,7 @@ static int luks_setup(
|
||||
static int luks_open(
|
||||
const char *dm_name,
|
||||
char **passwords,
|
||||
char **pkcs11_decrypted_passwords,
|
||||
PasswordCache *cache,
|
||||
struct crypt_device **ret,
|
||||
sd_id128_t *ret_found_uuid,
|
||||
void **ret_volume_key,
|
||||
@@ -321,6 +324,7 @@ static int luks_open(
|
||||
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
|
||||
_cleanup_(erase_and_freep) void *vk = NULL;
|
||||
sd_id128_t p;
|
||||
char **list;
|
||||
size_t vks;
|
||||
int r;
|
||||
|
||||
@@ -361,12 +365,14 @@ static int luks_open(
|
||||
if (!vk)
|
||||
return log_oom();
|
||||
|
||||
r = luks_try_passwords(cd, pkcs11_decrypted_passwords, vk, &vks);
|
||||
if (r == -ENOKEY) {
|
||||
r = luks_try_passwords(cd, passwords, vk, &vks);
|
||||
if (r == -ENOKEY)
|
||||
return log_error_errno(r, "No valid password for LUKS superblock.");
|
||||
r = -ENOKEY;
|
||||
FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, passwords) {
|
||||
r = luks_try_passwords(cd, list, vk, &vks);
|
||||
if (r != -ENOKEY)
|
||||
break;
|
||||
}
|
||||
if (r == -ENOKEY)
|
||||
return log_error_errno(r, "No valid password for LUKS superblock.");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to unlocks LUKS superblock: %m");
|
||||
|
||||
@@ -622,7 +628,7 @@ static int luks_validate_home_record(
|
||||
struct crypt_device *cd,
|
||||
UserRecord *h,
|
||||
const void *volume_key,
|
||||
char ***pkcs11_decrypted_passwords,
|
||||
PasswordCache *cache,
|
||||
UserRecord **ret_luks_home_record) {
|
||||
|
||||
int r, token;
|
||||
@@ -727,7 +733,7 @@ static int luks_validate_home_record(
|
||||
if (!user_record_compatible(h, lhr))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EREMCHG), "LUKS home record not compatible with host record, refusing.");
|
||||
|
||||
r = user_record_authenticate(lhr, h, pkcs11_decrypted_passwords, /* strict_verify= */ true);
|
||||
r = user_record_authenticate(lhr, h, cache, /* strict_verify= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
assert(r > 0); /* Insist that a password was verified */
|
||||
@@ -982,7 +988,7 @@ int home_prepare_luks(
|
||||
UserRecord *h,
|
||||
bool already_activated,
|
||||
const char *force_image_path,
|
||||
char ***pkcs11_decrypted_passwords,
|
||||
PasswordCache *cache,
|
||||
HomeSetup *setup,
|
||||
UserRecord **ret_luks_home) {
|
||||
|
||||
@@ -1010,7 +1016,7 @@ int home_prepare_luks(
|
||||
|
||||
r = luks_open(setup->dm_name,
|
||||
h->password,
|
||||
pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
|
||||
cache,
|
||||
&cd,
|
||||
&found_luks_uuid,
|
||||
&volume_key,
|
||||
@@ -1018,7 +1024,7 @@ int home_prepare_luks(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = luks_validate_home_record(cd, h, volume_key, pkcs11_decrypted_passwords, &luks_home);
|
||||
r = luks_validate_home_record(cd, h, volume_key, cache, &luks_home);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1133,7 +1139,7 @@ int home_prepare_luks(
|
||||
h->luks_cipher_mode,
|
||||
h->luks_volume_key_size,
|
||||
h->password,
|
||||
pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
|
||||
cache,
|
||||
user_record_luks_discard(h) || user_record_luks_offline_discard(h),
|
||||
&cd,
|
||||
&found_luks_uuid,
|
||||
@@ -1144,7 +1150,7 @@ int home_prepare_luks(
|
||||
|
||||
dm_activated = true;
|
||||
|
||||
r = luks_validate_home_record(cd, h, volume_key, pkcs11_decrypted_passwords, &luks_home);
|
||||
r = luks_validate_home_record(cd, h, volume_key, cache, &luks_home);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
@@ -1218,7 +1224,7 @@ static void print_size_summary(uint64_t host_size, uint64_t encrypted_size, stru
|
||||
|
||||
int home_activate_luks(
|
||||
UserRecord *h,
|
||||
char ***pkcs11_decrypted_passwords,
|
||||
PasswordCache *cache,
|
||||
UserRecord **ret_home) {
|
||||
|
||||
_cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *luks_home_record = NULL;
|
||||
@@ -1250,7 +1256,7 @@ int home_activate_luks(
|
||||
h,
|
||||
false,
|
||||
NULL,
|
||||
pkcs11_decrypted_passwords,
|
||||
cache,
|
||||
&setup,
|
||||
&luks_home_record);
|
||||
if (r < 0)
|
||||
@@ -1268,7 +1274,7 @@ int home_activate_luks(
|
||||
h,
|
||||
&setup,
|
||||
luks_home_record,
|
||||
pkcs11_decrypted_passwords,
|
||||
cache,
|
||||
&sfs,
|
||||
&new_home);
|
||||
if (r < 0)
|
||||
@@ -1464,7 +1470,7 @@ static int luks_format(
|
||||
const char *dm_name,
|
||||
sd_id128_t uuid,
|
||||
const char *label,
|
||||
char **pkcs11_decrypted_passwords,
|
||||
const PasswordCache *cache,
|
||||
char **effective_passwords,
|
||||
bool discard,
|
||||
UserRecord *hr,
|
||||
@@ -1533,7 +1539,8 @@ static int luks_format(
|
||||
|
||||
STRV_FOREACH(pp, effective_passwords) {
|
||||
|
||||
if (strv_contains(pkcs11_decrypted_passwords, *pp)) {
|
||||
if (strv_contains(cache->pkcs11_passwords, *pp) ||
|
||||
strv_contains(cache->fido2_passwords, *pp)) {
|
||||
log_debug("Using minimal PBKDF for slot %i", slot);
|
||||
r = crypt_set_pbkdf_type(cd, &minimal_pbkdf);
|
||||
} else {
|
||||
@@ -1858,7 +1865,7 @@ static int home_truncate(
|
||||
|
||||
int home_create_luks(
|
||||
UserRecord *h,
|
||||
char **pkcs11_decrypted_passwords,
|
||||
PasswordCache *cache,
|
||||
char **effective_passwords,
|
||||
UserRecord **ret_home) {
|
||||
|
||||
@@ -2055,7 +2062,7 @@ int home_create_luks(
|
||||
dm_name,
|
||||
luks_uuid,
|
||||
user_record_user_name_and_realm(h),
|
||||
pkcs11_decrypted_passwords,
|
||||
cache,
|
||||
effective_passwords,
|
||||
user_record_luks_discard(h) || user_record_luks_offline_discard(h),
|
||||
h,
|
||||
@@ -2561,7 +2568,7 @@ static int apply_resize_partition(int fd, sd_id128_t disk_uuids, struct fdisk_ta
|
||||
int home_resize_luks(
|
||||
UserRecord *h,
|
||||
bool already_activated,
|
||||
char ***pkcs11_decrypted_passwords,
|
||||
PasswordCache *cache,
|
||||
HomeSetup *setup,
|
||||
UserRecord **ret_home) {
|
||||
|
||||
@@ -2647,11 +2654,11 @@ int home_resize_luks(
|
||||
}
|
||||
}
|
||||
|
||||
r = home_prepare_luks(h, already_activated, whole_disk, pkcs11_decrypted_passwords, setup, &header_home);
|
||||
r = home_prepare_luks(h, already_activated, whole_disk, cache, setup, &header_home);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, pkcs11_decrypted_passwords, &embedded_home, &new_home);
|
||||
r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, cache, &embedded_home, &new_home);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -2855,13 +2862,14 @@ int home_resize_luks(
|
||||
int home_passwd_luks(
|
||||
UserRecord *h,
|
||||
HomeSetup *setup,
|
||||
char **pkcs11_decrypted_passwords, /* the passwords acquired via PKCS#11 security tokens */
|
||||
char **effective_passwords /* new passwords */) {
|
||||
PasswordCache *cache, /* the passwords acquired via PKCS#11/FIDO2 security tokens */
|
||||
char **effective_passwords /* new passwords */) {
|
||||
|
||||
size_t volume_key_size, i, max_key_slots, n_effective;
|
||||
_cleanup_(erase_and_freep) void *volume_key = NULL;
|
||||
struct crypt_pbkdf_type good_pbkdf, minimal_pbkdf;
|
||||
const char *type;
|
||||
char **list;
|
||||
int r;
|
||||
|
||||
assert(h);
|
||||
@@ -2886,12 +2894,14 @@ int home_passwd_luks(
|
||||
if (!volume_key)
|
||||
return log_oom();
|
||||
|
||||
r = luks_try_passwords(setup->crypt_device, pkcs11_decrypted_passwords, volume_key, &volume_key_size);
|
||||
if (r == -ENOKEY) {
|
||||
r = luks_try_passwords(setup->crypt_device, h->password, volume_key, &volume_key_size);
|
||||
if (r == -ENOKEY)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Failed to unlock LUKS superblock with supplied passwords.");
|
||||
r = -ENOKEY;
|
||||
FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, h->password) {
|
||||
r = luks_try_passwords(setup->crypt_device, list, volume_key, &volume_key_size);
|
||||
if (r != -ENOKEY)
|
||||
break;
|
||||
}
|
||||
if (r == -ENOKEY)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Failed to unlock LUKS superblock with supplied passwords.");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to unlocks LUKS superblock: %m");
|
||||
|
||||
@@ -2911,7 +2921,8 @@ int home_passwd_luks(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strv_find(pkcs11_decrypted_passwords, effective_passwords[i])) {
|
||||
if (strv_contains(cache->pkcs11_passwords, effective_passwords[i]) ||
|
||||
strv_contains(cache->fido2_passwords, effective_passwords[i])) {
|
||||
log_debug("Using minimal PBKDF for slot %zu", i);
|
||||
r = crypt_set_pbkdf_type(setup->crypt_device, &minimal_pbkdf);
|
||||
} else {
|
||||
@@ -3008,9 +3019,10 @@ static int luks_try_resume(
|
||||
return -ENOKEY;
|
||||
}
|
||||
|
||||
int home_unlock_luks(UserRecord *h, char ***pkcs11_decrypted_passwords) {
|
||||
int home_unlock_luks(UserRecord *h, PasswordCache *cache) {
|
||||
_cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
|
||||
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
|
||||
char **list;
|
||||
int r;
|
||||
|
||||
assert(h);
|
||||
@@ -3026,12 +3038,14 @@ int home_unlock_luks(UserRecord *h, char ***pkcs11_decrypted_passwords) {
|
||||
log_info("Discovered used LUKS device %s.", dm_node);
|
||||
crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
|
||||
|
||||
r = luks_try_resume(cd, dm_name, pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL);
|
||||
if (r == -ENOKEY) {
|
||||
r = luks_try_resume(cd, dm_name, h->password);
|
||||
if (r == -ENOKEY)
|
||||
return log_error_errno(r, "No valid password for LUKS superblock.");
|
||||
r = -ENOKEY;
|
||||
FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, h->password) {
|
||||
r = luks_try_resume(cd, dm_name, list);
|
||||
if (r != -ENOKEY)
|
||||
break;
|
||||
}
|
||||
if (r == -ENOKEY)
|
||||
return log_error_errno(r, "No valid password for LUKS superblock.");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to resume LUKS superblock: %m");
|
||||
|
||||
|
||||
@@ -5,24 +5,24 @@
|
||||
#include "homework.h"
|
||||
#include "user-record.h"
|
||||
|
||||
int home_prepare_luks(UserRecord *h, bool already_activated, const char *force_image_path, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_luks_home);
|
||||
int home_prepare_luks(UserRecord *h, bool already_activated, const char *force_image_path, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_luks_home);
|
||||
|
||||
int home_activate_luks(UserRecord *h, char ***pkcs11_decrypted_passwords, UserRecord **ret_home);
|
||||
int home_activate_luks(UserRecord *h, PasswordCache *cache, UserRecord **ret_home);
|
||||
int home_deactivate_luks(UserRecord *h);
|
||||
int home_trim_luks(UserRecord *h);
|
||||
|
||||
int home_store_header_identity_luks(UserRecord *h, HomeSetup *setup, UserRecord *old_home);
|
||||
|
||||
int home_create_luks(UserRecord *h, char **pkcs11_decrypted_passwords, char **effective_passwords, UserRecord **ret_home);
|
||||
int home_create_luks(UserRecord *h, PasswordCache *cache, char **effective_passwords, UserRecord **ret_home);
|
||||
|
||||
int home_validate_update_luks(UserRecord *h, HomeSetup *setup);
|
||||
|
||||
int home_resize_luks(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_home);
|
||||
int home_resize_luks(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_home);
|
||||
|
||||
int home_passwd_luks(UserRecord *h, HomeSetup *setup, char **pkcs11_decrypted_passwords, char **effective_passwords);
|
||||
int home_passwd_luks(UserRecord *h, HomeSetup *setup, PasswordCache *cache, char **effective_passwords);
|
||||
|
||||
int home_lock_luks(UserRecord *h);
|
||||
int home_unlock_luks(UserRecord *h, char ***pkcs11_decrypted_passwords);
|
||||
int home_unlock_luks(UserRecord *h, PasswordCache *cache);
|
||||
|
||||
static inline uint64_t luks_volume_key_size_convert(struct crypt_device *cd) {
|
||||
int k;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,14 @@ typedef struct HomeSetup {
|
||||
uint64_t partition_size;
|
||||
} HomeSetup;
|
||||
|
||||
typedef struct PasswordCache {
|
||||
/* Decoding passwords from security tokens is expensive and typically requires user interaction, hence cache any we already figured out. */
|
||||
char **pkcs11_passwords;
|
||||
char **fido2_passwords;
|
||||
} PasswordCache;
|
||||
|
||||
void password_cache_free(PasswordCache *cache);
|
||||
|
||||
#define HOME_SETUP_INIT \
|
||||
{ \
|
||||
.root_fd = -1, \
|
||||
@@ -46,16 +54,16 @@ typedef struct HomeSetup {
|
||||
|
||||
int home_setup_undo(HomeSetup *setup);
|
||||
|
||||
int home_prepare(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_header_home);
|
||||
int home_prepare(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_header_home);
|
||||
|
||||
int home_refresh(UserRecord *h, HomeSetup *setup, UserRecord *header_home, char ***pkcs11_decrypted_passwords, struct statfs *ret_statfs, UserRecord **ret_new_home);
|
||||
int home_refresh(UserRecord *h, HomeSetup *setup, UserRecord *header_home, PasswordCache *cache, struct statfs *ret_statfs, UserRecord **ret_new_home);
|
||||
|
||||
int home_populate(UserRecord *h, int dir_fd);
|
||||
|
||||
int home_load_embedded_identity(UserRecord *h, int root_fd, UserRecord *header_home, UserReconcileMode mode, char ***pkcs11_decrypted_passwords, UserRecord **ret_embedded_home, UserRecord **ret_new_home);
|
||||
int home_load_embedded_identity(UserRecord *h, int root_fd, UserRecord *header_home, UserReconcileMode mode, PasswordCache *cache, UserRecord **ret_embedded_home, UserRecord **ret_new_home);
|
||||
int home_store_embedded_identity(UserRecord *h, int root_fd, uid_t uid, UserRecord *old_home);
|
||||
int home_extend_embedded_identity(UserRecord *h, UserRecord *used, HomeSetup *setup);
|
||||
|
||||
int user_record_authenticate(UserRecord *h, UserRecord *secret, char ***pkcs11_decrypted_passwords, bool strict_verify);
|
||||
int user_record_authenticate(UserRecord *h, UserRecord *secret, PasswordCache *cache, bool strict_verify);
|
||||
|
||||
int home_sync_and_statfs(int root_fd, struct statfs *ret);
|
||||
|
||||
@@ -14,6 +14,7 @@ systemd_homework_sources = files('''
|
||||
homework-mount.c
|
||||
homework-mount.h
|
||||
homework-pkcs11.h
|
||||
homework-fido2.h
|
||||
homework-quota.c
|
||||
homework-quota.h
|
||||
homework.c
|
||||
@@ -25,6 +26,9 @@ systemd_homework_sources = files('''
|
||||
if conf.get('HAVE_P11KIT') == 1
|
||||
systemd_homework_sources += files('homework-pkcs11.c')
|
||||
endif
|
||||
if conf.get('HAVE_LIBFIDO2') == 1
|
||||
systemd_homework_sources += files('homework-fido2.c')
|
||||
endif
|
||||
|
||||
systemd_homed_sources = files('''
|
||||
home-util.c
|
||||
|
||||
@@ -375,6 +375,16 @@ static int handle_generic_user_record_error(
|
||||
return PAM_SERVICE_ERR;
|
||||
}
|
||||
|
||||
} else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED)) {
|
||||
|
||||
(void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Please verify presence on security token of user %s.", user_name);
|
||||
|
||||
r = user_record_set_fido2_user_presence_permitted(secret, true);
|
||||
if (r < 0) {
|
||||
pam_syslog(handle, LOG_ERR, "Failed to set FIDO2 user presence permitted flag: %s", strerror_safe(r));
|
||||
return PAM_SERVICE_ERR;
|
||||
}
|
||||
|
||||
} else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) {
|
||||
_cleanup_(erase_and_freep) char *newp = NULL;
|
||||
|
||||
|
||||
@@ -980,6 +980,34 @@ int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord *h
|
||||
return 0;
|
||||
}
|
||||
|
||||
int user_record_set_fido2_user_presence_permitted(UserRecord *h, int b) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
|
||||
int r;
|
||||
|
||||
assert(h);
|
||||
|
||||
w = json_variant_ref(json_variant_by_key(h->json, "secret"));
|
||||
|
||||
if (b < 0)
|
||||
r = json_variant_filter(&w, STRV_MAKE("fido2UserPresencePermitted"));
|
||||
else
|
||||
r = json_variant_set_field_boolean(&w, "fido2UserPresencePermitted", b);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (json_variant_is_blank_object(w))
|
||||
r = json_variant_filter(&h->json, STRV_MAKE("secret"));
|
||||
else
|
||||
r = json_variant_set_field(&h->json, "secret", w);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
h->fido2_user_presence_permitted = b;
|
||||
|
||||
SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool per_machine_entry_empty(JsonVariant *v) {
|
||||
const char *k;
|
||||
_unused_ JsonVariant *e;
|
||||
@@ -1067,7 +1095,17 @@ int user_record_merge_secret(UserRecord *h, UserRecord *secret) {
|
||||
return r;
|
||||
|
||||
if (secret->pkcs11_protected_authentication_path_permitted >= 0) {
|
||||
r = user_record_set_pkcs11_protected_authentication_path_permitted(h, secret->pkcs11_protected_authentication_path_permitted);
|
||||
r = user_record_set_pkcs11_protected_authentication_path_permitted(
|
||||
h,
|
||||
secret->pkcs11_protected_authentication_path_permitted);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (secret->fido2_user_presence_permitted >= 0) {
|
||||
r = user_record_set_fido2_user_presence_permitted(
|
||||
h,
|
||||
secret->fido2_user_presence_permitted);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ int user_record_make_hashed_password(UserRecord *h, char **password, bool extend
|
||||
int user_record_set_hashed_password(UserRecord *h, char **hashed_password);
|
||||
int user_record_set_token_pin(UserRecord *h, char **pin, bool prepend);
|
||||
int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord *h, int b);
|
||||
int user_record_set_fido2_user_presence_permitted(UserRecord *h, int b);
|
||||
int user_record_set_password_change_now(UserRecord *h, int b);
|
||||
int user_record_merge_secret(UserRecord *h, UserRecord *secret);
|
||||
int user_record_good_authentication(UserRecord *h);
|
||||
|
||||
@@ -120,6 +120,8 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN, EBADSLT),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_PIN_NEEDED, ENOANO),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED, ERFKILL),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED, EMEDIUMTYPE),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_ACTION_TIMEOUT, ENOSTR),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_PIN_LOCKED, EOWNERDEAD),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_BAD_PIN, ENOLCK),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT, ETOOMANYREFS),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user