summaryrefslogtreecommitdiff
path: root/src/sysusers
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2022-08-19 15:38:56 +0200
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2022-08-24 10:02:46 +0200
commit5f465fda4ec9f1e70a1bb993944ea92b2469b0db (patch)
treef89c98c7c4034e1cd0e570f3b3c57d2366ca6369 /src/sysusers
parent0336c23e98caf9a8e374ef3823858a9926077ec8 (diff)
downloadsystemd-5f465fda4ec9f1e70a1bb993944ea92b2469b0db.tar.gz
sysusers: do not warn about values that equivalent
We'd warn that "-" and "/sbin/nologin" are different, even even though "/sbin/nologin" is the default we'd use. So let's stop warning in all cases where the config would lead to the same file, also under different paths, or when both shells are nologin shells. The general idea is to avoid warnings when sysusers config is moved between packages (and not exactly the same), or when it is generated from some template and the details change in an unimportant way. We try to chase symlinks. This means that on unmerged-usr systems we'll find that e.g. /usr/bin/bash and /bin/bash are equivalent if the basic fs structure is already in place (bash doesn't actually have to be installed, enough that the /bin symlink exists). I think this is a good result: after all, /bin/bash and /usr/bin/bash *may* be different things on an unmerged-usr system. Fixes #24215.
Diffstat (limited to 'src/sysusers')
-rw-r--r--src/sysusers/sysusers.c60
1 files changed, 52 insertions, 8 deletions
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index 4bb173da46..5eb67ea084 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -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 "/bin/sh";
+ 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 */
@@ -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;
}
@@ -1743,8 +1784,11 @@ 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))
+ /* 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);