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
{
73 static char *arg_root
= NULL
;
75 static const char conf_file_dirs
[] = CONF_PATHS_NULSTR("sysusers.d");
77 static Hashmap
*users
= NULL
, *groups
= NULL
;
78 static Hashmap
*todo_uids
= NULL
, *todo_gids
= NULL
;
79 static Hashmap
*members
= NULL
;
81 static Hashmap
*database_uid
= NULL
, *database_user
= NULL
;
82 static Hashmap
*database_gid
= NULL
, *database_group
= NULL
;
84 static uid_t search_uid
= UID_INVALID
;
85 static UidRange
*uid_range
= NULL
;
86 static unsigned n_uid_range
= 0;
88 static int load_user_database(void) {
89 _cleanup_fclose_
FILE *f
= NULL
;
90 const char *passwd_path
;
94 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
95 f
= fopen(passwd_path
, "re");
97 return errno
== ENOENT
? 0 : -errno
;
99 r
= hashmap_ensure_allocated(&database_user
, &string_hash_ops
);
103 r
= hashmap_ensure_allocated(&database_uid
, NULL
);
108 while ((pw
= fgetpwent(f
))) {
112 n
= strdup(pw
->pw_name
);
116 k
= hashmap_put(database_user
, n
, UID_TO_PTR(pw
->pw_uid
));
117 if (k
< 0 && k
!= -EEXIST
) {
122 q
= hashmap_put(database_uid
, UID_TO_PTR(pw
->pw_uid
), n
);
123 if (q
< 0 && q
!= -EEXIST
) {
134 if (!IN_SET(errno
, 0, ENOENT
))
140 static int load_group_database(void) {
141 _cleanup_fclose_
FILE *f
= NULL
;
142 const char *group_path
;
146 group_path
= prefix_roota(arg_root
, "/etc/group");
147 f
= fopen(group_path
, "re");
149 return errno
== ENOENT
? 0 : -errno
;
151 r
= hashmap_ensure_allocated(&database_group
, &string_hash_ops
);
155 r
= hashmap_ensure_allocated(&database_gid
, NULL
);
160 while ((gr
= fgetgrent(f
))) {
164 n
= strdup(gr
->gr_name
);
168 k
= hashmap_put(database_group
, n
, GID_TO_PTR(gr
->gr_gid
));
169 if (k
< 0 && k
!= -EEXIST
) {
174 q
= hashmap_put(database_gid
, GID_TO_PTR(gr
->gr_gid
), n
);
175 if (q
< 0 && q
!= -EEXIST
) {
186 if (!IN_SET(errno
, 0, ENOENT
))
192 static int make_backup(const char *target
, const char *x
) {
193 _cleanup_close_
int src
= -1;
194 _cleanup_fclose_
FILE *dst
= NULL
;
195 _cleanup_free_
char *temp
= NULL
;
197 struct timespec ts
[2];
201 src
= open(x
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
203 if (errno
== ENOENT
) /* No backup necessary... */
209 if (fstat(src
, &st
) < 0)
212 r
= fopen_temporary_label(target
, x
, &dst
, &temp
);
216 r
= copy_bytes(src
, fileno(dst
), (uint64_t) -1, COPY_REFLINK
);
220 /* Don't fail on chmod() or chown(). If it stays owned by us
221 * and/or unreadable by others, then it isn't too bad... */
223 backup
= strjoina(x
, "-");
225 /* Copy over the access mask */
226 if (fchmod(fileno(dst
), st
.st_mode
& 07777) < 0)
227 log_warning_errno(errno
, "Failed to change mode on %s: %m", backup
);
229 if (fchown(fileno(dst
), st
.st_uid
, st
.st_gid
)< 0)
230 log_warning_errno(errno
, "Failed to change ownership of %s: %m", backup
);
234 if (futimens(fileno(dst
), ts
) < 0)
235 log_warning_errno(errno
, "Failed to fix access and modification time of %s: %m", backup
);
237 r
= fflush_sync_and_check(dst
);
241 if (rename(temp
, backup
) < 0) {
253 static int putgrent_with_members(const struct group
*gr
, FILE *group
) {
259 a
= hashmap_get(members
, gr
->gr_name
);
261 _cleanup_strv_free_
char **l
= NULL
;
265 l
= strv_copy(gr
->gr_mem
);
270 if (strv_find(l
, *i
))
273 if (strv_extend(&l
, *i
) < 0)
289 if (putgrent(&t
, group
) != 0)
290 return errno
> 0 ? -errno
: -EIO
;
297 if (putgrent(gr
, group
) != 0)
298 return errno
> 0 ? -errno
: -EIO
;
304 static int putsgent_with_members(const struct sgrp
*sg
, FILE *gshadow
) {
310 a
= hashmap_get(members
, sg
->sg_namp
);
312 _cleanup_strv_free_
char **l
= NULL
;
316 l
= strv_copy(sg
->sg_mem
);
321 if (strv_find(l
, *i
))
324 if (strv_extend(&l
, *i
) < 0)
340 if (putsgent(&t
, gshadow
) != 0)
341 return errno
> 0 ? -errno
: -EIO
;
348 if (putsgent(sg
, gshadow
) != 0)
349 return errno
> 0 ? -errno
: -EIO
;
355 static int sync_rights(FILE *from
, FILE *to
) {
358 if (fstat(fileno(from
), &st
) < 0)
361 if (fchmod(fileno(to
), st
.st_mode
& 07777) < 0)
364 if (fchown(fileno(to
), st
.st_uid
, st
.st_gid
) < 0)
370 static int rename_and_apply_smack(const char *temp_path
, const char *dest_path
) {
372 if (rename(temp_path
, dest_path
) < 0)
375 #ifdef SMACK_RUN_LABEL
376 r
= mac_smack_apply(dest_path
, SMACK_ATTR_ACCESS
, SMACK_FLOOR_LABEL
);
383 static int write_temporary_passwd(const char *passwd_path
, FILE **tmpfile
, char **tmpfile_path
) {
384 _cleanup_fclose_
FILE *original
= NULL
, *passwd
= NULL
;
385 _cleanup_(unlink_and_freep
) char *passwd_tmp
= NULL
;
390 if (hashmap_size(todo_uids
) == 0)
393 r
= fopen_temporary_label("/etc/passwd", passwd_path
, &passwd
, &passwd_tmp
);
397 original
= fopen(passwd_path
, "re");
401 r
= sync_rights(original
, passwd
);
406 while ((pw
= fgetpwent(original
))) {
408 i
= hashmap_get(users
, pw
->pw_name
);
409 if (i
&& i
->todo_user
) {
410 log_error("%s: User \"%s\" already exists.", passwd_path
, pw
->pw_name
);
414 if (hashmap_contains(todo_uids
, UID_TO_PTR(pw
->pw_uid
))) {
415 log_error("%s: Detected collision for UID " UID_FMT
".", passwd_path
, pw
->pw_uid
);
420 if (putpwent(pw
, passwd
) < 0)
421 return errno
? -errno
: -EIO
;
425 if (!IN_SET(errno
, 0, ENOENT
))
431 if (fchmod(fileno(passwd
), 0644) < 0)
435 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
440 .pw_gecos
= i
->description
,
442 /* "x" means the password is stored in the shadow file */
443 .pw_passwd
= (char*) "x",
445 /* We default to the root directory as home */
446 .pw_dir
= i
->home
? i
->home
: (char*) "/",
448 /* Initialize the shell to nologin, with one exception:
449 * for root we patch in something special */
450 .pw_shell
= i
->uid
== 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
454 if (putpwent(&n
, passwd
) != 0)
455 return errno
? -errno
: -EIO
;
458 r
= fflush_and_check(passwd
);
463 *tmpfile_path
= passwd_tmp
;
469 static int write_temporary_shadow(const char *shadow_path
, FILE **tmpfile
, char **tmpfile_path
) {
470 _cleanup_fclose_
FILE *original
= NULL
, *shadow
= NULL
;
471 _cleanup_(unlink_and_freep
) char *shadow_tmp
= NULL
;
477 if (hashmap_size(todo_uids
) == 0)
480 r
= fopen_temporary_label("/etc/shadow", shadow_path
, &shadow
, &shadow_tmp
);
484 lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
486 original
= fopen(shadow_path
, "re");
490 r
= sync_rights(original
, shadow
);
495 while ((sp
= fgetspent(original
))) {
497 i
= hashmap_get(users
, sp
->sp_namp
);
498 if (i
&& i
->todo_user
) {
499 /* we will update the existing entry */
500 sp
->sp_lstchg
= lstchg
;
502 /* only the /etc/shadow stage is left, so we can
503 * safely remove the item from the todo set */
504 i
->todo_user
= false;
505 hashmap_remove(todo_uids
, UID_TO_PTR(i
->uid
));
509 if (putspent(sp
, shadow
) < 0)
510 return errno
? -errno
: -EIO
;
514 if (!IN_SET(errno
, 0, ENOENT
))
520 if (fchmod(fileno(shadow
), 0000) < 0)
524 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
527 .sp_pwdp
= (char*) "!!",
534 .sp_flag
= (unsigned long) -1, /* this appears to be what everybody does ... */
538 if (putspent(&n
, shadow
) != 0)
539 return errno
? -errno
: -EIO
;
542 r
= fflush_sync_and_check(shadow
);
547 *tmpfile_path
= shadow_tmp
;
553 static int write_temporary_group(const char *group_path
, FILE **tmpfile
, char **tmpfile_path
) {
554 _cleanup_fclose_
FILE *original
= NULL
, *group
= NULL
;
555 _cleanup_(unlink_and_freep
) char *group_tmp
= NULL
;
556 bool group_changed
= false;
561 if (hashmap_size(todo_gids
) == 0 && hashmap_size(members
) == 0)
564 r
= fopen_temporary_label("/etc/group", group_path
, &group
, &group_tmp
);
568 original
= fopen(group_path
, "re");
572 r
= sync_rights(original
, group
);
577 while ((gr
= fgetgrent(original
))) {
578 /* Safety checks against name and GID collisions. Normally,
579 * this should be unnecessary, but given that we look at the
580 * entries anyway here, let's make an extra verification
581 * step that we don't generate duplicate entries. */
583 i
= hashmap_get(groups
, gr
->gr_name
);
584 if (i
&& i
->todo_group
) {
585 log_error("%s: Group \"%s\" already exists.", group_path
, gr
->gr_name
);
589 if (hashmap_contains(todo_gids
, GID_TO_PTR(gr
->gr_gid
))) {
590 log_error("%s: Detected collision for GID " GID_FMT
".", group_path
, gr
->gr_gid
);
594 r
= putgrent_with_members(gr
, group
);
598 group_changed
= true;
602 if (!IN_SET(errno
, 0, ENOENT
))
608 if (fchmod(fileno(group
), 0644) < 0)
612 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
616 .gr_passwd
= (char*) "x",
619 r
= putgrent_with_members(&n
, group
);
623 group_changed
= true;
626 r
= fflush_sync_and_check(group
);
632 *tmpfile_path
= group_tmp
;
639 static int write_temporary_gshadow(const char * gshadow_path
, FILE **tmpfile
, char **tmpfile_path
) {
641 _cleanup_fclose_
FILE *original
= NULL
, *gshadow
= NULL
;
642 _cleanup_(unlink_and_freep
) char *gshadow_tmp
= NULL
;
643 bool group_changed
= false;
648 if (hashmap_size(todo_gids
) == 0 && hashmap_size(members
) == 0)
651 r
= fopen_temporary_label("/etc/gshadow", gshadow_path
, &gshadow
, &gshadow_tmp
);
655 original
= fopen(gshadow_path
, "re");
659 r
= sync_rights(original
, gshadow
);
664 while ((sg
= fgetsgent(original
))) {
666 i
= hashmap_get(groups
, sg
->sg_namp
);
667 if (i
&& i
->todo_group
) {
668 log_error("%s: Group \"%s\" already exists.", gshadow_path
, sg
->sg_namp
);
672 r
= putsgent_with_members(sg
, gshadow
);
676 group_changed
= true;
680 if (!IN_SET(errno
, 0, ENOENT
))
686 if (fchmod(fileno(gshadow
), 0000) < 0)
690 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
693 .sg_passwd
= (char*) "!!",
696 r
= putsgent_with_members(&n
, gshadow
);
700 group_changed
= true;
703 r
= fflush_sync_and_check(gshadow
);
709 *tmpfile_path
= gshadow_tmp
;
719 static int write_files(void) {
720 _cleanup_fclose_
FILE *passwd
= NULL
, *group
= NULL
, *shadow
= NULL
, *gshadow
= NULL
;
721 _cleanup_(unlink_and_freep
) char *passwd_tmp
= NULL
, *group_tmp
= NULL
, *shadow_tmp
= NULL
, *gshadow_tmp
= NULL
;
722 const char *passwd_path
= NULL
, *group_path
= NULL
, *shadow_path
= NULL
, *gshadow_path
= NULL
;
725 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
726 shadow_path
= prefix_roota(arg_root
, "/etc/shadow");
727 group_path
= prefix_roota(arg_root
, "/etc/group");
728 gshadow_path
= prefix_roota(arg_root
, "/etc/gshadow");
730 r
= write_temporary_group(group_path
, &group
, &group_tmp
);
734 r
= write_temporary_gshadow(gshadow_path
, &gshadow
, &gshadow_tmp
);
738 r
= write_temporary_passwd(passwd_path
, &passwd
, &passwd_tmp
);
742 r
= write_temporary_shadow(shadow_path
, &shadow
, &shadow_tmp
);
746 /* Make a backup of the old files */
748 r
= make_backup("/etc/group", group_path
);
753 r
= make_backup("/etc/gshadow", gshadow_path
);
759 r
= make_backup("/etc/passwd", passwd_path
);
764 r
= make_backup("/etc/shadow", shadow_path
);
769 /* And make the new files count */
771 r
= rename_and_apply_smack(group_tmp
, group_path
);
775 group_tmp
= mfree(group_tmp
);
778 r
= rename_and_apply_smack(gshadow_tmp
, gshadow_path
);
782 gshadow_tmp
= mfree(gshadow_tmp
);
786 r
= rename_and_apply_smack(passwd_tmp
, passwd_path
);
790 passwd_tmp
= mfree(passwd_tmp
);
793 r
= rename_and_apply_smack(shadow_tmp
, shadow_path
);
797 shadow_tmp
= mfree(shadow_tmp
);
803 static int uid_is_ok(uid_t uid
, const char *name
) {
809 /* Let's see if we already have assigned the UID a second time */
810 if (hashmap_get(todo_uids
, UID_TO_PTR(uid
)))
813 /* Try to avoid using uids that are already used by a group
814 * that doesn't have the same name as our new user. */
815 i
= hashmap_get(todo_gids
, GID_TO_PTR(uid
));
816 if (i
&& !streq(i
->name
, name
))
819 /* Let's check the files directly */
820 if (hashmap_contains(database_uid
, UID_TO_PTR(uid
)))
823 n
= hashmap_get(database_gid
, GID_TO_PTR(uid
));
824 if (n
&& !streq(n
, name
))
827 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
833 if (!IN_SET(errno
, 0, ENOENT
))
837 g
= getgrgid((gid_t
) uid
);
839 if (!streq(g
->gr_name
, name
))
841 } else if (!IN_SET(errno
, 0, ENOENT
))
848 static int root_stat(const char *p
, struct stat
*st
) {
851 fix
= prefix_roota(arg_root
, p
);
852 if (stat(fix
, st
) < 0)
858 static int read_id_from_file(Item
*i
, uid_t
*_uid
, gid_t
*_gid
) {
860 bool found_uid
= false, found_gid
= false;
866 /* First, try to get the gid directly */
867 if (_gid
&& i
->gid_path
&& root_stat(i
->gid_path
, &st
) >= 0) {
872 /* Then, try to get the uid directly */
873 if ((_uid
|| (_gid
&& !found_gid
))
875 && root_stat(i
->uid_path
, &st
) >= 0) {
880 /* If we need the gid, but had no success yet, also derive it from the uid path */
881 if (_gid
&& !found_gid
) {
887 /* If that didn't work yet, then let's reuse the gid as uid */
888 if (_uid
&& !found_uid
&& i
->gid_path
) {
893 } else if (root_stat(i
->gid_path
, &st
) >= 0) {
894 uid
= (uid_t
) st
.st_gid
;
916 static int add_user(Item
*i
) {
922 /* Check the database directly */
923 z
= hashmap_get(database_user
, i
->name
);
925 log_debug("User %s already exists.", i
->name
);
926 i
->uid
= PTR_TO_UID(z
);
936 p
= getpwnam(i
->name
);
938 log_debug("User %s already exists.", i
->name
);
942 r
= free_and_strdup(&i
->description
, p
->pw_gecos
);
948 if (!IN_SET(errno
, 0, ENOENT
))
949 return log_error_errno(errno
, "Failed to check if user %s already exists: %m", i
->name
);
952 /* Try to use the suggested numeric uid */
954 r
= uid_is_ok(i
->uid
, i
->name
);
956 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
958 log_debug("Suggested user ID " UID_FMT
" for %s already used.", i
->uid
, i
->name
);
963 /* If that didn't work, try to read it from the specified path */
967 if (read_id_from_file(i
, &c
, NULL
) > 0) {
969 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
970 log_debug("User ID " UID_FMT
" of file not suitable for %s.", c
, i
->name
);
972 r
= uid_is_ok(c
, i
->name
);
974 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
979 log_debug("User ID " UID_FMT
" of file for %s is already used.", c
, i
->name
);
984 /* Otherwise, try to reuse the group ID */
985 if (!i
->uid_set
&& i
->gid_set
) {
986 r
= uid_is_ok((uid_t
) i
->gid
, i
->name
);
988 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
990 i
->uid
= (uid_t
) i
->gid
;
995 /* And if that didn't work either, let's try to find a free one */
998 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1000 log_error("No free user ID available for %s.", i
->name
);
1004 r
= uid_is_ok(search_uid
, i
->name
);
1006 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
1012 i
->uid
= search_uid
;
1015 r
= hashmap_ensure_allocated(&todo_uids
, NULL
);
1019 r
= hashmap_put(todo_uids
, UID_TO_PTR(i
->uid
), i
);
1023 i
->todo_user
= true;
1024 log_info("Creating user %s (%s) with uid " UID_FMT
" and gid " GID_FMT
".", i
->name
, strna(i
->description
), i
->uid
, i
->gid
);
1029 static int gid_is_ok(gid_t gid
) {
1033 if (hashmap_get(todo_gids
, GID_TO_PTR(gid
)))
1036 /* Avoid reusing gids that are already used by a different user */
1037 if (hashmap_get(todo_uids
, UID_TO_PTR(gid
)))
1040 if (hashmap_contains(database_gid
, GID_TO_PTR(gid
)))
1043 if (hashmap_contains(database_uid
, UID_TO_PTR(gid
)))
1051 if (!IN_SET(errno
, 0, ENOENT
))
1055 p
= getpwuid((uid_t
) gid
);
1058 if (!IN_SET(errno
, 0, ENOENT
))
1065 static int add_group(Item
*i
) {
1071 /* Check the database directly */
1072 z
= hashmap_get(database_group
, i
->name
);
1074 log_debug("Group %s already exists.", i
->name
);
1075 i
->gid
= PTR_TO_GID(z
);
1080 /* Also check NSS */
1085 g
= getgrnam(i
->name
);
1087 log_debug("Group %s already exists.", i
->name
);
1092 if (!IN_SET(errno
, 0, ENOENT
))
1093 return log_error_errno(errno
, "Failed to check if group %s already exists: %m", i
->name
);
1096 /* Try to use the suggested numeric gid */
1098 r
= gid_is_ok(i
->gid
);
1100 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1102 log_debug("Suggested group ID " GID_FMT
" for %s already used.", i
->gid
, i
->name
);
1107 /* Try to reuse the numeric uid, if there's one */
1108 if (!i
->gid_set
&& i
->uid_set
) {
1109 r
= gid_is_ok((gid_t
) i
->uid
);
1111 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1113 i
->gid
= (gid_t
) i
->uid
;
1118 /* If that didn't work, try to read it from the specified path */
1122 if (read_id_from_file(i
, NULL
, &c
) > 0) {
1124 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1125 log_debug("Group ID " GID_FMT
" of file not suitable for %s.", c
, i
->name
);
1129 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1134 log_debug("Group ID " GID_FMT
" of file for %s already used.", c
, i
->name
);
1139 /* And if that didn't work either, let's try to find a free one */
1142 /* We look for new GIDs in the UID pool! */
1143 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1145 log_error("No free group ID available for %s.", i
->name
);
1149 r
= gid_is_ok(search_uid
);
1151 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1157 i
->gid
= search_uid
;
1160 r
= hashmap_ensure_allocated(&todo_gids
, NULL
);
1164 r
= hashmap_put(todo_gids
, GID_TO_PTR(i
->gid
), i
);
1168 i
->todo_group
= true;
1169 log_info("Creating group %s with gid " GID_FMT
".", i
->name
, i
->gid
);
1174 static int process_item(Item
*i
) {
1191 j
= hashmap_get(users
, i
->name
);
1193 /* There's already user to be created for this
1194 * name, let's process that in one step */
1202 r
= free_and_strdup(&j
->gid_path
, i
->gid_path
);
1210 return add_group(i
);
1214 assert_not_reached("Unknown item type");
1218 static void item_free(Item
*i
) {
1226 free(i
->description
);
1231 DEFINE_TRIVIAL_CLEANUP_FUNC(Item
*, item_free
);
1233 static int add_implicit(void) {
1238 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1240 HASHMAP_FOREACH_KEY(l
, g
, members
, iterator
) {
1244 i
= hashmap_get(groups
, g
);
1246 _cleanup_(item_freep
) Item
*j
= NULL
;
1248 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1256 j
->type
= ADD_GROUP
;
1257 j
->name
= strdup(g
);
1261 r
= hashmap_put(groups
, j
->name
, j
);
1265 log_debug("Adding implicit group '%s' due to m line", j
->name
);
1269 STRV_FOREACH(m
, l
) {
1271 i
= hashmap_get(users
, *m
);
1273 _cleanup_(item_freep
) Item
*j
= NULL
;
1275 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1284 j
->name
= strdup(*m
);
1288 r
= hashmap_put(users
, j
->name
, j
);
1292 log_debug("Adding implicit user '%s' due to m line", j
->name
);
1301 static bool item_equal(Item
*a
, Item
*b
) {
1305 if (a
->type
!= b
->type
)
1308 if (!streq_ptr(a
->name
, b
->name
))
1311 if (!streq_ptr(a
->uid_path
, b
->uid_path
))
1314 if (!streq_ptr(a
->gid_path
, b
->gid_path
))
1317 if (!streq_ptr(a
->description
, b
->description
))
1320 if (a
->uid_set
!= b
->uid_set
)
1323 if (a
->uid_set
&& a
->uid
!= b
->uid
)
1326 if (a
->gid_set
!= b
->gid_set
)
1329 if (a
->gid_set
&& a
->gid
!= b
->gid
)
1332 if (!streq_ptr(a
->home
, b
->home
))
1338 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1340 static const Specifier specifier_table
[] = {
1341 { 'm', specifier_machine_id
, NULL
},
1342 { 'b', specifier_boot_id
, NULL
},
1343 { 'H', specifier_host_name
, NULL
},
1344 { 'v', specifier_kernel_release
, NULL
},
1348 _cleanup_free_
char *action
= NULL
, *name
= NULL
, *id
= NULL
, *resolved_name
= NULL
, *resolved_id
= NULL
, *description
= NULL
, *home
= NULL
;
1349 _cleanup_(item_freep
) Item
*i
= NULL
;
1361 r
= extract_many_words(&p
, NULL
, EXTRACT_QUOTES
, &action
, &name
, &id
, &description
, &home
, NULL
);
1363 log_error("[%s:%u] Syntax error.", fname
, line
);
1367 log_error("[%s:%u] Missing action and name columns.", fname
, line
);
1371 log_error("[%s:%u] Trailing garbage.", fname
, line
);
1376 if (strlen(action
) != 1) {
1377 log_error("[%s:%u] Unknown modifier '%s'", fname
, line
, action
);
1381 if (!IN_SET(action
[0], ADD_USER
, ADD_GROUP
, ADD_MEMBER
, ADD_RANGE
)) {
1382 log_error("[%s:%u] Unknown command type '%c'.", fname
, line
, action
[0]);
1387 if (isempty(name
) || streq(name
, "-"))
1391 r
= specifier_printf(name
, specifier_table
, NULL
, &resolved_name
);
1393 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1397 if (!valid_user_group_name(resolved_name
)) {
1398 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_name
);
1404 if (isempty(id
) || streq(id
, "-"))
1408 r
= specifier_printf(id
, specifier_table
, NULL
, &resolved_id
);
1410 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1415 /* Verify description */
1416 if (isempty(description
) || streq(description
, "-"))
1417 description
= mfree(description
);
1420 if (!valid_gecos(description
)) {
1421 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname
, line
, description
);
1427 if (isempty(home
) || streq(home
, "-"))
1431 if (!valid_home(home
)) {
1432 log_error("[%s:%u] '%s' is not a valid home directory field.", fname
, line
, home
);
1437 switch (action
[0]) {
1440 if (resolved_name
) {
1441 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname
, line
);
1446 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname
, line
);
1451 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname
, line
);
1456 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname
, line
);
1460 r
= uid_range_add_str(&uid_range
, &n_uid_range
, resolved_id
);
1462 log_error("[%s:%u] Invalid UID range %s.", fname
, line
, resolved_id
);
1471 /* Try to extend an existing member or group item */
1473 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname
, line
);
1478 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname
, line
);
1482 if (!valid_user_group_name(resolved_id
)) {
1483 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_id
);
1488 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname
, line
);
1493 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname
, line
);
1497 r
= hashmap_ensure_allocated(&members
, &string_hash_ops
);
1501 l
= hashmap_get(members
, resolved_id
);
1503 /* A list for this group name already exists, let's append to it */
1504 r
= strv_push(&l
, resolved_name
);
1508 resolved_name
= NULL
;
1510 assert_se(hashmap_update(members
, resolved_id
, l
) >= 0);
1512 /* No list for this group name exists yet, create one */
1514 l
= new0(char *, 2);
1518 l
[0] = resolved_name
;
1521 r
= hashmap_put(members
, resolved_id
, l
);
1527 resolved_id
= resolved_name
= NULL
;
1535 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname
, line
);
1539 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1548 if (path_is_absolute(resolved_id
)) {
1549 i
->uid_path
= resolved_id
;
1552 path_kill_slashes(i
->uid_path
);
1554 r
= parse_uid(resolved_id
, &i
->uid
);
1556 log_error("Failed to parse UID: %s", id
);
1564 i
->description
= description
;
1575 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname
, line
);
1580 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname
, line
);
1585 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname
, line
);
1589 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1598 if (path_is_absolute(resolved_id
)) {
1599 i
->gid_path
= resolved_id
;
1602 path_kill_slashes(i
->gid_path
);
1604 r
= parse_gid(resolved_id
, &i
->gid
);
1606 log_error("Failed to parse GID: %s", id
);
1621 i
->type
= action
[0];
1622 i
->name
= resolved_name
;
1623 resolved_name
= NULL
;
1625 existing
= hashmap_get(h
, i
->name
);
1628 /* Two identical items are fine */
1629 if (!item_equal(existing
, i
))
1630 log_warning("Two or more conflicting lines for %s configured, ignoring.", i
->name
);
1635 r
= hashmap_put(h
, i
->name
, i
);
1643 static int read_config_file(const char *fn
, bool ignore_enoent
) {
1644 _cleanup_fclose_
FILE *rf
= NULL
;
1646 char line
[LINE_MAX
];
1655 r
= search_and_fopen_nulstr(fn
, "re", arg_root
, conf_file_dirs
, &rf
);
1657 if (ignore_enoent
&& r
== -ENOENT
)
1660 return log_error_errno(r
, "Failed to open '%s', ignoring: %m", fn
);
1666 FOREACH_LINE(line
, f
, break) {
1673 if (IN_SET(*l
, 0, '#'))
1676 k
= parse_line(fn
, v
, l
);
1677 if (k
< 0 && r
== 0)
1682 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
1690 static void free_database(Hashmap
*by_name
, Hashmap
*by_id
) {
1694 name
= hashmap_first(by_id
);
1698 hashmap_remove(by_name
, name
);
1700 hashmap_steal_first_key(by_id
);
1704 while ((name
= hashmap_steal_first_key(by_name
)))
1707 hashmap_free(by_name
);
1708 hashmap_free(by_id
);
1711 static void help(void) {
1712 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1713 "Creates system user accounts.\n\n"
1714 " -h --help Show this help\n"
1715 " --version Show package version\n"
1716 " --root=PATH Operate on an alternate filesystem root\n"
1717 , program_invocation_short_name
);
1720 static int parse_argv(int argc
, char *argv
[]) {
1723 ARG_VERSION
= 0x100,
1727 static const struct option options
[] = {
1728 { "help", no_argument
, NULL
, 'h' },
1729 { "version", no_argument
, NULL
, ARG_VERSION
},
1730 { "root", required_argument
, NULL
, ARG_ROOT
},
1739 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
1751 r
= parse_path_argument_and_warn(optarg
, true, &arg_root
);
1760 assert_not_reached("Unhandled option");
1766 int main(int argc
, char *argv
[]) {
1768 _cleanup_close_
int lock
= -1;
1774 r
= parse_argv(argc
, argv
);
1778 log_set_target(LOG_TARGET_AUTO
);
1779 log_parse_environment();
1784 r
= mac_selinux_init();
1786 log_error_errno(r
, "SELinux setup failed: %m");
1790 if (optind
< argc
) {
1793 for (j
= optind
; j
< argc
; j
++) {
1794 k
= read_config_file(argv
[j
], false);
1795 if (k
< 0 && r
== 0)
1799 _cleanup_strv_free_
char **files
= NULL
;
1802 r
= conf_files_list_nulstr(&files
, ".conf", arg_root
, 0, conf_file_dirs
);
1804 log_error_errno(r
, "Failed to enumerate sysusers.d files: %m");
1808 STRV_FOREACH(f
, files
) {
1809 k
= read_config_file(*f
, true);
1810 if (k
< 0 && r
== 0)
1815 /* Let's tell nss-systemd not to synthesize the "root" and "nobody" entries for it, so that our detection
1816 * whether the names or UID/GID area already used otherwise doesn't get confused. After all, even though
1817 * nss-systemd synthesizes these users/groups, they should still appear in /etc/passwd and /etc/group, as the
1818 * synthesizing logic is merely supposed to be fallback for cases where we run with a completely unpopulated
1820 if (setenv("SYSTEMD_NSS_BYPASS_SYNTHETIC", "1", 1) < 0) {
1821 r
= log_error_errno(errno
, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m");
1826 /* Default to default range of 1..SYSTEMD_UID_MAX */
1827 r
= uid_range_add(&uid_range
, &n_uid_range
, 1, SYSTEM_UID_MAX
);
1838 lock
= take_etc_passwd_lock(arg_root
);
1840 log_error_errno(lock
, "Failed to take lock: %m");
1844 r
= load_user_database();
1846 log_error_errno(r
, "Failed to load user database: %m");
1850 r
= load_group_database();
1852 log_error_errno(r
, "Failed to read group database: %m");
1856 HASHMAP_FOREACH(i
, groups
, iterator
)
1859 HASHMAP_FOREACH(i
, users
, iterator
)
1864 log_error_errno(r
, "Failed to write files: %m");
1867 while ((i
= hashmap_steal_first(groups
)))
1870 while ((i
= hashmap_steal_first(users
)))
1873 while ((n
= hashmap_first_key(members
))) {
1874 strv_free(hashmap_steal_first(members
));
1878 hashmap_free(groups
);
1879 hashmap_free(users
);
1880 hashmap_free(members
);
1881 hashmap_free(todo_uids
);
1882 hashmap_free(todo_gids
);
1884 free_database(database_user
, database_uid
);
1885 free_database(database_group
, database_gid
);
1889 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;