From 229d4a980607e9478cf1935793652ddd9a14618b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 6 Sep 2024 16:30:54 +0200 Subject: [PATCH] shell: define three system credentials we can propagate into shell prompts and welcome messages --- TODO | 10 ++---- man/pam_systemd.xml | 15 +++++++++ man/systemd.system-credentials.xml | 21 ++++++++++++ meson.build | 12 ++++++- meson_options.txt | 2 ++ profile.d/70-systemd-shell-extra.sh | 25 +++++++++++++++ profile.d/meson.build | 10 ++++++ src/login/pam_systemd.c | 39 +++++++++++++++++++++++ tmpfiles.d/20-systemd-shell-extra.conf.in | 12 +++++++ tmpfiles.d/meson.build | 1 + units/console-getty.service.in | 1 + units/container-getty@.service.in | 1 + units/getty@.service.in | 1 + units/serial-getty@.service.in | 1 + 14 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 profile.d/70-systemd-shell-extra.sh create mode 100644 profile.d/meson.build create mode 100644 tmpfiles.d/20-systemd-shell-extra.conf.in diff --git a/TODO b/TODO index d2eeed503f0..83bce330d76 100644 --- a/TODO +++ b/TODO @@ -130,14 +130,8 @@ Deprecations and removals: Features: -* introduce a new system credential "shell.prompt.extra" or so that may contain - a string to include in $PS1. Inherit it down into gettys/logins and suchlike. - And provide an /etc/profile.d/*.sh drop-in which reads the credential if set, - and suffixes $PS1. Usecase: for wsl-like environments use this to include - additional information in prompts that is inherited from the execution - context. For example include 📦 emoji via this mechanism in the prompt. Or, - in run0 include a ☢️ emoji in the prompt. Or in mkosi include a 👷 emoji in - the prompt. You get the idea. +* maybe set shell.prompt.prefix credential in run0 to some warning emoji, + i.e. ⚠️ or ☢️ or ⚡ or 👊 or 🧑‍🔧 or so. * introduce new structure Tpm2CombinedPolicy, that combines the various TPm2 policy bits into one structure, i.e. public key info, pcr masks, pcrlock diff --git a/man/pam_systemd.xml b/man/pam_systemd.xml index 40a4564a22c..c55cdee894a 100644 --- a/man/pam_systemd.xml +++ b/man/pam_systemd.xml @@ -268,6 +268,21 @@ + + $SHELL_PROMPT_PREFIX + $SHELL_PROMPT_SUFFIX + $SHELL_WELCOME + + These environment variables are initialized from the service credentials + shell.prompt.prefix, shell.prompt.suffix and + shell.welcome if set. They are passed to the invoked session processes, where they + are imported into any shell prompt (specifically $SHELL_PROMPT_PREFIX is added as + prefix to $PS1, and $SHELL_PROMPT_SUFFIX as suffix) or printed + on screen when a shell first initializes. + + + + The following environment variables are read by the module and may be used by the PAM service to pass diff --git a/man/systemd.system-credentials.xml b/man/systemd.system-credentials.xml index f8c27d04acc..7962bb257ab 100644 --- a/man/systemd.system-credentials.xml +++ b/man/systemd.system-credentials.xml @@ -334,6 +334,27 @@ + + shell.prompt.prefix + shell.prompt.suffix + + Defines strings to prefix and suffix any interactive UNIX shell prompt with. For details see + pam_systemd8. + + + + + + + shell.welcome + + Define a string to print when an interactive UNIX shell initializes. For details see + pam_systemd8. + + + + + system.machine_id diff --git a/meson.build b/meson.build index c26302d25ca..80e0047281b 100644 --- a/meson.build +++ b/meson.build @@ -188,7 +188,7 @@ mimepackagesdir = prefixdir / 'share/mime/packages' configfiledir = get_option('configfiledir') if configfiledir == '' - configfiledir= sysconfdir + configfiledir = sysconfdir endif pkgconfigfiledir = configfiledir / 'systemd' @@ -228,6 +228,13 @@ if libcryptsetup_plugins_dir == '' libcryptsetup_plugins_dir = libdir / 'cryptsetup' endif +shellprofiledir = get_option('shellprofiledir') +if shellprofiledir == '' + shellprofiledir = sysconfdir / 'profile.d' +endif +conf.set10('LINK_SHELL_EXTRA_DROPIN', shellprofiledir != 'no' and not shellprofiledir.startswith('/usr/')) +conf.set('SHELLPROFILEDIR', shellprofiledir, description : 'shell profile directory') + memory_accounting_default = get_option('memory-accounting-default') status_unit_format_default = get_option('status-unit-format-default') if status_unit_format_default == 'auto' @@ -257,6 +264,7 @@ conf.set_quoted('RANDOM_SEED_DIR', randomseeddir) conf.set_quoted('RC_LOCAL_PATH', get_option('rc-local')) conf.set_quoted('SSHCONFDIR', sshconfdir) conf.set_quoted('SSHDCONFDIR', sshdconfdir) +conf.set_quoted('SHELLPROFILEDIR', shellprofiledir) conf.set_quoted('SYSCONF_DIR', sysconfdir) conf.set_quoted('SYSCTL_DIR', sysctldir) conf.set_quoted('SYSTEMCTL_BINARY_PATH', bindir / 'systemctl') @@ -2701,6 +2709,7 @@ subdir('man') subdir('modprobe.d') subdir('network') subdir('presets') +subdir('profile.d') subdir('shell-completion/bash') subdir('shell-completion/zsh') subdir('sysctl.d') @@ -2923,6 +2932,7 @@ summary({ 'ssh server privilege separation directory' : sshdprivsepdir, 'ssh client configuration directory' : sshconfdir, 'libcryptsetup plugins directory' : libcryptsetup_plugins_dir, + 'Shell profile directory' : shellprofiledir, 'RPM macros directory' : rpmmacrosdir, 'modprobe.d directory' : modprobedir, 'D-Bus policy directory' : dbuspolicydir, diff --git a/meson_options.txt b/meson_options.txt index 185aa85a8de..e90debdd3e0 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -225,6 +225,8 @@ option('sshdprivsepdir', type : 'string', description : 'directory for SSH privilege separation ["no" disables]', value : '/run/sshd') option('libcryptsetup-plugins-dir', type : 'string', description : 'directory for libcryptsetup plugins') +option('shellprofiledir', type : 'string', + description : 'directory for Shell profile drop-ins ["no" disables]') option('docdir', type : 'string', description : 'documentation directory') option('install-sysconfdir', type : 'combo', choices : ['true', 'no-samples', 'false'], value : 'true', diff --git a/profile.d/70-systemd-shell-extra.sh b/profile.d/70-systemd-shell-extra.sh new file mode 100644 index 00000000000..70be3341b95 --- /dev/null +++ b/profile.d/70-systemd-shell-extra.sh @@ -0,0 +1,25 @@ +# shellcheck shell=sh + +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# Import the additional shell prompt prefix and suffix strings into $PS1, and +# show the shell welcome string. These can be provisioned as system or service +# credentials shell.prompt.prefix, shell.prompt.suffix and shell.welcome, and +# are propagated into these environment variables by pam_systemd(8). + +if [ -n "$SHELL_PROMPT_PREFIX" ]; then + PS1="$SHELL_PROMPT_PREFIX$PS1" +fi + +if [ -n "$SHELL_PROMPT_SUFFIX" ]; then + PS1="$PS1$SHELL_PROMPT_SUFFIX" +fi + +if [ -n "$SHELL_WELCOME" ]; then + printf '%b\n' "$SHELL_WELCOME" +fi diff --git a/profile.d/meson.build b/profile.d/meson.build new file mode 100644 index 00000000000..b87dc18eaa5 --- /dev/null +++ b/profile.d/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +install_data('70-systemd-shell-extra.sh', install_dir : shellprofiledir.startswith('/usr/') ? shellprofiledir : libexecdir / 'profile.d') + +if conf.get('LINK_SHELL_EXTRA_DROPIN') == 1 + install_emptydir(shellprofiledir) + + meson.add_install_script(sh, '-c', + ln_s.format(libexecdir / 'profile.d' / '70-systemd-shell-extra.sh', shellprofiledir / '70-systemd-shell-extra.sh')) +endif diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index 40721ebfd9f..77b23d54dbd 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -27,6 +27,7 @@ #include "cap-list.h" #include "capability-util.h" #include "cgroup-setup.h" +#include "creds-util.h" #include "devnum-util.h" #include "errno-util.h" #include "fd-util.h" @@ -567,6 +568,31 @@ static int update_environment(pam_handle_t *handle, const char *key, const char return PAM_SUCCESS; } +static int propagate_credential_to_environment(pam_handle_t *handle, const char *credential, const char *varname) { + int r; + + assert(handle); + assert(credential); + assert(varname); + + _cleanup_free_ char *value = NULL; + + /* Read a service credential, and propagate it into an environment variable */ + + r = read_credential(credential, (void**) &value, /* ret_size= */ NULL); + if (r < 0) { + log_debug_errno(r, "Failed to read credential '%s', ignoring: %m", credential); + return PAM_SUCCESS; + } + + r = pam_misc_setenv(handle, varname, value, 0); + if (r != PAM_SUCCESS) + return pam_syslog_pam_error(handle, LOG_ERR, r, + "Failed to set environment variable %s: @PAMERR@", varname); + + return PAM_SUCCESS; +} + static bool validate_runtime_directory(pam_handle_t *handle, const char *path, uid_t uid) { struct stat st; @@ -1192,6 +1218,19 @@ _public_ PAM_EXTERN int pam_sm_open_session( if (r != PAM_SUCCESS) return r; + static const char *const propagate[] = { + "shell.prompt.prefix", "SHELL_PROMPT_PREFIX", + "shell.prompt.suffix", "SHELL_PROMPT_SUFFIX", + "shell.welcome", "SHELL_WELCOME", + NULL + }; + + STRV_FOREACH_PAIR(k, v, propagate) { + r = propagate_credential_to_environment(handle, *k, *v); + if (r != PAM_SUCCESS) + return r; + } + if (vtnr > 0) { char buf[DECIMAL_STR_MAX(vtnr)]; sprintf(buf, "%u", vtnr); diff --git a/tmpfiles.d/20-systemd-shell-extra.conf.in b/tmpfiles.d/20-systemd-shell-extra.conf.in new file mode 100644 index 00000000000..8ebe83dd697 --- /dev/null +++ b/tmpfiles.d/20-systemd-shell-extra.conf.in @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +{% if LINK_SHELL_EXTRA_DROPIN %} +L {{SHELLPROFILEDIR}}/70-systemd-shell-extra.sh - - - - {{LIBEXECDIR}}/profile.d/70-systemd-shell-extra.sh +{% endif %} diff --git a/tmpfiles.d/meson.build b/tmpfiles.d/meson.build index bec24ac7b4d..8d05abcfc14 100644 --- a/tmpfiles.d/meson.build +++ b/tmpfiles.d/meson.build @@ -36,6 +36,7 @@ in_files = [['etc.conf', ''], ['var.conf', ''], ['20-systemd-userdb.conf', 'ENABLE_SSH_USERDB_CONFIG'], ['20-systemd-ssh-generator.conf', 'ENABLE_SSH_PROXY_CONFIG'], + ['20-systemd-shell-extra.conf', 'LINK_SHELL_EXTRA_DROPIN'], ] foreach pair : in_files diff --git a/units/console-getty.service.in b/units/console-getty.service.in index 575a7770c9d..33e6368db15 100644 --- a/units/console-getty.service.in +++ b/units/console-getty.service.in @@ -40,6 +40,7 @@ ImportCredential=tty.console.agetty.*:agetty. ImportCredential=tty.console.login.*:login. ImportCredential=agetty.* ImportCredential=login.* +ImportCredential=shell.* [Install] WantedBy=getty.target diff --git a/units/container-getty@.service.in b/units/container-getty@.service.in index 6bee309bf5a..7573532d6dd 100644 --- a/units/container-getty@.service.in +++ b/units/container-getty@.service.in @@ -46,3 +46,4 @@ ImportCredential=tty.container.%I.agetty.*:agetty. ImportCredential=tty.container.%I.login.*:login. ImportCredential=agetty.* ImportCredential=login.* +ImportCredential=shell.* diff --git a/units/getty@.service.in b/units/getty@.service.in index 3ea95e1ee4f..f30bba406d5 100644 --- a/units/getty@.service.in +++ b/units/getty@.service.in @@ -56,6 +56,7 @@ ImportCredential=tty.virtual.%I.agetty.*:agetty. ImportCredential=tty.virtual.%I.login.*:login. ImportCredential=agetty.* ImportCredential=login.* +ImportCredential=shell.* # Unset locale for the console getty since the console has problems # displaying some internationalized messages. diff --git a/units/serial-getty@.service.in b/units/serial-getty@.service.in index a071241ef90..20a5eb2754f 100644 --- a/units/serial-getty@.service.in +++ b/units/serial-getty@.service.in @@ -50,6 +50,7 @@ ImportCredential=tty.serial.%I.agetty.*:agetty. ImportCredential=tty.serial.%I.login.*:login. ImportCredential=agetty.* ImportCredential=login.* +ImportCredential=shell.* [Install] WantedBy=getty.target