1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "alloc-util.h"
7 #include "conf-files.h"
9 #include "creds-util.h"
11 #include "dissect-image.h"
14 #include "format-util.h"
17 #include "libcrypt-util.h"
18 #include "main-func.h"
19 #include "memory-util.h"
20 #include "mount-util.h"
21 #include "nscd-flush.h"
23 #include "parse-argument.h"
24 #include "path-util.h"
25 #include "pretty-print.h"
26 #include "selinux-util.h"
28 #include "smack-util.h"
29 #include "specifier.h"
30 #include "string-util.h"
32 #include "tmpfile-util-label.h"
33 #include "uid-range.h"
34 #include "user-record.h"
35 #include "user-util.h"
39 typedef enum ItemType
{
62 /* When set the group with the specified gid must exist
63 * and the check if a uid clashes with the gid is skipped.
73 static char *arg_root
= NULL
;
74 static char *arg_image
= NULL
;
75 static bool arg_cat_config
= false;
76 static const char *arg_replace
= NULL
;
77 static bool arg_inline
= false;
78 static PagerFlags arg_pager_flags
= 0;
80 static OrderedHashmap
*users
= NULL
, *groups
= NULL
;
81 static OrderedHashmap
*todo_uids
= NULL
, *todo_gids
= NULL
;
82 static OrderedHashmap
*members
= NULL
;
84 static Hashmap
*database_by_uid
= NULL
, *database_by_username
= NULL
;
85 static Hashmap
*database_by_gid
= NULL
, *database_by_groupname
= NULL
;
86 static Set
*database_users
= NULL
, *database_groups
= NULL
;
88 static uid_t search_uid
= UID_INVALID
;
89 static UidRange
*uid_range
= NULL
;
90 static unsigned n_uid_range
= 0;
92 static UGIDAllocationRange login_defs
= {};
93 static bool login_defs_need_warning
= false;
95 STATIC_DESTRUCTOR_REGISTER(groups
, ordered_hashmap_freep
);
96 STATIC_DESTRUCTOR_REGISTER(users
, ordered_hashmap_freep
);
97 STATIC_DESTRUCTOR_REGISTER(members
, ordered_hashmap_freep
);
98 STATIC_DESTRUCTOR_REGISTER(todo_uids
, ordered_hashmap_freep
);
99 STATIC_DESTRUCTOR_REGISTER(todo_gids
, ordered_hashmap_freep
);
100 STATIC_DESTRUCTOR_REGISTER(database_by_uid
, hashmap_freep
);
101 STATIC_DESTRUCTOR_REGISTER(database_by_username
, hashmap_freep
);
102 STATIC_DESTRUCTOR_REGISTER(database_users
, set_free_freep
);
103 STATIC_DESTRUCTOR_REGISTER(database_by_gid
, hashmap_freep
);
104 STATIC_DESTRUCTOR_REGISTER(database_by_groupname
, hashmap_freep
);
105 STATIC_DESTRUCTOR_REGISTER(database_groups
, set_free_freep
);
106 STATIC_DESTRUCTOR_REGISTER(uid_range
, freep
);
107 STATIC_DESTRUCTOR_REGISTER(arg_root
, freep
);
108 STATIC_DESTRUCTOR_REGISTER(arg_image
, freep
);
110 static int errno_is_not_exists(int code
) {
111 /* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are
113 return IN_SET(code
, 0, ENOENT
, ESRCH
, EBADF
, EPERM
);
116 static void maybe_emit_login_defs_warning(void) {
117 if (!login_defs_need_warning
)
120 if (login_defs
.system_alloc_uid_min
!= SYSTEM_ALLOC_UID_MIN
||
121 login_defs
.system_uid_max
!= SYSTEM_UID_MAX
)
122 log_warning("login.defs specifies UID allocation range "UID_FMT
"–"UID_FMT
123 " that is different than the built-in defaults ("UID_FMT
"–"UID_FMT
")",
124 login_defs
.system_alloc_uid_min
, login_defs
.system_uid_max
,
125 SYSTEM_ALLOC_UID_MIN
, SYSTEM_UID_MAX
);
126 if (login_defs
.system_alloc_gid_min
!= SYSTEM_ALLOC_GID_MIN
||
127 login_defs
.system_gid_max
!= SYSTEM_GID_MAX
)
128 log_warning("login.defs specifies GID allocation range "GID_FMT
"–"GID_FMT
129 " that is different than the built-in defaults ("GID_FMT
"–"GID_FMT
")",
130 login_defs
.system_alloc_gid_min
, login_defs
.system_gid_max
,
131 SYSTEM_ALLOC_GID_MIN
, SYSTEM_GID_MAX
);
133 login_defs_need_warning
= false;
136 static int load_user_database(void) {
137 _cleanup_fclose_
FILE *f
= NULL
;
138 const char *passwd_path
;
142 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
143 f
= fopen(passwd_path
, "re");
145 return errno
== ENOENT
? 0 : -errno
;
147 r
= hashmap_ensure_allocated(&database_by_username
, &string_hash_ops
);
151 r
= hashmap_ensure_allocated(&database_by_uid
, NULL
);
155 r
= set_ensure_allocated(&database_users
, NULL
);
159 while ((r
= fgetpwent_sane(f
, &pw
)) > 0) {
163 n
= strdup(pw
->pw_name
);
167 k
= set_put(database_users
, n
);
173 k
= hashmap_put(database_by_username
, n
, UID_TO_PTR(pw
->pw_uid
));
174 if (k
< 0 && k
!= -EEXIST
)
177 q
= hashmap_put(database_by_uid
, UID_TO_PTR(pw
->pw_uid
), n
);
178 if (q
< 0 && q
!= -EEXIST
)
184 static int load_group_database(void) {
185 _cleanup_fclose_
FILE *f
= NULL
;
186 const char *group_path
;
190 group_path
= prefix_roota(arg_root
, "/etc/group");
191 f
= fopen(group_path
, "re");
193 return errno
== ENOENT
? 0 : -errno
;
195 r
= hashmap_ensure_allocated(&database_by_groupname
, &string_hash_ops
);
199 r
= hashmap_ensure_allocated(&database_by_gid
, NULL
);
203 r
= set_ensure_allocated(&database_groups
, NULL
);
207 while ((r
= fgetgrent_sane(f
, &gr
)) > 0) {
211 n
= strdup(gr
->gr_name
);
215 k
= set_put(database_groups
, n
);
221 k
= hashmap_put(database_by_groupname
, n
, GID_TO_PTR(gr
->gr_gid
));
222 if (k
< 0 && k
!= -EEXIST
)
225 q
= hashmap_put(database_by_gid
, GID_TO_PTR(gr
->gr_gid
), n
);
226 if (q
< 0 && q
!= -EEXIST
)
232 static int make_backup(const char *target
, const char *x
) {
233 _cleanup_(unlink_and_freep
) char *dst_tmp
= NULL
;
234 _cleanup_fclose_
FILE *dst
= NULL
;
235 _cleanup_close_
int src
= -1;
243 src
= open(x
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
245 if (errno
== ENOENT
) /* No backup necessary... */
251 if (fstat(src
, &st
) < 0)
254 r
= fopen_temporary_label(
255 target
, /* The path for which to the lookup the label */
256 x
, /* Where we want the file actually to end up */
258 &dst_tmp
/* The temporary file we write to */);
262 r
= copy_bytes(src
, fileno(dst
), UINT64_MAX
, COPY_REFLINK
);
266 backup
= strjoina(x
, "-");
268 /* Copy over the access mask. Don't fail on chmod() or chown(). If it stays owned by us and/or
269 * unreadable by others, then it isn't too bad... */
270 r
= fchmod_and_chown(fileno(dst
), st
.st_mode
& 07777, st
.st_uid
, st
.st_gid
);
272 log_warning_errno(r
, "Failed to change access mode or ownership of %s: %m", backup
);
274 if (futimens(fileno(dst
), (const struct timespec
[2]) { st
.st_atim
, st
.st_mtim
}) < 0)
275 log_warning_errno(errno
, "Failed to fix access and modification time of %s: %m", backup
);
277 r
= fsync_full(fileno(dst
));
281 if (rename(dst_tmp
, backup
) < 0)
284 dst_tmp
= mfree(dst_tmp
); /* disable the unlink_and_freep() hook now that the file has been renamed */
288 static int putgrent_with_members(const struct group
*gr
, FILE *group
) {
294 a
= ordered_hashmap_get(members
, gr
->gr_name
);
296 _cleanup_strv_free_
char **l
= NULL
;
300 l
= strv_copy(gr
->gr_mem
);
305 if (strv_find(l
, *i
))
308 if (strv_extend(&l
, *i
) < 0)
324 r
= putgrent_sane(&t
, group
);
325 return r
< 0 ? r
: 1;
329 return putgrent_sane(gr
, group
);
333 static int putsgent_with_members(const struct sgrp
*sg
, FILE *gshadow
) {
339 a
= ordered_hashmap_get(members
, sg
->sg_namp
);
341 _cleanup_strv_free_
char **l
= NULL
;
345 l
= strv_copy(sg
->sg_mem
);
350 if (strv_find(l
, *i
))
353 if (strv_extend(&l
, *i
) < 0)
369 r
= putsgent_sane(&t
, gshadow
);
370 return r
< 0 ? r
: 1;
374 return putsgent_sane(sg
, gshadow
);
378 static const char* default_shell(uid_t uid
) {
379 return uid
== 0 ? "/bin/sh" : NOLOGIN
;
382 static int write_temporary_passwd(const char *passwd_path
, FILE **tmpfile
, char **tmpfile_path
) {
383 _cleanup_fclose_
FILE *original
= NULL
, *passwd
= NULL
;
384 _cleanup_(unlink_and_freep
) char *passwd_tmp
= NULL
;
385 struct passwd
*pw
= NULL
;
389 if (ordered_hashmap_size(todo_uids
) == 0)
392 r
= fopen_temporary_label("/etc/passwd", passwd_path
, &passwd
, &passwd_tmp
);
394 return log_debug_errno(r
, "Failed to open temporary copy of %s: %m", passwd_path
);
396 original
= fopen(passwd_path
, "re");
399 /* Allow fallback path for when /proc is not mounted. On any normal system /proc will be
400 * mounted, but e.g. when 'dnf --installroot' is used, it might not be. There is no security
401 * relevance here, since the environment is ultimately trusted, and not requiring /proc makes
402 * it easier to depend on sysusers in packaging scripts and suchlike. */
403 r
= copy_rights_with_fallback(fileno(original
), fileno(passwd
), passwd_tmp
);
405 return log_debug_errno(r
, "Failed to copy permissions from %s to %s: %m",
406 passwd_path
, passwd_tmp
);
408 while ((r
= fgetpwent_sane(original
, &pw
)) > 0) {
409 i
= ordered_hashmap_get(users
, pw
->pw_name
);
410 if (i
&& i
->todo_user
)
411 return log_error_errno(SYNTHETIC_ERRNO(EEXIST
),
412 "%s: User \"%s\" already exists.",
413 passwd_path
, pw
->pw_name
);
415 if (ordered_hashmap_contains(todo_uids
, UID_TO_PTR(pw
->pw_uid
)))
416 return log_error_errno(SYNTHETIC_ERRNO(EEXIST
),
417 "%s: Detected collision for UID " UID_FMT
".",
418 passwd_path
, pw
->pw_uid
);
420 /* Make sure we keep the NIS entries (if any) at the end. */
421 if (IN_SET(pw
->pw_name
[0], '+', '-'))
424 r
= putpwent_sane(pw
, passwd
);
426 return log_debug_errno(r
, "Failed to add existing user \"%s\" to temporary passwd file: %m",
430 return log_debug_errno(r
, "Failed to read %s: %m", passwd_path
);
434 return log_debug_errno(errno
, "Failed to open %s: %m", passwd_path
);
435 if (fchmod(fileno(passwd
), 0644) < 0)
436 return log_debug_errno(errno
, "Failed to fchmod %s: %m", passwd_tmp
);
439 ORDERED_HASHMAP_FOREACH(i
, todo_uids
) {
440 _cleanup_free_
char *creds_shell
= NULL
, *cn
= NULL
;
446 .pw_gecos
= i
->description
,
448 /* "x" means the password is stored in the shadow file */
449 .pw_passwd
= (char*) PASSWORD_SEE_SHADOW
,
451 /* We default to the root directory as home */
452 .pw_dir
= i
->home
?: (char*) "/",
454 /* Initialize the shell to nologin, with one exception:
455 * for root we patch in something special */
456 .pw_shell
= i
->shell
?: (char*) default_shell(i
->uid
),
459 /* Try to pick up the shell for this account via the credentials logic */
460 cn
= strjoin("passwd.shell.", i
->name
);
464 r
= read_credential(cn
, (void**) &creds_shell
, NULL
);
466 log_debug_errno(r
, "Couldn't read credential '%s', ignoring: %m", cn
);
468 n
.pw_shell
= creds_shell
;
470 r
= putpwent_sane(&n
, passwd
);
472 return log_debug_errno(r
, "Failed to add new user \"%s\" to temporary passwd file: %m",
476 /* Append the remaining NIS entries if any */
478 r
= putpwent_sane(pw
, passwd
);
480 return log_debug_errno(r
, "Failed to add existing user \"%s\" to temporary passwd file: %m",
483 r
= fgetpwent_sane(original
, &pw
);
485 return log_debug_errno(r
, "Failed to read %s: %m", passwd_path
);
490 r
= fflush_and_check(passwd
);
492 return log_debug_errno(r
, "Failed to flush %s: %m", passwd_tmp
);
494 *tmpfile
= TAKE_PTR(passwd
);
495 *tmpfile_path
= TAKE_PTR(passwd_tmp
);
500 static int write_temporary_shadow(const char *shadow_path
, FILE **tmpfile
, char **tmpfile_path
) {
501 _cleanup_fclose_
FILE *original
= NULL
, *shadow
= NULL
;
502 _cleanup_(unlink_and_freep
) char *shadow_tmp
= NULL
;
503 struct spwd
*sp
= NULL
;
508 if (ordered_hashmap_size(todo_uids
) == 0)
511 r
= fopen_temporary_label("/etc/shadow", shadow_path
, &shadow
, &shadow_tmp
);
513 return log_debug_errno(r
, "Failed to open temporary copy of %s: %m", shadow_path
);
515 lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
517 original
= fopen(shadow_path
, "re");
520 r
= copy_rights_with_fallback(fileno(original
), fileno(shadow
), shadow_tmp
);
522 return log_debug_errno(r
, "Failed to copy permissions from %s to %s: %m",
523 shadow_path
, shadow_tmp
);
525 while ((r
= fgetspent_sane(original
, &sp
)) > 0) {
526 i
= ordered_hashmap_get(users
, sp
->sp_namp
);
527 if (i
&& i
->todo_user
) {
528 /* we will update the existing entry */
529 sp
->sp_lstchg
= lstchg
;
531 /* only the /etc/shadow stage is left, so we can
532 * safely remove the item from the todo set */
533 i
->todo_user
= false;
534 ordered_hashmap_remove(todo_uids
, UID_TO_PTR(i
->uid
));
537 /* Make sure we keep the NIS entries (if any) at the end. */
538 if (IN_SET(sp
->sp_namp
[0], '+', '-'))
541 r
= putspent_sane(sp
, shadow
);
543 return log_debug_errno(r
, "Failed to add existing user \"%s\" to temporary shadow file: %m",
548 return log_debug_errno(r
, "Failed to read %s: %m", shadow_path
);
552 return log_debug_errno(errno
, "Failed to open %s: %m", shadow_path
);
553 if (fchmod(fileno(shadow
), 0000) < 0)
554 return log_debug_errno(errno
, "Failed to fchmod %s: %m", shadow_tmp
);
557 ORDERED_HASHMAP_FOREACH(i
, todo_uids
) {
558 _cleanup_(erase_and_freep
) char *creds_password
= NULL
;
559 _cleanup_free_
char *cn
= NULL
;
563 .sp_pwdp
= (char*) PASSWORD_LOCKED_AND_INVALID
,
570 .sp_flag
= ULONG_MAX
, /* this appears to be what everybody does ... */
573 /* Try to pick up the password for this account via the credentials logic */
574 cn
= strjoin("passwd.hashed-password.", i
->name
);
578 r
= read_credential(cn
, (void**) &creds_password
, NULL
);
580 _cleanup_(erase_and_freep
) char *plaintext_password
= NULL
;
583 cn
= strjoin("passwd.plaintext-password.", i
->name
);
587 r
= read_credential(cn
, (void**) &plaintext_password
, NULL
);
589 log_debug_errno(r
, "Couldn't read credential '%s', ignoring: %m", cn
);
591 r
= hash_password(plaintext_password
, &creds_password
);
593 return log_debug_errno(r
, "Failed to hash password: %m");
596 log_debug_errno(r
, "Couldn't read credential '%s', ignoring: %m", cn
);
599 n
.sp_pwdp
= creds_password
;
601 r
= putspent_sane(&n
, shadow
);
603 return log_debug_errno(r
, "Failed to add new user \"%s\" to temporary shadow file: %m",
607 /* Append the remaining NIS entries if any */
609 r
= putspent_sane(sp
, shadow
);
611 return log_debug_errno(r
, "Failed to add existing user \"%s\" to temporary shadow file: %m",
614 r
= fgetspent_sane(original
, &sp
);
616 return log_debug_errno(r
, "Failed to read %s: %m", shadow_path
);
620 if (!IN_SET(errno
, 0, ENOENT
))
623 r
= fflush_sync_and_check(shadow
);
625 return log_debug_errno(r
, "Failed to flush %s: %m", shadow_tmp
);
627 *tmpfile
= TAKE_PTR(shadow
);
628 *tmpfile_path
= TAKE_PTR(shadow_tmp
);
633 static int write_temporary_group(const char *group_path
, FILE **tmpfile
, char **tmpfile_path
) {
634 _cleanup_fclose_
FILE *original
= NULL
, *group
= NULL
;
635 _cleanup_(unlink_and_freep
) char *group_tmp
= NULL
;
636 bool group_changed
= false;
637 struct group
*gr
= NULL
;
641 if (ordered_hashmap_size(todo_gids
) == 0 && ordered_hashmap_size(members
) == 0)
644 r
= fopen_temporary_label("/etc/group", group_path
, &group
, &group_tmp
);
646 return log_debug_errno(r
, "Failed to open temporary copy of %s: %m", group_path
);
648 original
= fopen(group_path
, "re");
651 r
= copy_rights_with_fallback(fileno(original
), fileno(group
), group_tmp
);
653 return log_debug_errno(r
, "Failed to copy permissions from %s to %s: %m",
654 group_path
, group_tmp
);
656 while ((r
= fgetgrent_sane(original
, &gr
)) > 0) {
657 /* Safety checks against name and GID collisions. Normally,
658 * this should be unnecessary, but given that we look at the
659 * entries anyway here, let's make an extra verification
660 * step that we don't generate duplicate entries. */
662 i
= ordered_hashmap_get(groups
, gr
->gr_name
);
663 if (i
&& i
->todo_group
)
664 return log_error_errno(SYNTHETIC_ERRNO(EEXIST
),
665 "%s: Group \"%s\" already exists.",
666 group_path
, gr
->gr_name
);
668 if (ordered_hashmap_contains(todo_gids
, GID_TO_PTR(gr
->gr_gid
)))
669 return log_error_errno(SYNTHETIC_ERRNO(EEXIST
),
670 "%s: Detected collision for GID " GID_FMT
".",
671 group_path
, gr
->gr_gid
);
673 /* Make sure we keep the NIS entries (if any) at the end. */
674 if (IN_SET(gr
->gr_name
[0], '+', '-'))
677 r
= putgrent_with_members(gr
, group
);
679 return log_debug_errno(r
, "Failed to add existing group \"%s\" to temporary group file: %m",
682 group_changed
= true;
685 return log_debug_errno(r
, "Failed to read %s: %m", group_path
);
689 return log_debug_errno(errno
, "Failed to open %s: %m", group_path
);
690 if (fchmod(fileno(group
), 0644) < 0)
691 return log_debug_errno(errno
, "Failed to fchmod %s: %m", group_tmp
);
694 ORDERED_HASHMAP_FOREACH(i
, todo_gids
) {
698 .gr_passwd
= (char*) PASSWORD_SEE_SHADOW
,
701 r
= putgrent_with_members(&n
, group
);
703 return log_debug_errno(r
, "Failed to add new group \"%s\" to temporary group file: %m",
706 group_changed
= true;
709 /* Append the remaining NIS entries if any */
711 r
= putgrent_sane(gr
, group
);
713 return log_debug_errno(r
, "Failed to add existing group \"%s\" to temporary group file: %m",
716 r
= fgetgrent_sane(original
, &gr
);
718 return log_debug_errno(r
, "Failed to read %s: %m", group_path
);
723 r
= fflush_sync_and_check(group
);
725 return log_debug_errno(r
, "Failed to flush %s: %m", group_tmp
);
728 *tmpfile
= TAKE_PTR(group
);
729 *tmpfile_path
= TAKE_PTR(group_tmp
);
734 static int write_temporary_gshadow(const char * gshadow_path
, FILE **tmpfile
, char **tmpfile_path
) {
736 _cleanup_fclose_
FILE *original
= NULL
, *gshadow
= NULL
;
737 _cleanup_(unlink_and_freep
) char *gshadow_tmp
= NULL
;
738 bool group_changed
= false;
742 if (ordered_hashmap_size(todo_gids
) == 0 && ordered_hashmap_size(members
) == 0)
745 r
= fopen_temporary_label("/etc/gshadow", gshadow_path
, &gshadow
, &gshadow_tmp
);
747 return log_debug_errno(r
, "Failed to open temporary copy of %s: %m", gshadow_path
);
749 original
= fopen(gshadow_path
, "re");
753 r
= copy_rights_with_fallback(fileno(original
), fileno(gshadow
), gshadow_tmp
);
755 return log_debug_errno(r
, "Failed to copy permissions from %s to %s: %m",
756 gshadow_path
, gshadow_tmp
);
758 while ((r
= fgetsgent_sane(original
, &sg
)) > 0) {
760 i
= ordered_hashmap_get(groups
, sg
->sg_namp
);
761 if (i
&& i
->todo_group
)
762 return log_error_errno(SYNTHETIC_ERRNO(EEXIST
),
763 "%s: Group \"%s\" already exists.",
764 gshadow_path
, sg
->sg_namp
);
766 r
= putsgent_with_members(sg
, gshadow
);
768 return log_debug_errno(r
, "Failed to add existing group \"%s\" to temporary gshadow file: %m",
771 group_changed
= true;
778 return log_debug_errno(errno
, "Failed to open %s: %m", gshadow_path
);
779 if (fchmod(fileno(gshadow
), 0000) < 0)
780 return log_debug_errno(errno
, "Failed to fchmod %s: %m", gshadow_tmp
);
783 ORDERED_HASHMAP_FOREACH(i
, todo_gids
) {
786 .sg_passwd
= (char*) PASSWORD_LOCKED_AND_INVALID
,
789 r
= putsgent_with_members(&n
, gshadow
);
791 return log_debug_errno(r
, "Failed to add new group \"%s\" to temporary gshadow file: %m",
794 group_changed
= true;
797 r
= fflush_sync_and_check(gshadow
);
799 return log_debug_errno(r
, "Failed to flush %s: %m", gshadow_tmp
);
802 *tmpfile
= TAKE_PTR(gshadow
);
803 *tmpfile_path
= TAKE_PTR(gshadow_tmp
);
809 static int write_files(void) {
810 _cleanup_fclose_
FILE *passwd
= NULL
, *group
= NULL
, *shadow
= NULL
, *gshadow
= NULL
;
811 _cleanup_(unlink_and_freep
) char *passwd_tmp
= NULL
, *group_tmp
= NULL
, *shadow_tmp
= NULL
, *gshadow_tmp
= NULL
;
812 const char *passwd_path
, *shadow_path
, *group_path
, *gshadow_path
;
815 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
816 shadow_path
= prefix_roota(arg_root
, "/etc/shadow");
817 group_path
= prefix_roota(arg_root
, "/etc/group");
818 gshadow_path
= prefix_roota(arg_root
, "/etc/gshadow");
820 r
= write_temporary_group(group_path
, &group
, &group_tmp
);
824 r
= write_temporary_gshadow(gshadow_path
, &gshadow
, &gshadow_tmp
);
828 r
= write_temporary_passwd(passwd_path
, &passwd
, &passwd_tmp
);
832 r
= write_temporary_shadow(shadow_path
, &shadow
, &shadow_tmp
);
836 /* Make a backup of the old files */
838 r
= make_backup("/etc/group", group_path
);
840 return log_debug_errno(r
, "Failed to make backup %s: %m", group_path
);
843 r
= make_backup("/etc/gshadow", gshadow_path
);
845 return log_debug_errno(r
, "Failed to make backup %s: %m", gshadow_path
);
849 r
= make_backup("/etc/passwd", passwd_path
);
851 return log_debug_errno(r
, "Failed to make backup %s: %m", passwd_path
);
854 r
= make_backup("/etc/shadow", shadow_path
);
856 return log_debug_errno(r
, "Failed to make backup %s: %m", shadow_path
);
859 /* And make the new files count */
861 r
= rename_and_apply_smack_floor_label(group_tmp
, group_path
);
863 return log_debug_errno(r
, "Failed to rename %s to %s: %m",
864 group_tmp
, group_path
);
865 group_tmp
= mfree(group_tmp
);
867 if (!arg_root
&& !arg_image
)
868 (void) nscd_flush_cache(STRV_MAKE("group"));
871 r
= rename_and_apply_smack_floor_label(gshadow_tmp
, gshadow_path
);
873 return log_debug_errno(r
, "Failed to rename %s to %s: %m",
874 gshadow_tmp
, gshadow_path
);
876 gshadow_tmp
= mfree(gshadow_tmp
);
880 r
= rename_and_apply_smack_floor_label(passwd_tmp
, passwd_path
);
882 return log_debug_errno(r
, "Failed to rename %s to %s: %m",
883 passwd_tmp
, passwd_path
);
885 passwd_tmp
= mfree(passwd_tmp
);
887 if (!arg_root
&& !arg_image
)
888 (void) nscd_flush_cache(STRV_MAKE("passwd"));
891 r
= rename_and_apply_smack_floor_label(shadow_tmp
, shadow_path
);
893 return log_debug_errno(r
, "Failed to rename %s to %s: %m",
894 shadow_tmp
, shadow_path
);
896 shadow_tmp
= mfree(shadow_tmp
);
902 static int uid_is_ok(uid_t uid
, const char *name
, bool check_with_gid
) {
904 /* Let's see if we already have assigned the UID a second time */
905 if (ordered_hashmap_get(todo_uids
, UID_TO_PTR(uid
)))
908 /* Try to avoid using uids that are already used by a group
909 * that doesn't have the same name as our new user. */
910 if (check_with_gid
) {
913 i
= ordered_hashmap_get(todo_gids
, GID_TO_PTR(uid
));
914 if (i
&& !streq(i
->name
, name
))
918 /* Let's check the files directly */
919 if (hashmap_contains(database_by_uid
, UID_TO_PTR(uid
)))
922 if (check_with_gid
) {
925 n
= hashmap_get(database_by_gid
, GID_TO_PTR(uid
));
926 if (n
&& !streq(n
, name
))
930 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
939 if (!IN_SET(errno
, 0, ENOENT
))
942 if (check_with_gid
) {
944 g
= getgrgid((gid_t
) uid
);
946 if (!streq(g
->gr_name
, name
))
948 } else if (!IN_SET(errno
, 0, ENOENT
))
956 static int root_stat(const char *p
, struct stat
*st
) {
959 fix
= prefix_roota(arg_root
, p
);
960 if (stat(fix
, st
) < 0)
966 static int read_id_from_file(Item
*i
, uid_t
*_uid
, gid_t
*_gid
) {
968 bool found_uid
= false, found_gid
= false;
974 /* First, try to get the gid directly */
975 if (_gid
&& i
->gid_path
&& root_stat(i
->gid_path
, &st
) >= 0) {
980 /* Then, try to get the uid directly */
981 if ((_uid
|| (_gid
&& !found_gid
))
983 && root_stat(i
->uid_path
, &st
) >= 0) {
988 /* If we need the gid, but had no success yet, also derive it from the uid path */
989 if (_gid
&& !found_gid
) {
995 /* If that didn't work yet, then let's reuse the gid as uid */
996 if (_uid
&& !found_uid
&& i
->gid_path
) {
1001 } else if (root_stat(i
->gid_path
, &st
) >= 0) {
1002 uid
= (uid_t
) st
.st_gid
;
1024 static int add_user(Item
*i
) {
1030 /* Check the database directly */
1031 z
= hashmap_get(database_by_username
, i
->name
);
1033 log_debug("User %s already exists.", i
->name
);
1034 i
->uid
= PTR_TO_UID(z
);
1042 /* Also check NSS */
1044 p
= getpwnam(i
->name
);
1046 log_debug("User %s already exists.", i
->name
);
1050 r
= free_and_strdup(&i
->description
, p
->pw_gecos
);
1056 if (!errno_is_not_exists(errno
))
1057 return log_error_errno(errno
, "Failed to check if user %s already exists: %m", i
->name
);
1060 /* Try to use the suggested numeric uid */
1062 r
= uid_is_ok(i
->uid
, i
->name
, !i
->id_set_strict
);
1064 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
1066 log_debug("Suggested user ID " UID_FMT
" for %s already used.", i
->uid
, i
->name
);
1071 /* If that didn't work, try to read it from the specified path */
1075 if (read_id_from_file(i
, &c
, NULL
) > 0) {
1077 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1078 log_debug("User ID " UID_FMT
" of file not suitable for %s.", c
, i
->name
);
1080 r
= uid_is_ok(c
, i
->name
, true);
1082 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
1087 log_debug("User ID " UID_FMT
" of file for %s is already used.", c
, i
->name
);
1092 /* Otherwise, try to reuse the group ID */
1093 if (!i
->uid_set
&& i
->gid_set
) {
1094 r
= uid_is_ok((uid_t
) i
->gid
, i
->name
, true);
1096 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
1098 i
->uid
= (uid_t
) i
->gid
;
1103 /* And if that didn't work either, let's try to find a free one */
1105 maybe_emit_login_defs_warning();
1108 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1110 return log_error_errno(r
, "No free user ID available for %s.", i
->name
);
1112 r
= uid_is_ok(search_uid
, i
->name
, true);
1114 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
1120 i
->uid
= search_uid
;
1123 r
= ordered_hashmap_ensure_put(&todo_uids
, NULL
, UID_TO_PTR(i
->uid
), i
);
1125 return log_error_errno(r
, "Requested user %s with uid " UID_FMT
" and gid" GID_FMT
" to be created is duplicated "
1126 "or conflicts with another user.", i
->name
, i
->uid
, i
->gid
);
1130 return log_error_errno(r
, "Failed to store user %s with uid " UID_FMT
" and gid " GID_FMT
" to be created: %m",
1131 i
->name
, i
->uid
, i
->gid
);
1133 i
->todo_user
= true;
1134 log_info("Creating user %s (%s) with uid " UID_FMT
" and gid " GID_FMT
".",
1135 i
->name
, strna(i
->description
), i
->uid
, i
->gid
);
1140 static int gid_is_ok(gid_t gid
) {
1144 if (ordered_hashmap_get(todo_gids
, GID_TO_PTR(gid
)))
1147 /* Avoid reusing gids that are already used by a different user */
1148 if (ordered_hashmap_get(todo_uids
, UID_TO_PTR(gid
)))
1151 if (hashmap_contains(database_by_gid
, GID_TO_PTR(gid
)))
1154 if (hashmap_contains(database_by_uid
, UID_TO_PTR(gid
)))
1162 if (!IN_SET(errno
, 0, ENOENT
))
1166 p
= getpwuid((uid_t
) gid
);
1169 if (!IN_SET(errno
, 0, ENOENT
))
1176 static int get_gid_by_name(const char *name
, gid_t
*gid
) {
1181 /* Check the database directly */
1182 z
= hashmap_get(database_by_groupname
, name
);
1184 *gid
= PTR_TO_GID(z
);
1188 /* Also check NSS */
1198 if (!errno_is_not_exists(errno
))
1199 return log_error_errno(errno
, "Failed to check if group %s already exists: %m", name
);
1205 static int add_group(Item
*i
) {
1210 r
= get_gid_by_name(i
->name
, &i
->gid
);
1214 log_debug("Group %s already exists.", i
->name
);
1219 /* Try to use the suggested numeric gid */
1221 r
= gid_is_ok(i
->gid
);
1223 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1224 if (i
->id_set_strict
) {
1225 /* If we require the gid to already exist we can return here:
1226 * r > 0: means the gid does not exist -> fail
1227 * r == 0: means the gid exists -> nothing more to do.
1230 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1231 "Failed to create %s: please create GID %d",
1237 log_debug("Suggested group ID " GID_FMT
" for %s already used.", i
->gid
, i
->name
);
1242 /* Try to reuse the numeric uid, if there's one */
1243 if (!i
->gid_set
&& i
->uid_set
) {
1244 r
= gid_is_ok((gid_t
) i
->uid
);
1246 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1248 i
->gid
= (gid_t
) i
->uid
;
1253 /* If that didn't work, try to read it from the specified path */
1257 if (read_id_from_file(i
, NULL
, &c
) > 0) {
1259 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1260 log_debug("Group ID " GID_FMT
" of file not suitable for %s.", c
, i
->name
);
1264 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1269 log_debug("Group ID " GID_FMT
" of file for %s already used.", c
, i
->name
);
1274 /* And if that didn't work either, let's try to find a free one */
1276 maybe_emit_login_defs_warning();
1279 /* We look for new GIDs in the UID pool! */
1280 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1282 return log_error_errno(r
, "No free group ID available for %s.", i
->name
);
1284 r
= gid_is_ok(search_uid
);
1286 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1292 i
->gid
= search_uid
;
1295 r
= ordered_hashmap_ensure_put(&todo_gids
, NULL
, GID_TO_PTR(i
->gid
), i
);
1297 return log_error_errno(r
, "Requested group %s with gid "GID_FMT
" to be created is duplicated or conflicts with another user.", i
->name
, i
->gid
);
1301 return log_error_errno(r
, "Failed to store group %s with gid " GID_FMT
" to be created: %m", i
->name
, i
->gid
);
1303 i
->todo_group
= true;
1304 log_info("Creating group %s with gid " GID_FMT
".", i
->name
, i
->gid
);
1309 static int process_item(Item
*i
) {
1319 j
= ordered_hashmap_get(groups
, i
->group_name
?: i
->name
);
1320 if (j
&& j
->todo_group
) {
1321 /* When a group with the target name is already in queue,
1322 * use the information about the group and do not create
1323 * duplicated group entry. */
1324 i
->gid_set
= j
->gid_set
;
1326 i
->id_set_strict
= true;
1327 } else if (i
->group_name
) {
1328 /* When a group name was given instead of a GID and it's
1329 * not in queue, then it must already exist. */
1330 r
= get_gid_by_name(i
->group_name
, &i
->gid
);
1332 return log_error_errno(r
, "Group %s not found.", i
->group_name
);
1334 i
->id_set_strict
= true;
1345 return add_group(i
);
1348 assert_not_reached();
1352 static Item
* item_free(Item
*i
) {
1357 free(i
->group_name
);
1360 free(i
->description
);
1366 DEFINE_TRIVIAL_CLEANUP_FUNC(Item
*, item_free
);
1367 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(item_hash_ops
, char, string_hash_func
, string_compare_func
, Item
, item_free
);
1369 static int add_implicit(void) {
1373 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1374 ORDERED_HASHMAP_FOREACH_KEY(l
, g
, members
) {
1378 if (!ordered_hashmap_get(users
, *m
)) {
1379 _cleanup_(item_freep
) Item
*j
= NULL
;
1386 j
->name
= strdup(*m
);
1390 r
= ordered_hashmap_ensure_put(&users
, &item_hash_ops
, j
->name
, j
);
1394 return log_error_errno(r
, "Failed to add implicit user '%s': %m", j
->name
);
1396 log_debug("Adding implicit user '%s' due to m line", j
->name
);
1400 if (!(ordered_hashmap_get(users
, g
) ||
1401 ordered_hashmap_get(groups
, g
))) {
1402 _cleanup_(item_freep
) Item
*j
= NULL
;
1408 j
->type
= ADD_GROUP
;
1409 j
->name
= strdup(g
);
1413 r
= ordered_hashmap_ensure_put(&groups
, &item_hash_ops
, j
->name
, j
);
1417 return log_error_errno(r
, "Failed to add implicit group '%s': %m", j
->name
);
1419 log_debug("Adding implicit group '%s' due to m line", j
->name
);
1427 static bool item_equal(Item
*a
, Item
*b
) {
1431 if (a
->type
!= b
->type
)
1434 if (!streq_ptr(a
->name
, b
->name
))
1437 if (!streq_ptr(a
->uid_path
, b
->uid_path
))
1440 if (!streq_ptr(a
->gid_path
, b
->gid_path
))
1443 if (!streq_ptr(a
->description
, b
->description
))
1446 if (a
->uid_set
!= b
->uid_set
)
1449 if (a
->uid_set
&& a
->uid
!= b
->uid
)
1452 if (a
->gid_set
!= b
->gid_set
)
1455 if (a
->gid_set
&& a
->gid
!= b
->gid
)
1458 if (!streq_ptr(a
->home
, b
->home
))
1461 if (!streq_ptr(a
->shell
, b
->shell
))
1467 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1469 _cleanup_free_
char *action
= NULL
,
1470 *name
= NULL
, *resolved_name
= NULL
,
1471 *id
= NULL
, *resolved_id
= NULL
,
1472 *description
= NULL
, *resolved_description
= NULL
,
1473 *home
= NULL
, *resolved_home
= NULL
,
1474 *shell
= NULL
, *resolved_shell
= NULL
;
1475 _cleanup_(item_freep
) Item
*i
= NULL
;
1487 r
= extract_many_words(&p
, NULL
, EXTRACT_UNQUOTE
,
1488 &action
, &name
, &id
, &description
, &home
, &shell
, NULL
);
1490 return log_error_errno(r
, "[%s:%u] Syntax error.", fname
, line
);
1492 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1493 "[%s:%u] Missing action and name columns.", fname
, line
);
1495 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1496 "[%s:%u] Trailing garbage.", fname
, line
);
1499 if (strlen(action
) != 1)
1500 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1501 "[%s:%u] Unknown modifier '%s'", fname
, line
, action
);
1503 if (!IN_SET(action
[0], ADD_USER
, ADD_GROUP
, ADD_MEMBER
, ADD_RANGE
))
1504 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
1505 "[%s:%u] Unknown command type '%c'.", fname
, line
, action
[0]);
1508 if (empty_or_dash(name
))
1512 r
= specifier_printf(name
, NAME_MAX
, system_and_tmp_specifier_table
, arg_root
, NULL
, &resolved_name
);
1514 return log_error_errno(r
, "[%s:%u] Failed to replace specifiers in '%s': %m", fname
, line
, name
);
1516 if (!valid_user_group_name(resolved_name
, 0))
1517 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1518 "[%s:%u] '%s' is not a valid user or group name.",
1519 fname
, line
, resolved_name
);
1523 if (empty_or_dash(id
))
1527 r
= specifier_printf(id
, PATH_MAX
-1, system_and_tmp_specifier_table
, arg_root
, NULL
, &resolved_id
);
1529 return log_error_errno(r
, "[%s:%u] Failed to replace specifiers in '%s': %m",
1533 /* Verify description */
1534 if (empty_or_dash(description
))
1535 description
= mfree(description
);
1538 r
= specifier_printf(description
, LONG_LINE_MAX
, system_and_tmp_specifier_table
, arg_root
, NULL
, &resolved_description
);
1540 return log_error_errno(r
, "[%s:%u] Failed to replace specifiers in '%s': %m",
1541 fname
, line
, description
);
1543 if (!valid_gecos(resolved_description
))
1544 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1545 "[%s:%u] '%s' is not a valid GECOS field.",
1546 fname
, line
, resolved_description
);
1550 if (empty_or_dash(home
))
1554 r
= specifier_printf(home
, PATH_MAX
-1, system_and_tmp_specifier_table
, arg_root
, NULL
, &resolved_home
);
1556 return log_error_errno(r
, "[%s:%u] Failed to replace specifiers in '%s': %m",
1559 if (!valid_home(resolved_home
))
1560 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1561 "[%s:%u] '%s' is not a valid home directory field.",
1562 fname
, line
, resolved_home
);
1566 if (empty_or_dash(shell
))
1567 shell
= mfree(shell
);
1570 r
= specifier_printf(shell
, PATH_MAX
-1, system_and_tmp_specifier_table
, arg_root
, NULL
, &resolved_shell
);
1572 return log_error_errno(r
, "[%s:%u] Failed to replace specifiers in '%s': %m",
1573 fname
, line
, shell
);
1575 if (!valid_shell(resolved_shell
))
1576 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1577 "[%s:%u] '%s' is not a valid login shell field.",
1578 fname
, line
, resolved_shell
);
1581 switch (action
[0]) {
1585 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1586 "[%s:%u] Lines of type 'r' don't take a name field.",
1590 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1591 "[%s:%u] Lines of type 'r' require an ID range in the third field.",
1594 if (description
|| home
|| shell
)
1595 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1596 "[%s:%u] Lines of type '%c' don't take a %s field.",
1597 fname
, line
, action
[0],
1598 description
? "GECOS" : home
? "home directory" : "login shell");
1600 r
= uid_range_add_str(&uid_range
, &n_uid_range
, resolved_id
);
1602 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1603 "[%s:%u] Invalid UID range %s.", fname
, line
, resolved_id
);
1608 /* Try to extend an existing member or group item */
1610 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1611 "[%s:%u] Lines of type 'm' require a user name in the second field.",
1615 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1616 "[%s:%u] Lines of type 'm' require a group name in the third field.",
1619 if (!valid_user_group_name(resolved_id
, 0))
1620 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1621 "[%s:%u] '%s' is not a valid user or group name.",
1622 fname
, line
, resolved_id
);
1624 if (description
|| home
|| shell
)
1625 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1626 "[%s:%u] Lines of type '%c' don't take a %s field.",
1627 fname
, line
, action
[0],
1628 description
? "GECOS" : home
? "home directory" : "login shell");
1630 r
= string_strv_ordered_hashmap_put(&members
, resolved_id
, resolved_name
);
1632 return log_error_errno(r
, "Failed to store mapping for %s: %m", resolved_id
);
1639 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1640 "[%s:%u] Lines of type 'u' require a user name in the second field.",
1643 r
= ordered_hashmap_ensure_allocated(&users
, &item_hash_ops
);
1652 if (path_is_absolute(resolved_id
)) {
1653 i
->uid_path
= TAKE_PTR(resolved_id
);
1654 path_simplify(i
->uid_path
);
1656 _cleanup_free_
char *uid
= NULL
, *gid
= NULL
;
1657 if (split_pair(resolved_id
, ":", &uid
, &gid
) == 0) {
1658 r
= parse_gid(gid
, &i
->gid
);
1660 if (valid_user_group_name(gid
, 0))
1661 i
->group_name
= TAKE_PTR(gid
);
1663 return log_error_errno(r
, "Failed to parse GID: '%s': %m", id
);
1666 i
->id_set_strict
= true;
1668 free_and_replace(resolved_id
, uid
);
1670 if (!streq(resolved_id
, "-")) {
1671 r
= parse_uid(resolved_id
, &i
->uid
);
1673 return log_error_errno(r
, "Failed to parse UID: '%s': %m", id
);
1679 i
->description
= TAKE_PTR(resolved_description
);
1680 i
->home
= TAKE_PTR(resolved_home
);
1681 i
->shell
= TAKE_PTR(resolved_shell
);
1688 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1689 "[%s:%u] Lines of type 'g' require a user name in the second field.",
1692 if (description
|| home
|| shell
)
1693 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1694 "[%s:%u] Lines of type '%c' don't take a %s field.",
1695 fname
, line
, action
[0],
1696 description
? "GECOS" : home
? "home directory" : "login shell");
1698 r
= ordered_hashmap_ensure_allocated(&groups
, &item_hash_ops
);
1707 if (path_is_absolute(resolved_id
)) {
1708 i
->gid_path
= TAKE_PTR(resolved_id
);
1709 path_simplify(i
->gid_path
);
1711 r
= parse_gid(resolved_id
, &i
->gid
);
1713 return log_error_errno(r
, "Failed to parse GID: '%s': %m", id
);
1726 i
->type
= action
[0];
1727 i
->name
= TAKE_PTR(resolved_name
);
1729 existing
= ordered_hashmap_get(h
, i
->name
);
1731 /* Two identical items are fine */
1732 if (!item_equal(existing
, i
))
1733 log_warning("Two or more conflicting lines for %s configured, ignoring.", i
->name
);
1738 r
= ordered_hashmap_put(h
, i
->name
, i
);
1746 static int read_config_file(const char *fn
, bool ignore_enoent
) {
1747 _cleanup_fclose_
FILE *rf
= NULL
;
1748 _cleanup_free_
char *pp
= NULL
;
1758 r
= search_and_fopen(fn
, "re", arg_root
, (const char**) CONF_PATHS_STRV("sysusers.d"), &rf
, &pp
);
1760 if (ignore_enoent
&& r
== -ENOENT
)
1763 return log_error_errno(r
, "Failed to open '%s', ignoring: %m", fn
);
1771 _cleanup_free_
char *line
= NULL
;
1775 k
= read_line(f
, LONG_LINE_MAX
, &line
);
1777 return log_error_errno(k
, "Failed to read '%s': %m", fn
);
1784 if (IN_SET(*l
, 0, '#'))
1787 k
= parse_line(fn
, v
, l
);
1788 if (k
< 0 && r
== 0)
1793 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
1801 static int cat_config(void) {
1802 _cleanup_strv_free_
char **files
= NULL
;
1805 r
= conf_files_list_with_replacement(arg_root
, CONF_PATHS_STRV("sysusers.d"), arg_replace
, &files
, NULL
);
1809 (void) pager_open(arg_pager_flags
);
1811 return cat_files(NULL
, files
, 0);
1814 static int help(void) {
1815 _cleanup_free_
char *link
= NULL
;
1818 r
= terminal_urlify_man("systemd-sysusers.service", "8", &link
);
1822 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1823 "Creates system user accounts.\n\n"
1824 " -h --help Show this help\n"
1825 " --version Show package version\n"
1826 " --cat-config Show configuration files\n"
1827 " --root=PATH Operate on an alternate filesystem root\n"
1828 " --image=PATH Operate on disk image as filesystem root\n"
1829 " --replace=PATH Treat arguments as replacement for PATH\n"
1830 " --inline Treat arguments as configuration lines\n"
1831 " --no-pager Do not pipe output into a pager\n"
1832 "\nSee the %s for details.\n",
1833 program_invocation_short_name
,
1839 static int parse_argv(int argc
, char *argv
[]) {
1842 ARG_VERSION
= 0x100,
1851 static const struct option options
[] = {
1852 { "help", no_argument
, NULL
, 'h' },
1853 { "version", no_argument
, NULL
, ARG_VERSION
},
1854 { "cat-config", no_argument
, NULL
, ARG_CAT_CONFIG
},
1855 { "root", required_argument
, NULL
, ARG_ROOT
},
1856 { "image", required_argument
, NULL
, ARG_IMAGE
},
1857 { "replace", required_argument
, NULL
, ARG_REPLACE
},
1858 { "inline", no_argument
, NULL
, ARG_INLINE
},
1859 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1868 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
1878 case ARG_CAT_CONFIG
:
1879 arg_cat_config
= true;
1883 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_root
);
1890 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
1891 "This systemd-sysusers version is compiled without support for --image=.");
1893 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_image
);
1900 if (!path_is_absolute(optarg
) ||
1901 !endswith(optarg
, ".conf"))
1902 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1903 "The argument to --replace= must an absolute path to a config file");
1905 arg_replace
= optarg
;
1913 arg_pager_flags
|= PAGER_DISABLE
;
1920 assert_not_reached();
1923 if (arg_replace
&& arg_cat_config
)
1924 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1925 "Option --replace= is not supported with --cat-config");
1927 if (arg_replace
&& optind
>= argc
)
1928 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1929 "When --replace= is given, some configuration items must be specified");
1931 if (arg_image
&& arg_root
)
1932 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Please specify either --root= or --image=, the combination of both is not supported.");
1937 static int parse_arguments(char **args
) {
1942 STRV_FOREACH(arg
, args
) {
1944 /* Use (argument):n, where n==1 for the first positional arg */
1945 r
= parse_line("(argument)", pos
, *arg
);
1947 r
= read_config_file(*arg
, false);
1957 static int read_config_files(char **args
) {
1958 _cleanup_strv_free_
char **files
= NULL
;
1959 _cleanup_free_
char *p
= NULL
;
1963 r
= conf_files_list_with_replacement(arg_root
, CONF_PATHS_STRV("sysusers.d"), arg_replace
, &files
, &p
);
1967 STRV_FOREACH(f
, files
)
1968 if (p
&& path_equal(*f
, p
)) {
1969 log_debug("Parsing arguments at position \"%s\"…", *f
);
1971 r
= parse_arguments(args
);
1975 log_debug("Reading config file \"%s\"…", *f
);
1977 /* Just warn, ignore result otherwise */
1978 (void) read_config_file(*f
, true);
1984 static int run(int argc
, char *argv
[]) {
1986 _cleanup_(loop_device_unrefp
) LoopDevice
*loop_device
= NULL
;
1987 _cleanup_(decrypted_image_unrefp
) DecryptedImage
*decrypted_image
= NULL
;
1988 _cleanup_(umount_and_rmdir_and_freep
) char *unlink_dir
= NULL
;
1990 _cleanup_close_
int lock
= -1;
1994 r
= parse_argv(argc
, argv
);
2001 return cat_config();
2005 r
= mac_selinux_init();
2013 r
= mount_image_privately_interactively(
2015 DISSECT_IMAGE_GENERIC_ROOT
|
2016 DISSECT_IMAGE_REQUIRE_ROOT
|
2017 DISSECT_IMAGE_VALIDATE_OS
|
2018 DISSECT_IMAGE_RELAX_VAR_CHECK
|
2019 DISSECT_IMAGE_FSCK
|
2020 DISSECT_IMAGE_GROWFS
,
2027 arg_root
= strdup(unlink_dir
);
2035 /* If command line arguments are specified along with --replace, read all
2036 * configuration files and insert the positional arguments at the specified
2037 * place. Otherwise, if command line arguments are specified, execute just
2038 * them, and finally, without --replace= or any positional arguments, just
2039 * read configuration and execute it.
2041 if (arg_replace
|| optind
>= argc
)
2042 r
= read_config_files(argv
+ optind
);
2044 r
= parse_arguments(argv
+ optind
);
2048 /* Let's tell nss-systemd not to synthesize the "root" and "nobody" entries for it, so that our detection
2049 * whether the names or UID/GID area already used otherwise doesn't get confused. After all, even though
2050 * nss-systemd synthesizes these users/groups, they should still appear in /etc/passwd and /etc/group, as the
2051 * synthesizing logic is merely supposed to be fallback for cases where we run with a completely unpopulated
2053 if (setenv("SYSTEMD_NSS_BYPASS_SYNTHETIC", "1", 1) < 0)
2054 return log_error_errno(errno
, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m");
2057 /* Default to default range of SYSTEMD_UID_MIN..SYSTEM_UID_MAX. */
2058 r
= read_login_defs(&login_defs
, NULL
, arg_root
);
2060 return log_error_errno(r
, "Failed to read %s%s: %m",
2061 strempty(arg_root
), "/etc/login.defs");
2063 login_defs_need_warning
= true;
2065 /* We pick a range that very conservative: we look at compiled-in maximum and the value in
2066 * /etc/login.defs. That way the uids/gids which we allocate will be interpreted correctly,
2067 * even if /etc/login.defs is removed later. (The bottom bound doesn't matter much, since
2068 * it's only used during allocation, so we use the configured value directly). */
2069 uid_t begin
= login_defs
.system_alloc_uid_min
,
2070 end
= MIN3((uid_t
) SYSTEM_UID_MAX
, login_defs
.system_uid_max
, login_defs
.system_gid_max
);
2072 r
= uid_range_add(&uid_range
, &n_uid_range
, begin
, end
- begin
+ 1);
2082 lock
= take_etc_passwd_lock(arg_root
);
2084 return log_error_errno(lock
, "Failed to take /etc/passwd lock: %m");
2086 r
= load_user_database();
2088 return log_error_errno(r
, "Failed to load user database: %m");
2090 r
= load_group_database();
2092 return log_error_errno(r
, "Failed to read group database: %m");
2094 ORDERED_HASHMAP_FOREACH(i
, groups
)
2095 (void) process_item(i
);
2097 ORDERED_HASHMAP_FOREACH(i
, users
)
2098 (void) process_item(i
);
2102 return log_error_errno(r
, "Failed to write files: %m");
2107 DEFINE_MAIN_FUNCTION(run
);