diff options
28 files changed, 363 insertions, 252 deletions
diff --git a/.github/workflows/build_test.sh b/.github/workflows/build_test.sh index a512e5101d..c0872117ab 100755 --- a/.github/workflows/build_test.sh +++ b/.github/workflows/build_test.sh @@ -68,8 +68,6 @@ LINKER="${LINKER:?}" CRYPTOLIB="${CRYPTOLIB:?}" RELEASE="$(lsb_release -cs)" -bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ $RELEASE main restricted universe multiverse' >>/etc/apt/sources.list" - # Note: As we use postfixed clang/gcc binaries, we need to override $AR # as well, otherwise meson falls back to ar from binutils which # doesn't work with LTO @@ -79,12 +77,13 @@ if [[ "$COMPILER" == clang ]]; then AR="llvm-ar-$COMPILER_VERSION" # Prefer the distro version if available - if ! apt install --dry-run "llvm-$COMPILER_VERSION" >/dev/null; then + if ! apt-get -y install --dry-run "llvm-$COMPILER_VERSION" >/dev/null; then # Latest LLVM stack deb packages provided by https://apt.llvm.org/ # Following snippet was partly borrowed from https://apt.llvm.org/llvm.sh - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | gpg --yes --dearmor --output /usr/share/keyrings/apt-llvm-org.gpg - printf "deb [signed-by=/usr/share/keyrings/apt-llvm-org.gpg] http://apt.llvm.org/%s/ llvm-toolchain-%s-%s main\n" \ - "$RELEASE" "$RELEASE" "$COMPILER_VERSION" >/etc/apt/sources.list.d/llvm-toolchain.list + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | \ + sudo gpg --yes --dearmor --output /usr/share/keyrings/apt-llvm-org.gpg + echo "deb [signed-by=/usr/share/keyrings/apt-llvm-org.gpg] http://apt.llvm.org/$RELEASE/ llvm-toolchain-$RELEASE-$COMPILER_VERSION main" | \ + sudo tee /etc/apt/sources.list.d/llvm-toolchain.list fi PACKAGES+=("clang-$COMPILER_VERSION" "lldb-$COMPILER_VERSION" "python3-lldb-$COMPILER_VERSION" "lld-$COMPILER_VERSION" "clangd-$COMPILER_VERSION") @@ -93,10 +92,10 @@ elif [[ "$COMPILER" == gcc ]]; then CXX="g++-$COMPILER_VERSION" AR="gcc-ar-$COMPILER_VERSION" - if ! apt install --dry-run "gcc-$COMPILER_VERSION" >/dev/null; then + if ! apt-get -y install --dry-run "gcc-$COMPILER_VERSION" >/dev/null; then # Latest gcc stack deb packages provided by # https://launchpad.net/~ubuntu-toolchain-r/+archive/ubuntu/test - add-apt-repository -y ppa:ubuntu-toolchain-r/test + sudo add-apt-repository -y --no-update ppa:ubuntu-toolchain-r/test fi PACKAGES+=("gcc-$COMPILER_VERSION" "gcc-$COMPILER_VERSION-multilib") @@ -105,10 +104,11 @@ else fi # PPA with some newer build dependencies (like zstd) -add-apt-repository -y ppa:upstream-systemd-ci/systemd-ci -apt-get -y update -apt-get -y build-dep systemd -apt-get -y install "${PACKAGES[@]}" +sudo add-apt-repository -y --no-update ppa:upstream-systemd-ci/systemd-ci +sudo add-apt-repository -y --no-update --enable-source +sudo apt-get -y update +sudo apt-get -y build-dep systemd +sudo apt-get -y install "${PACKAGES[@]}" # Install more or less recent meson and ninja with pip, since the distro versions don't # always support all the features we need (like --optimization=). Since the build-dep # command above installs the distro versions, let's install the pip ones just @@ -139,7 +139,7 @@ for args in "${ARGS[@]}"; do CXX="$CXX" CXX_LD="$LD" CXXFLAGS="-Werror" \ meson setup \ -Dtests=unsafe -Dslow-tests=true -Dfuzz-tests=true --werror \ - -Dnobody-group=nogroup -Dcryptolib="${CRYPTOLIB:?}" \ + -Dnobody-group=nogroup -Dcryptolib="${CRYPTOLIB:?}" -Ddebug=false \ $args build; then cat build/meson-logs/meson-log.txt diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index 16dcd30c08..ccbbe26ecd 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -34,5 +34,5 @@ jobs: steps: - name: Repository checkout uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 - - name: ${{ format('Build check ({0}-{1}-{2}-{3})', env.COMPILER, env.COMPILER_VERSION, env.LINKER, env.CRYPTOLIB) }} - run: sudo -E .github/workflows/build_test.sh + - name: Build check + run: .github/workflows/build_test.sh diff --git a/.github/workflows/cflite_pr.yml b/.github/workflows/cflite_pr.yml index a35a97f046..707ea0b6ba 100644 --- a/.github/workflows/cflite_pr.yml +++ b/.github/workflows/cflite_pr.yml @@ -23,13 +23,13 @@ jobs: matrix: sanitizer: [address, undefined, memory] steps: - - name: Build Fuzzers (${{ matrix.sanitizer }}) + - name: Build Fuzzers id: build uses: google/clusterfuzzlite/actions/build_fuzzers@v1 with: sanitizer: ${{ matrix.sanitizer }} github-token: ${{ secrets.GITHUB_TOKEN }} - - name: Run Fuzzers (${{ matrix.sanitizer }}) + - name: Run Fuzzers id: run uses: google/clusterfuzzlite/actions/run_fuzzers@v1 with: diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index a7e4d2b842..f7530b7507 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -39,7 +39,7 @@ jobs: security-events: write steps: - - name: Build Fuzzers (${{ matrix.sanitizer }}) + - name: Build Fuzzers id: build uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master with: @@ -51,7 +51,7 @@ jobs: sanitizer: ${{ matrix.sanitizer }} architecture: ${{ matrix.architecture }} output-sarif: true - - name: Run Fuzzers (${{ matrix.sanitizer }}) + - name: Run Fuzzers uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master with: oss-fuzz-project-name: 'systemd' diff --git a/.github/workflows/mkosi.yml b/.github/workflows/mkosi.yml index c353423c58..5b70c607c2 100644 --- a/.github/workflows/mkosi.yml +++ b/.github/workflows/mkosi.yml @@ -140,14 +140,14 @@ jobs: - name: Generate secure boot key run: mkosi --debug genkey - - name: Show ${{ matrix.distro }} image summary + - name: Show image summary run: mkosi summary - - name: Build ${{ matrix.distro }} + - name: Build run: mkosi --debug - - name: Boot ${{ matrix.distro }} systemd-nspawn + - name: Boot systemd-nspawn run: test "$(sudo mkosi --debug boot 1>&2; echo $?)" -eq 123 - - name: Boot ${{ matrix.distro }} QEMU + - name: Boot QEMU run: timeout -k 30 10m test "$(mkosi --debug qemu 1>&2; echo $?)" -eq 123 diff --git a/.github/workflows/unit_tests.sh b/.github/workflows/unit_tests.sh index 0985817b72..a5b98e089b 100755 --- a/.github/workflows/unit_tests.sh +++ b/.github/workflows/unit_tests.sh @@ -3,7 +3,6 @@ # shellcheck disable=SC2206 PHASES=(${@:-SETUP RUN RUN_ASAN_UBSAN CLEANUP}) -RELEASE="$(lsb_release -cs)" ADDITIONAL_DEPS=( clang expect @@ -46,9 +45,9 @@ for phase in "${PHASES[@]}"; do case $phase in SETUP) info "Setup phase" - bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ $RELEASE main restricted universe multiverse' >>/etc/apt/sources.list" # PPA with some newer build dependencies - add-apt-repository -y ppa:upstream-systemd-ci/systemd-ci + add-apt-repository -y --no-update ppa:upstream-systemd-ci/systemd-ci + add-apt-repository -y --no-update --enable-source apt-get -y update apt-get -y build-dep systemd apt-get -y install "${ADDITIONAL_DEPS[@]}" diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 051e8ec3bf..e2dd8c3da2 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -39,7 +39,7 @@ jobs: # Pass only specific env variables through sudo, to avoid having # the already existing XDG_* stuff on the "other side" sudo --preserve-env=CRYPTOLIB,GITHUB_ACTIONS,CI .github/workflows/unit_tests.sh SETUP - - name: Build & test (${{ matrix.run_phase }}-${{ matrix.cryptolib }}) + - name: Build & test run: sudo --preserve-env=CRYPTOLIB,GITHUB_ACTIONS,CI .github/workflows/unit_tests.sh RUN_${{ matrix.run_phase }} env: CRYPTOLIB: ${{ matrix.cryptolib }} @@ -95,14 +95,6 @@ Janitorial Clean-ups: Deprecations and removals: -* homed: add a basic form of of secrets management to homed, that stores - secrets in $HOME somewhere, is protected by the accounts own authentication - mechanisms. Should implement something PKCS#11-like that can be used to - implement emulated FIDO2 in unpriv userspace on top (which should happen - outside of homed), emulated PKCS11, and libsecrets support. Operate with a - 2nd key derived from volume key of the user, with which to wrap all - keys. maintain keys in kernel keyring if possible. - * Remove any support for booting without /usr pre-mounted in the initrd entirely. Update INITRD_INTERFACE.md accordingly. @@ -144,6 +136,19 @@ Deprecations and removals: Features: +* ddi must be listed as block device fstype + +* measure some string via pcrphase whenever we end up booting into emergency + mode. + +* homed: add a basic form of of secrets management to homed, that stores + secrets in $HOME somewhere, is protected by the accounts own authentication + mechanisms. Should implement something PKCS#11-like that can be used to + implement emulated FIDO2 in unpriv userspace on top (which should happen + outside of homed), emulated PKCS11, and libsecrets support. Operate with a + 2nd key derived from volume key of the user, with which to wrap all + keys. maintain keys in kernel keyring if possible. + * add ConditionSecurity=stub-measured or so that checks if we are booted with systemd-stub and its measurements diff --git a/hwdb.d/60-input-id.hwdb b/hwdb.d/60-input-id.hwdb index 802ed9a1e7..1c84777fe0 100644 --- a/hwdb.d/60-input-id.hwdb +++ b/hwdb.d/60-input-id.hwdb @@ -75,3 +75,7 @@ id-input:modalias:input:b0003v046Dp4066e0111* id-input:modalias:input:b0003v068Ep00F2e0100* ID_INPUT_ACCELEROMETER=0 ID_INPUT_JOYSTICK=1 + +# TEX Shinobi Trackpoint +id-input:modalias:input:b0003v04D9p0407e0111-e0,1,2,4* + ID_INPUT_POINTINGSTICK=1 diff --git a/man/loader.conf.xml b/man/loader.conf.xml index 1c71a9b7de..0e9f6e6924 100644 --- a/man/loader.conf.xml +++ b/man/loader.conf.xml @@ -308,33 +308,43 @@ <programlisting>uuid=$(systemd-id128 new --uuid) for key in PK KEK db; do - openssl req -new -x509 -subj "/CN=${key}/" -keyout "${key}.key" -out "${key}.crt" - openssl x509 -outform DER -in "${key}.crt" -out "${key}.der" + openssl req -new -x509 -subj "/CN=${key}/" -keyout "${key}.key" -out "${key}.pem" + openssl x509 -outform DER -in "${key}.pem" -out "${key}.der" sbsiglist --owner "${uuid}" --type x509 --output "${key}.esl" "${key}.der" done -for key in MicWinProPCA2011_2011-10-19.crt MicCorUEFCA2011_2011-06-27.crt MicCorKEKCA2011_2011-06-24.crt; do - curl "https://www.microsoft.com/pkiops/certs/${key}" --output "${key}" - sbsiglist --owner 77fa9abd-0359-4d32-bd60-28f4e78f784b --type x509 --output "${key%crt}esl" "${key}" +# See also: <ulink url="https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/windows-secure-boot-key-creation-and-management-guidance">Windows Secure Boot Key Creation and Management Guidance</ulink> +curl --location \ + "https://go.microsoft.com/fwlink/p/?linkid=321192" -o ms-db-2011.der \ + "https://go.microsoft.com/fwlink/p/?linkid=321185" -o ms-kek-2011.der \ + "https://go.microsoft.com/fwlink/p/?linkid=321194" -o ms-uefi-db-2011.der \ + "https://go.microsoft.com/fwlink/p/?linkid=2239775" -o ms-kek-2023.base64 \ + "https://go.microsoft.com/fwlink/p/?linkid=2239776" -o ms-db-2023.base64 \ + "https://go.microsoft.com/fwlink/p/?linkid=2239872" -o ms-uefi-db-2023.base64 +for key in ms-*.base64; do + base64 -d "${key}" >"${key%base64}der" +done +for key in ms-*.der; do + sbsiglist --owner 77fa9abd-0359-4d32-bd60-28f4e78f784b --type x509 --output "${key%der}esl" "${key}" done -# Optionally add Microsoft Windows Production CA 2011 (needed to boot into Windows). -cat MicWinProPCA2011_2011-10-19.esl >>db.esl +# Optionally add Microsoft Windows certificates (needed to boot into Windows). +cat ms-db-*.esl >>db.esl -# Optionally add Microsoft Corporation UEFI CA 2011 for firmware drivers / option ROMs -# and third-party boot loaders (including shim). This is highly recommended on real -# hardware as not including this may soft-brick your device (see next paragraph). -cat MicCorUEFCA2011_2011-06-27.esl >>db.esl +# Optionally add Microsoft UEFI certificates for firmware drivers / option ROMs and third-party +# boot loaders (including shim). This is highly recommended on real hardware as not including this +# may soft-brick your device (see next paragraph). +cat ms-uefi-*.esl >>db.esl -# Optionally add Microsoft Corporation KEK CA 2011. Recommended if either of the -# Microsoft keys is used as the official UEFI revocation database is signed with this -# key. The revocation database can be updated with <citerefentry project='man-pages'><refentrytitle>fwupdmgr</refentrytitle><manvolnum>1</manvolnum></citerefentry>. -cat MicCorKEKCA2011_2011-06-24.esl >>KEK.esl +# Optionally add Microsoft KEK certificates. Recommended if either of the Microsoft keys is used as +# the official UEFI revocation database is signed with this key. The revocation database can be +# updated with <citerefentry project='man-pages'><refentrytitle>fwupdmgr</refentrytitle><manvolnum>1</manvolnum></citerefentry>. +cat ms-kek-*.esl >>KEK.esl attr=NON_VOLATILE,RUNTIME_ACCESS,BOOTSERVICE_ACCESS,TIME_BASED_AUTHENTICATED_WRITE_ACCESS -sbvarsign --attr ${attr} --key PK.key --cert PK.crt --output PK.auth PK PK.esl -sbvarsign --attr ${attr} --key PK.key --cert PK.crt --output KEK.auth KEK KEK.esl -sbvarsign --attr ${attr} --key KEK.key --cert KEK.crt --output db.auth db db.esl +sbvarsign --attr "${attr}" --key PK.key --cert PK.pem --output PK.auth PK PK.esl +sbvarsign --attr "${attr}" --key PK.key --cert PK.pem --output KEK.auth KEK KEK.esl +sbvarsign --attr "${attr}" --key KEK.key --cert KEK.pem --output db.auth db db.esl </programlisting> <para>This feature is considered dangerous because even if all the required files are signed with the diff --git a/mkosi.kernel.config b/mkosi.kernel.config index 098d1c230f..77657c2240 100644 --- a/mkosi.kernel.config +++ b/mkosi.kernel.config @@ -132,6 +132,8 @@ CONFIG_NETFILTER=y CONFIG_NETLABEL=y CONFIG_NF_CONNTRACK_FTP=y CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_SECMARK=y CONFIG_NF_CONNTRACK_SIP=y CONFIG_NF_CONNTRACK=y CONFIG_NF_CT_NETLINK=y @@ -212,7 +214,6 @@ CONFIG_XFS_POSIX_ACL=y # CONFIG_AGP is not set # CONFIG_FB is not set # CONFIG_HID is not set -# CONFIG_USB is not set # CONFIG_NETDEVSIM is not set # CONFIG_NET_VENDOR_3COM is not set diff --git a/mkosi.presets/system/mkosi.kernel.build b/mkosi.presets/system/mkosi.kernel.build index 64cc48863f..5938330d4b 100755 --- a/mkosi.presets/system/mkosi.kernel.build +++ b/mkosi.presets/system/mkosi.kernel.build @@ -30,7 +30,7 @@ if [ -d "$SRCDIR"/mkosi.kernel/ ]; then make O="$BUILDDIR" VERSION=99 INSTALL_MOD_PATH="$DESTDIR/usr" modules_install make O="$BUILDDIR" VERSION=99 INSTALL_PATH="$DESTDIR/usr/lib/modules/$KERNEL_RELEASE" install mkdir -p "$DESTDIR/usr/lib/kernel/selftests" - make -C tools/testing/selftests -j "$(nproc)" O="$BUILDDIR" VERSION=99 KSFT_INSTALL_PATH="$DESTDIR/usr/lib/kernel/selftests" SKIP_TARGETS="" install + make -C tools/testing/selftests -j "$(nproc)" O="$BUILDDIR" VERSION=99 KSFT_INSTALL_PATH="$DESTDIR/usr/lib/kernel/selftests" SKIP_TARGETS="hid" install mkdir -p "$DESTDIR"/usr/bin ln -sf /usr/lib/kernel/selftests/bpf/bpftool "$DESTDIR/usr/bin/bpftool" diff --git a/src/boot/measure.c b/src/boot/measure.c index a62caf8a3f..45febe121a 100644 --- a/src/boot/measure.c +++ b/src/boot/measure.c @@ -837,31 +837,12 @@ static int verb_sign(int argc, char *argv[], void *userdata) { if (r < 0) return r; - _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* mdctx = NULL; - mdctx = EVP_MD_CTX_new(); - if (!mdctx) - return log_oom(); - - if (EVP_DigestSignInit(mdctx, NULL, p->md, NULL, privkey) != 1) - return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), - "Failed to initialize signature context."); - - if (EVP_DigestSignUpdate(mdctx, pcr_policy_digest.buffer, pcr_policy_digest.size) != 1) - return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), - "Failed to sign data."); - + _cleanup_free_ void *sig = NULL; size_t ss; - if (EVP_DigestSignFinal(mdctx, NULL, &ss) != 1) - return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), - "Failed to finalize signature"); - _cleanup_free_ void *sig = malloc(ss); - if (!sig) - return log_oom(); - - if (EVP_DigestSignFinal(mdctx, sig, &ss) != 1) - return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), - "Failed to acquire signature data"); + r = digest_and_sign(p->md, privkey, pcr_policy_digest.buffer, pcr_policy_digest.size, &sig, &ss); + if (r < 0) + return log_error_errno(r, "Failed to sign PCR policy: %m"); _cleanup_free_ void *pubkey_fp = NULL; size_t pubkey_fp_size = 0; diff --git a/src/core/manager.c b/src/core/manager.c index 304abf4697..7492a0b1b8 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -4019,13 +4019,20 @@ static int manager_run_generators(Manager *m) { _exit(r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE); } if (r < 0) { - if (!ERRNO_IS_PRIVILEGE(r)) { + if (!ERRNO_IS_PRIVILEGE(r) && r != -EINVAL) { log_error_errno(r, "Failed to fork off sandboxing environment for executing generators: %m"); goto finish; } /* Failed to fork with new mount namespace? Maybe, running in a container environment with - * seccomp or without capability. */ + * seccomp or without capability. + * + * We also allow -EINVAL to allow running without CLONE_NEWNS. + * + * Also, when running on non-native userland architecture via systemd-nspawn and + * qemu-user-static QEMU-emulator, clone() with CLONE_NEWNS fails with EINVAL, see + * https://github.com/systemd/systemd/issues/28901. + */ log_debug_errno(r, "Failed to fork off sandboxing environment for executing generators. " "Falling back to execute generators without sandboxing: %m"); diff --git a/src/home/user-record-sign.c b/src/home/user-record-sign.c index cf72eaa556..dd099a0a6f 100644 --- a/src/home/user-record-sign.c +++ b/src/home/user-record-sign.c @@ -33,7 +33,6 @@ int user_record_sign(UserRecord *ur, EVP_PKEY *private_key, UserRecord **ret) { _cleanup_(memstream_done) MemStream m = {}; _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_(user_record_unrefp) UserRecord *signed_ur = NULL; - _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *md_ctx = NULL; _cleanup_free_ char *text = NULL, *key = NULL; _cleanup_free_ void *signature = NULL; size_t signature_size = 0; @@ -48,23 +47,9 @@ int user_record_sign(UserRecord *ur, EVP_PKEY *private_key, UserRecord **ret) { if (r < 0) return r; - md_ctx = EVP_MD_CTX_new(); - if (!md_ctx) - return -ENOMEM; - - if (EVP_DigestSignInit(md_ctx, NULL, NULL, NULL, private_key) <= 0) - return -EIO; - - /* Request signature size */ - if (EVP_DigestSign(md_ctx, NULL, &signature_size, (uint8_t*) text, strlen(text)) <= 0) - return -EIO; - - signature = malloc(signature_size); - if (!signature) - return -ENOMEM; - - if (EVP_DigestSign(md_ctx, signature, &signature_size, (uint8_t*) text, strlen(text)) <= 0) - return -EIO; + r = digest_and_sign(/* md= */ NULL, private_key, text, SIZE_MAX, &signature, &signature_size); + if (r < 0) + return r; f = memstream_init(&m); if (!f) diff --git a/src/journal/journald-wall.c b/src/journal/journald-wall.c index 21ec5a785b..c8d92c4d3e 100644 --- a/src/journal/journald-wall.c +++ b/src/journal/journald-wall.c @@ -6,7 +6,7 @@ #include "journald-wall.h" #include "process-util.h" #include "string-util.h" -#include "utmp-wtmp.h" +#include "wall.h" void server_forward_wall( Server *s, @@ -48,7 +48,7 @@ void server_forward_wall( } else l = message; - r = utmp_wall(l, "systemd-journald", NULL, NULL, NULL); + r = wall(l, "systemd-journald", NULL, NULL, NULL); if (r < 0) log_debug_errno(r, "Failed to send wall message: %m"); } diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 2d1af602c0..dcbd195fe2 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -57,6 +57,7 @@ #include "user-util.h" #include "utmp-wtmp.h" #include "virt.h" +#include "wall.h" /* As a random fun fact sysvinit had a 252 (256-(strlen(" \r\n")+1)) * character limit for the wall message. @@ -2342,8 +2343,8 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_CANCELED_STR, username ? "OPERATOR=%s" : NULL, username); - utmp_wall("System shutdown has been cancelled", - username, tty, logind_wall_tty_filter, m); + (void) wall("System shutdown has been cancelled", + username, tty, logind_wall_tty_filter, m); } reset_scheduled_shutdown(m); diff --git a/src/login/logind-utmp.c b/src/login/logind-wall.c index 4db127a4c5..aa73522334 100644 --- a/src/login/logind-utmp.c +++ b/src/login/logind-wall.c @@ -18,7 +18,7 @@ #include "strv.h" #include "unit-name.h" #include "user-util.h" -#include "utmp-wtmp.h" +#include "wall.h" static usec_t when_wall(usec_t n, usec_t elapse) { static const int wall_timers[] = { @@ -94,7 +94,7 @@ static int warn_wall(Manager *m, usec_t n) { username ? "OPERATOR=%s" : NULL, username); if (m->enable_wall_messages) - utmp_wall(l, username, m->scheduled_shutdown_tty, logind_wall_tty_filter, m); + (void) wall(l, username, m->scheduled_shutdown_tty, logind_wall_tty_filter, m); return 1; } diff --git a/src/login/meson.build b/src/login/meson.build index 9804a36ce1..276e920dfc 100644 --- a/src/login/meson.build +++ b/src/login/meson.build @@ -26,7 +26,7 @@ liblogind_core_sources = files( 'logind-session.c', 'logind-user-dbus.c', 'logind-user.c', - 'logind-utmp.c', + 'logind-wall.c', ) liblogind_core_sources += [logind_gperf_c] diff --git a/src/shared/meson.build b/src/shared/meson.build index 01e85b617f..67f4f4517d 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -168,6 +168,7 @@ shared_sources = files( 'verbs.c', 'vlan-util.c', 'volatile-util.c', + 'wall.c', 'watchdog.c', 'web-util.c', 'wifi-util.c', diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index 3d3d8090f8..31a1a55df7 100644 --- a/src/shared/openssl-util.c +++ b/src/shared/openssl-util.c @@ -545,6 +545,49 @@ int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_s return 0; } +int digest_and_sign( + const EVP_MD *md, + EVP_PKEY *privkey, + const void *data, size_t size, + void **ret, size_t *ret_size) { + + assert(privkey); + assert(ret); + assert(ret_size); + + if (size == 0) + data = ""; /* make sure to pass a valid pointer to OpenSSL */ + else { + assert(data); + + if (size == SIZE_MAX) /* If SIZE_MAX input is a string whose size we determine automatically */ + size = strlen(data); + } + + _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* mdctx = EVP_MD_CTX_new(); + if (!mdctx) + return log_openssl_errors("Failed to create new EVP_MD_CTX"); + + if (EVP_DigestSignInit(mdctx, NULL, md, NULL, privkey) != 1) + return log_openssl_errors("Failed to initialize signature context"); + + /* Determine signature size */ + size_t ss; + if (EVP_DigestSign(mdctx, NULL, &ss, data, size) != 1) + return log_openssl_errors("Failed to determine size of signature"); + + _cleanup_free_ void *sig = malloc(ss); + if (!sig) + return log_oom_debug(); + + if (EVP_DigestSign(mdctx, sig, &ss, data, size) != 1) + return log_openssl_errors("Failed to sign data"); + + *ret = TAKE_PTR(sig); + *ret_size = ss; + return 0; +} + # if PREFER_OPENSSL int string_hashsum( const char *s, diff --git a/src/shared/openssl-util.h b/src/shared/openssl-util.h index 90158f589b..4ea82b5f27 100644 --- a/src/shared/openssl-util.h +++ b/src/shared/openssl-util.h @@ -75,6 +75,8 @@ int ecc_pkey_new(int curve_id, EVP_PKEY **ret); int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size); +int digest_and_sign(const EVP_MD *md, EVP_PKEY *privkey, const void *data, size_t size, void **ret, size_t *ret_size); + #else typedef struct X509 X509; diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c index c79764ced0..c90e07d4d1 100644 --- a/src/shared/utmp-wtmp.c +++ b/src/shared/utmp-wtmp.c @@ -280,130 +280,3 @@ int utmp_put_runlevel(int runlevel, int previous) { return write_entry_both(&store); } - -#define TIMEOUT_USEC (50 * USEC_PER_MSEC) - -static int write_to_terminal(const char *tty, const char *message) { - _cleanup_close_ int fd = -EBADF; - const char *p; - size_t left; - usec_t end; - - assert(tty); - assert(message); - - fd = open(tty, O_WRONLY|O_NONBLOCK|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return -errno; - if (!isatty(fd)) - return -ENOTTY; - - p = message; - left = strlen(message); - - end = usec_add(now(CLOCK_MONOTONIC), TIMEOUT_USEC); - - while (left > 0) { - ssize_t n; - usec_t t; - int k; - - t = now(CLOCK_MONOTONIC); - if (t >= end) - return -ETIME; - - k = fd_wait_for_event(fd, POLLOUT, end - t); - if (ERRNO_IS_NEG_TRANSIENT(k)) - continue; - if (k < 0) - return k; - if (k == 0) - return -ETIME; - - n = write(fd, p, left); - if (n < 0) { - if (ERRNO_IS_TRANSIENT(errno)) - continue; - - return -errno; - } - - assert((size_t) n <= left); - - p += n; - left -= n; - } - - return 0; -} - -int utmp_wall( - const char *message, - const char *username, - const char *origin_tty, - bool (*match_tty)(const char *tty, bool is_local, void *userdata), - void *userdata) { - - _unused_ _cleanup_(utxent_cleanup) bool utmpx = false; - _cleanup_free_ char *text = NULL, *hn = NULL, *un = NULL, *stdin_tty = NULL; - struct utmpx *u; - int r; - - hn = gethostname_malloc(); - if (!hn) - return -ENOMEM; - if (!username) { - un = getlogname_malloc(); - if (!un) - return -ENOMEM; - } - - if (!origin_tty) { - getttyname_harder(STDIN_FILENO, &stdin_tty); - origin_tty = stdin_tty; - } - - if (asprintf(&text, - "\r\n" - "Broadcast message from %s@%s%s%s (%s):\r\n\r\n" - "%s\r\n\r\n", - un ?: username, hn, - origin_tty ? " on " : "", strempty(origin_tty), - FORMAT_TIMESTAMP(now(CLOCK_REALTIME)), - message) < 0) - return -ENOMEM; - - utmpx = utxent_start(); - - r = 0; - - while ((u = getutxent())) { - _cleanup_free_ char *buf = NULL; - const char *path; - int q; - - if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0) - continue; - - /* This access is fine, because strlen("/dev/") < 32 (UT_LINESIZE) */ - if (path_startswith(u->ut_line, "/dev/")) - path = u->ut_line; - else { - if (asprintf(&buf, "/dev/%.*s", (int) sizeof(u->ut_line), u->ut_line) < 0) - return -ENOMEM; - path = buf; - } - - /* It seems that the address field is always set for remote logins. - * For local logins and other local entries, we get [0,0,0,0]. */ - bool is_local = memeqzero(u->ut_addr_v6, sizeof(u->ut_addr_v6)); - - if (!match_tty || match_tty(path, is_local, userdata)) { - q = write_to_terminal(path, text); - if (q < 0) - r = q; - } - } - - return r; -} diff --git a/src/shared/utmp-wtmp.h b/src/shared/utmp-wtmp.h index 97be719abf..ec1e27771e 100644 --- a/src/shared/utmp-wtmp.h +++ b/src/shared/utmp-wtmp.h @@ -18,13 +18,6 @@ int utmp_put_runlevel(int runlevel, int previous); int utmp_put_dead_process(const char *id, pid_t pid, int code, int status); int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user); -int utmp_wall( - const char *message, - const char *username, - const char *origin_tty, - bool (*match_tty)(const char *tty, bool is_local, void *userdata), - void *userdata); - static inline bool utxent_start(void) { setutxent(); return true; @@ -54,13 +47,5 @@ static inline int utmp_put_dead_process(const char *id, pid_t pid, int code, int static inline int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) { return 0; } -static inline int utmp_wall( - const char *message, - const char *username, - const char *origin_tty, - bool (*match_tty)(const char *tty, bool is_local, void *userdata), - void *userdata) { - return 0; -} #endif /* ENABLE_UTMP */ diff --git a/src/shared/wall.c b/src/shared/wall.c new file mode 100644 index 0000000000..e99fd9029c --- /dev/null +++ b/src/shared/wall.c @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <errno.h> +#include <poll.h> +#include <string.h> +#include <unistd.h> + +#include "sd-login.h" + +#include "errno-util.h" +#include "fd-util.h" +#include "hostname-util.h" +#include "io-util.h" +#include "path-util.h" +#include "string-util.h" +#include "terminal-util.h" +#include "user-util.h" +#include "utmp-wtmp.h" +#include "wall.h" + +#define TIMEOUT_USEC (50 * USEC_PER_MSEC) + +static int write_to_terminal(const char *tty, const char *message) { + _cleanup_close_ int fd = -EBADF; + const char *p; + size_t left; + usec_t end; + + assert(tty); + assert(message); + + fd = open(tty, O_WRONLY|O_NONBLOCK|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return -errno; + if (!isatty(fd)) + return -ENOTTY; + + p = message; + left = strlen(message); + + end = usec_add(now(CLOCK_MONOTONIC), TIMEOUT_USEC); + + while (left > 0) { + ssize_t n; + usec_t t; + int k; + + t = now(CLOCK_MONOTONIC); + if (t >= end) + return -ETIME; + + k = fd_wait_for_event(fd, POLLOUT, end - t); + if (ERRNO_IS_NEG_TRANSIENT(k)) + continue; + if (k < 0) + return k; + if (k == 0) + return -ETIME; + + n = write(fd, p, left); + if (n < 0) { + if (ERRNO_IS_TRANSIENT(errno)) + continue; + + return -errno; + } + + assert((size_t) n <= left); + + p += n; + left -= n; + } + + return 0; +} + +#if ENABLE_UTMP +static int do_wall( + const char *message, + const char *username, + const char *origin_tty, + bool (*match_tty)(const char *tty, bool is_local, void *userdata), + void *userdata) { + + _unused_ _cleanup_(utxent_cleanup) bool utmpx = false; + struct utmpx *u; + int r; + + utmpx = utxent_start(); + + r = 0; + + while ((u = getutxent())) { + _cleanup_free_ char *buf = NULL; + const char *path; + int q; + + if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0) + continue; + + /* This access is fine, because strlen("/dev/") < 32 (UT_LINESIZE) */ + if (path_startswith(u->ut_line, "/dev/")) + path = u->ut_line; + else { + if (asprintf(&buf, "/dev/%.*s", (int) sizeof(u->ut_line), u->ut_line) < 0) + return -ENOMEM; + path = buf; + } + + /* It seems that the address field is always set for remote logins. + * For local logins and other local entries, we get [0,0,0,0]. */ + bool is_local = memeqzero(u->ut_addr_v6, sizeof(u->ut_addr_v6)); + + if (!match_tty || match_tty(path, is_local, userdata)) { + q = write_to_terminal(path, message); + if (q < 0) + r = q; + } + } + + return r; +} + +#else + +static int do_wall( + const char *message, + const char *username, + const char *origin_tty, + bool (*match_tty)(const char *tty, bool is_local, void *userdata), + void *userdata) { + + int r; + _cleanup_strv_free_ char **sessions = NULL; + + r = sd_get_sessions(&sessions); + if (r < 0) + return r; + + STRV_FOREACH(s, sessions) { + _cleanup_free_ char *path = NULL, *tty = NULL, *rhost = NULL; + int q; + + q = sd_session_get_tty(*s, &tty); + if (q < 0) { + if (q != -ENXIO && q != -ENODATA) + r = q; + continue; + } + + path = strjoin("/dev/", tty); + if (!path) + return -ENOMEM; + + (void) sd_session_get_remote_host(*s, &rhost); + bool is_local = !rhost; + + if (!match_tty || match_tty(path, is_local, userdata)) { + q = write_to_terminal(path, message); + if (q < 0) + r = q; + } + } + return r; +} + +#endif + +int wall( + const char *message, + const char *username, + const char *origin_tty, + bool (*match_tty)(const char *tty, bool is_local, void *userdata), + void *userdata) { + + _cleanup_free_ char *text = NULL, *hn = NULL, *un = NULL, *stdin_tty = NULL; + + hn = gethostname_malloc(); + if (!hn) + return -ENOMEM; + if (!username) { + un = getlogname_malloc(); + if (!un) + return -ENOMEM; + } + + if (!origin_tty) { + (void) getttyname_harder(STDIN_FILENO, &stdin_tty); + origin_tty = stdin_tty; + } + + if (asprintf(&text, + "\r\n" + "Broadcast message from %s@%s%s%s (%s):\r\n\r\n" + "%s\r\n\r\n", + un ?: username, hn, + origin_tty ? " on " : "", strempty(origin_tty), + FORMAT_TIMESTAMP(now(CLOCK_REALTIME)), + message) < 0) + return -ENOMEM; + + return do_wall(text, username, origin_tty, match_tty, userdata); +} diff --git a/src/shared/wall.h b/src/shared/wall.h new file mode 100644 index 0000000000..4423c39404 --- /dev/null +++ b/src/shared/wall.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <stdbool.h> + +int wall( + const char *message, + const char *username, + const char *origin_tty, + bool (*match_tty)(const char *tty, bool is_local, void *userdata), + void *userdata); diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 46d2307ad3..87c610bc73 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -41,7 +41,7 @@ #include "string-util.h" #include "strv.h" #include "terminal-util.h" -#include "utmp-wtmp.h" +#include "wall.h" static enum { ACTION_LIST, @@ -216,16 +216,16 @@ static int process_one_password_file(const char *filename) { return 0; case ACTION_WALL: { - _cleanup_free_ char *wall = NULL; + _cleanup_free_ char *msg = NULL; - if (asprintf(&wall, + if (asprintf(&msg, "Password entry required for \'%s\' (PID " PID_FMT ").\r\n" "Please enter password with the systemd-tty-ask-password-agent tool.", strna(message), pid) < 0) return log_oom(); - (void) utmp_wall(wall, NULL, NULL, wall_tty_match, NULL); + (void) wall(msg, NULL, NULL, wall_tty_match, NULL); return 0; } case ACTION_QUERY: diff --git a/test/README.testsuite b/test/README.testsuite index c0f1a2b102..bd72f413d0 100644 --- a/test/README.testsuite +++ b/test/README.testsuite @@ -191,7 +191,7 @@ the PR (set by the $UPSTREAM_PULL_REQUEST env variable) you'd like to debug: Now install necessary build & test dependencies: ## PPA with some newer Ubuntu packages required by upstream systemd -# add-apt-repository -y ppa:upstream-systemd-ci/systemd-ci +# add-apt-repository -y --enable-source ppa:upstream-systemd-ci/systemd-ci # apt build-dep -y systemd # apt install -y autopkgtest debhelper genisoimage git qemu-system-x86 |