New upstream version 245.5

This commit is contained in:
Michael Biebl 2020-04-18 20:24:24 +02:00
parent 7d4b9ad6f6
commit d0648cfe4f
61 changed files with 4247 additions and 428 deletions

View File

@ -412,3 +412,26 @@ as the best process to terminate and has been forcibly terminated by the
kernel.
Note that the memory pressure might or might not have been caused by @UNIT@.
-- b61fdac612e94b9182285b998843061f
Subject: Accepting user/group name @USER_GROUP_NAME@, which does not match strict user/group name rules.
Defined-By: systemd
Support: %SUPPORT_URL%
The user/group name @USER_GROUP_NAME@ has been specified, which is accepted
according the relaxed user/group name rules, but does not qualify under the
strict rules.
The strict user/group name rules written as regular expression are:
^[a-zA-Z_][a-zA-Z0-9_-]{0,30}$
The relaxed user/group name rules accept all names, except for the empty
string; names containing NUL bytes, control characters, colon or slash
characters; names not valid UTF-8; names with leading or trailing whitespace;
the strings "." or ".."; fully numeric strings, or strings beginning in a
hyphen and otherwise fully numeric.
For further details on strict and relaxed user/group name rules, see:
https://systemd.io/USER_NAMES

View File

@ -205,7 +205,8 @@ object. The following fields are currently defined:
UNIX user name. This field is the only mandatory field, all others are
optional. Corresponds with the `pw_name` field of of `struct passwd` and the
`sp_namp` field of `struct spwd` (i.e. the shadow user record stored in
`/etc/shadow`).
`/etc/shadow`). See [User/Group Name Syntax](https://systemd.io/USER_NAMES) for
the (relaxed) rules the various systemd components enforce on user/group names.
`realm` → The "realm" a user is defined in. This concept allows distinguishing
users with the same name that originate in different organizations or

File diff suppressed because it is too large Load Diff

View File

@ -217,6 +217,17 @@
will be used. </para></listitem>
</varlistentry>
<varlistentry>
<term><option>--file=<replaceable>GLOB</replaceable></option></term>
<listitem><para>Takes a file glob as an argument. If
specified, coredumpctl will operate on the specified journal
files matching <replaceable>GLOB</replaceable> instead of the
default runtime and system journal paths. May be specified
multiple times, in which case files will be suitably
interleaved.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-D</option> <replaceable>DIR</replaceable></term>
<term><option>--directory=</option><replaceable>DIR</replaceable></term>

View File

@ -677,7 +677,10 @@
<listitem><para>Create a new home directory/user account of the specified name. Use the various
user record property options (as documented above) to control various aspects of the home directory
and its user accounts.</para></listitem>
and its user accounts.</para>
<para>The specified user name should follow the strict syntax described on <ulink
url="https://systemd.io/USER_NAMES">User/Group Name Syntax</ulink>.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -398,8 +398,8 @@
<varlistentry>
<term><option>--output-fields=</option></term>
<listitem><para>A comma separated list of the fields which should be included in the output. This only has an
effect for the output modes which would normally show all fields (<option>verbose</option>,
<listitem><para>A comma separated list of the fields which should be included in the output. This has an
effect only for the output modes which would normally show all fields (<option>verbose</option>,
<option>export</option>, <option>json</option>, <option>json-pretty</option>, <option>json-sse</option> and
<option>json-seq</option>). The <literal>__CURSOR</literal>, <literal>__REALTIME_TIMESTAMP</literal>,
<literal>__MONOTONIC_TIMESTAMP</literal>, and <literal>_BOOT_ID</literal> fields are always
@ -416,8 +416,13 @@
<varlistentry>
<term><option>--no-hostname</option></term>
<listitem><para>Don't show the hostname field of log messages originating from the local host. This switch only
has an effect on the <option>short</option> family of output modes (see above).</para></listitem>
<listitem><para>Don't show the hostname field of log messages originating from the local host. This
switch has an effect only on the <option>short</option> family of output modes (see above).
</para>
<para>Note: this option does not remove occurrences of the hostname from log entries themselves, so
it does not prevent the hostname from being visible in the logs.</para>
</listitem>
</varlistentry>
<varlistentry>

View File

@ -146,8 +146,10 @@
<varlistentry>
<term><option>--slice=</option></term>
<listitem><para>Make the new <filename>.service</filename> or <filename>.scope</filename> unit part of the
specified slice, instead of <filename>system.slice</filename>.</para>
<listitem><para>Make the new <filename>.service</filename> or <filename>.scope</filename> unit part
of the specified slice, instead of <filename>system.slice</filename> (when running in
<option>--system</option> mode) or the root slice (when running in <option>--user</option>
mode).</para>
</listitem>
</varlistentry>

View File

@ -217,12 +217,15 @@
is set, the default group of the user is used. This setting does not affect commands whose command line is
prefixed with <literal>+</literal>.</para>
<para>Note that restrictions on the user/group name syntax are enforced: the specified name must consist only
of the characters a-z, A-Z, 0-9, <literal>_</literal> and <literal>-</literal>, except for the first character
which must be one of a-z, A-Z or <literal>_</literal> (i.e. numbers and <literal>-</literal> are not permitted
as first character). The user/group name must have at least one character, and at most 31. These restrictions
are enforced in order to avoid ambiguities and to ensure user/group names and unit files remain portable among
Linux systems.</para>
<para>Note that this enforces only weak restrictions on the user/group name syntax, but will generate
warnings in many cases where user/group names do not adhere to the following rules: the specified
name should consist only of the characters a-z, A-Z, 0-9, <literal>_</literal> and
<literal>-</literal>, except for the first character which must be one of a-z, A-Z and
<literal>_</literal> (i.e. digits and <literal>-</literal> are not permitted as first character). The
user/group name must have at least one character, and at most 31. These restrictions are made in
order to avoid ambiguities and to ensure user/group names and unit files remain portable among Linux
systems. For further details on the names accepted and the names warned about see <ulink
url="https://systemd.io/USER_NAMES">User/Group Name Syntax</ulink>.</para>
<para>When used in conjunction with <varname>DynamicUser=</varname> the user/group name specified is
dynamically allocated at the time the service is started, and released at the time the service is

View File

@ -423,6 +423,9 @@
<varname>ExecStart=</varname>, or <varname>ExecStartPost=</varname> fail (and are not prefixed with
<literal>-</literal>, see above) or time out before the service is fully up, execution continues with commands
specified in <varname>ExecStopPost=</varname>, the commands in <varname>ExecStop=</varname> are skipped.</para>
<para>Note that the execution of <varname>ExecStartPost=</varname> is taken into account for the purpose of
<varname>Before=</varname>/<varname>After=</varname> ordering constraints.</para>
</listitem>
</varlistentry>
@ -534,7 +537,10 @@
service, as well as the main process' exit code and status, set in the <varname>$SERVICE_RESULT</varname>,
<varname>$EXIT_CODE</varname> and <varname>$EXIT_STATUS</varname> environment variables, see
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details.</para></listitem>
details.</para>
<para>Note that the execution of <varname>ExecStopPost=</varname> is taken into account for the purpose of
<varname>Before=</varname>/<varname>After=</varname> ordering constraints.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -750,7 +750,7 @@
type when precisely a unit has finished starting up. Most importantly, for service units start-up is
considered completed for the purpose of <varname>Before=</varname>/<varname>After=</varname> when all
its configured start-up commands have been invoked and they either failed or reported start-up
success.</para>
success. Note that this does includes ExecStartPost (or ExecStopPost for the shutdown case).</para>
<para>Note that those settings are independent of and orthogonal to the requirement dependencies as
configured by <varname>Requires=</varname>, <varname>Wants=</varname>, <varname>Requisite=</varname>,

View File

@ -154,6 +154,9 @@ r - 500-900
A-Z or <literal>_</literal> (i.e. numbers and <literal>-</literal> are not permitted as first character). The
user/group name must have at least one character, and at most 31.</para>
<para>For further details about the syntax of user/group names, see <ulink
url="https://systemd.io/USER_NAMES">User/Group Name Syntax</ulink>.</para>
<para>It is strongly recommended to pick user and group names that are unlikely to clash with normal users
created by the administrator. A good scheme to guarantee this is by prefixing all system and group names with the
underscore, and avoiding too generic names.</para>

View File

@ -124,10 +124,13 @@ static int verify_socket(Unit *u) {
return 0;
}
static int verify_executable(Unit *u, ExecCommand *exec) {
int verify_executable(Unit *u, const ExecCommand *exec) {
if (!exec)
return 0;
if (exec->flags & EXEC_COMMAND_IGNORE_FAILURE)
return 0;
if (access(exec->path, X_OK) < 0)
return log_unit_error_errno(u, errno, "Command %s is not executable: %m", exec->path);

View File

@ -3,6 +3,8 @@
#include <stdbool.h>
#include "execute.h"
#include "path-lookup.h"
int verify_executable(Unit *u, const ExecCommand *exec);
int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators);

19
src/analyze/test-verify.c Normal file
View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "analyze-verify.h"
#include "tests.h"
static void test_verify_nonexistent(void) {
/* Negative cases */
assert_se(verify_executable(NULL, &(ExecCommand) {.flags = EXEC_COMMAND_IGNORE_FAILURE, .path = (char*) "/non/existent"}) == 0);
assert_se(verify_executable(NULL, &(ExecCommand) {.path = (char*) "/non/existent"}) < 0);
/* Ordinary cases */
assert_se(verify_executable(NULL, &(ExecCommand) {.path = (char*) "/bin/echo"}) == 0);
assert_se(verify_executable(NULL, &(ExecCommand) {.flags = EXEC_COMMAND_IGNORE_FAILURE, .path = (char*) "/bin/echo"}) == 0);
}
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
test_verify_nonexistent();
}

View File

@ -107,11 +107,13 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
unsigned long i;
int r;
/* Add the capabilities to the ambient set (an possibly also the inheritable set) */
/* Check that we can use PR_CAP_AMBIENT or quit early. */
if (!ambient_capabilities_supported())
return 0;
/* Add the capabilities to the ambient set. */
return (set & all_capabilities()) == 0 ?
0 : -EOPNOTSUPP; /* if actually no ambient caps are to be set, be silent,
* otherwise fail recognizably */
if (also_inherit) {
caps = cap_get_proc();

View File

@ -81,7 +81,7 @@ int write_string_stream_ts(
struct timespec *ts) {
bool needs_nl;
int r;
int r, fd;
assert(f);
assert(line);
@ -89,6 +89,14 @@ int write_string_stream_ts(
if (ferror(f))
return -EIO;
if (ts) {
/* If we shall set the timestamp we need the fd. But fmemopen() streams generally don't have
* an fd. Let's fail early in that case. */
fd = fileno(f);
if (fd < 0)
return -EBADF;
}
needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n");
if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) {
@ -116,7 +124,7 @@ int write_string_stream_ts(
if (ts) {
struct timespec twice[2] = {*ts, *ts};
if (futimens(fileno(f), twice) < 0)
if (futimens(fd, twice) < 0)
return -errno;
}
@ -848,7 +856,7 @@ int fflush_and_check(FILE *f) {
}
int fflush_sync_and_check(FILE *f) {
int r;
int r, fd;
assert(f);
@ -856,10 +864,16 @@ int fflush_sync_and_check(FILE *f) {
if (r < 0)
return r;
if (fsync(fileno(f)) < 0)
/* Not all file streams have an fd associated (think: fmemopen()), let's handle this gracefully and
* assume that in that case we need no explicit syncing */
fd = fileno(f);
if (fd < 0)
return 0;
if (fsync(fd) < 0)
return -errno;
r = fsync_directory_of_file(fileno(f));
r = fsync_directory_of_file(fd);
if (r < 0)
return r;
@ -957,7 +971,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, funlockfile);
int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
size_t n = 0, allocated = 0, count = 0;
_cleanup_free_ char *buffer = NULL;
int r, tty = -1;
int r;
assert(f);
@ -1032,13 +1046,23 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
count++;
if (eol != EOL_NONE) {
/* If we are on a tty, we can't wait for more input. But we expect only
* \n as the single EOL marker, so there is no need to wait. We check
* this condition last to avoid isatty() check if not necessary. */
/* If we are on a tty, we can't shouldn't wait for more input, because that
* generally means waiting for the user, interactively. In the case of a TTY
* we expect only \n as the single EOL marker, so we are in the lucky
* position that there is no need to wait. We check this condition last, to
* avoid isatty() check if not necessary. */
if (tty < 0)
tty = isatty(fileno(f));
if (tty > 0)
if ((flags & (READ_LINE_IS_A_TTY|READ_LINE_NOT_A_TTY)) == 0) {
int fd;
fd = fileno(f);
if (fd < 0) /* Maybe an fmemopen() stream? Handle this gracefully,
* and don't call isatty() on an invalid fd */
flags |= READ_LINE_NOT_A_TTY;
else
flags |= isatty(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY;
}
if (FLAGS_SET(flags, READ_LINE_IS_A_TTY))
break;
}

View File

@ -85,7 +85,9 @@ int read_timestamp_file(const char *fn, usec_t *ret);
int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space);
typedef enum ReadLineFlags {
READ_LINE_ONLY_NUL = 1 << 0,
READ_LINE_ONLY_NUL = 1 << 0,
READ_LINE_IS_A_TTY = 1 << 1,
READ_LINE_NOT_A_TTY = 1 << 2,
} ReadLineFlags;
int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret);

View File

@ -113,7 +113,7 @@ static size_t strcspn_escaped(const char *s, const char *reject) {
bool escaped = false;
int n;
for (n=0; s[n]; n++) {
for (n = 0; s[n] != '\0'; n++) {
if (escaped)
escaped = false;
else if (s[n] == '\\')
@ -122,8 +122,7 @@ static size_t strcspn_escaped(const char *s, const char *reject) {
break;
}
/* if s ends in \, return index of previous char */
return n - escaped;
return n;
}
/* Split a string into words. */

View File

@ -81,31 +81,34 @@ int chvt(int vt) {
int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
_cleanup_free_ char *line = NULL;
struct termios old_termios;
int r;
int r, fd;
assert(f);
assert(ret);
/* If this is a terminal, then switch canonical mode off, so that we can read a single character */
if (tcgetattr(fileno(f), &old_termios) >= 0) {
/* If this is a terminal, then switch canonical mode off, so that we can read a single
* character. (Note that fmemopen() streams do not have an fd associated with them, let's handle that
* nicely.) */
fd = fileno(f);
if (fd >= 0 && tcgetattr(fd, &old_termios) >= 0) {
struct termios new_termios = old_termios;
new_termios.c_lflag &= ~ICANON;
new_termios.c_cc[VMIN] = 1;
new_termios.c_cc[VTIME] = 0;
if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) {
if (tcsetattr(fd, TCSADRAIN, &new_termios) >= 0) {
char c;
if (t != USEC_INFINITY) {
if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
(void) tcsetattr(fileno(f), TCSADRAIN, &old_termios);
if (fd_wait_for_event(fd, POLLIN, t) <= 0) {
(void) tcsetattr(fd, TCSADRAIN, &old_termios);
return -ETIMEDOUT;
}
}
r = safe_fgetc(f, &c);
(void) tcsetattr(fileno(f), TCSADRAIN, &old_termios);
(void) tcsetattr(fd, TCSADRAIN, &old_termios);
if (r < 0)
return r;
if (r == 0)
@ -119,8 +122,13 @@ int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
}
}
if (t != USEC_INFINITY) {
if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0)
if (t != USEC_INFINITY && fd > 0) {
/* Let's wait the specified amount of time for input. When we have no fd we skip this, under
* the assumption that this is an fmemopen() stream or so where waiting doesn't make sense
* anyway, as the data is either already in the stream or cannot possible be placed there
* while we access the stream */
if (fd_wait_for_event(fd, POLLIN, t) <= 0)
return -ETIMEDOUT;
}
@ -778,6 +786,9 @@ const char *default_term_for_tty(const char *tty) {
int fd_columns(int fd) {
struct winsize ws = {};
if (fd < 0)
return -EBADF;
if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
return -errno;
@ -812,6 +823,9 @@ unsigned columns(void) {
int fd_lines(int fd) {
struct winsize ws = {};
if (fd < 0)
return -EBADF;
if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
return -errno;

View File

@ -10,6 +10,8 @@
#include <unistd.h>
#include <utmp.h>
#include "sd-messages.h"
#include "alloc-util.h"
#include "errno-util.h"
#include "fd-util.h"
@ -18,6 +20,7 @@
#include "macro.h"
#include "parse-util.h"
#include "path-util.h"
#include "path-util.h"
#include "random-util.h"
#include "string-util.h"
#include "strv.h"
@ -698,92 +701,125 @@ int take_etc_passwd_lock(const char *root) {
return fd;
}
bool valid_user_group_name_full(const char *u, bool strict) {
bool valid_user_group_name(const char *u, ValidUserFlags flags) {
const char *i;
long sz;
bool warned = false;
/* Checks if the specified name is a valid user/group name. Also see POSIX IEEE Std 1003.1-2008, 2016 Edition,
* 3.437. We are a bit stricter here however. Specifically we deviate from POSIX rules:
/* Checks if the specified name is a valid user/group name. There are two flavours of this call:
* strict mode is the default which is POSIX plus some extra rules; and relaxed mode where we accept
* pretty much everything except the really worst offending names.
*
* - We require that names fit into the appropriate utmp field
* - We don't allow empty user names
* - No dots in the first character
*
* If strict==true, additionally:
* - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator)
* - We don't allow a digit as the first character
*
* Note that other systems are even more restrictive, and don't permit underscores or uppercase characters.
*/
* Whenever we synthesize users ourselves we should use the strict mode. But when we process users
* created by other stuff, let's be more liberal. */
if (isempty(u))
if (isempty(u)) /* An empty user name is never valid */
return false;
if (!(u[0] >= 'a' && u[0] <= 'z') &&
!(u[0] >= 'A' && u[0] <= 'Z') &&
!(u[0] >= '0' && u[0] <= '9' && !strict) &&
u[0] != '_')
return false;
if (parse_uid(u, NULL) >= 0) /* Something that parses as numeric UID string is valid exactly when the
* flag for it is set */
return FLAGS_SET(flags, VALID_USER_ALLOW_NUMERIC);
bool only_digits_seen = u[0] >= '0' && u[0] <= '9';
if (FLAGS_SET(flags, VALID_USER_RELAX)) {
if (only_digits_seen) {
log_warning("User or group name \"%s\" starts with a digit, accepting for compatibility.", u);
warned = true;
/* In relaxed mode we just check very superficially. Apparently SSSD and other stuff is
* extremely liberal (way too liberal if you ask me, even inserting "@" in user names, which
* is bound to cause problems for example when used with an MTA), hence only filter the most
* obvious cases, or where things would result in an invalid entry if such a user name would
* show up in /etc/passwd (or equivalent getent output).
*
* Note that we stepped far out of POSIX territory here. It's not our fault though, but
* SSSD's, Samba's and everybody else who ignored POSIX on this. (I mean, I am happy to step
* outside of POSIX' bounds any day, but I must say in this case I probably wouldn't
* have...) */
if (startswith(u, " ") || endswith(u, " ")) /* At least expect whitespace padding is removed
* at front and back (accept in the middle, since
* that's apparently a thing on Windows). Note
* that this also blocks usernames consisting of
* whitespace only. */
return false;
if (!utf8_is_valid(u)) /* We want to synthesize JSON from this, hence insist on UTF-8 */
return false;
if (string_has_cc(u, NULL)) /* CC characters are just dangerous (and \n in particular is the
* record separator in /etc/passwd), so we can't allow that. */
return false;
if (strpbrk(u, ":/")) /* Colons are the field separator in /etc/passwd, we can't allow
* that. Slashes are special to file systems paths and user names
* typically show up in the file system as home directories, hence
* don't allow slashes. */
return false;
if (in_charset(u, "0123456789")) /* Don't allow fully numeric strings, they might be confused
* with with UIDs (note that this test is more broad than
* the parse_uid() test above, as it will cover more than
* the 32bit range, and it will detect 65535 (which is in
* invalid UID, even though in the unsigned 32 bit range) */
return false;
if (u[0] == '-' && in_charset(u + 1, "0123456789")) /* Don't allow negative fully numeric
* strings either. After all some people
* write 65535 as -1 (even though that's
* not even true on 32bit uid_t
* anyway) */
return false;
if (dot_or_dot_dot(u)) /* User names typically become home directory names, and these two are
* special in that context, don't allow that. */
return false;
/* Compare with strict result and warn if result doesn't match */
if (FLAGS_SET(flags, VALID_USER_WARN) && !valid_user_group_name(u, 0))
log_struct(LOG_NOTICE,
"MESSAGE=Accepting user/group name '%s', which does not match strict user/group name rules.", u,
"USER_GROUP_NAME=%s", u,
"MESSAGE_ID=" SD_MESSAGE_UNSAFE_USER_NAME_STR);
/* Note that we make no restrictions on the length in relaxed mode! */
} else {
long sz;
size_t l;
/* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.437. We are a bit stricter here
* however. Specifically we deviate from POSIX rules:
*
* - We don't allow empty user names (see above)
* - We require that names fit into the appropriate utmp field
* - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator)
* - We don't allow dashes or digit as the first character
*
* Note that other systems are even more restrictive, and don't permit underscores or uppercase characters.
*/
if (!(u[0] >= 'a' && u[0] <= 'z') &&
!(u[0] >= 'A' && u[0] <= 'Z') &&
u[0] != '_')
return false;
for (i = u+1; *i; i++)
if (!(*i >= 'a' && *i <= 'z') &&
!(*i >= 'A' && *i <= 'Z') &&
!(*i >= '0' && *i <= '9') &&
!IN_SET(*i, '_', '-'))
return false;
l = i - u;
sz = sysconf(_SC_LOGIN_NAME_MAX);
assert_se(sz > 0);
if (l > (size_t) sz)
return false;
if (l > FILENAME_MAX)
return false;
if (l > UT_NAMESIZE - 1)
return false;
}
for (i = u+1; *i; i++) {
if (((*i >= 'a' && *i <= 'z') ||
(*i >= 'A' && *i <= 'Z') ||
(*i >= '0' && *i <= '9') ||
IN_SET(*i, '_', '-'))) {
if (!(*i >= '0' && *i <= '9'))
only_digits_seen = false;
continue;
}
if (*i == '.' && !strict) {
if (!warned) {
log_warning("Bad user or group name \"%s\", accepting for compatibility.", u);
warned = true;
}
continue;
}
return false;
}
if (only_digits_seen)
return false;
sz = sysconf(_SC_LOGIN_NAME_MAX);
assert_se(sz > 0);
if ((size_t) (i-u) > (size_t) sz)
return false;
if ((size_t) (i-u) > UT_NAMESIZE - 1)
return false;
return true;
}
bool valid_user_group_name_or_id_full(const char *u, bool strict) {
/* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the
* right range, and not the invalid user ids. */
if (isempty(u))
return false;
if (parse_uid(u, NULL) >= 0)
return true;
return valid_user_group_name_full(u, strict);
}
bool valid_gecos(const char *d) {
if (!d)

View File

@ -97,20 +97,13 @@ static inline bool userns_supported(void) {
return access("/proc/self/uid_map", F_OK) >= 0;
}
bool valid_user_group_name_full(const char *u, bool strict);
bool valid_user_group_name_or_id_full(const char *u, bool strict);
static inline bool valid_user_group_name(const char *u) {
return valid_user_group_name_full(u, true);
}
static inline bool valid_user_group_name_or_id(const char *u) {
return valid_user_group_name_or_id_full(u, true);
}
static inline bool valid_user_group_name_compat(const char *u) {
return valid_user_group_name_full(u, false);
}
static inline bool valid_user_group_name_or_id_compat(const char *u) {
return valid_user_group_name_or_id_full(u, false);
}
typedef enum ValidUserFlags {
VALID_USER_RELAX = 1 << 0,
VALID_USER_WARN = 1 << 1,
VALID_USER_ALLOW_NUMERIC = 1 << 2,
} ValidUserFlags;
bool valid_user_group_name(const char *u, ValidUserFlags flags);
bool valid_gecos(const char *d);
bool valid_home(const char *p);

View File

@ -462,13 +462,14 @@ int detect_container(void) {
goto finish;
}
/* "Official" way of detecting WSL https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364 */
/* "Official" way of detecting WSL https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364,
* ... and a working one, since the official one doesn't actually work ;(
*/
r = read_one_line_file("/proc/sys/kernel/osrelease", &o);
if (r >= 0) {
if (strstr(o, "Microsoft") || strstr(o, "WSL")) {
r = VIRTUALIZATION_WSL;
goto finish;
}
if (r >= 0 &&
(strstr(o, "Microsoft") || strstr(o, "microsoft") || strstr(o, "WSL"))) {
r = VIRTUALIZATION_WSL;
goto finish;
}
if (getpid_cached() == 1) {

View File

@ -795,6 +795,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("MountFlags", "t", bus_property_get_ulong, offsetof(ExecContext, mount_flags), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PrivateTmp", "b", bus_property_get_bool, offsetof(ExecContext, private_tmp), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PrivateDevices", "b", bus_property_get_bool, offsetof(ExecContext, private_devices), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ProtectClock", "b", bus_property_get_bool, offsetof(ExecContext, protect_clock), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ProtectKernelTunables", "b", bus_property_get_bool, offsetof(ExecContext, protect_kernel_tunables), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ProtectKernelModules", "b", bus_property_get_bool, offsetof(ExecContext, protect_kernel_modules), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ProtectKernelLogs", "b", bus_property_get_bool, offsetof(ExecContext, protect_kernel_logs), SD_BUS_VTABLE_PROPERTY_CONST),
@ -1203,10 +1204,10 @@ int bus_exec_context_set_transient_property(
flags |= UNIT_PRIVATE;
if (streq(name, "User"))
return bus_set_transient_user_compat(u, name, &c->user, message, flags, error);
return bus_set_transient_user_relaxed(u, name, &c->user, message, flags, error);
if (streq(name, "Group"))
return bus_set_transient_user_compat(u, name, &c->group, message, flags, error);
return bus_set_transient_user_relaxed(u, name, &c->group, message, flags, error);
if (streq(name, "TTYPath"))
return bus_set_transient_path(u, name, &c->tty_path, message, flags, error);
@ -1391,7 +1392,7 @@ int bus_exec_context_set_transient_property(
return r;
STRV_FOREACH(p, l)
if (!isempty(*p) && !valid_user_group_name_or_id_compat(*p))
if (!isempty(*p) && !valid_user_group_name(*p, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX|VALID_USER_WARN))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid supplementary group names");

View File

@ -1643,7 +1643,7 @@ static int method_lookup_dynamic_user_by_name(sd_bus_message *message, void *use
if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Dynamic users are only supported in the system instance.");
if (!valid_user_group_name(name))
if (!valid_user_group_name(name, VALID_USER_RELAX))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name invalid: %s", name);
r = dynamic_user_lookup_name(m, name, &uid);

View File

@ -278,10 +278,10 @@ static int bus_socket_set_transient_property(
return bus_set_transient_fdname(u, name, &s->fdname, message, flags, error);
if (streq(name, "SocketUser"))
return bus_set_transient_user_compat(u, name, &s->user, message, flags, error);
return bus_set_transient_user_relaxed(u, name, &s->user, message, flags, error);
if (streq(name, "SocketGroup"))
return bus_set_transient_user_compat(u, name, &s->group, message, flags, error);
return bus_set_transient_user_relaxed(u, name, &s->group, message, flags, error);
if (streq(name, "BindIPv6Only"))
return bus_set_transient_bind_ipv6_only(u, name, &s->bind_ipv6_only, message, flags, error);

View File

@ -30,7 +30,12 @@ int bus_property_get_triggered_unit(
BUS_DEFINE_SET_TRANSIENT(mode_t, "u", uint32_t, mode_t, "%040o");
BUS_DEFINE_SET_TRANSIENT(unsigned, "u", uint32_t, unsigned, "%" PRIu32);
BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(user_compat, valid_user_group_name_or_id_compat);
static inline bool valid_user_group_name_or_id_relaxed(const char *u) {
return valid_user_group_name(u, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX);
}
BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(user_relaxed, valid_user_group_name_or_id_relaxed);
BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(path, path_is_absolute);
int bus_set_transient_string(

View File

@ -236,7 +236,7 @@ int bus_property_get_triggered_unit(sd_bus *bus, const char *path, const char *i
int bus_set_transient_mode_t(Unit *u, const char *name, mode_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_unsigned(Unit *u, const char *name, unsigned *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_user_compat(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_user_relaxed(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_path(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_string(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_bool(Unit *u, const char *name, bool *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);

View File

@ -116,7 +116,7 @@ static int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret)
return 0;
}
if (!valid_user_group_name_or_id(name))
if (!valid_user_group_name(name, VALID_USER_ALLOW_NUMERIC))
return -EINVAL;
if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, storage_socket) < 0)

View File

@ -1577,7 +1577,7 @@ static int apply_protect_kernel_logs(const Unit *u, const ExecContext *c) {
return seccomp_protect_syslog();
}
static int apply_protect_clock(const Unit *u, const ExecContext *c) {
static int apply_protect_clock(const Unit *u, const ExecContext *c) {
assert(u);
assert(c);
@ -1646,6 +1646,41 @@ static int apply_lock_personality(const Unit* u, const ExecContext *c) {
#endif
static int apply_protect_hostname(const Unit *u, const ExecContext *c, int *ret_exit_status) {
int r;
assert(u);
assert(c);
if (!c->protect_hostname)
return 0;
if (ns_type_supported(NAMESPACE_UTS)) {
if (unshare(CLONE_NEWUTS) < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno)) {
*ret_exit_status = EXIT_NAMESPACE;
return log_unit_error_errno(u, errno, "Failed to set up UTS namespacing: %m");
}
log_unit_warning(u, "ProtectHostname=yes is configured, but UTS namespace setup is prohibited (container manager?), ignoring namespace setup.");
}
} else
log_unit_warning(u, "ProtectHostname=yes is configured, but the kernel does not support UTS namespaces, ignoring namespace setup.");
#if HAVE_SECCOMP
if (skip_seccomp_unavailable(u, "ProtectHostname="))
return 0;
r = seccomp_protect_hostname();
if (r < 0) {
*ret_exit_status = EXIT_SECCOMP;
return log_unit_error_errno(u, r, "Failed to apply hostname restrictions: %m");
}
#endif
return 0;
}
static void do_idle_pipe_dance(int idle_pipe[static 4]) {
assert(idle_pipe);
@ -3588,25 +3623,10 @@ static int exec_child(
}
}
if (context->protect_hostname) {
if (ns_type_supported(NAMESPACE_UTS)) {
if (unshare(CLONE_NEWUTS) < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno)) {
*exit_status = EXIT_NAMESPACE;
return log_unit_error_errno(unit, errno, "Failed to set up UTS namespacing: %m");
}
log_unit_warning(unit, "ProtectHostname=yes is configured, but UTS namespace setup is prohibited (container manager?), ignoring namespace setup.");
}
} else
log_unit_warning(unit, "ProtectHostname=yes is configured, but the kernel does not support UTS namespaces, ignoring namespace setup.");
#if HAVE_SECCOMP
r = seccomp_protect_hostname();
if (r < 0) {
*exit_status = EXIT_SECCOMP;
return log_unit_error_errno(unit, r, "Failed to apply hostname restrictions: %m");
}
#endif
if (needs_sandboxing) {
r = apply_protect_hostname(unit, context, exit_status);
if (r < 0)
return r;
}
/* Drop groups as early as possible.

View File

@ -2068,7 +2068,7 @@ int config_parse_user_group_compat(
return -ENOEXEC;
}
if (!valid_user_group_name_or_id_compat(k)) {
if (!valid_user_group_name(k, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX|VALID_USER_WARN)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
return -ENOEXEC;
}
@ -2122,7 +2122,7 @@ int config_parse_user_group_strv_compat(
return -ENOEXEC;
}
if (!valid_user_group_name_or_id_compat(k)) {
if (!valid_user_group_name(k, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX|VALID_USER_WARN)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
return -ENOEXEC;
}

View File

@ -4278,7 +4278,7 @@ static int user_from_unit_name(Unit *u, char **ret) {
if (r < 0)
return r;
if (valid_user_group_name(n)) {
if (valid_user_group_name(n, 0)) {
*ret = TAKE_PTR(n);
return 0;
}

View File

@ -17,6 +17,7 @@
#include "def.h"
#include "fd-util.h"
#include "fs-util.h"
#include "glob-util.h"
#include "journal-internal.h"
#include "journal-util.h"
#include "log.h"
@ -44,6 +45,7 @@ static usec_t arg_since = USEC_INFINITY, arg_until = USEC_INFINITY;
static const char* arg_field = NULL;
static const char *arg_debugger = NULL;
static const char *arg_directory = NULL;
static char **arg_file = NULL;
static PagerFlags arg_pager_flags = 0;
static int arg_no_legend = false;
static int arg_one = false;
@ -51,6 +53,8 @@ static const char* arg_output = NULL;
static bool arg_reverse = false;
static bool arg_quiet = false;
STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
static int add_match(sd_journal *j, const char *match) {
_cleanup_free_ char *p = NULL;
const char* prefix, *pattern;
@ -111,6 +115,10 @@ static int acquire_journal(sd_journal **ret, char **matches) {
r = sd_journal_open_directory(&j, arg_directory, 0);
if (r < 0)
return log_error_errno(r, "Failed to open journals in directory: %s: %m", arg_directory);
} else if (arg_file) {
r = sd_journal_open_files(&j, (const char**)arg_file, 0);
if (r < 0)
return log_error_errno(r, "Failed to open journal files: %m");
} else {
r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
if (r < 0)
@ -164,6 +172,7 @@ static int help(void) {
" -r --reverse Show the newest entries first\n"
" -F --field=FIELD List all values a certain field takes\n"
" -o --output=FILE Write output to FILE\n"
" --file=PATH Use journal file\n"
" -D --directory=DIR Use journal files from directory\n\n"
" -q --quiet Do not show info messages and privilege warning\n"
"\nSee the %s for details.\n"
@ -182,6 +191,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_NO_PAGER,
ARG_NO_LEGEND,
ARG_DEBUGGER,
ARG_FILE,
};
int c, r;
@ -194,6 +204,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "debugger", required_argument, NULL, ARG_DEBUGGER },
{ "output", required_argument, NULL, 'o' },
{ "field", required_argument, NULL, 'F' },
{ "file", required_argument, NULL, ARG_FILE },
{ "directory", required_argument, NULL, 'D' },
{ "reverse", no_argument, NULL, 'r' },
{ "since", required_argument, NULL, 'S' },
@ -225,6 +236,12 @@ static int parse_argv(int argc, char *argv[]) {
arg_debugger = optarg;
break;
case ARG_FILE:
r = glob_extend(&arg_file, optarg);
if (r < 0)
return log_error_errno(r, "Failed to add paths: %m");
break;
case 'o':
if (arg_output)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@ -1096,6 +1113,7 @@ static int run(int argc, char *argv[]) {
ansi_highlight_red(),
units_active, units_active == 1 ? "unit is running" : "units are running",
ansi_normal());
return r;
}

View File

@ -17,7 +17,7 @@ bool suitable_user_name(const char *name) {
* restrictive, so that we can change the rules server-side without having to update things
* client-side too. */
if (!valid_user_group_name(name))
if (!valid_user_group_name(name, 0))
return false;
/* We generally rely on NSS to tell us which users not to care for, but let's filter out some

View File

@ -540,7 +540,7 @@ static int inspect_home(int argc, char *argv[], void *userdata) {
r = parse_uid(*i, &uid);
if (r < 0) {
if (!valid_user_group_name(*i)) {
if (!valid_user_group_name(*i, 0)) {
log_error("Invalid user name '%s'.", *i);
if (ret == 0)
ret = -EINVAL;
@ -1395,7 +1395,7 @@ static int create_home(int argc, char *argv[], void *userdata) {
if (argc >= 2) {
/* If a username was specified, use it */
if (valid_user_group_name(argv[1]))
if (valid_user_group_name(argv[1], 0))
r = json_variant_set_field_string(&arg_identity_extra, "userName", argv[1]);
else {
_cleanup_free_ char *un = NULL, *rr = NULL;
@ -3357,7 +3357,7 @@ static int parse_argv(int argc, char *argv[]) {
if (r == 0)
break;
if (!valid_user_group_name(word))
if (!valid_user_group_name(word, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid group name %s.", word);
mo = json_variant_ref(json_variant_by_key(arg_identity_extra, "memberOf"));

View File

@ -81,7 +81,7 @@ static int method_get_home_by_name(
r = sd_bus_message_read(message, "s", &user_name);
if (r < 0)
return r;
if (!valid_user_group_name(user_name))
if (!valid_user_group_name(user_name, 0))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
h = hashmap_get(m->homes_by_name, user_name);
@ -212,7 +212,7 @@ static int method_get_user_record_by_name(
r = sd_bus_message_read(message, "s", &user_name);
if (r < 0)
return r;
if (!valid_user_group_name(user_name))
if (!valid_user_group_name(user_name, 0))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
h = hashmap_get(m->homes_by_name, user_name);
@ -287,7 +287,7 @@ static int generic_home_method(
if (r < 0)
return r;
if (!valid_user_group_name(user_name))
if (!valid_user_group_name(user_name, 0))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
h = hashmap_get(m->homes_by_name, user_name);

View File

@ -45,9 +45,12 @@ static int parse_argv(
else if (please_suspend)
*please_suspend = k;
} else if (streq(argv[i], "debug")) {
if (debug)
*debug = true;
} else if ((v = startswith(argv[i], "debug="))) {
int k;
k = parse_boolean(v);
if (k < 0)
pam_syslog(handle, LOG_WARNING, "Failed to parse debug= argument, ignoring: %s", v);
@ -88,7 +91,7 @@ static int acquire_user_record(
/* Let's bypass all IPC complexity for the two user names we know for sure we don't manage, and for
* user names we don't consider valid. */
if (STR_IN_SET(username, "root", NOBODY_USER_NAME) || !valid_user_group_name(username))
if (STR_IN_SET(username, "root", NOBODY_USER_NAME) || !valid_user_group_name(username, 0))
return PAM_USER_UNKNOWN;
/* Let's check if a previous run determined that this user is not managed by homed. If so, let's exit early */

View File

@ -786,7 +786,7 @@ static int help(void) {
" --listen-http=ADDR Listen for HTTP connections at ADDR\n"
" --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
" -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
" --compress[=BOOL] XZ-compress the output journal (default: yes)\n"
" --compress[=BOOL] Use compression in the output journal (default: yes)\n"
" --seal[=BOOL] Use event sealing (default: no)\n"
" --key=FILENAME SSL key in PEM format (default:\n"
" \"" PRIV_KEY_FILE "\")\n"

View File

@ -488,7 +488,7 @@ static int import_file(struct trie *trie, const char *filename, uint16_t file_pr
size_t len;
char *pos;
r = read_line(f, LONG_LINE_MAX, &line);
r = read_line_full(f, LONG_LINE_MAX, READ_LINE_NOT_A_TTY, &line);
if (r < 0)
return r;
if (r == 0)

View File

@ -3170,6 +3170,12 @@ static int method_set_wall_message(
if (r < 0)
return r;
/* Short-circuit the operation if the desired state is already in place, to
* avoid an unnecessary polkit permission check. */
if (streq_ptr(m->wall_message, empty_to_null(wall_message)) &&
m->enable_wall_messages == enable_wall_messages)
goto done;
r = bus_verify_polkit_async(message,
CAP_SYS_ADMIN,
"org.freedesktop.login1.set-wall-message",
@ -3189,6 +3195,7 @@ static int method_set_wall_message(
m->enable_wall_messages = enable_wall_messages;
done:
return sd_bus_reply_method_return(message, NULL);
}

View File

@ -149,7 +149,10 @@ int config_parse_ip_protocol(
void *data,
void *userdata) {
uint8_t *protocol = data;
uint8_t *ret = data;
unsigned protocol;
/* linux/fou.h defines the netlink field as one byte, so we need to reject protocols numbers that
* don't fit in one byte. */
int r;
assert(filename);
@ -158,19 +161,26 @@ int config_parse_ip_protocol(
assert(rvalue);
assert(data);
assert_cc(IPPROTO_MAX-1 <= UINT8_MAX);
r = parse_ip_protocol(rvalue);
if (r < 0) {
r = safe_atou8(rvalue, protocol);
if (r >= 0)
protocol = r;
else {
r = safe_atou(rvalue, &protocol);
if (r < 0)
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse IP protocol '%s' for Foo over UDP tunnel, "
"Failed to parse IP protocol '%s' for FooOverUDP tunnel, "
"ignoring assignment: %m", rvalue);
return 0;
}
*protocol = r;
if (protocol > UINT8_MAX) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"IP protocol '%s' for FooOverUDP tunnel out of range, "
"ignoring assignment: %m", rvalue);
return 0;
}
*ret = protocol;
return 0;
}
@ -203,7 +213,7 @@ int config_parse_fou_tunnel_address(
r = in_addr_from_string_auto(rvalue, f, addr);
if (r < 0)
log_syntax(unit, LOG_ERR, filename, line, r,
"Foo over UDP tunnel '%s' address is invalid, ignoring assignment: %s",
"FooOverUDP tunnel '%s' address is invalid, ignoring assignment: %s",
lvalue, rvalue);
return 0;

View File

@ -95,7 +95,7 @@ enum nss_status _nss_systemd_getpwnam_r(
/* If the username is not valid, then we don't know it. Ideally libc would filter these for us
* anyway. We don't generate EINVAL here, because it isn't really out business to complain about
* invalid user names. */
if (!valid_user_group_name(name))
if (!valid_user_group_name(name, VALID_USER_RELAX))
return NSS_STATUS_NOTFOUND;
/* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
@ -192,7 +192,7 @@ enum nss_status _nss_systemd_getgrnam_r(
assert(gr);
assert(errnop);
if (!valid_user_group_name(name))
if (!valid_user_group_name(name, VALID_USER_RELAX))
return NSS_STATUS_NOTFOUND;
/* Synthesize records for root and nobody, in case they are missing from /etc/group */
@ -559,7 +559,7 @@ enum nss_status _nss_systemd_initgroups_dyn(
assert(groupsp);
assert(errnop);
if (!valid_user_group_name(user_name))
if (!valid_user_group_name(user_name, VALID_USER_RELAX))
return NSS_STATUS_NOTFOUND;
/* Don't allow extending these two special users, the same as we won't resolve them via getpwnam() */

View File

@ -37,6 +37,8 @@ void etc_hosts_free(EtcHosts *hosts) {
void manager_etc_hosts_flush(Manager *m) {
etc_hosts_free(&m->etc_hosts);
m->etc_hosts_mtime = USEC_INFINITY;
m->etc_hosts_ino = 0;
m->etc_hosts_dev = 0;
}
static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) {
@ -224,8 +226,9 @@ static int manager_etc_hosts_read(Manager *m) {
return 0;
}
/* Did the mtime change? If not, there's no point in re-reading the file. */
if (timespec_load(&st.st_mtim) == m->etc_hosts_mtime)
/* Did the mtime or ino/dev change? If not, there's no point in re-reading the file. */
if (timespec_load(&st.st_mtim) == m->etc_hosts_mtime &&
st.st_ino == m->etc_hosts_ino && st.st_dev == m->etc_hosts_dev)
return 0;
}
@ -249,6 +252,8 @@ static int manager_etc_hosts_read(Manager *m) {
return r;
m->etc_hosts_mtime = timespec_load(&st.st_mtim);
m->etc_hosts_ino = st.st_ino;
m->etc_hosts_dev = st.st_dev;
m->etc_hosts_last = ts;
return 1;

View File

@ -591,6 +591,8 @@ int manager_new(Manager **ret) {
.need_builtin_fallbacks = true,
.etc_hosts_last = USEC_INFINITY,
.etc_hosts_mtime = USEC_INFINITY,
.etc_hosts_ino = 0,
.etc_hosts_dev = 0,
.read_etc_hosts = true,
};

View File

@ -127,6 +127,8 @@ struct Manager {
/* Data from /etc/hosts */
EtcHosts etc_hosts;
usec_t etc_hosts_last, etc_hosts_mtime;
ino_t etc_hosts_ino;
dev_t etc_hosts_dev;
bool read_etc_hosts;
/* Local DNS stub on 127.0.0.53:53 */

View File

@ -283,9 +283,9 @@ static int write_uplink_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSe
"# This is a dynamic resolv.conf file for connecting local clients directly to\n"
"# all known uplink DNS servers. This file lists all configured search domains.\n"
"#\n"
"# Third party programs must not access this file directly, but only through the\n"
"# symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a different way,\n"
"# replace this symlink by a static file or a different symlink.\n"
"# Third party programs should typically not access this file directly, but only\n"
"# through the symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a\n"
"# different way, replace this symlink by a static file or a different symlink.\n"
"#\n"
"# See man:systemd-resolved.service(8) for details about the supported modes of\n"
"# operation for /etc/resolv.conf.\n"
@ -308,24 +308,24 @@ static int write_uplink_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSe
}
static int write_stub_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) {
fputs_unlocked("# This file is managed by man:systemd-resolved(8). Do not edit.\n"
"#\n"
"# This is a dynamic resolv.conf file for connecting local clients to the\n"
"# internal DNS stub resolver of systemd-resolved. This file lists all\n"
"# configured search domains.\n"
"#\n"
"# Run \"resolvectl status\" to see details about the uplink DNS servers\n"
"# currently in use.\n"
"#\n"
"# Third party programs must not access this file directly, but only through the\n"
"# symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a different way,\n"
"# replace this symlink by a static file or a different symlink.\n"
"#\n"
"# See man:systemd-resolved.service(8) for details about the supported modes of\n"
"# operation for /etc/resolv.conf.\n"
"\n"
"nameserver 127.0.0.53\n"
"options edns0\n", f);
fputs("# This file is managed by man:systemd-resolved(8). Do not edit.\n"
"#\n"
"# This is a dynamic resolv.conf file for connecting local clients to the\n"
"# internal DNS stub resolver of systemd-resolved. This file lists all\n"
"# configured search domains.\n"
"#\n"
"# Run \"resolvectl status\" to see details about the uplink DNS servers\n"
"# currently in use.\n"
"#\n"
"# Third party programs should typically not access this file directly, but only\n"
"# through the symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a\n"
"# different way, replace this symlink by a static file or a different symlink.\n"
"#\n"
"# See man:systemd-resolved.service(8) for details about the supported modes of\n"
"# operation for /etc/resolv.conf.\n"
"\n"
"nameserver 127.0.0.53\n"
"options edns0\n", f);
if (!ordered_set_isempty(domains))
write_resolv_conf_search(domains, f);

View File

@ -294,7 +294,7 @@ int config_parse(const char *unit,
_cleanup_fclose_ FILE *ours = NULL;
unsigned line = 0, section_line = 0;
bool section_ignored = false, bom_seen = false;
int r;
int r, fd;
assert(filename);
assert(lookup);
@ -311,7 +311,9 @@ int config_parse(const char *unit,
}
}
fd_warn_permissions(filename, fileno(f));
fd = fileno(f);
if (fd >= 0) /* stream might not have an fd, let's be careful hence */
fd_warn_permissions(filename, fd);
for (;;) {
_cleanup_free_ char *buf = NULL;

View File

@ -1188,6 +1188,8 @@ static int decrypt_partition(
if (r < 0)
return log_debug_errno(r, "Failed to initialize dm-crypt: %m");
crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
r = crypt_load(cd, CRYPT_LUKS, NULL);
if (r < 0)
return log_debug_errno(r, "Failed to load LUKS metadata: %m");
@ -1246,6 +1248,8 @@ static int verity_partition(
if (r < 0)
return r;
crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
r = crypt_load(cd, CRYPT_VERITY, NULL);
if (r < 0)
return r;

View File

@ -86,8 +86,8 @@ static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDisp
{ "matchMachineId", _JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
{ "matchHostname", _JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
{ "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(GroupRecord, gid), 0 },
{ "members", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, members), 0 },
{ "administrators", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, administrators), 0 },
{ "members", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, members), JSON_RELAX},
{ "administrators", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, administrators), JSON_RELAX},
{},
};
@ -190,14 +190,14 @@ int group_record_load(
UserRecordLoadFlags load_flags) {
static const JsonDispatch group_dispatch_table[] = {
{ "groupName", JSON_VARIANT_STRING, json_dispatch_user_group_name, offsetof(GroupRecord, group_name), 0 },
{ "groupName", JSON_VARIANT_STRING, json_dispatch_user_group_name, offsetof(GroupRecord, group_name), JSON_RELAX},
{ "realm", JSON_VARIANT_STRING, json_dispatch_realm, offsetof(GroupRecord, realm), 0 },
{ "disposition", JSON_VARIANT_STRING, json_dispatch_user_disposition, offsetof(GroupRecord, disposition), 0 },
{ "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(GroupRecord, service), JSON_SAFE },
{ "lastChangeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(GroupRecord, last_change_usec), 0 },
{ "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(GroupRecord, gid), 0 },
{ "members", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, members), 0 },
{ "administrators", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, administrators), 0 },
{ "members", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, members), JSON_RELAX},
{ "administrators", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, administrators), JSON_RELAX},
{ "privileged", JSON_VARIANT_OBJECT, dispatch_privileged, 0, 0 },

View File

@ -4107,7 +4107,7 @@ int json_dispatch_user_group_name(const char *name, JsonVariant *variant, JsonDi
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
n = json_variant_string(variant);
if (!valid_user_group_name_compat(n))
if (!valid_user_group_name(n, FLAGS_SET(flags, JSON_RELAX) ? VALID_USER_RELAX : 0))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid user/group name.", strna(name));
r = free_and_strdup(s, n);

View File

@ -255,6 +255,7 @@ typedef enum JsonDispatchFlags {
JSON_MANDATORY = 1 << 1, /* Should existence of this property be mandatory? */
JSON_LOG = 1 << 2, /* Should the parser log about errors? */
JSON_SAFE = 1 << 3, /* Don't accept "unsafe" strings in json_dispatch_string() + json_dispatch_string() */
JSON_RELAX = 1 << 4, /* Use relaxed user name checking in json_dispatch_user_group_name */
/* The following two may be passed into log_json() in addition to the three above */
JSON_DEBUG = 1 << 4, /* Indicates that this log message is a debug message */

View File

@ -223,7 +223,7 @@ static int calculate_swap_file_offset(const SwapEntry *swap, uint64_t *ret_offse
fd = open(swap->device, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return log_error_errno(errno, "Failed to open %s: %m", swap->device);
return log_error_errno(errno, "Failed to open swap file %s to determine on-disk offset: %m", swap->device);
if (fstat(fd, &sb) < 0)
return log_error_errno(errno, "Failed to stat %s: %m", swap->device);

View File

@ -600,7 +600,7 @@ int json_dispatch_user_group_list(const char *name, JsonVariant *variant, JsonDi
if (!json_variant_is_string(e))
return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a string.");
if (!valid_user_group_name_compat(json_variant_string(e)))
if (!valid_user_group_name(json_variant_string(e), FLAGS_SET(flags, JSON_RELAX) ? VALID_USER_RELAX : 0))
return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a valid user/group name: %s", json_variant_string(e));
r = strv_extend(&l, json_variant_string(e));
@ -938,7 +938,7 @@ static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDisp
{ "imagePath", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, image_path), 0 },
{ "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, uid), 0 },
{ "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, gid), 0 },
{ "memberOf", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(UserRecord, member_of), 0 },
{ "memberOf", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(UserRecord, member_of), JSON_RELAX},
{ "fileSystemType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, file_system_type), JSON_SAFE },
{ "partitionUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, partition_uuid), 0 },
{ "luksUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, luks_uuid), 0 },
@ -1231,7 +1231,7 @@ int user_group_record_mangle(
int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags load_flags) {
static const JsonDispatch user_dispatch_table[] = {
{ "userName", JSON_VARIANT_STRING, json_dispatch_user_group_name, offsetof(UserRecord, user_name), 0 },
{ "userName", JSON_VARIANT_STRING, json_dispatch_user_group_name, offsetof(UserRecord, user_name), JSON_RELAX},
{ "realm", JSON_VARIANT_STRING, json_dispatch_realm, offsetof(UserRecord, realm), 0 },
{ "realName", JSON_VARIANT_STRING, json_dispatch_gecos, offsetof(UserRecord, real_name), 0 },
{ "emailAddress", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, email_address), JSON_SAFE },
@ -1270,7 +1270,7 @@ int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags load_fla
{ "homeDirectory", JSON_VARIANT_STRING, json_dispatch_home_directory, offsetof(UserRecord, home_directory), 0 },
{ "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, uid), 0 },
{ "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, gid), 0 },
{ "memberOf", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(UserRecord, member_of), 0 },
{ "memberOf", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(UserRecord, member_of), JSON_RELAX},
{ "fileSystemType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, file_system_type), JSON_SAFE },
{ "partitionUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, partition_uuid), 0 },
{ "luksUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, luks_uuid), 0 },

View File

@ -587,7 +587,7 @@ int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret) {
_cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
int r;
if (!valid_user_group_name_compat(name))
if (!valid_user_group_name(name, VALID_USER_RELAX))
return -EINVAL;
r = json_build(&query, JSON_BUILD_OBJECT(
@ -795,7 +795,7 @@ int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret) {
_cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
int r;
if (!valid_user_group_name_compat(name))
if (!valid_user_group_name(name, VALID_USER_RELAX))
return -EINVAL;
r = json_build(&query, JSON_BUILD_OBJECT(
@ -982,7 +982,7 @@ int membershipdb_by_user(const char *name, UserDBFlags flags, UserDBIterator **r
assert(ret);
if (!valid_user_group_name_compat(name))
if (!valid_user_group_name(name, VALID_USER_RELAX))
return -EINVAL;
r = json_build(&query, JSON_BUILD_OBJECT(
@ -1025,7 +1025,7 @@ int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **
assert(ret);
if (!valid_user_group_name_compat(name))
if (!valid_user_group_name(name, VALID_USER_RELAX))
return -EINVAL;
r = json_build(&query, JSON_BUILD_OBJECT(

View File

@ -158,6 +158,9 @@ _SD_BEGIN_DECLARATIONS;
#define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57)
#define SD_MESSAGE_DNSSEC_DOWNGRADE_STR SD_ID128_MAKE_STR(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57)
#define SD_MESSAGE_UNSAFE_USER_NAME SD_ID128_MAKE(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f)
#define SD_MESSAGE_UNSAFE_USER_NAME_STR SD_ID128_MAKE_STR(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f)
_SD_END_DECLARATIONS;
#endif

View File

@ -1445,7 +1445,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
if (r < 0)
log_error_errno(r, "[%s:%u] Failed to replace specifiers: %s", fname, line, name);
if (!valid_user_group_name(resolved_name))
if (!valid_user_group_name(resolved_name, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"[%s:%u] '%s' is not a valid user or group name.",
fname, line, resolved_name);
@ -1548,7 +1548,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
"[%s:%u] Lines of type 'm' require a group name in the third field.",
fname, line);
if (!valid_user_group_name(resolved_id))
if (!valid_user_group_name(resolved_id, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"[%s:%u] '%s' is not a valid user or group name.",
fname, line, resolved_id);
@ -1589,7 +1589,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
if (split_pair(resolved_id, ":", &uid, &gid) == 0) {
r = parse_gid(gid, &i->gid);
if (r < 0) {
if (valid_user_group_name(gid))
if (valid_user_group_name(gid, 0))
i->group_name = TAKE_PTR(gid);
else
return log_error_errno(r, "Failed to parse GID: '%s': %m", id);

View File

@ -1123,6 +1123,10 @@ tests += [
[],
[]],
[['src/analyze/test-verify.c', 'src/analyze/analyze-verify.c', 'src/analyze/analyze-verify.h'],
[libcore, libshared],
[]],
[['src/login/test-inhibit.c'],
[],
[],

View File

@ -307,6 +307,12 @@ static void test_strv_split(void) {
l = strv_split_full(" 'one' \" two\t three \"' four five", NULL, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(l);
assert_se(strv_equal(l, (char**) input_table_quoted));
strv_free_erase(l);
l = strv_split_full("\\", NULL, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(l);
assert_se(strv_equal(l, STRV_MAKE("\\")));
}
static void test_strv_split_empty(void) {

View File

@ -63,144 +63,163 @@ static void test_uid_ptr(void) {
assert_se(PTR_TO_UID(UID_TO_PTR(1000)) == 1000);
}
static void test_valid_user_group_name_compat(void) {
static void test_valid_user_group_name_relaxed(void) {
log_info("/* %s */", __func__);
assert_se(!valid_user_group_name_compat(NULL));
assert_se(!valid_user_group_name_compat(""));
assert_se(!valid_user_group_name_compat("1"));
assert_se(!valid_user_group_name_compat("65535"));
assert_se(!valid_user_group_name_compat("-1"));
assert_se(!valid_user_group_name_compat("-kkk"));
assert_se(!valid_user_group_name_compat("rööt"));
assert_se(!valid_user_group_name_compat("."));
assert_se(!valid_user_group_name_compat(".eff"));
assert_se(!valid_user_group_name_compat("foo\nbar"));
assert_se(!valid_user_group_name_compat("0123456789012345678901234567890123456789"));
assert_se(!valid_user_group_name_or_id_compat("aaa:bbb"));
assert_se(!valid_user_group_name_compat("."));
assert_se(!valid_user_group_name_compat(".1"));
assert_se(!valid_user_group_name_compat(".65535"));
assert_se(!valid_user_group_name_compat(".-1"));
assert_se(!valid_user_group_name_compat(".-kkk"));
assert_se(!valid_user_group_name_compat(".rööt"));
assert_se(!valid_user_group_name_or_id_compat(".aaa:bbb"));
assert_se(!valid_user_group_name(NULL, VALID_USER_RELAX));
assert_se(!valid_user_group_name("", VALID_USER_RELAX));
assert_se(!valid_user_group_name("1", VALID_USER_RELAX));
assert_se(!valid_user_group_name("65535", VALID_USER_RELAX));
assert_se(!valid_user_group_name("-1", VALID_USER_RELAX));
assert_se(!valid_user_group_name("foo\nbar", VALID_USER_RELAX));
assert_se(!valid_user_group_name("0123456789012345678901234567890123456789", VALID_USER_RELAX));
assert_se(!valid_user_group_name("aaa:bbb", VALID_USER_RELAX|VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name(".aaa:bbb", VALID_USER_RELAX|VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name(".", VALID_USER_RELAX));
assert_se(!valid_user_group_name("..", VALID_USER_RELAX));
assert_se(valid_user_group_name_compat("root"));
assert_se(valid_user_group_name_compat("lennart"));
assert_se(valid_user_group_name_compat("LENNART"));
assert_se(valid_user_group_name_compat("_kkk"));
assert_se(valid_user_group_name_compat("kkk-"));
assert_se(valid_user_group_name_compat("kk-k"));
assert_se(valid_user_group_name_compat("eff.eff"));
assert_se(valid_user_group_name_compat("eff."));
assert_se(valid_user_group_name("root", VALID_USER_RELAX));
assert_se(valid_user_group_name("lennart", VALID_USER_RELAX));
assert_se(valid_user_group_name("LENNART", VALID_USER_RELAX));
assert_se(valid_user_group_name("_kkk", VALID_USER_RELAX));
assert_se(valid_user_group_name("kkk-", VALID_USER_RELAX));
assert_se(valid_user_group_name("kk-k", VALID_USER_RELAX));
assert_se(valid_user_group_name("eff.eff", VALID_USER_RELAX));
assert_se(valid_user_group_name("eff.", VALID_USER_RELAX));
assert_se(valid_user_group_name("-kkk", VALID_USER_RELAX));
assert_se(valid_user_group_name("rööt", VALID_USER_RELAX));
assert_se(valid_user_group_name(".eff", VALID_USER_RELAX));
assert_se(valid_user_group_name(".1", VALID_USER_RELAX));
assert_se(valid_user_group_name(".65535", VALID_USER_RELAX));
assert_se(valid_user_group_name(".-1", VALID_USER_RELAX));
assert_se(valid_user_group_name(".-kkk", VALID_USER_RELAX));
assert_se(valid_user_group_name(".rööt", VALID_USER_RELAX));
assert_se(valid_user_group_name("...", VALID_USER_RELAX));
assert_se(valid_user_group_name_compat("some5"));
assert_se(valid_user_group_name_compat("5some"));
assert_se(valid_user_group_name_compat("INNER5NUMBER"));
assert_se(valid_user_group_name("some5", VALID_USER_RELAX));
assert_se(valid_user_group_name("5some", VALID_USER_RELAX));
assert_se(valid_user_group_name("INNER5NUMBER", VALID_USER_RELAX));
assert_se(valid_user_group_name("piff.paff@ad.domain.example", VALID_USER_RELAX));
assert_se(valid_user_group_name("Dāvis", VALID_USER_RELAX));
}
static void test_valid_user_group_name(void) {
log_info("/* %s */", __func__);
assert_se(!valid_user_group_name(NULL));
assert_se(!valid_user_group_name(""));
assert_se(!valid_user_group_name("1"));
assert_se(!valid_user_group_name("65535"));
assert_se(!valid_user_group_name("-1"));
assert_se(!valid_user_group_name("-kkk"));
assert_se(!valid_user_group_name("rööt"));
assert_se(!valid_user_group_name("."));
assert_se(!valid_user_group_name(".eff"));
assert_se(!valid_user_group_name("foo\nbar"));
assert_se(!valid_user_group_name("0123456789012345678901234567890123456789"));
assert_se(!valid_user_group_name_or_id("aaa:bbb"));
assert_se(!valid_user_group_name("."));
assert_se(!valid_user_group_name(".1"));
assert_se(!valid_user_group_name(".65535"));
assert_se(!valid_user_group_name(".-1"));
assert_se(!valid_user_group_name(".-kkk"));
assert_se(!valid_user_group_name(".rööt"));
assert_se(!valid_user_group_name_or_id(".aaa:bbb"));
assert_se(!valid_user_group_name(NULL, 0));
assert_se(!valid_user_group_name("", 0));
assert_se(!valid_user_group_name("1", 0));
assert_se(!valid_user_group_name("65535", 0));
assert_se(!valid_user_group_name("-1", 0));
assert_se(!valid_user_group_name("-kkk", 0));
assert_se(!valid_user_group_name("rööt", 0));
assert_se(!valid_user_group_name(".", 0));
assert_se(!valid_user_group_name(".eff", 0));
assert_se(!valid_user_group_name("foo\nbar", 0));
assert_se(!valid_user_group_name("0123456789012345678901234567890123456789", 0));
assert_se(!valid_user_group_name("aaa:bbb", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name(".", 0));
assert_se(!valid_user_group_name("..", 0));
assert_se(!valid_user_group_name("...", 0));
assert_se(!valid_user_group_name(".1", 0));
assert_se(!valid_user_group_name(".65535", 0));
assert_se(!valid_user_group_name(".-1", 0));
assert_se(!valid_user_group_name(".-kkk", 0));
assert_se(!valid_user_group_name(".rööt", 0));
assert_se(!valid_user_group_name(".aaa:bbb", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("root"));
assert_se(valid_user_group_name("lennart"));
assert_se(valid_user_group_name("LENNART"));
assert_se(valid_user_group_name("_kkk"));
assert_se(valid_user_group_name("kkk-"));
assert_se(valid_user_group_name("kk-k"));
assert_se(!valid_user_group_name("eff.eff"));
assert_se(!valid_user_group_name("eff."));
assert_se(valid_user_group_name("root", 0));
assert_se(valid_user_group_name("lennart", 0));
assert_se(valid_user_group_name("LENNART", 0));
assert_se(valid_user_group_name("_kkk", 0));
assert_se(valid_user_group_name("kkk-", 0));
assert_se(valid_user_group_name("kk-k", 0));
assert_se(!valid_user_group_name("eff.eff", 0));
assert_se(!valid_user_group_name("eff.", 0));
assert_se(valid_user_group_name("some5"));
assert_se(!valid_user_group_name("5some"));
assert_se(valid_user_group_name("INNER5NUMBER"));
assert_se(valid_user_group_name("some5", 0));
assert_se(!valid_user_group_name("5some", 0));
assert_se(valid_user_group_name("INNER5NUMBER", 0));
assert_se(!valid_user_group_name("piff.paff@ad.domain.example", 0));
assert_se(!valid_user_group_name("Dāvis", 0));
}
static void test_valid_user_group_name_or_id_compat(void) {
static void test_valid_user_group_name_or_numeric_relaxed(void) {
log_info("/* %s */", __func__);
assert_se(!valid_user_group_name_or_id_compat(NULL));
assert_se(!valid_user_group_name_or_id_compat(""));
assert_se(valid_user_group_name_or_id_compat("0"));
assert_se(valid_user_group_name_or_id_compat("1"));
assert_se(valid_user_group_name_or_id_compat("65534"));
assert_se(!valid_user_group_name_or_id_compat("65535"));
assert_se(valid_user_group_name_or_id_compat("65536"));
assert_se(!valid_user_group_name_or_id_compat("-1"));
assert_se(!valid_user_group_name_or_id_compat("-kkk"));
assert_se(!valid_user_group_name_or_id_compat("rööt"));
assert_se(!valid_user_group_name_or_id_compat("."));
assert_se(!valid_user_group_name_or_id_compat(".eff"));
assert_se(valid_user_group_name_or_id_compat("eff.eff"));
assert_se(valid_user_group_name_or_id_compat("eff."));
assert_se(!valid_user_group_name_or_id_compat("foo\nbar"));
assert_se(!valid_user_group_name_or_id_compat("0123456789012345678901234567890123456789"));
assert_se(!valid_user_group_name_or_id_compat("aaa:bbb"));
assert_se(!valid_user_group_name(NULL, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(!valid_user_group_name("", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("0", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("1", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("65534", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(!valid_user_group_name("65535", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("65536", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(!valid_user_group_name("-1", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(!valid_user_group_name("foo\nbar", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(!valid_user_group_name("0123456789012345678901234567890123456789", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(!valid_user_group_name("aaa:bbb", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(!valid_user_group_name(".", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(!valid_user_group_name("..", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name_or_id_compat("root"));
assert_se(valid_user_group_name_or_id_compat("lennart"));
assert_se(valid_user_group_name_or_id_compat("LENNART"));
assert_se(valid_user_group_name_or_id_compat("_kkk"));
assert_se(valid_user_group_name_or_id_compat("kkk-"));
assert_se(valid_user_group_name_or_id_compat("kk-k"));
assert_se(valid_user_group_name("root", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("lennart", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("LENNART", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("_kkk", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("kkk-", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("kk-k", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("-kkk", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("rööt", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name(".eff", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("eff.eff", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("eff.", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("...", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name_or_id_compat("some5"));
assert_se(valid_user_group_name_or_id_compat("5some"));
assert_se(valid_user_group_name_or_id_compat("INNER5NUMBER"));
assert_se(valid_user_group_name("some5", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("5some", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("INNER5NUMBER", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("piff.paff@ad.domain.example", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
assert_se(valid_user_group_name("Dāvis", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX));
}
static void test_valid_user_group_name_or_id(void) {
static void test_valid_user_group_name_or_numeric(void) {
log_info("/* %s */", __func__);
assert_se(!valid_user_group_name_or_id(NULL));
assert_se(!valid_user_group_name_or_id(""));
assert_se(valid_user_group_name_or_id("0"));
assert_se(valid_user_group_name_or_id("1"));
assert_se(valid_user_group_name_or_id("65534"));
assert_se(!valid_user_group_name_or_id("65535"));
assert_se(valid_user_group_name_or_id("65536"));
assert_se(!valid_user_group_name_or_id("-1"));
assert_se(!valid_user_group_name_or_id("-kkk"));
assert_se(!valid_user_group_name_or_id("rööt"));
assert_se(!valid_user_group_name_or_id("."));
assert_se(!valid_user_group_name_or_id(".eff"));
assert_se(!valid_user_group_name_or_id("eff.eff"));
assert_se(!valid_user_group_name_or_id("eff."));
assert_se(!valid_user_group_name_or_id("foo\nbar"));
assert_se(!valid_user_group_name_or_id("0123456789012345678901234567890123456789"));
assert_se(!valid_user_group_name_or_id("aaa:bbb"));
assert_se(!valid_user_group_name(NULL, VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("0", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("1", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("65534", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("65535", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("65536", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("-1", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("-kkk", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("rööt", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name(".", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("..", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("...", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name(".eff", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("eff.eff", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("eff.", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("foo\nbar", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("0123456789012345678901234567890123456789", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("aaa:bbb", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name_or_id("root"));
assert_se(valid_user_group_name_or_id("lennart"));
assert_se(valid_user_group_name_or_id("LENNART"));
assert_se(valid_user_group_name_or_id("_kkk"));
assert_se(valid_user_group_name_or_id("kkk-"));
assert_se(valid_user_group_name_or_id("kk-k"));
assert_se(valid_user_group_name("root", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("lennart", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("LENNART", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("_kkk", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("kkk-", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("kk-k", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name_or_id("some5"));
assert_se(!valid_user_group_name_or_id("5some"));
assert_se(valid_user_group_name_or_id("INNER5NUMBER"));
assert_se(valid_user_group_name("some5", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("5some", VALID_USER_ALLOW_NUMERIC));
assert_se(valid_user_group_name("INNER5NUMBER", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("piff.paff@ad.domain.example", VALID_USER_ALLOW_NUMERIC));
assert_se(!valid_user_group_name("Dāvis", VALID_USER_ALLOW_NUMERIC));
}
static void test_valid_gecos(void) {
@ -355,10 +374,10 @@ int main(int argc, char *argv[]) {
test_parse_uid();
test_uid_ptr();
test_valid_user_group_name_compat();
test_valid_user_group_name_relaxed();
test_valid_user_group_name();
test_valid_user_group_name_or_id_compat();
test_valid_user_group_name_or_id();
test_valid_user_group_name_or_numeric_relaxed();
test_valid_user_group_name_or_numeric();
test_valid_gecos();
test_valid_home();

View File

@ -490,7 +490,7 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
if (config->alternative_names_policy)
for (NamePolicy *p = config->alternative_names_policy; *p != _NAMEPOLICY_INVALID; p++) {
const char *n;
const char *n = NULL;
switch (*p) {
case NAMEPOLICY_DATABASE:

View File

@ -541,16 +541,15 @@ static int ssh_authorized_keys(int argc, char *argv[], void *userdata) {
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
int r;
if (!valid_user_group_name(argv[1]))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid user name '%s'.", argv[1]);
r = userdb_by_name(argv[1], arg_userdb_flags, &ur);
if (r == -ESRCH)
log_error_errno(r, "User %s does not exist.", argv[1]);
return log_error_errno(r, "User %s does not exist.", argv[1]);
else if (r == -EHOSTDOWN)
log_error_errno(r, "Selected user database service is not available for this request.");
return log_error_errno(r, "Selected user database service is not available for this request.");
else if (r == -EINVAL)
return log_error_errno(r, "Failed to find user %s: %m (Invalid user name?)", argv[1]);
else if (r < 0)
log_error_errno(r, "Failed to find user %s: %m", argv[1]);
return log_error_errno(r, "Failed to find user %s: %m", argv[1]);
if (strv_isempty(ur->ssh_authorized_keys))
log_debug("User record for %s has no public SSH keys.", argv[1]);

View File

@ -12,6 +12,7 @@ systemctl status issue_14566_test
leaked_pid=$(cat /leakedtestpid)
systemctl stop issue_14566_test
sleep 1
# Leaked PID will still be around if we're buggy.
# I personally prefer to see 42.