1
0
mirror of https://github.com/systemd/systemd synced 2026-03-25 16:25:04 +01:00

Compare commits

..

19 Commits

Author SHA1 Message Date
Luca Boccassi
468d9bc901
Merge pull request #20436 from fbuihuu/add-no-build-support-on-opensuse
Add no build support on opensuse
2021-08-19 21:11:31 +01:00
Lennart Poettering
7b58fcdd70
Merge pull request #19797 from oniko/systemd-fido2-pkcs11-plugins
Add support for remaining systemd fido2 and pkcs11 libcryptsetup plugins
2021-08-19 16:37:55 +02:00
Luca Boccassi
61a6aa21a5
Merge pull request #20471 from poettering/format-str-proc-fd
add FORMAT_PROC_FD_PATH() macro for generating /proc/self/fd/ paths on-the-fly
2021-08-19 14:05:22 +01:00
Ondrej Kozina
8186022c9d Add support for systemd-pkcs11 libcryptsetup plugin.
Add support for systemd-pkcs11 based LUKS2 device activation
via libcryptsetup plugin. This make the feature (pkcs11 sealed
LUKS2 keyslot passphrase) usable from both systemd utilities
and cryptsetup cli.

The feature is configured via -Dlibcryptsetup-plugins combo
with default value set to 'auto'. It get's enabled automatically
when cryptsetup 2.4.0 or later is installed in build system.
2021-08-19 13:58:10 +02:00
Ondrej Kozina
0ff605665a pkcs11-util: split pkcs11_token_login function
Future systemd-pkcs11 plugin requires unlock via single
call with supplied pin. To reduce needless code duplication
in plugin itself split original pkcs_11_token_login call in
two calls:

new pkcs11_token_login_by_pin and the former where loop
for retrying via PIN query callback remains.
2021-08-19 13:58:10 +02:00
Ondrej Kozina
ed3d3af148 cryptsetup-pkcs11: move pkcs11_callback and data in shared utils.
To be used later by both (future) systemd-pkcs11 libcryptsetup
plugin and cryptsetup-pkcs11.
2021-08-19 13:58:10 +02:00
Ondrej Kozina
351716e111 Add support for systemd-fido2 libcryptsetup plugin.
Add support for systemd-fido2 based LUKS2 device activation
via libcryptsetup plugin. This make the feature (fido2 sealed
LUKS2 keyslot passphrase) usable from both systemd utilities
and cryptsetup cli.

The feature is configured via -Dlibcryptsetup-plugins combo
with default value set to 'auto'. It get's enabled automatically
when cryptsetup 2.4.0 or later is installed in build system.
2021-08-19 13:58:10 +02:00
Lennart Poettering
ddb6eeafe2 tree-wide: port things over to FORMAT_PROC_FD_PATH() 2021-08-19 09:19:27 +02:00
Lennart Poettering
48a01cd934 test: add test for FORMAT_PROC_FD_PATH() 2021-08-19 09:19:23 +02:00
Lennart Poettering
6e1e4b59f9 fd-util: add macro for generating /proc/self/fd/ paths on the fly 2021-08-19 09:19:11 +02:00
Lennart Poettering
3832cb90ba stdio-util: give snprintf_ok() some love
as per docs snprintf() can fail in which case it returns -1. The
snprintf_ok() macro so far unconditionally cast the return value of
snprintf() to size_t, which would turn -1 to (size_t) INT_MAX,
presumably, at least on 2 complements system.

Let's be more careful with types here, and first check if return value
is positive, before casting to size_t.

Also, while we are at it, let's return the input buffer as return value
or NULL instead of 1 or 0. It's marginally more useful, but more
importantly, is more inline with most of our other codebase that
typically doesn't use booleans to signal success.

All uses of snprintf_ok() don't care for the type of the return, hence
this change does not propagate anywhere else.
2021-08-19 09:19:03 +02:00
Lennart Poettering
12a7f04a2b discover-image: pass the right fd to fd_getcrtime() 2021-08-19 09:18:45 +02:00
Franck Bui
d93857ae09 test: if haveged is part of initrd it needs to be installed in the image too
Otherwise haveged won't survive when switching root from initrd to host making
haveged service in host fail.
2021-08-18 17:37:55 +02:00
Franck Bui
138f761904 test: adapt install_pam() for openSUSE
On openSUSE the default pam config files are shipped in /usr/etc/pam.d.

Also empty password is not allowed by default.
2021-08-18 17:37:55 +02:00
Franck Bui
d8167c5212 Revert "test: adapt TEST-13-NSPAWN-SMOKE for SUSE"
This reverts commit 491b736a49fb9d64b0b515aa773297a30c8bab1d.

If the _static_ linked version of busybox is installed, openSUSE doesn't need
any specific code.

A following commit will make sure that the static linked version of busybox is
installed in the busybox container.
2021-08-18 17:37:55 +02:00
Franck Bui
5231ec50e9 test: on openSUSE the static linked version of busybox is named "busybox-static" 2021-08-18 17:37:55 +02:00
Franck Bui
6c8ba239d5 TEST-13-*: in busybox container sleep(1) takes a delay in seconds only 2021-08-18 17:37:55 +02:00
Franck Bui
dfd73ccb14 test: don't try to find BUILD_DIR when NO_BUILD is set
NO_BUILD=1 indicates that we want to test systemd from the local system and not
the one from the local build. Hence there should be no need to call
find-build-dir.sh when NO_BUID=1 especially since it's likely that the script
will fail to find a local build in this case.

This avoids find-build-dir.sh to emit 'Specify build directory with $BUILD_DIR'
message when NO_BUILD=1 and no local build can be found.

This introduces a behavior change though: systemd from the local system will
always be preferred when NO_BUILD=1 even if a local build can be found.
2021-08-18 17:37:55 +02:00
Franck Bui
abf062674e test: add support for NO_BUILD=1 on openSUSE 2021-08-18 17:37:24 +02:00
38 changed files with 1443 additions and 334 deletions

View File

@ -1787,6 +1787,34 @@ if conf.get('HAVE_LIBCRYPTSETUP_PLUGINS') == 1
install : true, install : true,
install_dir : libcryptsetup_plugins_dir) install_dir : libcryptsetup_plugins_dir)
endif endif
if conf.get('HAVE_LIBFIDO2') == 1
cryptsetup_token_systemd_fido2 = shared_library(
'cryptsetup-token-systemd-fido2',
link_args : ['-shared',
'-Wl,--version-script=' + cryptsetup_token_sym_path],
dependencies : libshared_deps + [libcryptsetup, versiondep],
link_with : [libshared],
link_whole : [cryptsetup_token_systemd_fido2_static],
link_depends : cryptsetup_token_sym,
install_rpath : rootlibexecdir,
install : true,
install_dir : libcryptsetup_plugins_dir)
endif
if conf.get('HAVE_P11KIT') == 1
cryptsetup_token_systemd_pkcs11 = shared_library(
'cryptsetup-token-systemd-pkcs11',
link_args : ['-shared',
'-Wl,--version-script=' + cryptsetup_token_sym_path],
dependencies : libshared_deps + [libcryptsetup, versiondep],
link_with : [libshared],
link_whole : [cryptsetup_token_systemd_pkcs11_static],
link_depends : cryptsetup_token_sym,
install_rpath : rootlibexecdir,
install : true,
install_dir : libcryptsetup_plugins_dir)
endif
endif endif
############################################################ ############################################################

View File

@ -433,11 +433,9 @@ bool fdname_is_valid(const char *s) {
} }
int fd_get_path(int fd, char **ret) { int fd_get_path(int fd, char **ret) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
int r; int r;
xsprintf(procfs_path, "/proc/self/fd/%i", fd); r = readlink_malloc(FORMAT_PROC_FD_PATH(fd), ret);
r = readlink_malloc(procfs_path, ret);
if (r == -ENOENT) { if (r == -ENOENT) {
/* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make
* things debuggable and distinguish the two. */ * things debuggable and distinguish the two. */
@ -647,7 +645,6 @@ finish:
} }
int fd_reopen(int fd, int flags) { int fd_reopen(int fd, int flags) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
int new_fd; int new_fd;
/* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to /* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to
@ -657,8 +654,7 @@ int fd_reopen(int fd, int flags) {
* *
* This implicitly resets the file read index to 0. */ * This implicitly resets the file read index to 0. */
xsprintf(procfs_path, "/proc/self/fd/%i", fd); new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
new_fd = open(procfs_path, flags);
if (new_fd < 0) { if (new_fd < 0) {
if (errno != ENOENT) if (errno != ENOENT)
return -errno; return -errno;

View File

@ -7,6 +7,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include "macro.h" #include "macro.h"
#include "stdio-util.h"
/* maximum length of fdname */ /* maximum length of fdname */
#define FDNAME_MAX 255 #define FDNAME_MAX 255
@ -104,7 +105,20 @@ static inline int make_null_stdio(void) {
0; \ 0; \
}) })
int fd_reopen(int fd, int flags); int fd_reopen(int fd, int flags);
int read_nr_open(void); int read_nr_open(void);
int btrfs_defrag_fd(int fd); int btrfs_defrag_fd(int fd);
/* The maximum length a buffer for a /proc/self/fd/<fd> path needs */
#define PROC_FD_PATH_MAX \
(STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int))
static inline char *format_proc_fd_path(char buf[static PROC_FD_PATH_MAX], int fd) {
assert(buf);
assert(fd >= 0);
assert_se(snprintf_ok(buf, PROC_FD_PATH_MAX, "/proc/self/fd/%i", fd));
return buf;
}
#define FORMAT_PROC_FD_PATH(fd) \
format_proc_fd_path((char[PROC_FD_PATH_MAX]) {}, (fd))

View File

@ -721,8 +721,6 @@ int read_full_file_full(
if (dir_fd == AT_FDCWD) if (dir_fd == AT_FDCWD)
r = sockaddr_un_set_path(&sa.un, filename); r = sockaddr_un_set_path(&sa.un, filename);
else { else {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
/* If we shall operate relative to some directory, then let's use O_PATH first to /* If we shall operate relative to some directory, then let's use O_PATH first to
* open the socket inode, and then connect to it via /proc/self/fd/. We have to do * open the socket inode, and then connect to it via /proc/self/fd/. We have to do
* this since there's not connectat() that takes a directory fd as first arg. */ * this since there's not connectat() that takes a directory fd as first arg. */
@ -731,8 +729,7 @@ int read_full_file_full(
if (dfd < 0) if (dfd < 0)
return -errno; return -errno;
xsprintf(procfs_path, "/proc/self/fd/%i", dfd); r = sockaddr_un_set_path(&sa.un, FORMAT_PROC_FD_PATH(dfd));
r = sockaddr_un_set_path(&sa.un, procfs_path);
} }
if (r < 0) if (r < 0)
return r; return r;

View File

@ -308,14 +308,11 @@ int fchmod_umask(int fd, mode_t m) {
} }
int fchmod_opath(int fd, mode_t m) { int fchmod_opath(int fd, mode_t m) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
/* This function operates also on fd that might have been opened with /* This function operates also on fd that might have been opened with
* O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like * O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like
* fchownat() does. */ * fchownat() does. */
xsprintf(procfs_path, "/proc/self/fd/%i", fd); if (chmod(FORMAT_PROC_FD_PATH(fd), m) < 0) {
if (chmod(procfs_path, m) < 0) {
if (errno != ENOENT) if (errno != ENOENT)
return -errno; return -errno;
@ -329,12 +326,9 @@ int fchmod_opath(int fd, mode_t m) {
} }
int futimens_opath(int fd, const struct timespec ts[2]) { int futimens_opath(int fd, const struct timespec ts[2]) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
/* Similar to fchmod_path() but for futimens() */ /* Similar to fchmod_path() but for futimens() */
xsprintf(procfs_path, "/proc/self/fd/%i", fd); if (utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, 0) < 0) {
if (utimensat(AT_FDCWD, procfs_path, ts, 0) < 0) {
if (errno != ENOENT) if (errno != ENOENT)
return -errno; return -errno;
@ -380,7 +374,6 @@ int fd_warn_permissions(const char *path, int fd) {
} }
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) { int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
_cleanup_close_ int fd = -1; _cleanup_close_ int fd = -1;
int r, ret = 0; int r, ret = 0;
@ -412,8 +405,6 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
/* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode, /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode,
* ownership and time of the file node in all cases, even if the fd refers to an O_PATH object which is * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object which is
* something fchown(), fchmod(), futimensat() don't allow. */ * something fchown(), fchmod(), futimensat() don't allow. */
xsprintf(fdpath, "/proc/self/fd/%i", fd);
ret = fchmod_and_chown(fd, mode, uid, gid); ret = fchmod_and_chown(fd, mode, uid, gid);
if (stamp != USEC_INFINITY) { if (stamp != USEC_INFINITY) {
@ -421,9 +412,9 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
timespec_store(&ts[0], stamp); timespec_store(&ts[0], stamp);
ts[1] = ts[0]; ts[1] = ts[0];
r = utimensat(AT_FDCWD, fdpath, ts, 0); r = utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, 0);
} else } else
r = utimensat(AT_FDCWD, fdpath, NULL, 0); r = utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), NULL, 0);
if (r < 0 && ret >= 0) if (r < 0 && ret >= 0)
return -errno; return -errno;
@ -703,13 +694,10 @@ int unlink_or_warn(const char *filename) {
} }
int inotify_add_watch_fd(int fd, int what, uint32_t mask) { int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
int wd; int wd;
/* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */
xsprintf(path, "/proc/self/fd/%i", what); wd = inotify_add_watch(fd, FORMAT_PROC_FD_PATH(what), mask);
wd = inotify_add_watch(fd, path, mask);
if (wd < 0) if (wd < 0)
return -errno; return -errno;
@ -1156,7 +1144,6 @@ int chase_symlinks_and_opendir(
char **ret_path, char **ret_path,
DIR **ret_dir) { DIR **ret_dir) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
_cleanup_close_ int path_fd = -1; _cleanup_close_ int path_fd = -1;
_cleanup_free_ char *p = NULL; _cleanup_free_ char *p = NULL;
DIR *d; DIR *d;
@ -1182,8 +1169,7 @@ int chase_symlinks_and_opendir(
return r; return r;
assert(path_fd >= 0); assert(path_fd >= 0);
xsprintf(procfs_path, "/proc/self/fd/%i", path_fd); d = opendir(FORMAT_PROC_FD_PATH(path_fd));
d = opendir(procfs_path);
if (!d) if (!d)
return -errno; return -errno;
@ -1237,12 +1223,9 @@ int chase_symlinks_and_stat(
} }
int access_fd(int fd, int mode) { int access_fd(int fd, int mode) {
char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
/* Like access() but operates on an already open fd */ /* Like access() but operates on an already open fd */
xsprintf(p, "/proc/self/fd/%i", fd); if (access(FORMAT_PROC_FD_PATH(fd), mode) < 0) {
if (access(p, mode) < 0) {
if (errno != ENOENT) if (errno != ENOENT)
return -errno; return -errno;

View File

@ -89,9 +89,7 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
/* Can't setns to your own userns, since then you could escalate from non-root to root in /* Can't setns to your own userns, since then you could escalate from non-root to root in
* your own namespace, so check if namespaces are equal before attempting to enter. */ * your own namespace, so check if namespaces are equal before attempting to enter. */
char userns_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; r = files_same(FORMAT_PROC_FD_PATH(userns_fd), "/proc/self/ns/user", 0);
xsprintf(userns_fd_path, "/proc/self/fd/%d", userns_fd);
r = files_same(userns_fd_path, "/proc/self/ns/user", 0);
if (r < 0) if (r < 0)
return r; return r;
if (r) if (r)

View File

@ -9,8 +9,13 @@
#include "macro.h" #include "macro.h"
#include "memory-util.h" #include "memory-util.h"
#define snprintf_ok(buf, len, fmt, ...) \ #define snprintf_ok(buf, len, fmt, ...) \
((size_t) snprintf(buf, len, fmt, __VA_ARGS__) < (len)) ({ \
char *_buf = (buf); \
size_t _len = (len); \
int _snpf = snprintf(_buf, _len, (fmt), __VA_ARGS__); \
_snpf >= 0 && (size_t) _snpf < _len ? _buf : NULL; \
})
#define xsprintf(buf, fmt, ...) \ #define xsprintf(buf, fmt, ...) \
assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__), "xsprintf: " #buf "[] must be big enough") assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__), "xsprintf: " #buf "[] must be big enough")

View File

@ -300,11 +300,7 @@ int link_tmpfile(int fd, const char *path, const char *target) {
if (r < 0) if (r < 0)
return r; return r;
} else { } else {
char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; if (linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
xsprintf(proc_fd_path, "/proc/self/fd/%i", fd);
if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
return -errno; return -errno;
} }

View File

@ -108,11 +108,10 @@ static int getxattrat_fake_prepare(
int dirfd, int dirfd,
const char *filename, const char *filename,
int flags, int flags,
char ret_fn[static STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1], char ret_fn[static PROC_FD_PATH_MAX],
int *ret_fd) { int *ret_fd) {
_cleanup_close_ int fd = -1; _cleanup_close_ int fd = -1;
assert(ret_fn); assert(ret_fn);
assert(ret_fd); assert(ret_fd);
@ -125,13 +124,15 @@ static int getxattrat_fake_prepare(
if (!(flags & AT_EMPTY_PATH)) if (!(flags & AT_EMPTY_PATH))
return -EINVAL; return -EINVAL;
snprintf(ret_fn, STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1, "/proc/self/fd/%i", dirfd); assert(dirfd >= 0);
format_proc_fd_path(ret_fn, dirfd);
} else { } else {
fd = openat(dirfd, filename, O_CLOEXEC|O_PATH|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0)); fd = openat(dirfd, filename, O_CLOEXEC|O_PATH|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
if (fd < 0) if (fd < 0)
return -errno; return -errno;
snprintf(ret_fn, STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1, "/proc/self/fd/%i", fd); format_proc_fd_path(ret_fn, fd);
} }
/* Pass the FD to the caller, since in case we do openat() the filename depends on it. */ /* Pass the FD to the caller, since in case we do openat() the filename depends on it. */
@ -148,8 +149,8 @@ int fgetxattrat_fake(
int flags, int flags,
size_t *ret_size) { size_t *ret_size) {
char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
_cleanup_close_ int fd = -1; _cleanup_close_ int fd = -1;
char fn[PROC_FD_PATH_MAX];
ssize_t l; ssize_t l;
int r; int r;
@ -172,8 +173,8 @@ int fgetxattrat_fake_malloc(
int flags, int flags,
char **value) { char **value) {
char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
_cleanup_close_ int fd = -1; _cleanup_close_ int fd = -1;
char fn[PROC_FD_PATH_MAX];
int r; int r;
r = getxattrat_fake_prepare(dirfd, filename, flags, fn, &fd); r = getxattrat_fake_prepare(dirfd, filename, flags, fn, &fd);

View File

@ -916,7 +916,6 @@ static int service_is_suitable_main_pid(Service *s, pid_t pid, int prio) {
} }
static int service_load_pid_file(Service *s, bool may_warn) { static int service_load_pid_file(Service *s, bool may_warn) {
char procfs[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
bool questionable_pid_file = false; bool questionable_pid_file = false;
_cleanup_free_ char *k = NULL; _cleanup_free_ char *k = NULL;
_cleanup_close_ int fd = -1; _cleanup_close_ int fd = -1;
@ -945,8 +944,7 @@ static int service_load_pid_file(Service *s, bool may_warn) {
/* Let's read the PID file now that we chased it down. But we need to convert the O_PATH fd /* Let's read the PID file now that we chased it down. But we need to convert the O_PATH fd
* chase_symlinks() returned us into a proper fd first. */ * chase_symlinks() returned us into a proper fd first. */
xsprintf(procfs, "/proc/self/fd/%i", fd); r = read_one_line_file(FORMAT_PROC_FD_PATH(fd), &k);
r = read_one_line_file(procfs, &k);
if (r < 0) if (r < 0)
return log_unit_error_errno(UNIT(s), r, return log_unit_error_errno(UNIT(s), r,
"Can't convert PID files %s O_PATH file descriptor to proper file descriptor: %m", "Can't convert PID files %s O_PATH file descriptor to proper file descriptor: %m",

View File

@ -24,83 +24,6 @@
#include "stat-util.h" #include "stat-util.h"
#include "strv.h" #include "strv.h"
struct pkcs11_callback_data {
const char *friendly_name;
usec_t until;
void *encrypted_key;
size_t encrypted_key_size;
void *decrypted_key;
size_t decrypted_key_size;
bool free_encrypted_key;
bool headless;
};
static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) {
erase_and_free(data->decrypted_key);
if (data->free_encrypted_key)
free(data->encrypted_key);
}
static int 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) {
struct pkcs11_callback_data *data = userdata;
CK_OBJECT_HANDLE object;
int r;
assert(m);
assert(slot_info);
assert(token_info);
assert(uri);
assert(data);
/* Called for every token matching our URI */
r = pkcs11_token_login(
m,
session,
slot_id,
token_info,
data->friendly_name,
"drive-harddisk",
"pkcs11-pin",
"cryptsetup.pkcs11-pin",
data->until,
data->headless,
NULL);
if (r < 0)
return r;
/* We are likely called during early boot, where entropy is scarce. Mix some data from the PKCS#11
* token, if it supports that. It should be cheap, given that we already are talking to it anyway and
* shouldn't hurt. */
(void) pkcs11_token_acquire_rng(m, session);
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;
}
int decrypt_pkcs11_key( int decrypt_pkcs11_key(
const char *volume_name, const char *volume_name,
const char *friendly_name, const char *friendly_name,
@ -115,7 +38,7 @@ int decrypt_pkcs11_key(
void **ret_decrypted_key, void **ret_decrypted_key,
size_t *ret_decrypted_key_size) { size_t *ret_decrypted_key_size) {
_cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = { _cleanup_(pkcs11_crypt_device_callback_data_release) pkcs11_crypt_device_callback_data data = {
.friendly_name = friendly_name, .friendly_name = friendly_name,
.until = until, .until = until,
.headless = headless, .headless = headless,
@ -155,7 +78,7 @@ int decrypt_pkcs11_key(
data.free_encrypted_key = true; data.free_encrypted_key = true;
} }
r = pkcs11_find_token(pkcs11_uri, pkcs11_callback, &data); r = pkcs11_find_token(pkcs11_uri, pkcs11_crypt_device_callback, &data);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -0,0 +1,224 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
#include <libcryptsetup.h>
#include <string.h>
#include "cryptsetup-token.h"
#include "cryptsetup-token-util.h"
#include "hexdecoct.h"
#include "json.h"
#include "luks2-fido2.h"
#include "memory-util.h"
#include "version.h"
#define TOKEN_NAME "systemd-fido2"
#define TOKEN_VERSION_MAJOR "1"
#define TOKEN_VERSION_MINOR "0"
/* for libcryptsetup debug purpose */
_public_ const char *cryptsetup_token_version(void) {
return TOKEN_VERSION_MAJOR "." TOKEN_VERSION_MINOR " systemd-v" STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")";
}
_public_ int cryptsetup_token_open_pin(
struct crypt_device *cd, /* is always LUKS2 context */
int token /* is always >= 0 */,
const char *pin,
size_t pin_size,
char **password, /* freed by cryptsetup_token_buffer_free */
size_t *password_len,
void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
int r;
const char *json;
_cleanup_(erase_and_freep) char *pin_string = NULL;
assert(!pin || pin_size);
assert(token >= 0);
/* This must not fail at this moment (internal error) */
r = crypt_token_json_get(cd, token, &json);
assert(token == r);
assert(json);
if (pin && memchr(pin, 0, pin_size - 1))
return crypt_log_error_errno(cd, ENOANO, "PIN must be characters string.");
/* pin was passed as pin = pin, pin_size = strlen(pin). We need to add terminating
* NULL byte to addressable memory*/
if (pin && pin[pin_size-1] != '\0') {
pin_string = strndup(pin, pin_size);
if (!pin_string)
return crypt_log_oom(cd);
}
return acquire_luks2_key(cd, json, (const char *)usrptr, pin_string ?: pin, password, password_len);
}
/*
* This function is called from within following libcryptsetup calls
* provided conditions further below are met:
*
* crypt_activate_by_token(), crypt_activate_by_token_type(type == 'systemd-fido2'):
*
* - token is assigned to at least one luks2 keyslot eligible to activate LUKS2 device
* (alternatively: name is set to null, flags contains CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY
* and token is assigned to at least single keyslot).
*
* - if plugin defines validate funtion (see cryptsetup_token_validate below) it must have
* passed the check (aka return 0)
*/
_public_ int cryptsetup_token_open(
struct crypt_device *cd, /* is always LUKS2 context */
int token /* is always >= 0 */,
char **password, /* freed by cryptsetup_token_buffer_free */
size_t *password_len,
void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
return cryptsetup_token_open_pin(cd, token, NULL, 0, password, password_len, usrptr);
}
/*
* libcryptsetup callback for memory deallocation of 'password' parameter passed in
* any crypt_token_open_* plugin function
*/
_public_ void cryptsetup_token_buffer_free(void *buffer, size_t buffer_len) {
erase_and_free(buffer);
}
/*
* prints systemd-fido2 token content in crypt_dump().
* 'type' and 'keyslots' fields are printed by libcryptsetup
*/
_public_ void cryptsetup_token_dump(
struct crypt_device *cd /* is always LUKS2 context */,
const char *json /* validated 'systemd-tpm2' token if cryptsetup_token_validate is defined */) {
int r;
Fido2EnrollFlags required;
size_t cid_size, salt_size;
const char *client_pin_req_str, *up_req_str, *uv_req_str;
_cleanup_free_ void *cid = NULL, *salt = NULL;
_cleanup_free_ char *rp_id = NULL, *cid_str = NULL, *salt_str = NULL;
assert(json);
r = parse_luks2_fido2_data(cd, json, &rp_id, &salt, &salt_size, &cid, &cid_size, &required);
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " metadata: %m.");
r = crypt_dump_buffer_to_hex_string(cid, cid_size, &cid_str);
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Can not dump " TOKEN_NAME " content: %m");
r = crypt_dump_buffer_to_hex_string(salt, salt_size, &salt_str);
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Can not dump " TOKEN_NAME " content: %m");
if (required & FIDO2ENROLL_PIN)
client_pin_req_str = "true";
else if (required & FIDO2ENROLL_PIN_IF_NEEDED)
client_pin_req_str = NULL;
else
client_pin_req_str = "false";
if (required & FIDO2ENROLL_UP)
up_req_str = "true";
else if (required & FIDO2ENROLL_UP_IF_NEEDED)
up_req_str = NULL;
else
up_req_str = "false";
if (required & FIDO2ENROLL_UV)
uv_req_str = "true";
else if (required & FIDO2ENROLL_UV_OMIT)
uv_req_str = NULL;
else
uv_req_str = "false";
crypt_log(cd, "\tfido2-credential:" CRYPT_DUMP_LINE_SEP "%s\n", cid_str);
crypt_log(cd, "\tfido2-salt: %s\n", salt_str);
/* optional fields */
if (rp_id)
crypt_log(cd, "\tfido2-rp: %s\n", rp_id);
if (client_pin_req_str)
crypt_log(cd, "\tfido2-clientPin-required:" CRYPT_DUMP_LINE_SEP "%s\n",
client_pin_req_str);
if (up_req_str)
crypt_log(cd, "\tfido2-up-required:" CRYPT_DUMP_LINE_SEP "%s\n", up_req_str);
if (uv_req_str)
crypt_log(cd, "\tfido2-uv-required:" CRYPT_DUMP_LINE_SEP "%s\n", uv_req_str);
}
/*
* Note:
* If plugin is available in library path, it's called in before following libcryptsetup calls:
*
* crypt_token_json_set, crypt_dump, any crypt_activate_by_token_* flavour
*/
_public_ int cryptsetup_token_validate(
struct crypt_device *cd, /* is always LUKS2 context */
const char *json /* contains valid 'type' and 'keyslots' fields. 'type' is 'systemd-tpm2' */) {
int r;
JsonVariant *w;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
assert(json);
r = json_parse(json, 0, &v, NULL, NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Could not parse " TOKEN_NAME " json object: %m.");
w = json_variant_by_key(v, "fido2-credential");
if (!w || !json_variant_is_string(w)) {
crypt_log_debug(cd, "FIDO2 token data lacks 'fido2-credential' field.");
return 1;
}
r = unbase64mem(json_variant_string(w), SIZE_MAX, NULL, NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'fido2-credential' field: %m");
w = json_variant_by_key(v, "fido2-salt");
if (!w || !json_variant_is_string(w)) {
crypt_log_debug(cd, "FIDO2 token data lacks 'fido2-salt' field.");
return 1;
}
r = unbase64mem(json_variant_string(w), SIZE_MAX, NULL, NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Failed to decode base64 encoded salt: %m.");
/* The "rp" field is optional. */
w = json_variant_by_key(v, "fido2-rp");
if (w && !json_variant_is_string(w)) {
crypt_log_debug(cd, "FIDO2 token data's 'fido2-rp' field is not a string.");
return 1;
}
/* The "fido2-clientPin-required" field is optional. */
w = json_variant_by_key(v, "fido2-clientPin-required");
if (w && !json_variant_is_boolean(w)) {
crypt_log_debug(cd, "FIDO2 token data's 'fido2-clientPin-required' field is not a boolean.");
return 1;
}
/* The "fido2-up-required" field is optional. */
w = json_variant_by_key(v, "fido2-up-required");
if (w && !json_variant_is_boolean(w)) {
crypt_log_debug(cd, "FIDO2 token data's 'fido2-up-required' field is not a boolean.");
return 1;
}
/* The "fido2-uv-required" field is optional. */
w = json_variant_by_key(v, "fido2-uv-required");
if (w && !json_variant_is_boolean(w)) {
crypt_log_debug(cd, "FIDO2 token data's 'fido2-uv-required' field is not a boolean.");
return 1;
}
return 0;
}

View File

@ -0,0 +1,143 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
#include <libcryptsetup.h>
#include "cryptsetup-token.h"
#include "cryptsetup-token-util.h"
#include "hexdecoct.h"
#include "json.h"
#include "luks2-pkcs11.h"
#include "memory-util.h"
#include "pkcs11-util.h"
#include "version.h"
#define TOKEN_NAME "systemd-pkcs11"
#define TOKEN_VERSION_MAJOR "1"
#define TOKEN_VERSION_MINOR "0"
/* for libcryptsetup debug purpose */
_public_ const char *cryptsetup_token_version(void) {
return TOKEN_VERSION_MAJOR "." TOKEN_VERSION_MINOR " systemd-v" STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")";
}
_public_ int cryptsetup_token_open_pin(
struct crypt_device *cd, /* is always LUKS2 context */
int token /* is always >= 0 */,
const char *pin,
size_t pin_size,
char **password, /* freed by cryptsetup_token_buffer_free */
size_t *password_len,
void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
const char *json;
int r;
assert(!pin || pin_size);
assert(token >= 0);
/* This must not fail at this moment (internal error) */
r = crypt_token_json_get(cd, token, &json);
assert(token == r);
assert(json);
return acquire_luks2_key(cd, json, usrptr, pin, pin_size, password, password_len);
}
/*
* This function is called from within following libcryptsetup calls
* provided conditions further below are met:
*
* crypt_activate_by_token(), crypt_activate_by_token_type(type == 'systemd-pkcs11'):
*
* - token is assigned to at least one luks2 keyslot eligible to activate LUKS2 device
* (alternatively: name is set to null, flags contains CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY
* and token is assigned to at least single keyslot).
*
* - if plugin defines validate funtion (see cryptsetup_token_validate below) it must have
* passed the check (aka return 0)
*/
_public_ int cryptsetup_token_open(
struct crypt_device *cd, /* is always LUKS2 context */
int token /* is always >= 0 */,
char **password, /* freed by cryptsetup_token_buffer_free */
size_t *password_len,
void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
return cryptsetup_token_open_pin(cd, token, NULL, 0, password, password_len, usrptr);
}
/*
* libcryptsetup callback for memory deallocation of 'password' parameter passed in
* any crypt_token_open_* plugin function
*/
_public_ void cryptsetup_token_buffer_free(void *buffer, size_t buffer_len) {
erase_and_free(buffer);
}
/*
* prints systemd-pkcs11 token content in crypt_dump().
* 'type' and 'keyslots' fields are printed by libcryptsetup
*/
_public_ void cryptsetup_token_dump(
struct crypt_device *cd /* is always LUKS2 context */,
const char *json /* validated 'systemd-pkcs11' token if cryptsetup_token_validate is defined */) {
int r;
size_t pkcs11_key_size;
_cleanup_free_ char *pkcs11_uri = NULL, *key_str = NULL;
_cleanup_free_ void *pkcs11_key = NULL;
r = parse_luks2_pkcs11_data(cd, json, &pkcs11_uri, &pkcs11_key, &pkcs11_key_size);
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " metadata: %m.");
r = crypt_dump_buffer_to_hex_string(pkcs11_key, pkcs11_key_size, &key_str);
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Can not dump " TOKEN_NAME " content: %m");
crypt_log(cd, "\tpkcs11-uri: %s\n", pkcs11_uri);
crypt_log(cd, "\tpkcs11-key: %s\n", key_str);
}
/*
* Note:
* If plugin is available in library path, it's called in before following libcryptsetup calls:
*
* crypt_token_json_set, crypt_dump, any crypt_activate_by_token_* flavour
*/
_public_ int cryptsetup_token_validate(
struct crypt_device *cd, /* is always LUKS2 context */
const char *json /* contains valid 'type' and 'keyslots' fields. 'type' is 'systemd-pkcs11' */) {
int r;
JsonVariant *w;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
r = json_parse(json, 0, &v, NULL, NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Could not parse " TOKEN_NAME " json object: %m.");
w = json_variant_by_key(v, "pkcs11-uri");
if (!w || !json_variant_is_string(w)) {
crypt_log_debug(cd, "PKCS#11 token data lacks 'pkcs11-uri' field.");
return 1;
}
if (!pkcs11_uri_valid(json_variant_string(w))) {
crypt_log_debug(cd, "PKCS#11 token data contains invalid PKCS#11 URI.");
return 1;
}
w = json_variant_by_key(v, "pkcs11-key");
if (!w || !json_variant_is_string(w)) {
crypt_log_debug(cd, "PKCS#11 token data lacks 'pkcs11-key' field.");
return 1;
}
r = unbase64mem(json_variant_string(w), SIZE_MAX, NULL, NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Failed to decode base64 encoded key: %m.");
return 0;
}

View File

@ -2,23 +2,34 @@
#pragma once #pragma once
#include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <libcryptsetup.h>
/* crypt_dump() internal indentation magic */ /* crypt_dump() internal indentation magic */
#define CRYPT_DUMP_LINE_SEP "\n\t " #define CRYPT_DUMP_LINE_SEP "\n\t "
#define crypt_log_debug(cd, ...) crypt_logf(cd, CRYPT_LOG_DEBUG, __VA_ARGS__) #define crypt_log_debug(cd, ...) crypt_logf(cd, CRYPT_LOG_DEBUG, __VA_ARGS__)
#define crypt_log_error(cd, ...) crypt_logf(cd, CRYPT_LOG_ERROR, __VA_ARGS__) #define crypt_log_error(cd, ...) crypt_logf(cd, CRYPT_LOG_ERROR, __VA_ARGS__)
#define crypt_log(cd, ...) crypt_logf(cd, CRYPT_LOG_NORMAL, __VA_ARGS__) #define crypt_log_verbose(cd, ...) crypt_logf(cd, CRYPT_LOG_VERBOSE, __VA_ARGS__)
#define crypt_log(cd, ...) crypt_logf(cd, CRYPT_LOG_NORMAL, __VA_ARGS__)
#define crypt_log_debug_errno(cd, e, ...) ({ \ #define crypt_log_full_errno(cd, e, lvl, ...) ({ \
int _e = abs(e), _s = errno; \ int _e = abs(e), _s = errno; \
errno = _e; \ errno = _e; \
crypt_logf(cd, CRYPT_LOG_DEBUG, __VA_ARGS__); \ crypt_logf(cd, lvl, __VA_ARGS__); \
errno = _s; \ errno = _s; \
-_e; \ -_e; \
}) })
#define crypt_log_debug_errno(cd, e, ...) \
crypt_log_full_errno(cd, e, CRYPT_LOG_DEBUG, __VA_ARGS__)
#define crypt_log_error_errno(cd, e, ...) \
crypt_log_full_errno(cd, e, CRYPT_LOG_ERROR, __VA_ARGS__)
#define crypt_log_oom(cd) crypt_log_error_errno(cd, ENOMEM, "Not enough memory.")
int crypt_dump_buffer_to_hex_string( int crypt_dump_buffer_to_hex_string(
const char *buf, const char *buf,
size_t buf_size, size_t buf_size,

View File

@ -8,6 +8,10 @@ const char *cryptsetup_token_version(void);
int cryptsetup_token_open(struct crypt_device *cd, int token, int cryptsetup_token_open(struct crypt_device *cd, int token,
char **password, size_t *password_len, void *usrptr); char **password, size_t *password_len, void *usrptr);
int cryptsetup_token_open_pin(struct crypt_device *cd, int token,
const char *pin, size_t pin_size,
char **password, size_t *password_len, void *usrptr);
void cryptsetup_token_dump(struct crypt_device *cd, const char *json); void cryptsetup_token_dump(struct crypt_device *cd, const char *json);
int cryptsetup_token_validate(struct crypt_device *cd, const char *json); int cryptsetup_token_validate(struct crypt_device *cd, const char *json);

View File

@ -10,6 +10,7 @@
CRYPTSETUP_TOKEN_1.0 { CRYPTSETUP_TOKEN_1.0 {
global: global:
cryptsetup_token_open; cryptsetup_token_open;
cryptsetup_token_open_pin;
cryptsetup_token_buffer_free; cryptsetup_token_buffer_free;
cryptsetup_token_validate; cryptsetup_token_validate;
cryptsetup_token_dump; cryptsetup_token_dump;

View File

@ -0,0 +1,158 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <libcryptsetup.h>
#include "cryptsetup-token-util.h"
#include "hexdecoct.h"
#include "json.h"
#include "luks2-fido2.h"
#include "memory-util.h"
#include "strv.h"
int acquire_luks2_key(
struct crypt_device *cd,
const char *json,
const char *device,
const char *pin,
char **ret_keyslot_passphrase,
size_t *ret_keyslot_passphrase_size) {
int r;
Fido2EnrollFlags required;
size_t cid_size, salt_size, decrypted_key_size;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
_cleanup_free_ void *cid = NULL, *salt = NULL;
_cleanup_free_ char *rp_id = NULL;
_cleanup_(erase_and_freep) void *decrypted_key = NULL;
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
_cleanup_strv_free_erase_ char **pins = NULL;
assert(ret_keyslot_passphrase);
assert(ret_keyslot_passphrase_size);
r = parse_luks2_fido2_data(cd, json, &rp_id, &salt, &salt_size, &cid, &cid_size, &required);
if (r < 0)
return r;
if (pin) {
pins = strv_new(pin);
if (!pins)
return crypt_log_oom(cd);
}
/* configured to use pin but none was provided */
if ((required & FIDO2ENROLL_PIN) && strv_isempty(pins))
return -ENOANO;
r = fido2_use_hmac_hash(
device,
rp_id ?: "io.systemd.cryptsetup",
salt, salt_size,
cid, cid_size,
pins,
required,
&decrypted_key,
&decrypted_key_size);
if (r == -ENOLCK) /* libcryptsetup returns -ENOANO also on wrong pin */
r = -ENOANO;
if (r < 0)
return r;
/* Before using this key as passphrase we base64 encode it, for compat with homed */
r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
if (r < 0)
return crypt_log_error_errno(cd, r, "Can not base64 encode key: %m");
*ret_keyslot_passphrase = TAKE_PTR(base64_encoded);
*ret_keyslot_passphrase_size = strlen(*ret_keyslot_passphrase);
return 0;
}
/* this function expects valid "systemd-fido2" in json */
int parse_luks2_fido2_data(
struct crypt_device *cd,
const char *json,
char **ret_rp_id,
void **ret_salt,
size_t *ret_salt_size,
void **ret_cid,
size_t *ret_cid_size,
Fido2EnrollFlags *ret_required) {
_cleanup_free_ void *cid = NULL, *salt = NULL;
size_t cid_size = 0, salt_size = 0;
_cleanup_free_ char *rp = NULL;
int r;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
JsonVariant *w;
Fido2EnrollFlags required = 0;
assert(json);
assert(ret_rp_id);
assert(ret_salt);
assert(ret_salt_size);
assert(ret_cid);
assert(ret_cid_size);
assert(ret_required);
r = json_parse(json, 0, &v, NULL, NULL);
if (r < 0)
return crypt_log_error_errno(cd, r, "Failed to parse JSON token data: %m");
w = json_variant_by_key(v, "fido2-credential");
if (!w)
return -EINVAL;
r = unbase64mem(json_variant_string(w), SIZE_MAX, &cid, &cid_size);
if (r < 0)
return crypt_log_error_errno(cd, r, "Failed to parse 'fido2-credentials' field: %m");
w = json_variant_by_key(v, "fido2-salt");
if (!w)
return -EINVAL;
r = unbase64mem(json_variant_string(w), SIZE_MAX, &salt, &salt_size);
if (r < 0)
return crypt_log_error_errno(cd, r, "Failed to parse 'fido2-salt' field: %m");
w = json_variant_by_key(v, "fido2-rp");
if (w) {
/* The "rp" field is optional. */
rp = strdup(json_variant_string(w));
if (!rp) {
crypt_log_error(cd, "Not enough memory.");
return -ENOMEM;
}
}
w = json_variant_by_key(v, "fido2-clientPin-required");
if (w)
/* The "fido2-clientPin-required" field is optional. */
SET_FLAG(required, FIDO2ENROLL_PIN, json_variant_boolean(w));
else
required |= FIDO2ENROLL_PIN_IF_NEEDED; /* compat with 248, where the field was unset */
w = json_variant_by_key(v, "fido2-up-required");
if (w)
/* The "fido2-up-required" field is optional. */
SET_FLAG(required, FIDO2ENROLL_UP, json_variant_boolean(w));
else
required |= FIDO2ENROLL_UP_IF_NEEDED; /* compat with 248 */
w = json_variant_by_key(v, "fido2-uv-required");
if (w)
/* The "fido2-uv-required" field is optional. */
SET_FLAG(required, FIDO2ENROLL_UV, json_variant_boolean(w));
else
required |= FIDO2ENROLL_UV_OMIT; /* compat with 248 */
*ret_rp_id = TAKE_PTR(rp);
*ret_cid = TAKE_PTR(cid);
*ret_cid_size = cid_size;
*ret_salt = TAKE_PTR(salt);
*ret_salt_size = salt_size;
*ret_required = required;
return 0;
}

View File

@ -0,0 +1,24 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "libfido2-util.h"
struct crypt_device;
int acquire_luks2_key(
struct crypt_device *cd,
const char *json,
const char *device,
const char *pin,
char **ret_keyslot_passphrase,
size_t *ret_keyslot_passphrase_size);
int parse_luks2_fido2_data(
struct crypt_device *cd,
const char *json,
char **ret_rp_id,
void **ret_salt,
size_t *ret_salt_size,
void **ret_cid,
size_t *ret_cid_size,
Fido2EnrollFlags *ret_required);

View File

@ -0,0 +1,271 @@
/* 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 = userdata;
assert(m);
assert(slot_info);
assert(token_info);
assert(uri);
assert(data);
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;
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;
r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
if (r < 0)
return crypt_log_error_errno(cd, r, "Can not base64 encode key: %m");
*ret_password = TAKE_PTR(base64_encoded);
*ret_password_size = strlen(*ret_password);
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;
}

View File

@ -0,0 +1,21 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
struct crypt_device;
int acquire_luks2_key(
struct crypt_device *cd,
const char *json,
void *userdata,
const void *pin,
size_t pin_size,
char **password,
size_t *password_size);
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);

View File

@ -25,4 +25,40 @@ if conf.get('HAVE_TPM2') == 1
c_args : cryptsetup_token_c_args) c_args : cryptsetup_token_c_args)
endif endif
if conf.get('HAVE_LIBFIDO2') == 1
cryptsetup_token_systemd_fido2_sources = files('''
cryptsetup-token-systemd-fido2.c
cryptsetup-token.h
cryptsetup-token-util.h
cryptsetup-token-util.c
luks2-fido2.c
luks2-fido2.h
'''.split())
cryptsetup_token_systemd_fido2_static = static_library(
'cryptsetup-token-systemd-fido2_static',
cryptsetup_token_systemd_fido2_sources,
include_directories : includes,
dependencies : libshared_deps + [libcryptsetup, versiondep],
c_args : cryptsetup_token_c_args)
endif
if conf.get('HAVE_P11KIT') == 1
cryptsetup_token_systemd_pkcs11_sources = files('''
cryptsetup-token-systemd-pkcs11.c
cryptsetup-token.h
cryptsetup-token-util.h
cryptsetup-token-util.c
luks2-pkcs11.c
luks2-pkcs11.h
'''.split())
cryptsetup_token_systemd_pkcs11_static = static_library(
'cryptsetup-token-systemd-pkcs11_static',
cryptsetup_token_systemd_pkcs11_sources,
include_directories : includes,
dependencies : libshared_deps + [libcryptsetup, versiondep],
c_args : cryptsetup_token_c_args)
endif
endif endif

View File

@ -736,6 +736,105 @@ static int make_security_device_monitor(sd_event *event, sd_device_monitor **ret
return 0; return 0;
} }
static bool libcryptsetup_plugins_support(void) {
#if HAVE_LIBCRYPTSETUP_PLUGINS
return crypt_token_external_path() != NULL;
#else
return false;
#endif
}
#if HAVE_LIBCRYPTSETUP_PLUGINS
static int acquire_pins_from_env_variable(char ***ret_pins) {
char *e;
_cleanup_strv_free_erase_ char **pins = NULL;
assert(ret_pins);
e = getenv("PIN");
if (e) {
pins = strv_new(e);
if (!pins)
return log_oom();
string_erase(e);
if (unsetenv("PIN") < 0)
return log_error_errno(errno, "Failed to unset $PIN: %m");
}
*ret_pins = TAKE_PTR(pins);
return 0;
}
#endif
static int attach_luks2_by_fido2(
struct crypt_device *cd,
const char *name,
usec_t until,
bool headless,
void *usrptr,
uint32_t activation_flags) {
int r = -EOPNOTSUPP;
#if HAVE_LIBCRYPTSETUP_PLUGINS
char **p;
_cleanup_strv_free_erase_ char **pins = NULL;
AskPasswordFlags flags = ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_ACCEPT_CACHED;
r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, NULL, 0, usrptr, activation_flags);
if (r > 0) /* returns unlocked keyslot id on success */
r = 0;
if (r != -ENOANO) /* needs pin or pin is wrong */
return r;
r = acquire_pins_from_env_variable(&pins);
if (r < 0)
return r;
STRV_FOREACH(p, pins) {
r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, *p, strlen(*p), usrptr, activation_flags);
if (r > 0) /* returns unlocked keyslot id on success */
r = 0;
if (r != -ENOANO) /* needs pin or pin is wrong */
return r;
}
if (headless)
return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the '$PIN' environment variable.");
pins = strv_free_erase(pins);
r = ask_password_auto("Please enter security token PIN:", "drive-harddisk", NULL, "fido2-pin", "cryptsetup.fido2-pin", until, flags, &pins);
if (r < 0)
return r;
STRV_FOREACH(p, pins) {
r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, *p, strlen(*p), usrptr, activation_flags);
if (r > 0) /* returns unlocked keyslot id on success */
r = 0;
if (r != -ENOANO) /* needs pin or pin is wrong */
return r;
}
flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
for (;;) {
pins = strv_free_erase(pins);
r = ask_password_auto("Please enter security token PIN:", "drive-harddisk", NULL, "fido2-pin", "cryptsetup.fido2-pin", until, flags, &pins);
if (r < 0)
return r;
STRV_FOREACH(p, pins) {
r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, *p, strlen(*p), usrptr, activation_flags);
if (r > 0) /* returns unlocked keyslot id on success */
r = 0;
if (r != -ENOANO) /* needs pin or pin is wrong */
return r;
}
}
#endif
return r;
}
static int attach_luks_or_plain_or_bitlk_by_fido2( static int attach_luks_or_plain_or_bitlk_by_fido2(
struct crypt_device *cd, struct crypt_device *cd,
const char *name, const char *name,
@ -750,12 +849,13 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
_cleanup_(erase_and_freep) void *decrypted_key = NULL; _cleanup_(erase_and_freep) void *decrypted_key = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL; _cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_free_ void *discovered_salt = NULL, *discovered_cid = NULL; _cleanup_free_ void *discovered_salt = NULL, *discovered_cid = NULL;
size_t discovered_salt_size, discovered_cid_size, cid_size, decrypted_key_size; size_t discovered_salt_size, discovered_cid_size, decrypted_key_size, cid_size = 0;
_cleanup_free_ char *friendly = NULL, *discovered_rp_id = NULL; _cleanup_free_ char *friendly = NULL, *discovered_rp_id = NULL;
int keyslot = arg_key_slot, r; int keyslot = arg_key_slot, r;
const char *rp_id; const char *rp_id = NULL;
const void *cid; const void *cid = NULL;
Fido2EnrollFlags required; Fido2EnrollFlags required;
bool use_libcryptsetup_plugin = libcryptsetup_plugins_support();
assert(cd); assert(cd);
assert(name); assert(name);
@ -775,7 +875,7 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
* use PIN + UP when needed, and do not configure UV at all. Eventually, we should make this * use PIN + UP when needed, and do not configure UV at all. Eventually, we should make this
* explicitly configurable. */ * explicitly configurable. */
required = FIDO2ENROLL_PIN_IF_NEEDED | FIDO2ENROLL_UP_IF_NEEDED | FIDO2ENROLL_UV_OMIT; required = FIDO2ENROLL_PIN_IF_NEEDED | FIDO2ENROLL_UP_IF_NEEDED | FIDO2ENROLL_UV_OMIT;
} else { } else if (!use_libcryptsetup_plugin) {
r = find_fido2_auto_data( r = find_fido2_auto_data(
cd, cd,
&discovered_rp_id, &discovered_rp_id,
@ -810,21 +910,30 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
for (;;) { for (;;) {
bool processed = false; bool processed = false;
r = acquire_fido2_key( if (use_libcryptsetup_plugin && !arg_fido2_cid) {
name, r = attach_luks2_by_fido2(cd, name, until, arg_headless, arg_fido2_device, flags);
friendly, if (IN_SET(r, -ENOTUNIQ, -ENXIO, -ENOENT))
arg_fido2_device, return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
rp_id, "Automatic FIDO2 metadata discovery was not possible because missing or not unique, falling back to traditional unlocking.");
cid, cid_size,
key_file, arg_keyfile_size, arg_keyfile_offset, } else {
key_data, key_data_size, r = acquire_fido2_key(
until, name,
arg_headless, friendly,
required, arg_fido2_device,
&decrypted_key, &decrypted_key_size, rp_id,
arg_ask_password_flags); cid, cid_size,
if (r >= 0) key_file, arg_keyfile_size, arg_keyfile_offset,
break; key_data, key_data_size,
until,
arg_headless,
required,
&decrypted_key, &decrypted_key_size,
arg_ask_password_flags);
if (r >= 0)
break;
}
if (r != -EAGAIN) /* EAGAIN means: token not found */ if (r != -EAGAIN) /* EAGAIN means: token not found */
return r; return r;
@ -887,6 +996,32 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
return 0; return 0;
} }
static int attach_luks2_by_pkcs11(
struct crypt_device *cd,
const char *name,
const char *friendly_name,
usec_t until,
bool headless,
uint32_t flags) {
int r = -ENOTSUP;
#if HAVE_LIBCRYPTSETUP_PLUGINS
if (!crypt_get_type(cd) || strcmp(crypt_get_type(cd), CRYPT_LUKS2))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Automatic PKCS#11 metadata requires LUKS2 device.");
systemd_pkcs11_plugin_params params = {
.friendly_name = friendly_name,
.until = until,
.headless = headless
};
r = crypt_activate_by_token_pin(cd, name, "systemd-pkcs11", CRYPT_ANY_TOKEN, NULL, 0, &params, flags);
if (r > 0) /* returns unlocked keyslot id on success */
r = 0;
#endif
return r;
}
static int attach_luks_or_plain_or_bitlk_by_pkcs11( static int attach_luks_or_plain_or_bitlk_by_pkcs11(
struct crypt_device *cd, struct crypt_device *cd,
const char *name, const char *name,
@ -904,23 +1039,26 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
_cleanup_(sd_event_unrefp) sd_event *event = NULL; _cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_free_ void *discovered_key = NULL; _cleanup_free_ void *discovered_key = NULL;
int keyslot = arg_key_slot, r; int keyslot = arg_key_slot, r;
const char *uri; const char *uri = NULL;
bool use_libcryptsetup_plugin = libcryptsetup_plugins_support();
assert(cd); assert(cd);
assert(name); assert(name);
assert(arg_pkcs11_uri || arg_pkcs11_uri_auto); assert(arg_pkcs11_uri || arg_pkcs11_uri_auto);
if (arg_pkcs11_uri_auto) { if (arg_pkcs11_uri_auto) {
r = find_pkcs11_auto_data(cd, &discovered_uri, &discovered_key, &discovered_key_size, &keyslot); if (!use_libcryptsetup_plugin) {
if (IN_SET(r, -ENOTUNIQ, -ENXIO)) r = find_pkcs11_auto_data(cd, &discovered_uri, &discovered_key, &discovered_key_size, &keyslot);
return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), if (IN_SET(r, -ENOTUNIQ, -ENXIO))
"Automatic PKCS#11 metadata discovery was not possible because missing or not unique, falling back to traditional unlocking."); return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
if (r < 0) "Automatic PKCS#11 metadata discovery was not possible because missing or not unique, falling back to traditional unlocking.");
return r; if (r < 0)
return r;
uri = discovered_uri; uri = discovered_uri;
key_data = discovered_key; key_data = discovered_key;
key_data_size = discovered_key_size; key_data_size = discovered_key_size;
}
} else { } else {
uri = arg_pkcs11_uri; uri = arg_pkcs11_uri;
@ -935,17 +1073,22 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
for (;;) { for (;;) {
bool processed = false; bool processed = false;
r = decrypt_pkcs11_key( if (use_libcryptsetup_plugin && arg_pkcs11_uri_auto)
name, r = attach_luks2_by_pkcs11(cd, name, friendly, until, arg_headless, flags);
friendly, else {
uri, r = decrypt_pkcs11_key(
key_file, arg_keyfile_size, arg_keyfile_offset, name,
key_data, key_data_size, friendly,
until, uri,
arg_headless, key_file, arg_keyfile_size, arg_keyfile_offset,
&decrypted_key, &decrypted_key_size); key_data, key_data_size,
if (r >= 0) until,
break; arg_headless,
&decrypted_key, &decrypted_key_size);
if (r >= 0)
break;
}
if (r != -EAGAIN) /* EAGAIN means: token not found */ if (r != -EAGAIN) /* EAGAIN means: token not found */
return r; return r;
@ -985,6 +1128,7 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
log_debug("Got one or more potentially relevant udev events, rescanning PKCS#11..."); log_debug("Got one or more potentially relevant udev events, rescanning PKCS#11...");
} }
assert(decrypted_key);
if (pass_volume_key) if (pass_volume_key)
r = crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags); r = crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags);

View File

@ -22,7 +22,6 @@
#if HAVE_ACL #if HAVE_ACL
static int get_acl(int fd, const char *name, acl_type_t type, acl_t *ret) { static int get_acl(int fd, const char *name, acl_type_t type, acl_t *ret) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
acl_t acl; acl_t acl;
assert(fd >= 0); assert(fd >= 0);
@ -35,14 +34,11 @@ static int get_acl(int fd, const char *name, acl_type_t type, acl_t *ret) {
if (child_fd < 0) if (child_fd < 0)
return -errno; return -errno;
xsprintf(procfs_path, "/proc/self/fd/%i", child_fd); acl = acl_get_file(FORMAT_PROC_FD_PATH(child_fd), type);
acl = acl_get_file(procfs_path, type);
} else if (type == ACL_TYPE_ACCESS) } else if (type == ACL_TYPE_ACCESS)
acl = acl_get_fd(fd); acl = acl_get_fd(fd);
else { else
xsprintf(procfs_path, "/proc/self/fd/%i", fd); acl = acl_get_file(FORMAT_PROC_FD_PATH(fd), type);
acl = acl_get_file(procfs_path, type);
}
if (!acl) if (!acl)
return -errno; return -errno;
@ -51,7 +47,6 @@ static int get_acl(int fd, const char *name, acl_type_t type, acl_t *ret) {
} }
static int set_acl(int fd, const char *name, acl_type_t type, acl_t acl) { static int set_acl(int fd, const char *name, acl_type_t type, acl_t acl) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
int r; int r;
assert(fd >= 0); assert(fd >= 0);
@ -64,14 +59,11 @@ static int set_acl(int fd, const char *name, acl_type_t type, acl_t acl) {
if (child_fd < 0) if (child_fd < 0)
return -errno; return -errno;
xsprintf(procfs_path, "/proc/self/fd/%i", child_fd); r = acl_set_file(FORMAT_PROC_FD_PATH(child_fd), type, acl);
r = acl_set_file(procfs_path, type, acl);
} else if (type == ACL_TYPE_ACCESS) } else if (type == ACL_TYPE_ACCESS)
r = acl_set_fd(fd, acl); r = acl_set_fd(fd, acl);
else { else
xsprintf(procfs_path, "/proc/self/fd/%i", fd); r = acl_set_file(FORMAT_PROC_FD_PATH(fd), type, acl);
r = acl_set_file(procfs_path, type, acl);
}
if (r < 0) if (r < 0)
return -errno; return -errno;

View File

@ -1581,12 +1581,8 @@ static int context_load_partition_table(
* /proc/self/fd/ magic path if we have an existing fd. Open the original file otherwise. */ * /proc/self/fd/ magic path if we have an existing fd. Open the original file otherwise. */
if (*backing_fd < 0) if (*backing_fd < 0)
r = fdisk_assign_device(c, node, arg_dry_run); r = fdisk_assign_device(c, node, arg_dry_run);
else { else
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; r = fdisk_assign_device(c, FORMAT_PROC_FD_PATH(*backing_fd), arg_dry_run);
xsprintf(procfs_path, "/proc/self/fd/%i", *backing_fd);
r = fdisk_assign_device(c, procfs_path, arg_dry_run);
}
if (r == -EINVAL && arg_size_auto) { if (r == -EINVAL && arg_size_auto) {
struct stat st; struct stat st;
@ -4593,7 +4589,6 @@ static int find_root(char **ret, int *ret_fd) {
} }
static int resize_pt(int fd) { static int resize_pt(int fd) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
_cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL; _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
int r; int r;
@ -4605,14 +4600,13 @@ static int resize_pt(int fd) {
if (!c) if (!c)
return log_oom(); return log_oom();
xsprintf(procfs_path, "/proc/self/fd/%i", fd); r = fdisk_assign_device(c, FORMAT_PROC_FD_PATH(fd), 0);
r = fdisk_assign_device(c, procfs_path, 0);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to open device '%s': %m", procfs_path); return log_error_errno(r, "Failed to open device '%s': %m", FORMAT_PROC_FD_PATH(fd));
r = fdisk_has_label(c); r = fdisk_has_label(c);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to determine whether disk '%s' has a disk label: %m", procfs_path); return log_error_errno(r, "Failed to determine whether disk '%s' has a disk label: %m", FORMAT_PROC_FD_PATH(fd));
if (r == 0) { if (r == 0) {
log_debug("Not resizing partition table, as there currently is none."); log_debug("Not resizing partition table, as there currently is none.");
return 0; return 0;

View File

@ -21,7 +21,6 @@ static int chown_one(
gid_t gid, gid_t gid,
mode_t mask) { mode_t mask) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
const char *n; const char *n;
int r; int r;
@ -30,11 +29,10 @@ static int chown_one(
/* We change ACLs through the /proc/self/fd/%i path, so that we have a stable reference that works /* We change ACLs through the /proc/self/fd/%i path, so that we have a stable reference that works
* with O_PATH. */ * with O_PATH. */
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
/* Drop any ACL if there is one */ /* Drop any ACL if there is one */
FOREACH_STRING(n, "system.posix_acl_access", "system.posix_acl_default") FOREACH_STRING(n, "system.posix_acl_access", "system.posix_acl_default")
if (removexattr(procfs_path, n) < 0) if (removexattr(FORMAT_PROC_FD_PATH(fd), n) < 0)
if (!IN_SET(errno, ENODATA, EOPNOTSUPP, ENOSYS, ENOTTY)) if (!IN_SET(errno, ENODATA, EOPNOTSUPP, ENOSYS, ENOTTY))
return -errno; return -errno;

View File

@ -305,7 +305,7 @@ static int image_make(
} }
/* Get directory creation time (not available everywhere, but that's OK */ /* Get directory creation time (not available everywhere, but that's OK */
(void) fd_getcrtime(dfd, &crtime); (void) fd_getcrtime(fd, &crtime);
/* If the IMMUTABLE bit is set, we consider the directory read-only. Since the ioctl is not /* If the IMMUTABLE bit is set, we consider the directory read-only. Since the ioctl is not
* supported everywhere we ignore failures. */ * supported everywhere we ignore failures. */

View File

@ -42,10 +42,7 @@ int mount_fd(const char *source,
unsigned long mountflags, unsigned long mountflags,
const void *data) { const void *data) {
char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; if (mount(source, FORMAT_PROC_FD_PATH(target_fd), filesystemtype, mountflags, data) < 0) {
xsprintf(path, "/proc/self/fd/%i", target_fd);
if (mount(source, path, filesystemtype, mountflags, data) < 0) {
if (errno != ENOENT) if (errno != ENOENT)
return -errno; return -errno;
@ -733,8 +730,7 @@ static int mount_in_namespace(
_cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
_cleanup_close_ int self_mntns_fd = -1, mntns_fd = -1, root_fd = -1, pidns_fd = -1, chased_src_fd = -1; _cleanup_close_ int self_mntns_fd = -1, mntns_fd = -1, root_fd = -1, pidns_fd = -1, chased_src_fd = -1;
char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p, char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
chased_src[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
bool mount_slave_created = false, mount_slave_mounted = false, bool mount_slave_created = false, mount_slave_mounted = false,
mount_tmp_created = false, mount_tmp_mounted = false, mount_tmp_created = false, mount_tmp_mounted = false,
mount_outside_created = false, mount_outside_mounted = false; mount_outside_created = false, mount_outside_mounted = false;
@ -767,9 +763,8 @@ static int mount_in_namespace(
if (st.st_ino == self_mntns_st.st_ino && st.st_dev == self_mntns_st.st_dev) if (st.st_ino == self_mntns_st.st_ino && st.st_dev == self_mntns_st.st_dev)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to activate bind mount in target, not running in a mount namespace"); return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to activate bind mount in target, not running in a mount namespace");
/* One day, when bind mounting /proc/self/fd/n works across /* One day, when bind mounting /proc/self/fd/n works across namespace boundaries we should rework
* namespace boundaries we should rework this logic to make * this logic to make use of it... */
* use of it... */
p = strjoina(propagate_path, "/"); p = strjoina(propagate_path, "/");
r = laccess(p, F_OK); r = laccess(p, F_OK);
@ -779,7 +774,6 @@ static int mount_in_namespace(
r = chase_symlinks(src, NULL, CHASE_TRAIL_SLASH, NULL, &chased_src_fd); r = chase_symlinks(src, NULL, CHASE_TRAIL_SLASH, NULL, &chased_src_fd);
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to resolve source path of %s: %m", src); return log_debug_errno(r, "Failed to resolve source path of %s: %m", src);
xsprintf(chased_src, "/proc/self/fd/%i", chased_src_fd);
if (fstat(chased_src_fd, &st) < 0) if (fstat(chased_src_fd, &st) < 0)
return log_debug_errno(errno, "Failed to stat() resolved source path %s: %m", src); return log_debug_errno(errno, "Failed to stat() resolved source path %s: %m", src);
@ -824,9 +818,9 @@ static int mount_in_namespace(
mount_tmp_created = true; mount_tmp_created = true;
if (is_image) if (is_image)
r = verity_dissect_and_mount(chased_src, mount_tmp, options, NULL, NULL, NULL); r = verity_dissect_and_mount(FORMAT_PROC_FD_PATH(chased_src_fd), mount_tmp, options, NULL, NULL, NULL);
else else
r = mount_follow_verbose(LOG_DEBUG, chased_src, mount_tmp, NULL, MS_BIND, NULL); r = mount_follow_verbose(LOG_DEBUG, FORMAT_PROC_FD_PATH(chased_src_fd), mount_tmp, NULL, MS_BIND, NULL);
if (r < 0) if (r < 0)
goto finish; goto finish;

View File

@ -175,6 +175,55 @@ char *pkcs11_token_model(const CK_TOKEN_INFO *token_info) {
return t; return t;
} }
int pkcs11_token_login_by_pin(
CK_FUNCTION_LIST *m,
CK_SESSION_HANDLE session,
const CK_TOKEN_INFO *token_info,
const char *token_label,
const void *pin,
size_t pin_size) {
CK_RV rv;
assert(m);
assert(token_info);
if (FLAGS_SET(token_info->flags, CKF_PROTECTED_AUTHENTICATION_PATH)) {
rv = m->C_Login(session, CKU_USER, NULL, 0);
if (rv != CKR_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
log_info("Successfully logged into security token '%s' via protected authentication path.", token_label);
return 0;
}
if (!FLAGS_SET(token_info->flags, CKF_LOGIN_REQUIRED)) {
log_info("No login into security token '%s' required.", token_label);
return 0;
}
if (!pin)
return -ENOANO;
rv = m->C_Login(session, CKU_USER, (CK_UTF8CHAR*) pin, pin_size);
if (rv == CKR_OK) {
log_info("Successfully logged into security token '%s'.", token_label);
return 0;
}
if (rv == CKR_PIN_LOCKED)
return log_error_errno(SYNTHETIC_ERRNO(EPERM),
"PIN has been locked, please reset PIN of security token '%s'.", token_label);
if (!IN_SET(rv, CKR_PIN_INCORRECT, CKR_PIN_LEN_RANGE))
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
log_notice("PIN for token '%s' is incorrect, please try again.", token_label);
return -ENOLCK;
}
int pkcs11_token_login( int pkcs11_token_login(
CK_FUNCTION_LIST *m, CK_FUNCTION_LIST *m,
CK_SESSION_HANDLE session, CK_SESSION_HANDLE session,
@ -209,24 +258,12 @@ int pkcs11_token_login(
if (uri_result != P11_KIT_URI_OK) if (uri_result != P11_KIT_URI_OK)
return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN), "Failed to format slot URI: %s", p11_kit_uri_message(uri_result)); return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN), "Failed to format slot URI: %s", p11_kit_uri_message(uri_result));
if (FLAGS_SET(token_info->flags, CKF_PROTECTED_AUTHENTICATION_PATH)) { r = pkcs11_token_login_by_pin(m, session, token_info, token_label, /* pin= */ NULL, 0);
rv = m->C_Login(session, CKU_USER, NULL, 0); if (r == 0 && ret_used_pin)
if (rv != CKR_OK) *ret_used_pin = NULL;
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
log_info("Successfully logged into security token '%s' via protected authentication path.", token_label); if (r != -ENOANO) /* pin required */
if (ret_used_pin) return r;
*ret_used_pin = NULL;
return 0;
}
if (!FLAGS_SET(token_info->flags, CKF_LOGIN_REQUIRED)) {
log_info("No login into security token '%s' required.", token_label);
if (ret_used_pin)
*ret_used_pin = NULL;
return 0;
}
token_uri_escaped = cescape(token_uri_string); token_uri_escaped = cescape(token_uri_string);
if (!token_uri_escaped) if (!token_uri_escaped)
@ -278,28 +315,19 @@ int pkcs11_token_login(
} }
STRV_FOREACH(i, passwords) { STRV_FOREACH(i, passwords) {
rv = m->C_Login(session, CKU_USER, (CK_UTF8CHAR*) *i, strlen(*i)); r = pkcs11_token_login_by_pin(m, session, token_info, token_label, *i, strlen(*i));
if (rv == CKR_OK) { if (r == 0 && ret_used_pin) {
char *c;
if (ret_used_pin) { c = strdup(*i);
char *c; if (!c)
return log_oom();
c = strdup(*i); *ret_used_pin = c;
if (!c)
return log_oom();
*ret_used_pin = c;
}
log_info("Successfully logged into security token '%s'.", token_label);
return 0;
} }
if (rv == CKR_PIN_LOCKED)
return log_error_errno(SYNTHETIC_ERRNO(EPERM), if (r != -ENOLCK)
"PIN has been locked, please reset PIN of security token '%s'.", token_label); return r;
if (!IN_SET(rv, CKR_PIN_INCORRECT, CKR_PIN_LEN_RANGE))
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
/* Referesh the token info, so that we can prompt knowing the new flags if they changed. */ /* Referesh the token info, so that we can prompt knowing the new flags if they changed. */
rv = m->C_GetTokenInfo(slotid, &updated_token_info); rv = m->C_GetTokenInfo(slotid, &updated_token_info);
@ -309,7 +337,6 @@ int pkcs11_token_login(
slotid, p11_kit_strerror(rv)); slotid, p11_kit_strerror(rv));
token_info = &updated_token_info; token_info = &updated_token_info;
log_notice("PIN for token '%s' is incorrect, please try again.", token_label);
} }
} }
@ -1154,3 +1181,71 @@ int pkcs11_find_token_auto(char **ret) {
"PKCS#11 tokens not supported on this build."); "PKCS#11 tokens not supported on this build.");
#endif #endif
} }
#if HAVE_P11KIT
void pkcs11_crypt_device_callback_data_release(pkcs11_crypt_device_callback_data *data) {
erase_and_free(data->decrypted_key);
if (data->free_encrypted_key)
free(data->encrypted_key);
}
int pkcs11_crypt_device_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) {
pkcs11_crypt_device_callback_data *data = userdata;
CK_OBJECT_HANDLE object;
int r;
assert(m);
assert(slot_info);
assert(token_info);
assert(uri);
assert(data);
/* Called for every token matching our URI */
r = pkcs11_token_login(
m,
session,
slot_id,
token_info,
data->friendly_name,
"drive-harddisk",
"pkcs11-pin",
"cryptsetup.pkcs11-pin",
data->until,
data->headless,
NULL);
if (r < 0)
return r;
/* We are likely called during early boot, where entropy is scarce. Mix some data from the PKCS#11
* token, if it supports that. It should be cheap, given that we already are talking to it anyway and
* shouldn't hurt. */
(void) pkcs11_token_acquire_rng(m, session);
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;
}
#endif

View File

@ -30,6 +30,7 @@ char *pkcs11_token_label(const CK_TOKEN_INFO *token_info);
char *pkcs11_token_manufacturer_id(const CK_TOKEN_INFO *token_info); char *pkcs11_token_manufacturer_id(const CK_TOKEN_INFO *token_info);
char *pkcs11_token_model(const CK_TOKEN_INFO *token_info); char *pkcs11_token_model(const CK_TOKEN_INFO *token_info);
int pkcs11_token_login_by_pin(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, const CK_TOKEN_INFO *token_info, const char *token_label, const void *pin, size_t pin_size);
int pkcs11_token_login(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_SLOT_ID slotid, const CK_TOKEN_INFO *token_info, const char *friendly_name, const char *icon_name, const char *key_name, const char *credential_name, usec_t until, bool headless, char **ret_used_pin); int pkcs11_token_login(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_SLOT_ID slotid, const CK_TOKEN_INFO *token_info, const char *friendly_name, const char *icon_name, const char *key_name, const char *credential_name, usec_t until, bool headless, char **ret_used_pin);
int pkcs11_token_find_x509_certificate(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, P11KitUri *search_uri, CK_OBJECT_HANDLE *ret_object); int pkcs11_token_find_x509_certificate(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, P11KitUri *search_uri, CK_OBJECT_HANDLE *ret_object);
@ -49,7 +50,35 @@ int pkcs11_find_token(const char *pkcs11_uri, pkcs11_find_token_callback_t callb
int pkcs11_acquire_certificate(const char *uri, const char *askpw_friendly_name, const char *askpw_icon_name, X509 **ret_cert, char **ret_pin_used); int pkcs11_acquire_certificate(const char *uri, const char *askpw_friendly_name, const char *askpw_icon_name, X509 **ret_cert, char **ret_pin_used);
#endif #endif
typedef struct {
const char *friendly_name;
usec_t until;
void *encrypted_key;
size_t encrypted_key_size;
void *decrypted_key;
size_t decrypted_key_size;
bool free_encrypted_key;
bool headless;
} pkcs11_crypt_device_callback_data;
void pkcs11_crypt_device_callback_data_release(pkcs11_crypt_device_callback_data *data);
int pkcs11_crypt_device_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);
#endif #endif
typedef struct {
const char *friendly_name;
usec_t until;
bool headless;
} systemd_pkcs11_plugin_params;
int pkcs11_list_tokens(void); int pkcs11_list_tokens(void);
int pkcs11_find_token_auto(char **ret); int pkcs11_find_token_auto(char **ret);

View File

@ -266,7 +266,6 @@ int mac_selinux_fix_container_fd(int fd, const char *path, const char *inside_pa
assert(inside_path); assert(inside_path);
#if HAVE_SELINUX #if HAVE_SELINUX
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
_cleanup_freecon_ char* fcon = NULL; _cleanup_freecon_ char* fcon = NULL;
struct stat st; struct stat st;
int r; int r;
@ -292,8 +291,7 @@ int mac_selinux_fix_container_fd(int fd, const char *path, const char *inside_pa
goto fail; goto fail;
} }
xsprintf(procfs_path, "/proc/self/fd/%i", fd); if (setfilecon_raw(FORMAT_PROC_FD_PATH(fd), fcon) < 0) {
if (setfilecon_raw(procfs_path, fcon) < 0) {
_cleanup_freecon_ char *oldcon = NULL; _cleanup_freecon_ char *oldcon = NULL;
/* If the FS doesn't support labels, then exit without warning */ /* If the FS doesn't support labels, then exit without warning */
@ -307,7 +305,7 @@ int mac_selinux_fix_container_fd(int fd, const char *path, const char *inside_pa
r = -errno; r = -errno;
/* If the old label is identical to the new one, suppress any kind of error */ /* If the old label is identical to the new one, suppress any kind of error */
if (getfilecon_raw(procfs_path, &oldcon) >= 0 && streq(fcon, oldcon)) if (getfilecon_raw(FORMAT_PROC_FD_PATH(fd), &oldcon) >= 0 && streq(fcon, oldcon))
return 0; return 0;
goto fail; goto fail;

View File

@ -121,8 +121,7 @@ int mac_smack_apply_pid(pid_t pid, const char *label) {
return r; return r;
} }
static int smack_fix_fd(int fd , const char *abspath, LabelFixFlags flags) { static int smack_fix_fd(int fd, const char *abspath, LabelFixFlags flags) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
const char *label; const char *label;
struct stat st; struct stat st;
int r; int r;
@ -153,8 +152,7 @@ static int smack_fix_fd(int fd , const char *abspath, LabelFixFlags flags) {
else else
return 0; return 0;
xsprintf(procfs_path, "/proc/self/fd/%i", fd); if (setxattr(FORMAT_PROC_FD_PATH(fd), "security.SMACK64", label, strlen(label), 0) < 0) {
if (setxattr(procfs_path, "security.SMACK64", label, strlen(label), 0) < 0) {
_cleanup_free_ char *old_label = NULL; _cleanup_free_ char *old_label = NULL;
r = -errno; r = -errno;
@ -168,7 +166,7 @@ static int smack_fix_fd(int fd , const char *abspath, LabelFixFlags flags) {
return 0; return 0;
/* If the old label is identical to the new one, suppress any kind of error */ /* If the old label is identical to the new one, suppress any kind of error */
if (getxattr_malloc(procfs_path, "security.SMACK64", &old_label, false) >= 0 && if (getxattr_malloc(FORMAT_PROC_FD_PATH(fd), "security.SMACK64", &old_label, false) >= 0 &&
streq(old_label, label)) streq(old_label, label))
return 0; return 0;

View File

@ -50,12 +50,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
else else
log_info(r == 0 ? "Done!" : "Action!"); log_info(r == 0 ? "Done!" : "Action!");
if (orig_stdout_fd >= 0) { if (orig_stdout_fd >= 0)
char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; assert_se(freopen(FORMAT_PROC_FD_PATH(orig_stdout_fd), "w", stdout));
xsprintf(path, "/proc/self/fd/%d", orig_stdout_fd);
assert_se(freopen(path, "w", stdout));
}
release_busses(); /* We open the bus for communication with logind. release_busses(); /* We open the bus for communication with logind.
* It needs to be closed to avoid apparent leaks. */ * It needs to be closed to avoid apparent leaks. */

View File

@ -278,6 +278,14 @@ static void test_close_all_fds(void) {
log_open(); log_open();
} }
static void test_format_proc_fd_path(void) {
assert_se(streq_ptr(FORMAT_PROC_FD_PATH(0), "/proc/self/fd/0"));
assert_se(streq_ptr(FORMAT_PROC_FD_PATH(1), "/proc/self/fd/1"));
assert_se(streq_ptr(FORMAT_PROC_FD_PATH(2), "/proc/self/fd/2"));
assert_se(streq_ptr(FORMAT_PROC_FD_PATH(3), "/proc/self/fd/3"));
assert_se(streq_ptr(FORMAT_PROC_FD_PATH(2147483647), "/proc/self/fd/2147483647"));
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG); test_setup_logging(LOG_DEBUG);
@ -290,6 +298,7 @@ int main(int argc, char *argv[]) {
test_rearrange_stdio(); test_rearrange_stdio();
test_read_nr_open(); test_read_nr_open();
test_close_all_fds(); test_close_all_fds();
test_format_proc_fd_path();
return 0; return 0;
} }

View File

@ -1058,18 +1058,15 @@ static int parse_xattrs_from_arg(Item *i) {
} }
static int fd_set_xattrs(Item *i, int fd, const char *path, const struct stat *st) { static int fd_set_xattrs(Item *i, int fd, const char *path, const struct stat *st) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
char **name, **value; char **name, **value;
assert(i); assert(i);
assert(fd >= 0); assert(fd >= 0);
assert(path); assert(path);
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
STRV_FOREACH_PAIR(name, value, i->xattrs) { STRV_FOREACH_PAIR(name, value, i->xattrs) {
log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path); log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path);
if (setxattr(procfs_path, *name, *value, strlen(*value), 0) < 0) if (setxattr(FORMAT_PROC_FD_PATH(fd), *name, *value, strlen(*value), 0) < 0)
return log_error_errno(errno, "Setting extended attribute %s=%s on %s failed: %m", return log_error_errno(errno, "Setting extended attribute %s=%s on %s failed: %m",
*name, *value, path); *name, *value, path);
} }
@ -1161,7 +1158,6 @@ static int path_set_acl(const char *path, const char *pretty, acl_type_t type, a
static int fd_set_acls(Item *item, int fd, const char *path, const struct stat *st) { static int fd_set_acls(Item *item, int fd, const char *path, const struct stat *st) {
int r = 0; int r = 0;
#if HAVE_ACL #if HAVE_ACL
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
struct stat stbuf; struct stat stbuf;
assert(item); assert(item);
@ -1184,14 +1180,12 @@ static int fd_set_acls(Item *item, int fd, const char *path, const struct stat *
return 0; return 0;
} }
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
if (item->acl_access) if (item->acl_access)
r = path_set_acl(procfs_path, path, ACL_TYPE_ACCESS, item->acl_access, item->append_or_force); r = path_set_acl(FORMAT_PROC_FD_PATH(fd), path, ACL_TYPE_ACCESS, item->acl_access, item->append_or_force);
/* set only default acls to folders */ /* set only default acls to folders */
if (r == 0 && item->acl_default && S_ISDIR(st->st_mode)) if (r == 0 && item->acl_default && S_ISDIR(st->st_mode))
r = path_set_acl(procfs_path, path, ACL_TYPE_DEFAULT, item->acl_default, item->append_or_force); r = path_set_acl(FORMAT_PROC_FD_PATH(fd), path, ACL_TYPE_DEFAULT, item->acl_default, item->append_or_force);
if (ERRNO_IS_NOT_SUPPORTED(r)) { if (ERRNO_IS_NOT_SUPPORTED(r)) {
log_debug_errno(r, "ACLs not supported by file system at %s", path); log_debug_errno(r, "ACLs not supported by file system at %s", path);
@ -1938,17 +1932,14 @@ static int item_do(Item *i, int fd, const char *path, fdaction_t action) {
r = action(i, fd, path, &st); r = action(i, fd, path, &st);
if (S_ISDIR(st.st_mode)) { if (S_ISDIR(st.st_mode)) {
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
_cleanup_closedir_ DIR *d = NULL; _cleanup_closedir_ DIR *d = NULL;
struct dirent *de; struct dirent *de;
/* The passed 'fd' was opened with O_PATH. We need to convert /* The passed 'fd' was opened with O_PATH. We need to convert it into a 'regular' fd before
* it into a 'regular' fd before reading the directory content. */ * reading the directory content. */
xsprintf(procfs_path, "/proc/self/fd/%i", fd); d = opendir(FORMAT_PROC_FD_PATH(fd));
d = opendir(procfs_path);
if (!d) { if (!d) {
log_error_errno(errno, "Failed to opendir() '%s': %m", procfs_path); log_error_errno(errno, "Failed to opendir() '%s': %m", FORMAT_PROC_FD_PATH(fd));
if (r == 0) if (r == 0)
r = -errno; r = -errno;
goto finish; goto finish;

View File

@ -11,7 +11,10 @@ TEST_NO_NSPAWN=1
test_append_files() { test_append_files() {
( (
local workspace="${1:?}" local workspace="${1:?}"
dracut_install busybox
# On openSUSE the static linked version of busybox is named "busybox-static".
busybox="$(type -P busybox-static || type -P busybox)"
inst_simple "$busybox" "$(dirname $busybox)/busybox"
if selinuxenabled >/dev/null; then if selinuxenabled >/dev/null; then
dracut_install selinuxenabled dracut_install selinuxenabled

View File

@ -7,18 +7,10 @@ set -o pipefail
root="${1:?Usage $0 container-root}" root="${1:?Usage $0 container-root}"
mkdir -p "$root" mkdir -p "$root"
mkdir "$root/bin" mkdir "$root/bin"
cp $(type -P busybox) "$root/bin"
os_release=$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release) # On openSUSE the static linked version of busybox is named "busybox-static".
ID_LIKE=$(awk -F= '$1=="ID_LIKE" { print $2 ;}' $os_release) busybox="$(type -P busybox-static || type -P busybox)"
if [[ "$ID_LIKE" = *"suse"* ]]; then cp "$busybox" "$root/bin/busybox"
mkdir -p "$root/lib"
mkdir -p "$root/lib64"
for lib in $(find /lib*/ld*); do
[[ -d $root/$(dirname $lib) ]] || mkdir -p $root/$(dirname $lib)
cp $lib $root/$lib
done
fi
mkdir -p "$root/usr/lib" mkdir -p "$root/usr/lib"
touch "$root/usr/lib/os-release" touch "$root/usr/lib/os-release"
@ -30,6 +22,7 @@ ln -s busybox "$root/bin/ps"
ln -s busybox "$root/bin/ip" ln -s busybox "$root/bin/ip"
ln -s busybox "$root/bin/seq" ln -s busybox "$root/bin/seq"
ln -s busybox "$root/bin/sleep" ln -s busybox "$root/bin/sleep"
ln -s busybox "$root/bin/usleep"
ln -s busybox "$root/bin/test" ln -s busybox "$root/bin/test"
mkdir -p "$root/sbin" mkdir -p "$root/sbin"

View File

@ -83,13 +83,11 @@ TOOLS_DIR="$SOURCE_DIR/tools"
export TEST_BASE_DIR TEST_UNITS_DIR SOURCE_DIR TOOLS_DIR export TEST_BASE_DIR TEST_UNITS_DIR SOURCE_DIR TOOLS_DIR
# note that find-build-dir.sh will return $BUILD_DIR if provided, else it will try to find it # note that find-build-dir.sh will return $BUILD_DIR if provided, else it will try to find it
if ! BUILD_DIR="$("$TOOLS_DIR"/find-build-dir.sh)"; then if get_bool "${NO_BUILD:=}"; then
if get_bool "${NO_BUILD:=}"; then BUILD_DIR="$SOURCE_DIR"
BUILD_DIR="$SOURCE_DIR" elif ! BUILD_DIR="$("$TOOLS_DIR"/find-build-dir.sh)"; then
else echo "ERROR: no build found, please set BUILD_DIR or use NO_BUILD" >&2
echo "ERROR: no build found, please set BUILD_DIR or use NO_BUILD" >&2 exit 1
exit 1
fi
fi fi
PATH_TO_INIT="$ROOTLIBDIR/systemd" PATH_TO_INIT="$ROOTLIBDIR/systemd"
@ -667,6 +665,7 @@ setup_basic_environment() {
install_fs_tools install_fs_tools
install_modules install_modules
install_plymouth install_plymouth
install_haveged
install_debug_tools install_debug_tools
install_ld_so_conf install_ld_so_conf
install_testuser install_testuser
@ -936,11 +935,52 @@ install_debian_systemd() {
done < <(grep -E '^Package:' "${SOURCE_DIR}/debian/control" | cut -d ':' -f 2) done < <(grep -E '^Package:' "${SOURCE_DIR}/debian/control" | cut -d ':' -f 2)
} }
install_suse_systemd() {
local testsdir=/usr/lib/systemd/tests
local pkgs
dinfo "Install SUSE systemd"
pkgs=(
systemd
systemd-container
systemd-coredump
systemd-experimental
systemd-journal-remote
systemd-portable
udev
)
for p in "${pkgs[@]}"; do
rpm -q "$p" &>/dev/null || continue
ddebug "Install files from package $p"
while read -r f; do
[ -e "$f" ] || continue
[ -d "$f" ] && continue
inst "$f"
done < <(rpm -ql "$p")
done
# we only need testsdata dir as well as the unit tests (for
# TEST-02-UNITTESTS) in the image.
dinfo "Install unit tests and testdata directory"
mkdir -p "$initdir/$testsdir"
cp "$testsdir"/test-* "$initdir/$testsdir/"
cp -a "$testsdir/testdata" "$initdir/$testsdir/"
# On openSUSE, these dirs are not created at package install for now on.
mkdir -p "$initdir/var/log/journal/remote"
}
install_distro_systemd() { install_distro_systemd() {
dinfo "Install distro systemd" dinfo "Install distro systemd"
if get_bool "$LOOKS_LIKE_DEBIAN"; then if get_bool "$LOOKS_LIKE_DEBIAN"; then
install_debian_systemd install_debian_systemd
elif get_bool "$LOOKS_LIKE_SUSE"; then
install_suse_systemd
else else
dfatal "NO_BUILD not supported for this distro" dfatal "NO_BUILD not supported for this distro"
exit 1 exit 1
@ -958,8 +998,6 @@ install_systemd() {
# remove unneeded documentation # remove unneeded documentation
rm -fr "$initdir"/usr/share/{man,doc} rm -fr "$initdir"/usr/share/{man,doc}
get_bool "$LOOKS_LIKE_SUSE" && setup_suse
# enable debug logging in PID1 # enable debug logging in PID1
echo LogLevel=debug >>"$initdir/etc/systemd/system.conf" echo LogLevel=debug >>"$initdir/etc/systemd/system.conf"
# store coredumps in journal # store coredumps in journal
@ -1390,6 +1428,16 @@ install_plymouth() {
# fi # fi
} }
install_haveged() {
# If haveged is installed and probably included in initrd, it needs to be
# installed in the image too.
if [ -x /usr/sbin/haveged ]; then
dinfo "Install haveged files"
inst /usr/sbin/haveged
inst /usr/lib/systemd/system/haveged.service
fi
}
install_ld_so_conf() { install_ld_so_conf() {
dinfo "Install /etc/ld.so.conf*" dinfo "Install /etc/ld.so.conf*"
cp -a /etc/ld.so.conf* "${initdir:?}/etc" cp -a /etc/ld.so.conf* "${initdir:?}/etc"
@ -1551,7 +1599,7 @@ install_pam() {
paths+=(/lib*/security) paths+=(/lib*/security)
fi fi
for d in /etc/pam.d /etc/security /usr/lib/pam.d; do for d in /etc/pam.d /etc/security /usr/{etc,lib}/pam.d; do
[ -d "$d" ] && paths+=("$d") [ -d "$d" ] && paths+=("$d")
done done
@ -1565,6 +1613,13 @@ install_pam() {
# set empty root password for easy debugging # set empty root password for easy debugging
sed -i 's/^root:x:/root::/' "${initdir:?}/etc/passwd" sed -i 's/^root:x:/root::/' "${initdir:?}/etc/passwd"
# And make sure pam_unix will accept it by making sure that
# the PAM module has the nullok option.
for d in /etc/pam.d /usr/{etc,lib}/pam.d; do
[ -d "$initdir/$d" ] || continue
sed -i '/^auth.*pam_unix.so/s/$/ nullok/' "$initdir/$d"/*
done
} }
install_keymaps() { install_keymaps() {
@ -2406,12 +2461,6 @@ instmods() {
return 0 return 0
} }
setup_suse() {
ln -fs ../usr/bin/systemctl "${initdir:?}/bin/"
ln -fs ../usr/lib/systemd "$initdir/lib/"
inst_simple "/usr/lib/systemd/system/haveged.service"
}
_umount_dir() { _umount_dir() {
local mountpoint="${1:?}" local mountpoint="${1:?}"
if mountpoint -q "$mountpoint"; then if mountpoint -q "$mountpoint"; then

View File

@ -30,19 +30,13 @@ if unshare -U sh -c :; then
is_user_ns_supported=yes is_user_ns_supported=yes
fi fi
SUSE_OPTS=()
ID_LIKE=$(awk -F= '$1=="ID_LIKE" { print $2 ;}' /etc/os-release)
if [[ "$ID_LIKE" = *"suse"* ]]; then
SUSE_OPTS+=(--bind /lib64 --bind /usr/lib64)
fi
function check_bind_tmp_path { function check_bind_tmp_path {
# https://github.com/systemd/systemd/issues/4789 # https://github.com/systemd/systemd/issues/4789
local _root="/var/lib/machines/testsuite-13.bind-tmp-path" local _root="/var/lib/machines/testsuite-13.bind-tmp-path"
rm -rf "$_root" rm -rf "$_root"
/usr/lib/systemd/tests/testdata/create-busybox-container "$_root" /usr/lib/systemd/tests/testdata/create-busybox-container "$_root"
: >/tmp/bind : >/tmp/bind
systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" --bind=/tmp/bind /bin/sh -c 'test -e /tmp/bind' systemd-nspawn --register=no -D "$_root" --bind=/tmp/bind /bin/sh -c 'test -e /tmp/bind'
} }
function check_norbind { function check_norbind {
@ -54,15 +48,15 @@ function check_norbind {
mount -t tmpfs tmpfs /tmp/binddir/subdir mount -t tmpfs tmpfs /tmp/binddir/subdir
echo -n "inner" >/tmp/binddir/subdir/file echo -n "inner" >/tmp/binddir/subdir/file
/usr/lib/systemd/tests/testdata/create-busybox-container "$_root" /usr/lib/systemd/tests/testdata/create-busybox-container "$_root"
systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" --bind=/tmp/binddir:/mnt:norbind /bin/sh -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; return 1; fi' systemd-nspawn --register=no -D "$_root" --bind=/tmp/binddir:/mnt:norbind /bin/sh -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; return 1; fi'
} }
function check_notification_socket { function check_notification_socket {
# https://github.com/systemd/systemd/issues/4944 # https://github.com/systemd/systemd/issues/4944
local _cmd='echo a | $(busybox which nc) -U -u -w 1 /run/host/notify' local _cmd='echo a | $(busybox which nc) -U -u -w 1 /run/host/notify'
# /testsuite-13.nc-container is prepared by test.sh # /testsuite-13.nc-container is prepared by test.sh
systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D /testsuite-13.nc-container /bin/sh -x -c "$_cmd" systemd-nspawn --register=no -D /testsuite-13.nc-container /bin/sh -x -c "$_cmd"
systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D /testsuite-13.nc-container -U /bin/sh -x -c "$_cmd" systemd-nspawn --register=no -D /testsuite-13.nc-container -U /bin/sh -x -c "$_cmd"
} }
function check_os_release { function check_os_release {
@ -84,7 +78,7 @@ if echo test >>/run/host/os-release; then exit 1; fi
echo MARKER=1 >>/etc/os-release echo MARKER=1 >>/etc/os-release
fi fi
systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D /testsuite-13.nc-container --bind="${_os_release_source}":/tmp/os-release /bin/sh -x -e -c "$_cmd" systemd-nspawn --register=no -D /testsuite-13.nc-container --bind="${_os_release_source}":/tmp/os-release /bin/sh -x -e -c "$_cmd"
if grep -q MARKER /etc/os-release; then if grep -q MARKER /etc/os-release; then
rm /etc/os-release rm /etc/os-release
@ -93,7 +87,7 @@ if echo test >>/run/host/os-release; then exit 1; fi
} }
function check_machinectl_bind { function check_machinectl_bind {
local _cmd='for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; sleep 0.5; done; exit 1;' local _cmd='for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; usleep 500000; done; exit 1;'
cat >/run/systemd/system/nspawn_machinectl_bind.service <<EOF cat >/run/systemd/system/nspawn_machinectl_bind.service <<EOF
[Service] [Service]
@ -138,16 +132,16 @@ function run {
local _root="/var/lib/machines/testsuite-13.unified-$1-cgns-$2-api-vfs-writable-$3" local _root="/var/lib/machines/testsuite-13.unified-$1-cgns-$2-api-vfs-writable-$3"
rm -rf "$_root" rm -rf "$_root"
/usr/lib/systemd/tests/testdata/create-busybox-container "$_root" /usr/lib/systemd/tests/testdata/create-busybox-container "$_root"
SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" -b SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -b
SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" --private-network -b SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" --private-network -b
if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" -U -b; then if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -U -b; then
[[ "$is_user_ns_supported" = "yes" && "$3" = "network" ]] && return 1 [[ "$is_user_ns_supported" = "yes" && "$3" = "network" ]] && return 1
else else
[[ "$is_user_ns_supported" = "no" && "$3" = "network" ]] && return 1 [[ "$is_user_ns_supported" = "no" && "$3" = "network" ]] && return 1
fi fi
if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" --private-network -U -b; then if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" --private-network -U -b; then
[[ "$is_user_ns_supported" = "yes" && "$3" = "yes" ]] && return 1 [[ "$is_user_ns_supported" = "yes" && "$3" = "yes" ]] && return 1
else else
[[ "$is_user_ns_supported" = "no" && "$3" = "yes" ]] && return 1 [[ "$is_user_ns_supported" = "no" && "$3" = "yes" ]] && return 1
@ -167,21 +161,21 @@ function run {
# --network-namespace-path and network-related options cannot be used together # --network-namespace-path and network-related options cannot be used together
for netopt in "${_net_opts[@]}"; do for netopt in "${_net_opts[@]}"; do
echo "$_netns_opt in combination with $netopt should fail" echo "$_netns_opt in combination with $netopt should fail"
if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" -b "$_netns_opt" "$netopt"; then if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -b "$_netns_opt" "$netopt"; then
echo >&2 "unexpected pass" echo >&2 "unexpected pass"
return 1 return 1
fi fi
done done
# allow combination of --network-namespace-path and --private-network # allow combination of --network-namespace-path and --private-network
if ! SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" -b "$_netns_opt" --private-network; then if ! SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -b "$_netns_opt" --private-network; then
return 1 return 1
fi fi
# test --network-namespace-path works with a network namespace created by "ip netns" # test --network-namespace-path works with a network namespace created by "ip netns"
ip netns add nspawn_test ip netns add nspawn_test
_netns_opt="--network-namespace-path=/run/netns/nspawn_test" _netns_opt="--network-namespace-path=/run/netns/nspawn_test"
SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" "$_netns_opt" /bin/ip a | grep -v -E '^1: lo.*UP' SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" /bin/ip a | grep -v -E '^1: lo.*UP'
local r=$? local r=$?
ip netns del nspawn_test ip netns del nspawn_test