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 #include "user-util.h"
46 typedef enum ItemType
{
71 static char *arg_root
= NULL
;
73 static const char conf_file_dirs
[] = CONF_DIRS_NULSTR("sysusers");
75 static Hashmap
*users
= NULL
, *groups
= NULL
;
76 static Hashmap
*todo_uids
= NULL
, *todo_gids
= NULL
;
77 static Hashmap
*members
= NULL
;
79 static Hashmap
*database_uid
= NULL
, *database_user
= NULL
;
80 static Hashmap
*database_gid
= NULL
, *database_group
= NULL
;
82 static uid_t search_uid
= UID_INVALID
;
83 static UidRange
*uid_range
= NULL
;
84 static unsigned n_uid_range
= 0;
86 static int load_user_database(void) {
87 _cleanup_fclose_
FILE *f
= NULL
;
88 const char *passwd_path
;
92 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
93 f
= fopen(passwd_path
, "re");
95 return errno
== ENOENT
? 0 : -errno
;
97 r
= hashmap_ensure_allocated(&database_user
, &string_hash_ops
);
101 r
= hashmap_ensure_allocated(&database_uid
, NULL
);
106 while ((pw
= fgetpwent(f
))) {
110 n
= strdup(pw
->pw_name
);
114 k
= hashmap_put(database_user
, n
, UID_TO_PTR(pw
->pw_uid
));
115 if (k
< 0 && k
!= -EEXIST
) {
120 q
= hashmap_put(database_uid
, UID_TO_PTR(pw
->pw_uid
), n
);
121 if (q
< 0 && q
!= -EEXIST
) {
132 if (!IN_SET(errno
, 0, ENOENT
))
138 static int load_group_database(void) {
139 _cleanup_fclose_
FILE *f
= NULL
;
140 const char *group_path
;
144 group_path
= prefix_roota(arg_root
, "/etc/group");
145 f
= fopen(group_path
, "re");
147 return errno
== ENOENT
? 0 : -errno
;
149 r
= hashmap_ensure_allocated(&database_group
, &string_hash_ops
);
153 r
= hashmap_ensure_allocated(&database_gid
, NULL
);
158 while ((gr
= fgetgrent(f
))) {
162 n
= strdup(gr
->gr_name
);
166 k
= hashmap_put(database_group
, n
, GID_TO_PTR(gr
->gr_gid
));
167 if (k
< 0 && k
!= -EEXIST
) {
172 q
= hashmap_put(database_gid
, GID_TO_PTR(gr
->gr_gid
), n
);
173 if (q
< 0 && q
!= -EEXIST
) {
184 if (!IN_SET(errno
, 0, ENOENT
))
190 static int make_backup(const char *target
, const char *x
) {
191 _cleanup_close_
int src
= -1;
192 _cleanup_fclose_
FILE *dst
= NULL
;
194 struct timespec ts
[2];
198 src
= open(x
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
200 if (errno
== ENOENT
) /* No backup necessary... */
206 if (fstat(src
, &st
) < 0)
209 r
= fopen_temporary_label(target
, x
, &dst
, &temp
);
213 r
= copy_bytes(src
, fileno(dst
), (uint64_t) -1, true);
217 /* Don't fail on chmod() or chown(). If it stays owned by us
218 * and/or unreadable by others, then it isn't too bad... */
220 backup
= strjoina(x
, "-");
222 /* Copy over the access mask */
223 if (fchmod(fileno(dst
), st
.st_mode
& 07777) < 0)
224 log_warning_errno(errno
, "Failed to change mode on %s: %m", backup
);
226 if (fchown(fileno(dst
), st
.st_uid
, st
.st_gid
)< 0)
227 log_warning_errno(errno
, "Failed to change ownership of %s: %m", backup
);
231 if (futimens(fileno(dst
), ts
) < 0)
232 log_warning_errno(errno
, "Failed to fix access and modification time of %s: %m", backup
);
234 if (rename(temp
, backup
) < 0)
244 static int putgrent_with_members(const struct group
*gr
, FILE *group
) {
250 a
= hashmap_get(members
, gr
->gr_name
);
252 _cleanup_strv_free_
char **l
= NULL
;
256 l
= strv_copy(gr
->gr_mem
);
261 if (strv_find(l
, *i
))
264 if (strv_extend(&l
, *i
) < 0)
280 if (putgrent(&t
, group
) != 0)
281 return errno
? -errno
: -EIO
;
288 if (putgrent(gr
, group
) != 0)
289 return errno
? -errno
: -EIO
;
294 static int putsgent_with_members(const struct sgrp
*sg
, FILE *gshadow
) {
300 a
= hashmap_get(members
, sg
->sg_namp
);
302 _cleanup_strv_free_
char **l
= NULL
;
306 l
= strv_copy(sg
->sg_mem
);
311 if (strv_find(l
, *i
))
314 if (strv_extend(&l
, *i
) < 0)
330 if (putsgent(&t
, gshadow
) != 0)
331 return errno
? -errno
: -EIO
;
338 if (putsgent(sg
, gshadow
) != 0)
339 return errno
? -errno
: -EIO
;
344 static int sync_rights(FILE *from
, FILE *to
) {
347 if (fstat(fileno(from
), &st
) < 0)
350 if (fchmod(fileno(to
), st
.st_mode
& 07777) < 0)
353 if (fchown(fileno(to
), st
.st_uid
, st
.st_gid
) < 0)
359 static int rename_and_apply_smack(const char *temp_path
, const char *dest_path
) {
361 if (rename(temp_path
, dest_path
) < 0)
364 #ifdef SMACK_RUN_LABEL
365 r
= mac_smack_apply(dest_path
, SMACK_ATTR_ACCESS
, SMACK_FLOOR_LABEL
);
372 static int write_files(void) {
374 _cleanup_fclose_
FILE *passwd
= NULL
, *group
= NULL
, *shadow
= NULL
, *gshadow
= NULL
;
375 _cleanup_free_
char *passwd_tmp
= NULL
, *group_tmp
= NULL
, *shadow_tmp
= NULL
, *gshadow_tmp
= NULL
;
376 const char *passwd_path
= NULL
, *group_path
= NULL
, *shadow_path
= NULL
, *gshadow_path
= NULL
;
377 bool group_changed
= false;
382 if (hashmap_size(todo_gids
) > 0 || hashmap_size(members
) > 0) {
383 _cleanup_fclose_
FILE *original
= NULL
;
385 /* First we update the actual group list file */
386 group_path
= prefix_roota(arg_root
, "/etc/group");
387 r
= fopen_temporary_label("/etc/group", group_path
, &group
, &group_tmp
);
391 original
= fopen(group_path
, "re");
395 r
= sync_rights(original
, group
);
400 while ((gr
= fgetgrent(original
))) {
401 /* Safety checks against name and GID
402 * collisions. Normally, this should
403 * be unnecessary, but given that we
404 * look at the entries anyway here,
405 * let's make an extra verification
406 * step that we don't generate
407 * duplicate entries. */
409 i
= hashmap_get(groups
, gr
->gr_name
);
410 if (i
&& i
->todo_group
) {
415 if (hashmap_contains(todo_gids
, GID_TO_PTR(gr
->gr_gid
))) {
420 r
= putgrent_with_members(gr
, group
);
424 group_changed
= true;
428 if (!IN_SET(errno
, 0, ENOENT
)) {
433 } else if (errno
!= ENOENT
) {
436 } else if (fchmod(fileno(group
), 0644) < 0) {
441 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
445 .gr_passwd
= (char*) "x",
448 r
= putgrent_with_members(&n
, group
);
452 group_changed
= true;
455 r
= fflush_and_check(group
);
464 /* OK, now also update the shadow file for the group list */
465 gshadow_path
= prefix_roota(arg_root
, "/etc/gshadow");
466 r
= fopen_temporary_label("/etc/gshadow", gshadow_path
, &gshadow
, &gshadow_tmp
);
470 original
= fopen(gshadow_path
, "re");
474 r
= sync_rights(original
, gshadow
);
479 while ((sg
= fgetsgent(original
))) {
481 i
= hashmap_get(groups
, sg
->sg_namp
);
482 if (i
&& i
->todo_group
) {
487 r
= putsgent_with_members(sg
, gshadow
);
491 group_changed
= true;
495 if (!IN_SET(errno
, 0, ENOENT
)) {
500 } else if (errno
!= ENOENT
) {
503 } else if (fchmod(fileno(gshadow
), 0000) < 0) {
508 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
511 .sg_passwd
= (char*) "!!",
514 r
= putsgent_with_members(&n
, gshadow
);
518 group_changed
= true;
521 r
= fflush_and_check(gshadow
);
526 if (hashmap_size(todo_uids
) > 0) {
527 _cleanup_fclose_
FILE *original
= NULL
;
530 /* First we update the user database itself */
531 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
532 r
= fopen_temporary_label("/etc/passwd", passwd_path
, &passwd
, &passwd_tmp
);
536 original
= fopen(passwd_path
, "re");
540 r
= sync_rights(original
, passwd
);
545 while ((pw
= fgetpwent(original
))) {
547 i
= hashmap_get(users
, pw
->pw_name
);
548 if (i
&& i
->todo_user
) {
553 if (hashmap_contains(todo_uids
, UID_TO_PTR(pw
->pw_uid
))) {
559 if (putpwent(pw
, passwd
) < 0) {
560 r
= errno
? -errno
: -EIO
;
566 if (!IN_SET(errno
, 0, ENOENT
)) {
571 } else if (errno
!= ENOENT
) {
574 } else if (fchmod(fileno(passwd
), 0644) < 0) {
579 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
584 .pw_gecos
= i
->description
,
586 /* "x" means the password is stored in
588 .pw_passwd
= (char*) "x",
590 /* We default to the root directory as home */
591 .pw_dir
= i
->home
? i
->home
: (char*) "/",
593 /* Initialize the shell to nologin,
594 * with one exception: for root we
595 * patch in something special */
596 .pw_shell
= i
->uid
== 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
600 if (putpwent(&n
, passwd
) != 0) {
601 r
= errno
? -errno
: -EIO
;
606 r
= fflush_and_check(passwd
);
615 /* The we update the shadow database */
616 shadow_path
= prefix_roota(arg_root
, "/etc/shadow");
617 r
= fopen_temporary_label("/etc/shadow", shadow_path
, &shadow
, &shadow_tmp
);
621 lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
623 original
= fopen(shadow_path
, "re");
627 r
= sync_rights(original
, shadow
);
632 while ((sp
= fgetspent(original
))) {
634 i
= hashmap_get(users
, sp
->sp_namp
);
635 if (i
&& i
->todo_user
) {
636 /* we will update the existing entry */
637 sp
->sp_lstchg
= lstchg
;
639 /* only the /etc/shadow stage is left, so we can
640 * safely remove the item from the todo set */
641 i
->todo_user
= false;
642 hashmap_remove(todo_uids
, UID_TO_PTR(i
->uid
));
646 if (putspent(sp
, shadow
) < 0) {
647 r
= errno
? -errno
: -EIO
;
653 if (!IN_SET(errno
, 0, ENOENT
)) {
657 } else if (errno
!= ENOENT
) {
660 } else if (fchmod(fileno(shadow
), 0000) < 0) {
665 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
668 .sp_pwdp
= (char*) "!!",
675 .sp_flag
= (unsigned long) -1, /* this appears to be what everybody does ... */
679 if (putspent(&n
, shadow
) != 0) {
680 r
= errno
? -errno
: -EIO
;
685 r
= fflush_and_check(shadow
);
690 /* Make a backup of the old files */
693 r
= make_backup("/etc/group", group_path
);
698 r
= make_backup("/etc/gshadow", gshadow_path
);
705 r
= make_backup("/etc/passwd", passwd_path
);
710 r
= make_backup("/etc/shadow", shadow_path
);
715 /* And make the new files count */
718 r
= rename_and_apply_smack(group_tmp
, group_path
);
722 group_tmp
= mfree(group_tmp
);
725 r
= rename_and_apply_smack(gshadow_tmp
, gshadow_path
);
729 gshadow_tmp
= mfree(gshadow_tmp
);
734 r
= rename_and_apply_smack(passwd_tmp
, passwd_path
);
738 passwd_tmp
= mfree(passwd_tmp
);
741 r
= rename_and_apply_smack(shadow_tmp
, shadow_path
);
745 shadow_tmp
= mfree(shadow_tmp
);
763 static int uid_is_ok(uid_t uid
, const char *name
) {
769 /* Let's see if we already have assigned the UID a second time */
770 if (hashmap_get(todo_uids
, UID_TO_PTR(uid
)))
773 /* Try to avoid using uids that are already used by a group
774 * that doesn't have the same name as our new user. */
775 i
= hashmap_get(todo_gids
, GID_TO_PTR(uid
));
776 if (i
&& !streq(i
->name
, name
))
779 /* Let's check the files directly */
780 if (hashmap_contains(database_uid
, UID_TO_PTR(uid
)))
783 n
= hashmap_get(database_gid
, GID_TO_PTR(uid
));
784 if (n
&& !streq(n
, name
))
787 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
793 if (!IN_SET(errno
, 0, ENOENT
))
797 g
= getgrgid((gid_t
) uid
);
799 if (!streq(g
->gr_name
, name
))
801 } else if (!IN_SET(errno
, 0, ENOENT
))
808 static int root_stat(const char *p
, struct stat
*st
) {
811 fix
= prefix_roota(arg_root
, p
);
812 if (stat(fix
, st
) < 0)
818 static int read_id_from_file(Item
*i
, uid_t
*_uid
, gid_t
*_gid
) {
820 bool found_uid
= false, found_gid
= false;
826 /* First, try to get the gid directly */
827 if (_gid
&& i
->gid_path
&& root_stat(i
->gid_path
, &st
) >= 0) {
832 /* Then, try to get the uid directly */
833 if ((_uid
|| (_gid
&& !found_gid
))
835 && root_stat(i
->uid_path
, &st
) >= 0) {
840 /* If we need the gid, but had no success yet, also derive it from the uid path */
841 if (_gid
&& !found_gid
) {
847 /* If that didn't work yet, then let's reuse the gid as uid */
848 if (_uid
&& !found_uid
&& i
->gid_path
) {
853 } else if (root_stat(i
->gid_path
, &st
) >= 0) {
854 uid
= (uid_t
) st
.st_gid
;
876 static int add_user(Item
*i
) {
882 /* Check the database directly */
883 z
= hashmap_get(database_user
, i
->name
);
885 log_debug("User %s already exists.", i
->name
);
886 i
->uid
= PTR_TO_UID(z
);
896 p
= getpwnam(i
->name
);
898 log_debug("User %s already exists.", i
->name
);
902 r
= free_and_strdup(&i
->description
, p
->pw_gecos
);
908 if (!IN_SET(errno
, 0, ENOENT
))
909 return log_error_errno(errno
, "Failed to check if user %s already exists: %m", i
->name
);
912 /* Try to use the suggested numeric uid */
914 r
= uid_is_ok(i
->uid
, i
->name
);
916 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
918 log_debug("Suggested user ID " UID_FMT
" for %s already used.", i
->uid
, i
->name
);
923 /* If that didn't work, try to read it from the specified path */
927 if (read_id_from_file(i
, &c
, NULL
) > 0) {
929 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
930 log_debug("User ID " UID_FMT
" of file not suitable for %s.", c
, i
->name
);
932 r
= uid_is_ok(c
, i
->name
);
934 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
939 log_debug("User ID " UID_FMT
" of file for %s is already used.", c
, i
->name
);
944 /* Otherwise try to reuse the group ID */
945 if (!i
->uid_set
&& i
->gid_set
) {
946 r
= uid_is_ok((uid_t
) i
->gid
, i
->name
);
948 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
950 i
->uid
= (uid_t
) i
->gid
;
955 /* And if that didn't work either, let's try to find a free one */
958 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
960 log_error("No free user ID available for %s.", i
->name
);
964 r
= uid_is_ok(search_uid
, i
->name
);
966 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
975 r
= hashmap_ensure_allocated(&todo_uids
, NULL
);
979 r
= hashmap_put(todo_uids
, UID_TO_PTR(i
->uid
), i
);
984 log_info("Creating user %s (%s) with uid " UID_FMT
" and gid " GID_FMT
".", i
->name
, strna(i
->description
), i
->uid
, i
->gid
);
989 static int gid_is_ok(gid_t gid
) {
993 if (hashmap_get(todo_gids
, GID_TO_PTR(gid
)))
996 /* Avoid reusing gids that are already used by a different user */
997 if (hashmap_get(todo_uids
, UID_TO_PTR(gid
)))
1000 if (hashmap_contains(database_gid
, GID_TO_PTR(gid
)))
1003 if (hashmap_contains(database_uid
, UID_TO_PTR(gid
)))
1011 if (!IN_SET(errno
, 0, ENOENT
))
1015 p
= getpwuid((uid_t
) gid
);
1018 if (!IN_SET(errno
, 0, ENOENT
))
1025 static int add_group(Item
*i
) {
1031 /* Check the database directly */
1032 z
= hashmap_get(database_group
, i
->name
);
1034 log_debug("Group %s already exists.", i
->name
);
1035 i
->gid
= PTR_TO_GID(z
);
1040 /* Also check NSS */
1045 g
= getgrnam(i
->name
);
1047 log_debug("Group %s already exists.", i
->name
);
1052 if (!IN_SET(errno
, 0, ENOENT
))
1053 return log_error_errno(errno
, "Failed to check if group %s already exists: %m", i
->name
);
1056 /* Try to use the suggested numeric gid */
1058 r
= gid_is_ok(i
->gid
);
1060 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1062 log_debug("Suggested group ID " GID_FMT
" for %s already used.", i
->gid
, i
->name
);
1067 /* Try to reuse the numeric uid, if there's one */
1068 if (!i
->gid_set
&& i
->uid_set
) {
1069 r
= gid_is_ok((gid_t
) i
->uid
);
1071 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1073 i
->gid
= (gid_t
) i
->uid
;
1078 /* If that didn't work, try to read it from the specified path */
1082 if (read_id_from_file(i
, NULL
, &c
) > 0) {
1084 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1085 log_debug("Group ID " GID_FMT
" of file not suitable for %s.", c
, i
->name
);
1089 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1094 log_debug("Group ID " GID_FMT
" of file for %s already used.", c
, i
->name
);
1099 /* And if that didn't work either, let's try to find a free one */
1102 /* We look for new GIDs in the UID pool! */
1103 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1105 log_error("No free group ID available for %s.", i
->name
);
1109 r
= gid_is_ok(search_uid
);
1111 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1117 i
->gid
= search_uid
;
1120 r
= hashmap_ensure_allocated(&todo_gids
, NULL
);
1124 r
= hashmap_put(todo_gids
, GID_TO_PTR(i
->gid
), i
);
1128 i
->todo_group
= true;
1129 log_info("Creating group %s with gid " GID_FMT
".", i
->name
, i
->gid
);
1134 static int process_item(Item
*i
) {
1151 j
= hashmap_get(users
, i
->name
);
1153 /* There's already user to be created for this
1154 * name, let's process that in one step */
1162 r
= free_and_strdup(&j
->gid_path
, i
->gid_path
);
1170 return add_group(i
);
1174 assert_not_reached("Unknown item type");
1178 static void item_free(Item
*i
) {
1186 free(i
->description
);
1190 DEFINE_TRIVIAL_CLEANUP_FUNC(Item
*, item_free
);
1192 static int add_implicit(void) {
1197 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1199 HASHMAP_FOREACH_KEY(l
, g
, members
, iterator
) {
1203 i
= hashmap_get(groups
, g
);
1205 _cleanup_(item_freep
) Item
*j
= NULL
;
1207 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1215 j
->type
= ADD_GROUP
;
1216 j
->name
= strdup(g
);
1220 r
= hashmap_put(groups
, j
->name
, j
);
1224 log_debug("Adding implicit group '%s' due to m line", j
->name
);
1228 STRV_FOREACH(m
, l
) {
1230 i
= hashmap_get(users
, *m
);
1232 _cleanup_(item_freep
) Item
*j
= NULL
;
1234 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1243 j
->name
= strdup(*m
);
1247 r
= hashmap_put(users
, j
->name
, j
);
1251 log_debug("Adding implicit user '%s' due to m line", j
->name
);
1260 static bool item_equal(Item
*a
, Item
*b
) {
1264 if (a
->type
!= b
->type
)
1267 if (!streq_ptr(a
->name
, b
->name
))
1270 if (!streq_ptr(a
->uid_path
, b
->uid_path
))
1273 if (!streq_ptr(a
->gid_path
, b
->gid_path
))
1276 if (!streq_ptr(a
->description
, b
->description
))
1279 if (a
->uid_set
!= b
->uid_set
)
1282 if (a
->uid_set
&& a
->uid
!= b
->uid
)
1285 if (a
->gid_set
!= b
->gid_set
)
1288 if (a
->gid_set
&& a
->gid
!= b
->gid
)
1291 if (!streq_ptr(a
->home
, b
->home
))
1297 static bool valid_user_group_name(const char *u
) {
1304 if (!(u
[0] >= 'a' && u
[0] <= 'z') &&
1305 !(u
[0] >= 'A' && u
[0] <= 'Z') &&
1309 for (i
= u
+1; *i
; i
++) {
1310 if (!(*i
>= 'a' && *i
<= 'z') &&
1311 !(*i
>= 'A' && *i
<= 'Z') &&
1312 !(*i
>= '0' && *i
<= '9') &&
1318 sz
= sysconf(_SC_LOGIN_NAME_MAX
);
1321 if ((size_t) (i
-u
) > (size_t) sz
)
1324 if ((size_t) (i
-u
) > UT_NAMESIZE
- 1)
1330 static bool valid_gecos(const char *d
) {
1335 if (!utf8_is_valid(d
))
1338 if (string_has_cc(d
, NULL
))
1341 /* Colons are used as field separators, and hence not OK */
1348 static bool valid_home(const char *p
) {
1353 if (!utf8_is_valid(p
))
1356 if (string_has_cc(p
, NULL
))
1359 if (!path_is_absolute(p
))
1362 if (!path_is_safe(p
))
1365 /* Colons are used as field separators, and hence not OK */
1372 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1374 static const Specifier specifier_table
[] = {
1375 { 'm', specifier_machine_id
, NULL
},
1376 { 'b', specifier_boot_id
, NULL
},
1377 { 'H', specifier_host_name
, NULL
},
1378 { 'v', specifier_kernel_release
, NULL
},
1382 _cleanup_free_
char *action
= NULL
, *name
= NULL
, *id
= NULL
, *resolved_name
= NULL
, *resolved_id
= NULL
, *description
= NULL
, *home
= NULL
;
1383 _cleanup_(item_freep
) Item
*i
= NULL
;
1395 r
= extract_many_words(&p
, NULL
, EXTRACT_QUOTES
, &action
, &name
, &id
, &description
, &home
, NULL
);
1397 log_error("[%s:%u] Syntax error.", fname
, line
);
1401 log_error("[%s:%u] Missing action and name columns.", fname
, line
);
1405 log_error("[%s:%u] Trailing garbage.", fname
, line
);
1410 if (strlen(action
) != 1) {
1411 log_error("[%s:%u] Unknown modifier '%s'", fname
, line
, action
);
1415 if (!IN_SET(action
[0], ADD_USER
, ADD_GROUP
, ADD_MEMBER
, ADD_RANGE
)) {
1416 log_error("[%s:%u] Unknown command command type '%c'.", fname
, line
, action
[0]);
1421 if (isempty(name
) || streq(name
, "-"))
1425 r
= specifier_printf(name
, specifier_table
, NULL
, &resolved_name
);
1427 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1431 if (!valid_user_group_name(resolved_name
)) {
1432 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_name
);
1438 if (isempty(id
) || streq(id
, "-"))
1442 r
= specifier_printf(id
, specifier_table
, NULL
, &resolved_id
);
1444 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1449 /* Verify description */
1450 if (isempty(description
) || streq(description
, "-"))
1451 description
= mfree(description
);
1454 if (!valid_gecos(description
)) {
1455 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname
, line
, description
);
1461 if (isempty(home
) || streq(home
, "-"))
1465 if (!valid_home(home
)) {
1466 log_error("[%s:%u] '%s' is not a valid home directory field.", fname
, line
, home
);
1471 switch (action
[0]) {
1474 if (resolved_name
) {
1475 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname
, line
);
1480 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname
, line
);
1485 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname
, line
);
1490 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname
, line
);
1494 r
= uid_range_add_str(&uid_range
, &n_uid_range
, resolved_id
);
1496 log_error("[%s:%u] Invalid UID range %s.", fname
, line
, resolved_id
);
1505 /* Try to extend an existing member or group item */
1507 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname
, line
);
1512 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname
, line
);
1516 if (!valid_user_group_name(resolved_id
)) {
1517 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_id
);
1522 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname
, line
);
1527 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname
, line
);
1531 r
= hashmap_ensure_allocated(&members
, &string_hash_ops
);
1535 l
= hashmap_get(members
, resolved_id
);
1537 /* A list for this group name already exists, let's append to it */
1538 r
= strv_push(&l
, resolved_name
);
1542 resolved_name
= NULL
;
1544 assert_se(hashmap_update(members
, resolved_id
, l
) >= 0);
1546 /* No list for this group name exists yet, create one */
1548 l
= new0(char *, 2);
1552 l
[0] = resolved_name
;
1555 r
= hashmap_put(members
, resolved_id
, l
);
1561 resolved_id
= resolved_name
= NULL
;
1569 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname
, line
);
1573 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1582 if (path_is_absolute(resolved_id
)) {
1583 i
->uid_path
= resolved_id
;
1586 path_kill_slashes(i
->uid_path
);
1588 r
= parse_uid(resolved_id
, &i
->uid
);
1590 log_error("Failed to parse UID: %s", id
);
1598 i
->description
= description
;
1609 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname
, line
);
1614 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname
, line
);
1619 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname
, line
);
1623 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1632 if (path_is_absolute(resolved_id
)) {
1633 i
->gid_path
= resolved_id
;
1636 path_kill_slashes(i
->gid_path
);
1638 r
= parse_gid(resolved_id
, &i
->gid
);
1640 log_error("Failed to parse GID: %s", id
);
1655 i
->type
= action
[0];
1656 i
->name
= resolved_name
;
1657 resolved_name
= NULL
;
1659 existing
= hashmap_get(h
, i
->name
);
1662 /* Two identical items are fine */
1663 if (!item_equal(existing
, i
))
1664 log_warning("Two or more conflicting lines for %s configured, ignoring.", i
->name
);
1669 r
= hashmap_put(h
, i
->name
, i
);
1677 static int read_config_file(const char *fn
, bool ignore_enoent
) {
1678 _cleanup_fclose_
FILE *rf
= NULL
;
1680 char line
[LINE_MAX
];
1689 r
= search_and_fopen_nulstr(fn
, "re", arg_root
, conf_file_dirs
, &rf
);
1691 if (ignore_enoent
&& r
== -ENOENT
)
1694 return log_error_errno(r
, "Failed to open '%s', ignoring: %m", fn
);
1700 FOREACH_LINE(line
, f
, break) {
1707 if (*l
== '#' || *l
== 0)
1710 k
= parse_line(fn
, v
, l
);
1711 if (k
< 0 && r
== 0)
1716 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
1724 static void free_database(Hashmap
*by_name
, Hashmap
*by_id
) {
1728 name
= hashmap_first(by_id
);
1732 hashmap_remove(by_name
, name
);
1734 hashmap_steal_first_key(by_id
);
1738 while ((name
= hashmap_steal_first_key(by_name
)))
1741 hashmap_free(by_name
);
1742 hashmap_free(by_id
);
1745 static void help(void) {
1746 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1747 "Creates system user accounts.\n\n"
1748 " -h --help Show this help\n"
1749 " --version Show package version\n"
1750 " --root=PATH Operate on an alternate filesystem root\n"
1751 , program_invocation_short_name
);
1754 static int parse_argv(int argc
, char *argv
[]) {
1757 ARG_VERSION
= 0x100,
1761 static const struct option options
[] = {
1762 { "help", no_argument
, NULL
, 'h' },
1763 { "version", no_argument
, NULL
, ARG_VERSION
},
1764 { "root", required_argument
, NULL
, ARG_ROOT
},
1773 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
1785 r
= parse_path_argument_and_warn(optarg
, true, &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_etc_passwd_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
;