1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
31 #include "specifier.h"
32 #include "path-util.h"
35 #include "conf-files.h"
38 #include "fileio-label.h"
39 #include "uid-range.h"
40 #include "selinux-util.h"
41 #include "formats-util.h"
43 typedef enum ItemType
{
68 static char *arg_root
= NULL
;
70 static const char conf_file_dirs
[] = CONF_DIRS_NULSTR("sysusers");
72 static Hashmap
*users
= NULL
, *groups
= NULL
;
73 static Hashmap
*todo_uids
= NULL
, *todo_gids
= NULL
;
74 static Hashmap
*members
= NULL
;
76 static Hashmap
*database_uid
= NULL
, *database_user
= NULL
;
77 static Hashmap
*database_gid
= NULL
, *database_group
= NULL
;
79 static uid_t search_uid
= UID_INVALID
;
80 static UidRange
*uid_range
= NULL
;
81 static unsigned n_uid_range
= 0;
83 static int load_user_database(void) {
84 _cleanup_fclose_
FILE *f
= NULL
;
85 const char *passwd_path
;
89 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
90 f
= fopen(passwd_path
, "re");
92 return errno
== ENOENT
? 0 : -errno
;
94 r
= hashmap_ensure_allocated(&database_user
, &string_hash_ops
);
98 r
= hashmap_ensure_allocated(&database_uid
, NULL
);
103 while ((pw
= fgetpwent(f
))) {
107 n
= strdup(pw
->pw_name
);
111 k
= hashmap_put(database_user
, n
, UID_TO_PTR(pw
->pw_uid
));
112 if (k
< 0 && k
!= -EEXIST
) {
117 q
= hashmap_put(database_uid
, UID_TO_PTR(pw
->pw_uid
), n
);
118 if (q
< 0 && q
!= -EEXIST
) {
129 if (!IN_SET(errno
, 0, ENOENT
))
135 static int load_group_database(void) {
136 _cleanup_fclose_
FILE *f
= NULL
;
137 const char *group_path
;
141 group_path
= prefix_roota(arg_root
, "/etc/group");
142 f
= fopen(group_path
, "re");
144 return errno
== ENOENT
? 0 : -errno
;
146 r
= hashmap_ensure_allocated(&database_group
, &string_hash_ops
);
150 r
= hashmap_ensure_allocated(&database_gid
, NULL
);
155 while ((gr
= fgetgrent(f
))) {
159 n
= strdup(gr
->gr_name
);
163 k
= hashmap_put(database_group
, n
, GID_TO_PTR(gr
->gr_gid
));
164 if (k
< 0 && k
!= -EEXIST
) {
169 q
= hashmap_put(database_gid
, GID_TO_PTR(gr
->gr_gid
), n
);
170 if (q
< 0 && q
!= -EEXIST
) {
181 if (!IN_SET(errno
, 0, ENOENT
))
187 static int make_backup(const char *target
, const char *x
) {
188 _cleanup_close_
int src
= -1;
189 _cleanup_fclose_
FILE *dst
= NULL
;
191 struct timespec ts
[2];
195 src
= open(x
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
197 if (errno
== ENOENT
) /* No backup necessary... */
203 if (fstat(src
, &st
) < 0)
206 r
= fopen_temporary_label(target
, x
, &dst
, &temp
);
210 r
= copy_bytes(src
, fileno(dst
), (off_t
) -1, true);
214 /* Don't fail on chmod() or chown(). If it stays owned by us
215 * and/or unreadable by others, then it isn't too bad... */
217 backup
= strjoina(x
, "-");
219 /* Copy over the access mask */
220 if (fchmod(fileno(dst
), st
.st_mode
& 07777) < 0)
221 log_warning_errno(errno
, "Failed to change mode on %s: %m", backup
);
223 if (fchown(fileno(dst
), st
.st_uid
, st
.st_gid
)< 0)
224 log_warning_errno(errno
, "Failed to change ownership of %s: %m", backup
);
228 if (futimens(fileno(dst
), ts
) < 0)
229 log_warning_errno(errno
, "Failed to fix access and modification time of %s: %m", backup
);
231 if (rename(temp
, backup
) < 0)
241 static int putgrent_with_members(const struct group
*gr
, FILE *group
) {
247 a
= hashmap_get(members
, gr
->gr_name
);
249 _cleanup_strv_free_
char **l
= NULL
;
253 l
= strv_copy(gr
->gr_mem
);
258 if (strv_find(l
, *i
))
261 if (strv_extend(&l
, *i
) < 0)
277 if (putgrent(&t
, group
) != 0)
278 return errno
? -errno
: -EIO
;
285 if (putgrent(gr
, group
) != 0)
286 return errno
? -errno
: -EIO
;
291 static int putsgent_with_members(const struct sgrp
*sg
, FILE *gshadow
) {
297 a
= hashmap_get(members
, sg
->sg_namp
);
299 _cleanup_strv_free_
char **l
= NULL
;
303 l
= strv_copy(sg
->sg_mem
);
308 if (strv_find(l
, *i
))
311 if (strv_extend(&l
, *i
) < 0)
327 if (putsgent(&t
, gshadow
) != 0)
328 return errno
? -errno
: -EIO
;
335 if (putsgent(sg
, gshadow
) != 0)
336 return errno
? -errno
: -EIO
;
341 static int sync_rights(FILE *from
, FILE *to
) {
344 if (fstat(fileno(from
), &st
) < 0)
347 if (fchmod(fileno(to
), st
.st_mode
& 07777) < 0)
350 if (fchown(fileno(to
), st
.st_uid
, st
.st_gid
) < 0)
356 static int write_files(void) {
358 _cleanup_fclose_
FILE *passwd
= NULL
, *group
= NULL
, *shadow
= NULL
, *gshadow
= NULL
;
359 _cleanup_free_
char *passwd_tmp
= NULL
, *group_tmp
= NULL
, *shadow_tmp
= NULL
, *gshadow_tmp
= NULL
;
360 const char *passwd_path
= NULL
, *group_path
= NULL
, *shadow_path
= NULL
, *gshadow_path
= NULL
;
361 bool group_changed
= false;
366 if (hashmap_size(todo_gids
) > 0 || hashmap_size(members
) > 0) {
367 _cleanup_fclose_
FILE *original
= NULL
;
369 /* First we update the actual group list file */
370 group_path
= prefix_roota(arg_root
, "/etc/group");
371 r
= fopen_temporary_label("/etc/group", group_path
, &group
, &group_tmp
);
375 original
= fopen(group_path
, "re");
379 r
= sync_rights(original
, group
);
384 while ((gr
= fgetgrent(original
))) {
385 /* Safety checks against name and GID
386 * collisions. Normally, this should
387 * be unnecessary, but given that we
388 * look at the entries anyway here,
389 * let's make an extra verification
390 * step that we don't generate
391 * duplicate entries. */
393 i
= hashmap_get(groups
, gr
->gr_name
);
394 if (i
&& i
->todo_group
) {
399 if (hashmap_contains(todo_gids
, GID_TO_PTR(gr
->gr_gid
))) {
404 r
= putgrent_with_members(gr
, group
);
408 group_changed
= true;
412 if (!IN_SET(errno
, 0, ENOENT
)) {
417 } else if (errno
!= ENOENT
) {
420 } else if (fchmod(fileno(group
), 0644) < 0) {
425 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
429 .gr_passwd
= (char*) "x",
432 r
= putgrent_with_members(&n
, group
);
436 group_changed
= true;
439 r
= fflush_and_check(group
);
448 /* OK, now also update the shadow file for the group list */
449 gshadow_path
= prefix_roota(arg_root
, "/etc/gshadow");
450 r
= fopen_temporary_label("/etc/gshadow", gshadow_path
, &gshadow
, &gshadow_tmp
);
454 original
= fopen(gshadow_path
, "re");
458 r
= sync_rights(original
, gshadow
);
463 while ((sg
= fgetsgent(original
))) {
465 i
= hashmap_get(groups
, sg
->sg_namp
);
466 if (i
&& i
->todo_group
) {
471 r
= putsgent_with_members(sg
, gshadow
);
475 group_changed
= true;
479 if (!IN_SET(errno
, 0, ENOENT
)) {
484 } else if (errno
!= ENOENT
) {
487 } else if (fchmod(fileno(gshadow
), 0000) < 0) {
492 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
495 .sg_passwd
= (char*) "!!",
498 r
= putsgent_with_members(&n
, gshadow
);
502 group_changed
= true;
505 r
= fflush_and_check(gshadow
);
510 if (hashmap_size(todo_uids
) > 0) {
511 _cleanup_fclose_
FILE *original
= NULL
;
514 /* First we update the user database itself */
515 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
516 r
= fopen_temporary_label("/etc/passwd", passwd_path
, &passwd
, &passwd_tmp
);
520 original
= fopen(passwd_path
, "re");
524 r
= sync_rights(original
, passwd
);
529 while ((pw
= fgetpwent(original
))) {
531 i
= hashmap_get(users
, pw
->pw_name
);
532 if (i
&& i
->todo_user
) {
537 if (hashmap_contains(todo_uids
, UID_TO_PTR(pw
->pw_uid
))) {
543 if (putpwent(pw
, passwd
) < 0) {
544 r
= errno
? -errno
: -EIO
;
550 if (!IN_SET(errno
, 0, ENOENT
)) {
555 } else if (errno
!= ENOENT
) {
558 } else if (fchmod(fileno(passwd
), 0644) < 0) {
563 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
568 .pw_gecos
= i
->description
,
570 /* "x" means the password is stored in
572 .pw_passwd
= (char*) "x",
574 /* We default to the root directory as home */
575 .pw_dir
= i
->home
? i
->home
: (char*) "/",
577 /* Initialize the shell to nologin,
578 * with one exception: for root we
579 * patch in something special */
580 .pw_shell
= i
->uid
== 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
584 if (putpwent(&n
, passwd
) != 0) {
585 r
= errno
? -errno
: -EIO
;
590 r
= fflush_and_check(passwd
);
599 /* The we update the shadow database */
600 shadow_path
= prefix_roota(arg_root
, "/etc/shadow");
601 r
= fopen_temporary_label("/etc/shadow", shadow_path
, &shadow
, &shadow_tmp
);
605 lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
607 original
= fopen(shadow_path
, "re");
611 r
= sync_rights(original
, shadow
);
616 while ((sp
= fgetspent(original
))) {
618 i
= hashmap_get(users
, sp
->sp_namp
);
619 if (i
&& i
->todo_user
) {
620 /* we will update the existing entry */
621 sp
->sp_lstchg
= lstchg
;
623 /* only the /etc/shadow stage is left, so we can
624 * safely remove the item from the todo set */
625 i
->todo_user
= false;
626 hashmap_remove(todo_uids
, UID_TO_PTR(i
->uid
));
630 if (putspent(sp
, shadow
) < 0) {
631 r
= errno
? -errno
: -EIO
;
637 if (!IN_SET(errno
, 0, ENOENT
)) {
641 } else if (errno
!= ENOENT
) {
644 } else if (fchmod(fileno(shadow
), 0000) < 0) {
649 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
652 .sp_pwdp
= (char*) "!!",
659 .sp_flag
= (unsigned long) -1, /* this appears to be what everybody does ... */
663 if (putspent(&n
, shadow
) != 0) {
664 r
= errno
? -errno
: -EIO
;
669 r
= fflush_and_check(shadow
);
674 /* Make a backup of the old files */
677 r
= make_backup("/etc/group", group_path
);
682 r
= make_backup("/etc/gshadow", gshadow_path
);
689 r
= make_backup("/etc/passwd", passwd_path
);
694 r
= make_backup("/etc/shadow", shadow_path
);
699 /* And make the new files count */
702 if (rename(group_tmp
, group_path
) < 0) {
711 if (rename(gshadow_tmp
, gshadow_path
) < 0) {
722 if (rename(passwd_tmp
, passwd_path
) < 0) {
731 if (rename(shadow_tmp
, shadow_path
) < 0) {
755 static int uid_is_ok(uid_t uid
, const char *name
) {
761 /* Let's see if we already have assigned the UID a second time */
762 if (hashmap_get(todo_uids
, UID_TO_PTR(uid
)))
765 /* Try to avoid using uids that are already used by a group
766 * that doesn't have the same name as our new user. */
767 i
= hashmap_get(todo_gids
, GID_TO_PTR(uid
));
768 if (i
&& !streq(i
->name
, name
))
771 /* Let's check the files directly */
772 if (hashmap_contains(database_uid
, UID_TO_PTR(uid
)))
775 n
= hashmap_get(database_gid
, GID_TO_PTR(uid
));
776 if (n
&& !streq(n
, name
))
779 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
785 if (!IN_SET(errno
, 0, ENOENT
))
789 g
= getgrgid((gid_t
) uid
);
791 if (!streq(g
->gr_name
, name
))
793 } else if (!IN_SET(errno
, 0, ENOENT
))
800 static int root_stat(const char *p
, struct stat
*st
) {
803 fix
= prefix_roota(arg_root
, p
);
804 if (stat(fix
, st
) < 0)
810 static int read_id_from_file(Item
*i
, uid_t
*_uid
, gid_t
*_gid
) {
812 bool found_uid
= false, found_gid
= false;
818 /* First, try to get the gid directly */
819 if (_gid
&& i
->gid_path
&& root_stat(i
->gid_path
, &st
) >= 0) {
824 /* Then, try to get the uid directly */
825 if ((_uid
|| (_gid
&& !found_gid
))
827 && root_stat(i
->uid_path
, &st
) >= 0) {
832 /* If we need the gid, but had no success yet, also derive it from the uid path */
833 if (_gid
&& !found_gid
) {
839 /* If that didn't work yet, then let's reuse the gid as uid */
840 if (_uid
&& !found_uid
&& i
->gid_path
) {
845 } else if (root_stat(i
->gid_path
, &st
) >= 0) {
846 uid
= (uid_t
) st
.st_gid
;
868 static int add_user(Item
*i
) {
874 /* Check the database directly */
875 z
= hashmap_get(database_user
, i
->name
);
877 log_debug("User %s already exists.", i
->name
);
878 i
->uid
= PTR_TO_UID(z
);
888 p
= getpwnam(i
->name
);
890 log_debug("User %s already exists.", i
->name
);
894 free(i
->description
);
895 i
->description
= strdup(p
->pw_gecos
);
898 if (!IN_SET(errno
, 0, ENOENT
))
899 return log_error_errno(errno
, "Failed to check if user %s already exists: %m", i
->name
);
902 /* Try to use the suggested numeric uid */
904 r
= uid_is_ok(i
->uid
, i
->name
);
906 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
908 log_debug("Suggested user ID " UID_FMT
" for %s already used.", i
->uid
, i
->name
);
913 /* If that didn't work, try to read it from the specified path */
917 if (read_id_from_file(i
, &c
, NULL
) > 0) {
919 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
920 log_debug("User ID " UID_FMT
" of file not suitable for %s.", c
, i
->name
);
922 r
= uid_is_ok(c
, i
->name
);
924 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
929 log_debug("User ID " UID_FMT
" of file for %s is already used.", c
, i
->name
);
934 /* Otherwise try to reuse the group ID */
935 if (!i
->uid_set
&& i
->gid_set
) {
936 r
= uid_is_ok((uid_t
) i
->gid
, i
->name
);
938 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
940 i
->uid
= (uid_t
) i
->gid
;
945 /* And if that didn't work either, let's try to find a free one */
948 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
950 log_error("No free user ID available for %s.", i
->name
);
954 r
= uid_is_ok(search_uid
, i
->name
);
956 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
965 r
= hashmap_ensure_allocated(&todo_uids
, NULL
);
969 r
= hashmap_put(todo_uids
, UID_TO_PTR(i
->uid
), i
);
974 log_info("Creating user %s (%s) with uid " UID_FMT
" and gid " GID_FMT
".", i
->name
, strna(i
->description
), i
->uid
, i
->gid
);
979 static int gid_is_ok(gid_t gid
) {
983 if (hashmap_get(todo_gids
, GID_TO_PTR(gid
)))
986 /* Avoid reusing gids that are already used by a different user */
987 if (hashmap_get(todo_uids
, UID_TO_PTR(gid
)))
990 if (hashmap_contains(database_gid
, GID_TO_PTR(gid
)))
993 if (hashmap_contains(database_uid
, UID_TO_PTR(gid
)))
1001 if (!IN_SET(errno
, 0, ENOENT
))
1005 p
= getpwuid((uid_t
) gid
);
1008 if (!IN_SET(errno
, 0, ENOENT
))
1015 static int add_group(Item
*i
) {
1021 /* Check the database directly */
1022 z
= hashmap_get(database_group
, i
->name
);
1024 log_debug("Group %s already exists.", i
->name
);
1025 i
->gid
= PTR_TO_GID(z
);
1030 /* Also check NSS */
1035 g
= getgrnam(i
->name
);
1037 log_debug("Group %s already exists.", i
->name
);
1042 if (!IN_SET(errno
, 0, ENOENT
))
1043 return log_error_errno(errno
, "Failed to check if group %s already exists: %m", i
->name
);
1046 /* Try to use the suggested numeric gid */
1048 r
= gid_is_ok(i
->gid
);
1050 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1052 log_debug("Suggested group ID " GID_FMT
" for %s already used.", i
->gid
, i
->name
);
1057 /* Try to reuse the numeric uid, if there's one */
1058 if (!i
->gid_set
&& i
->uid_set
) {
1059 r
= gid_is_ok((gid_t
) i
->uid
);
1061 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1063 i
->gid
= (gid_t
) i
->uid
;
1068 /* If that didn't work, try to read it from the specified path */
1072 if (read_id_from_file(i
, NULL
, &c
) > 0) {
1074 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1075 log_debug("Group ID " GID_FMT
" of file not suitable for %s.", c
, i
->name
);
1079 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1084 log_debug("Group ID " GID_FMT
" of file for %s already used.", c
, i
->name
);
1089 /* And if that didn't work either, let's try to find a free one */
1092 /* We look for new GIDs in the UID pool! */
1093 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1095 log_error("No free group ID available for %s.", i
->name
);
1099 r
= gid_is_ok(search_uid
);
1101 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1107 i
->gid
= search_uid
;
1110 r
= hashmap_ensure_allocated(&todo_gids
, NULL
);
1114 r
= hashmap_put(todo_gids
, GID_TO_PTR(i
->gid
), i
);
1118 i
->todo_group
= true;
1119 log_info("Creating group %s with gid " GID_FMT
".", i
->name
, i
->gid
);
1124 static int process_item(Item
*i
) {
1141 j
= hashmap_get(users
, i
->name
);
1143 /* There's already user to be created for this
1144 * name, let's process that in one step */
1153 j
->gid_path
= strdup(i
->gid_path
);
1161 return add_group(i
);
1165 assert_not_reached("Unknown item type");
1169 static void item_free(Item
*i
) {
1177 free(i
->description
);
1181 DEFINE_TRIVIAL_CLEANUP_FUNC(Item
*, item_free
);
1183 static int add_implicit(void) {
1188 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1190 HASHMAP_FOREACH_KEY(l
, g
, members
, iterator
) {
1194 i
= hashmap_get(groups
, g
);
1196 _cleanup_(item_freep
) Item
*j
= NULL
;
1198 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1206 j
->type
= ADD_GROUP
;
1207 j
->name
= strdup(g
);
1211 r
= hashmap_put(groups
, j
->name
, j
);
1215 log_debug("Adding implicit group '%s' due to m line", j
->name
);
1219 STRV_FOREACH(m
, l
) {
1221 i
= hashmap_get(users
, *m
);
1223 _cleanup_(item_freep
) Item
*j
= NULL
;
1225 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1234 j
->name
= strdup(*m
);
1238 r
= hashmap_put(users
, j
->name
, j
);
1242 log_debug("Adding implicit user '%s' due to m line", j
->name
);
1251 static bool item_equal(Item
*a
, Item
*b
) {
1255 if (a
->type
!= b
->type
)
1258 if (!streq_ptr(a
->name
, b
->name
))
1261 if (!streq_ptr(a
->uid_path
, b
->uid_path
))
1264 if (!streq_ptr(a
->gid_path
, b
->gid_path
))
1267 if (!streq_ptr(a
->description
, b
->description
))
1270 if (a
->uid_set
!= b
->uid_set
)
1273 if (a
->uid_set
&& a
->uid
!= b
->uid
)
1276 if (a
->gid_set
!= b
->gid_set
)
1279 if (a
->gid_set
&& a
->gid
!= b
->gid
)
1282 if (!streq_ptr(a
->home
, b
->home
))
1288 static bool valid_user_group_name(const char *u
) {
1295 if (!(u
[0] >= 'a' && u
[0] <= 'z') &&
1296 !(u
[0] >= 'A' && u
[0] <= 'Z') &&
1300 for (i
= u
+1; *i
; i
++) {
1301 if (!(*i
>= 'a' && *i
<= 'z') &&
1302 !(*i
>= 'A' && *i
<= 'Z') &&
1303 !(*i
>= '0' && *i
<= '9') &&
1309 sz
= sysconf(_SC_LOGIN_NAME_MAX
);
1312 if ((size_t) (i
-u
) > (size_t) sz
)
1315 if ((size_t) (i
-u
) > UT_NAMESIZE
- 1)
1321 static bool valid_gecos(const char *d
) {
1326 if (!utf8_is_valid(d
))
1329 if (string_has_cc(d
, NULL
))
1332 /* Colons are used as field separators, and hence not OK */
1339 static bool valid_home(const char *p
) {
1344 if (!utf8_is_valid(p
))
1347 if (string_has_cc(p
, NULL
))
1350 if (!path_is_absolute(p
))
1353 if (!path_is_safe(p
))
1356 /* Colons are used as field separators, and hence not OK */
1363 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1365 static const Specifier specifier_table
[] = {
1366 { 'm', specifier_machine_id
, NULL
},
1367 { 'b', specifier_boot_id
, NULL
},
1368 { 'H', specifier_host_name
, NULL
},
1369 { 'v', specifier_kernel_release
, NULL
},
1373 _cleanup_free_
char *action
= NULL
, *name
= NULL
, *id
= NULL
, *resolved_name
= NULL
, *resolved_id
= NULL
, *description
= NULL
, *home
= NULL
;
1374 _cleanup_(item_freep
) Item
*i
= NULL
;
1386 r
= unquote_many_words(&p
, 0, &action
, &name
, &id
, &description
, &home
, NULL
);
1388 log_error("[%s:%u] Syntax error.", fname
, line
);
1392 log_error("[%s:%u] Missing action and name columns.", fname
, line
);
1396 log_error("[%s:%u] Trailing garbage.", fname
, line
);
1401 if (strlen(action
) != 1) {
1402 log_error("[%s:%u] Unknown modifier '%s'", fname
, line
, action
);
1406 if (!IN_SET(action
[0], ADD_USER
, ADD_GROUP
, ADD_MEMBER
, ADD_RANGE
)) {
1407 log_error("[%s:%u] Unknown command command type '%c'.", fname
, line
, action
[0]);
1412 if (isempty(name
) || streq(name
, "-")) {
1418 r
= specifier_printf(name
, specifier_table
, NULL
, &resolved_name
);
1420 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1424 if (!valid_user_group_name(resolved_name
)) {
1425 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_name
);
1431 if (isempty(id
) || streq(id
, "-")) {
1437 r
= specifier_printf(id
, specifier_table
, NULL
, &resolved_id
);
1439 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1444 /* Verify description */
1445 if (isempty(description
) || streq(description
, "-")) {
1451 if (!valid_gecos(description
)) {
1452 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname
, line
, description
);
1458 if (isempty(home
) || streq(home
, "-")) {
1464 if (!valid_home(home
)) {
1465 log_error("[%s:%u] '%s' is not a valid home directory field.", fname
, line
, home
);
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
);
1484 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname
, line
);
1489 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname
, line
);
1493 r
= uid_range_add_str(&uid_range
, &n_uid_range
, resolved_id
);
1495 log_error("[%s:%u] Invalid UID range %s.", fname
, line
, resolved_id
);
1504 /* Try to extend an existing member or group item */
1506 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname
, line
);
1511 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname
, line
);
1515 if (!valid_user_group_name(resolved_id
)) {
1516 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_id
);
1521 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname
, line
);
1526 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname
, line
);
1530 r
= hashmap_ensure_allocated(&members
, &string_hash_ops
);
1534 l
= hashmap_get(members
, resolved_id
);
1536 /* A list for this group name already exists, let's append to it */
1537 r
= strv_push(&l
, resolved_name
);
1541 resolved_name
= NULL
;
1543 assert_se(hashmap_update(members
, resolved_id
, l
) >= 0);
1545 /* No list for this group name exists yet, create one */
1547 l
= new0(char *, 2);
1551 l
[0] = resolved_name
;
1554 r
= hashmap_put(members
, resolved_id
, l
);
1560 resolved_id
= resolved_name
= NULL
;
1568 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname
, line
);
1572 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1581 if (path_is_absolute(resolved_id
)) {
1582 i
->uid_path
= resolved_id
;
1585 path_kill_slashes(i
->uid_path
);
1587 r
= parse_uid(resolved_id
, &i
->uid
);
1589 log_error("Failed to parse UID: %s", id
);
1597 i
->description
= description
;
1608 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname
, line
);
1613 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname
, line
);
1618 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname
, line
);
1622 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1631 if (path_is_absolute(resolved_id
)) {
1632 i
->gid_path
= resolved_id
;
1635 path_kill_slashes(i
->gid_path
);
1637 r
= parse_gid(resolved_id
, &i
->gid
);
1639 log_error("Failed to parse GID: %s", id
);
1654 i
->type
= action
[0];
1655 i
->name
= resolved_name
;
1656 resolved_name
= NULL
;
1658 existing
= hashmap_get(h
, i
->name
);
1661 /* Two identical items are fine */
1662 if (!item_equal(existing
, i
))
1663 log_warning("Two or more conflicting lines for %s configured, ignoring.", i
->name
);
1668 r
= hashmap_put(h
, i
->name
, i
);
1676 static int read_config_file(const char *fn
, bool ignore_enoent
) {
1677 _cleanup_fclose_
FILE *rf
= NULL
;
1679 char line
[LINE_MAX
];
1688 r
= search_and_fopen_nulstr(fn
, "re", arg_root
, conf_file_dirs
, &rf
);
1690 if (ignore_enoent
&& r
== -ENOENT
)
1693 return log_error_errno(r
, "Failed to open '%s', ignoring: %m", fn
);
1699 FOREACH_LINE(line
, f
, break) {
1706 if (*l
== '#' || *l
== 0)
1709 k
= parse_line(fn
, v
, l
);
1710 if (k
< 0 && r
== 0)
1715 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
1723 static void free_database(Hashmap
*by_name
, Hashmap
*by_id
) {
1727 name
= hashmap_first(by_id
);
1731 hashmap_remove(by_name
, name
);
1733 hashmap_steal_first_key(by_id
);
1737 while ((name
= hashmap_steal_first_key(by_name
)))
1740 hashmap_free(by_name
);
1741 hashmap_free(by_id
);
1744 static void help(void) {
1745 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1746 "Creates system user accounts.\n\n"
1747 " -h --help Show this help\n"
1748 " --version Show package version\n"
1749 " --root=PATH Operate on an alternate filesystem root\n"
1750 , program_invocation_short_name
);
1753 static int parse_argv(int argc
, char *argv
[]) {
1756 ARG_VERSION
= 0x100,
1760 static const struct option options
[] = {
1761 { "help", no_argument
, NULL
, 'h' },
1762 { "version", no_argument
, NULL
, ARG_VERSION
},
1763 { "root", required_argument
, NULL
, ARG_ROOT
},
1772 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
1781 puts(PACKAGE_STRING
);
1782 puts(SYSTEMD_FEATURES
);
1787 arg_root
= path_make_absolute_cwd(optarg
);
1791 path_kill_slashes(arg_root
);
1798 assert_not_reached("Unhandled option");
1804 int main(int argc
, char *argv
[]) {
1806 _cleanup_close_
int lock
= -1;
1812 r
= parse_argv(argc
, argv
);
1816 log_set_target(LOG_TARGET_AUTO
);
1817 log_parse_environment();
1822 r
= mac_selinux_init(NULL
);
1824 log_error_errno(r
, "SELinux setup failed: %m");
1828 if (optind
< argc
) {
1831 for (j
= optind
; j
< argc
; j
++) {
1832 k
= read_config_file(argv
[j
], false);
1833 if (k
< 0 && r
== 0)
1837 _cleanup_strv_free_
char **files
= NULL
;
1840 r
= conf_files_list_nulstr(&files
, ".conf", arg_root
, conf_file_dirs
);
1842 log_error_errno(r
, "Failed to enumerate sysusers.d files: %m");
1846 STRV_FOREACH(f
, files
) {
1847 k
= read_config_file(*f
, true);
1848 if (k
< 0 && r
== 0)
1854 /* Default to default range of 1..SYSTEMD_UID_MAX */
1855 r
= uid_range_add(&uid_range
, &n_uid_range
, 1, SYSTEM_UID_MAX
);
1866 lock
= take_password_lock(arg_root
);
1868 log_error_errno(lock
, "Failed to take lock: %m");
1872 r
= load_user_database();
1874 log_error_errno(r
, "Failed to load user database: %m");
1878 r
= load_group_database();
1880 log_error_errno(r
, "Failed to read group database: %m");
1884 HASHMAP_FOREACH(i
, groups
, iterator
)
1887 HASHMAP_FOREACH(i
, users
, iterator
)
1892 log_error_errno(r
, "Failed to write files: %m");
1895 while ((i
= hashmap_steal_first(groups
)))
1898 while ((i
= hashmap_steal_first(users
)))
1901 while ((n
= hashmap_first_key(members
))) {
1902 strv_free(hashmap_steal_first(members
));
1906 hashmap_free(groups
);
1907 hashmap_free(users
);
1908 hashmap_free(members
);
1909 hashmap_free(todo_uids
);
1910 hashmap_free(todo_gids
);
1912 free_database(database_user
, database_uid
);
1913 free_database(database_group
, database_gid
);
1917 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;