summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--man/sysusers.d.xml89
-rw-r--r--src/basic/user-util.c2
-rw-r--r--src/basic/user-util.h9
-rw-r--r--src/sysusers/sysusers.c75
4 files changed, 111 insertions, 64 deletions
diff --git a/man/sysusers.d.xml b/man/sysusers.d.xml
index c0d8a1682a..47f018f402 100644
--- a/man/sysusers.d.xml
+++ b/man/sysusers.d.xml
@@ -57,11 +57,14 @@
<refsect1>
<title>Description</title>
- <para><command>systemd-sysusers</command> uses the files from <filename>sysusers.d</filename> directory to create
- system users and groups at package installation or boot time. This tool may be used to allocate system users and
- groups only, it is not useful for creating non-system (i.e. regular, "human") users and groups, as it accesses
- <filename>/etc/passwd</filename> and <filename>/etc/group</filename> directly, bypassing any more complex user
- databases, for example any database involving NIS or LDAP.</para>
+ <para><command>systemd-sysusers</command> uses the files from
+ <filename>sysusers.d</filename> directory to create system users and groups and
+ to add users to groups, at package installation or boot time. This tool may be
+ used to allocate system users and groups only, it is not useful for creating
+ non-system (i.e. regular, "human") users and groups, as it accesses
+ <filename>/etc/passwd</filename> and <filename>/etc/group</filename> directly,
+ bypassing any more complex user databases, for example any database involving NIS
+ or LDAP.</para>
</refsect1>
<refsect1>
@@ -100,15 +103,16 @@
<refsect1>
<title>Configuration File Format</title>
- <para>The file format is one line per user or group containing
- name, ID, GECOS field description and home directory:</para>
+ <para>The file format is one line per user or group containing name, ID, GECOS
+ field description, home directory, and login shell:</para>
- <programlisting>#Type Name ID GECOS Home directory
-u httpd 440 "HTTP User"
-u authd /usr/bin/authd "Authorization user"
-g input - -
-m authd input
-u root 0 "Superuser" /root</programlisting>
+ <programlisting>#Type Name ID GECOS Home directory Shell
+u httpd 404 "HTTP User"
+u authd /usr/bin/authd "Authorization user"
+u postgres - "Postgresql Database" /var/lib/pgsql /usr/libexec/postgresdb
+g input - -
+m authd input
+u root 0 "Superuser" /root /bin/zsh</programlisting>
<para>Empty lines and lines beginning with the <literal>#</literal> character are ignored, and may be used for
commenting.</para>
@@ -122,14 +126,10 @@ u root 0 "Superuser" /root</programlisting>
<variablelist>
<varlistentry>
<term><varname>u</varname></term>
- <listitem><para>Create a system user and group of the
- specified name should they not exist yet. The user's primary
- group will be set to the group bearing the same name. The
- user's shell will be set to
- <filename>/sbin/nologin</filename>, the home directory to
- the specified home directory, or <filename>/</filename> if
- none is given. The account will be created disabled, so that
- logins are not allowed.</para></listitem>
+ <listitem><para>Create a system user and group of the specified name should
+ they not exist yet. The user's primary group will be set to the group
+ bearing the same name. The account will be created disabled, so that logins
+ are not allowed.</para></listitem>
</varlistentry>
<varlistentry>
@@ -187,7 +187,8 @@ u root 0 "Superuser" /root</programlisting>
numeric 32-bit UID or GID of the user/group. Do not use IDs 65535
or 4294967295, as they have special placeholder meanings.
Specify <literal>-</literal> for automatic UID/GID allocation
- for the user or group. Alternatively, specify an absolute path
+ for the user or group (this is strongly recommended unless it is strictly
+ necessary to use a specific UID or GID). Alternatively, specify an absolute path
in the file system. In this case, the UID/GID is read from the
path's owner/group. This is useful to create users whose UID/GID
match the owners of pre-existing files (such as SUID or SGID
@@ -209,37 +210,45 @@ u root 0 "Superuser" /root</programlisting>
<refsect2>
<title>GECOS</title>
- <para>A short, descriptive string for users to be created,
- enclosed in quotation marks. Note that this field may not
- contain colons.</para>
+ <para>A short, descriptive string for users to be created, enclosed in
+ quotation marks. Note that this field may not contain colons.</para>
- <para>Only applies to lines of type <varname>u</varname> and
- should otherwise be left unset, or be set to
- <literal>-</literal>.</para>
+ <para>Only applies to lines of type <varname>u</varname> and should otherwise
+ be left unset (or <literal>-</literal>).</para>
</refsect2>
<refsect2>
<title>Home Directory</title>
- <para>The home directory for a new system user. If omitted,
- defaults to the root directory. It is recommended to not
- unnecessarily specify home directories for system users, unless
- software strictly requires one to be set.</para>
+ <para>The home directory for a new system user. If omitted, defaults to the
+ root directory.</para>
- <para>Only applies to lines of type <varname>u</varname> and
- should otherwise be left unset, or be set to
- <literal>-</literal>.</para>
+ <para>Only applies to lines of type <varname>u</varname> and should otherwise
+ be left unset (or <literal>-</literal>). It is recommended to omit this, unless
+ software strictly requires a home directory to be set.</para>
+ </refsect2>
+
+ <refsect2>
+ <title>Shell</title>
+
+ <para>The login shell of the user. If not specified, this will be set to
+ <filename>/sbin/nologin</filename>, except if the UID of the user is 0, in
+ which case <filename>/bin/sh</filename> will be used.</para>
+
+ <para>Only applies to lines of type <varname>u</varname> and should otherwise
+ be left unset (or <literal>-</literal>). It is recommended to omit this, unless
+ a shell different <filename>/sbin/nologin</filename> must be used.</para>
</refsect2>
</refsect1>
<refsect1>
<title>Idempotence</title>
- <para>Note that <command>systemd-sysusers</command> will do
- nothing if the specified users or groups already exist, so
- normally, there is no reason to override
- <filename>sysusers.d</filename> vendor configuration, except to
- block certain users or groups from being created.</para>
+ <para>Note that <command>systemd-sysusers</command> will do nothing if the
+ specified users or groups already exist or the users are members of specified
+ groups, so normally there is no reason to override
+ <filename>sysusers.d</filename> vendor configuration, except to block certain
+ users or groups from being created.</para>
</refsect1>
<refsect1>
diff --git a/src/basic/user-util.c b/src/basic/user-util.c
index 011b29ad02..db18ee31c0 100644
--- a/src/basic/user-util.c
+++ b/src/basic/user-util.c
@@ -645,6 +645,8 @@ bool valid_gecos(const char *d) {
}
bool valid_home(const char *p) {
+ /* Note that this function is also called by valid_shell(), any
+ * changes must account for that. */
if (isempty(p))
return false;
diff --git a/src/basic/user-util.h b/src/basic/user-util.h
index 290d17a0c6..e1259a1582 100644
--- a/src/basic/user-util.h
+++ b/src/basic/user-util.h
@@ -98,6 +98,15 @@ bool valid_user_group_name_or_id(const char *u);
bool valid_gecos(const char *d);
bool valid_home(const char *p);
+static inline bool valid_shell(const char *p) {
+ /* We have the same requirements, so just piggy-back on the home check.
+ *
+ * Let's ignore /etc/shells because this is only applicable to real and
+ * not system users. It is also incompatible with the idea of empty /etc.
+ */
+ return valid_home(p);
+}
+
int maybe_setgroups(size_t size, const gid_t *list);
bool synthesize_nobody(void);
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index 340e0db798..e2a3b9968c 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -59,6 +59,7 @@ typedef struct Item {
char *gid_path;
char *description;
char *home;
+ char *shell;
gid_t gid;
uid_t uid;
@@ -384,6 +385,10 @@ static int rename_and_apply_smack(const char *temp_path, const char *dest_path)
return r;
}
+static const char* default_shell(uid_t uid) {
+ return uid == 0 ? "/bin/sh" : "/sbin/nologin";
+}
+
static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char **tmpfile_path) {
_cleanup_fclose_ FILE *original = NULL, *passwd = NULL;
_cleanup_(unlink_and_freep) char *passwd_tmp = NULL;
@@ -451,7 +456,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->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
+ .pw_shell = i->shell ?: (char*) default_shell(i->uid),
};
errno = 0;
@@ -1225,6 +1230,7 @@ static void item_free(Item *i) {
free(i->gid_path);
free(i->description);
free(i->home);
+ free(i->shell);
free(i);
}
@@ -1332,6 +1338,9 @@ 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;
+
return true;
}
@@ -1345,7 +1354,12 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
{}
};
- _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL, *resolved_id = NULL, *description = NULL, *home = NULL;
+ _cleanup_free_ char *action = NULL,
+ *name = NULL, *resolved_name = NULL,
+ *id = NULL, *resolved_id = NULL,
+ *description = NULL,
+ *home = NULL,
+ *shell, *resolved_shell = NULL;
_cleanup_(item_freep) Item *i = NULL;
Item *existing;
OrderedHashmap *h;
@@ -1358,7 +1372,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
/* Parse columns */
p = buffer;
- r = extract_many_words(&p, NULL, EXTRACT_QUOTES, &action, &name, &id, &description, &home, NULL);
+ r = extract_many_words(&p, NULL, EXTRACT_QUOTES,
+ &action, &name, &id, &description, &home, &shell, NULL);
if (r < 0) {
log_error("[%s:%u] Syntax error.", fname, line);
return r;
@@ -1434,6 +1449,24 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
}
}
+ /* Verify shell */
+ if (isempty(shell) || streq(shell, "-"))
+ shell = mfree(shell);
+
+ if (shell) {
+ r = specifier_printf(shell, specifier_table, NULL, &resolved_shell);
+ if (r < 0) {
+ log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, shell);
+ return r;
+ }
+
+ if (!valid_shell(resolved_shell)) {
+ log_error("[%s:%u] '%s' is not a valid login shell field.", fname, line, resolved_shell);
+ return -EINVAL;
+ }
+ }
+
+
switch (action[0]) {
case ADD_RANGE:
@@ -1447,13 +1480,10 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
return -EINVAL;
}
- if (description) {
- log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname, line);
- return -EINVAL;
- }
-
- if (home) {
- log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname, line);
+ if (description || home || shell) {
+ log_error("[%s:%u] Lines of type '%c' don't take a %s field.",
+ fname, line, action[0],
+ description ? "GECOS" : home ? "home directory" : "login shell");
return -EINVAL;
}
@@ -1484,13 +1514,10 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
return -EINVAL;
}
- if (description) {
- log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
- return -EINVAL;
- }
-
- if (home) {
- log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname, line);
+ if (description || home || shell) {
+ log_error("[%s:%u] Lines of type '%c' don't take a %s field.",
+ fname, line, action[0],
+ description ? "GECOS" : home ? "home directory" : "login shell");
return -EINVAL;
}
@@ -1574,6 +1601,9 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
i->home = home;
home = NULL;
+ i->shell = resolved_shell;
+ resolved_shell = NULL;
+
h = users;
break;
@@ -1583,13 +1613,10 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
return -EINVAL;
}
- if (description) {
- log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
- return -EINVAL;
- }
-
- if (home) {
- log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname, line);
+ if (description || home || shell) {
+ log_error("[%s:%u] Lines of type '%c' don't take a %s field.",
+ fname, line, action[0],
+ description ? "GECOS" : home ? "home directory" : "login shell");
return -EINVAL;
}