mirror of
https://github.com/systemd/systemd.git
synced 2025-01-24 06:04:05 +03:00
Merge pull request #24370 from keszybz/sysusers-equivs
Use /bin/bash for root shell and suppress some warnings from sysusers
This commit is contained in:
commit
298b3de6d4
@ -35,23 +35,22 @@
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><command>systemd-sysusers</command> creates system users and
|
||||
groups, based on the file format and location specified in
|
||||
<para><command>systemd-sysusers</command> creates system users and groups, based on files in the format
|
||||
described in
|
||||
<citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
||||
</para>
|
||||
|
||||
<para>If invoked with no arguments, it applies all directives from all files
|
||||
found in the directories specified by
|
||||
<citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
||||
When invoked with positional arguments, if option
|
||||
<option>--replace=<replaceable>PATH</replaceable></option> is specified, arguments
|
||||
specified on the command line are used instead of the configuration file
|
||||
<replaceable>PATH</replaceable>. Otherwise, just the configuration specified by
|
||||
the command line arguments is executed. The string <literal>-</literal> may be
|
||||
specified instead of a filename to instruct <command>systemd-sysusers</command>
|
||||
to read the configuration from standard input. If only the basename of a file is
|
||||
specified, all configuration directories are searched for a matching file and
|
||||
the file found that has the highest priority is executed.</para>
|
||||
<para>If invoked with no arguments, it applies all directives from all files found in the directories
|
||||
specified by
|
||||
<citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>. When
|
||||
invoked with positional arguments, if option <option>--replace=<replaceable>PATH</replaceable></option>
|
||||
is specified, arguments specified on the command line are used instead of the configuration file
|
||||
<replaceable>PATH</replaceable>. Otherwise, just the configuration specified by the command line
|
||||
arguments is executed. The string <literal>-</literal> may be specified instead of a filename to instruct
|
||||
<command>systemd-sysusers</command> to read the configuration from standard input. If the argument is a
|
||||
relative path, all configuration directories are searched for a matching file and the file found that has
|
||||
the highest priority is executed. If the argument is an absolute path, that file is used directly without
|
||||
searching of the configuration directories.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@ -2108,7 +2108,7 @@ Note that this setting is <emphasis>not</emphasis> influenced by the <varname>Us
|
||||
<row>
|
||||
<entry><literal>%s</literal></entry>
|
||||
<entry>User shell</entry>
|
||||
<entry>This is the shell of the user running the service manager instance. In case of the system manager this resolves to <literal>/bin/sh</literal>.</entry>
|
||||
<entry>This is the shell of the user running the service manager instance.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>%S</literal></entry>
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "sd-messages.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "chase-symlinks.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
@ -136,7 +137,6 @@ char *getusername_malloc(void) {
|
||||
}
|
||||
|
||||
bool is_nologin_shell(const char *shell) {
|
||||
|
||||
return PATH_IN_SET(shell,
|
||||
/* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice
|
||||
* message and exits. Different distributions place the binary at different places though,
|
||||
@ -154,6 +154,21 @@ bool is_nologin_shell(const char *shell) {
|
||||
"/usr/bin/true");
|
||||
}
|
||||
|
||||
const char* default_root_shell(const char *root) {
|
||||
/* We want to use the preferred shell, i.e. DEFAULT_USER_SHELL, which usually
|
||||
* will be /bin/bash. Fall back to /bin/sh if DEFAULT_USER_SHELL is not found,
|
||||
* or any access errors. */
|
||||
|
||||
int r = chase_symlinks(DEFAULT_USER_SHELL, root, CHASE_PREFIX_ROOT, NULL, NULL);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_debug_errno(r, "Failed to look up shell '%s%s%s': %m",
|
||||
strempty(root), root ? "/" : "", DEFAULT_USER_SHELL);
|
||||
if (r > 0)
|
||||
return DEFAULT_USER_SHELL;
|
||||
|
||||
return "/bin/sh";
|
||||
}
|
||||
|
||||
static int synthesize_user_creds(
|
||||
const char **username,
|
||||
uid_t *uid, gid_t *gid,
|
||||
@ -176,13 +191,13 @@ static int synthesize_user_creds(
|
||||
*home = "/root";
|
||||
|
||||
if (shell)
|
||||
*shell = "/bin/sh";
|
||||
*shell = default_root_shell(NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (synthesize_nobody() &&
|
||||
STR_IN_SET(*username, NOBODY_USER_NAME, "65534")) {
|
||||
if (STR_IN_SET(*username, NOBODY_USER_NAME, "65534") &&
|
||||
synthesize_nobody()) {
|
||||
*username = NOBODY_USER_NAME;
|
||||
|
||||
if (uid)
|
||||
@ -326,8 +341,8 @@ int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (synthesize_nobody() &&
|
||||
STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534")) {
|
||||
if (STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534") &&
|
||||
synthesize_nobody()) {
|
||||
*groupname = NOBODY_GROUP_NAME;
|
||||
|
||||
if (gid)
|
||||
@ -373,8 +388,7 @@ char* uid_to_name(uid_t uid) {
|
||||
/* Shortcut things to avoid NSS lookups */
|
||||
if (uid == 0)
|
||||
return strdup("root");
|
||||
if (synthesize_nobody() &&
|
||||
uid == UID_NOBODY)
|
||||
if (uid == UID_NOBODY && synthesize_nobody())
|
||||
return strdup(NOBODY_USER_NAME);
|
||||
|
||||
if (uid_is_valid(uid)) {
|
||||
@ -417,8 +431,7 @@ char* gid_to_name(gid_t gid) {
|
||||
|
||||
if (gid == 0)
|
||||
return strdup("root");
|
||||
if (synthesize_nobody() &&
|
||||
gid == GID_NOBODY)
|
||||
if (gid == GID_NOBODY && synthesize_nobody())
|
||||
return strdup(NOBODY_GROUP_NAME);
|
||||
|
||||
if (gid_is_valid(gid)) {
|
||||
@ -556,43 +569,29 @@ int getgroups_alloc(gid_t** gids) {
|
||||
return ngroups;
|
||||
}
|
||||
|
||||
int get_home_dir(char **_h) {
|
||||
int get_home_dir(char **ret) {
|
||||
struct passwd *p;
|
||||
const char *e;
|
||||
char *h;
|
||||
uid_t u;
|
||||
|
||||
assert(_h);
|
||||
assert(ret);
|
||||
|
||||
/* Take the user specified one */
|
||||
e = secure_getenv("HOME");
|
||||
if (e && path_is_valid(e) && path_is_absolute(e)) {
|
||||
h = strdup(e);
|
||||
if (!h)
|
||||
return -ENOMEM;
|
||||
|
||||
*_h = path_simplify(h);
|
||||
return 0;
|
||||
}
|
||||
if (e && path_is_valid(e) && path_is_absolute(e))
|
||||
goto found;
|
||||
|
||||
/* Hardcode home directory for root and nobody to avoid NSS */
|
||||
u = getuid();
|
||||
if (u == 0) {
|
||||
h = strdup("/root");
|
||||
if (!h)
|
||||
return -ENOMEM;
|
||||
|
||||
*_h = h;
|
||||
return 0;
|
||||
e = "/root";
|
||||
goto found;
|
||||
}
|
||||
if (synthesize_nobody() &&
|
||||
u == UID_NOBODY) {
|
||||
h = strdup("/");
|
||||
if (!h)
|
||||
return -ENOMEM;
|
||||
|
||||
*_h = h;
|
||||
return 0;
|
||||
if (u == UID_NOBODY && synthesize_nobody()) {
|
||||
e = "/";
|
||||
goto found;
|
||||
}
|
||||
|
||||
/* Check the database... */
|
||||
@ -600,56 +599,42 @@ int get_home_dir(char **_h) {
|
||||
p = getpwuid(u);
|
||||
if (!p)
|
||||
return errno_or_else(ESRCH);
|
||||
e = p->pw_dir;
|
||||
|
||||
if (!path_is_valid(p->pw_dir) ||
|
||||
!path_is_absolute(p->pw_dir))
|
||||
if (!path_is_valid(e) || !path_is_absolute(e))
|
||||
return -EINVAL;
|
||||
|
||||
h = strdup(p->pw_dir);
|
||||
found:
|
||||
h = strdup(e);
|
||||
if (!h)
|
||||
return -ENOMEM;
|
||||
|
||||
*_h = path_simplify(h);
|
||||
*ret = path_simplify(h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_shell(char **_s) {
|
||||
int get_shell(char **ret) {
|
||||
struct passwd *p;
|
||||
const char *e;
|
||||
char *s;
|
||||
uid_t u;
|
||||
|
||||
assert(_s);
|
||||
assert(ret);
|
||||
|
||||
/* Take the user specified one */
|
||||
e = secure_getenv("SHELL");
|
||||
if (e && path_is_valid(e) && path_is_absolute(e)) {
|
||||
s = strdup(e);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
*_s = path_simplify(s);
|
||||
return 0;
|
||||
}
|
||||
if (e && path_is_valid(e) && path_is_absolute(e))
|
||||
goto found;
|
||||
|
||||
/* Hardcode shell for root and nobody to avoid NSS */
|
||||
u = getuid();
|
||||
if (u == 0) {
|
||||
s = strdup("/bin/sh");
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
*_s = s;
|
||||
return 0;
|
||||
e = default_root_shell(NULL);
|
||||
goto found;
|
||||
}
|
||||
if (synthesize_nobody() &&
|
||||
u == UID_NOBODY) {
|
||||
s = strdup(NOLOGIN);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
*_s = s;
|
||||
return 0;
|
||||
if (u == UID_NOBODY && synthesize_nobody()) {
|
||||
e = NOLOGIN;
|
||||
goto found;
|
||||
}
|
||||
|
||||
/* Check the database... */
|
||||
@ -657,16 +642,17 @@ int get_shell(char **_s) {
|
||||
p = getpwuid(u);
|
||||
if (!p)
|
||||
return errno_or_else(ESRCH);
|
||||
e = p->pw_shell;
|
||||
|
||||
if (!path_is_valid(p->pw_shell) ||
|
||||
!path_is_absolute(p->pw_shell))
|
||||
if (!path_is_valid(e) || !path_is_absolute(e))
|
||||
return -EINVAL;
|
||||
|
||||
s = strdup(p->pw_shell);
|
||||
found:
|
||||
s = strdup(e);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
*_s = path_simplify(s);
|
||||
*ret = path_simplify(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ int merge_gid_lists(const gid_t *list1, size_t size1, const gid_t *list2, size_t
|
||||
int getgroups_alloc(gid_t** gids);
|
||||
|
||||
int get_home_dir(char **ret);
|
||||
int get_shell(char **_ret);
|
||||
int get_shell(char **ret);
|
||||
|
||||
int reset_uid_gid(void);
|
||||
|
||||
@ -130,6 +130,7 @@ int putsgent_sane(const struct sgrp *sg, FILE *stream);
|
||||
#endif
|
||||
|
||||
bool is_nologin_shell(const char *shell);
|
||||
const char* default_root_shell(const char *root);
|
||||
|
||||
int is_this_me(const char *username);
|
||||
|
||||
|
@ -755,7 +755,7 @@ static int write_root_passwd(const char *passwd_path, const char *password, cons
|
||||
.pw_gid = 0,
|
||||
.pw_gecos = (char *) "Super User",
|
||||
.pw_dir = (char *) "/root",
|
||||
.pw_shell = (char *) (shell ?: "/bin/sh"),
|
||||
.pw_shell = (char *) (shell ?: default_root_shell(arg_root)),
|
||||
};
|
||||
|
||||
if (errno != ENOENT)
|
||||
|
@ -26,7 +26,7 @@ static const struct passwd root_passwd = {
|
||||
.pw_gid = 0,
|
||||
.pw_gecos = (char*) "Super User",
|
||||
.pw_dir = (char*) "/root",
|
||||
.pw_shell = (char*) "/bin/sh",
|
||||
.pw_shell = NULL,
|
||||
};
|
||||
|
||||
static const struct spwd root_spwd = {
|
||||
@ -142,24 +142,25 @@ NSS_INITGROUPS_PROTOTYPE(systemd);
|
||||
static enum nss_status copy_synthesized_passwd(
|
||||
struct passwd *dest,
|
||||
const struct passwd *src,
|
||||
const char *fallback_shell,
|
||||
char *buffer, size_t buflen,
|
||||
int *errnop) {
|
||||
|
||||
size_t required;
|
||||
|
||||
assert(dest);
|
||||
assert(src);
|
||||
assert(src->pw_name);
|
||||
assert(src->pw_passwd);
|
||||
assert(src->pw_gecos);
|
||||
assert(src->pw_dir);
|
||||
assert(src->pw_shell);
|
||||
|
||||
required = strlen(src->pw_name) + 1;
|
||||
required += strlen(src->pw_passwd) + 1;
|
||||
required += strlen(src->pw_gecos) + 1;
|
||||
required += strlen(src->pw_dir) + 1;
|
||||
required += strlen(src->pw_shell) + 1;
|
||||
const char *shell = ASSERT_PTR(src->pw_shell ?: fallback_shell);
|
||||
|
||||
size_t required =
|
||||
strlen(src->pw_name) + 1 +
|
||||
strlen(src->pw_passwd) + 1 +
|
||||
strlen(src->pw_gecos) + 1 +
|
||||
strlen(src->pw_dir) + 1 +
|
||||
strlen(shell) + 1;
|
||||
|
||||
if (buflen < required) {
|
||||
*errnop = ERANGE;
|
||||
@ -176,7 +177,7 @@ static enum nss_status copy_synthesized_passwd(
|
||||
dest->pw_gecos = stpcpy(dest->pw_passwd, src->pw_passwd) + 1;
|
||||
dest->pw_dir = stpcpy(dest->pw_gecos, src->pw_gecos) + 1;
|
||||
dest->pw_shell = stpcpy(dest->pw_dir, src->pw_dir) + 1;
|
||||
strcpy(dest->pw_shell, src->pw_shell);
|
||||
strcpy(dest->pw_shell, shell);
|
||||
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
@ -187,15 +188,14 @@ static enum nss_status copy_synthesized_spwd(
|
||||
char *buffer, size_t buflen,
|
||||
int *errnop) {
|
||||
|
||||
size_t required;
|
||||
|
||||
assert(dest);
|
||||
assert(src);
|
||||
assert(src->sp_namp);
|
||||
assert(src->sp_pwdp);
|
||||
|
||||
required = strlen(src->sp_namp) + 1;
|
||||
required += strlen(src->sp_pwdp) + 1;
|
||||
size_t required =
|
||||
strlen(src->sp_namp) + 1 +
|
||||
strlen(src->sp_pwdp) + 1;
|
||||
|
||||
if (buflen < required) {
|
||||
*errnop = ERANGE;
|
||||
@ -220,8 +220,6 @@ static enum nss_status copy_synthesized_group(
|
||||
char *buffer, size_t buflen,
|
||||
int *errnop) {
|
||||
|
||||
size_t required;
|
||||
|
||||
assert(dest);
|
||||
assert(src);
|
||||
assert(src->gr_name);
|
||||
@ -229,9 +227,10 @@ static enum nss_status copy_synthesized_group(
|
||||
assert(src->gr_mem);
|
||||
assert(!*src->gr_mem); /* Our synthesized records' gr_mem is always just NULL... */
|
||||
|
||||
required = strlen(src->gr_name) + 1;
|
||||
required += strlen(src->gr_passwd) + 1;
|
||||
required += sizeof(char*); /* ...but that NULL still needs to be stored into the buffer! */
|
||||
size_t required =
|
||||
strlen(src->gr_name) + 1 +
|
||||
strlen(src->gr_passwd) + 1 +
|
||||
sizeof(char*); /* ...but that NULL still needs to be stored into the buffer! */
|
||||
|
||||
if (buflen < ALIGN(required)) {
|
||||
*errnop = ERANGE;
|
||||
@ -257,15 +256,14 @@ static enum nss_status copy_synthesized_sgrp(
|
||||
char *buffer, size_t buflen,
|
||||
int *errnop) {
|
||||
|
||||
size_t required;
|
||||
|
||||
assert(dest);
|
||||
assert(src);
|
||||
assert(src->sg_namp);
|
||||
assert(src->sg_passwd);
|
||||
|
||||
required = strlen(src->sg_namp) + 1;
|
||||
required += strlen(src->sg_passwd) + 1;
|
||||
size_t required =
|
||||
strlen(src->sg_namp) + 1 +
|
||||
strlen(src->sg_passwd) + 1;
|
||||
|
||||
if (buflen < required) {
|
||||
*errnop = ERANGE;
|
||||
@ -310,13 +308,17 @@ enum nss_status _nss_systemd_getpwnam_r(
|
||||
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
|
||||
|
||||
if (streq(name, root_passwd.pw_name))
|
||||
return copy_synthesized_passwd(pwd, &root_passwd, buffer, buflen, errnop);
|
||||
return copy_synthesized_passwd(pwd, &root_passwd,
|
||||
default_root_shell(NULL),
|
||||
buffer, buflen, errnop);
|
||||
|
||||
if (streq(name, nobody_passwd.pw_name)) {
|
||||
if (!synthesize_nobody())
|
||||
return NSS_STATUS_NOTFOUND;
|
||||
|
||||
return copy_synthesized_passwd(pwd, &nobody_passwd, buffer, buflen, errnop);
|
||||
return copy_synthesized_passwd(pwd, &nobody_passwd,
|
||||
NULL,
|
||||
buffer, buflen, errnop);
|
||||
}
|
||||
|
||||
} else if (STR_IN_SET(name, root_passwd.pw_name, nobody_passwd.pw_name))
|
||||
@ -354,13 +356,17 @@ enum nss_status _nss_systemd_getpwuid_r(
|
||||
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
|
||||
|
||||
if (uid == root_passwd.pw_uid)
|
||||
return copy_synthesized_passwd(pwd, &root_passwd, buffer, buflen, errnop);
|
||||
return copy_synthesized_passwd(pwd, &root_passwd,
|
||||
default_root_shell(NULL),
|
||||
buffer, buflen, errnop);
|
||||
|
||||
if (uid == nobody_passwd.pw_uid) {
|
||||
if (!synthesize_nobody())
|
||||
return NSS_STATUS_NOTFOUND;
|
||||
|
||||
return copy_synthesized_passwd(pwd, &nobody_passwd, buffer, buflen, errnop);
|
||||
return copy_synthesized_passwd(pwd, &nobody_passwd,
|
||||
NULL,
|
||||
buffer, buflen, errnop);
|
||||
}
|
||||
|
||||
} else if (uid == root_passwd.pw_uid || uid == nobody_passwd.pw_uid)
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <utmp.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "chase-symlinks.h"
|
||||
#include "conf-files.h"
|
||||
#include "copy.h"
|
||||
#include "creds-util.h"
|
||||
@ -27,6 +28,7 @@
|
||||
#include "set.h"
|
||||
#include "smack-util.h"
|
||||
#include "specifier.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "sync-util.h"
|
||||
@ -390,8 +392,14 @@ static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char* default_shell(uid_t uid) {
|
||||
return uid == 0 ? "/bin/sh" : NOLOGIN;
|
||||
static const char* pick_shell(const Item *i) {
|
||||
if (i->type != ADD_USER)
|
||||
return NULL;
|
||||
if (i->shell)
|
||||
return i->shell;
|
||||
if (i->uid_set && i->uid == 0)
|
||||
return default_root_shell(arg_root);
|
||||
return NOLOGIN;
|
||||
}
|
||||
|
||||
static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char **tmpfile_path) {
|
||||
@ -473,7 +481,7 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char
|
||||
|
||||
/* Initialize the shell to nologin, with one exception:
|
||||
* for root we patch in something special */
|
||||
.pw_shell = i->shell ?: (char*) default_shell(i->uid),
|
||||
.pw_shell = (char*) pick_shell(i),
|
||||
};
|
||||
|
||||
/* Try to pick up the shell for this account via the credentials logic */
|
||||
@ -659,14 +667,14 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char **
|
||||
|
||||
r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to open temporary copy of %s: %m", group_path);
|
||||
return log_error_errno(r, "Failed to open temporary copy of %s: %m", group_path);
|
||||
|
||||
original = fopen(group_path, "re");
|
||||
if (original) {
|
||||
|
||||
r = copy_rights_with_fallback(fileno(original), fileno(group), group_tmp);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to copy permissions from %s to %s: %m",
|
||||
return log_error_errno(r, "Failed to copy permissions from %s to %s: %m",
|
||||
group_path, group_tmp);
|
||||
|
||||
while ((r = fgetgrent_sane(original, &gr)) > 0) {
|
||||
@ -692,19 +700,19 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char **
|
||||
|
||||
r = putgrent_with_members(gr, group);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to add existing group \"%s\" to temporary group file: %m",
|
||||
return log_error_errno(r, "Failed to add existing group \"%s\" to temporary group file: %m",
|
||||
gr->gr_name);
|
||||
if (r > 0)
|
||||
group_changed = true;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to read %s: %m", group_path);
|
||||
return log_error_errno(r, "Failed to read %s: %m", group_path);
|
||||
|
||||
} else {
|
||||
if (errno != ENOENT)
|
||||
return log_debug_errno(errno, "Failed to open %s: %m", group_path);
|
||||
return log_error_errno(errno, "Failed to open %s: %m", group_path);
|
||||
if (fchmod(fileno(group), 0644) < 0)
|
||||
return log_debug_errno(errno, "Failed to fchmod %s: %m", group_tmp);
|
||||
return log_error_errno(errno, "Failed to fchmod %s: %m", group_tmp);
|
||||
}
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(i, todo_gids) {
|
||||
@ -716,7 +724,7 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char **
|
||||
|
||||
r = putgrent_with_members(&n, group);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to add new group \"%s\" to temporary group file: %m",
|
||||
return log_error_errno(r, "Failed to add new group \"%s\" to temporary group file: %m",
|
||||
gr->gr_name);
|
||||
|
||||
group_changed = true;
|
||||
@ -726,19 +734,19 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char **
|
||||
while (gr) {
|
||||
r = putgrent_sane(gr, group);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to add existing group \"%s\" to temporary group file: %m",
|
||||
return log_error_errno(r, "Failed to add existing group \"%s\" to temporary group file: %m",
|
||||
gr->gr_name);
|
||||
|
||||
r = fgetgrent_sane(original, &gr);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to read %s: %m", group_path);
|
||||
return log_error_errno(r, "Failed to read %s: %m", group_path);
|
||||
if (r == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
r = fflush_sync_and_check(group);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to flush %s: %m", group_tmp);
|
||||
return log_error_errno(r, "Failed to flush %s: %m", group_tmp);
|
||||
|
||||
if (group_changed) {
|
||||
*tmpfile = TAKE_PTR(group);
|
||||
@ -765,7 +773,7 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch
|
||||
|
||||
r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to open temporary copy of %s: %m", gshadow_path);
|
||||
return log_error_errno(r, "Failed to open temporary copy of %s: %m", gshadow_path);
|
||||
|
||||
original = fopen(gshadow_path, "re");
|
||||
if (original) {
|
||||
@ -773,7 +781,7 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch
|
||||
|
||||
r = copy_rights_with_fallback(fileno(original), fileno(gshadow), gshadow_tmp);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to copy permissions from %s to %s: %m",
|
||||
return log_error_errno(r, "Failed to copy permissions from %s to %s: %m",
|
||||
gshadow_path, gshadow_tmp);
|
||||
|
||||
while ((r = fgetsgent_sane(original, &sg)) > 0) {
|
||||
@ -786,7 +794,7 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch
|
||||
|
||||
r = putsgent_with_members(sg, gshadow);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to add existing group \"%s\" to temporary gshadow file: %m",
|
||||
return log_error_errno(r, "Failed to add existing group \"%s\" to temporary gshadow file: %m",
|
||||
sg->sg_namp);
|
||||
if (r > 0)
|
||||
group_changed = true;
|
||||
@ -796,9 +804,9 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch
|
||||
|
||||
} else {
|
||||
if (errno != ENOENT)
|
||||
return log_debug_errno(errno, "Failed to open %s: %m", gshadow_path);
|
||||
return log_error_errno(errno, "Failed to open %s: %m", gshadow_path);
|
||||
if (fchmod(fileno(gshadow), 0000) < 0)
|
||||
return log_debug_errno(errno, "Failed to fchmod %s: %m", gshadow_tmp);
|
||||
return log_error_errno(errno, "Failed to fchmod %s: %m", gshadow_tmp);
|
||||
}
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(i, todo_gids) {
|
||||
@ -809,7 +817,7 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch
|
||||
|
||||
r = putsgent_with_members(&n, gshadow);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to add new group \"%s\" to temporary gshadow file: %m",
|
||||
return log_error_errno(r, "Failed to add new group \"%s\" to temporary gshadow file: %m",
|
||||
n.sg_namp);
|
||||
|
||||
group_changed = true;
|
||||
@ -817,7 +825,7 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch
|
||||
|
||||
r = fflush_sync_and_check(gshadow);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to flush %s: %m", gshadow_tmp);
|
||||
return log_error_errno(r, "Failed to flush %s: %m", gshadow_tmp);
|
||||
|
||||
if (group_changed) {
|
||||
*tmpfile = TAKE_PTR(gshadow);
|
||||
@ -858,30 +866,30 @@ static int write_files(void) {
|
||||
if (group) {
|
||||
r = make_backup("/etc/group", group_path);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to make backup %s: %m", group_path);
|
||||
return log_error_errno(r, "Failed to make backup %s: %m", group_path);
|
||||
}
|
||||
if (gshadow) {
|
||||
r = make_backup("/etc/gshadow", gshadow_path);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to make backup %s: %m", gshadow_path);
|
||||
return log_error_errno(r, "Failed to make backup %s: %m", gshadow_path);
|
||||
}
|
||||
|
||||
if (passwd) {
|
||||
r = make_backup("/etc/passwd", passwd_path);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to make backup %s: %m", passwd_path);
|
||||
return log_error_errno(r, "Failed to make backup %s: %m", passwd_path);
|
||||
}
|
||||
if (shadow) {
|
||||
r = make_backup("/etc/shadow", shadow_path);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to make backup %s: %m", shadow_path);
|
||||
return log_error_errno(r, "Failed to make backup %s: %m", shadow_path);
|
||||
}
|
||||
|
||||
/* And make the new files count */
|
||||
if (group) {
|
||||
r = rename_and_apply_smack_floor_label(group_tmp, group_path);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to rename %s to %s: %m",
|
||||
return log_error_errno(r, "Failed to rename %s to %s: %m",
|
||||
group_tmp, group_path);
|
||||
group_tmp = mfree(group_tmp);
|
||||
|
||||
@ -891,7 +899,7 @@ static int write_files(void) {
|
||||
if (gshadow) {
|
||||
r = rename_and_apply_smack_floor_label(gshadow_tmp, gshadow_path);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to rename %s to %s: %m",
|
||||
return log_error_errno(r, "Failed to rename %s to %s: %m",
|
||||
gshadow_tmp, gshadow_path);
|
||||
|
||||
gshadow_tmp = mfree(gshadow_tmp);
|
||||
@ -900,7 +908,7 @@ static int write_files(void) {
|
||||
if (passwd) {
|
||||
r = rename_and_apply_smack_floor_label(passwd_tmp, passwd_path);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to rename %s to %s: %m",
|
||||
return log_error_errno(r, "Failed to rename %s to %s: %m",
|
||||
passwd_tmp, passwd_path);
|
||||
|
||||
passwd_tmp = mfree(passwd_tmp);
|
||||
@ -911,7 +919,7 @@ static int write_files(void) {
|
||||
if (shadow) {
|
||||
r = rename_and_apply_smack_floor_label(shadow_tmp, shadow_path);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to rename %s to %s: %m",
|
||||
return log_error_errno(r, "Failed to rename %s to %s: %m",
|
||||
shadow_tmp, shadow_path);
|
||||
|
||||
shadow_tmp = mfree(shadow_tmp);
|
||||
@ -981,7 +989,7 @@ static int root_stat(const char *p, struct stat *st) {
|
||||
return RET_NERRNO(stat(fix, st));
|
||||
}
|
||||
|
||||
static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
|
||||
static int read_id_from_file(Item *i, uid_t *ret_uid, gid_t *ret_gid) {
|
||||
struct stat st;
|
||||
bool found_uid = false, found_gid = false;
|
||||
uid_t uid = 0;
|
||||
@ -990,13 +998,13 @@ static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
|
||||
assert(i);
|
||||
|
||||
/* First, try to get the GID directly */
|
||||
if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
|
||||
if (ret_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
|
||||
gid = st.st_gid;
|
||||
found_gid = true;
|
||||
}
|
||||
|
||||
/* Then, try to get the UID directly */
|
||||
if ((_uid || (_gid && !found_gid))
|
||||
if ((ret_uid || (ret_gid && !found_gid))
|
||||
&& i->uid_path
|
||||
&& root_stat(i->uid_path, &st) >= 0) {
|
||||
|
||||
@ -1004,14 +1012,14 @@ static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
|
||||
found_uid = true;
|
||||
|
||||
/* If we need the gid, but had no success yet, also derive it from the UID path */
|
||||
if (_gid && !found_gid) {
|
||||
if (ret_gid && !found_gid) {
|
||||
gid = st.st_gid;
|
||||
found_gid = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* If that didn't work yet, then let's reuse the GID as UID */
|
||||
if (_uid && !found_uid && i->gid_path) {
|
||||
if (ret_uid && !found_uid && i->gid_path) {
|
||||
|
||||
if (found_gid) {
|
||||
uid = (uid_t) gid;
|
||||
@ -1022,18 +1030,18 @@ static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
|
||||
}
|
||||
}
|
||||
|
||||
if (_uid) {
|
||||
if (ret_uid) {
|
||||
if (!found_uid)
|
||||
return 0;
|
||||
|
||||
*_uid = uid;
|
||||
*ret_uid = uid;
|
||||
}
|
||||
|
||||
if (_gid) {
|
||||
if (ret_gid) {
|
||||
if (!found_gid)
|
||||
return 0;
|
||||
|
||||
*_gid = gid;
|
||||
*ret_gid = gid;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -1444,7 +1452,9 @@ static int add_implicit(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool item_equal(Item *a, Item *b) {
|
||||
static int item_equivalent(Item *a, Item *b) {
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
assert(b);
|
||||
|
||||
@ -1454,6 +1464,7 @@ static bool item_equal(Item *a, Item *b) {
|
||||
if (!streq_ptr(a->name, b->name))
|
||||
return false;
|
||||
|
||||
/* Paths were simplified previously, so we can use streq. */
|
||||
if (!streq_ptr(a->uid_path, b->uid_path))
|
||||
return false;
|
||||
|
||||
@ -1478,8 +1489,38 @@ static bool item_equal(Item *a, Item *b) {
|
||||
if (!streq_ptr(a->home, b->home))
|
||||
return false;
|
||||
|
||||
if (!streq_ptr(a->shell, b->shell))
|
||||
return false;
|
||||
/* Check if the two paths refer to the same file.
|
||||
* If the paths are equal (after normalization), it's obviously the same file.
|
||||
* If both paths specify a nologin shell, treat them as the same (e.g. /bin/true and /bin/false).
|
||||
* Otherwise, try to resolve the paths, and see if we get the same result, (e.g. /sbin/nologin and
|
||||
* /usr/sbin/nologin).
|
||||
* If we can't resolve something, treat different paths as different. */
|
||||
|
||||
const char *a_shell = pick_shell(a),
|
||||
*b_shell = pick_shell(b);
|
||||
if (!path_equal_ptr(a_shell, b_shell) &&
|
||||
!(is_nologin_shell(a_shell) && is_nologin_shell(b_shell))) {
|
||||
_cleanup_free_ char *pa = NULL, *pb = NULL;
|
||||
|
||||
r = chase_symlinks(a_shell, arg_root, CHASE_PREFIX_ROOT | CHASE_NONEXISTENT, &pa, NULL);
|
||||
if (r < 0) {
|
||||
log_full_errno(ERRNO_IS_RESOURCE(r) ? LOG_ERR : LOG_DEBUG,
|
||||
r, "Failed to look up path '%s%s%s': %m",
|
||||
strempty(arg_root), arg_root ? "/" : "", a_shell);
|
||||
return ERRNO_IS_RESOURCE(r) ? r : false;
|
||||
}
|
||||
|
||||
r = chase_symlinks(b_shell, arg_root, CHASE_PREFIX_ROOT | CHASE_NONEXISTENT, &pb, NULL);
|
||||
if (r < 0) {
|
||||
log_full_errno(ERRNO_IS_RESOURCE(r) ? LOG_ERR : LOG_DEBUG,
|
||||
r, "Failed to look up path '%s%s%s': %m",
|
||||
strempty(arg_root), arg_root ? "/" : "", b_shell);
|
||||
return ERRNO_IS_RESOURCE(r) ? r : false;
|
||||
}
|
||||
|
||||
if (!path_equal(pa, pb))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1506,22 +1547,22 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
|
||||
r = extract_many_words(&p, NULL, EXTRACT_UNQUOTE,
|
||||
&action, &name, &id, &description, &home, &shell, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "[%s:%u] Syntax error.", fname, line);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, r, "Syntax error.");
|
||||
if (r < 2)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"[%s:%u] Missing action and name columns.", fname, line);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Missing action and name columns.");
|
||||
if (!isempty(p))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"[%s:%u] Trailing garbage.", fname, line);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Trailing garbage.");
|
||||
|
||||
/* Verify action */
|
||||
if (strlen(action) != 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"[%s:%u] Unknown modifier '%s'", fname, line, action);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Unknown modifier '%s'.", action);
|
||||
|
||||
if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
|
||||
"[%s:%u] Unknown command type '%c'.", fname, line, action[0]);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Unknown command type '%c'.", action[0]);
|
||||
|
||||
/* Verify name */
|
||||
if (empty_or_dash(name))
|
||||
@ -1530,12 +1571,11 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
|
||||
if (name) {
|
||||
r = specifier_printf(name, NAME_MAX, system_and_tmp_specifier_table, arg_root, NULL, &resolved_name);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m", fname, line, name);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to replace specifiers in '%s': %m", 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);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"'%s' is not a valid user or group name.", resolved_name);
|
||||
}
|
||||
|
||||
/* Verify id */
|
||||
@ -1545,8 +1585,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
|
||||
if (id) {
|
||||
r = specifier_printf(id, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_id);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
|
||||
fname, line, name);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, r,
|
||||
"Failed to replace specifiers in '%s': %m", name);
|
||||
}
|
||||
|
||||
/* Verify description */
|
||||
@ -1556,13 +1596,12 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
|
||||
if (description) {
|
||||
r = specifier_printf(description, LONG_LINE_MAX, system_and_tmp_specifier_table, arg_root, NULL, &resolved_description);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
|
||||
fname, line, description);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, r,
|
||||
"Failed to replace specifiers in '%s': %m", description);
|
||||
|
||||
if (!valid_gecos(resolved_description))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"[%s:%u] '%s' is not a valid GECOS field.",
|
||||
fname, line, resolved_description);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"'%s' is not a valid GECOS field.", resolved_description);
|
||||
}
|
||||
|
||||
/* Verify home */
|
||||
@ -1572,13 +1611,14 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
|
||||
if (home) {
|
||||
r = specifier_printf(home, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_home);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
|
||||
fname, line, home);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, r,
|
||||
"Failed to replace specifiers in '%s': %m", home);
|
||||
|
||||
path_simplify(resolved_home);
|
||||
|
||||
if (!valid_home(resolved_home))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"[%s:%u] '%s' is not a valid home directory field.",
|
||||
fname, line, resolved_home);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"'%s' is not a valid home directory field.", resolved_home);
|
||||
}
|
||||
|
||||
/* Verify shell */
|
||||
@ -1588,63 +1628,59 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
|
||||
if (shell) {
|
||||
r = specifier_printf(shell, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_shell);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
|
||||
fname, line, shell);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, r,
|
||||
"Failed to replace specifiers in '%s': %m", shell);
|
||||
|
||||
path_simplify(resolved_shell);
|
||||
|
||||
if (!valid_shell(resolved_shell))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"[%s:%u] '%s' is not a valid login shell field.",
|
||||
fname, line, resolved_shell);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"'%s' is not a valid login shell field.", resolved_shell);
|
||||
}
|
||||
|
||||
switch (action[0]) {
|
||||
|
||||
case ADD_RANGE:
|
||||
if (resolved_name)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"[%s:%u] Lines of type 'r' don't take a name field.",
|
||||
fname, line);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Lines of type 'r' don't take a name field.");
|
||||
|
||||
if (!resolved_id)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"[%s:%u] Lines of type 'r' require an ID range in the third field.",
|
||||
fname, line);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Lines of type 'r' require an ID range in the third field.");
|
||||
|
||||
if (description || home || shell)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"[%s:%u] Lines of type '%c' don't take a %s field.",
|
||||
fname, line, action[0],
|
||||
description ? "GECOS" : home ? "home directory" : "login shell");
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Lines of type '%c' don't take a %s field.",
|
||||
action[0],
|
||||
description ? "GECOS" : home ? "home directory" : "login shell");
|
||||
|
||||
r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id);
|
||||
if (r < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"[%s:%u] Invalid UID range %s.", fname, line, resolved_id);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Invalid UID range %s.", resolved_id);
|
||||
|
||||
return 0;
|
||||
|
||||
case ADD_MEMBER: {
|
||||
/* Try to extend an existing member or group item */
|
||||
if (!name)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"[%s:%u] Lines of type 'm' require a user name in the second field.",
|
||||
fname, line);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Lines of type 'm' require a user name in the second field.");
|
||||
|
||||
if (!resolved_id)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"[%s:%u] Lines of type 'm' require a group name in the third field.",
|
||||
fname, line);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Lines of type 'm' require a group name in the third field.");
|
||||
|
||||
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);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"'%s' is not a valid user or group name.", resolved_id);
|
||||
|
||||
if (description || home || shell)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"[%s:%u] Lines of type '%c' don't take a %s field.",
|
||||
fname, line, action[0],
|
||||
description ? "GECOS" : home ? "home directory" : "login shell");
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Lines of type '%c' don't take a %s field.",
|
||||
action[0],
|
||||
description ? "GECOS" : home ? "home directory" : "login shell");
|
||||
|
||||
r = string_strv_ordered_hashmap_put(&members, resolved_id, resolved_name);
|
||||
if (r < 0)
|
||||
@ -1655,9 +1691,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
|
||||
|
||||
case ADD_USER:
|
||||
if (!name)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"[%s:%u] Lines of type 'u' require a user name in the second field.",
|
||||
fname, line);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Lines of type 'u' require a user name in the second field.");
|
||||
|
||||
r = ordered_hashmap_ensure_allocated(&users, &item_hash_ops);
|
||||
if (r < 0)
|
||||
@ -1679,7 +1714,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
|
||||
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);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, r,
|
||||
"Failed to parse GID: '%s': %m", id);
|
||||
} else {
|
||||
i->gid_set = true;
|
||||
i->id_set_strict = true;
|
||||
@ -1689,7 +1725,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
|
||||
if (!streq(resolved_id, "-")) {
|
||||
r = parse_uid(resolved_id, &i->uid);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse UID: '%s': %m", id);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, r,
|
||||
"Failed to parse UID: '%s': %m", id);
|
||||
i->uid_set = true;
|
||||
}
|
||||
}
|
||||
@ -1704,15 +1741,14 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
|
||||
|
||||
case ADD_GROUP:
|
||||
if (!name)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"[%s:%u] Lines of type 'g' require a user name in the second field.",
|
||||
fname, line);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Lines of type 'g' require a user name in the second field.");
|
||||
|
||||
if (description || home || shell)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"[%s:%u] Lines of type '%c' don't take a %s field.",
|
||||
fname, line, action[0],
|
||||
description ? "GECOS" : home ? "home directory" : "login shell");
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Lines of type '%c' don't take a %s field.",
|
||||
action[0],
|
||||
description ? "GECOS" : home ? "home directory" : "login shell");
|
||||
|
||||
r = ordered_hashmap_ensure_allocated(&groups, &item_hash_ops);
|
||||
if (r < 0)
|
||||
@ -1729,7 +1765,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
|
||||
} else {
|
||||
r = parse_gid(resolved_id, &i->gid);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, r,
|
||||
"Failed to parse GID: '%s': %m", id);
|
||||
|
||||
i->gid_set = true;
|
||||
}
|
||||
@ -1747,11 +1784,14 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
|
||||
|
||||
existing = ordered_hashmap_get(h, i->name);
|
||||
if (existing) {
|
||||
/* Two identical items are fine */
|
||||
if (!item_equal(existing, i))
|
||||
log_warning("%s:%u: conflict with earlier configuration for %s '%s', ignoring line.",
|
||||
fname, line,
|
||||
item_type_to_string(i->type), i->name);
|
||||
/* Two functionally-equivalent items are fine */
|
||||
r = item_equivalent(existing, i);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
log_syntax(NULL, LOG_WARNING, fname, line, SYNTHETIC_ERRNO(EUCLEAN),
|
||||
"Conflict with earlier configuration for %s '%s', ignoring line.",
|
||||
item_type_to_string(i->type), i->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2146,11 +2186,7 @@ static int run(int argc, char *argv[]) {
|
||||
ORDERED_HASHMAP_FOREACH(i, users)
|
||||
(void) process_item(i);
|
||||
|
||||
r = write_files();
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write files: %m");
|
||||
|
||||
return 0;
|
||||
return write_files();
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
|
@ -347,8 +347,8 @@ static void test_get_user_creds_one(const char *id, const char *name, uid_t uid,
|
||||
}
|
||||
|
||||
TEST(get_user_creds) {
|
||||
test_get_user_creds_one("root", "root", 0, 0, "/root", "/bin/sh");
|
||||
test_get_user_creds_one("0", "root", 0, 0, "/root", "/bin/sh");
|
||||
test_get_user_creds_one("root", "root", 0, 0, "/root", DEFAULT_USER_SHELL);
|
||||
test_get_user_creds_one("0", "root", 0, 0, "/root", DEFAULT_USER_SHELL);
|
||||
test_get_user_creds_one(NOBODY_USER_NAME, NOBODY_USER_NAME, UID_NOBODY, GID_NOBODY, "/", NOLOGIN);
|
||||
test_get_user_creds_one("65534", NOBODY_USER_NAME, UID_NOBODY, GID_NOBODY, "/", NOLOGIN);
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ ExecStart=sh -c 'test %U = $$(id -u)'
|
||||
ExecStart=sh -c 'test %g = $$(id -gn)'
|
||||
ExecStart=sh -c 'test %G = $$(id -g)'
|
||||
ExecStart=test %h = /root
|
||||
ExecStart=sh -c 'test %s = /bin/sh'
|
||||
ExecStart=sh -c 'test -x %s'
|
||||
ExecStart=sh -c 'test %m = $$(cat /etc/machine-id)'
|
||||
ExecStart=sh -c 'test %b = $$(cat /proc/sys/kernel/random/boot_id | sed -e 's/-//g')'
|
||||
ExecStart=sh -c 'test %H = $$(uname -n)'
|
||||
|
@ -23,7 +23,7 @@ ExecStart=sh -c 'test %U = $$(id -u)'
|
||||
ExecStart=sh -c 'test %g = $$(id -gn)'
|
||||
ExecStart=sh -c 'test %G = $$(id -g)'
|
||||
ExecStart=test %h = /root
|
||||
ExecStart=sh -c 'test %s = /bin/sh'
|
||||
ExecStart=sh -c 'test -x %s'
|
||||
ExecStart=sh -c 'test %m = $$(cat /etc/machine-id)'
|
||||
ExecStart=sh -c 'test %b = $$(cat /proc/sys/kernel/random/boot_id | sed -e 's/-//g')'
|
||||
ExecStart=sh -c 'test %H = $$(uname -n)'
|
||||
|
@ -152,7 +152,7 @@ for f in $(ls -1 $SOURCE/unhappy-*.input | sort -V); do
|
||||
echo "*** Running test $f"
|
||||
prepare_testdir ${f%.input}
|
||||
cp $f $TESTDIR/usr/lib/sysusers.d/test.conf
|
||||
$SYSUSERS --root=$TESTDIR 2>&1 | tail -n1 > $TESTDIR/err
|
||||
$SYSUSERS --root=$TESTDIR 2>&1 | tail -n1 | sed -r 's/^[^:]+:[^:]+://' >$TESTDIR/err
|
||||
if ! diff -u $TESTDIR/err ${f%.*}.expected-err; then
|
||||
echo "**** Unexpected error output for $f"
|
||||
cat $TESTDIR/err
|
||||
|
@ -1 +1 @@
|
||||
Failed to parse UID: '9999999999': Numerical result out of range
|
||||
Failed to parse UID: '9999999999': Numerical result out of range
|
||||
|
Loading…
x
Reference in New Issue
Block a user