Skip to content

Commit

Permalink
pager: make pager secure when under sudo or explicitly requested
Browse files Browse the repository at this point in the history
The variable is renamed to SYSTEMD_PAGERSECURE (because it's not just about
less now), and we automatically enable secure mode under sudo, but not otherwise.

This approach is more nuanced, but should provide a better experience for
users:

- Previusly we would set LESSSECURE=1 and trust the pager to make use of
  it. But this has an effect only on less. We need to not start pagers which
  are insecure when in secure mode. In particular more is like that and is a
  very popular pager.

- We don't enable secure mode always, which means that those other pagers can
  reasonably used.

- We do the right thing by default, but the user has ultimate control by
  setting SYSTEMD_PAGERSECURE.

Fixes systemd#5666.
  • Loading branch information
keszybz committed Oct 7, 2020
1 parent 612ebf6 commit 43f5a36
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 28 deletions.
23 changes: 17 additions & 6 deletions man/less-variables.xml
Expand Up @@ -65,12 +65,23 @@
</varlistentry>

<varlistentry id='lesssecure'>
<term><varname>$SYSTEMD_LESSSECURE</varname></term>

<listitem><para>Takes a boolean argument. Overrides the <varname>$LESSSECURE</varname> environment
variable when invoking the pager, which controls the "secure" mode of less (which disables commands
such as <literal>|</literal> which allow to easily shell out to external command lines). By default
less secure mode is enabled, with this setting it may be disabled.</para></listitem>
<term><varname>$SYSTEMD_PAGERSECURE</varname></term>

<listitem><para>Takes a boolean argument. When true, the "secure" mode of the pager is enabled; if
false, disabled. If <varname>$SYSTEMD_PAGERSECURE</varname> is not set at all, secure mode is enabled
by default when <varname>$SUDO_USER</varname> is set, and disabled otherwise. In secure mode
<option>LESSSECURE=1</option> will be set when invoking the pager and the pager shall disable commands
that open or create new files or start new subprocesses. When <varname>$SYSTEMD_PAGERSECURE</varname>
is not set at all, pagers which are not known to implement secure mode will not be used. (Currently
only <citerefentry><refentrytitle>less</refentrytitle><manvolnum>1</manvolnum></citerefentry>
implements secure mode.)</para>

<para>When operating under <citerefentry
project='die-net'><refentrytitle>sudo</refentrytitle><manvolnum>8</manvolnum></citerefentry>, the
secure mode is enabled be default. Setting <varname>SYSTEMD_PAGERSECURE=0</varname> or not removing it
from the inherited environment allows the user to invoke arbitrary commands. Note that if the
<varname>$SYSTEMD_PAGER</varname> or <varname>$PAGER</varname> variables are to be honoured,
<varname>$SYSTEMD_PAGERSECURE</varname> must be set too.</para></listitem>
</varlistentry>

<varlistentry id='colors'>
Expand Down
55 changes: 33 additions & 22 deletions src/shared/pager.c
Expand Up @@ -165,25 +165,34 @@ int pager_open(PagerFlags flags) {
}

/* People might invoke us from sudo, don't needlessly allow less to be a way to shell out
* privileged stuff. */
r = getenv_bool("SYSTEMD_LESSSECURE");
if (r == 0) { /* Remove env var if off */
if (unsetenv("LESSSECURE") < 0) {
log_error_errno(errno, "Failed to uset environment variable LESSSECURE: %m");
_exit(EXIT_FAILURE);
}
} else {
/* Set env var otherwise */
if (r < 0)
log_warning_errno(r, "Unable to parse $SYSTEMD_LESSSECURE, ignoring: %m");
* privileged stuff. If the user set $SYSTEMD_PAGERSECURE, trust their configuration of the
* pager. If they didn't, use secure mode when under sudo. If $SYSTEMD_PAGERSECURE wasn't
* explicitly set, only use the pager we know to be good. */
int secure = getenv_bool("SYSTEMD_PAGERSECURE");
bool trust_pager = secure >= 0;
if (secure == -ENXIO)
secure = !!getenv("SUDO_COMMAND");
else if (secure < 0) {
log_warning_errno(secure, "Unable to parse $SYSTEMD_PAGERSECURE, assuming true: %m");
secure = true;
}

if (setenv("LESSSECURE", "1", 1) < 0) {
log_error_errno(errno, "Failed to set environment variable LESSSECURE: %m");
_exit(EXIT_FAILURE);
}
/* We generally always set variables used by less, even if we end up using a different pager.
* They shouldn't hurt in any case, and ideally other pagers would look at them too. */
if (secure)
r = setenv("LESSSECURE", "1", 1);
else
r = unsetenv("LESSSECURE");
if (r < 0) {
log_error_errno(errno, "Failed to adjust environment variable LESSSECURE: %m");
_exit(EXIT_FAILURE);
}

if (pager_args) {
if (trust_pager && pager_args) { /* The pager config might be set globally, and we cannot
* know if the user adjusted it to be appropriate for the
* secure mode. Thus, start the pager specified through
* envvars only when $SYSTEMD_PAGERSECURE was explicitly set
* as well. */
r = loop_write(exe_name_pipe[1], pager_args[0], strlen(pager_args[0]) + 1, false);
if (r < 0) {
log_error_errno(r, "Failed to write pager name to socket: %m");
Expand All @@ -195,13 +204,14 @@ int pager_open(PagerFlags flags) {
"Failed to execute '%s', using fallback pagers: %m", pager_args[0]);
}

/* Debian's alternatives command for pagers is
* called 'pager'. Note that we do not call
* sensible-pagers here, since that is just a
* shell script that implements a logic that
* is similar to this one anyway, but is
* Debian-specific. */
/* Debian's alternatives command for pagers is called 'pager'. Note that we do not call
* sensible-pagers here, since that is just a shell script that implements a logic that is
* similar to this one anyway, but is Debian-specific. */
FOREACH_STRING(exe, "pager", "less", "more") {
/* Only less implements secure mode right now. */
if (secure && !streq(exe, "less"))
continue;

r = loop_write(exe_name_pipe[1], exe, strlen(exe) + 1, false);
if (r < 0) {
log_error_errno(r, "Failed to write pager name to socket: %m");
Expand All @@ -212,6 +222,7 @@ int pager_open(PagerFlags flags) {
"Failed to execute '%s', using next fallback pager: %m", exe);
}

/* Our builtin is also very secure. */
r = loop_write(exe_name_pipe[1], "(built-in)", strlen("(built-in)") + 1, false);
if (r < 0) {
log_error_errno(r, "Failed to write pager name to socket: %m");
Expand Down

0 comments on commit 43f5a36

Please sign in to comment.