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/>.
29 #include "conf-files.h"
31 #include "fileio-label.h"
32 #include "formats-util.h"
34 #include "path-util.h"
35 #include "selinux-util.h"
36 #include "specifier.h"
38 #include "uid-range.h"
41 #include "smack-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
), (uint64_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 rename_and_apply_smack(const char *temp_path
, const char *dest_path
) {
358 if (rename(temp_path
, dest_path
) < 0)
361 #ifdef SMACK_RUN_LABEL
362 r
= mac_smack_apply(dest_path
, SMACK_ATTR_ACCESS
, SMACK_FLOOR_LABEL
);
369 static int write_files(void) {
371 _cleanup_fclose_
FILE *passwd
= NULL
, *group
= NULL
, *shadow
= NULL
, *gshadow
= NULL
;
372 _cleanup_free_
char *passwd_tmp
= NULL
, *group_tmp
= NULL
, *shadow_tmp
= NULL
, *gshadow_tmp
= NULL
;
373 const char *passwd_path
= NULL
, *group_path
= NULL
, *shadow_path
= NULL
, *gshadow_path
= NULL
;
374 bool group_changed
= false;
379 if (hashmap_size(todo_gids
) > 0 || hashmap_size(members
) > 0) {
380 _cleanup_fclose_
FILE *original
= NULL
;
382 /* First we update the actual group list file */
383 group_path
= prefix_roota(arg_root
, "/etc/group");
384 r
= fopen_temporary_label("/etc/group", group_path
, &group
, &group_tmp
);
388 original
= fopen(group_path
, "re");
392 r
= sync_rights(original
, group
);
397 while ((gr
= fgetgrent(original
))) {
398 /* Safety checks against name and GID
399 * collisions. Normally, this should
400 * be unnecessary, but given that we
401 * look at the entries anyway here,
402 * let's make an extra verification
403 * step that we don't generate
404 * duplicate entries. */
406 i
= hashmap_get(groups
, gr
->gr_name
);
407 if (i
&& i
->todo_group
) {
412 if (hashmap_contains(todo_gids
, GID_TO_PTR(gr
->gr_gid
))) {
417 r
= putgrent_with_members(gr
, group
);
421 group_changed
= true;
425 if (!IN_SET(errno
, 0, ENOENT
)) {
430 } else if (errno
!= ENOENT
) {
433 } else if (fchmod(fileno(group
), 0644) < 0) {
438 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
442 .gr_passwd
= (char*) "x",
445 r
= putgrent_with_members(&n
, group
);
449 group_changed
= true;
452 r
= fflush_and_check(group
);
461 /* OK, now also update the shadow file for the group list */
462 gshadow_path
= prefix_roota(arg_root
, "/etc/gshadow");
463 r
= fopen_temporary_label("/etc/gshadow", gshadow_path
, &gshadow
, &gshadow_tmp
);
467 original
= fopen(gshadow_path
, "re");
471 r
= sync_rights(original
, gshadow
);
476 while ((sg
= fgetsgent(original
))) {
478 i
= hashmap_get(groups
, sg
->sg_namp
);
479 if (i
&& i
->todo_group
) {
484 r
= putsgent_with_members(sg
, gshadow
);
488 group_changed
= true;
492 if (!IN_SET(errno
, 0, ENOENT
)) {
497 } else if (errno
!= ENOENT
) {
500 } else if (fchmod(fileno(gshadow
), 0000) < 0) {
505 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
508 .sg_passwd
= (char*) "!!",
511 r
= putsgent_with_members(&n
, gshadow
);
515 group_changed
= true;
518 r
= fflush_and_check(gshadow
);
523 if (hashmap_size(todo_uids
) > 0) {
524 _cleanup_fclose_
FILE *original
= NULL
;
527 /* First we update the user database itself */
528 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
529 r
= fopen_temporary_label("/etc/passwd", passwd_path
, &passwd
, &passwd_tmp
);
533 original
= fopen(passwd_path
, "re");
537 r
= sync_rights(original
, passwd
);
542 while ((pw
= fgetpwent(original
))) {
544 i
= hashmap_get(users
, pw
->pw_name
);
545 if (i
&& i
->todo_user
) {
550 if (hashmap_contains(todo_uids
, UID_TO_PTR(pw
->pw_uid
))) {
556 if (putpwent(pw
, passwd
) < 0) {
557 r
= errno
? -errno
: -EIO
;
563 if (!IN_SET(errno
, 0, ENOENT
)) {
568 } else if (errno
!= ENOENT
) {
571 } else if (fchmod(fileno(passwd
), 0644) < 0) {
576 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
581 .pw_gecos
= i
->description
,
583 /* "x" means the password is stored in
585 .pw_passwd
= (char*) "x",
587 /* We default to the root directory as home */
588 .pw_dir
= i
->home
? i
->home
: (char*) "/",
590 /* Initialize the shell to nologin,
591 * with one exception: for root we
592 * patch in something special */
593 .pw_shell
= i
->uid
== 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
597 if (putpwent(&n
, passwd
) != 0) {
598 r
= errno
? -errno
: -EIO
;
603 r
= fflush_and_check(passwd
);
612 /* The we update the shadow database */
613 shadow_path
= prefix_roota(arg_root
, "/etc/shadow");
614 r
= fopen_temporary_label("/etc/shadow", shadow_path
, &shadow
, &shadow_tmp
);
618 lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
620 original
= fopen(shadow_path
, "re");
624 r
= sync_rights(original
, shadow
);
629 while ((sp
= fgetspent(original
))) {
631 i
= hashmap_get(users
, sp
->sp_namp
);
632 if (i
&& i
->todo_user
) {
633 /* we will update the existing entry */
634 sp
->sp_lstchg
= lstchg
;
636 /* only the /etc/shadow stage is left, so we can
637 * safely remove the item from the todo set */
638 i
->todo_user
= false;
639 hashmap_remove(todo_uids
, UID_TO_PTR(i
->uid
));
643 if (putspent(sp
, shadow
) < 0) {
644 r
= errno
? -errno
: -EIO
;
650 if (!IN_SET(errno
, 0, ENOENT
)) {
654 } else if (errno
!= ENOENT
) {
657 } else if (fchmod(fileno(shadow
), 0000) < 0) {
662 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
665 .sp_pwdp
= (char*) "!!",
672 .sp_flag
= (unsigned long) -1, /* this appears to be what everybody does ... */
676 if (putspent(&n
, shadow
) != 0) {
677 r
= errno
? -errno
: -EIO
;
682 r
= fflush_and_check(shadow
);
687 /* Make a backup of the old files */
690 r
= make_backup("/etc/group", group_path
);
695 r
= make_backup("/etc/gshadow", gshadow_path
);
702 r
= make_backup("/etc/passwd", passwd_path
);
707 r
= make_backup("/etc/shadow", shadow_path
);
712 /* And make the new files count */
715 r
= rename_and_apply_smack(group_tmp
, group_path
);
719 group_tmp
= mfree(group_tmp
);
722 r
= rename_and_apply_smack(gshadow_tmp
, gshadow_path
);
726 gshadow_tmp
= mfree(gshadow_tmp
);
731 r
= rename_and_apply_smack(passwd_tmp
, passwd_path
);
735 passwd_tmp
= mfree(passwd_tmp
);
738 r
= rename_and_apply_smack(shadow_tmp
, shadow_path
);
742 shadow_tmp
= mfree(shadow_tmp
);
760 static int uid_is_ok(uid_t uid
, const char *name
) {
766 /* Let's see if we already have assigned the UID a second time */
767 if (hashmap_get(todo_uids
, UID_TO_PTR(uid
)))
770 /* Try to avoid using uids that are already used by a group
771 * that doesn't have the same name as our new user. */
772 i
= hashmap_get(todo_gids
, GID_TO_PTR(uid
));
773 if (i
&& !streq(i
->name
, name
))
776 /* Let's check the files directly */
777 if (hashmap_contains(database_uid
, UID_TO_PTR(uid
)))
780 n
= hashmap_get(database_gid
, GID_TO_PTR(uid
));
781 if (n
&& !streq(n
, name
))
784 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
790 if (!IN_SET(errno
, 0, ENOENT
))
794 g
= getgrgid((gid_t
) uid
);
796 if (!streq(g
->gr_name
, name
))
798 } else if (!IN_SET(errno
, 0, ENOENT
))
805 static int root_stat(const char *p
, struct stat
*st
) {
808 fix
= prefix_roota(arg_root
, p
);
809 if (stat(fix
, st
) < 0)
815 static int read_id_from_file(Item
*i
, uid_t
*_uid
, gid_t
*_gid
) {
817 bool found_uid
= false, found_gid
= false;
823 /* First, try to get the gid directly */
824 if (_gid
&& i
->gid_path
&& root_stat(i
->gid_path
, &st
) >= 0) {
829 /* Then, try to get the uid directly */
830 if ((_uid
|| (_gid
&& !found_gid
))
832 && root_stat(i
->uid_path
, &st
) >= 0) {
837 /* If we need the gid, but had no success yet, also derive it from the uid path */
838 if (_gid
&& !found_gid
) {
844 /* If that didn't work yet, then let's reuse the gid as uid */
845 if (_uid
&& !found_uid
&& i
->gid_path
) {
850 } else if (root_stat(i
->gid_path
, &st
) >= 0) {
851 uid
= (uid_t
) st
.st_gid
;
873 static int add_user(Item
*i
) {
879 /* Check the database directly */
880 z
= hashmap_get(database_user
, i
->name
);
882 log_debug("User %s already exists.", i
->name
);
883 i
->uid
= PTR_TO_UID(z
);
893 p
= getpwnam(i
->name
);
895 log_debug("User %s already exists.", i
->name
);
899 r
= free_and_strdup(&i
->description
, p
->pw_gecos
);
905 if (!IN_SET(errno
, 0, ENOENT
))
906 return log_error_errno(errno
, "Failed to check if user %s already exists: %m", i
->name
);
909 /* Try to use the suggested numeric uid */
911 r
= uid_is_ok(i
->uid
, i
->name
);
913 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
915 log_debug("Suggested user ID " UID_FMT
" for %s already used.", i
->uid
, i
->name
);
920 /* If that didn't work, try to read it from the specified path */
924 if (read_id_from_file(i
, &c
, NULL
) > 0) {
926 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
927 log_debug("User ID " UID_FMT
" of file not suitable for %s.", c
, i
->name
);
929 r
= uid_is_ok(c
, i
->name
);
931 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
936 log_debug("User ID " UID_FMT
" of file for %s is already used.", c
, i
->name
);
941 /* Otherwise try to reuse the group ID */
942 if (!i
->uid_set
&& i
->gid_set
) {
943 r
= uid_is_ok((uid_t
) i
->gid
, i
->name
);
945 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
947 i
->uid
= (uid_t
) i
->gid
;
952 /* And if that didn't work either, let's try to find a free one */
955 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
957 log_error("No free user ID available for %s.", i
->name
);
961 r
= uid_is_ok(search_uid
, i
->name
);
963 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
972 r
= hashmap_ensure_allocated(&todo_uids
, NULL
);
976 r
= hashmap_put(todo_uids
, UID_TO_PTR(i
->uid
), i
);
981 log_info("Creating user %s (%s) with uid " UID_FMT
" and gid " GID_FMT
".", i
->name
, strna(i
->description
), i
->uid
, i
->gid
);
986 static int gid_is_ok(gid_t gid
) {
990 if (hashmap_get(todo_gids
, GID_TO_PTR(gid
)))
993 /* Avoid reusing gids that are already used by a different user */
994 if (hashmap_get(todo_uids
, UID_TO_PTR(gid
)))
997 if (hashmap_contains(database_gid
, GID_TO_PTR(gid
)))
1000 if (hashmap_contains(database_uid
, UID_TO_PTR(gid
)))
1008 if (!IN_SET(errno
, 0, ENOENT
))
1012 p
= getpwuid((uid_t
) gid
);
1015 if (!IN_SET(errno
, 0, ENOENT
))
1022 static int add_group(Item
*i
) {
1028 /* Check the database directly */
1029 z
= hashmap_get(database_group
, i
->name
);
1031 log_debug("Group %s already exists.", i
->name
);
1032 i
->gid
= PTR_TO_GID(z
);
1037 /* Also check NSS */
1042 g
= getgrnam(i
->name
);
1044 log_debug("Group %s already exists.", i
->name
);
1049 if (!IN_SET(errno
, 0, ENOENT
))
1050 return log_error_errno(errno
, "Failed to check if group %s already exists: %m", i
->name
);
1053 /* Try to use the suggested numeric gid */
1055 r
= gid_is_ok(i
->gid
);
1057 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1059 log_debug("Suggested group ID " GID_FMT
" for %s already used.", i
->gid
, i
->name
);
1064 /* Try to reuse the numeric uid, if there's one */
1065 if (!i
->gid_set
&& i
->uid_set
) {
1066 r
= gid_is_ok((gid_t
) i
->uid
);
1068 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1070 i
->gid
= (gid_t
) i
->uid
;
1075 /* If that didn't work, try to read it from the specified path */
1079 if (read_id_from_file(i
, NULL
, &c
) > 0) {
1081 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1082 log_debug("Group ID " GID_FMT
" of file not suitable for %s.", c
, i
->name
);
1086 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1091 log_debug("Group ID " GID_FMT
" of file for %s already used.", c
, i
->name
);
1096 /* And if that didn't work either, let's try to find a free one */
1099 /* We look for new GIDs in the UID pool! */
1100 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1102 log_error("No free group ID available for %s.", i
->name
);
1106 r
= gid_is_ok(search_uid
);
1108 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1114 i
->gid
= search_uid
;
1117 r
= hashmap_ensure_allocated(&todo_gids
, NULL
);
1121 r
= hashmap_put(todo_gids
, GID_TO_PTR(i
->gid
), i
);
1125 i
->todo_group
= true;
1126 log_info("Creating group %s with gid " GID_FMT
".", i
->name
, i
->gid
);
1131 static int process_item(Item
*i
) {
1148 j
= hashmap_get(users
, i
->name
);
1150 /* There's already user to be created for this
1151 * name, let's process that in one step */
1159 r
= free_and_strdup(&j
->gid_path
, i
->gid_path
);
1167 return add_group(i
);
1171 assert_not_reached("Unknown item type");
1175 static void item_free(Item
*i
) {
1183 free(i
->description
);
1187 DEFINE_TRIVIAL_CLEANUP_FUNC(Item
*, item_free
);
1189 static int add_implicit(void) {
1194 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1196 HASHMAP_FOREACH_KEY(l
, g
, members
, iterator
) {
1200 i
= hashmap_get(groups
, g
);
1202 _cleanup_(item_freep
) Item
*j
= NULL
;
1204 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1212 j
->type
= ADD_GROUP
;
1213 j
->name
= strdup(g
);
1217 r
= hashmap_put(groups
, j
->name
, j
);
1221 log_debug("Adding implicit group '%s' due to m line", j
->name
);
1225 STRV_FOREACH(m
, l
) {
1227 i
= hashmap_get(users
, *m
);
1229 _cleanup_(item_freep
) Item
*j
= NULL
;
1231 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1240 j
->name
= strdup(*m
);
1244 r
= hashmap_put(users
, j
->name
, j
);
1248 log_debug("Adding implicit user '%s' due to m line", j
->name
);
1257 static bool item_equal(Item
*a
, Item
*b
) {
1261 if (a
->type
!= b
->type
)
1264 if (!streq_ptr(a
->name
, b
->name
))
1267 if (!streq_ptr(a
->uid_path
, b
->uid_path
))
1270 if (!streq_ptr(a
->gid_path
, b
->gid_path
))
1273 if (!streq_ptr(a
->description
, b
->description
))
1276 if (a
->uid_set
!= b
->uid_set
)
1279 if (a
->uid_set
&& a
->uid
!= b
->uid
)
1282 if (a
->gid_set
!= b
->gid_set
)
1285 if (a
->gid_set
&& a
->gid
!= b
->gid
)
1288 if (!streq_ptr(a
->home
, b
->home
))
1294 static bool valid_user_group_name(const char *u
) {
1301 if (!(u
[0] >= 'a' && u
[0] <= 'z') &&
1302 !(u
[0] >= 'A' && u
[0] <= 'Z') &&
1306 for (i
= u
+1; *i
; i
++) {
1307 if (!(*i
>= 'a' && *i
<= 'z') &&
1308 !(*i
>= 'A' && *i
<= 'Z') &&
1309 !(*i
>= '0' && *i
<= '9') &&
1315 sz
= sysconf(_SC_LOGIN_NAME_MAX
);
1318 if ((size_t) (i
-u
) > (size_t) sz
)
1321 if ((size_t) (i
-u
) > UT_NAMESIZE
- 1)
1327 static bool valid_gecos(const char *d
) {
1332 if (!utf8_is_valid(d
))
1335 if (string_has_cc(d
, NULL
))
1338 /* Colons are used as field separators, and hence not OK */
1345 static bool valid_home(const char *p
) {
1350 if (!utf8_is_valid(p
))
1353 if (string_has_cc(p
, NULL
))
1356 if (!path_is_absolute(p
))
1359 if (!path_is_safe(p
))
1362 /* Colons are used as field separators, and hence not OK */
1369 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1371 static const Specifier specifier_table
[] = {
1372 { 'm', specifier_machine_id
, NULL
},
1373 { 'b', specifier_boot_id
, NULL
},
1374 { 'H', specifier_host_name
, NULL
},
1375 { 'v', specifier_kernel_release
, NULL
},
1379 _cleanup_free_
char *action
= NULL
, *name
= NULL
, *id
= NULL
, *resolved_name
= NULL
, *resolved_id
= NULL
, *description
= NULL
, *home
= NULL
;
1380 _cleanup_(item_freep
) Item
*i
= NULL
;
1392 r
= extract_many_words(&p
, NULL
, EXTRACT_QUOTES
, &action
, &name
, &id
, &description
, &home
, NULL
);
1394 log_error("[%s:%u] Syntax error.", fname
, line
);
1398 log_error("[%s:%u] Missing action and name columns.", fname
, line
);
1402 log_error("[%s:%u] Trailing garbage.", fname
, line
);
1407 if (strlen(action
) != 1) {
1408 log_error("[%s:%u] Unknown modifier '%s'", fname
, line
, action
);
1412 if (!IN_SET(action
[0], ADD_USER
, ADD_GROUP
, ADD_MEMBER
, ADD_RANGE
)) {
1413 log_error("[%s:%u] Unknown command command type '%c'.", fname
, line
, action
[0]);
1418 if (isempty(name
) || streq(name
, "-"))
1422 r
= specifier_printf(name
, specifier_table
, NULL
, &resolved_name
);
1424 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1428 if (!valid_user_group_name(resolved_name
)) {
1429 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_name
);
1435 if (isempty(id
) || streq(id
, "-"))
1439 r
= specifier_printf(id
, specifier_table
, NULL
, &resolved_id
);
1441 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1446 /* Verify description */
1447 if (isempty(description
) || streq(description
, "-"))
1448 description
= mfree(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
, "-"))
1462 if (!valid_home(home
)) {
1463 log_error("[%s:%u] '%s' is not a valid home directory field.", fname
, line
, home
);
1468 switch (action
[0]) {
1471 if (resolved_name
) {
1472 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname
, line
);
1477 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname
, line
);
1482 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname
, line
);
1487 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname
, line
);
1491 r
= uid_range_add_str(&uid_range
, &n_uid_range
, resolved_id
);
1493 log_error("[%s:%u] Invalid UID range %s.", fname
, line
, resolved_id
);
1502 /* Try to extend an existing member or group item */
1504 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname
, line
);
1509 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname
, line
);
1513 if (!valid_user_group_name(resolved_id
)) {
1514 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_id
);
1519 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname
, line
);
1524 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname
, line
);
1528 r
= hashmap_ensure_allocated(&members
, &string_hash_ops
);
1532 l
= hashmap_get(members
, resolved_id
);
1534 /* A list for this group name already exists, let's append to it */
1535 r
= strv_push(&l
, resolved_name
);
1539 resolved_name
= NULL
;
1541 assert_se(hashmap_update(members
, resolved_id
, l
) >= 0);
1543 /* No list for this group name exists yet, create one */
1545 l
= new0(char *, 2);
1549 l
[0] = resolved_name
;
1552 r
= hashmap_put(members
, resolved_id
, l
);
1558 resolved_id
= resolved_name
= NULL
;
1566 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname
, line
);
1570 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1579 if (path_is_absolute(resolved_id
)) {
1580 i
->uid_path
= resolved_id
;
1583 path_kill_slashes(i
->uid_path
);
1585 r
= parse_uid(resolved_id
, &i
->uid
);
1587 log_error("Failed to parse UID: %s", id
);
1595 i
->description
= description
;
1606 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname
, line
);
1611 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname
, line
);
1616 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname
, line
);
1620 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1629 if (path_is_absolute(resolved_id
)) {
1630 i
->gid_path
= resolved_id
;
1633 path_kill_slashes(i
->gid_path
);
1635 r
= parse_gid(resolved_id
, &i
->gid
);
1637 log_error("Failed to parse GID: %s", id
);
1652 i
->type
= action
[0];
1653 i
->name
= resolved_name
;
1654 resolved_name
= NULL
;
1656 existing
= hashmap_get(h
, i
->name
);
1659 /* Two identical items are fine */
1660 if (!item_equal(existing
, i
))
1661 log_warning("Two or more conflicting lines for %s configured, ignoring.", i
->name
);
1666 r
= hashmap_put(h
, i
->name
, i
);
1674 static int read_config_file(const char *fn
, bool ignore_enoent
) {
1675 _cleanup_fclose_
FILE *rf
= NULL
;
1677 char line
[LINE_MAX
];
1686 r
= search_and_fopen_nulstr(fn
, "re", arg_root
, conf_file_dirs
, &rf
);
1688 if (ignore_enoent
&& r
== -ENOENT
)
1691 return log_error_errno(r
, "Failed to open '%s', ignoring: %m", fn
);
1697 FOREACH_LINE(line
, f
, break) {
1704 if (*l
== '#' || *l
== 0)
1707 k
= parse_line(fn
, v
, l
);
1708 if (k
< 0 && r
== 0)
1713 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
1721 static void free_database(Hashmap
*by_name
, Hashmap
*by_id
) {
1725 name
= hashmap_first(by_id
);
1729 hashmap_remove(by_name
, name
);
1731 hashmap_steal_first_key(by_id
);
1735 while ((name
= hashmap_steal_first_key(by_name
)))
1738 hashmap_free(by_name
);
1739 hashmap_free(by_id
);
1742 static void help(void) {
1743 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1744 "Creates system user accounts.\n\n"
1745 " -h --help Show this help\n"
1746 " --version Show package version\n"
1747 " --root=PATH Operate on an alternate filesystem root\n"
1748 , program_invocation_short_name
);
1751 static int parse_argv(int argc
, char *argv
[]) {
1754 ARG_VERSION
= 0x100,
1758 static const struct option options
[] = {
1759 { "help", no_argument
, NULL
, 'h' },
1760 { "version", no_argument
, NULL
, ARG_VERSION
},
1761 { "root", required_argument
, NULL
, ARG_ROOT
},
1770 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
1783 arg_root
= path_make_absolute_cwd(optarg
);
1787 path_kill_slashes(arg_root
);
1794 assert_not_reached("Unhandled option");
1800 int main(int argc
, char *argv
[]) {
1802 _cleanup_close_
int lock
= -1;
1808 r
= parse_argv(argc
, argv
);
1812 log_set_target(LOG_TARGET_AUTO
);
1813 log_parse_environment();
1818 r
= mac_selinux_init(NULL
);
1820 log_error_errno(r
, "SELinux setup failed: %m");
1824 if (optind
< argc
) {
1827 for (j
= optind
; j
< argc
; j
++) {
1828 k
= read_config_file(argv
[j
], false);
1829 if (k
< 0 && r
== 0)
1833 _cleanup_strv_free_
char **files
= NULL
;
1836 r
= conf_files_list_nulstr(&files
, ".conf", arg_root
, conf_file_dirs
);
1838 log_error_errno(r
, "Failed to enumerate sysusers.d files: %m");
1842 STRV_FOREACH(f
, files
) {
1843 k
= read_config_file(*f
, true);
1844 if (k
< 0 && r
== 0)
1850 /* Default to default range of 1..SYSTEMD_UID_MAX */
1851 r
= uid_range_add(&uid_range
, &n_uid_range
, 1, SYSTEM_UID_MAX
);
1862 lock
= take_password_lock(arg_root
);
1864 log_error_errno(lock
, "Failed to take lock: %m");
1868 r
= load_user_database();
1870 log_error_errno(r
, "Failed to load user database: %m");
1874 r
= load_group_database();
1876 log_error_errno(r
, "Failed to read group database: %m");
1880 HASHMAP_FOREACH(i
, groups
, iterator
)
1883 HASHMAP_FOREACH(i
, users
, iterator
)
1888 log_error_errno(r
, "Failed to write files: %m");
1891 while ((i
= hashmap_steal_first(groups
)))
1894 while ((i
= hashmap_steal_first(users
)))
1897 while ((n
= hashmap_first_key(members
))) {
1898 strv_free(hashmap_steal_first(members
));
1902 hashmap_free(groups
);
1903 hashmap_free(users
);
1904 hashmap_free(members
);
1905 hashmap_free(todo_uids
);
1906 hashmap_free(todo_gids
);
1908 free_database(database_user
, database_uid
);
1909 free_database(database_group
, database_gid
);
1913 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;