shell: define three system credentials we can propagate into shell prompts and welcome messages

This commit is contained in:
Lennart Poettering 2024-09-06 16:30:54 +02:00
parent 8b29949a41
commit 229d4a9806
14 changed files with 142 additions and 9 deletions

10
TODO
View File

@ -130,14 +130,8 @@ Deprecations and removals:
Features: Features:
* introduce a new system credential "shell.prompt.extra" or so that may contain * maybe set shell.prompt.prefix credential in run0 to some warning emoji,
a string to include in $PS1. Inherit it down into gettys/logins and suchlike. i.e. ⚠️ or ☢️ or ⚡ or 👊 or 🧑‍🔧 or so.
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.
* introduce new structure Tpm2CombinedPolicy, that combines the various TPm2 * introduce new structure Tpm2CombinedPolicy, that combines the various TPm2
policy bits into one structure, i.e. public key info, pcr masks, pcrlock policy bits into one structure, i.e. public key info, pcr masks, pcrlock

View File

@ -268,6 +268,21 @@
<xi:include href="version-info.xml" xpointer="v245"/></listitem> <xi:include href="version-info.xml" xpointer="v245"/></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>$SHELL_PROMPT_PREFIX</varname></term>
<term><varname>$SHELL_PROMPT_SUFFIX</varname></term>
<term><varname>$SHELL_WELCOME</varname></term>
<listitem><para>These environment variables are initialized from the service credentials
<literal>shell.prompt.prefix</literal>, <literal>shell.prompt.suffix</literal> and
<literal>shell.welcome</literal> if set. They are passed to the invoked session processes, where they
are imported into any shell prompt (specifically <varname>$SHELL_PROMPT_PREFIX</varname> is added as
prefix to <varname>$PS1</varname>, and <varname>$SHELL_PROMPT_SUFFIX</varname> as suffix) or printed
on screen when a shell first initializes.</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
</variablelist> </variablelist>
<para>The following environment variables are read by the module and may be used by the PAM service to pass <para>The following environment variables are read by the module and may be used by the PAM service to pass

View File

@ -334,6 +334,27 @@
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>shell.prompt.prefix</varname></term>
<term><varname>shell.prompt.suffix</varname></term>
<listitem>
<para>Defines strings to prefix and suffix any interactive UNIX shell prompt with. For details see
<citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<xi:include href="version-info.xml" xpointer="v257"/>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>shell.welcome</varname></term>
<listitem>
<para>Define a string to print when an interactive UNIX shell initializes. For details see
<citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<xi:include href="version-info.xml" xpointer="v257"/>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><varname>system.machine_id</varname></term> <term><varname>system.machine_id</varname></term>
<listitem> <listitem>

View File

@ -188,7 +188,7 @@ mimepackagesdir = prefixdir / 'share/mime/packages'
configfiledir = get_option('configfiledir') configfiledir = get_option('configfiledir')
if configfiledir == '' if configfiledir == ''
configfiledir= sysconfdir configfiledir = sysconfdir
endif endif
pkgconfigfiledir = configfiledir / 'systemd' pkgconfigfiledir = configfiledir / 'systemd'
@ -228,6 +228,13 @@ if libcryptsetup_plugins_dir == ''
libcryptsetup_plugins_dir = libdir / 'cryptsetup' libcryptsetup_plugins_dir = libdir / 'cryptsetup'
endif 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') memory_accounting_default = get_option('memory-accounting-default')
status_unit_format_default = get_option('status-unit-format-default') status_unit_format_default = get_option('status-unit-format-default')
if status_unit_format_default == 'auto' 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('RC_LOCAL_PATH', get_option('rc-local'))
conf.set_quoted('SSHCONFDIR', sshconfdir) conf.set_quoted('SSHCONFDIR', sshconfdir)
conf.set_quoted('SSHDCONFDIR', sshdconfdir) conf.set_quoted('SSHDCONFDIR', sshdconfdir)
conf.set_quoted('SHELLPROFILEDIR', shellprofiledir)
conf.set_quoted('SYSCONF_DIR', sysconfdir) conf.set_quoted('SYSCONF_DIR', sysconfdir)
conf.set_quoted('SYSCTL_DIR', sysctldir) conf.set_quoted('SYSCTL_DIR', sysctldir)
conf.set_quoted('SYSTEMCTL_BINARY_PATH', bindir / 'systemctl') conf.set_quoted('SYSTEMCTL_BINARY_PATH', bindir / 'systemctl')
@ -2701,6 +2709,7 @@ subdir('man')
subdir('modprobe.d') subdir('modprobe.d')
subdir('network') subdir('network')
subdir('presets') subdir('presets')
subdir('profile.d')
subdir('shell-completion/bash') subdir('shell-completion/bash')
subdir('shell-completion/zsh') subdir('shell-completion/zsh')
subdir('sysctl.d') subdir('sysctl.d')
@ -2923,6 +2932,7 @@ summary({
'ssh server privilege separation directory' : sshdprivsepdir, 'ssh server privilege separation directory' : sshdprivsepdir,
'ssh client configuration directory' : sshconfdir, 'ssh client configuration directory' : sshconfdir,
'libcryptsetup plugins directory' : libcryptsetup_plugins_dir, 'libcryptsetup plugins directory' : libcryptsetup_plugins_dir,
'Shell profile directory' : shellprofiledir,
'RPM macros directory' : rpmmacrosdir, 'RPM macros directory' : rpmmacrosdir,
'modprobe.d directory' : modprobedir, 'modprobe.d directory' : modprobedir,
'D-Bus policy directory' : dbuspolicydir, 'D-Bus policy directory' : dbuspolicydir,

View File

@ -225,6 +225,8 @@ option('sshdprivsepdir', type : 'string',
description : 'directory for SSH privilege separation ["no" disables]', value : '/run/sshd') description : 'directory for SSH privilege separation ["no" disables]', value : '/run/sshd')
option('libcryptsetup-plugins-dir', type : 'string', option('libcryptsetup-plugins-dir', type : 'string',
description : 'directory for libcryptsetup plugins') description : 'directory for libcryptsetup plugins')
option('shellprofiledir', type : 'string',
description : 'directory for Shell profile drop-ins ["no" disables]')
option('docdir', type : 'string', option('docdir', type : 'string',
description : 'documentation directory') description : 'documentation directory')
option('install-sysconfdir', type : 'combo', choices : ['true', 'no-samples', 'false'], value : 'true', option('install-sysconfdir', type : 'combo', choices : ['true', 'no-samples', 'false'], value : 'true',

View File

@ -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

10
profile.d/meson.build Normal file
View File

@ -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

View File

@ -27,6 +27,7 @@
#include "cap-list.h" #include "cap-list.h"
#include "capability-util.h" #include "capability-util.h"
#include "cgroup-setup.h" #include "cgroup-setup.h"
#include "creds-util.h"
#include "devnum-util.h" #include "devnum-util.h"
#include "errno-util.h" #include "errno-util.h"
#include "fd-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; 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) { static bool validate_runtime_directory(pam_handle_t *handle, const char *path, uid_t uid) {
struct stat st; struct stat st;
@ -1192,6 +1218,19 @@ _public_ PAM_EXTERN int pam_sm_open_session(
if (r != PAM_SUCCESS) if (r != PAM_SUCCESS)
return r; 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) { if (vtnr > 0) {
char buf[DECIMAL_STR_MAX(vtnr)]; char buf[DECIMAL_STR_MAX(vtnr)];
sprintf(buf, "%u", vtnr); sprintf(buf, "%u", vtnr);

View File

@ -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 %}

View File

@ -36,6 +36,7 @@ in_files = [['etc.conf', ''],
['var.conf', ''], ['var.conf', ''],
['20-systemd-userdb.conf', 'ENABLE_SSH_USERDB_CONFIG'], ['20-systemd-userdb.conf', 'ENABLE_SSH_USERDB_CONFIG'],
['20-systemd-ssh-generator.conf', 'ENABLE_SSH_PROXY_CONFIG'], ['20-systemd-ssh-generator.conf', 'ENABLE_SSH_PROXY_CONFIG'],
['20-systemd-shell-extra.conf', 'LINK_SHELL_EXTRA_DROPIN'],
] ]
foreach pair : in_files foreach pair : in_files

View File

@ -40,6 +40,7 @@ ImportCredential=tty.console.agetty.*:agetty.
ImportCredential=tty.console.login.*:login. ImportCredential=tty.console.login.*:login.
ImportCredential=agetty.* ImportCredential=agetty.*
ImportCredential=login.* ImportCredential=login.*
ImportCredential=shell.*
[Install] [Install]
WantedBy=getty.target WantedBy=getty.target

View File

@ -46,3 +46,4 @@ ImportCredential=tty.container.%I.agetty.*:agetty.
ImportCredential=tty.container.%I.login.*:login. ImportCredential=tty.container.%I.login.*:login.
ImportCredential=agetty.* ImportCredential=agetty.*
ImportCredential=login.* ImportCredential=login.*
ImportCredential=shell.*

View File

@ -56,6 +56,7 @@ ImportCredential=tty.virtual.%I.agetty.*:agetty.
ImportCredential=tty.virtual.%I.login.*:login. ImportCredential=tty.virtual.%I.login.*:login.
ImportCredential=agetty.* ImportCredential=agetty.*
ImportCredential=login.* ImportCredential=login.*
ImportCredential=shell.*
# Unset locale for the console getty since the console has problems # Unset locale for the console getty since the console has problems
# displaying some internationalized messages. # displaying some internationalized messages.

View File

@ -50,6 +50,7 @@ ImportCredential=tty.serial.%I.agetty.*:agetty.
ImportCredential=tty.serial.%I.login.*:login. ImportCredential=tty.serial.%I.login.*:login.
ImportCredential=agetty.* ImportCredential=agetty.*
ImportCredential=login.* ImportCredential=login.*
ImportCredential=shell.*
[Install] [Install]
WantedBy=getty.target WantedBy=getty.target