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/>.
31 #include "specifier.h"
32 #include "path-util.h"
35 #include "conf-files.h"
38 #include "fileio-label.h"
39 #include "uid-range.h"
40 #include "selinux-util.h"
42 typedef enum ItemType
{
67 static char *arg_root
= NULL
;
69 static const char conf_file_dirs
[] = CONF_DIRS_NULSTR("sysusers");
71 static Hashmap
*users
= NULL
, *groups
= NULL
;
72 static Hashmap
*todo_uids
= NULL
, *todo_gids
= NULL
;
73 static Hashmap
*members
= NULL
;
75 static Hashmap
*database_uid
= NULL
, *database_user
= NULL
;
76 static Hashmap
*database_gid
= NULL
, *database_group
= NULL
;
78 static uid_t search_uid
= UID_INVALID
;
79 static UidRange
*uid_range
= NULL
;
80 static unsigned n_uid_range
= 0;
82 #define fix_root(x) (arg_root ? strjoina(arg_root, x) : x)
84 static int load_user_database(void) {
85 _cleanup_fclose_
FILE *f
= NULL
;
86 const char *passwd_path
;
90 passwd_path
= fix_root("/etc/passwd");
91 f
= fopen(passwd_path
, "re");
93 return errno
== ENOENT
? 0 : -errno
;
95 r
= hashmap_ensure_allocated(&database_user
, &string_hash_ops
);
99 r
= hashmap_ensure_allocated(&database_uid
, NULL
);
104 while ((pw
= fgetpwent(f
))) {
108 n
= strdup(pw
->pw_name
);
112 k
= hashmap_put(database_user
, n
, UID_TO_PTR(pw
->pw_uid
));
113 if (k
< 0 && k
!= -EEXIST
) {
118 q
= hashmap_put(database_uid
, UID_TO_PTR(pw
->pw_uid
), n
);
119 if (q
< 0 && q
!= -EEXIST
) {
130 if (!IN_SET(errno
, 0, ENOENT
))
136 static int load_group_database(void) {
137 _cleanup_fclose_
FILE *f
= NULL
;
138 const char *group_path
;
142 group_path
= fix_root("/etc/group");
143 f
= fopen(group_path
, "re");
145 return errno
== ENOENT
? 0 : -errno
;
147 r
= hashmap_ensure_allocated(&database_group
, &string_hash_ops
);
151 r
= hashmap_ensure_allocated(&database_gid
, NULL
);
156 while ((gr
= fgetgrent(f
))) {
160 n
= strdup(gr
->gr_name
);
164 k
= hashmap_put(database_group
, n
, GID_TO_PTR(gr
->gr_gid
));
165 if (k
< 0 && k
!= -EEXIST
) {
170 q
= hashmap_put(database_gid
, GID_TO_PTR(gr
->gr_gid
), n
);
171 if (q
< 0 && q
!= -EEXIST
) {
182 if (!IN_SET(errno
, 0, ENOENT
))
188 static int make_backup(const char *target
, const char *x
) {
189 _cleanup_close_
int src
= -1;
190 _cleanup_fclose_
FILE *dst
= NULL
;
192 struct timespec ts
[2];
196 src
= open(x
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
198 if (errno
== ENOENT
) /* No backup necessary... */
204 if (fstat(src
, &st
) < 0)
207 r
= fopen_temporary_label(target
, x
, &dst
, &temp
);
211 r
= copy_bytes(src
, fileno(dst
), (off_t
) -1, true);
215 /* Don't fail on chmod() or chown(). If it stays owned by us
216 * and/or unreadable by others, then it isn't too bad... */
218 backup
= strjoina(x
, "-");
220 /* Copy over the access mask */
221 if (fchmod(fileno(dst
), st
.st_mode
& 07777) < 0)
222 log_warning_errno(errno
, "Failed to change mode on %s: %m", backup
);
224 if (fchown(fileno(dst
), st
.st_uid
, st
.st_gid
)< 0)
225 log_warning_errno(errno
, "Failed to change ownership of %s: %m", backup
);
229 if (futimens(fileno(dst
), ts
) < 0)
230 log_warning_errno(errno
, "Failed to fix access and modification time of %s: %m", backup
);
232 if (rename(temp
, backup
) < 0)
242 static int putgrent_with_members(const struct group
*gr
, FILE *group
) {
248 a
= hashmap_get(members
, gr
->gr_name
);
250 _cleanup_strv_free_
char **l
= NULL
;
254 l
= strv_copy(gr
->gr_mem
);
259 if (strv_find(l
, *i
))
262 if (strv_extend(&l
, *i
) < 0)
278 if (putgrent(&t
, group
) != 0)
279 return errno
? -errno
: -EIO
;
286 if (putgrent(gr
, group
) != 0)
287 return errno
? -errno
: -EIO
;
292 static int putsgent_with_members(const struct sgrp
*sg
, FILE *gshadow
) {
298 a
= hashmap_get(members
, sg
->sg_namp
);
300 _cleanup_strv_free_
char **l
= NULL
;
304 l
= strv_copy(sg
->sg_mem
);
309 if (strv_find(l
, *i
))
312 if (strv_extend(&l
, *i
) < 0)
328 if (putsgent(&t
, gshadow
) != 0)
329 return errno
? -errno
: -EIO
;
336 if (putsgent(sg
, gshadow
) != 0)
337 return errno
? -errno
: -EIO
;
342 static int sync_rights(FILE *from
, FILE *to
) {
345 if (fstat(fileno(from
), &st
) < 0)
348 if (fchmod(fileno(to
), st
.st_mode
& 07777) < 0)
351 if (fchown(fileno(to
), st
.st_uid
, st
.st_gid
) < 0)
357 static int write_files(void) {
359 _cleanup_fclose_
FILE *passwd
= NULL
, *group
= NULL
, *shadow
= NULL
, *gshadow
= NULL
;
360 _cleanup_free_
char *passwd_tmp
= NULL
, *group_tmp
= NULL
, *shadow_tmp
= NULL
, *gshadow_tmp
= NULL
;
361 const char *passwd_path
= NULL
, *group_path
= NULL
, *shadow_path
= NULL
, *gshadow_path
= NULL
;
362 bool group_changed
= false;
367 if (hashmap_size(todo_gids
) > 0 || hashmap_size(members
) > 0) {
368 _cleanup_fclose_
FILE *original
= NULL
;
370 /* First we update the actual group list file */
371 group_path
= fix_root("/etc/group");
372 r
= fopen_temporary_label("/etc/group", group_path
, &group
, &group_tmp
);
376 original
= fopen(group_path
, "re");
380 r
= sync_rights(original
, group
);
385 while ((gr
= fgetgrent(original
))) {
386 /* Safety checks against name and GID
387 * collisions. Normally, this should
388 * be unnecessary, but given that we
389 * look at the entries anyway here,
390 * let's make an extra verification
391 * step that we don't generate
392 * duplicate entries. */
394 i
= hashmap_get(groups
, gr
->gr_name
);
395 if (i
&& i
->todo_group
) {
400 if (hashmap_contains(todo_gids
, GID_TO_PTR(gr
->gr_gid
))) {
405 r
= putgrent_with_members(gr
, group
);
409 group_changed
= true;
413 if (!IN_SET(errno
, 0, ENOENT
)) {
418 } else if (errno
!= ENOENT
) {
421 } else if (fchmod(fileno(group
), 0644) < 0) {
426 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
430 .gr_passwd
= (char*) "x",
433 r
= putgrent_with_members(&n
, group
);
437 group_changed
= true;
440 r
= fflush_and_check(group
);
449 /* OK, now also update the shadow file for the group list */
450 gshadow_path
= fix_root("/etc/gshadow");
451 r
= fopen_temporary_label("/etc/gshadow", gshadow_path
, &gshadow
, &gshadow_tmp
);
455 original
= fopen(gshadow_path
, "re");
459 r
= sync_rights(original
, gshadow
);
464 while ((sg
= fgetsgent(original
))) {
466 i
= hashmap_get(groups
, sg
->sg_namp
);
467 if (i
&& i
->todo_group
) {
472 r
= putsgent_with_members(sg
, gshadow
);
476 group_changed
= true;
480 if (!IN_SET(errno
, 0, ENOENT
)) {
485 } else if (errno
!= ENOENT
) {
488 } else if (fchmod(fileno(gshadow
), 0000) < 0) {
493 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
496 .sg_passwd
= (char*) "!!",
499 r
= putsgent_with_members(&n
, gshadow
);
503 group_changed
= true;
506 r
= fflush_and_check(gshadow
);
511 if (hashmap_size(todo_uids
) > 0) {
512 _cleanup_fclose_
FILE *original
= NULL
;
515 /* First we update the user database itself */
516 passwd_path
= fix_root("/etc/passwd");
517 r
= fopen_temporary_label("/etc/passwd", passwd_path
, &passwd
, &passwd_tmp
);
521 original
= fopen(passwd_path
, "re");
525 r
= sync_rights(original
, passwd
);
530 while ((pw
= fgetpwent(original
))) {
532 i
= hashmap_get(users
, pw
->pw_name
);
533 if (i
&& i
->todo_user
) {
538 if (hashmap_contains(todo_uids
, UID_TO_PTR(pw
->pw_uid
))) {
544 if (putpwent(pw
, passwd
) < 0) {
545 r
= errno
? -errno
: -EIO
;
551 if (!IN_SET(errno
, 0, ENOENT
)) {
556 } else if (errno
!= ENOENT
) {
559 } else if (fchmod(fileno(passwd
), 0644) < 0) {
564 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
569 .pw_gecos
= i
->description
,
571 /* "x" means the password is stored in
573 .pw_passwd
= (char*) "x",
575 /* We default to the root directory as home */
576 .pw_dir
= i
->home
? i
->home
: (char*) "/",
578 /* Initialize the shell to nologin,
579 * with one exception: for root we
580 * patch in something special */
581 .pw_shell
= i
->uid
== 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
585 if (putpwent(&n
, passwd
) != 0) {
586 r
= errno
? -errno
: -EIO
;
591 r
= fflush_and_check(passwd
);
600 /* The we update the shadow database */
601 shadow_path
= fix_root("/etc/shadow");
602 r
= fopen_temporary_label("/etc/shadow", shadow_path
, &shadow
, &shadow_tmp
);
606 original
= fopen(shadow_path
, "re");
610 r
= sync_rights(original
, shadow
);
615 while ((sp
= fgetspent(original
))) {
617 i
= hashmap_get(users
, sp
->sp_namp
);
618 if (i
&& i
->todo_user
) {
624 if (putspent(sp
, shadow
) < 0) {
625 r
= errno
? -errno
: -EIO
;
631 if (!IN_SET(errno
, 0, ENOENT
)) {
635 } else if (errno
!= ENOENT
) {
638 } else if (fchmod(fileno(shadow
), 0000) < 0) {
643 lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
644 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
647 .sp_pwdp
= (char*) "!!",
654 .sp_flag
= (unsigned long) -1, /* this appears to be what everybody does ... */
658 if (putspent(&n
, shadow
) != 0) {
659 r
= errno
? -errno
: -EIO
;
664 r
= fflush_and_check(shadow
);
669 /* Make a backup of the old files */
672 r
= make_backup("/etc/group", group_path
);
677 r
= make_backup("/etc/gshadow", gshadow_path
);
684 r
= make_backup("/etc/passwd", passwd_path
);
689 r
= make_backup("/etc/shadow", shadow_path
);
694 /* And make the new files count */
697 if (rename(group_tmp
, group_path
) < 0) {
706 if (rename(gshadow_tmp
, gshadow_path
) < 0) {
717 if (rename(passwd_tmp
, passwd_path
) < 0) {
726 if (rename(shadow_tmp
, shadow_path
) < 0) {
750 static int uid_is_ok(uid_t uid
, const char *name
) {
756 /* Let's see if we already have assigned the UID a second time */
757 if (hashmap_get(todo_uids
, UID_TO_PTR(uid
)))
760 /* Try to avoid using uids that are already used by a group
761 * that doesn't have the same name as our new user. */
762 i
= hashmap_get(todo_gids
, GID_TO_PTR(uid
));
763 if (i
&& !streq(i
->name
, name
))
766 /* Let's check the files directly */
767 if (hashmap_contains(database_uid
, UID_TO_PTR(uid
)))
770 n
= hashmap_get(database_gid
, GID_TO_PTR(uid
));
771 if (n
&& !streq(n
, name
))
774 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
780 if (!IN_SET(errno
, 0, ENOENT
))
784 g
= getgrgid((gid_t
) uid
);
786 if (!streq(g
->gr_name
, name
))
788 } else if (!IN_SET(errno
, 0, ENOENT
))
795 static int root_stat(const char *p
, struct stat
*st
) {
799 if (stat(fix
, st
) < 0)
805 static int read_id_from_file(Item
*i
, uid_t
*_uid
, gid_t
*_gid
) {
807 bool found_uid
= false, found_gid
= false;
813 /* First, try to get the gid directly */
814 if (_gid
&& i
->gid_path
&& root_stat(i
->gid_path
, &st
) >= 0) {
819 /* Then, try to get the uid directly */
820 if ((_uid
|| (_gid
&& !found_gid
))
822 && root_stat(i
->uid_path
, &st
) >= 0) {
827 /* If we need the gid, but had no success yet, also derive it from the uid path */
828 if (_gid
&& !found_gid
) {
834 /* If that didn't work yet, then let's reuse the gid as uid */
835 if (_uid
&& !found_uid
&& i
->gid_path
) {
840 } else if (root_stat(i
->gid_path
, &st
) >= 0) {
841 uid
= (uid_t
) st
.st_gid
;
863 static int add_user(Item
*i
) {
869 /* Check the database directly */
870 z
= hashmap_get(database_user
, i
->name
);
872 log_debug("User %s already exists.", i
->name
);
873 i
->uid
= PTR_TO_UID(z
);
884 p
= getpwnam(i
->name
);
886 log_debug("User %s already exists.", i
->name
);
890 free(i
->description
);
891 i
->description
= strdup(p
->pw_gecos
);
894 if (!IN_SET(errno
, 0, ENOENT
))
895 return log_error_errno(errno
, "Failed to check if user %s already exists: %m", i
->name
);
897 /* And shadow too, just to be sure */
899 sp
= getspnam(i
->name
);
901 log_error("User %s already exists in shadow database, but not in user database.", i
->name
);
904 if (!IN_SET(errno
, 0, ENOENT
))
905 return log_error_errno(errno
, "Failed to check if user %s already exists in shadow database: %m", i
->name
);
908 /* Try to use the suggested numeric uid */
910 r
= uid_is_ok(i
->uid
, i
->name
);
912 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
914 log_debug("Suggested user ID " UID_FMT
" for %s already used.", i
->uid
, i
->name
);
919 /* If that didn't work, try to read it from the specified path */
923 if (read_id_from_file(i
, &c
, NULL
) > 0) {
925 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
926 log_debug("User ID " UID_FMT
" of file not suitable for %s.", c
, i
->name
);
928 r
= uid_is_ok(c
, i
->name
);
930 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
935 log_debug("User ID " UID_FMT
" of file for %s is already used.", c
, i
->name
);
940 /* Otherwise try to reuse the group ID */
941 if (!i
->uid_set
&& i
->gid_set
) {
942 r
= uid_is_ok((uid_t
) i
->gid
, i
->name
);
944 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
946 i
->uid
= (uid_t
) i
->gid
;
951 /* And if that didn't work either, let's try to find a free one */
954 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
956 log_error("No free user ID available for %s.", i
->name
);
960 r
= uid_is_ok(search_uid
, i
->name
);
962 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
971 r
= hashmap_ensure_allocated(&todo_uids
, NULL
);
975 r
= hashmap_put(todo_uids
, UID_TO_PTR(i
->uid
), i
);
980 log_info("Creating user %s (%s) with uid " UID_FMT
" and gid " GID_FMT
".", i
->name
, strna(i
->description
), i
->uid
, i
->gid
);
985 static int gid_is_ok(gid_t gid
) {
989 if (hashmap_get(todo_gids
, GID_TO_PTR(gid
)))
992 /* Avoid reusing gids that are already used by a different user */
993 if (hashmap_get(todo_uids
, UID_TO_PTR(gid
)))
996 if (hashmap_contains(database_gid
, GID_TO_PTR(gid
)))
999 if (hashmap_contains(database_uid
, UID_TO_PTR(gid
)))
1007 if (!IN_SET(errno
, 0, ENOENT
))
1011 p
= getpwuid((uid_t
) gid
);
1014 if (!IN_SET(errno
, 0, ENOENT
))
1021 static int add_group(Item
*i
) {
1027 /* Check the database directly */
1028 z
= hashmap_get(database_group
, i
->name
);
1030 log_debug("Group %s already exists.", i
->name
);
1031 i
->gid
= PTR_TO_GID(z
);
1036 /* Also check NSS */
1041 g
= getgrnam(i
->name
);
1043 log_debug("Group %s already exists.", i
->name
);
1048 if (!IN_SET(errno
, 0, ENOENT
))
1049 return log_error_errno(errno
, "Failed to check if group %s already exists: %m", i
->name
);
1052 /* Try to use the suggested numeric gid */
1054 r
= gid_is_ok(i
->gid
);
1056 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1058 log_debug("Suggested group ID " GID_FMT
" for %s already used.", i
->gid
, i
->name
);
1063 /* Try to reuse the numeric uid, if there's one */
1064 if (!i
->gid_set
&& i
->uid_set
) {
1065 r
= gid_is_ok((gid_t
) i
->uid
);
1067 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1069 i
->gid
= (gid_t
) i
->uid
;
1074 /* If that didn't work, try to read it from the specified path */
1078 if (read_id_from_file(i
, NULL
, &c
) > 0) {
1080 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1081 log_debug("Group ID " GID_FMT
" of file not suitable for %s.", c
, i
->name
);
1085 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1090 log_debug("Group ID " GID_FMT
" of file for %s already used.", c
, i
->name
);
1095 /* And if that didn't work either, let's try to find a free one */
1098 /* We look for new GIDs in the UID pool! */
1099 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1101 log_error("No free group ID available for %s.", i
->name
);
1105 r
= gid_is_ok(search_uid
);
1107 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1113 i
->gid
= search_uid
;
1116 r
= hashmap_ensure_allocated(&todo_gids
, NULL
);
1120 r
= hashmap_put(todo_gids
, GID_TO_PTR(i
->gid
), i
);
1124 i
->todo_group
= true;
1125 log_info("Creating group %s with gid " GID_FMT
".", i
->name
, i
->gid
);
1130 static int process_item(Item
*i
) {
1147 j
= hashmap_get(users
, i
->name
);
1149 /* There's already user to be created for this
1150 * name, let's process that in one step */
1159 j
->gid_path
= strdup(i
->gid_path
);
1167 return add_group(i
);
1171 assert_not_reached("Unknown item type");
1175 static void item_free(Item
*i
) {
1183 free(i
->description
);
1187 DEFINE_TRIVIAL_CLEANUP_FUNC(Item
*, item_free
);
1189 static int add_implicit(void) {
1194 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1196 HASHMAP_FOREACH_KEY(l
, g
, members
, iterator
) {
1200 i
= hashmap_get(groups
, g
);
1202 _cleanup_(item_freep
) Item
*j
= NULL
;
1204 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1212 j
->type
= ADD_GROUP
;
1213 j
->name
= strdup(g
);
1217 r
= hashmap_put(groups
, j
->name
, j
);
1221 log_debug("Adding implicit group '%s' due to m line", j
->name
);
1225 STRV_FOREACH(m
, l
) {
1227 i
= hashmap_get(users
, *m
);
1229 _cleanup_(item_freep
) Item
*j
= NULL
;
1231 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1240 j
->name
= strdup(*m
);
1244 r
= hashmap_put(users
, j
->name
, j
);
1248 log_debug("Adding implicit user '%s' due to m line", j
->name
);
1257 static bool item_equal(Item
*a
, Item
*b
) {
1261 if (a
->type
!= b
->type
)
1264 if (!streq_ptr(a
->name
, b
->name
))
1267 if (!streq_ptr(a
->uid_path
, b
->uid_path
))
1270 if (!streq_ptr(a
->gid_path
, b
->gid_path
))
1273 if (!streq_ptr(a
->description
, b
->description
))
1276 if (a
->uid_set
!= b
->uid_set
)
1279 if (a
->uid_set
&& a
->uid
!= b
->uid
)
1282 if (a
->gid_set
!= b
->gid_set
)
1285 if (a
->gid_set
&& a
->gid
!= b
->gid
)
1288 if (!streq_ptr(a
->home
, b
->home
))
1294 static bool valid_user_group_name(const char *u
) {
1301 if (!(u
[0] >= 'a' && u
[0] <= 'z') &&
1302 !(u
[0] >= 'A' && u
[0] <= 'Z') &&
1306 for (i
= u
+1; *i
; i
++) {
1307 if (!(*i
>= 'a' && *i
<= 'z') &&
1308 !(*i
>= 'A' && *i
<= 'Z') &&
1309 !(*i
>= '0' && *i
<= '9') &&
1315 sz
= sysconf(_SC_LOGIN_NAME_MAX
);
1318 if ((size_t) (i
-u
) > (size_t) sz
)
1321 if ((size_t) (i
-u
) > UT_NAMESIZE
- 1)
1327 static bool valid_gecos(const char *d
) {
1332 if (!utf8_is_valid(d
))
1335 if (string_has_cc(d
, NULL
))
1338 /* Colons are used as field separators, and hence not OK */
1345 static bool valid_home(const char *p
) {
1350 if (!utf8_is_valid(p
))
1353 if (string_has_cc(p
, NULL
))
1356 if (!path_is_absolute(p
))
1359 if (!path_is_safe(p
))
1362 /* Colons are used as field separators, and hence not OK */
1369 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1371 static const Specifier specifier_table
[] = {
1372 { 'm', specifier_machine_id
, NULL
},
1373 { 'b', specifier_boot_id
, NULL
},
1374 { 'H', specifier_host_name
, NULL
},
1375 { 'v', specifier_kernel_release
, NULL
},
1379 _cleanup_free_
char *action
= NULL
, *name
= NULL
, *id
= NULL
, *resolved_name
= NULL
, *resolved_id
= NULL
, *description
= NULL
, *home
= NULL
;
1380 _cleanup_(item_freep
) Item
*i
= NULL
;
1392 r
= unquote_many_words(&p
, &action
, &name
, &id
, &description
, &home
, NULL
);
1394 log_error("[%s:%u] Syntax error.", fname
, line
);
1398 log_error("[%s:%u] Missing action and name columns.", fname
, line
);
1402 log_error("[%s:%u] Trailing garbage.", fname
, line
);
1407 if (strlen(action
) != 1) {
1408 log_error("[%s:%u] Unknown modifier '%s'", fname
, line
, action
);
1412 if (!IN_SET(action
[0], ADD_USER
, ADD_GROUP
, ADD_MEMBER
, ADD_RANGE
)) {
1413 log_error("[%s:%u] Unknown command command type '%c'.", fname
, line
, action
[0]);
1418 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
, "-")) {
1443 r
= specifier_printf(id
, specifier_table
, NULL
, &resolved_id
);
1445 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1450 /* Verify description */
1451 if (isempty(description
) || streq(description
, "-")) {
1457 if (!valid_gecos(description
)) {
1458 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname
, line
, description
);
1464 if (isempty(home
) || streq(home
, "-")) {
1470 if (!valid_home(home
)) {
1471 log_error("[%s:%u] '%s' is not a valid home directory field.", fname
, line
, home
);
1476 switch (action
[0]) {
1479 if (resolved_name
) {
1480 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname
, line
);
1485 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname
, line
);
1490 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname
, line
);
1495 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname
, line
);
1499 r
= uid_range_add_str(&uid_range
, &n_uid_range
, resolved_id
);
1501 log_error("[%s:%u] Invalid UID range %s.", fname
, line
, resolved_id
);
1510 /* Try to extend an existing member or group item */
1512 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname
, line
);
1517 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname
, line
);
1521 if (!valid_user_group_name(resolved_id
)) {
1522 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_id
);
1527 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname
, line
);
1532 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname
, line
);
1536 r
= hashmap_ensure_allocated(&members
, &string_hash_ops
);
1540 l
= hashmap_get(members
, resolved_id
);
1542 /* A list for this group name already exists, let's append to it */
1543 r
= strv_push(&l
, resolved_name
);
1547 resolved_name
= NULL
;
1549 assert_se(hashmap_update(members
, resolved_id
, l
) >= 0);
1551 /* No list for this group name exists yet, create one */
1553 l
= new0(char *, 2);
1557 l
[0] = resolved_name
;
1560 r
= hashmap_put(members
, resolved_id
, l
);
1566 resolved_id
= resolved_name
= NULL
;
1574 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname
, line
);
1578 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1587 if (path_is_absolute(resolved_id
)) {
1588 i
->uid_path
= resolved_id
;
1591 path_kill_slashes(i
->uid_path
);
1593 r
= parse_uid(resolved_id
, &i
->uid
);
1595 log_error("Failed to parse UID: %s", id
);
1603 i
->description
= description
;
1614 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname
, line
);
1619 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname
, line
);
1624 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname
, line
);
1628 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1637 if (path_is_absolute(resolved_id
)) {
1638 i
->gid_path
= resolved_id
;
1641 path_kill_slashes(i
->gid_path
);
1643 r
= parse_gid(resolved_id
, &i
->gid
);
1645 log_error("Failed to parse GID: %s", id
);
1660 i
->type
= action
[0];
1661 i
->name
= resolved_name
;
1662 resolved_name
= NULL
;
1664 existing
= hashmap_get(h
, i
->name
);
1667 /* Two identical items are fine */
1668 if (!item_equal(existing
, i
))
1669 log_warning("Two or more conflicting lines for %s configured, ignoring.", i
->name
);
1674 r
= hashmap_put(h
, i
->name
, i
);
1682 static int read_config_file(const char *fn
, bool ignore_enoent
) {
1683 _cleanup_fclose_
FILE *rf
= NULL
;
1685 char line
[LINE_MAX
];
1694 r
= search_and_fopen_nulstr(fn
, "re", arg_root
, conf_file_dirs
, &rf
);
1696 if (ignore_enoent
&& r
== -ENOENT
)
1699 return log_error_errno(r
, "Failed to open '%s', ignoring: %m", fn
);
1705 FOREACH_LINE(line
, f
, break) {
1712 if (*l
== '#' || *l
== 0)
1715 k
= parse_line(fn
, v
, l
);
1716 if (k
< 0 && r
== 0)
1721 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
1729 static void free_database(Hashmap
*by_name
, Hashmap
*by_id
) {
1733 name
= hashmap_first(by_id
);
1737 hashmap_remove(by_name
, name
);
1739 hashmap_steal_first_key(by_id
);
1743 while ((name
= hashmap_steal_first_key(by_name
)))
1746 hashmap_free(by_name
);
1747 hashmap_free(by_id
);
1750 static void help(void) {
1751 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1752 "Creates system user accounts.\n\n"
1753 " -h --help Show this help\n"
1754 " --version Show package version\n"
1755 " --root=PATH Operate on an alternate filesystem root\n"
1756 , program_invocation_short_name
);
1759 static int parse_argv(int argc
, char *argv
[]) {
1762 ARG_VERSION
= 0x100,
1766 static const struct option options
[] = {
1767 { "help", no_argument
, NULL
, 'h' },
1768 { "version", no_argument
, NULL
, ARG_VERSION
},
1769 { "root", required_argument
, NULL
, ARG_ROOT
},
1778 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
1787 puts(PACKAGE_STRING
);
1788 puts(SYSTEMD_FEATURES
);
1793 arg_root
= path_make_absolute_cwd(optarg
);
1797 path_kill_slashes(arg_root
);
1804 assert_not_reached("Unhandled option");
1810 int main(int argc
, char *argv
[]) {
1812 _cleanup_close_
int lock
= -1;
1818 r
= parse_argv(argc
, argv
);
1822 log_set_target(LOG_TARGET_AUTO
);
1823 log_parse_environment();
1828 r
= mac_selinux_init(NULL
);
1830 log_error_errno(r
, "SELinux setup failed: %m");
1834 if (optind
< argc
) {
1837 for (j
= optind
; j
< argc
; j
++) {
1838 k
= read_config_file(argv
[j
], false);
1839 if (k
< 0 && r
== 0)
1843 _cleanup_strv_free_
char **files
= NULL
;
1846 r
= conf_files_list_nulstr(&files
, ".conf", arg_root
, conf_file_dirs
);
1848 log_error_errno(r
, "Failed to enumerate sysusers.d files: %m");
1852 STRV_FOREACH(f
, files
) {
1853 k
= read_config_file(*f
, true);
1854 if (k
< 0 && r
== 0)
1860 /* Default to default range of 1..SYSTEMD_UID_MAX */
1861 r
= uid_range_add(&uid_range
, &n_uid_range
, 1, SYSTEM_UID_MAX
);
1872 lock
= take_password_lock(arg_root
);
1874 log_error_errno(lock
, "Failed to take lock: %m");
1878 r
= load_user_database();
1880 log_error_errno(r
, "Failed to load user database: %m");
1884 r
= load_group_database();
1886 log_error_errno(r
, "Failed to read group database: %m");
1890 HASHMAP_FOREACH(i
, groups
, iterator
)
1893 HASHMAP_FOREACH(i
, users
, iterator
)
1898 log_error_errno(r
, "Failed to write files: %m");
1901 while ((i
= hashmap_steal_first(groups
)))
1904 while ((i
= hashmap_steal_first(users
)))
1907 while ((n
= hashmap_first_key(members
))) {
1908 strv_free(hashmap_steal_first(members
));
1912 hashmap_free(groups
);
1913 hashmap_free(users
);
1914 hashmap_free(members
);
1915 hashmap_free(todo_uids
);
1916 hashmap_free(todo_gids
);
1918 free_database(database_user
, database_uid
);
1919 free_database(database_group
, database_gid
);
1923 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;