1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2014 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include "alloc-util.h"
29 #include "conf-files.h"
34 #include "fileio-label.h"
35 #include "format-util.h"
37 #include "path-util.h"
38 #include "selinux-util.h"
39 #include "smack-util.h"
40 #include "specifier.h"
41 #include "string-util.h"
43 #include "uid-range.h"
44 #include "user-util.h"
48 typedef enum ItemType
{
68 // id_set_strict means that the group with the specified gid must
69 // exist and that the check if a uid clashes with a gid is skipped
77 static char *arg_root
= NULL
;
78 static bool arg_inline
= false;
80 static const char conf_file_dirs
[] = CONF_PATHS_NULSTR("sysusers.d");
82 static OrderedHashmap
*users
= NULL
, *groups
= NULL
;
83 static OrderedHashmap
*todo_uids
= NULL
, *todo_gids
= NULL
;
84 static OrderedHashmap
*members
= NULL
;
86 static Hashmap
*database_uid
= NULL
, *database_user
= NULL
;
87 static Hashmap
*database_gid
= NULL
, *database_group
= NULL
;
89 static uid_t search_uid
= UID_INVALID
;
90 static UidRange
*uid_range
= NULL
;
91 static unsigned n_uid_range
= 0;
93 static int load_user_database(void) {
94 _cleanup_fclose_
FILE *f
= NULL
;
95 const char *passwd_path
;
99 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
100 f
= fopen(passwd_path
, "re");
102 return errno
== ENOENT
? 0 : -errno
;
104 r
= hashmap_ensure_allocated(&database_user
, &string_hash_ops
);
108 r
= hashmap_ensure_allocated(&database_uid
, NULL
);
113 while ((pw
= fgetpwent(f
))) {
117 n
= strdup(pw
->pw_name
);
121 k
= hashmap_put(database_user
, n
, UID_TO_PTR(pw
->pw_uid
));
122 if (k
< 0 && k
!= -EEXIST
) {
127 q
= hashmap_put(database_uid
, UID_TO_PTR(pw
->pw_uid
), n
);
128 if (q
< 0 && q
!= -EEXIST
) {
139 if (!IN_SET(errno
, 0, ENOENT
))
145 static int load_group_database(void) {
146 _cleanup_fclose_
FILE *f
= NULL
;
147 const char *group_path
;
151 group_path
= prefix_roota(arg_root
, "/etc/group");
152 f
= fopen(group_path
, "re");
154 return errno
== ENOENT
? 0 : -errno
;
156 r
= hashmap_ensure_allocated(&database_group
, &string_hash_ops
);
160 r
= hashmap_ensure_allocated(&database_gid
, NULL
);
165 while ((gr
= fgetgrent(f
))) {
169 n
= strdup(gr
->gr_name
);
173 k
= hashmap_put(database_group
, n
, GID_TO_PTR(gr
->gr_gid
));
174 if (k
< 0 && k
!= -EEXIST
) {
179 q
= hashmap_put(database_gid
, GID_TO_PTR(gr
->gr_gid
), n
);
180 if (q
< 0 && q
!= -EEXIST
) {
191 if (!IN_SET(errno
, 0, ENOENT
))
197 static int make_backup(const char *target
, const char *x
) {
198 _cleanup_close_
int src
= -1;
199 _cleanup_fclose_
FILE *dst
= NULL
;
200 _cleanup_free_
char *temp
= NULL
;
202 struct timespec ts
[2];
206 src
= open(x
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
208 if (errno
== ENOENT
) /* No backup necessary... */
214 if (fstat(src
, &st
) < 0)
217 r
= fopen_temporary_label(target
, x
, &dst
, &temp
);
221 r
= copy_bytes(src
, fileno(dst
), (uint64_t) -1, COPY_REFLINK
);
225 /* Don't fail on chmod() or chown(). If it stays owned by us
226 * and/or unreadable by others, then it isn't too bad... */
228 backup
= strjoina(x
, "-");
230 /* Copy over the access mask */
231 if (fchmod(fileno(dst
), st
.st_mode
& 07777) < 0)
232 log_warning_errno(errno
, "Failed to change mode on %s: %m", backup
);
234 if (fchown(fileno(dst
), st
.st_uid
, st
.st_gid
)< 0)
235 log_warning_errno(errno
, "Failed to change ownership of %s: %m", backup
);
239 if (futimens(fileno(dst
), ts
) < 0)
240 log_warning_errno(errno
, "Failed to fix access and modification time of %s: %m", backup
);
242 r
= fflush_sync_and_check(dst
);
246 if (rename(temp
, backup
) < 0) {
258 static int putgrent_with_members(const struct group
*gr
, FILE *group
) {
264 a
= ordered_hashmap_get(members
, gr
->gr_name
);
266 _cleanup_strv_free_
char **l
= NULL
;
270 l
= strv_copy(gr
->gr_mem
);
275 if (strv_find(l
, *i
))
278 if (strv_extend(&l
, *i
) < 0)
294 if (putgrent(&t
, group
) != 0)
295 return errno
> 0 ? -errno
: -EIO
;
302 if (putgrent(gr
, group
) != 0)
303 return errno
> 0 ? -errno
: -EIO
;
309 static int putsgent_with_members(const struct sgrp
*sg
, FILE *gshadow
) {
315 a
= ordered_hashmap_get(members
, sg
->sg_namp
);
317 _cleanup_strv_free_
char **l
= NULL
;
321 l
= strv_copy(sg
->sg_mem
);
326 if (strv_find(l
, *i
))
329 if (strv_extend(&l
, *i
) < 0)
345 if (putsgent(&t
, gshadow
) != 0)
346 return errno
> 0 ? -errno
: -EIO
;
353 if (putsgent(sg
, gshadow
) != 0)
354 return errno
> 0 ? -errno
: -EIO
;
360 static int sync_rights(FILE *from
, FILE *to
) {
363 if (fstat(fileno(from
), &st
) < 0)
366 if (fchmod(fileno(to
), st
.st_mode
& 07777) < 0)
369 if (fchown(fileno(to
), st
.st_uid
, st
.st_gid
) < 0)
375 static int rename_and_apply_smack(const char *temp_path
, const char *dest_path
) {
377 if (rename(temp_path
, dest_path
) < 0)
380 #ifdef SMACK_RUN_LABEL
381 r
= mac_smack_apply(dest_path
, SMACK_ATTR_ACCESS
, SMACK_FLOOR_LABEL
);
388 static const char* default_shell(uid_t uid
) {
389 return uid
== 0 ? "/bin/sh" : "/sbin/nologin";
392 static int write_temporary_passwd(const char *passwd_path
, FILE **tmpfile
, char **tmpfile_path
) {
393 _cleanup_fclose_
FILE *original
= NULL
, *passwd
= NULL
;
394 _cleanup_(unlink_and_freep
) char *passwd_tmp
= NULL
;
399 if (ordered_hashmap_size(todo_uids
) == 0)
402 r
= fopen_temporary_label("/etc/passwd", passwd_path
, &passwd
, &passwd_tmp
);
406 original
= fopen(passwd_path
, "re");
410 r
= sync_rights(original
, passwd
);
415 while ((pw
= fgetpwent(original
))) {
417 i
= ordered_hashmap_get(users
, pw
->pw_name
);
418 if (i
&& i
->todo_user
) {
419 log_error("%s: User \"%s\" already exists.", passwd_path
, pw
->pw_name
);
423 if (ordered_hashmap_contains(todo_uids
, UID_TO_PTR(pw
->pw_uid
))) {
424 log_error("%s: Detected collision for UID " UID_FMT
".", passwd_path
, pw
->pw_uid
);
429 if (putpwent(pw
, passwd
) < 0)
430 return errno
? -errno
: -EIO
;
434 if (!IN_SET(errno
, 0, ENOENT
))
440 if (fchmod(fileno(passwd
), 0644) < 0)
444 ORDERED_HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
449 .pw_gecos
= i
->description
,
451 /* "x" means the password is stored in the shadow file */
452 .pw_passwd
= (char*) "x",
454 /* We default to the root directory as home */
455 .pw_dir
= i
->home
? i
->home
: (char*) "/",
457 /* Initialize the shell to nologin, with one exception:
458 * for root we patch in something special */
459 .pw_shell
= i
->shell
?: (char*) default_shell(i
->uid
),
463 if (putpwent(&n
, passwd
) != 0)
464 return errno
? -errno
: -EIO
;
467 r
= fflush_and_check(passwd
);
472 *tmpfile_path
= passwd_tmp
;
478 static int write_temporary_shadow(const char *shadow_path
, FILE **tmpfile
, char **tmpfile_path
) {
479 _cleanup_fclose_
FILE *original
= NULL
, *shadow
= NULL
;
480 _cleanup_(unlink_and_freep
) char *shadow_tmp
= NULL
;
486 if (ordered_hashmap_size(todo_uids
) == 0)
489 r
= fopen_temporary_label("/etc/shadow", shadow_path
, &shadow
, &shadow_tmp
);
493 lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
495 original
= fopen(shadow_path
, "re");
499 r
= sync_rights(original
, shadow
);
504 while ((sp
= fgetspent(original
))) {
506 i
= ordered_hashmap_get(users
, sp
->sp_namp
);
507 if (i
&& i
->todo_user
) {
508 /* we will update the existing entry */
509 sp
->sp_lstchg
= lstchg
;
511 /* only the /etc/shadow stage is left, so we can
512 * safely remove the item from the todo set */
513 i
->todo_user
= false;
514 ordered_hashmap_remove(todo_uids
, UID_TO_PTR(i
->uid
));
518 if (putspent(sp
, shadow
) < 0)
519 return errno
? -errno
: -EIO
;
523 if (!IN_SET(errno
, 0, ENOENT
))
529 if (fchmod(fileno(shadow
), 0000) < 0)
533 ORDERED_HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
536 .sp_pwdp
= (char*) "!!",
543 .sp_flag
= (unsigned long) -1, /* this appears to be what everybody does ... */
547 if (putspent(&n
, shadow
) != 0)
548 return errno
? -errno
: -EIO
;
551 r
= fflush_sync_and_check(shadow
);
556 *tmpfile_path
= shadow_tmp
;
562 static int write_temporary_group(const char *group_path
, FILE **tmpfile
, char **tmpfile_path
) {
563 _cleanup_fclose_
FILE *original
= NULL
, *group
= NULL
;
564 _cleanup_(unlink_and_freep
) char *group_tmp
= NULL
;
565 bool group_changed
= false;
570 if (ordered_hashmap_size(todo_gids
) == 0 && ordered_hashmap_size(members
) == 0)
573 r
= fopen_temporary_label("/etc/group", group_path
, &group
, &group_tmp
);
577 original
= fopen(group_path
, "re");
581 r
= sync_rights(original
, group
);
586 while ((gr
= fgetgrent(original
))) {
587 /* Safety checks against name and GID collisions. Normally,
588 * this should be unnecessary, but given that we look at the
589 * entries anyway here, let's make an extra verification
590 * step that we don't generate duplicate entries. */
592 i
= ordered_hashmap_get(groups
, gr
->gr_name
);
593 if (i
&& i
->todo_group
) {
594 log_error("%s: Group \"%s\" already exists.", group_path
, gr
->gr_name
);
598 if (ordered_hashmap_contains(todo_gids
, GID_TO_PTR(gr
->gr_gid
))) {
599 log_error("%s: Detected collision for GID " GID_FMT
".", group_path
, gr
->gr_gid
);
603 r
= putgrent_with_members(gr
, group
);
607 group_changed
= true;
611 if (!IN_SET(errno
, 0, ENOENT
))
617 if (fchmod(fileno(group
), 0644) < 0)
621 ORDERED_HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
625 .gr_passwd
= (char*) "x",
628 r
= putgrent_with_members(&n
, group
);
632 group_changed
= true;
635 r
= fflush_sync_and_check(group
);
641 *tmpfile_path
= group_tmp
;
648 static int write_temporary_gshadow(const char * gshadow_path
, FILE **tmpfile
, char **tmpfile_path
) {
650 _cleanup_fclose_
FILE *original
= NULL
, *gshadow
= NULL
;
651 _cleanup_(unlink_and_freep
) char *gshadow_tmp
= NULL
;
652 bool group_changed
= false;
657 if (ordered_hashmap_size(todo_gids
) == 0 && ordered_hashmap_size(members
) == 0)
660 r
= fopen_temporary_label("/etc/gshadow", gshadow_path
, &gshadow
, &gshadow_tmp
);
664 original
= fopen(gshadow_path
, "re");
668 r
= sync_rights(original
, gshadow
);
673 while ((sg
= fgetsgent(original
))) {
675 i
= ordered_hashmap_get(groups
, sg
->sg_namp
);
676 if (i
&& i
->todo_group
) {
677 log_error("%s: Group \"%s\" already exists.", gshadow_path
, sg
->sg_namp
);
681 r
= putsgent_with_members(sg
, gshadow
);
685 group_changed
= true;
689 if (!IN_SET(errno
, 0, ENOENT
))
695 if (fchmod(fileno(gshadow
), 0000) < 0)
699 ORDERED_HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
702 .sg_passwd
= (char*) "!!",
705 r
= putsgent_with_members(&n
, gshadow
);
709 group_changed
= true;
712 r
= fflush_sync_and_check(gshadow
);
718 *tmpfile_path
= gshadow_tmp
;
728 static int write_files(void) {
729 _cleanup_fclose_
FILE *passwd
= NULL
, *group
= NULL
, *shadow
= NULL
, *gshadow
= NULL
;
730 _cleanup_(unlink_and_freep
) char *passwd_tmp
= NULL
, *group_tmp
= NULL
, *shadow_tmp
= NULL
, *gshadow_tmp
= NULL
;
731 const char *passwd_path
= NULL
, *group_path
= NULL
, *shadow_path
= NULL
, *gshadow_path
= NULL
;
734 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
735 shadow_path
= prefix_roota(arg_root
, "/etc/shadow");
736 group_path
= prefix_roota(arg_root
, "/etc/group");
737 gshadow_path
= prefix_roota(arg_root
, "/etc/gshadow");
739 r
= write_temporary_group(group_path
, &group
, &group_tmp
);
743 r
= write_temporary_gshadow(gshadow_path
, &gshadow
, &gshadow_tmp
);
747 r
= write_temporary_passwd(passwd_path
, &passwd
, &passwd_tmp
);
751 r
= write_temporary_shadow(shadow_path
, &shadow
, &shadow_tmp
);
755 /* Make a backup of the old files */
757 r
= make_backup("/etc/group", group_path
);
762 r
= make_backup("/etc/gshadow", gshadow_path
);
768 r
= make_backup("/etc/passwd", passwd_path
);
773 r
= make_backup("/etc/shadow", shadow_path
);
778 /* And make the new files count */
780 r
= rename_and_apply_smack(group_tmp
, group_path
);
784 group_tmp
= mfree(group_tmp
);
787 r
= rename_and_apply_smack(gshadow_tmp
, gshadow_path
);
791 gshadow_tmp
= mfree(gshadow_tmp
);
795 r
= rename_and_apply_smack(passwd_tmp
, passwd_path
);
799 passwd_tmp
= mfree(passwd_tmp
);
802 r
= rename_and_apply_smack(shadow_tmp
, shadow_path
);
806 shadow_tmp
= mfree(shadow_tmp
);
812 static int uid_is_ok(uid_t uid
, const char *name
, bool check_with_gid
) {
818 /* Let's see if we already have assigned the UID a second time */
819 if (ordered_hashmap_get(todo_uids
, UID_TO_PTR(uid
)))
822 /* Try to avoid using uids that are already used by a group
823 * that doesn't have the same name as our new user. */
824 if (check_with_gid
) {
825 i
= ordered_hashmap_get(todo_gids
, GID_TO_PTR(uid
));
826 if (i
&& !streq(i
->name
, name
))
830 /* Let's check the files directly */
831 if (hashmap_contains(database_uid
, UID_TO_PTR(uid
)))
834 if (check_with_gid
) {
835 n
= hashmap_get(database_gid
, GID_TO_PTR(uid
));
836 if (n
&& !streq(n
, name
))
840 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
846 if (!IN_SET(errno
, 0, ENOENT
))
849 if (check_with_gid
) {
851 g
= getgrgid((gid_t
) uid
);
853 if (!streq(g
->gr_name
, name
))
855 } else if (!IN_SET(errno
, 0, ENOENT
))
863 static int root_stat(const char *p
, struct stat
*st
) {
866 fix
= prefix_roota(arg_root
, p
);
867 if (stat(fix
, st
) < 0)
873 static int read_id_from_file(Item
*i
, uid_t
*_uid
, gid_t
*_gid
) {
875 bool found_uid
= false, found_gid
= false;
881 /* First, try to get the gid directly */
882 if (_gid
&& i
->gid_path
&& root_stat(i
->gid_path
, &st
) >= 0) {
887 /* Then, try to get the uid directly */
888 if ((_uid
|| (_gid
&& !found_gid
))
890 && root_stat(i
->uid_path
, &st
) >= 0) {
895 /* If we need the gid, but had no success yet, also derive it from the uid path */
896 if (_gid
&& !found_gid
) {
902 /* If that didn't work yet, then let's reuse the gid as uid */
903 if (_uid
&& !found_uid
&& i
->gid_path
) {
908 } else if (root_stat(i
->gid_path
, &st
) >= 0) {
909 uid
= (uid_t
) st
.st_gid
;
931 static int add_user(Item
*i
) {
937 /* Check the database directly */
938 z
= hashmap_get(database_user
, i
->name
);
940 log_debug("User %s already exists.", i
->name
);
941 i
->uid
= PTR_TO_UID(z
);
951 p
= getpwnam(i
->name
);
953 log_debug("User %s already exists.", i
->name
);
957 r
= free_and_strdup(&i
->description
, p
->pw_gecos
);
963 if (!IN_SET(errno
, 0, ENOENT
))
964 return log_error_errno(errno
, "Failed to check if user %s already exists: %m", i
->name
);
967 /* Try to use the suggested numeric uid */
969 r
= uid_is_ok(i
->uid
, i
->name
, !i
->id_set_strict
);
971 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
973 log_debug("Suggested user ID " UID_FMT
" for %s already used.", i
->uid
, i
->name
);
978 /* If that didn't work, try to read it from the specified path */
982 if (read_id_from_file(i
, &c
, NULL
) > 0) {
984 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
985 log_debug("User ID " UID_FMT
" of file not suitable for %s.", c
, i
->name
);
987 r
= uid_is_ok(c
, i
->name
, true);
989 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
994 log_debug("User ID " UID_FMT
" of file for %s is already used.", c
, i
->name
);
999 /* Otherwise, try to reuse the group ID */
1000 if (!i
->uid_set
&& i
->gid_set
) {
1001 r
= uid_is_ok((uid_t
) i
->gid
, i
->name
, true);
1003 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
1005 i
->uid
= (uid_t
) i
->gid
;
1010 /* And if that didn't work either, let's try to find a free one */
1013 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1015 log_error("No free user ID available for %s.", i
->name
);
1019 r
= uid_is_ok(search_uid
, i
->name
, true);
1021 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
1027 i
->uid
= search_uid
;
1030 r
= ordered_hashmap_ensure_allocated(&todo_uids
, NULL
);
1034 r
= ordered_hashmap_put(todo_uids
, UID_TO_PTR(i
->uid
), i
);
1038 i
->todo_user
= true;
1039 log_info("Creating user %s (%s) with uid " UID_FMT
" and gid " GID_FMT
".", i
->name
, strna(i
->description
), i
->uid
, i
->gid
);
1044 static int gid_is_ok(gid_t gid
) {
1048 if (ordered_hashmap_get(todo_gids
, GID_TO_PTR(gid
)))
1051 /* Avoid reusing gids that are already used by a different user */
1052 if (ordered_hashmap_get(todo_uids
, UID_TO_PTR(gid
)))
1055 if (hashmap_contains(database_gid
, GID_TO_PTR(gid
)))
1058 if (hashmap_contains(database_uid
, UID_TO_PTR(gid
)))
1066 if (!IN_SET(errno
, 0, ENOENT
))
1070 p
= getpwuid((uid_t
) gid
);
1073 if (!IN_SET(errno
, 0, ENOENT
))
1080 static int add_group(Item
*i
) {
1086 /* Check the database directly */
1087 z
= hashmap_get(database_group
, i
->name
);
1089 log_debug("Group %s already exists.", i
->name
);
1090 i
->gid
= PTR_TO_GID(z
);
1095 /* Also check NSS */
1100 g
= getgrnam(i
->name
);
1102 log_debug("Group %s already exists.", i
->name
);
1107 if (!IN_SET(errno
, 0, ENOENT
))
1108 return log_error_errno(errno
, "Failed to check if group %s already exists: %m", i
->name
);
1111 /* Try to use the suggested numeric gid */
1113 r
= gid_is_ok(i
->gid
);
1115 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1116 if (i
->id_set_strict
) {
1117 /* If we require the gid to already exist we can return here:
1118 * r > 0: means the gid does not exist -> fail
1119 * r == 0: means the gid exists -> nothing more to do.
1122 log_error("Failed to create %s: please create GID %d", i
->name
, i
->gid
);
1129 log_debug("Suggested group ID " GID_FMT
" for %s already used.", i
->gid
, i
->name
);
1134 /* Try to reuse the numeric uid, if there's one */
1135 if (!i
->gid_set
&& i
->uid_set
) {
1136 r
= gid_is_ok((gid_t
) i
->uid
);
1138 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1140 i
->gid
= (gid_t
) i
->uid
;
1145 /* If that didn't work, try to read it from the specified path */
1149 if (read_id_from_file(i
, NULL
, &c
) > 0) {
1151 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1152 log_debug("Group ID " GID_FMT
" of file not suitable for %s.", c
, i
->name
);
1156 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1161 log_debug("Group ID " GID_FMT
" of file for %s already used.", c
, i
->name
);
1166 /* And if that didn't work either, let's try to find a free one */
1169 /* We look for new GIDs in the UID pool! */
1170 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1172 log_error("No free group ID available for %s.", i
->name
);
1176 r
= gid_is_ok(search_uid
);
1178 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1184 i
->gid
= search_uid
;
1187 r
= ordered_hashmap_ensure_allocated(&todo_gids
, NULL
);
1191 r
= ordered_hashmap_put(todo_gids
, GID_TO_PTR(i
->gid
), i
);
1195 i
->todo_group
= true;
1196 log_info("Creating group %s with gid " GID_FMT
".", i
->name
, i
->gid
);
1201 static int process_item(Item
*i
) {
1216 return add_group(i
);
1219 assert_not_reached("Unknown item type");
1223 static void item_free(Item
*i
) {
1231 free(i
->description
);
1237 DEFINE_TRIVIAL_CLEANUP_FUNC(Item
*, item_free
);
1239 static int add_implicit(void) {
1244 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1246 ORDERED_HASHMAP_FOREACH_KEY(l
, g
, members
, iterator
) {
1250 i
= ordered_hashmap_get(groups
, g
);
1252 _cleanup_(item_freep
) Item
*j
= NULL
;
1254 r
= ordered_hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1262 j
->type
= ADD_GROUP
;
1263 j
->name
= strdup(g
);
1267 r
= ordered_hashmap_put(groups
, j
->name
, j
);
1271 log_debug("Adding implicit group '%s' due to m line", j
->name
);
1275 STRV_FOREACH(m
, l
) {
1277 i
= ordered_hashmap_get(users
, *m
);
1279 _cleanup_(item_freep
) Item
*j
= NULL
;
1281 r
= ordered_hashmap_ensure_allocated(&users
, &string_hash_ops
);
1290 j
->name
= strdup(*m
);
1294 r
= ordered_hashmap_put(users
, j
->name
, j
);
1298 log_debug("Adding implicit user '%s' due to m line", j
->name
);
1307 static bool item_equal(Item
*a
, Item
*b
) {
1311 if (a
->type
!= b
->type
)
1314 if (!streq_ptr(a
->name
, b
->name
))
1317 if (!streq_ptr(a
->uid_path
, b
->uid_path
))
1320 if (!streq_ptr(a
->gid_path
, b
->gid_path
))
1323 if (!streq_ptr(a
->description
, b
->description
))
1326 if (a
->uid_set
!= b
->uid_set
)
1329 if (a
->uid_set
&& a
->uid
!= b
->uid
)
1332 if (a
->gid_set
!= b
->gid_set
)
1335 if (a
->gid_set
&& a
->gid
!= b
->gid
)
1338 if (!streq_ptr(a
->home
, b
->home
))
1341 if (!streq_ptr(a
->shell
, b
->shell
))
1347 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1349 static const Specifier specifier_table
[] = {
1350 { 'm', specifier_machine_id
, NULL
},
1351 { 'b', specifier_boot_id
, NULL
},
1352 { 'H', specifier_host_name
, NULL
},
1353 { 'v', specifier_kernel_release
, NULL
},
1357 _cleanup_free_
char *action
= NULL
,
1358 *name
= NULL
, *resolved_name
= NULL
,
1359 *id
= NULL
, *resolved_id
= NULL
,
1360 *description
= NULL
,
1362 *shell
, *resolved_shell
= NULL
;
1363 _cleanup_(item_freep
) Item
*i
= NULL
;
1375 r
= extract_many_words(&p
, NULL
, EXTRACT_QUOTES
,
1376 &action
, &name
, &id
, &description
, &home
, &shell
, NULL
);
1378 log_error("[%s:%u] Syntax error.", fname
, line
);
1382 log_error("[%s:%u] Missing action and name columns.", fname
, line
);
1386 log_error("[%s:%u] Trailing garbage.", fname
, line
);
1391 if (strlen(action
) != 1) {
1392 log_error("[%s:%u] Unknown modifier '%s'", fname
, line
, action
);
1396 if (!IN_SET(action
[0], ADD_USER
, ADD_GROUP
, ADD_MEMBER
, ADD_RANGE
)) {
1397 log_error("[%s:%u] Unknown command type '%c'.", fname
, line
, action
[0]);
1402 if (isempty(name
) || streq(name
, "-"))
1406 r
= specifier_printf(name
, specifier_table
, NULL
, &resolved_name
);
1408 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1412 if (!valid_user_group_name(resolved_name
)) {
1413 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_name
);
1419 if (isempty(id
) || streq(id
, "-"))
1423 r
= specifier_printf(id
, specifier_table
, NULL
, &resolved_id
);
1425 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1430 /* Verify description */
1431 if (isempty(description
) || streq(description
, "-"))
1432 description
= mfree(description
);
1435 if (!valid_gecos(description
)) {
1436 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname
, line
, description
);
1442 if (isempty(home
) || streq(home
, "-"))
1446 if (!valid_home(home
)) {
1447 log_error("[%s:%u] '%s' is not a valid home directory field.", fname
, line
, home
);
1453 if (isempty(shell
) || streq(shell
, "-"))
1454 shell
= mfree(shell
);
1457 r
= specifier_printf(shell
, specifier_table
, NULL
, &resolved_shell
);
1459 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, shell
);
1463 if (!valid_shell(resolved_shell
)) {
1464 log_error("[%s:%u] '%s' is not a valid login shell field.", fname
, line
, resolved_shell
);
1470 switch (action
[0]) {
1473 if (resolved_name
) {
1474 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname
, line
);
1479 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname
, line
);
1483 if (description
|| home
|| shell
) {
1484 log_error("[%s:%u] Lines of type '%c' don't take a %s field.",
1485 fname
, line
, action
[0],
1486 description
? "GECOS" : home
? "home directory" : "login shell");
1490 r
= uid_range_add_str(&uid_range
, &n_uid_range
, resolved_id
);
1492 log_error("[%s:%u] Invalid UID range %s.", fname
, line
, resolved_id
);
1501 /* Try to extend an existing member or group item */
1503 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname
, line
);
1508 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname
, line
);
1512 if (!valid_user_group_name(resolved_id
)) {
1513 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_id
);
1517 if (description
|| home
|| shell
) {
1518 log_error("[%s:%u] Lines of type '%c' don't take a %s field.",
1519 fname
, line
, action
[0],
1520 description
? "GECOS" : home
? "home directory" : "login shell");
1524 r
= ordered_hashmap_ensure_allocated(&members
, &string_hash_ops
);
1528 l
= ordered_hashmap_get(members
, resolved_id
);
1530 /* A list for this group name already exists, let's append to it */
1531 r
= strv_push(&l
, resolved_name
);
1535 resolved_name
= NULL
;
1537 assert_se(ordered_hashmap_update(members
, resolved_id
, l
) >= 0);
1539 /* No list for this group name exists yet, create one */
1541 l
= new0(char *, 2);
1545 l
[0] = resolved_name
;
1548 r
= ordered_hashmap_put(members
, resolved_id
, l
);
1554 resolved_id
= resolved_name
= NULL
;
1562 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname
, line
);
1566 r
= ordered_hashmap_ensure_allocated(&users
, &string_hash_ops
);
1575 if (path_is_absolute(resolved_id
)) {
1576 i
->uid_path
= resolved_id
;
1579 path_kill_slashes(i
->uid_path
);
1581 _cleanup_free_
char *uid
= NULL
, *gid
= NULL
;
1582 if (split_pair(resolved_id
, ":", &uid
, &gid
) == 0) {
1583 r
= parse_gid(gid
, &i
->gid
);
1585 return log_error_errno(r
, "Failed to parse GID: '%s': %m", id
);
1587 i
->id_set_strict
= true;
1588 free_and_replace(resolved_id
, uid
);
1590 r
= parse_uid(resolved_id
, &i
->uid
);
1592 return log_error_errno(r
, "Failed to parse UID: '%s': %m", id
);
1598 i
->description
= description
;
1604 i
->shell
= resolved_shell
;
1605 resolved_shell
= NULL
;
1612 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname
, line
);
1616 if (description
|| home
|| shell
) {
1617 log_error("[%s:%u] Lines of type '%c' don't take a %s field.",
1618 fname
, line
, action
[0],
1619 description
? "GECOS" : home
? "home directory" : "login shell");
1623 r
= ordered_hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1632 if (path_is_absolute(resolved_id
)) {
1633 i
->gid_path
= resolved_id
;
1636 path_kill_slashes(i
->gid_path
);
1638 r
= parse_gid(resolved_id
, &i
->gid
);
1640 return log_error_errno(r
, "Failed to parse GID: '%s': %m", id
);
1653 i
->type
= action
[0];
1654 i
->name
= resolved_name
;
1655 resolved_name
= NULL
;
1657 existing
= ordered_hashmap_get(h
, i
->name
);
1660 /* Two identical items are fine */
1661 if (!item_equal(existing
, i
))
1662 log_warning("Two or more conflicting lines for %s configured, ignoring.", i
->name
);
1667 r
= ordered_hashmap_put(h
, i
->name
, i
);
1675 static int read_config_file(const char *fn
, bool ignore_enoent
) {
1676 _cleanup_fclose_
FILE *rf
= NULL
;
1678 char line
[LINE_MAX
];
1687 r
= search_and_fopen_nulstr(fn
, "re", arg_root
, conf_file_dirs
, &rf
);
1689 if (ignore_enoent
&& r
== -ENOENT
)
1692 return log_error_errno(r
, "Failed to open '%s', ignoring: %m", fn
);
1698 FOREACH_LINE(line
, f
, break) {
1705 if (IN_SET(*l
, 0, '#'))
1708 k
= parse_line(fn
, v
, l
);
1709 if (k
< 0 && r
== 0)
1714 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
1722 static void free_database(Hashmap
*by_name
, Hashmap
*by_id
) {
1726 name
= hashmap_first(by_id
);
1730 hashmap_remove(by_name
, name
);
1732 hashmap_steal_first_key(by_id
);
1736 while ((name
= hashmap_steal_first_key(by_name
)))
1739 hashmap_free(by_name
);
1740 hashmap_free(by_id
);
1743 static void help(void) {
1744 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1745 "Creates system user accounts.\n\n"
1746 " -h --help Show this help\n"
1747 " --version Show package version\n"
1748 " --root=PATH Operate on an alternate filesystem root\n"
1749 " --inline Treat arguments as configuration lines\n"
1750 , program_invocation_short_name
);
1753 static int parse_argv(int argc
, char *argv
[]) {
1756 ARG_VERSION
= 0x100,
1761 static const struct option options
[] = {
1762 { "help", no_argument
, NULL
, 'h' },
1763 { "version", no_argument
, NULL
, ARG_VERSION
},
1764 { "root", required_argument
, NULL
, ARG_ROOT
},
1765 { "inline", no_argument
, NULL
, ARG_INLINE
},
1774 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
1786 r
= parse_path_argument_and_warn(optarg
, true, &arg_root
);
1799 assert_not_reached("Unhandled option");
1805 int main(int argc
, char *argv
[]) {
1807 _cleanup_close_
int lock
= -1;
1813 r
= parse_argv(argc
, argv
);
1817 log_set_target(LOG_TARGET_AUTO
);
1818 log_parse_environment();
1823 r
= mac_selinux_init();
1825 log_error_errno(r
, "SELinux setup failed: %m");
1829 if (optind
< argc
) {
1832 for (j
= optind
; j
< argc
; j
++) {
1834 /* Use (argument):n, where n==1 for the first positional arg */
1835 r
= parse_line("(argument)", j
- optind
+ 1, argv
[j
]);
1837 r
= read_config_file(argv
[j
], false);
1842 _cleanup_strv_free_
char **files
= NULL
;
1845 r
= conf_files_list_nulstr(&files
, ".conf", arg_root
, 0, conf_file_dirs
);
1847 log_error_errno(r
, "Failed to enumerate sysusers.d files: %m");
1851 STRV_FOREACH(f
, files
) {
1852 k
= read_config_file(*f
, true);
1853 if (k
< 0 && r
== 0)
1858 /* Let's tell nss-systemd not to synthesize the "root" and "nobody" entries for it, so that our detection
1859 * whether the names or UID/GID area already used otherwise doesn't get confused. After all, even though
1860 * nss-systemd synthesizes these users/groups, they should still appear in /etc/passwd and /etc/group, as the
1861 * synthesizing logic is merely supposed to be fallback for cases where we run with a completely unpopulated
1863 if (setenv("SYSTEMD_NSS_BYPASS_SYNTHETIC", "1", 1) < 0) {
1864 r
= log_error_errno(errno
, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m");
1869 /* Default to default range of 1..SYSTEM_UID_MAX */
1870 r
= uid_range_add(&uid_range
, &n_uid_range
, 1, SYSTEM_UID_MAX
);
1881 lock
= take_etc_passwd_lock(arg_root
);
1883 log_error_errno(lock
, "Failed to take /etc/passwd lock: %m");
1887 r
= load_user_database();
1889 log_error_errno(r
, "Failed to load user database: %m");
1893 r
= load_group_database();
1895 log_error_errno(r
, "Failed to read group database: %m");
1899 ORDERED_HASHMAP_FOREACH(i
, groups
, iterator
)
1902 ORDERED_HASHMAP_FOREACH(i
, users
, iterator
)
1907 log_error_errno(r
, "Failed to write files: %m");
1910 ordered_hashmap_free_with_destructor(groups
, item_free
);
1911 ordered_hashmap_free_with_destructor(users
, item_free
);
1913 while ((n
= ordered_hashmap_first_key(members
))) {
1914 strv_free(ordered_hashmap_steal_first(members
));
1917 ordered_hashmap_free(members
);
1919 ordered_hashmap_free(todo_uids
);
1920 ordered_hashmap_free(todo_gids
);
1922 free_database(database_user
, database_uid
);
1923 free_database(database_group
, database_gid
);
1927 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;