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
) {
417 if (hashmap_contains(todo_gids
, GID_TO_PTR(gr
->gr_gid
))) {
422 r
= putgrent_with_members(gr
, group
);
426 group_changed
= true;
430 if (!IN_SET(errno
, 0, ENOENT
)) {
435 } else if (errno
!= ENOENT
) {
438 } else if (fchmod(fileno(group
), 0644) < 0) {
443 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
447 .gr_passwd
= (char*) "x",
450 r
= putgrent_with_members(&n
, group
);
454 group_changed
= true;
457 r
= fflush_and_check(group
);
466 /* OK, now also update the shadow file for the group list */
467 gshadow_path
= prefix_roota(arg_root
, "/etc/gshadow");
468 r
= fopen_temporary_label("/etc/gshadow", gshadow_path
, &gshadow
, &gshadow_tmp
);
472 original
= fopen(gshadow_path
, "re");
476 r
= sync_rights(original
, gshadow
);
481 while ((sg
= fgetsgent(original
))) {
483 i
= hashmap_get(groups
, sg
->sg_namp
);
484 if (i
&& i
->todo_group
) {
489 r
= putsgent_with_members(sg
, gshadow
);
493 group_changed
= true;
497 if (!IN_SET(errno
, 0, ENOENT
)) {
502 } else if (errno
!= ENOENT
) {
505 } else if (fchmod(fileno(gshadow
), 0000) < 0) {
510 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
513 .sg_passwd
= (char*) "!!",
516 r
= putsgent_with_members(&n
, gshadow
);
520 group_changed
= true;
523 r
= fflush_and_check(gshadow
);
528 if (hashmap_size(todo_uids
) > 0) {
529 _cleanup_fclose_
FILE *original
= NULL
;
532 /* First we update the user database itself */
533 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
534 r
= fopen_temporary_label("/etc/passwd", passwd_path
, &passwd
, &passwd_tmp
);
538 original
= fopen(passwd_path
, "re");
542 r
= sync_rights(original
, passwd
);
547 while ((pw
= fgetpwent(original
))) {
549 i
= hashmap_get(users
, pw
->pw_name
);
550 if (i
&& i
->todo_user
) {
555 if (hashmap_contains(todo_uids
, UID_TO_PTR(pw
->pw_uid
))) {
561 if (putpwent(pw
, passwd
) < 0) {
562 r
= errno
? -errno
: -EIO
;
568 if (!IN_SET(errno
, 0, ENOENT
)) {
573 } else if (errno
!= ENOENT
) {
576 } else if (fchmod(fileno(passwd
), 0644) < 0) {
581 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
586 .pw_gecos
= i
->description
,
588 /* "x" means the password is stored in
590 .pw_passwd
= (char*) "x",
592 /* We default to the root directory as home */
593 .pw_dir
= i
->home
? i
->home
: (char*) "/",
595 /* Initialize the shell to nologin,
596 * with one exception: for root we
597 * patch in something special */
598 .pw_shell
= i
->uid
== 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
602 if (putpwent(&n
, passwd
) != 0) {
603 r
= errno
? -errno
: -EIO
;
608 r
= fflush_and_check(passwd
);
617 /* The we update the shadow database */
618 shadow_path
= prefix_roota(arg_root
, "/etc/shadow");
619 r
= fopen_temporary_label("/etc/shadow", shadow_path
, &shadow
, &shadow_tmp
);
623 lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
625 original
= fopen(shadow_path
, "re");
629 r
= sync_rights(original
, shadow
);
634 while ((sp
= fgetspent(original
))) {
636 i
= hashmap_get(users
, sp
->sp_namp
);
637 if (i
&& i
->todo_user
) {
638 /* we will update the existing entry */
639 sp
->sp_lstchg
= lstchg
;
641 /* only the /etc/shadow stage is left, so we can
642 * safely remove the item from the todo set */
643 i
->todo_user
= false;
644 hashmap_remove(todo_uids
, UID_TO_PTR(i
->uid
));
648 if (putspent(sp
, shadow
) < 0) {
649 r
= errno
? -errno
: -EIO
;
655 if (!IN_SET(errno
, 0, ENOENT
)) {
659 } else if (errno
!= ENOENT
) {
662 } else if (fchmod(fileno(shadow
), 0000) < 0) {
667 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
670 .sp_pwdp
= (char*) "!!",
677 .sp_flag
= (unsigned long) -1, /* this appears to be what everybody does ... */
681 if (putspent(&n
, shadow
) != 0) {
682 r
= errno
? -errno
: -EIO
;
687 r
= fflush_and_check(shadow
);
692 /* Make a backup of the old files */
695 r
= make_backup("/etc/group", group_path
);
700 r
= make_backup("/etc/gshadow", gshadow_path
);
707 r
= make_backup("/etc/passwd", passwd_path
);
712 r
= make_backup("/etc/shadow", shadow_path
);
717 /* And make the new files count */
720 r
= rename_and_apply_smack(group_tmp
, group_path
);
724 group_tmp
= mfree(group_tmp
);
727 r
= rename_and_apply_smack(gshadow_tmp
, gshadow_path
);
731 gshadow_tmp
= mfree(gshadow_tmp
);
736 r
= rename_and_apply_smack(passwd_tmp
, passwd_path
);
740 passwd_tmp
= mfree(passwd_tmp
);
743 r
= rename_and_apply_smack(shadow_tmp
, shadow_path
);
747 shadow_tmp
= mfree(shadow_tmp
);
765 static int uid_is_ok(uid_t uid
, const char *name
) {
771 /* Let's see if we already have assigned the UID a second time */
772 if (hashmap_get(todo_uids
, UID_TO_PTR(uid
)))
775 /* Try to avoid using uids that are already used by a group
776 * that doesn't have the same name as our new user. */
777 i
= hashmap_get(todo_gids
, GID_TO_PTR(uid
));
778 if (i
&& !streq(i
->name
, name
))
781 /* Let's check the files directly */
782 if (hashmap_contains(database_uid
, UID_TO_PTR(uid
)))
785 n
= hashmap_get(database_gid
, GID_TO_PTR(uid
));
786 if (n
&& !streq(n
, name
))
789 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
795 if (!IN_SET(errno
, 0, ENOENT
))
799 g
= getgrgid((gid_t
) uid
);
801 if (!streq(g
->gr_name
, name
))
803 } else if (!IN_SET(errno
, 0, ENOENT
))
810 static int root_stat(const char *p
, struct stat
*st
) {
813 fix
= prefix_roota(arg_root
, p
);
814 if (stat(fix
, st
) < 0)
820 static int read_id_from_file(Item
*i
, uid_t
*_uid
, gid_t
*_gid
) {
822 bool found_uid
= false, found_gid
= false;
828 /* First, try to get the gid directly */
829 if (_gid
&& i
->gid_path
&& root_stat(i
->gid_path
, &st
) >= 0) {
834 /* Then, try to get the uid directly */
835 if ((_uid
|| (_gid
&& !found_gid
))
837 && root_stat(i
->uid_path
, &st
) >= 0) {
842 /* If we need the gid, but had no success yet, also derive it from the uid path */
843 if (_gid
&& !found_gid
) {
849 /* If that didn't work yet, then let's reuse the gid as uid */
850 if (_uid
&& !found_uid
&& i
->gid_path
) {
855 } else if (root_stat(i
->gid_path
, &st
) >= 0) {
856 uid
= (uid_t
) st
.st_gid
;
878 static int add_user(Item
*i
) {
884 /* Check the database directly */
885 z
= hashmap_get(database_user
, i
->name
);
887 log_debug("User %s already exists.", i
->name
);
888 i
->uid
= PTR_TO_UID(z
);
898 p
= getpwnam(i
->name
);
900 log_debug("User %s already exists.", i
->name
);
904 r
= free_and_strdup(&i
->description
, p
->pw_gecos
);
910 if (!IN_SET(errno
, 0, ENOENT
))
911 return log_error_errno(errno
, "Failed to check if user %s already exists: %m", i
->name
);
914 /* Try to use the suggested numeric uid */
916 r
= uid_is_ok(i
->uid
, i
->name
);
918 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
920 log_debug("Suggested user ID " UID_FMT
" for %s already used.", i
->uid
, i
->name
);
925 /* If that didn't work, try to read it from the specified path */
929 if (read_id_from_file(i
, &c
, NULL
) > 0) {
931 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
932 log_debug("User ID " UID_FMT
" of file not suitable for %s.", c
, i
->name
);
934 r
= uid_is_ok(c
, i
->name
);
936 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
941 log_debug("User ID " UID_FMT
" of file for %s is already used.", c
, i
->name
);
946 /* Otherwise, try to reuse the group ID */
947 if (!i
->uid_set
&& i
->gid_set
) {
948 r
= uid_is_ok((uid_t
) i
->gid
, i
->name
);
950 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
952 i
->uid
= (uid_t
) i
->gid
;
957 /* And if that didn't work either, let's try to find a free one */
960 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
962 log_error("No free user ID available for %s.", i
->name
);
966 r
= uid_is_ok(search_uid
, i
->name
);
968 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
977 r
= hashmap_ensure_allocated(&todo_uids
, NULL
);
981 r
= hashmap_put(todo_uids
, UID_TO_PTR(i
->uid
), i
);
986 log_info("Creating user %s (%s) with uid " UID_FMT
" and gid " GID_FMT
".", i
->name
, strna(i
->description
), i
->uid
, i
->gid
);
991 static int gid_is_ok(gid_t gid
) {
995 if (hashmap_get(todo_gids
, GID_TO_PTR(gid
)))
998 /* Avoid reusing gids that are already used by a different user */
999 if (hashmap_get(todo_uids
, UID_TO_PTR(gid
)))
1002 if (hashmap_contains(database_gid
, GID_TO_PTR(gid
)))
1005 if (hashmap_contains(database_uid
, UID_TO_PTR(gid
)))
1013 if (!IN_SET(errno
, 0, ENOENT
))
1017 p
= getpwuid((uid_t
) gid
);
1020 if (!IN_SET(errno
, 0, ENOENT
))
1027 static int add_group(Item
*i
) {
1033 /* Check the database directly */
1034 z
= hashmap_get(database_group
, i
->name
);
1036 log_debug("Group %s already exists.", i
->name
);
1037 i
->gid
= PTR_TO_GID(z
);
1042 /* Also check NSS */
1047 g
= getgrnam(i
->name
);
1049 log_debug("Group %s already exists.", i
->name
);
1054 if (!IN_SET(errno
, 0, ENOENT
))
1055 return log_error_errno(errno
, "Failed to check if group %s already exists: %m", i
->name
);
1058 /* Try to use the suggested numeric gid */
1060 r
= gid_is_ok(i
->gid
);
1062 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1064 log_debug("Suggested group ID " GID_FMT
" for %s already used.", i
->gid
, i
->name
);
1069 /* Try to reuse the numeric uid, if there's one */
1070 if (!i
->gid_set
&& i
->uid_set
) {
1071 r
= gid_is_ok((gid_t
) i
->uid
);
1073 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1075 i
->gid
= (gid_t
) i
->uid
;
1080 /* If that didn't work, try to read it from the specified path */
1084 if (read_id_from_file(i
, NULL
, &c
) > 0) {
1086 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1087 log_debug("Group ID " GID_FMT
" of file not suitable for %s.", c
, i
->name
);
1091 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1096 log_debug("Group ID " GID_FMT
" of file for %s already used.", c
, i
->name
);
1101 /* And if that didn't work either, let's try to find a free one */
1104 /* We look for new GIDs in the UID pool! */
1105 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1107 log_error("No free group ID available for %s.", i
->name
);
1111 r
= gid_is_ok(search_uid
);
1113 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1119 i
->gid
= search_uid
;
1122 r
= hashmap_ensure_allocated(&todo_gids
, NULL
);
1126 r
= hashmap_put(todo_gids
, GID_TO_PTR(i
->gid
), i
);
1130 i
->todo_group
= true;
1131 log_info("Creating group %s with gid " GID_FMT
".", i
->name
, i
->gid
);
1136 static int process_item(Item
*i
) {
1153 j
= hashmap_get(users
, i
->name
);
1155 /* There's already user to be created for this
1156 * name, let's process that in one step */
1164 r
= free_and_strdup(&j
->gid_path
, i
->gid_path
);
1172 return add_group(i
);
1176 assert_not_reached("Unknown item type");
1180 static void item_free(Item
*i
) {
1188 free(i
->description
);
1192 DEFINE_TRIVIAL_CLEANUP_FUNC(Item
*, item_free
);
1194 static int add_implicit(void) {
1199 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1201 HASHMAP_FOREACH_KEY(l
, g
, members
, iterator
) {
1205 i
= hashmap_get(groups
, g
);
1207 _cleanup_(item_freep
) Item
*j
= NULL
;
1209 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1217 j
->type
= ADD_GROUP
;
1218 j
->name
= strdup(g
);
1222 r
= hashmap_put(groups
, j
->name
, j
);
1226 log_debug("Adding implicit group '%s' due to m line", j
->name
);
1230 STRV_FOREACH(m
, l
) {
1232 i
= hashmap_get(users
, *m
);
1234 _cleanup_(item_freep
) Item
*j
= NULL
;
1236 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1245 j
->name
= strdup(*m
);
1249 r
= hashmap_put(users
, j
->name
, j
);
1253 log_debug("Adding implicit user '%s' due to m line", j
->name
);
1262 static bool item_equal(Item
*a
, Item
*b
) {
1266 if (a
->type
!= b
->type
)
1269 if (!streq_ptr(a
->name
, b
->name
))
1272 if (!streq_ptr(a
->uid_path
, b
->uid_path
))
1275 if (!streq_ptr(a
->gid_path
, b
->gid_path
))
1278 if (!streq_ptr(a
->description
, b
->description
))
1281 if (a
->uid_set
!= b
->uid_set
)
1284 if (a
->uid_set
&& a
->uid
!= b
->uid
)
1287 if (a
->gid_set
!= b
->gid_set
)
1290 if (a
->gid_set
&& a
->gid
!= b
->gid
)
1293 if (!streq_ptr(a
->home
, b
->home
))
1299 static bool valid_user_group_name(const char *u
) {
1306 if (!(u
[0] >= 'a' && u
[0] <= 'z') &&
1307 !(u
[0] >= 'A' && u
[0] <= 'Z') &&
1311 for (i
= u
+1; *i
; i
++) {
1312 if (!(*i
>= 'a' && *i
<= 'z') &&
1313 !(*i
>= 'A' && *i
<= 'Z') &&
1314 !(*i
>= '0' && *i
<= '9') &&
1320 sz
= sysconf(_SC_LOGIN_NAME_MAX
);
1323 if ((size_t) (i
-u
) > (size_t) sz
)
1326 if ((size_t) (i
-u
) > UT_NAMESIZE
- 1)
1332 static bool valid_gecos(const char *d
) {
1337 if (!utf8_is_valid(d
))
1340 if (string_has_cc(d
, NULL
))
1343 /* Colons are used as field separators, and hence not OK */
1350 static bool valid_home(const char *p
) {
1355 if (!utf8_is_valid(p
))
1358 if (string_has_cc(p
, NULL
))
1361 if (!path_is_absolute(p
))
1364 if (!path_is_safe(p
))
1367 /* Colons are used as field separators, and hence not OK */
1374 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1376 static const Specifier specifier_table
[] = {
1377 { 'm', specifier_machine_id
, NULL
},
1378 { 'b', specifier_boot_id
, NULL
},
1379 { 'H', specifier_host_name
, NULL
},
1380 { 'v', specifier_kernel_release
, NULL
},
1384 _cleanup_free_
char *action
= NULL
, *name
= NULL
, *id
= NULL
, *resolved_name
= NULL
, *resolved_id
= NULL
, *description
= NULL
, *home
= NULL
;
1385 _cleanup_(item_freep
) Item
*i
= NULL
;
1397 r
= extract_many_words(&p
, NULL
, EXTRACT_QUOTES
, &action
, &name
, &id
, &description
, &home
, NULL
);
1399 log_error("[%s:%u] Syntax error.", fname
, line
);
1403 log_error("[%s:%u] Missing action and name columns.", fname
, line
);
1407 log_error("[%s:%u] Trailing garbage.", fname
, line
);
1412 if (strlen(action
) != 1) {
1413 log_error("[%s:%u] Unknown modifier '%s'", fname
, line
, action
);
1417 if (!IN_SET(action
[0], ADD_USER
, ADD_GROUP
, ADD_MEMBER
, ADD_RANGE
)) {
1418 log_error("[%s:%u] Unknown command command type '%c'.", fname
, line
, action
[0]);
1423 if (isempty(name
) || streq(name
, "-"))
1427 r
= specifier_printf(name
, specifier_table
, NULL
, &resolved_name
);
1429 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1433 if (!valid_user_group_name(resolved_name
)) {
1434 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_name
);
1440 if (isempty(id
) || streq(id
, "-"))
1444 r
= specifier_printf(id
, specifier_table
, NULL
, &resolved_id
);
1446 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1451 /* Verify description */
1452 if (isempty(description
) || streq(description
, "-"))
1453 description
= mfree(description
);
1456 if (!valid_gecos(description
)) {
1457 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname
, line
, description
);
1463 if (isempty(home
) || streq(home
, "-"))
1467 if (!valid_home(home
)) {
1468 log_error("[%s:%u] '%s' is not a valid home directory field.", fname
, line
, home
);
1473 switch (action
[0]) {
1476 if (resolved_name
) {
1477 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname
, line
);
1482 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname
, line
);
1487 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname
, line
);
1492 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname
, line
);
1496 r
= uid_range_add_str(&uid_range
, &n_uid_range
, resolved_id
);
1498 log_error("[%s:%u] Invalid UID range %s.", fname
, line
, resolved_id
);
1507 /* Try to extend an existing member or group item */
1509 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname
, line
);
1514 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname
, line
);
1518 if (!valid_user_group_name(resolved_id
)) {
1519 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_id
);
1524 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname
, line
);
1529 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname
, line
);
1533 r
= hashmap_ensure_allocated(&members
, &string_hash_ops
);
1537 l
= hashmap_get(members
, resolved_id
);
1539 /* A list for this group name already exists, let's append to it */
1540 r
= strv_push(&l
, resolved_name
);
1544 resolved_name
= NULL
;
1546 assert_se(hashmap_update(members
, resolved_id
, l
) >= 0);
1548 /* No list for this group name exists yet, create one */
1550 l
= new0(char *, 2);
1554 l
[0] = resolved_name
;
1557 r
= hashmap_put(members
, resolved_id
, l
);
1563 resolved_id
= resolved_name
= NULL
;
1571 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname
, line
);
1575 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1584 if (path_is_absolute(resolved_id
)) {
1585 i
->uid_path
= resolved_id
;
1588 path_kill_slashes(i
->uid_path
);
1590 r
= parse_uid(resolved_id
, &i
->uid
);
1592 log_error("Failed to parse UID: %s", id
);
1600 i
->description
= description
;
1611 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname
, line
);
1616 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname
, line
);
1621 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname
, line
);
1625 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1634 if (path_is_absolute(resolved_id
)) {
1635 i
->gid_path
= resolved_id
;
1638 path_kill_slashes(i
->gid_path
);
1640 r
= parse_gid(resolved_id
, &i
->gid
);
1642 log_error("Failed to parse GID: %s", id
);
1657 i
->type
= action
[0];
1658 i
->name
= resolved_name
;
1659 resolved_name
= NULL
;
1661 existing
= hashmap_get(h
, i
->name
);
1664 /* Two identical items are fine */
1665 if (!item_equal(existing
, i
))
1666 log_warning("Two or more conflicting lines for %s configured, ignoring.", i
->name
);
1671 r
= hashmap_put(h
, i
->name
, i
);
1679 static int read_config_file(const char *fn
, bool ignore_enoent
) {
1680 _cleanup_fclose_
FILE *rf
= NULL
;
1682 char line
[LINE_MAX
];
1691 r
= search_and_fopen_nulstr(fn
, "re", arg_root
, conf_file_dirs
, &rf
);
1693 if (ignore_enoent
&& r
== -ENOENT
)
1696 return log_error_errno(r
, "Failed to open '%s', ignoring: %m", fn
);
1702 FOREACH_LINE(line
, f
, break) {
1709 if (*l
== '#' || *l
== 0)
1712 k
= parse_line(fn
, v
, l
);
1713 if (k
< 0 && r
== 0)
1718 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
1726 static void free_database(Hashmap
*by_name
, Hashmap
*by_id
) {
1730 name
= hashmap_first(by_id
);
1734 hashmap_remove(by_name
, name
);
1736 hashmap_steal_first_key(by_id
);
1740 while ((name
= hashmap_steal_first_key(by_name
)))
1743 hashmap_free(by_name
);
1744 hashmap_free(by_id
);
1747 static void help(void) {
1748 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1749 "Creates system user accounts.\n\n"
1750 " -h --help Show this help\n"
1751 " --version Show package version\n"
1752 " --root=PATH Operate on an alternate filesystem root\n"
1753 , program_invocation_short_name
);
1756 static int parse_argv(int argc
, char *argv
[]) {
1759 ARG_VERSION
= 0x100,
1763 static const struct option options
[] = {
1764 { "help", no_argument
, NULL
, 'h' },
1765 { "version", no_argument
, NULL
, ARG_VERSION
},
1766 { "root", required_argument
, NULL
, ARG_ROOT
},
1775 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
1787 r
= parse_path_argument_and_warn(optarg
, true, &arg_root
);
1796 assert_not_reached("Unhandled option");
1802 int main(int argc
, char *argv
[]) {
1804 _cleanup_close_
int lock
= -1;
1810 r
= parse_argv(argc
, argv
);
1814 log_set_target(LOG_TARGET_AUTO
);
1815 log_parse_environment();
1820 r
= mac_selinux_init(NULL
);
1822 log_error_errno(r
, "SELinux setup failed: %m");
1826 if (optind
< argc
) {
1829 for (j
= optind
; j
< argc
; j
++) {
1830 k
= read_config_file(argv
[j
], false);
1831 if (k
< 0 && r
== 0)
1835 _cleanup_strv_free_
char **files
= NULL
;
1838 r
= conf_files_list_nulstr(&files
, ".conf", arg_root
, conf_file_dirs
);
1840 log_error_errno(r
, "Failed to enumerate sysusers.d files: %m");
1844 STRV_FOREACH(f
, files
) {
1845 k
= read_config_file(*f
, true);
1846 if (k
< 0 && r
== 0)
1852 /* Default to default range of 1..SYSTEMD_UID_MAX */
1853 r
= uid_range_add(&uid_range
, &n_uid_range
, 1, SYSTEM_UID_MAX
);
1864 lock
= take_etc_passwd_lock(arg_root
);
1866 log_error_errno(lock
, "Failed to take lock: %m");
1870 r
= load_user_database();
1872 log_error_errno(r
, "Failed to load user database: %m");
1876 r
= load_group_database();
1878 log_error_errno(r
, "Failed to read group database: %m");
1882 HASHMAP_FOREACH(i
, groups
, iterator
)
1885 HASHMAP_FOREACH(i
, users
, iterator
)
1890 log_error_errno(r
, "Failed to write files: %m");
1893 while ((i
= hashmap_steal_first(groups
)))
1896 while ((i
= hashmap_steal_first(users
)))
1899 while ((n
= hashmap_first_key(members
))) {
1900 strv_free(hashmap_steal_first(members
));
1904 hashmap_free(groups
);
1905 hashmap_free(users
);
1906 hashmap_free(members
);
1907 hashmap_free(todo_uids
);
1908 hashmap_free(todo_gids
);
1910 free_database(database_user
, database_uid
);
1911 free_database(database_group
, database_gid
);
1915 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;