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"
45 typedef enum ItemType
{
70 static char *arg_root
= NULL
;
72 static const char conf_file_dirs
[] = CONF_DIRS_NULSTR("sysusers");
74 static Hashmap
*users
= NULL
, *groups
= NULL
;
75 static Hashmap
*todo_uids
= NULL
, *todo_gids
= NULL
;
76 static Hashmap
*members
= NULL
;
78 static Hashmap
*database_uid
= NULL
, *database_user
= NULL
;
79 static Hashmap
*database_gid
= NULL
, *database_group
= NULL
;
81 static uid_t search_uid
= UID_INVALID
;
82 static UidRange
*uid_range
= NULL
;
83 static unsigned n_uid_range
= 0;
85 static int load_user_database(void) {
86 _cleanup_fclose_
FILE *f
= NULL
;
87 const char *passwd_path
;
91 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
92 f
= fopen(passwd_path
, "re");
94 return errno
== ENOENT
? 0 : -errno
;
96 r
= hashmap_ensure_allocated(&database_user
, &string_hash_ops
);
100 r
= hashmap_ensure_allocated(&database_uid
, NULL
);
105 while ((pw
= fgetpwent(f
))) {
109 n
= strdup(pw
->pw_name
);
113 k
= hashmap_put(database_user
, n
, UID_TO_PTR(pw
->pw_uid
));
114 if (k
< 0 && k
!= -EEXIST
) {
119 q
= hashmap_put(database_uid
, UID_TO_PTR(pw
->pw_uid
), n
);
120 if (q
< 0 && q
!= -EEXIST
) {
131 if (!IN_SET(errno
, 0, ENOENT
))
137 static int load_group_database(void) {
138 _cleanup_fclose_
FILE *f
= NULL
;
139 const char *group_path
;
143 group_path
= prefix_roota(arg_root
, "/etc/group");
144 f
= fopen(group_path
, "re");
146 return errno
== ENOENT
? 0 : -errno
;
148 r
= hashmap_ensure_allocated(&database_group
, &string_hash_ops
);
152 r
= hashmap_ensure_allocated(&database_gid
, NULL
);
157 while ((gr
= fgetgrent(f
))) {
161 n
= strdup(gr
->gr_name
);
165 k
= hashmap_put(database_group
, n
, GID_TO_PTR(gr
->gr_gid
));
166 if (k
< 0 && k
!= -EEXIST
) {
171 q
= hashmap_put(database_gid
, GID_TO_PTR(gr
->gr_gid
), n
);
172 if (q
< 0 && q
!= -EEXIST
) {
183 if (!IN_SET(errno
, 0, ENOENT
))
189 static int make_backup(const char *target
, const char *x
) {
190 _cleanup_close_
int src
= -1;
191 _cleanup_fclose_
FILE *dst
= NULL
;
193 struct timespec ts
[2];
197 src
= open(x
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
199 if (errno
== ENOENT
) /* No backup necessary... */
205 if (fstat(src
, &st
) < 0)
208 r
= fopen_temporary_label(target
, x
, &dst
, &temp
);
212 r
= copy_bytes(src
, fileno(dst
), (uint64_t) -1, true);
216 /* Don't fail on chmod() or chown(). If it stays owned by us
217 * and/or unreadable by others, then it isn't too bad... */
219 backup
= strjoina(x
, "-");
221 /* Copy over the access mask */
222 if (fchmod(fileno(dst
), st
.st_mode
& 07777) < 0)
223 log_warning_errno(errno
, "Failed to change mode on %s: %m", backup
);
225 if (fchown(fileno(dst
), st
.st_uid
, st
.st_gid
)< 0)
226 log_warning_errno(errno
, "Failed to change ownership of %s: %m", backup
);
230 if (futimens(fileno(dst
), ts
) < 0)
231 log_warning_errno(errno
, "Failed to fix access and modification time of %s: %m", backup
);
233 if (rename(temp
, backup
) < 0)
243 static int putgrent_with_members(const struct group
*gr
, FILE *group
) {
249 a
= hashmap_get(members
, gr
->gr_name
);
251 _cleanup_strv_free_
char **l
= NULL
;
255 l
= strv_copy(gr
->gr_mem
);
260 if (strv_find(l
, *i
))
263 if (strv_extend(&l
, *i
) < 0)
279 if (putgrent(&t
, group
) != 0)
280 return errno
? -errno
: -EIO
;
287 if (putgrent(gr
, group
) != 0)
288 return errno
? -errno
: -EIO
;
293 static int putsgent_with_members(const struct sgrp
*sg
, FILE *gshadow
) {
299 a
= hashmap_get(members
, sg
->sg_namp
);
301 _cleanup_strv_free_
char **l
= NULL
;
305 l
= strv_copy(sg
->sg_mem
);
310 if (strv_find(l
, *i
))
313 if (strv_extend(&l
, *i
) < 0)
329 if (putsgent(&t
, gshadow
) != 0)
330 return errno
? -errno
: -EIO
;
337 if (putsgent(sg
, gshadow
) != 0)
338 return errno
? -errno
: -EIO
;
343 static int sync_rights(FILE *from
, FILE *to
) {
346 if (fstat(fileno(from
), &st
) < 0)
349 if (fchmod(fileno(to
), st
.st_mode
& 07777) < 0)
352 if (fchown(fileno(to
), st
.st_uid
, st
.st_gid
) < 0)
358 static int rename_and_apply_smack(const char *temp_path
, const char *dest_path
) {
360 if (rename(temp_path
, dest_path
) < 0)
363 #ifdef SMACK_RUN_LABEL
364 r
= mac_smack_apply(dest_path
, SMACK_ATTR_ACCESS
, SMACK_FLOOR_LABEL
);
371 static int write_files(void) {
373 _cleanup_fclose_
FILE *passwd
= NULL
, *group
= NULL
, *shadow
= NULL
, *gshadow
= NULL
;
374 _cleanup_free_
char *passwd_tmp
= NULL
, *group_tmp
= NULL
, *shadow_tmp
= NULL
, *gshadow_tmp
= NULL
;
375 const char *passwd_path
= NULL
, *group_path
= NULL
, *shadow_path
= NULL
, *gshadow_path
= NULL
;
376 bool group_changed
= false;
381 if (hashmap_size(todo_gids
) > 0 || hashmap_size(members
) > 0) {
382 _cleanup_fclose_
FILE *original
= NULL
;
384 /* First we update the actual group list file */
385 group_path
= prefix_roota(arg_root
, "/etc/group");
386 r
= fopen_temporary_label("/etc/group", group_path
, &group
, &group_tmp
);
390 original
= fopen(group_path
, "re");
394 r
= sync_rights(original
, group
);
399 while ((gr
= fgetgrent(original
))) {
400 /* Safety checks against name and GID
401 * collisions. Normally, this should
402 * be unnecessary, but given that we
403 * look at the entries anyway here,
404 * let's make an extra verification
405 * step that we don't generate
406 * duplicate entries. */
408 i
= hashmap_get(groups
, gr
->gr_name
);
409 if (i
&& i
->todo_group
) {
414 if (hashmap_contains(todo_gids
, GID_TO_PTR(gr
->gr_gid
))) {
419 r
= putgrent_with_members(gr
, group
);
423 group_changed
= true;
427 if (!IN_SET(errno
, 0, ENOENT
)) {
432 } else if (errno
!= ENOENT
) {
435 } else if (fchmod(fileno(group
), 0644) < 0) {
440 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
444 .gr_passwd
= (char*) "x",
447 r
= putgrent_with_members(&n
, group
);
451 group_changed
= true;
454 r
= fflush_and_check(group
);
463 /* OK, now also update the shadow file for the group list */
464 gshadow_path
= prefix_roota(arg_root
, "/etc/gshadow");
465 r
= fopen_temporary_label("/etc/gshadow", gshadow_path
, &gshadow
, &gshadow_tmp
);
469 original
= fopen(gshadow_path
, "re");
473 r
= sync_rights(original
, gshadow
);
478 while ((sg
= fgetsgent(original
))) {
480 i
= hashmap_get(groups
, sg
->sg_namp
);
481 if (i
&& i
->todo_group
) {
486 r
= putsgent_with_members(sg
, gshadow
);
490 group_changed
= true;
494 if (!IN_SET(errno
, 0, ENOENT
)) {
499 } else if (errno
!= ENOENT
) {
502 } else if (fchmod(fileno(gshadow
), 0000) < 0) {
507 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
510 .sg_passwd
= (char*) "!!",
513 r
= putsgent_with_members(&n
, gshadow
);
517 group_changed
= true;
520 r
= fflush_and_check(gshadow
);
525 if (hashmap_size(todo_uids
) > 0) {
526 _cleanup_fclose_
FILE *original
= NULL
;
529 /* First we update the user database itself */
530 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
531 r
= fopen_temporary_label("/etc/passwd", passwd_path
, &passwd
, &passwd_tmp
);
535 original
= fopen(passwd_path
, "re");
539 r
= sync_rights(original
, passwd
);
544 while ((pw
= fgetpwent(original
))) {
546 i
= hashmap_get(users
, pw
->pw_name
);
547 if (i
&& i
->todo_user
) {
552 if (hashmap_contains(todo_uids
, UID_TO_PTR(pw
->pw_uid
))) {
558 if (putpwent(pw
, passwd
) < 0) {
559 r
= errno
? -errno
: -EIO
;
565 if (!IN_SET(errno
, 0, ENOENT
)) {
570 } else if (errno
!= ENOENT
) {
573 } else if (fchmod(fileno(passwd
), 0644) < 0) {
578 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
583 .pw_gecos
= i
->description
,
585 /* "x" means the password is stored in
587 .pw_passwd
= (char*) "x",
589 /* We default to the root directory as home */
590 .pw_dir
= i
->home
? i
->home
: (char*) "/",
592 /* Initialize the shell to nologin,
593 * with one exception: for root we
594 * patch in something special */
595 .pw_shell
= i
->uid
== 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
599 if (putpwent(&n
, passwd
) != 0) {
600 r
= errno
? -errno
: -EIO
;
605 r
= fflush_and_check(passwd
);
614 /* The we update the shadow database */
615 shadow_path
= prefix_roota(arg_root
, "/etc/shadow");
616 r
= fopen_temporary_label("/etc/shadow", shadow_path
, &shadow
, &shadow_tmp
);
620 lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
622 original
= fopen(shadow_path
, "re");
626 r
= sync_rights(original
, shadow
);
631 while ((sp
= fgetspent(original
))) {
633 i
= hashmap_get(users
, sp
->sp_namp
);
634 if (i
&& i
->todo_user
) {
635 /* we will update the existing entry */
636 sp
->sp_lstchg
= lstchg
;
638 /* only the /etc/shadow stage is left, so we can
639 * safely remove the item from the todo set */
640 i
->todo_user
= false;
641 hashmap_remove(todo_uids
, UID_TO_PTR(i
->uid
));
645 if (putspent(sp
, shadow
) < 0) {
646 r
= errno
? -errno
: -EIO
;
652 if (!IN_SET(errno
, 0, ENOENT
)) {
656 } else if (errno
!= ENOENT
) {
659 } else if (fchmod(fileno(shadow
), 0000) < 0) {
664 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
667 .sp_pwdp
= (char*) "!!",
674 .sp_flag
= (unsigned long) -1, /* this appears to be what everybody does ... */
678 if (putspent(&n
, shadow
) != 0) {
679 r
= errno
? -errno
: -EIO
;
684 r
= fflush_and_check(shadow
);
689 /* Make a backup of the old files */
692 r
= make_backup("/etc/group", group_path
);
697 r
= make_backup("/etc/gshadow", gshadow_path
);
704 r
= make_backup("/etc/passwd", passwd_path
);
709 r
= make_backup("/etc/shadow", shadow_path
);
714 /* And make the new files count */
717 r
= rename_and_apply_smack(group_tmp
, group_path
);
721 group_tmp
= mfree(group_tmp
);
724 r
= rename_and_apply_smack(gshadow_tmp
, gshadow_path
);
728 gshadow_tmp
= mfree(gshadow_tmp
);
733 r
= rename_and_apply_smack(passwd_tmp
, passwd_path
);
737 passwd_tmp
= mfree(passwd_tmp
);
740 r
= rename_and_apply_smack(shadow_tmp
, shadow_path
);
744 shadow_tmp
= mfree(shadow_tmp
);
762 static int uid_is_ok(uid_t uid
, const char *name
) {
768 /* Let's see if we already have assigned the UID a second time */
769 if (hashmap_get(todo_uids
, UID_TO_PTR(uid
)))
772 /* Try to avoid using uids that are already used by a group
773 * that doesn't have the same name as our new user. */
774 i
= hashmap_get(todo_gids
, GID_TO_PTR(uid
));
775 if (i
&& !streq(i
->name
, name
))
778 /* Let's check the files directly */
779 if (hashmap_contains(database_uid
, UID_TO_PTR(uid
)))
782 n
= hashmap_get(database_gid
, GID_TO_PTR(uid
));
783 if (n
&& !streq(n
, name
))
786 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
792 if (!IN_SET(errno
, 0, ENOENT
))
796 g
= getgrgid((gid_t
) uid
);
798 if (!streq(g
->gr_name
, name
))
800 } else if (!IN_SET(errno
, 0, ENOENT
))
807 static int root_stat(const char *p
, struct stat
*st
) {
810 fix
= prefix_roota(arg_root
, p
);
811 if (stat(fix
, st
) < 0)
817 static int read_id_from_file(Item
*i
, uid_t
*_uid
, gid_t
*_gid
) {
819 bool found_uid
= false, found_gid
= false;
825 /* First, try to get the gid directly */
826 if (_gid
&& i
->gid_path
&& root_stat(i
->gid_path
, &st
) >= 0) {
831 /* Then, try to get the uid directly */
832 if ((_uid
|| (_gid
&& !found_gid
))
834 && root_stat(i
->uid_path
, &st
) >= 0) {
839 /* If we need the gid, but had no success yet, also derive it from the uid path */
840 if (_gid
&& !found_gid
) {
846 /* If that didn't work yet, then let's reuse the gid as uid */
847 if (_uid
&& !found_uid
&& i
->gid_path
) {
852 } else if (root_stat(i
->gid_path
, &st
) >= 0) {
853 uid
= (uid_t
) st
.st_gid
;
875 static int add_user(Item
*i
) {
881 /* Check the database directly */
882 z
= hashmap_get(database_user
, i
->name
);
884 log_debug("User %s already exists.", i
->name
);
885 i
->uid
= PTR_TO_UID(z
);
895 p
= getpwnam(i
->name
);
897 log_debug("User %s already exists.", i
->name
);
901 r
= free_and_strdup(&i
->description
, p
->pw_gecos
);
907 if (!IN_SET(errno
, 0, ENOENT
))
908 return log_error_errno(errno
, "Failed to check if user %s already exists: %m", i
->name
);
911 /* Try to use the suggested numeric uid */
913 r
= uid_is_ok(i
->uid
, i
->name
);
915 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
917 log_debug("Suggested user ID " UID_FMT
" for %s already used.", i
->uid
, i
->name
);
922 /* If that didn't work, try to read it from the specified path */
926 if (read_id_from_file(i
, &c
, NULL
) > 0) {
928 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
929 log_debug("User ID " UID_FMT
" of file not suitable for %s.", c
, i
->name
);
931 r
= uid_is_ok(c
, i
->name
);
933 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
938 log_debug("User ID " UID_FMT
" of file for %s is already used.", c
, i
->name
);
943 /* Otherwise try to reuse the group ID */
944 if (!i
->uid_set
&& i
->gid_set
) {
945 r
= uid_is_ok((uid_t
) i
->gid
, i
->name
);
947 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
949 i
->uid
= (uid_t
) i
->gid
;
954 /* And if that didn't work either, let's try to find a free one */
957 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
959 log_error("No free user ID available for %s.", i
->name
);
963 r
= uid_is_ok(search_uid
, i
->name
);
965 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
974 r
= hashmap_ensure_allocated(&todo_uids
, NULL
);
978 r
= hashmap_put(todo_uids
, UID_TO_PTR(i
->uid
), i
);
983 log_info("Creating user %s (%s) with uid " UID_FMT
" and gid " GID_FMT
".", i
->name
, strna(i
->description
), i
->uid
, i
->gid
);
988 static int gid_is_ok(gid_t gid
) {
992 if (hashmap_get(todo_gids
, GID_TO_PTR(gid
)))
995 /* Avoid reusing gids that are already used by a different user */
996 if (hashmap_get(todo_uids
, UID_TO_PTR(gid
)))
999 if (hashmap_contains(database_gid
, GID_TO_PTR(gid
)))
1002 if (hashmap_contains(database_uid
, UID_TO_PTR(gid
)))
1010 if (!IN_SET(errno
, 0, ENOENT
))
1014 p
= getpwuid((uid_t
) gid
);
1017 if (!IN_SET(errno
, 0, ENOENT
))
1024 static int add_group(Item
*i
) {
1030 /* Check the database directly */
1031 z
= hashmap_get(database_group
, i
->name
);
1033 log_debug("Group %s already exists.", i
->name
);
1034 i
->gid
= PTR_TO_GID(z
);
1039 /* Also check NSS */
1044 g
= getgrnam(i
->name
);
1046 log_debug("Group %s already exists.", i
->name
);
1051 if (!IN_SET(errno
, 0, ENOENT
))
1052 return log_error_errno(errno
, "Failed to check if group %s already exists: %m", i
->name
);
1055 /* Try to use the suggested numeric gid */
1057 r
= gid_is_ok(i
->gid
);
1059 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1061 log_debug("Suggested group ID " GID_FMT
" for %s already used.", i
->gid
, i
->name
);
1066 /* Try to reuse the numeric uid, if there's one */
1067 if (!i
->gid_set
&& i
->uid_set
) {
1068 r
= gid_is_ok((gid_t
) i
->uid
);
1070 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1072 i
->gid
= (gid_t
) i
->uid
;
1077 /* If that didn't work, try to read it from the specified path */
1081 if (read_id_from_file(i
, NULL
, &c
) > 0) {
1083 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1084 log_debug("Group ID " GID_FMT
" of file not suitable for %s.", c
, i
->name
);
1088 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1093 log_debug("Group ID " GID_FMT
" of file for %s already used.", c
, i
->name
);
1098 /* And if that didn't work either, let's try to find a free one */
1101 /* We look for new GIDs in the UID pool! */
1102 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1104 log_error("No free group ID available for %s.", i
->name
);
1108 r
= gid_is_ok(search_uid
);
1110 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1116 i
->gid
= search_uid
;
1119 r
= hashmap_ensure_allocated(&todo_gids
, NULL
);
1123 r
= hashmap_put(todo_gids
, GID_TO_PTR(i
->gid
), i
);
1127 i
->todo_group
= true;
1128 log_info("Creating group %s with gid " GID_FMT
".", i
->name
, i
->gid
);
1133 static int process_item(Item
*i
) {
1150 j
= hashmap_get(users
, i
->name
);
1152 /* There's already user to be created for this
1153 * name, let's process that in one step */
1161 r
= free_and_strdup(&j
->gid_path
, i
->gid_path
);
1169 return add_group(i
);
1173 assert_not_reached("Unknown item type");
1177 static void item_free(Item
*i
) {
1185 free(i
->description
);
1189 DEFINE_TRIVIAL_CLEANUP_FUNC(Item
*, item_free
);
1191 static int add_implicit(void) {
1196 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1198 HASHMAP_FOREACH_KEY(l
, g
, members
, iterator
) {
1202 i
= hashmap_get(groups
, g
);
1204 _cleanup_(item_freep
) Item
*j
= NULL
;
1206 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1214 j
->type
= ADD_GROUP
;
1215 j
->name
= strdup(g
);
1219 r
= hashmap_put(groups
, j
->name
, j
);
1223 log_debug("Adding implicit group '%s' due to m line", j
->name
);
1227 STRV_FOREACH(m
, l
) {
1229 i
= hashmap_get(users
, *m
);
1231 _cleanup_(item_freep
) Item
*j
= NULL
;
1233 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1242 j
->name
= strdup(*m
);
1246 r
= hashmap_put(users
, j
->name
, j
);
1250 log_debug("Adding implicit user '%s' due to m line", j
->name
);
1259 static bool item_equal(Item
*a
, Item
*b
) {
1263 if (a
->type
!= b
->type
)
1266 if (!streq_ptr(a
->name
, b
->name
))
1269 if (!streq_ptr(a
->uid_path
, b
->uid_path
))
1272 if (!streq_ptr(a
->gid_path
, b
->gid_path
))
1275 if (!streq_ptr(a
->description
, b
->description
))
1278 if (a
->uid_set
!= b
->uid_set
)
1281 if (a
->uid_set
&& a
->uid
!= b
->uid
)
1284 if (a
->gid_set
!= b
->gid_set
)
1287 if (a
->gid_set
&& a
->gid
!= b
->gid
)
1290 if (!streq_ptr(a
->home
, b
->home
))
1296 static bool valid_user_group_name(const char *u
) {
1303 if (!(u
[0] >= 'a' && u
[0] <= 'z') &&
1304 !(u
[0] >= 'A' && u
[0] <= 'Z') &&
1308 for (i
= u
+1; *i
; i
++) {
1309 if (!(*i
>= 'a' && *i
<= 'z') &&
1310 !(*i
>= 'A' && *i
<= 'Z') &&
1311 !(*i
>= '0' && *i
<= '9') &&
1317 sz
= sysconf(_SC_LOGIN_NAME_MAX
);
1320 if ((size_t) (i
-u
) > (size_t) sz
)
1323 if ((size_t) (i
-u
) > UT_NAMESIZE
- 1)
1329 static bool valid_gecos(const char *d
) {
1334 if (!utf8_is_valid(d
))
1337 if (string_has_cc(d
, NULL
))
1340 /* Colons are used as field separators, and hence not OK */
1347 static bool valid_home(const char *p
) {
1352 if (!utf8_is_valid(p
))
1355 if (string_has_cc(p
, NULL
))
1358 if (!path_is_absolute(p
))
1361 if (!path_is_safe(p
))
1364 /* Colons are used as field separators, and hence not OK */
1371 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1373 static const Specifier specifier_table
[] = {
1374 { 'm', specifier_machine_id
, NULL
},
1375 { 'b', specifier_boot_id
, NULL
},
1376 { 'H', specifier_host_name
, NULL
},
1377 { 'v', specifier_kernel_release
, NULL
},
1381 _cleanup_free_
char *action
= NULL
, *name
= NULL
, *id
= NULL
, *resolved_name
= NULL
, *resolved_id
= NULL
, *description
= NULL
, *home
= NULL
;
1382 _cleanup_(item_freep
) Item
*i
= NULL
;
1394 r
= extract_many_words(&p
, NULL
, EXTRACT_QUOTES
, &action
, &name
, &id
, &description
, &home
, NULL
);
1396 log_error("[%s:%u] Syntax error.", fname
, line
);
1400 log_error("[%s:%u] Missing action and name columns.", fname
, line
);
1404 log_error("[%s:%u] Trailing garbage.", fname
, line
);
1409 if (strlen(action
) != 1) {
1410 log_error("[%s:%u] Unknown modifier '%s'", fname
, line
, action
);
1414 if (!IN_SET(action
[0], ADD_USER
, ADD_GROUP
, ADD_MEMBER
, ADD_RANGE
)) {
1415 log_error("[%s:%u] Unknown command command type '%c'.", fname
, line
, action
[0]);
1420 if (isempty(name
) || streq(name
, "-"))
1424 r
= specifier_printf(name
, specifier_table
, NULL
, &resolved_name
);
1426 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1430 if (!valid_user_group_name(resolved_name
)) {
1431 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_name
);
1437 if (isempty(id
) || streq(id
, "-"))
1441 r
= specifier_printf(id
, specifier_table
, NULL
, &resolved_id
);
1443 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1448 /* Verify description */
1449 if (isempty(description
) || streq(description
, "-"))
1450 description
= mfree(description
);
1453 if (!valid_gecos(description
)) {
1454 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname
, line
, description
);
1460 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)
1784 r
= parse_path_argument_and_warn(optarg
, true, &arg_root
);
1793 assert_not_reached("Unhandled option");
1799 int main(int argc
, char *argv
[]) {
1801 _cleanup_close_
int lock
= -1;
1807 r
= parse_argv(argc
, argv
);
1811 log_set_target(LOG_TARGET_AUTO
);
1812 log_parse_environment();
1817 r
= mac_selinux_init(NULL
);
1819 log_error_errno(r
, "SELinux setup failed: %m");
1823 if (optind
< argc
) {
1826 for (j
= optind
; j
< argc
; j
++) {
1827 k
= read_config_file(argv
[j
], false);
1828 if (k
< 0 && r
== 0)
1832 _cleanup_strv_free_
char **files
= NULL
;
1835 r
= conf_files_list_nulstr(&files
, ".conf", arg_root
, conf_file_dirs
);
1837 log_error_errno(r
, "Failed to enumerate sysusers.d files: %m");
1841 STRV_FOREACH(f
, files
) {
1842 k
= read_config_file(*f
, true);
1843 if (k
< 0 && r
== 0)
1849 /* Default to default range of 1..SYSTEMD_UID_MAX */
1850 r
= uid_range_add(&uid_range
, &n_uid_range
, 1, SYSTEM_UID_MAX
);
1861 lock
= take_password_lock(arg_root
);
1863 log_error_errno(lock
, "Failed to take lock: %m");
1867 r
= load_user_database();
1869 log_error_errno(r
, "Failed to load user database: %m");
1873 r
= load_group_database();
1875 log_error_errno(r
, "Failed to read group database: %m");
1879 HASHMAP_FOREACH(i
, groups
, iterator
)
1882 HASHMAP_FOREACH(i
, users
, iterator
)
1887 log_error_errno(r
, "Failed to write files: %m");
1890 while ((i
= hashmap_steal_first(groups
)))
1893 while ((i
= hashmap_steal_first(users
)))
1896 while ((n
= hashmap_first_key(members
))) {
1897 strv_free(hashmap_steal_first(members
));
1901 hashmap_free(groups
);
1902 hashmap_free(users
);
1903 hashmap_free(members
);
1904 hashmap_free(todo_uids
);
1905 hashmap_free(todo_gids
);
1907 free_database(database_user
, database_uid
);
1908 free_database(database_group
, database_gid
);
1912 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;