summaryrefslogtreecommitdiff
path: root/src/basic/user-util.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2018-08-02 18:36:47 +0200
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2018-08-20 15:58:21 +0200
commitfafff8f1ffdf24517921d7779c2a9eb89766df30 (patch)
treea9f5bd4e5fb6e7680df49e9476b17366e58d06cf /src/basic/user-util.c
parent163a035aa654aa16b5d26f6436b13b94fc75b3f2 (diff)
downloadsystemd-fafff8f1ffdf24517921d7779c2a9eb89766df30.tar.gz
user-util: rework get_user_creds()
Let's fold get_user_creds_clean() into get_user_creds(), and introduce a flags argument for it to select "clean" behaviour. This flags parameter also learns to other new flags: - USER_CREDS_SYNTHESIZE_FALLBACK: in this mode the user records for root/nobody are only synthesized as fallback. Normally, the synthesized records take precedence over what is in the user database. With this flag set this is reversed, and the user database takes precedence, and the synthesized records are only used if they are missing there. This flag should be set in cases where doing NSS is deemed safe, and where there's interest in knowing the correct shell, for example if the admin changed root's shell to zsh or suchlike. - USER_CREDS_ALLOW_MISSING: if set, and a UID/GID is specified by numeric value, and there's no user/group record for it accept it anyway. This allows us to fix #9767 This then also ports all users to set the most appropriate flags. Fixes: #9767 [zj: remove one isempty() call]
Diffstat (limited to 'src/basic/user-util.c')
-rw-r--r--src/basic/user-util.c173
1 files changed, 105 insertions, 68 deletions
diff --git a/src/basic/user-util.c b/src/basic/user-util.c
index a562a397c7..b6185597de 100644
--- a/src/basic/user-util.c
+++ b/src/basic/user-util.c
@@ -87,17 +87,31 @@ char *getusername_malloc(void) {
return uid_to_name(getuid());
}
-int get_user_creds(
+static inline 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,
+ * hence let's list them all. */
+ "/bin/nologin",
+ "/sbin/nologin",
+ "/usr/bin/nologin",
+ "/usr/sbin/nologin",
+ /* 'true' and 'false' work too for the same purpose, but are less friendly as they don't do
+ * any message printing. Different distributions place the binary at various places but at
+ * least not in the 'sbin' directory. */
+ "/bin/false",
+ "/usr/bin/false",
+ "/bin/true",
+ "/usr/bin/true");
+}
+
+static int synthesize_user_creds(
const char **username,
uid_t *uid, gid_t *gid,
const char **home,
- const char **shell) {
-
- struct passwd *p;
- uid_t u;
-
- assert(username);
- assert(*username);
+ const char **shell,
+ UserCredsFlags flags) {
/* We enforce some special rules for uid=0 and uid=65534: in order to avoid NSS lookups for root we hardcode
* their user record data. */
@@ -129,32 +143,85 @@ int get_user_creds(
*gid = GID_NOBODY;
if (home)
- *home = "/";
+ *home = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/";
if (shell)
- *shell = "/sbin/nologin";
+ *shell = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/sbin/nologin";
return 0;
}
+ return -ENOMEDIUM;
+}
+
+int get_user_creds(
+ const char **username,
+ uid_t *uid, gid_t *gid,
+ const char **home,
+ const char **shell,
+ UserCredsFlags flags) {
+
+ uid_t u = UID_INVALID;
+ struct passwd *p;
+ int r;
+
+ assert(username);
+ assert(*username);
+
+ if (!FLAGS_SET(flags, USER_CREDS_SYNTHESIZE_FALLBACK) ||
+ (!home && !shell)) {
+
+ /* So here's the deal: normally, we'll try to synthesize all records we can synthesize, and override
+ * the user database with that. However, if the user specifies USER_CREDS_SYNTHESIZE_FALLBACK then the
+ * user database will override the synthetic records instead — except if the user is only interested in
+ * the UID and/or GID (but not the home directory, or the shell), in which case we'll always override
+ * the user database (i.e. the USER_CREDS_SYNTHESIZE_FALLBACK flag has no effect in this case). Why?
+ * Simply because there are valid usecase where the user might change the home directory or the shell
+ * of the relevant users, but changing the UID/GID mappings for them is something we explicitly don't
+ * support. */
+
+ r = synthesize_user_creds(username, uid, gid, home, shell, flags);
+ if (r >= 0)
+ return 0;
+ if (r != -ENOMEDIUM) /* not a username we can synthesize */
+ return r;
+ }
+
if (parse_uid(*username, &u) >= 0) {
errno = 0;
p = getpwuid(u);
- /* If there are multiple users with the same id, make
- * sure to leave $USER to the configured value instead
- * of the first occurrence in the database. However if
- * the uid was configured by a numeric uid, then let's
- * pick the real username from /etc/passwd. */
+ /* If there are multiple users with the same id, make sure to leave $USER to the configured value
+ * instead of the first occurrence in the database. However if the uid was configured by a numeric uid,
+ * then let's pick the real username from /etc/passwd. */
if (p)
*username = p->pw_name;
+ else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !gid && !home && !shell) {
+
+ /* If the specified user is a numeric UID and it isn't in the user database, and the caller
+ * passed USER_CREDS_ALLOW_MISSING and was only interested in the UID, then juts return that
+ * and don't complain. */
+
+ if (uid)
+ *uid = u;
+
+ return 0;
+ }
} else {
errno = 0;
p = getpwnam(*username);
}
+ if (!p) {
+ r = errno > 0 ? -errno : -ESRCH;
- if (!p)
- return errno > 0 ? -errno : -ESRCH;
+ /* If the user requested that we only synthesize as fallback, do so now */
+ if (FLAGS_SET(flags, USER_CREDS_SYNTHESIZE_FALLBACK)) {
+ if (synthesize_user_creds(username, uid, gid, home, shell, flags) >= 0)
+ return 0;
+ }
+
+ return r;
+ }
if (uid) {
if (!uid_is_valid(p->pw_uid))
@@ -170,66 +237,30 @@ int get_user_creds(
*gid = p->pw_gid;
}
- if (home)
- *home = p->pw_dir;
-
- if (shell)
- *shell = p->pw_shell;
-
- return 0;
-}
-
-static inline 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,
- * hence let's list them all. */
- "/bin/nologin",
- "/sbin/nologin",
- "/usr/bin/nologin",
- "/usr/sbin/nologin",
- /* 'true' and 'false' work too for the same purpose, but are less friendly as they don't do
- * any message printing. Different distributions place the binary at various places but at
- * least not in the 'sbin' directory. */
- "/bin/false",
- "/usr/bin/false",
- "/bin/true",
- "/usr/bin/true");
-}
-
-int get_user_creds_clean(
- const char **username,
- uid_t *uid, gid_t *gid,
- const char **home,
- const char **shell) {
-
- int r;
-
- /* Like get_user_creds(), but resets home/shell to NULL if they don't contain anything relevant. */
-
- r = get_user_creds(username, uid, gid, home, shell);
- if (r < 0)
- return r;
-
- if (shell &&
- (isempty(*shell) || is_nologin_shell(*shell)))
- *shell = NULL;
+ if (home) {
+ if (FLAGS_SET(flags, USER_CREDS_CLEAN) && empty_or_root(p->pw_dir))
+ *home = NULL;
+ else
+ *home = p->pw_dir;
+ }
- if (home && empty_or_root(*home))
- *home = NULL;
+ if (shell) {
+ if (FLAGS_SET(flags, USER_CREDS_CLEAN) && (isempty(p->pw_shell) || is_nologin_shell(p->pw_shell)))
+ *shell = NULL;
+ else
+ *shell = p->pw_shell;
+ }
return 0;
}
-int get_group_creds(const char **groupname, gid_t *gid) {
+int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) {
struct group *g;
gid_t id;
assert(groupname);
- /* We enforce some special rules for gid=0: in order to avoid
- * NSS lookups for root we hardcode its data. */
+ /* We enforce some special rules for gid=0: in order to avoid NSS lookups for root we hardcode its data. */
if (STR_IN_SET(*groupname, "root", "0")) {
*groupname = "root";
@@ -256,6 +287,12 @@ int get_group_creds(const char **groupname, gid_t *gid) {
if (g)
*groupname = g->gr_name;
+ else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING)) {
+ if (gid)
+ *gid = id;
+
+ return 0;
+ }
} else {
errno = 0;
g = getgrnam(*groupname);
@@ -391,7 +428,7 @@ int in_group(const char *name) {
int r;
gid_t gid;
- r = get_group_creds(&name, &gid);
+ r = get_group_creds(&name, &gid, 0);
if (r < 0)
return r;