Skip to content

Commit

Permalink
initramfs: fix running on root-only readable initrd filesystem
Browse files Browse the repository at this point in the history
Signed-off-by: Oldřich Jedlička <[email protected]>
  • Loading branch information
oldium committed Dec 5, 2024
1 parent bf66596 commit 49bf47a
Show file tree
Hide file tree
Showing 14 changed files with 116 additions and 56 deletions.
2 changes: 2 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ project('clevis', 'c', license: 'GPL3+',
libexecdir = join_paths(get_option('prefix'), get_option('libexecdir'))
sysconfdir = join_paths(get_option('prefix'), get_option('sysconfdir'))
bindir = join_paths(get_option('prefix'), get_option('bindir'))
libdir = join_paths(get_option('prefix'), get_option('libdir'))

data = configuration_data()
data.set('libexecdir', libexecdir)
data.set('sysconfdir', sysconfdir)
data.set('bindir', bindir)
data.set('libdir', libdir)

add_project_arguments(
'-Wall',
Expand Down
14 changes: 9 additions & 5 deletions src/initramfs-tools/hooks/clevis.in
Original file line number Diff line number Diff line change
Expand Up @@ -99,25 +99,29 @@ fi
if [ -x @bindir@/clevis-decrypt-tpm1 ]; then
copy_exec @bindir@/clevis-decrypt-tpm1 || die 1 "@bindir@/clevis-decrypt-tpm1 not found"
copy_exec @libexecdir@/clevis-luks-tpm1-functions || die 1 "@libexecdir@/clevis-luks-tpm1-functions not found"
copy_exec @libdir@/libclevis-tpm1-tcsd-preload.so || die 1 "@libdir@/libclevis-tpm1-tcsd-preload.so not found"

tcsd_bin=$(find_binary "tcsd")
tpm_version_bin=$(find_binary "tpm_version")
tpm_unsealdata_bin=$(find_binary "tpm_unsealdata")
stdbuf_bin=$(find_binary "stdbuf")
libstdbuf_bin=$(find_library "coreutils/libstdbuf.so*")

copy_exec "${tpm_version_bin}" || die 1 "Unable to copy ${tpm_version_bin}"
copy_exec "${tpm_unsealdata_bin}" || die 1 "Unable to copy ${tpm_unsealdata_bin}"

copy_exec "${tcsd_bin}" || die 1 "Unable to copy ${tcsd_bin}"
copy_file config /etc/tcsd.conf || dia 2 "Unable to copy /etc/tcsd.conf"

copy_exec "${stdbuf_bin}" || die 1 "Unable to copy ${stdbuf_bin}"
copy_exec "${libstdbuf_bin}" || die 1 "Unable to copy ${libstdbuf_bin}"

mkdir -p "${DESTDIR}/var/lib/tpm" || die 2 "Unable to create /var/lib/tpm"
cp /var/lib/tpm/* "${DESTDIR}/var/lib/tpm/" || die 2 "Unable to copy /var/lib/tpm"

if [ -n "$UMASK" ] && (( UMASK & 0004 )); then
# Root-only readable initrd filesystem, we need to run as root
# shellcheck disable=SC2154 # $verbose is a dracut variable
[ "${verbose}" = "y" ] && echo "Forcing tcsd to run as root"
sed -i 's/^\([ ]*remote_ops\)/#\1/' "${DESTDIR}/etc/tcsd.conf"
echo "TCSD_NO_PRIVILEGE_DROP=1" >> "${DESTDIR}/conf/conf.d/clevis"
fi

mkdir -p "${DESTDIR}/lib/udev/rules.d" || die 2 "Unable to create /lib/udev/rules.d"
# shellcheck disable=SC2043
for rule in 60-tpm-udev.rules; do
Expand Down
4 changes: 4 additions & 0 deletions src/initramfs-tools/scripts/local-top/clevis.in
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,10 @@ do_configure_tpm1() {

wait_for_udev 10

# shellcheck disable=SC2034 # setting default value
TCSD_NO_PRIVILEGE_DROP=0
[ -f /conf/conf.d/clevis ] && . /conf/conf.d/clevis

if ! tcsd_output=$(start_tcsd 2>&1); then
if [ -n "$tcsd_output" ]; then
log_failure_msg "failed to start TCSD: $tcsd_output"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ start_tcsd() {
# available for debugging, so start TCSD in foreground mode, but as a
# background job. Unfortunatelly the redirected output to pipe is
# block-buffered (see `man 3 setbuf`), so in order to see any output we
# need to set it to line-buffered with stdbuf tool
stdbuf -oL tcsd -f >$fifo_file 2>&1 &
# need to set it to line-buffered with LD_PRELOAD library
TCSD_NO_PRIVILEGE_DROP=${TCSD_NO_PRIVILEGE_DROP:-0} LD_PRELOAD="@libdir@/libclevis-tpm1-tcsd-preload.so" tcsd -f >$fifo_file 2>&1 &
tcsd_pid=$!

wait $sleep_pid 2>/dev/null
Expand Down
10 changes: 3 additions & 7 deletions src/luks/dracut/clevis-pin-tpm1/module-setup.sh.in
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ require_nonempty_dir() {
check() {
local _module_name="${moddir##*/[0-9][0-9]}"

require_binaries clevis-decrypt-tpm1 tpm_version tpm_unsealdata tcsd stdbuf || return 1
require_binaries clevis-decrypt-tpm1 tpm_version tpm_unsealdata tcsd || return 1
if [[ $hostonly ]]; then
require_nonempty_dir /var/lib/tpm || return 1
else
Expand Down Expand Up @@ -96,13 +96,9 @@ install() {
systemctl -q --root "$initdir" add-wants cryptsetup.target tcsd.service
else
inst_multiple \
awk chmod chown mkfifo mktemp ip ps stdbuf \
awk chmod chown mkfifo mktemp ip ps \
@libdir@/libclevis-tpm1-tcsd-preload.so \
@libexecdir@/clevis-luks-tpm1-functions
if [ -f /usr/libexec/coreutils/libstdbuf.so ]; then
inst_multiple /usr/libexec/coreutils/libstdbuf.so*
else
inst_libdir_file 'coreutils/libstdbuf.so*'
fi
fi

inst_multiple \
Expand Down
2 changes: 1 addition & 1 deletion src/luks/dracut/clevis/clevis-password-unlocker.in
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ do_configure_tpm1() {

info "Starting TCSD daemon"

if ! tcsd_output=$(start_tcsd 2>&1); then
if ! tcsd_output=$(TCSD_NO_PRIVILEGE_DROP=0 start_tcsd 2>&1); then
if [ -n "$tcsd_output" ]; then
echo "Unable to start TCSD: $tcsd_output" | vwarn
else
Expand Down
12 changes: 8 additions & 4 deletions src/luks/dracut/clevis/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,36 @@ dracut = dependency('dracut', required: false)
if dracut.found()
dracutdir = dracut.get_pkgconfig_variable('dracutmodulesdir') + '/60' + meson.project_name()

dracut_data = configuration_data()
dracut_data.merge_from(data)
dracut_data.set('SYSTEMD_REPLY_PASS', sd_reply_pass.path())

configure_file(
input: 'module-setup.sh.in',
output: 'module-setup.sh',
install_dir: dracutdir,
configuration: data,
configuration: dracut_data,
)

configure_file(
input: 'clevis-cleanup.in',
output: 'clevis-cleanup',
install_dir: dracutdir,
configuration: data,
configuration: dracut_data,
)

configure_file(
input: 'clevis-password-unlocker.in',
output: 'clevis-password-unlocker',
install_dir: dracutdir,
configuration: data,
configuration: dracut_data,
)

configure_file(
input: 'clevis-password-unlocker-prepare.in',
output: 'clevis-password-unlocker-prepare',
install_dir: dracutdir,
configuration: data,
configuration: dracut_data,
)

install_data('clevis-cleanup-hook.sh', install_dir: dracutdir)
Expand Down
23 changes: 20 additions & 3 deletions src/luks/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,31 @@ clevis_luks_common_functions = configure_file(
configuration: luksmeta_data
)

clevis_luks_tpm1_functions = configure_file(
input: 'clevis-luks-tpm1-functions.in',
output: 'clevis-luks-tpm1-functions',
configuration: data
)

clevis_luks_unbind = configure_file(input: 'clevis-luks-unbind.in',
output: 'clevis-luks-unbind',
configuration: luksmeta_data)

# SystemD dependencies checked here, used both in systemd and dracut subdirs
systemd = dependency('systemd', required: false)
systemdutildir = systemd.found() ? systemd.get_pkgconfig_variable('systemdutildir', default: '') : ''

sd_reply_pass = find_program(
(systemdutildir != '') ? join_paths(systemdutildir, 'systemd-reply-password') : '',
join_paths(get_option('prefix'), get_option('libdir'), 'systemd', 'systemd-reply-password'),
join_paths(get_option('prefix'), 'lib', 'systemd', 'systemd-reply-password'),
join_paths('/', 'usr', get_option('libdir'), 'systemd', 'systemd-reply-password'),
join_paths('/', 'usr', 'lib', 'systemd', 'systemd-reply-password'),
required: false
)

if libcryptsetup.found() and luksmeta.found()
subdir('systemd')
# systemd should come before dracut in order to set up
# variables like SYSTEMD_REPLY_PASS.
subdir('dracut')
subdir('udisks2')

Expand Down Expand Up @@ -67,7 +84,7 @@ if libcryptsetup.found() and luksmeta.found()
bins += join_paths(meson.current_source_dir(), 'clevis-luks-pass')
mans += join_paths(meson.current_source_dir(), 'clevis-luks-pass.1')

install_data('clevis-luks-tpm1-functions', install_dir: libexecdir)
install_data(clevis_luks_tpm1_functions, install_dir: libexecdir)
else
warning('Will not install LUKS support due to missing dependencies!')
endif
Expand Down
26 changes: 8 additions & 18 deletions src/luks/systemd/meson.build
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
systemd = dependency('systemd', required: false)
systemdutildir = systemd.found() ? systemd.get_pkgconfig_variable('systemdutildir', default: '') : ''

sd_reply_pass = find_program(
(systemdutildir != '') ? join_paths(systemdutildir, 'systemd-reply-password') : '',
join_paths(get_option('prefix'), get_option('libdir'), 'systemd', 'systemd-reply-password'),
join_paths(get_option('prefix'), 'lib', 'systemd', 'systemd-reply-password'),
join_paths('/', 'usr', get_option('libdir'), 'systemd', 'systemd-reply-password'),
join_paths('/', 'usr', 'lib', 'systemd', 'systemd-reply-password'),
required: false
)

if systemd.found() and sd_reply_pass.found()
data.set('SYSTEMD_REPLY_PASS', sd_reply_pass.path())
systemd_data = configuration_data()
systemd_data.merge_from(data)
systemd_data.set('SYSTEMD_REPLY_PASS', sd_reply_pass.path())

unitdir = systemd.get_pkgconfig_variable('systemdsystemunitdir')
tcsdoverridedir = join_paths(unitdir, 'tcsd.service.d')
Expand All @@ -20,31 +10,31 @@ if systemd.found() and sd_reply_pass.found()
input: 'clevis-luks-askpass.service.in',
output: 'clevis-luks-askpass.service',
install_dir: unitdir,
configuration: data,
configuration: systemd_data,
)
configure_file(
input: 'clevis-luks-pkcs11-askpass.service.in',
output: 'clevis-luks-pkcs11-askpass.service',
install_dir: unitdir,
configuration: data,
configuration: systemd_data,
)
configure_file(
input: 'clevis-luks-askpass.in',
output: 'clevis-luks-askpass',
install_dir: libexecdir,
configuration: data
configuration: systemd_data
)
configure_file(
input: 'clevis-luks-pkcs11-askpass.in',
output: 'clevis-luks-pkcs11-askpass',
install_dir: libexecdir,
configuration: data
configuration: systemd_data
)
configure_file(
input: 'clevis-luks-pkcs11-askpin.in',
output: 'clevis-luks-pkcs11-askpin',
install_dir: libexecdir,
configuration: data
configuration: systemd_data
)

install_data('clevis-luks-askpass.path', install_dir: unitdir)
Expand Down
46 changes: 46 additions & 0 deletions src/pins/tpm1/clevis-tpm1-tcsd-preload.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#define _GNU_SOURCE

#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define TCSD_NO_PRIVILEGE_DROP_ENV "TCSD_NO_PRIVILEGE_DROP"

static int no_privilege_drop(void) {
char *no_privilege_drop_env = getenv(TCSD_NO_PRIVILEGE_DROP_ENV);
return (no_privilege_drop_env != NULL
&& no_privilege_drop_env[0] != '\0'
&& no_privilege_drop_env[0] != '0');
}

int setuid(uid_t uid) {
static int (*real_setuid)(uid_t) = NULL;
if (no_privilege_drop()) {
return 0;
} else {
if (!real_setuid) {
real_setuid = dlsym(RTLD_NEXT, "setuid");
}
return real_setuid(uid);
}
}

int setgid(gid_t gid) {
static int (*real_setgid)(uid_t) = NULL;
if (no_privilege_drop()) {
return 0;
} else {
if (!real_setgid) {
real_setgid = dlsym(RTLD_NEXT, "setgid");
}
return real_setgid(gid);
}
return 0;
}

static void __attribute ((constructor))
set_line_buffering (void)
{
setvbuf(stdout, NULL, _IOLBF, 0);
}
7 changes: 7 additions & 0 deletions src/pins/tpm1/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ if all
bins += join_paths(meson.current_source_dir(), 'clevis-decrypt-tpm1')
bins += join_paths(meson.current_source_dir(), 'clevis-encrypt-tpm1')
mans += join_paths(meson.current_source_dir(), 'clevis-encrypt-tpm1.1')

libdl_dep = dependency('dl', required: true)
libclevis_tpm1_tcsd_preload = shared_library('clevis-tpm1-tcsd-preload', 'clevis-tpm1-tcsd-preload.c',
dependencies: libdl_dep,
install_dir: libdir,
install: true)

subdir('tests')
else
warning('Will not install tpm1 pin due to missing dependencies!')
Expand Down
1 change: 1 addition & 0 deletions src/pins/tpm1/tests/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ tpm1_data.set('TCSD_BIN', tcsd.found() ? tcsd.path() : '')
tpm1_data.set('SWTPM_BIN', swtpm.found() ? swtpm.path() : '')
tpm1_data.set('SWTPM_SETUP_BIN', swtpm_setup.found() ? swtpm_setup.path() : '')
tpm1_data.set('SWTPM_BIOS_BIN', swtpm_bios.found() ? swtpm_bios.path() : '')
tpm1_data.set('LIBCLEVIS_TPM1_TCSD_PRELOAD', libclevis_tpm1_tcsd_preload.path())

configure_file(
input: 'tpm1-common-test-functions.in',
Expand Down
15 changes: 0 additions & 15 deletions src/pins/tpm1/tests/tcsd-patch.c.in
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,3 @@ int __xstat(int ver, const char *pathname, struct stat *statbuf) {
return result;
}
#endif

int setuid(uid_t uid) {
return 0;
}

int setgid(gid_t gid) {
return 0;
}

static void __attribute ((constructor))
set_line_buffering (void)
{
setvbuf(stdout, NULL, _IOLBF, 0);
fprintf(stderr, "set_line_buffering : done\n");
}
6 changes: 5 additions & 1 deletion src/pins/tpm1/tests/tpm1-common-test-functions.in
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ TCSD_BIN="@TCSD_BIN@"
SWTPM_BIN="@SWTPM_BIN@"
SWTPM_SETUP_BIN="@SWTPM_SETUP_BIN@"
SWTPM_BIOS_BIN="@SWTPM_BIOS_BIN@"
LIBCLEVIS_TPM1_TCSD_PRELOAD="@LIBCLEVIS_TPM1_TCSD_PRELOAD@"

SWTPM_SOCKET_PID=
TCSD_PID=
Expand Down Expand Up @@ -136,7 +137,10 @@ EOM
chmod 0640 "$TESTDIR"/tcsd.conf
fi

LD_PRELOAD="$TCSD_PATCH_LIB" TCSD_UN_SOCKET_DEVICE_PATH="$TESTDIR"/swtpm.sock "${TCSD_BIN}" -f -e -c "$TESTDIR"/tcsd.conf >&2 </dev/null &
TCSD_NO_PRIVILEGE_DROP=1 \
LD_PRELOAD="$LIBCLEVIS_TPM1_TCSD_PRELOAD $TCSD_PATCH_LIB" \
TCSD_UN_SOCKET_DEVICE_PATH="$TESTDIR"/swtpm.sock \
"${TCSD_BIN}" -f -e -c "$TESTDIR"/tcsd.conf >&2 </dev/null &
TCSD_PID=$!

process_wait_until_port_ready $TCSD_PID "tcsd"
Expand Down

0 comments on commit 49bf47a

Please sign in to comment.