Compare commits

...

4 Commits

Author SHA1 Message Date
Yu Watanabe e149ba1b1f
Merge pull request #17429 from keszybz/revert-17188
Revert the change to allow arbitrary environment variable names
2020-10-24 12:16:44 +09:00
Zbigniew Jędrzejewski-Szmek 0dc9fd56a5 man: document what variables are allowed 2020-10-23 15:49:03 +02:00
Zbigniew Jędrzejewski-Szmek ff461576de Revert "basic/env-util: (mostly) follow POSIX for what variable names are allowed"
This reverts commit b45c068dd8.

I think the idea was generally sound, but didn't take into account the
limitations of show-environment and how it is used. People expect to be able to
eval systemctl show-environment output in bash, and no escaping syntax is
defined for environment *names* (we only do escaping for *values*). We could
skip such problematic variables in 'systemctl show-environment', and only allow
them to be inherited directly. But this would be confusing and ugly.

The original motivation for this change was that various import operations
would fail. a4ccce22d9 changed systemctl to filter
invalid variables in import-environment.
https://gitlab.gnome.org/GNOME/gnome-session/-/issues/71 does a similar change
in GNOME. So those problematic variables should not cause failures, but just
be silently ignored.

Finally, the environment block is becoming a dumping ground. In my gnome
session 'systemctl show-environment --user' includes stuff like PWD, FPATH
(from zsh), SHLVL=0 (no idea what that is). This is not directly related to
variable names (since all those are allowed under the stricter rules too), but
I think we should start pushing people away from running import-environment and
towards importing only select variables.

https://github.com/systemd/systemd/pull/17188#issuecomment-708676511
2020-10-23 15:07:07 +02:00
Zbigniew Jędrzejewski-Szmek 451ae5a11a basic/env-util: make function shorter 2020-10-23 13:49:05 +02:00
5 changed files with 45 additions and 49 deletions

View File

@ -1060,6 +1060,14 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<refsect2> <refsect2>
<title>Environment Commands</title> <title>Environment Commands</title>
<para><command>systemd</command> supports an environment block that is passed to processes the manager
spawns. The names of the variables can contain ASCII letters, digits, and the underscore
character. Variable names cannot be empty or start with a digit. In variable values, most characters
are allowed, but non-printable characters are currently rejected. The total length of the environment
block is limited to <constant>_SC_ARG_MAX</constant> value defined by
<citerefentry project='man-pages'><refentrytitle>sysconf</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
</para>
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term><command>show-environment</command></term> <term><command>show-environment</command></term>
@ -1091,8 +1099,9 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<term><command>set-environment <replaceable>VARIABLE=VALUE</replaceable></command></term> <term><command>set-environment <replaceable>VARIABLE=VALUE</replaceable></command></term>
<listitem> <listitem>
<para>Set one or more systemd manager environment variables, <para>Set one or more systemd manager environment variables, as specified on the command
as specified on the command line.</para> line. This command will fail if variable names and values do not conform to the rules listed
above.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
@ -1113,13 +1122,11 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
</term> </term>
<listitem> <listitem>
<para>Import all, one or more environment variables set on <para>Import all, one or more environment variables set on the client into the systemd manager
the client into the systemd manager environment block. If environment block. If no arguments are passed, the entire environment block is imported.
no arguments are passed, the entire environment block is Otherwise, a list of one or more environment variable names should be passed, whose client-side
imported. Otherwise, a list of one or more environment values are then imported into the manager's environment block. This command will silently ignore
variable names should be passed, whose client-side values any assignments which do not conform to the rules listed above.</para>
are then imported into the manager's environment
block.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
</variablelist> </variablelist>

View File

@ -2186,13 +2186,18 @@ SystemCallErrorNumber=EPERM</programlisting>
<varlistentry> <varlistentry>
<term><varname>Environment=</varname></term> <term><varname>Environment=</varname></term>
<listitem><para>Sets environment variables for executed processes. Takes a space-separated list of variable <listitem><para>Sets environment variables for executed processes. Takes a space-separated list of
assignments. This option may be specified more than once, in which case all listed variables will be set. If variable assignments. This option may be specified more than once, in which case all listed variables
the same variable is set twice, the later setting will override the earlier setting. If the empty string is will be set. If the same variable is set twice, the later setting will override the earlier
assigned to this option, the list of environment variables is reset, all prior assignments have no setting. If the empty string is assigned to this option, the list of environment variables is reset,
effect. Variable expansion is not performed inside the strings, however, specifier expansion is possible. The $ all prior assignments have no effect. Variable expansion is not performed inside the strings,
character has no special meaning. If you need to assign a value containing spaces or the equals sign to a however, specifier expansion is possible. The <literal>$</literal> character has no special
variable, use double quotes (") for the assignment.</para> meaning. If you need to assign a value containing spaces or the equals sign to a variable, use double
quotes (") for the assignment.</para>
<para>The names of the variables can contain ASCII letters, digits, and the underscore
character. Variable names cannot be empty or start with a digit. In variable values, most characters
are allowed, but non-printable characters are currently rejected.</para>
<para>Example: <para>Example:
<programlisting>Environment="VAR1=word1 word2" VAR2=word3 "VAR3=$word 5 6"</programlisting> <programlisting>Environment="VAR1=word1 word2" VAR2=word3 "VAR3=$word 5 6"</programlisting>

View File

@ -21,21 +21,18 @@
DIGITS LETTERS \ DIGITS LETTERS \
"_" "_"
static bool printable_portable_character(char c) {
/* POSIX.1-2008 specifies almost all ASCII characters as "portable". (Only DEL is excluded, and
* additionally NUL and = are not allowed in variable names). We are stricter, and additionally
* reject BEL, BS, HT, CR, LF, VT, FF and SPACE, i.e. all whitespace. */
return c >= '!' && c <= '~';
}
static bool env_name_is_valid_n(const char *e, size_t n) { static bool env_name_is_valid_n(const char *e, size_t n) {
const char *p;
if (!e) if (!e)
return false; return false;
if (n <= 0) if (n <= 0)
return false; return false;
if (e[0] >= '0' && e[0] <= '9')
return false;
/* POSIX says the overall size of the environment block cannot /* POSIX says the overall size of the environment block cannot
* be > ARG_MAX, an individual assignment hence cannot be * be > ARG_MAX, an individual assignment hence cannot be
* either. Discounting the equal sign and trailing NUL this * either. Discounting the equal sign and trailing NUL this
@ -44,18 +41,15 @@ static bool env_name_is_valid_n(const char *e, size_t n) {
if (n > (size_t) sysconf(_SC_ARG_MAX) - 2) if (n > (size_t) sysconf(_SC_ARG_MAX) - 2)
return false; return false;
for (const char *p = e; p < e + n; p++) for (p = e; p < e + n; p++)
if (!printable_portable_character(*p) || *p == '=') if (!strchr(VALID_BASH_ENV_NAME_CHARS, *p))
return false; return false;
return true; return true;
} }
bool env_name_is_valid(const char *e) { bool env_name_is_valid(const char *e) {
if (!e) return env_name_is_valid_n(e, strlen_ptr(e));
return false;
return env_name_is_valid_n(e, strlen(e));
} }
bool env_value_is_valid(const char *e) { bool env_value_is_valid(const char *e) {

View File

@ -274,12 +274,10 @@ static void test_env_clean(void) {
assert_se(streq(e[0], "FOOBAR=WALDO")); assert_se(streq(e[0], "FOOBAR=WALDO"));
assert_se(streq(e[1], "X=")); assert_se(streq(e[1], "X="));
assert_se(streq(e[2], "F=F")); assert_se(streq(e[2], "F=F"));
assert_se(streq(e[3], "0000=000")); assert_se(streq(e[3], "abcd=äöüß"));
assert_se(streq(e[4], "abcd=äöüß")); assert_se(streq(e[4], "xyz=xyz\n"));
assert_se(streq(e[5], "xyz=xyz\n")); assert_se(streq(e[5], "another=final one"));
assert_se(streq(e[6], "another=final one")); assert_se(e[6] == NULL);
assert_se(streq(e[7], "BASH_FUNC_foo%%=() { echo foo\n}"));
assert_se(e[8] == NULL);
} }
static void test_env_name_is_valid(void) { static void test_env_name_is_valid(void) {
@ -292,11 +290,8 @@ static void test_env_name_is_valid(void) {
assert_se(!env_name_is_valid("xxx\a")); assert_se(!env_name_is_valid("xxx\a"));
assert_se(!env_name_is_valid("xxx\007b")); assert_se(!env_name_is_valid("xxx\007b"));
assert_se(!env_name_is_valid("\007\009")); assert_se(!env_name_is_valid("\007\009"));
assert_se( env_name_is_valid("5_starting_with_a_number_is_unexpected_but_valid")); assert_se(!env_name_is_valid("5_starting_with_a_number_is_wrong"));
assert_se(!env_name_is_valid("#¤%&?_only_numbers_letters_and_underscore_allowed")); assert_se(!env_name_is_valid("#¤%&?_only_numbers_letters_and_underscore_allowed"));
assert_se( env_name_is_valid("BASH_FUNC_foo%%"));
assert_se(!env_name_is_valid("with spaces%%"));
assert_se(!env_name_is_valid("with\nnewline%%"));
} }
static void test_env_value_is_valid(void) { static void test_env_value_is_valid(void) {
@ -325,13 +320,9 @@ static void test_env_assignment_is_valid(void) {
assert_se(!env_assignment_is_valid("a b=")); assert_se(!env_assignment_is_valid("a b="));
assert_se(!env_assignment_is_valid("a =")); assert_se(!env_assignment_is_valid("a ="));
assert_se(!env_assignment_is_valid(" b=")); assert_se(!env_assignment_is_valid(" b="));
/* Names with dots and dashes makes those variables inaccessible as bash variables (as the syntax /* no dots or dashes: http://tldp.org/LDP/abs/html/gotchas.html */
* simply does not allow such variable names, see http://tldp.org/LDP/abs/html/gotchas.html). They assert_se(!env_assignment_is_valid("a.b="));
* are still valid variables according to POSIX though. */ assert_se(!env_assignment_is_valid("a-b="));
assert_se( env_assignment_is_valid("a.b="));
assert_se( env_assignment_is_valid("a-b="));
/* Those are not ASCII, so not valid according to POSIX (though zsh does allow unicode variable
* names). */
assert_se(!env_assignment_is_valid("\007=głąb kapuściany")); assert_se(!env_assignment_is_valid("\007=głąb kapuściany"));
assert_se(!env_assignment_is_valid("c\009=\007\009\011")); assert_se(!env_assignment_is_valid("c\009=\007\009\011"));
assert_se(!env_assignment_is_valid("głąb=printf \"\x1b]0;<mock-chroot>\x07<mock-chroot>\"")); assert_se(!env_assignment_is_valid("głąb=printf \"\x1b]0;<mock-chroot>\x07<mock-chroot>\""));

View File

@ -765,9 +765,8 @@ static void test_config_parse_pass_environ(void) {
"PassEnvironment", 0, "'invalid name' 'normal_name' A=1 'special_name$$' \\", "PassEnvironment", 0, "'invalid name' 'normal_name' A=1 'special_name$$' \\",
&passenv, NULL); &passenv, NULL);
assert_se(r >= 0); assert_se(r >= 0);
assert_se(strv_length(passenv) == 2); assert_se(strv_length(passenv) == 1);
assert_se(streq(passenv[0], "normal_name")); assert_se(streq(passenv[0], "normal_name"));
assert_se(streq(passenv[1], "special_name$$"));
} }
static void test_unit_dump_config_items(void) { static void test_unit_dump_config_items(void) {