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 "smack-util.h"
37 #include "specifier.h"
38 #include "string-util.h"
40 #include "uid-range.h"
44 typedef enum ItemType
{
69 static char *arg_root
= NULL
;
71 static const char conf_file_dirs
[] = CONF_DIRS_NULSTR("sysusers");
73 static Hashmap
*users
= NULL
, *groups
= NULL
;
74 static Hashmap
*todo_uids
= NULL
, *todo_gids
= NULL
;
75 static Hashmap
*members
= NULL
;
77 static Hashmap
*database_uid
= NULL
, *database_user
= NULL
;
78 static Hashmap
*database_gid
= NULL
, *database_group
= NULL
;
80 static uid_t search_uid
= UID_INVALID
;
81 static UidRange
*uid_range
= NULL
;
82 static unsigned n_uid_range
= 0;
84 static int load_user_database(void) {
85 _cleanup_fclose_
FILE *f
= NULL
;
86 const char *passwd_path
;
90 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
91 f
= fopen(passwd_path
, "re");
93 return errno
== ENOENT
? 0 : -errno
;
95 r
= hashmap_ensure_allocated(&database_user
, &string_hash_ops
);
99 r
= hashmap_ensure_allocated(&database_uid
, NULL
);
104 while ((pw
= fgetpwent(f
))) {
108 n
= strdup(pw
->pw_name
);
112 k
= hashmap_put(database_user
, n
, UID_TO_PTR(pw
->pw_uid
));
113 if (k
< 0 && k
!= -EEXIST
) {
118 q
= hashmap_put(database_uid
, UID_TO_PTR(pw
->pw_uid
), n
);
119 if (q
< 0 && q
!= -EEXIST
) {
130 if (!IN_SET(errno
, 0, ENOENT
))
136 static int load_group_database(void) {
137 _cleanup_fclose_
FILE *f
= NULL
;
138 const char *group_path
;
142 group_path
= prefix_roota(arg_root
, "/etc/group");
143 f
= fopen(group_path
, "re");
145 return errno
== ENOENT
? 0 : -errno
;
147 r
= hashmap_ensure_allocated(&database_group
, &string_hash_ops
);
151 r
= hashmap_ensure_allocated(&database_gid
, NULL
);
156 while ((gr
= fgetgrent(f
))) {
160 n
= strdup(gr
->gr_name
);
164 k
= hashmap_put(database_group
, n
, GID_TO_PTR(gr
->gr_gid
));
165 if (k
< 0 && k
!= -EEXIST
) {
170 q
= hashmap_put(database_gid
, GID_TO_PTR(gr
->gr_gid
), n
);
171 if (q
< 0 && q
!= -EEXIST
) {
182 if (!IN_SET(errno
, 0, ENOENT
))
188 static int make_backup(const char *target
, const char *x
) {
189 _cleanup_close_
int src
= -1;
190 _cleanup_fclose_
FILE *dst
= NULL
;
192 struct timespec ts
[2];
196 src
= open(x
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
198 if (errno
== ENOENT
) /* No backup necessary... */
204 if (fstat(src
, &st
) < 0)
207 r
= fopen_temporary_label(target
, x
, &dst
, &temp
);
211 r
= copy_bytes(src
, fileno(dst
), (uint64_t) -1, true);
215 /* Don't fail on chmod() or chown(). If it stays owned by us
216 * and/or unreadable by others, then it isn't too bad... */
218 backup
= strjoina(x
, "-");
220 /* Copy over the access mask */
221 if (fchmod(fileno(dst
), st
.st_mode
& 07777) < 0)
222 log_warning_errno(errno
, "Failed to change mode on %s: %m", backup
);
224 if (fchown(fileno(dst
), st
.st_uid
, st
.st_gid
)< 0)
225 log_warning_errno(errno
, "Failed to change ownership of %s: %m", backup
);
229 if (futimens(fileno(dst
), ts
) < 0)
230 log_warning_errno(errno
, "Failed to fix access and modification time of %s: %m", backup
);
232 if (rename(temp
, backup
) < 0)
242 static int putgrent_with_members(const struct group
*gr
, FILE *group
) {
248 a
= hashmap_get(members
, gr
->gr_name
);
250 _cleanup_strv_free_
char **l
= NULL
;
254 l
= strv_copy(gr
->gr_mem
);
259 if (strv_find(l
, *i
))
262 if (strv_extend(&l
, *i
) < 0)
278 if (putgrent(&t
, group
) != 0)
279 return errno
? -errno
: -EIO
;
286 if (putgrent(gr
, group
) != 0)
287 return errno
? -errno
: -EIO
;
292 static int putsgent_with_members(const struct sgrp
*sg
, FILE *gshadow
) {
298 a
= hashmap_get(members
, sg
->sg_namp
);
300 _cleanup_strv_free_
char **l
= NULL
;
304 l
= strv_copy(sg
->sg_mem
);
309 if (strv_find(l
, *i
))
312 if (strv_extend(&l
, *i
) < 0)
328 if (putsgent(&t
, gshadow
) != 0)
329 return errno
? -errno
: -EIO
;
336 if (putsgent(sg
, gshadow
) != 0)
337 return errno
? -errno
: -EIO
;
342 static int sync_rights(FILE *from
, FILE *to
) {
345 if (fstat(fileno(from
), &st
) < 0)
348 if (fchmod(fileno(to
), st
.st_mode
& 07777) < 0)
351 if (fchown(fileno(to
), st
.st_uid
, st
.st_gid
) < 0)
357 static int rename_and_apply_smack(const char *temp_path
, const char *dest_path
) {
359 if (rename(temp_path
, dest_path
) < 0)
362 #ifdef SMACK_RUN_LABEL
363 r
= mac_smack_apply(dest_path
, SMACK_ATTR_ACCESS
, SMACK_FLOOR_LABEL
);
370 static int write_files(void) {
372 _cleanup_fclose_
FILE *passwd
= NULL
, *group
= NULL
, *shadow
= NULL
, *gshadow
= NULL
;
373 _cleanup_free_
char *passwd_tmp
= NULL
, *group_tmp
= NULL
, *shadow_tmp
= NULL
, *gshadow_tmp
= NULL
;
374 const char *passwd_path
= NULL
, *group_path
= NULL
, *shadow_path
= NULL
, *gshadow_path
= NULL
;
375 bool group_changed
= false;
380 if (hashmap_size(todo_gids
) > 0 || hashmap_size(members
) > 0) {
381 _cleanup_fclose_
FILE *original
= NULL
;
383 /* First we update the actual group list file */
384 group_path
= prefix_roota(arg_root
, "/etc/group");
385 r
= fopen_temporary_label("/etc/group", group_path
, &group
, &group_tmp
);
389 original
= fopen(group_path
, "re");
393 r
= sync_rights(original
, group
);
398 while ((gr
= fgetgrent(original
))) {
399 /* Safety checks against name and GID
400 * collisions. Normally, this should
401 * be unnecessary, but given that we
402 * look at the entries anyway here,
403 * let's make an extra verification
404 * step that we don't generate
405 * duplicate entries. */
407 i
= hashmap_get(groups
, gr
->gr_name
);
408 if (i
&& i
->todo_group
) {
413 if (hashmap_contains(todo_gids
, GID_TO_PTR(gr
->gr_gid
))) {
418 r
= putgrent_with_members(gr
, group
);
422 group_changed
= true;
426 if (!IN_SET(errno
, 0, ENOENT
)) {
431 } else if (errno
!= ENOENT
) {
434 } else if (fchmod(fileno(group
), 0644) < 0) {
439 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
443 .gr_passwd
= (char*) "x",
446 r
= putgrent_with_members(&n
, group
);
450 group_changed
= true;
453 r
= fflush_and_check(group
);
462 /* OK, now also update the shadow file for the group list */
463 gshadow_path
= prefix_roota(arg_root
, "/etc/gshadow");
464 r
= fopen_temporary_label("/etc/gshadow", gshadow_path
, &gshadow
, &gshadow_tmp
);
468 original
= fopen(gshadow_path
, "re");
472 r
= sync_rights(original
, gshadow
);
477 while ((sg
= fgetsgent(original
))) {
479 i
= hashmap_get(groups
, sg
->sg_namp
);
480 if (i
&& i
->todo_group
) {
485 r
= putsgent_with_members(sg
, gshadow
);
489 group_changed
= true;
493 if (!IN_SET(errno
, 0, ENOENT
)) {
498 } else if (errno
!= ENOENT
) {
501 } else if (fchmod(fileno(gshadow
), 0000) < 0) {
506 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
509 .sg_passwd
= (char*) "!!",
512 r
= putsgent_with_members(&n
, gshadow
);
516 group_changed
= true;
519 r
= fflush_and_check(gshadow
);
524 if (hashmap_size(todo_uids
) > 0) {
525 _cleanup_fclose_
FILE *original
= NULL
;
528 /* First we update the user database itself */
529 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
530 r
= fopen_temporary_label("/etc/passwd", passwd_path
, &passwd
, &passwd_tmp
);
534 original
= fopen(passwd_path
, "re");
538 r
= sync_rights(original
, passwd
);
543 while ((pw
= fgetpwent(original
))) {
545 i
= hashmap_get(users
, pw
->pw_name
);
546 if (i
&& i
->todo_user
) {
551 if (hashmap_contains(todo_uids
, UID_TO_PTR(pw
->pw_uid
))) {
557 if (putpwent(pw
, passwd
) < 0) {
558 r
= errno
? -errno
: -EIO
;
564 if (!IN_SET(errno
, 0, ENOENT
)) {
569 } else if (errno
!= ENOENT
) {
572 } else if (fchmod(fileno(passwd
), 0644) < 0) {
577 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
582 .pw_gecos
= i
->description
,
584 /* "x" means the password is stored in
586 .pw_passwd
= (char*) "x",
588 /* We default to the root directory as home */
589 .pw_dir
= i
->home
? i
->home
: (char*) "/",
591 /* Initialize the shell to nologin,
592 * with one exception: for root we
593 * patch in something special */
594 .pw_shell
= i
->uid
== 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
598 if (putpwent(&n
, passwd
) != 0) {
599 r
= errno
? -errno
: -EIO
;
604 r
= fflush_and_check(passwd
);
613 /* The we update the shadow database */
614 shadow_path
= prefix_roota(arg_root
, "/etc/shadow");
615 r
= fopen_temporary_label("/etc/shadow", shadow_path
, &shadow
, &shadow_tmp
);
619 lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
621 original
= fopen(shadow_path
, "re");
625 r
= sync_rights(original
, shadow
);
630 while ((sp
= fgetspent(original
))) {
632 i
= hashmap_get(users
, sp
->sp_namp
);
633 if (i
&& i
->todo_user
) {
634 /* we will update the existing entry */
635 sp
->sp_lstchg
= lstchg
;
637 /* only the /etc/shadow stage is left, so we can
638 * safely remove the item from the todo set */
639 i
->todo_user
= false;
640 hashmap_remove(todo_uids
, UID_TO_PTR(i
->uid
));
644 if (putspent(sp
, shadow
) < 0) {
645 r
= errno
? -errno
: -EIO
;
651 if (!IN_SET(errno
, 0, ENOENT
)) {
655 } else if (errno
!= ENOENT
) {
658 } else if (fchmod(fileno(shadow
), 0000) < 0) {
663 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
666 .sp_pwdp
= (char*) "!!",
673 .sp_flag
= (unsigned long) -1, /* this appears to be what everybody does ... */
677 if (putspent(&n
, shadow
) != 0) {
678 r
= errno
? -errno
: -EIO
;
683 r
= fflush_and_check(shadow
);
688 /* Make a backup of the old files */
691 r
= make_backup("/etc/group", group_path
);
696 r
= make_backup("/etc/gshadow", gshadow_path
);
703 r
= make_backup("/etc/passwd", passwd_path
);
708 r
= make_backup("/etc/shadow", shadow_path
);
713 /* And make the new files count */
716 r
= rename_and_apply_smack(group_tmp
, group_path
);
720 group_tmp
= mfree(group_tmp
);
723 r
= rename_and_apply_smack(gshadow_tmp
, gshadow_path
);
727 gshadow_tmp
= mfree(gshadow_tmp
);
732 r
= rename_and_apply_smack(passwd_tmp
, passwd_path
);
736 passwd_tmp
= mfree(passwd_tmp
);
739 r
= rename_and_apply_smack(shadow_tmp
, shadow_path
);
743 shadow_tmp
= mfree(shadow_tmp
);
761 static int uid_is_ok(uid_t uid
, const char *name
) {
767 /* Let's see if we already have assigned the UID a second time */
768 if (hashmap_get(todo_uids
, UID_TO_PTR(uid
)))
771 /* Try to avoid using uids that are already used by a group
772 * that doesn't have the same name as our new user. */
773 i
= hashmap_get(todo_gids
, GID_TO_PTR(uid
));
774 if (i
&& !streq(i
->name
, name
))
777 /* Let's check the files directly */
778 if (hashmap_contains(database_uid
, UID_TO_PTR(uid
)))
781 n
= hashmap_get(database_gid
, GID_TO_PTR(uid
));
782 if (n
&& !streq(n
, name
))
785 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
791 if (!IN_SET(errno
, 0, ENOENT
))
795 g
= getgrgid((gid_t
) uid
);
797 if (!streq(g
->gr_name
, name
))
799 } else if (!IN_SET(errno
, 0, ENOENT
))
806 static int root_stat(const char *p
, struct stat
*st
) {
809 fix
= prefix_roota(arg_root
, p
);
810 if (stat(fix
, st
) < 0)
816 static int read_id_from_file(Item
*i
, uid_t
*_uid
, gid_t
*_gid
) {
818 bool found_uid
= false, found_gid
= false;
824 /* First, try to get the gid directly */
825 if (_gid
&& i
->gid_path
&& root_stat(i
->gid_path
, &st
) >= 0) {
830 /* Then, try to get the uid directly */
831 if ((_uid
|| (_gid
&& !found_gid
))
833 && root_stat(i
->uid_path
, &st
) >= 0) {
838 /* If we need the gid, but had no success yet, also derive it from the uid path */
839 if (_gid
&& !found_gid
) {
845 /* If that didn't work yet, then let's reuse the gid as uid */
846 if (_uid
&& !found_uid
&& i
->gid_path
) {
851 } else if (root_stat(i
->gid_path
, &st
) >= 0) {
852 uid
= (uid_t
) st
.st_gid
;
874 static int add_user(Item
*i
) {
880 /* Check the database directly */
881 z
= hashmap_get(database_user
, i
->name
);
883 log_debug("User %s already exists.", i
->name
);
884 i
->uid
= PTR_TO_UID(z
);
894 p
= getpwnam(i
->name
);
896 log_debug("User %s already exists.", i
->name
);
900 r
= free_and_strdup(&i
->description
, p
->pw_gecos
);
906 if (!IN_SET(errno
, 0, ENOENT
))
907 return log_error_errno(errno
, "Failed to check if user %s already exists: %m", i
->name
);
910 /* Try to use the suggested numeric uid */
912 r
= uid_is_ok(i
->uid
, i
->name
);
914 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
916 log_debug("Suggested user ID " UID_FMT
" for %s already used.", i
->uid
, i
->name
);
921 /* If that didn't work, try to read it from the specified path */
925 if (read_id_from_file(i
, &c
, NULL
) > 0) {
927 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
928 log_debug("User ID " UID_FMT
" of file not suitable for %s.", c
, i
->name
);
930 r
= uid_is_ok(c
, i
->name
);
932 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
937 log_debug("User ID " UID_FMT
" of file for %s is already used.", c
, i
->name
);
942 /* Otherwise try to reuse the group ID */
943 if (!i
->uid_set
&& i
->gid_set
) {
944 r
= uid_is_ok((uid_t
) i
->gid
, i
->name
);
946 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
948 i
->uid
= (uid_t
) i
->gid
;
953 /* And if that didn't work either, let's try to find a free one */
956 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
958 log_error("No free user ID available for %s.", i
->name
);
962 r
= uid_is_ok(search_uid
, i
->name
);
964 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
973 r
= hashmap_ensure_allocated(&todo_uids
, NULL
);
977 r
= hashmap_put(todo_uids
, UID_TO_PTR(i
->uid
), i
);
982 log_info("Creating user %s (%s) with uid " UID_FMT
" and gid " GID_FMT
".", i
->name
, strna(i
->description
), i
->uid
, i
->gid
);
987 static int gid_is_ok(gid_t gid
) {
991 if (hashmap_get(todo_gids
, GID_TO_PTR(gid
)))
994 /* Avoid reusing gids that are already used by a different user */
995 if (hashmap_get(todo_uids
, UID_TO_PTR(gid
)))
998 if (hashmap_contains(database_gid
, GID_TO_PTR(gid
)))
1001 if (hashmap_contains(database_uid
, UID_TO_PTR(gid
)))
1009 if (!IN_SET(errno
, 0, ENOENT
))
1013 p
= getpwuid((uid_t
) gid
);
1016 if (!IN_SET(errno
, 0, ENOENT
))
1023 static int add_group(Item
*i
) {
1029 /* Check the database directly */
1030 z
= hashmap_get(database_group
, i
->name
);
1032 log_debug("Group %s already exists.", i
->name
);
1033 i
->gid
= PTR_TO_GID(z
);
1038 /* Also check NSS */
1043 g
= getgrnam(i
->name
);
1045 log_debug("Group %s already exists.", i
->name
);
1050 if (!IN_SET(errno
, 0, ENOENT
))
1051 return log_error_errno(errno
, "Failed to check if group %s already exists: %m", i
->name
);
1054 /* Try to use the suggested numeric gid */
1056 r
= gid_is_ok(i
->gid
);
1058 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1060 log_debug("Suggested group ID " GID_FMT
" for %s already used.", i
->gid
, i
->name
);
1065 /* Try to reuse the numeric uid, if there's one */
1066 if (!i
->gid_set
&& i
->uid_set
) {
1067 r
= gid_is_ok((gid_t
) i
->uid
);
1069 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1071 i
->gid
= (gid_t
) i
->uid
;
1076 /* If that didn't work, try to read it from the specified path */
1080 if (read_id_from_file(i
, NULL
, &c
) > 0) {
1082 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1083 log_debug("Group ID " GID_FMT
" of file not suitable for %s.", c
, i
->name
);
1087 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1092 log_debug("Group ID " GID_FMT
" of file for %s already used.", c
, i
->name
);
1097 /* And if that didn't work either, let's try to find a free one */
1100 /* We look for new GIDs in the UID pool! */
1101 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1103 log_error("No free group ID available for %s.", i
->name
);
1107 r
= gid_is_ok(search_uid
);
1109 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1115 i
->gid
= search_uid
;
1118 r
= hashmap_ensure_allocated(&todo_gids
, NULL
);
1122 r
= hashmap_put(todo_gids
, GID_TO_PTR(i
->gid
), i
);
1126 i
->todo_group
= true;
1127 log_info("Creating group %s with gid " GID_FMT
".", i
->name
, i
->gid
);
1132 static int process_item(Item
*i
) {
1149 j
= hashmap_get(users
, i
->name
);
1151 /* There's already user to be created for this
1152 * name, let's process that in one step */
1160 r
= free_and_strdup(&j
->gid_path
, i
->gid_path
);
1168 return add_group(i
);
1172 assert_not_reached("Unknown item type");
1176 static void item_free(Item
*i
) {
1184 free(i
->description
);
1188 DEFINE_TRIVIAL_CLEANUP_FUNC(Item
*, item_free
);
1190 static int add_implicit(void) {
1195 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1197 HASHMAP_FOREACH_KEY(l
, g
, members
, iterator
) {
1201 i
= hashmap_get(groups
, g
);
1203 _cleanup_(item_freep
) Item
*j
= NULL
;
1205 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1213 j
->type
= ADD_GROUP
;
1214 j
->name
= strdup(g
);
1218 r
= hashmap_put(groups
, j
->name
, j
);
1222 log_debug("Adding implicit group '%s' due to m line", j
->name
);
1226 STRV_FOREACH(m
, l
) {
1228 i
= hashmap_get(users
, *m
);
1230 _cleanup_(item_freep
) Item
*j
= NULL
;
1232 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1241 j
->name
= strdup(*m
);
1245 r
= hashmap_put(users
, j
->name
, j
);
1249 log_debug("Adding implicit user '%s' due to m line", j
->name
);
1258 static bool item_equal(Item
*a
, Item
*b
) {
1262 if (a
->type
!= b
->type
)
1265 if (!streq_ptr(a
->name
, b
->name
))
1268 if (!streq_ptr(a
->uid_path
, b
->uid_path
))
1271 if (!streq_ptr(a
->gid_path
, b
->gid_path
))
1274 if (!streq_ptr(a
->description
, b
->description
))
1277 if (a
->uid_set
!= b
->uid_set
)
1280 if (a
->uid_set
&& a
->uid
!= b
->uid
)
1283 if (a
->gid_set
!= b
->gid_set
)
1286 if (a
->gid_set
&& a
->gid
!= b
->gid
)
1289 if (!streq_ptr(a
->home
, b
->home
))
1295 static bool valid_user_group_name(const char *u
) {
1302 if (!(u
[0] >= 'a' && u
[0] <= 'z') &&
1303 !(u
[0] >= 'A' && u
[0] <= 'Z') &&
1307 for (i
= u
+1; *i
; i
++) {
1308 if (!(*i
>= 'a' && *i
<= 'z') &&
1309 !(*i
>= 'A' && *i
<= 'Z') &&
1310 !(*i
>= '0' && *i
<= '9') &&
1316 sz
= sysconf(_SC_LOGIN_NAME_MAX
);
1319 if ((size_t) (i
-u
) > (size_t) sz
)
1322 if ((size_t) (i
-u
) > UT_NAMESIZE
- 1)
1328 static bool valid_gecos(const char *d
) {
1333 if (!utf8_is_valid(d
))
1336 if (string_has_cc(d
, NULL
))
1339 /* Colons are used as field separators, and hence not OK */
1346 static bool valid_home(const char *p
) {
1351 if (!utf8_is_valid(p
))
1354 if (string_has_cc(p
, NULL
))
1357 if (!path_is_absolute(p
))
1360 if (!path_is_safe(p
))
1363 /* Colons are used as field separators, and hence not OK */
1370 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1372 static const Specifier specifier_table
[] = {
1373 { 'm', specifier_machine_id
, NULL
},
1374 { 'b', specifier_boot_id
, NULL
},
1375 { 'H', specifier_host_name
, NULL
},
1376 { 'v', specifier_kernel_release
, NULL
},
1380 _cleanup_free_
char *action
= NULL
, *name
= NULL
, *id
= NULL
, *resolved_name
= NULL
, *resolved_id
= NULL
, *description
= NULL
, *home
= NULL
;
1381 _cleanup_(item_freep
) Item
*i
= NULL
;
1393 r
= extract_many_words(&p
, NULL
, EXTRACT_QUOTES
, &action
, &name
, &id
, &description
, &home
, NULL
);
1395 log_error("[%s:%u] Syntax error.", fname
, line
);
1399 log_error("[%s:%u] Missing action and name columns.", fname
, line
);
1403 log_error("[%s:%u] Trailing garbage.", fname
, line
);
1408 if (strlen(action
) != 1) {
1409 log_error("[%s:%u] Unknown modifier '%s'", fname
, line
, action
);
1413 if (!IN_SET(action
[0], ADD_USER
, ADD_GROUP
, ADD_MEMBER
, ADD_RANGE
)) {
1414 log_error("[%s:%u] Unknown command command type '%c'.", fname
, line
, action
[0]);
1419 if (isempty(name
) || streq(name
, "-"))
1423 r
= specifier_printf(name
, specifier_table
, NULL
, &resolved_name
);
1425 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1429 if (!valid_user_group_name(resolved_name
)) {
1430 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_name
);
1436 if (isempty(id
) || streq(id
, "-"))
1440 r
= specifier_printf(id
, specifier_table
, NULL
, &resolved_id
);
1442 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1447 /* Verify description */
1448 if (isempty(description
) || streq(description
, "-"))
1449 description
= mfree(description
);
1452 if (!valid_gecos(description
)) {
1453 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname
, line
, description
);
1459 if (isempty(home
) || streq(home
, "-"))
1463 if (!valid_home(home
)) {
1464 log_error("[%s:%u] '%s' is not a valid home directory field.", fname
, line
, home
);
1469 switch (action
[0]) {
1472 if (resolved_name
) {
1473 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname
, line
);
1478 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname
, line
);
1483 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname
, line
);
1488 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname
, line
);
1492 r
= uid_range_add_str(&uid_range
, &n_uid_range
, resolved_id
);
1494 log_error("[%s:%u] Invalid UID range %s.", fname
, line
, resolved_id
);
1503 /* Try to extend an existing member or group item */
1505 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname
, line
);
1510 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname
, line
);
1514 if (!valid_user_group_name(resolved_id
)) {
1515 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_id
);
1520 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname
, line
);
1525 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname
, line
);
1529 r
= hashmap_ensure_allocated(&members
, &string_hash_ops
);
1533 l
= hashmap_get(members
, resolved_id
);
1535 /* A list for this group name already exists, let's append to it */
1536 r
= strv_push(&l
, resolved_name
);
1540 resolved_name
= NULL
;
1542 assert_se(hashmap_update(members
, resolved_id
, l
) >= 0);
1544 /* No list for this group name exists yet, create one */
1546 l
= new0(char *, 2);
1550 l
[0] = resolved_name
;
1553 r
= hashmap_put(members
, resolved_id
, l
);
1559 resolved_id
= resolved_name
= NULL
;
1567 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname
, line
);
1571 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1580 if (path_is_absolute(resolved_id
)) {
1581 i
->uid_path
= resolved_id
;
1584 path_kill_slashes(i
->uid_path
);
1586 r
= parse_uid(resolved_id
, &i
->uid
);
1588 log_error("Failed to parse UID: %s", id
);
1596 i
->description
= description
;
1607 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname
, line
);
1612 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname
, line
);
1617 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname
, line
);
1621 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1630 if (path_is_absolute(resolved_id
)) {
1631 i
->gid_path
= resolved_id
;
1634 path_kill_slashes(i
->gid_path
);
1636 r
= parse_gid(resolved_id
, &i
->gid
);
1638 log_error("Failed to parse GID: %s", id
);
1653 i
->type
= action
[0];
1654 i
->name
= resolved_name
;
1655 resolved_name
= NULL
;
1657 existing
= 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
= 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 (*l
== '#' || *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 , program_invocation_short_name
);
1752 static int parse_argv(int argc
, char *argv
[]) {
1755 ARG_VERSION
= 0x100,
1759 static const struct option options
[] = {
1760 { "help", no_argument
, NULL
, 'h' },
1761 { "version", no_argument
, NULL
, ARG_VERSION
},
1762 { "root", required_argument
, NULL
, ARG_ROOT
},
1771 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
1783 r
= parse_path_argument_and_warn(optarg
, true, &arg_root
);
1792 assert_not_reached("Unhandled option");
1798 int main(int argc
, char *argv
[]) {
1800 _cleanup_close_
int lock
= -1;
1806 r
= parse_argv(argc
, argv
);
1810 log_set_target(LOG_TARGET_AUTO
);
1811 log_parse_environment();
1816 r
= mac_selinux_init(NULL
);
1818 log_error_errno(r
, "SELinux setup failed: %m");
1822 if (optind
< argc
) {
1825 for (j
= optind
; j
< argc
; j
++) {
1826 k
= read_config_file(argv
[j
], false);
1827 if (k
< 0 && r
== 0)
1831 _cleanup_strv_free_
char **files
= NULL
;
1834 r
= conf_files_list_nulstr(&files
, ".conf", arg_root
, conf_file_dirs
);
1836 log_error_errno(r
, "Failed to enumerate sysusers.d files: %m");
1840 STRV_FOREACH(f
, files
) {
1841 k
= read_config_file(*f
, true);
1842 if (k
< 0 && r
== 0)
1848 /* Default to default range of 1..SYSTEMD_UID_MAX */
1849 r
= uid_range_add(&uid_range
, &n_uid_range
, 1, SYSTEM_UID_MAX
);
1860 lock
= take_password_lock(arg_root
);
1862 log_error_errno(lock
, "Failed to take lock: %m");
1866 r
= load_user_database();
1868 log_error_errno(r
, "Failed to load user database: %m");
1872 r
= load_group_database();
1874 log_error_errno(r
, "Failed to read group database: %m");
1878 HASHMAP_FOREACH(i
, groups
, iterator
)
1881 HASHMAP_FOREACH(i
, users
, iterator
)
1886 log_error_errno(r
, "Failed to write files: %m");
1889 while ((i
= hashmap_steal_first(groups
)))
1892 while ((i
= hashmap_steal_first(users
)))
1895 while ((n
= hashmap_first_key(members
))) {
1896 strv_free(hashmap_steal_first(members
));
1900 hashmap_free(groups
);
1901 hashmap_free(users
);
1902 hashmap_free(members
);
1903 hashmap_free(todo_uids
);
1904 hashmap_free(todo_gids
);
1906 free_database(database_user
, database_uid
);
1907 free_database(database_group
, database_gid
);
1911 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;