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 "alloc-util.h"
30 #include "conf-files.h"
34 #include "fileio-label.h"
35 #include "formats-util.h"
37 #include "path-util.h"
38 #include "selinux-util.h"
39 #include "smack-util.h"
40 #include "specifier.h"
41 #include "string-util.h"
43 #include "uid-range.h"
44 #include "user-util.h"
48 typedef enum ItemType
{
73 static char *arg_root
= NULL
;
75 static const char conf_file_dirs
[] = CONF_PATHS_NULSTR("sysusers.d");
77 static Hashmap
*users
= NULL
, *groups
= NULL
;
78 static Hashmap
*todo_uids
= NULL
, *todo_gids
= NULL
;
79 static Hashmap
*members
= NULL
;
81 static Hashmap
*database_uid
= NULL
, *database_user
= NULL
;
82 static Hashmap
*database_gid
= NULL
, *database_group
= NULL
;
84 static uid_t search_uid
= UID_INVALID
;
85 static UidRange
*uid_range
= NULL
;
86 static unsigned n_uid_range
= 0;
88 static int load_user_database(void) {
89 _cleanup_fclose_
FILE *f
= NULL
;
90 const char *passwd_path
;
94 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
95 f
= fopen(passwd_path
, "re");
97 return errno
== ENOENT
? 0 : -errno
;
99 r
= hashmap_ensure_allocated(&database_user
, &string_hash_ops
);
103 r
= hashmap_ensure_allocated(&database_uid
, NULL
);
108 while ((pw
= fgetpwent(f
))) {
112 n
= strdup(pw
->pw_name
);
116 k
= hashmap_put(database_user
, n
, UID_TO_PTR(pw
->pw_uid
));
117 if (k
< 0 && k
!= -EEXIST
) {
122 q
= hashmap_put(database_uid
, UID_TO_PTR(pw
->pw_uid
), n
);
123 if (q
< 0 && q
!= -EEXIST
) {
134 if (!IN_SET(errno
, 0, ENOENT
))
140 static int load_group_database(void) {
141 _cleanup_fclose_
FILE *f
= NULL
;
142 const char *group_path
;
146 group_path
= prefix_roota(arg_root
, "/etc/group");
147 f
= fopen(group_path
, "re");
149 return errno
== ENOENT
? 0 : -errno
;
151 r
= hashmap_ensure_allocated(&database_group
, &string_hash_ops
);
155 r
= hashmap_ensure_allocated(&database_gid
, NULL
);
160 while ((gr
= fgetgrent(f
))) {
164 n
= strdup(gr
->gr_name
);
168 k
= hashmap_put(database_group
, n
, GID_TO_PTR(gr
->gr_gid
));
169 if (k
< 0 && k
!= -EEXIST
) {
174 q
= hashmap_put(database_gid
, GID_TO_PTR(gr
->gr_gid
), n
);
175 if (q
< 0 && q
!= -EEXIST
) {
186 if (!IN_SET(errno
, 0, ENOENT
))
192 static int make_backup(const char *target
, const char *x
) {
193 _cleanup_close_
int src
= -1;
194 _cleanup_fclose_
FILE *dst
= NULL
;
196 struct timespec ts
[2];
200 src
= open(x
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
202 if (errno
== ENOENT
) /* No backup necessary... */
208 if (fstat(src
, &st
) < 0)
211 r
= fopen_temporary_label(target
, x
, &dst
, &temp
);
215 r
= copy_bytes(src
, fileno(dst
), (uint64_t) -1, true);
219 /* Don't fail on chmod() or chown(). If it stays owned by us
220 * and/or unreadable by others, then it isn't too bad... */
222 backup
= strjoina(x
, "-");
224 /* Copy over the access mask */
225 if (fchmod(fileno(dst
), st
.st_mode
& 07777) < 0)
226 log_warning_errno(errno
, "Failed to change mode on %s: %m", backup
);
228 if (fchown(fileno(dst
), st
.st_uid
, st
.st_gid
)< 0)
229 log_warning_errno(errno
, "Failed to change ownership of %s: %m", backup
);
233 if (futimens(fileno(dst
), ts
) < 0)
234 log_warning_errno(errno
, "Failed to fix access and modification time of %s: %m", backup
);
236 if (rename(temp
, backup
) < 0)
246 static int putgrent_with_members(const struct group
*gr
, FILE *group
) {
252 a
= hashmap_get(members
, gr
->gr_name
);
254 _cleanup_strv_free_
char **l
= NULL
;
258 l
= strv_copy(gr
->gr_mem
);
263 if (strv_find(l
, *i
))
266 if (strv_extend(&l
, *i
) < 0)
282 if (putgrent(&t
, group
) != 0)
283 return errno
> 0 ? -errno
: -EIO
;
290 if (putgrent(gr
, group
) != 0)
291 return errno
> 0 ? -errno
: -EIO
;
296 static int putsgent_with_members(const struct sgrp
*sg
, FILE *gshadow
) {
302 a
= hashmap_get(members
, sg
->sg_namp
);
304 _cleanup_strv_free_
char **l
= NULL
;
308 l
= strv_copy(sg
->sg_mem
);
313 if (strv_find(l
, *i
))
316 if (strv_extend(&l
, *i
) < 0)
332 if (putsgent(&t
, gshadow
) != 0)
333 return errno
> 0 ? -errno
: -EIO
;
340 if (putsgent(sg
, gshadow
) != 0)
341 return errno
> 0 ? -errno
: -EIO
;
346 static int sync_rights(FILE *from
, FILE *to
) {
349 if (fstat(fileno(from
), &st
) < 0)
352 if (fchmod(fileno(to
), st
.st_mode
& 07777) < 0)
355 if (fchown(fileno(to
), st
.st_uid
, st
.st_gid
) < 0)
361 static int rename_and_apply_smack(const char *temp_path
, const char *dest_path
) {
363 if (rename(temp_path
, dest_path
) < 0)
366 #ifdef SMACK_RUN_LABEL
367 r
= mac_smack_apply(dest_path
, SMACK_ATTR_ACCESS
, SMACK_FLOOR_LABEL
);
374 static int write_files(void) {
376 _cleanup_fclose_
FILE *passwd
= NULL
, *group
= NULL
, *shadow
= NULL
, *gshadow
= NULL
;
377 _cleanup_free_
char *passwd_tmp
= NULL
, *group_tmp
= NULL
, *shadow_tmp
= NULL
, *gshadow_tmp
= NULL
;
378 const char *passwd_path
= NULL
, *group_path
= NULL
, *shadow_path
= NULL
, *gshadow_path
= NULL
;
379 bool group_changed
= false;
384 if (hashmap_size(todo_gids
) > 0 || hashmap_size(members
) > 0) {
385 _cleanup_fclose_
FILE *original
= NULL
;
387 /* First we update the actual group list file */
388 group_path
= prefix_roota(arg_root
, "/etc/group");
389 r
= fopen_temporary_label("/etc/group", group_path
, &group
, &group_tmp
);
393 original
= fopen(group_path
, "re");
397 r
= sync_rights(original
, group
);
402 while ((gr
= fgetgrent(original
))) {
403 /* Safety checks against name and GID
404 * collisions. Normally, this should
405 * be unnecessary, but given that we
406 * look at the entries anyway here,
407 * let's make an extra verification
408 * step that we don't generate
409 * duplicate entries. */
411 i
= hashmap_get(groups
, gr
->gr_name
);
412 if (i
&& i
->todo_group
) {
413 log_error("%s: Group \"%s\" already exists.", group_path
, gr
->gr_name
);
418 if (hashmap_contains(todo_gids
, GID_TO_PTR(gr
->gr_gid
))) {
419 log_error("%s: Detected collision for GID %d.", group_path
, gr
->gr_gid
);
424 r
= putgrent_with_members(gr
, group
);
428 group_changed
= true;
432 if (!IN_SET(errno
, 0, ENOENT
)) {
437 } else if (errno
!= ENOENT
) {
440 } else if (fchmod(fileno(group
), 0644) < 0) {
445 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
449 .gr_passwd
= (char*) "x",
452 r
= putgrent_with_members(&n
, group
);
456 group_changed
= true;
459 r
= fflush_and_check(group
);
468 /* OK, now also update the shadow file for the group list */
469 gshadow_path
= prefix_roota(arg_root
, "/etc/gshadow");
470 r
= fopen_temporary_label("/etc/gshadow", gshadow_path
, &gshadow
, &gshadow_tmp
);
474 original
= fopen(gshadow_path
, "re");
478 r
= sync_rights(original
, gshadow
);
483 while ((sg
= fgetsgent(original
))) {
485 i
= hashmap_get(groups
, sg
->sg_namp
);
486 if (i
&& i
->todo_group
) {
487 log_error("%s: Group \"%s\" already exists.", gshadow_path
, sg
->sg_namp
);
492 r
= putsgent_with_members(sg
, gshadow
);
496 group_changed
= true;
500 if (!IN_SET(errno
, 0, ENOENT
)) {
505 } else if (errno
!= ENOENT
) {
508 } else if (fchmod(fileno(gshadow
), 0000) < 0) {
513 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
516 .sg_passwd
= (char*) "!!",
519 r
= putsgent_with_members(&n
, gshadow
);
523 group_changed
= true;
526 r
= fflush_and_check(gshadow
);
531 if (hashmap_size(todo_uids
) > 0) {
532 _cleanup_fclose_
FILE *original
= NULL
;
535 /* First we update the user database itself */
536 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
537 r
= fopen_temporary_label("/etc/passwd", passwd_path
, &passwd
, &passwd_tmp
);
541 original
= fopen(passwd_path
, "re");
545 r
= sync_rights(original
, passwd
);
550 while ((pw
= fgetpwent(original
))) {
552 i
= hashmap_get(users
, pw
->pw_name
);
553 if (i
&& i
->todo_user
) {
554 log_error("%s: User \"%s\" already exists.", passwd_path
, pw
->pw_name
);
559 if (hashmap_contains(todo_uids
, UID_TO_PTR(pw
->pw_uid
))) {
560 log_error("%s: Detected collision for UID %d.", passwd_path
, pw
->pw_uid
);
566 if (putpwent(pw
, passwd
) < 0) {
567 r
= errno
? -errno
: -EIO
;
573 if (!IN_SET(errno
, 0, ENOENT
)) {
578 } else if (errno
!= ENOENT
) {
581 } else if (fchmod(fileno(passwd
), 0644) < 0) {
586 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
591 .pw_gecos
= i
->description
,
593 /* "x" means the password is stored in
595 .pw_passwd
= (char*) "x",
597 /* We default to the root directory as home */
598 .pw_dir
= i
->home
? i
->home
: (char*) "/",
600 /* Initialize the shell to nologin,
601 * with one exception: for root we
602 * patch in something special */
603 .pw_shell
= i
->uid
== 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
607 if (putpwent(&n
, passwd
) != 0) {
608 r
= errno
? -errno
: -EIO
;
613 r
= fflush_and_check(passwd
);
622 /* The we update the shadow database */
623 shadow_path
= prefix_roota(arg_root
, "/etc/shadow");
624 r
= fopen_temporary_label("/etc/shadow", shadow_path
, &shadow
, &shadow_tmp
);
628 lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
630 original
= fopen(shadow_path
, "re");
634 r
= sync_rights(original
, shadow
);
639 while ((sp
= fgetspent(original
))) {
641 i
= hashmap_get(users
, sp
->sp_namp
);
642 if (i
&& i
->todo_user
) {
643 /* we will update the existing entry */
644 sp
->sp_lstchg
= lstchg
;
646 /* only the /etc/shadow stage is left, so we can
647 * safely remove the item from the todo set */
648 i
->todo_user
= false;
649 hashmap_remove(todo_uids
, UID_TO_PTR(i
->uid
));
653 if (putspent(sp
, shadow
) < 0) {
654 r
= errno
? -errno
: -EIO
;
660 if (!IN_SET(errno
, 0, ENOENT
)) {
664 } else if (errno
!= ENOENT
) {
667 } else if (fchmod(fileno(shadow
), 0000) < 0) {
672 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
675 .sp_pwdp
= (char*) "!!",
682 .sp_flag
= (unsigned long) -1, /* this appears to be what everybody does ... */
686 if (putspent(&n
, shadow
) != 0) {
687 r
= errno
? -errno
: -EIO
;
692 r
= fflush_and_check(shadow
);
697 /* Make a backup of the old files */
700 r
= make_backup("/etc/group", group_path
);
705 r
= make_backup("/etc/gshadow", gshadow_path
);
712 r
= make_backup("/etc/passwd", passwd_path
);
717 r
= make_backup("/etc/shadow", shadow_path
);
722 /* And make the new files count */
725 r
= rename_and_apply_smack(group_tmp
, group_path
);
729 group_tmp
= mfree(group_tmp
);
732 r
= rename_and_apply_smack(gshadow_tmp
, gshadow_path
);
736 gshadow_tmp
= mfree(gshadow_tmp
);
741 r
= rename_and_apply_smack(passwd_tmp
, passwd_path
);
745 passwd_tmp
= mfree(passwd_tmp
);
748 r
= rename_and_apply_smack(shadow_tmp
, shadow_path
);
752 shadow_tmp
= mfree(shadow_tmp
);
770 static int uid_is_ok(uid_t uid
, const char *name
) {
776 /* Let's see if we already have assigned the UID a second time */
777 if (hashmap_get(todo_uids
, UID_TO_PTR(uid
)))
780 /* Try to avoid using uids that are already used by a group
781 * that doesn't have the same name as our new user. */
782 i
= hashmap_get(todo_gids
, GID_TO_PTR(uid
));
783 if (i
&& !streq(i
->name
, name
))
786 /* Let's check the files directly */
787 if (hashmap_contains(database_uid
, UID_TO_PTR(uid
)))
790 n
= hashmap_get(database_gid
, GID_TO_PTR(uid
));
791 if (n
&& !streq(n
, name
))
794 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
800 if (!IN_SET(errno
, 0, ENOENT
))
804 g
= getgrgid((gid_t
) uid
);
806 if (!streq(g
->gr_name
, name
))
808 } else if (!IN_SET(errno
, 0, ENOENT
))
815 static int root_stat(const char *p
, struct stat
*st
) {
818 fix
= prefix_roota(arg_root
, p
);
819 if (stat(fix
, st
) < 0)
825 static int read_id_from_file(Item
*i
, uid_t
*_uid
, gid_t
*_gid
) {
827 bool found_uid
= false, found_gid
= false;
833 /* First, try to get the gid directly */
834 if (_gid
&& i
->gid_path
&& root_stat(i
->gid_path
, &st
) >= 0) {
839 /* Then, try to get the uid directly */
840 if ((_uid
|| (_gid
&& !found_gid
))
842 && root_stat(i
->uid_path
, &st
) >= 0) {
847 /* If we need the gid, but had no success yet, also derive it from the uid path */
848 if (_gid
&& !found_gid
) {
854 /* If that didn't work yet, then let's reuse the gid as uid */
855 if (_uid
&& !found_uid
&& i
->gid_path
) {
860 } else if (root_stat(i
->gid_path
, &st
) >= 0) {
861 uid
= (uid_t
) st
.st_gid
;
883 static int add_user(Item
*i
) {
889 /* Check the database directly */
890 z
= hashmap_get(database_user
, i
->name
);
892 log_debug("User %s already exists.", i
->name
);
893 i
->uid
= PTR_TO_UID(z
);
903 p
= getpwnam(i
->name
);
905 log_debug("User %s already exists.", i
->name
);
909 r
= free_and_strdup(&i
->description
, p
->pw_gecos
);
915 if (!IN_SET(errno
, 0, ENOENT
))
916 return log_error_errno(errno
, "Failed to check if user %s already exists: %m", i
->name
);
919 /* Try to use the suggested numeric uid */
921 r
= uid_is_ok(i
->uid
, i
->name
);
923 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
925 log_debug("Suggested user ID " UID_FMT
" for %s already used.", i
->uid
, i
->name
);
930 /* If that didn't work, try to read it from the specified path */
934 if (read_id_from_file(i
, &c
, NULL
) > 0) {
936 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
937 log_debug("User ID " UID_FMT
" of file not suitable for %s.", c
, i
->name
);
939 r
= uid_is_ok(c
, i
->name
);
941 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
946 log_debug("User ID " UID_FMT
" of file for %s is already used.", c
, i
->name
);
951 /* Otherwise, try to reuse the group ID */
952 if (!i
->uid_set
&& i
->gid_set
) {
953 r
= uid_is_ok((uid_t
) i
->gid
, i
->name
);
955 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
957 i
->uid
= (uid_t
) i
->gid
;
962 /* And if that didn't work either, let's try to find a free one */
965 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
967 log_error("No free user ID available for %s.", i
->name
);
971 r
= uid_is_ok(search_uid
, i
->name
);
973 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
982 r
= hashmap_ensure_allocated(&todo_uids
, NULL
);
986 r
= hashmap_put(todo_uids
, UID_TO_PTR(i
->uid
), i
);
991 log_info("Creating user %s (%s) with uid " UID_FMT
" and gid " GID_FMT
".", i
->name
, strna(i
->description
), i
->uid
, i
->gid
);
996 static int gid_is_ok(gid_t gid
) {
1000 if (hashmap_get(todo_gids
, GID_TO_PTR(gid
)))
1003 /* Avoid reusing gids that are already used by a different user */
1004 if (hashmap_get(todo_uids
, UID_TO_PTR(gid
)))
1007 if (hashmap_contains(database_gid
, GID_TO_PTR(gid
)))
1010 if (hashmap_contains(database_uid
, UID_TO_PTR(gid
)))
1018 if (!IN_SET(errno
, 0, ENOENT
))
1022 p
= getpwuid((uid_t
) gid
);
1025 if (!IN_SET(errno
, 0, ENOENT
))
1032 static int add_group(Item
*i
) {
1038 /* Check the database directly */
1039 z
= hashmap_get(database_group
, i
->name
);
1041 log_debug("Group %s already exists.", i
->name
);
1042 i
->gid
= PTR_TO_GID(z
);
1047 /* Also check NSS */
1052 g
= getgrnam(i
->name
);
1054 log_debug("Group %s already exists.", i
->name
);
1059 if (!IN_SET(errno
, 0, ENOENT
))
1060 return log_error_errno(errno
, "Failed to check if group %s already exists: %m", i
->name
);
1063 /* Try to use the suggested numeric gid */
1065 r
= gid_is_ok(i
->gid
);
1067 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1069 log_debug("Suggested group ID " GID_FMT
" for %s already used.", i
->gid
, i
->name
);
1074 /* Try to reuse the numeric uid, if there's one */
1075 if (!i
->gid_set
&& i
->uid_set
) {
1076 r
= gid_is_ok((gid_t
) i
->uid
);
1078 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1080 i
->gid
= (gid_t
) i
->uid
;
1085 /* If that didn't work, try to read it from the specified path */
1089 if (read_id_from_file(i
, NULL
, &c
) > 0) {
1091 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1092 log_debug("Group ID " GID_FMT
" of file not suitable for %s.", c
, i
->name
);
1096 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1101 log_debug("Group ID " GID_FMT
" of file for %s already used.", c
, i
->name
);
1106 /* And if that didn't work either, let's try to find a free one */
1109 /* We look for new GIDs in the UID pool! */
1110 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1112 log_error("No free group ID available for %s.", i
->name
);
1116 r
= gid_is_ok(search_uid
);
1118 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1124 i
->gid
= search_uid
;
1127 r
= hashmap_ensure_allocated(&todo_gids
, NULL
);
1131 r
= hashmap_put(todo_gids
, GID_TO_PTR(i
->gid
), i
);
1135 i
->todo_group
= true;
1136 log_info("Creating group %s with gid " GID_FMT
".", i
->name
, i
->gid
);
1141 static int process_item(Item
*i
) {
1158 j
= hashmap_get(users
, i
->name
);
1160 /* There's already user to be created for this
1161 * name, let's process that in one step */
1169 r
= free_and_strdup(&j
->gid_path
, i
->gid_path
);
1177 return add_group(i
);
1181 assert_not_reached("Unknown item type");
1185 static void item_free(Item
*i
) {
1193 free(i
->description
);
1197 DEFINE_TRIVIAL_CLEANUP_FUNC(Item
*, item_free
);
1199 static int add_implicit(void) {
1204 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1206 HASHMAP_FOREACH_KEY(l
, g
, members
, iterator
) {
1210 i
= hashmap_get(groups
, g
);
1212 _cleanup_(item_freep
) Item
*j
= NULL
;
1214 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1222 j
->type
= ADD_GROUP
;
1223 j
->name
= strdup(g
);
1227 r
= hashmap_put(groups
, j
->name
, j
);
1231 log_debug("Adding implicit group '%s' due to m line", j
->name
);
1235 STRV_FOREACH(m
, l
) {
1237 i
= hashmap_get(users
, *m
);
1239 _cleanup_(item_freep
) Item
*j
= NULL
;
1241 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1250 j
->name
= strdup(*m
);
1254 r
= hashmap_put(users
, j
->name
, j
);
1258 log_debug("Adding implicit user '%s' due to m line", j
->name
);
1267 static bool item_equal(Item
*a
, Item
*b
) {
1271 if (a
->type
!= b
->type
)
1274 if (!streq_ptr(a
->name
, b
->name
))
1277 if (!streq_ptr(a
->uid_path
, b
->uid_path
))
1280 if (!streq_ptr(a
->gid_path
, b
->gid_path
))
1283 if (!streq_ptr(a
->description
, b
->description
))
1286 if (a
->uid_set
!= b
->uid_set
)
1289 if (a
->uid_set
&& a
->uid
!= b
->uid
)
1292 if (a
->gid_set
!= b
->gid_set
)
1295 if (a
->gid_set
&& a
->gid
!= b
->gid
)
1298 if (!streq_ptr(a
->home
, b
->home
))
1304 static bool valid_user_group_name(const char *u
) {
1311 if (!(u
[0] >= 'a' && u
[0] <= 'z') &&
1312 !(u
[0] >= 'A' && u
[0] <= 'Z') &&
1316 for (i
= u
+1; *i
; i
++) {
1317 if (!(*i
>= 'a' && *i
<= 'z') &&
1318 !(*i
>= 'A' && *i
<= 'Z') &&
1319 !(*i
>= '0' && *i
<= '9') &&
1325 sz
= sysconf(_SC_LOGIN_NAME_MAX
);
1328 if ((size_t) (i
-u
) > (size_t) sz
)
1331 if ((size_t) (i
-u
) > UT_NAMESIZE
- 1)
1337 static bool valid_gecos(const char *d
) {
1342 if (!utf8_is_valid(d
))
1345 if (string_has_cc(d
, NULL
))
1348 /* Colons are used as field separators, and hence not OK */
1355 static bool valid_home(const char *p
) {
1360 if (!utf8_is_valid(p
))
1363 if (string_has_cc(p
, NULL
))
1366 if (!path_is_absolute(p
))
1369 if (!path_is_safe(p
))
1372 /* Colons are used as field separators, and hence not OK */
1379 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1381 static const Specifier specifier_table
[] = {
1382 { 'm', specifier_machine_id
, NULL
},
1383 { 'b', specifier_boot_id
, NULL
},
1384 { 'H', specifier_host_name
, NULL
},
1385 { 'v', specifier_kernel_release
, NULL
},
1389 _cleanup_free_
char *action
= NULL
, *name
= NULL
, *id
= NULL
, *resolved_name
= NULL
, *resolved_id
= NULL
, *description
= NULL
, *home
= NULL
;
1390 _cleanup_(item_freep
) Item
*i
= NULL
;
1402 r
= extract_many_words(&p
, NULL
, EXTRACT_QUOTES
, &action
, &name
, &id
, &description
, &home
, NULL
);
1404 log_error("[%s:%u] Syntax error.", fname
, line
);
1408 log_error("[%s:%u] Missing action and name columns.", fname
, line
);
1412 log_error("[%s:%u] Trailing garbage.", fname
, line
);
1417 if (strlen(action
) != 1) {
1418 log_error("[%s:%u] Unknown modifier '%s'", fname
, line
, action
);
1422 if (!IN_SET(action
[0], ADD_USER
, ADD_GROUP
, ADD_MEMBER
, ADD_RANGE
)) {
1423 log_error("[%s:%u] Unknown command command type '%c'.", fname
, line
, action
[0]);
1428 if (isempty(name
) || streq(name
, "-"))
1432 r
= specifier_printf(name
, specifier_table
, NULL
, &resolved_name
);
1434 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1438 if (!valid_user_group_name(resolved_name
)) {
1439 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_name
);
1445 if (isempty(id
) || streq(id
, "-"))
1449 r
= specifier_printf(id
, specifier_table
, NULL
, &resolved_id
);
1451 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1456 /* Verify description */
1457 if (isempty(description
) || streq(description
, "-"))
1458 description
= mfree(description
);
1461 if (!valid_gecos(description
)) {
1462 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname
, line
, description
);
1468 if (isempty(home
) || streq(home
, "-"))
1472 if (!valid_home(home
)) {
1473 log_error("[%s:%u] '%s' is not a valid home directory field.", fname
, line
, home
);
1478 switch (action
[0]) {
1481 if (resolved_name
) {
1482 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname
, line
);
1487 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname
, line
);
1492 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname
, line
);
1497 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname
, line
);
1501 r
= uid_range_add_str(&uid_range
, &n_uid_range
, resolved_id
);
1503 log_error("[%s:%u] Invalid UID range %s.", fname
, line
, resolved_id
);
1512 /* Try to extend an existing member or group item */
1514 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname
, line
);
1519 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname
, line
);
1523 if (!valid_user_group_name(resolved_id
)) {
1524 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_id
);
1529 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname
, line
);
1534 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname
, line
);
1538 r
= hashmap_ensure_allocated(&members
, &string_hash_ops
);
1542 l
= hashmap_get(members
, resolved_id
);
1544 /* A list for this group name already exists, let's append to it */
1545 r
= strv_push(&l
, resolved_name
);
1549 resolved_name
= NULL
;
1551 assert_se(hashmap_update(members
, resolved_id
, l
) >= 0);
1553 /* No list for this group name exists yet, create one */
1555 l
= new0(char *, 2);
1559 l
[0] = resolved_name
;
1562 r
= hashmap_put(members
, resolved_id
, l
);
1568 resolved_id
= resolved_name
= NULL
;
1576 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname
, line
);
1580 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1589 if (path_is_absolute(resolved_id
)) {
1590 i
->uid_path
= resolved_id
;
1593 path_kill_slashes(i
->uid_path
);
1595 r
= parse_uid(resolved_id
, &i
->uid
);
1597 log_error("Failed to parse UID: %s", id
);
1605 i
->description
= description
;
1616 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname
, line
);
1621 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname
, line
);
1626 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname
, line
);
1630 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1639 if (path_is_absolute(resolved_id
)) {
1640 i
->gid_path
= resolved_id
;
1643 path_kill_slashes(i
->gid_path
);
1645 r
= parse_gid(resolved_id
, &i
->gid
);
1647 log_error("Failed to parse GID: %s", id
);
1662 i
->type
= action
[0];
1663 i
->name
= resolved_name
;
1664 resolved_name
= NULL
;
1666 existing
= hashmap_get(h
, i
->name
);
1669 /* Two identical items are fine */
1670 if (!item_equal(existing
, i
))
1671 log_warning("Two or more conflicting lines for %s configured, ignoring.", i
->name
);
1676 r
= hashmap_put(h
, i
->name
, i
);
1684 static int read_config_file(const char *fn
, bool ignore_enoent
) {
1685 _cleanup_fclose_
FILE *rf
= NULL
;
1687 char line
[LINE_MAX
];
1696 r
= search_and_fopen_nulstr(fn
, "re", arg_root
, conf_file_dirs
, &rf
);
1698 if (ignore_enoent
&& r
== -ENOENT
)
1701 return log_error_errno(r
, "Failed to open '%s', ignoring: %m", fn
);
1707 FOREACH_LINE(line
, f
, break) {
1714 if (*l
== '#' || *l
== 0)
1717 k
= parse_line(fn
, v
, l
);
1718 if (k
< 0 && r
== 0)
1723 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
1731 static void free_database(Hashmap
*by_name
, Hashmap
*by_id
) {
1735 name
= hashmap_first(by_id
);
1739 hashmap_remove(by_name
, name
);
1741 hashmap_steal_first_key(by_id
);
1745 while ((name
= hashmap_steal_first_key(by_name
)))
1748 hashmap_free(by_name
);
1749 hashmap_free(by_id
);
1752 static void help(void) {
1753 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1754 "Creates system user accounts.\n\n"
1755 " -h --help Show this help\n"
1756 " --version Show package version\n"
1757 " --root=PATH Operate on an alternate filesystem root\n"
1758 , program_invocation_short_name
);
1761 static int parse_argv(int argc
, char *argv
[]) {
1764 ARG_VERSION
= 0x100,
1768 static const struct option options
[] = {
1769 { "help", no_argument
, NULL
, 'h' },
1770 { "version", no_argument
, NULL
, ARG_VERSION
},
1771 { "root", required_argument
, NULL
, ARG_ROOT
},
1780 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
1792 r
= parse_path_argument_and_warn(optarg
, true, &arg_root
);
1801 assert_not_reached("Unhandled option");
1807 int main(int argc
, char *argv
[]) {
1809 _cleanup_close_
int lock
= -1;
1815 r
= parse_argv(argc
, argv
);
1819 log_set_target(LOG_TARGET_AUTO
);
1820 log_parse_environment();
1825 r
= mac_selinux_init(NULL
);
1827 log_error_errno(r
, "SELinux setup failed: %m");
1831 if (optind
< argc
) {
1834 for (j
= optind
; j
< argc
; j
++) {
1835 k
= read_config_file(argv
[j
], false);
1836 if (k
< 0 && r
== 0)
1840 _cleanup_strv_free_
char **files
= NULL
;
1843 r
= conf_files_list_nulstr(&files
, ".conf", arg_root
, conf_file_dirs
);
1845 log_error_errno(r
, "Failed to enumerate sysusers.d files: %m");
1849 STRV_FOREACH(f
, files
) {
1850 k
= read_config_file(*f
, true);
1851 if (k
< 0 && r
== 0)
1857 /* Default to default range of 1..SYSTEMD_UID_MAX */
1858 r
= uid_range_add(&uid_range
, &n_uid_range
, 1, SYSTEM_UID_MAX
);
1869 lock
= take_etc_passwd_lock(arg_root
);
1871 log_error_errno(lock
, "Failed to take lock: %m");
1875 r
= load_user_database();
1877 log_error_errno(r
, "Failed to load user database: %m");
1881 r
= load_group_database();
1883 log_error_errno(r
, "Failed to read group database: %m");
1887 HASHMAP_FOREACH(i
, groups
, iterator
)
1890 HASHMAP_FOREACH(i
, users
, iterator
)
1895 log_error_errno(r
, "Failed to write files: %m");
1898 while ((i
= hashmap_steal_first(groups
)))
1901 while ((i
= hashmap_steal_first(users
)))
1904 while ((n
= hashmap_first_key(members
))) {
1905 strv_free(hashmap_steal_first(members
));
1909 hashmap_free(groups
);
1910 hashmap_free(users
);
1911 hashmap_free(members
);
1912 hashmap_free(todo_uids
);
1913 hashmap_free(todo_gids
);
1915 free_database(database_user
, database_uid
);
1916 free_database(database_group
, database_gid
);
1920 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;