1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "alloc-util.h"
7 #include "chase-symlinks.h"
8 #include "conf-files.h"
10 #include "creds-util.h"
12 #include "dissect-image.h"
15 #include "format-util.h"
18 #include "libcrypt-util.h"
19 #include "main-func.h"
20 #include "memory-util.h"
21 #include "mount-util.h"
22 #include "nscd-flush.h"
24 #include "parse-argument.h"
25 #include "path-util.h"
26 #include "pretty-print.h"
27 #include "selinux-util.h"
29 #include "smack-util.h"
30 #include "specifier.h"
31 #include "stat-util.h"
32 #include "string-util.h"
34 #include "sync-util.h"
35 #include "tmpfile-util-label.h"
36 #include "uid-alloc-range.h"
37 #include "uid-range.h"
38 #include "user-util.h"
42 typedef enum ItemType
{
49 static inline const char* item_type_to_string(ItemType t
) {
80 /* When set the group with the specified GID must exist
81 * and the check if a UID clashes with the GID is skipped.
91 static char *arg_root
= NULL
;
92 static char *arg_image
= NULL
;
93 static bool arg_cat_config
= false;
94 static const char *arg_replace
= NULL
;
95 static bool arg_dry_run
= false;
96 static bool arg_inline
= false;
97 static PagerFlags arg_pager_flags
= 0;
99 static OrderedHashmap
*users
= NULL
, *groups
= NULL
;
100 static OrderedHashmap
*todo_uids
= NULL
, *todo_gids
= NULL
;
101 static OrderedHashmap
*members
= NULL
;
103 static Hashmap
*database_by_uid
= NULL
, *database_by_username
= NULL
;
104 static Hashmap
*database_by_gid
= NULL
, *database_by_groupname
= NULL
;
105 static Set
*database_users
= NULL
, *database_groups
= NULL
;
107 static uid_t search_uid
= UID_INVALID
;
108 static UidRange
*uid_range
= NULL
;
109 static size_t n_uid_range
= 0;
111 static UGIDAllocationRange login_defs
= {};
112 static bool login_defs_need_warning
= false;
114 STATIC_DESTRUCTOR_REGISTER(groups
, ordered_hashmap_freep
);
115 STATIC_DESTRUCTOR_REGISTER(users
, ordered_hashmap_freep
);
116 STATIC_DESTRUCTOR_REGISTER(members
, ordered_hashmap_freep
);
117 STATIC_DESTRUCTOR_REGISTER(todo_uids
, ordered_hashmap_freep
);
118 STATIC_DESTRUCTOR_REGISTER(todo_gids
, ordered_hashmap_freep
);
119 STATIC_DESTRUCTOR_REGISTER(database_by_uid
, hashmap_freep
);
120 STATIC_DESTRUCTOR_REGISTER(database_by_username
, hashmap_freep
);
121 STATIC_DESTRUCTOR_REGISTER(database_users
, set_free_freep
);
122 STATIC_DESTRUCTOR_REGISTER(database_by_gid
, hashmap_freep
);
123 STATIC_DESTRUCTOR_REGISTER(database_by_groupname
, hashmap_freep
);
124 STATIC_DESTRUCTOR_REGISTER(database_groups
, set_free_freep
);
125 STATIC_DESTRUCTOR_REGISTER(uid_range
, freep
);
126 STATIC_DESTRUCTOR_REGISTER(arg_root
, freep
);
127 STATIC_DESTRUCTOR_REGISTER(arg_image
, freep
);
129 static int errno_is_not_exists(int code
) {
130 /* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are
132 return IN_SET(code
, 0, ENOENT
, ESRCH
, EBADF
, EPERM
);
135 static void maybe_emit_login_defs_warning(void) {
136 if (!login_defs_need_warning
)
139 if (login_defs
.system_alloc_uid_min
!= SYSTEM_ALLOC_UID_MIN
||
140 login_defs
.system_uid_max
!= SYSTEM_UID_MAX
)
141 log_warning("login.defs specifies UID allocation range "UID_FMT
"–"UID_FMT
142 " that is different than the built-in defaults ("UID_FMT
"–"UID_FMT
")",
143 login_defs
.system_alloc_uid_min
, login_defs
.system_uid_max
,
144 SYSTEM_ALLOC_UID_MIN
, SYSTEM_UID_MAX
);
145 if (login_defs
.system_alloc_gid_min
!= SYSTEM_ALLOC_GID_MIN
||
146 login_defs
.system_gid_max
!= SYSTEM_GID_MAX
)
147 log_warning("login.defs specifies GID allocation range "GID_FMT
"–"GID_FMT
148 " that is different than the built-in defaults ("GID_FMT
"–"GID_FMT
")",
149 login_defs
.system_alloc_gid_min
, login_defs
.system_gid_max
,
150 SYSTEM_ALLOC_GID_MIN
, SYSTEM_GID_MAX
);
152 login_defs_need_warning
= false;
155 static int load_user_database(void) {
156 _cleanup_fclose_
FILE *f
= NULL
;
157 const char *passwd_path
;
161 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
162 f
= fopen(passwd_path
, "re");
164 return errno
== ENOENT
? 0 : -errno
;
166 r
= hashmap_ensure_allocated(&database_by_username
, &string_hash_ops
);
170 r
= hashmap_ensure_allocated(&database_by_uid
, NULL
);
174 r
= set_ensure_allocated(&database_users
, NULL
);
178 while ((r
= fgetpwent_sane(f
, &pw
)) > 0) {
182 n
= strdup(pw
->pw_name
);
186 k
= set_put(database_users
, n
);
192 k
= hashmap_put(database_by_username
, n
, UID_TO_PTR(pw
->pw_uid
));
193 if (k
< 0 && k
!= -EEXIST
)
196 q
= hashmap_put(database_by_uid
, UID_TO_PTR(pw
->pw_uid
), n
);
197 if (q
< 0 && q
!= -EEXIST
)
203 static int load_group_database(void) {
204 _cleanup_fclose_
FILE *f
= NULL
;
205 const char *group_path
;
209 group_path
= prefix_roota(arg_root
, "/etc/group");
210 f
= fopen(group_path
, "re");
212 return errno
== ENOENT
? 0 : -errno
;
214 r
= hashmap_ensure_allocated(&database_by_groupname
, &string_hash_ops
);
218 r
= hashmap_ensure_allocated(&database_by_gid
, NULL
);
222 r
= set_ensure_allocated(&database_groups
, NULL
);
226 while ((r
= fgetgrent_sane(f
, &gr
)) > 0) {
230 n
= strdup(gr
->gr_name
);
234 k
= set_put(database_groups
, n
);
240 k
= hashmap_put(database_by_groupname
, n
, GID_TO_PTR(gr
->gr_gid
));
241 if (k
< 0 && k
!= -EEXIST
)
244 q
= hashmap_put(database_by_gid
, GID_TO_PTR(gr
->gr_gid
), n
);
245 if (q
< 0 && q
!= -EEXIST
)
251 static int make_backup(const char *target
, const char *x
) {
252 _cleanup_(unlink_and_freep
) char *dst_tmp
= NULL
;
253 _cleanup_fclose_
FILE *dst
= NULL
;
254 _cleanup_close_
int src
= -1;
262 src
= open(x
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
264 if (errno
== ENOENT
) /* No backup necessary... */
270 if (fstat(src
, &st
) < 0)
273 r
= fopen_temporary_label(
274 target
, /* The path for which to the look up the label */
275 x
, /* Where we want the file actually to end up */
276 &dst
, /* The temporary file we write to */
281 r
= copy_bytes(src
, fileno(dst
), UINT64_MAX
, COPY_REFLINK
);
285 backup
= strjoina(x
, "-");
287 /* Copy over the access mask. Don't fail on chmod() or chown(). If it stays owned by us and/or
288 * unreadable by others, then it isn't too bad... */
289 r
= fchmod_and_chown_with_fallback(fileno(dst
), dst_tmp
, st
.st_mode
& 07777, st
.st_uid
, st
.st_gid
);
291 log_warning_errno(r
, "Failed to change access mode or ownership of %s: %m", backup
);
293 if (futimens(fileno(dst
), (const struct timespec
[2]) { st
.st_atim
, st
.st_mtim
}) < 0)
294 log_warning_errno(errno
, "Failed to fix access and modification time of %s: %m", backup
);
296 r
= fsync_full(fileno(dst
));
300 if (rename(dst_tmp
, backup
) < 0)
303 dst_tmp
= mfree(dst_tmp
); /* disable the unlink_and_freep() hook now that the file has been renamed */
307 static int putgrent_with_members(const struct group
*gr
, FILE *group
) {
313 a
= ordered_hashmap_get(members
, gr
->gr_name
);
315 _cleanup_strv_free_
char **l
= NULL
;
318 l
= strv_copy(gr
->gr_mem
);
323 if (strv_contains(l
, *i
))
326 if (strv_extend(&l
, *i
) < 0)
342 r
= putgrent_sane(&t
, group
);
343 return r
< 0 ? r
: 1;
347 return putgrent_sane(gr
, group
);
351 static int putsgent_with_members(const struct sgrp
*sg
, FILE *gshadow
) {
357 a
= ordered_hashmap_get(members
, sg
->sg_namp
);
359 _cleanup_strv_free_
char **l
= NULL
;
362 l
= strv_copy(sg
->sg_mem
);
367 if (strv_contains(l
, *i
))
370 if (strv_extend(&l
, *i
) < 0)
386 r
= putsgent_sane(&t
, gshadow
);
387 return r
< 0 ? r
: 1;
391 return putsgent_sane(sg
, gshadow
);
395 static const char* pick_shell(const Item
*i
) {
396 if (i
->type
!= ADD_USER
)
400 if (i
->uid_set
&& i
->uid
== 0)
401 return default_root_shell(arg_root
);
405 static int write_temporary_passwd(const char *passwd_path
, FILE **tmpfile
, char **tmpfile_path
) {
406 _cleanup_fclose_
FILE *original
= NULL
, *passwd
= NULL
;
407 _cleanup_(unlink_and_freep
) char *passwd_tmp
= NULL
;
408 struct passwd
*pw
= NULL
;
412 if (ordered_hashmap_isempty(todo_uids
))
416 log_info("Would write /etc/passwd%s", special_glyph(SPECIAL_GLYPH_ELLIPSIS
));
420 r
= fopen_temporary_label("/etc/passwd", passwd_path
, &passwd
, &passwd_tmp
);
422 return log_debug_errno(r
, "Failed to open temporary copy of %s: %m", passwd_path
);
424 original
= fopen(passwd_path
, "re");
427 /* Allow fallback path for when /proc is not mounted. On any normal system /proc will be
428 * mounted, but e.g. when 'dnf --installroot' is used, it might not be. There is no security
429 * relevance here, since the environment is ultimately trusted, and not requiring /proc makes
430 * it easier to depend on sysusers in packaging scripts and suchlike. */
431 r
= copy_rights_with_fallback(fileno(original
), fileno(passwd
), passwd_tmp
);
433 return log_debug_errno(r
, "Failed to copy permissions from %s to %s: %m",
434 passwd_path
, passwd_tmp
);
436 while ((r
= fgetpwent_sane(original
, &pw
)) > 0) {
437 i
= ordered_hashmap_get(users
, pw
->pw_name
);
438 if (i
&& i
->todo_user
)
439 return log_error_errno(SYNTHETIC_ERRNO(EEXIST
),
440 "%s: User \"%s\" already exists.",
441 passwd_path
, pw
->pw_name
);
443 if (ordered_hashmap_contains(todo_uids
, UID_TO_PTR(pw
->pw_uid
)))
444 return log_error_errno(SYNTHETIC_ERRNO(EEXIST
),
445 "%s: Detected collision for UID " UID_FMT
".",
446 passwd_path
, pw
->pw_uid
);
448 /* Make sure we keep the NIS entries (if any) at the end. */
449 if (IN_SET(pw
->pw_name
[0], '+', '-'))
452 r
= putpwent_sane(pw
, passwd
);
454 return log_debug_errno(r
, "Failed to add existing user \"%s\" to temporary passwd file: %m",
458 return log_debug_errno(r
, "Failed to read %s: %m", passwd_path
);
462 return log_debug_errno(errno
, "Failed to open %s: %m", passwd_path
);
463 if (fchmod(fileno(passwd
), 0644) < 0)
464 return log_debug_errno(errno
, "Failed to fchmod %s: %m", passwd_tmp
);
467 ORDERED_HASHMAP_FOREACH(i
, todo_uids
) {
468 _cleanup_free_
char *creds_shell
= NULL
, *cn
= NULL
;
474 .pw_gecos
= (char*) strempty(i
->description
),
476 /* "x" means the password is stored in the shadow file */
477 .pw_passwd
= (char*) PASSWORD_SEE_SHADOW
,
479 /* We default to the root directory as home */
480 .pw_dir
= i
->home
?: (char*) "/",
482 /* Initialize the shell to nologin, with one exception:
483 * for root we patch in something special */
484 .pw_shell
= (char*) pick_shell(i
),
487 /* Try to pick up the shell for this account via the credentials logic */
488 cn
= strjoin("passwd.shell.", i
->name
);
492 r
= read_credential(cn
, (void**) &creds_shell
, NULL
);
494 log_debug_errno(r
, "Couldn't read credential '%s', ignoring: %m", cn
);
496 n
.pw_shell
= creds_shell
;
498 r
= putpwent_sane(&n
, passwd
);
500 return log_debug_errno(r
, "Failed to add new user \"%s\" to temporary passwd file: %m",
504 /* Append the remaining NIS entries if any */
506 r
= putpwent_sane(pw
, passwd
);
508 return log_debug_errno(r
, "Failed to add existing user \"%s\" to temporary passwd file: %m",
511 r
= fgetpwent_sane(original
, &pw
);
513 return log_debug_errno(r
, "Failed to read %s: %m", passwd_path
);
518 r
= fflush_sync_and_check(passwd
);
520 return log_debug_errno(r
, "Failed to flush %s: %m", passwd_tmp
);
522 *tmpfile
= TAKE_PTR(passwd
);
523 *tmpfile_path
= TAKE_PTR(passwd_tmp
);
528 static int write_temporary_shadow(const char *shadow_path
, FILE **tmpfile
, char **tmpfile_path
) {
529 _cleanup_fclose_
FILE *original
= NULL
, *shadow
= NULL
;
530 _cleanup_(unlink_and_freep
) char *shadow_tmp
= NULL
;
531 struct spwd
*sp
= NULL
;
536 if (ordered_hashmap_isempty(todo_uids
))
540 log_info("Would write /etc/shadow%s", special_glyph(SPECIAL_GLYPH_ELLIPSIS
));
544 r
= fopen_temporary_label("/etc/shadow", shadow_path
, &shadow
, &shadow_tmp
);
546 return log_debug_errno(r
, "Failed to open temporary copy of %s: %m", shadow_path
);
548 lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
550 original
= fopen(shadow_path
, "re");
553 r
= copy_rights_with_fallback(fileno(original
), fileno(shadow
), shadow_tmp
);
555 return log_debug_errno(r
, "Failed to copy permissions from %s to %s: %m",
556 shadow_path
, shadow_tmp
);
558 while ((r
= fgetspent_sane(original
, &sp
)) > 0) {
559 i
= ordered_hashmap_get(users
, sp
->sp_namp
);
560 if (i
&& i
->todo_user
) {
561 /* we will update the existing entry */
562 sp
->sp_lstchg
= lstchg
;
564 /* only the /etc/shadow stage is left, so we can
565 * safely remove the item from the todo set */
566 i
->todo_user
= false;
567 ordered_hashmap_remove(todo_uids
, UID_TO_PTR(i
->uid
));
570 /* Make sure we keep the NIS entries (if any) at the end. */
571 if (IN_SET(sp
->sp_namp
[0], '+', '-'))
574 r
= putspent_sane(sp
, shadow
);
576 return log_debug_errno(r
, "Failed to add existing user \"%s\" to temporary shadow file: %m",
581 return log_debug_errno(r
, "Failed to read %s: %m", shadow_path
);
585 return log_debug_errno(errno
, "Failed to open %s: %m", shadow_path
);
586 if (fchmod(fileno(shadow
), 0000) < 0)
587 return log_debug_errno(errno
, "Failed to fchmod %s: %m", shadow_tmp
);
590 ORDERED_HASHMAP_FOREACH(i
, todo_uids
) {
591 _cleanup_(erase_and_freep
) char *creds_password
= NULL
;
596 .sp_pwdp
= (char*) PASSWORD_LOCKED_AND_INVALID
,
603 .sp_flag
= ULONG_MAX
, /* this appears to be what everybody does ... */
606 r
= get_credential_user_password(i
->name
, &creds_password
, &is_hashed
);
608 log_debug_errno(r
, "Couldn't read password credential for user '%s', ignoring: %m", i
->name
);
610 if (creds_password
&& !is_hashed
) {
611 _cleanup_(erase_and_freep
) char* plaintext_password
= TAKE_PTR(creds_password
);
612 r
= hash_password(plaintext_password
, &creds_password
);
614 return log_debug_errno(r
, "Failed to hash password: %m");
618 n
.sp_pwdp
= creds_password
;
620 r
= putspent_sane(&n
, shadow
);
622 return log_debug_errno(r
, "Failed to add new user \"%s\" to temporary shadow file: %m",
626 /* Append the remaining NIS entries if any */
628 r
= putspent_sane(sp
, shadow
);
630 return log_debug_errno(r
, "Failed to add existing user \"%s\" to temporary shadow file: %m",
633 r
= fgetspent_sane(original
, &sp
);
635 return log_debug_errno(r
, "Failed to read %s: %m", shadow_path
);
639 if (!IN_SET(errno
, 0, ENOENT
))
642 r
= fflush_sync_and_check(shadow
);
644 return log_debug_errno(r
, "Failed to flush %s: %m", shadow_tmp
);
646 *tmpfile
= TAKE_PTR(shadow
);
647 *tmpfile_path
= TAKE_PTR(shadow_tmp
);
652 static int write_temporary_group(const char *group_path
, FILE **tmpfile
, char **tmpfile_path
) {
653 _cleanup_fclose_
FILE *original
= NULL
, *group
= NULL
;
654 _cleanup_(unlink_and_freep
) char *group_tmp
= NULL
;
655 bool group_changed
= false;
656 struct group
*gr
= NULL
;
660 if (ordered_hashmap_isempty(todo_gids
) && ordered_hashmap_isempty(members
))
664 log_info("Would write /etc/group%s", special_glyph(SPECIAL_GLYPH_ELLIPSIS
));
668 r
= fopen_temporary_label("/etc/group", group_path
, &group
, &group_tmp
);
670 return log_error_errno(r
, "Failed to open temporary copy of %s: %m", group_path
);
672 original
= fopen(group_path
, "re");
675 r
= copy_rights_with_fallback(fileno(original
), fileno(group
), group_tmp
);
677 return log_error_errno(r
, "Failed to copy permissions from %s to %s: %m",
678 group_path
, group_tmp
);
680 while ((r
= fgetgrent_sane(original
, &gr
)) > 0) {
681 /* Safety checks against name and GID collisions. Normally,
682 * this should be unnecessary, but given that we look at the
683 * entries anyway here, let's make an extra verification
684 * step that we don't generate duplicate entries. */
686 i
= ordered_hashmap_get(groups
, gr
->gr_name
);
687 if (i
&& i
->todo_group
)
688 return log_error_errno(SYNTHETIC_ERRNO(EEXIST
),
689 "%s: Group \"%s\" already exists.",
690 group_path
, gr
->gr_name
);
692 if (ordered_hashmap_contains(todo_gids
, GID_TO_PTR(gr
->gr_gid
)))
693 return log_error_errno(SYNTHETIC_ERRNO(EEXIST
),
694 "%s: Detected collision for GID " GID_FMT
".",
695 group_path
, gr
->gr_gid
);
697 /* Make sure we keep the NIS entries (if any) at the end. */
698 if (IN_SET(gr
->gr_name
[0], '+', '-'))
701 r
= putgrent_with_members(gr
, group
);
703 return log_error_errno(r
, "Failed to add existing group \"%s\" to temporary group file: %m",
706 group_changed
= true;
709 return log_error_errno(r
, "Failed to read %s: %m", group_path
);
713 return log_error_errno(errno
, "Failed to open %s: %m", group_path
);
714 if (fchmod(fileno(group
), 0644) < 0)
715 return log_error_errno(errno
, "Failed to fchmod %s: %m", group_tmp
);
718 ORDERED_HASHMAP_FOREACH(i
, todo_gids
) {
722 .gr_passwd
= (char*) PASSWORD_SEE_SHADOW
,
725 r
= putgrent_with_members(&n
, group
);
727 return log_error_errno(r
, "Failed to add new group \"%s\" to temporary group file: %m",
730 group_changed
= true;
733 /* Append the remaining NIS entries if any */
735 r
= putgrent_sane(gr
, group
);
737 return log_error_errno(r
, "Failed to add existing group \"%s\" to temporary group file: %m",
740 r
= fgetgrent_sane(original
, &gr
);
742 return log_error_errno(r
, "Failed to read %s: %m", group_path
);
747 r
= fflush_sync_and_check(group
);
749 return log_error_errno(r
, "Failed to flush %s: %m", group_tmp
);
752 *tmpfile
= TAKE_PTR(group
);
753 *tmpfile_path
= TAKE_PTR(group_tmp
);
758 static int write_temporary_gshadow(const char * gshadow_path
, FILE **tmpfile
, char **tmpfile_path
) {
760 _cleanup_fclose_
FILE *original
= NULL
, *gshadow
= NULL
;
761 _cleanup_(unlink_and_freep
) char *gshadow_tmp
= NULL
;
762 bool group_changed
= false;
766 if (ordered_hashmap_isempty(todo_gids
) && ordered_hashmap_isempty(members
))
770 log_info("Would write /etc/gshadow%s", special_glyph(SPECIAL_GLYPH_ELLIPSIS
));
774 r
= fopen_temporary_label("/etc/gshadow", gshadow_path
, &gshadow
, &gshadow_tmp
);
776 return log_error_errno(r
, "Failed to open temporary copy of %s: %m", gshadow_path
);
778 original
= fopen(gshadow_path
, "re");
782 r
= copy_rights_with_fallback(fileno(original
), fileno(gshadow
), gshadow_tmp
);
784 return log_error_errno(r
, "Failed to copy permissions from %s to %s: %m",
785 gshadow_path
, gshadow_tmp
);
787 while ((r
= fgetsgent_sane(original
, &sg
)) > 0) {
789 i
= ordered_hashmap_get(groups
, sg
->sg_namp
);
790 if (i
&& i
->todo_group
)
791 return log_error_errno(SYNTHETIC_ERRNO(EEXIST
),
792 "%s: Group \"%s\" already exists.",
793 gshadow_path
, sg
->sg_namp
);
795 r
= putsgent_with_members(sg
, gshadow
);
797 return log_error_errno(r
, "Failed to add existing group \"%s\" to temporary gshadow file: %m",
800 group_changed
= true;
807 return log_error_errno(errno
, "Failed to open %s: %m", gshadow_path
);
808 if (fchmod(fileno(gshadow
), 0000) < 0)
809 return log_error_errno(errno
, "Failed to fchmod %s: %m", gshadow_tmp
);
812 ORDERED_HASHMAP_FOREACH(i
, todo_gids
) {
815 .sg_passwd
= (char*) PASSWORD_LOCKED_AND_INVALID
,
818 r
= putsgent_with_members(&n
, gshadow
);
820 return log_error_errno(r
, "Failed to add new group \"%s\" to temporary gshadow file: %m",
823 group_changed
= true;
826 r
= fflush_sync_and_check(gshadow
);
828 return log_error_errno(r
, "Failed to flush %s: %m", gshadow_tmp
);
831 *tmpfile
= TAKE_PTR(gshadow
);
832 *tmpfile_path
= TAKE_PTR(gshadow_tmp
);
838 static int write_files(void) {
839 _cleanup_fclose_
FILE *passwd
= NULL
, *group
= NULL
, *shadow
= NULL
, *gshadow
= NULL
;
840 _cleanup_(unlink_and_freep
) char *passwd_tmp
= NULL
, *group_tmp
= NULL
, *shadow_tmp
= NULL
, *gshadow_tmp
= NULL
;
841 const char *passwd_path
, *shadow_path
, *group_path
, *gshadow_path
;
844 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
845 shadow_path
= prefix_roota(arg_root
, "/etc/shadow");
846 group_path
= prefix_roota(arg_root
, "/etc/group");
847 gshadow_path
= prefix_roota(arg_root
, "/etc/gshadow");
849 r
= write_temporary_group(group_path
, &group
, &group_tmp
);
853 r
= write_temporary_gshadow(gshadow_path
, &gshadow
, &gshadow_tmp
);
857 r
= write_temporary_passwd(passwd_path
, &passwd
, &passwd_tmp
);
861 r
= write_temporary_shadow(shadow_path
, &shadow
, &shadow_tmp
);
865 /* Make a backup of the old files */
867 r
= make_backup("/etc/group", group_path
);
869 return log_error_errno(r
, "Failed to make backup %s: %m", group_path
);
872 r
= make_backup("/etc/gshadow", gshadow_path
);
874 return log_error_errno(r
, "Failed to make backup %s: %m", gshadow_path
);
878 r
= make_backup("/etc/passwd", passwd_path
);
880 return log_error_errno(r
, "Failed to make backup %s: %m", passwd_path
);
883 r
= make_backup("/etc/shadow", shadow_path
);
885 return log_error_errno(r
, "Failed to make backup %s: %m", shadow_path
);
888 /* And make the new files count */
890 r
= rename_and_apply_smack_floor_label(group_tmp
, group_path
);
892 return log_error_errno(r
, "Failed to rename %s to %s: %m",
893 group_tmp
, group_path
);
894 group_tmp
= mfree(group_tmp
);
896 if (!arg_root
&& !arg_image
)
897 (void) nscd_flush_cache(STRV_MAKE("group"));
900 r
= rename_and_apply_smack_floor_label(gshadow_tmp
, gshadow_path
);
902 return log_error_errno(r
, "Failed to rename %s to %s: %m",
903 gshadow_tmp
, gshadow_path
);
905 gshadow_tmp
= mfree(gshadow_tmp
);
909 r
= rename_and_apply_smack_floor_label(passwd_tmp
, passwd_path
);
911 return log_error_errno(r
, "Failed to rename %s to %s: %m",
912 passwd_tmp
, passwd_path
);
914 passwd_tmp
= mfree(passwd_tmp
);
916 if (!arg_root
&& !arg_image
)
917 (void) nscd_flush_cache(STRV_MAKE("passwd"));
920 r
= rename_and_apply_smack_floor_label(shadow_tmp
, shadow_path
);
922 return log_error_errno(r
, "Failed to rename %s to %s: %m",
923 shadow_tmp
, shadow_path
);
925 shadow_tmp
= mfree(shadow_tmp
);
931 static int uid_is_ok(uid_t uid
, const char *name
, bool check_with_gid
) {
933 /* Let's see if we already have assigned the UID a second time */
934 if (ordered_hashmap_get(todo_uids
, UID_TO_PTR(uid
)))
937 /* Try to avoid using uids that are already used by a group
938 * that doesn't have the same name as our new user. */
939 if (check_with_gid
) {
942 i
= ordered_hashmap_get(todo_gids
, GID_TO_PTR(uid
));
943 if (i
&& !streq(i
->name
, name
))
947 /* Let's check the files directly */
948 if (hashmap_contains(database_by_uid
, UID_TO_PTR(uid
)))
951 if (check_with_gid
) {
954 n
= hashmap_get(database_by_gid
, GID_TO_PTR(uid
));
955 if (n
&& !streq(n
, name
))
959 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
968 if (!IN_SET(errno
, 0, ENOENT
))
971 if (check_with_gid
) {
973 g
= getgrgid((gid_t
) uid
);
975 if (!streq(g
->gr_name
, name
))
977 } else if (!IN_SET(errno
, 0, ENOENT
))
985 static int root_stat(const char *p
, struct stat
*st
) {
988 fix
= prefix_roota(arg_root
, p
);
989 return RET_NERRNO(stat(fix
, st
));
992 static int read_id_from_file(Item
*i
, uid_t
*ret_uid
, gid_t
*ret_gid
) {
994 bool found_uid
= false, found_gid
= false;
1000 /* First, try to get the GID directly */
1001 if (ret_gid
&& i
->gid_path
&& root_stat(i
->gid_path
, &st
) >= 0) {
1006 /* Then, try to get the UID directly */
1007 if ((ret_uid
|| (ret_gid
&& !found_gid
))
1009 && root_stat(i
->uid_path
, &st
) >= 0) {
1014 /* If we need the gid, but had no success yet, also derive it from the UID path */
1015 if (ret_gid
&& !found_gid
) {
1021 /* If that didn't work yet, then let's reuse the GID as UID */
1022 if (ret_uid
&& !found_uid
&& i
->gid_path
) {
1027 } else if (root_stat(i
->gid_path
, &st
) >= 0) {
1028 uid
= (uid_t
) st
.st_gid
;
1050 static int add_user(Item
*i
) {
1056 /* Check the database directly */
1057 z
= hashmap_get(database_by_username
, i
->name
);
1059 log_debug("User %s already exists.", i
->name
);
1060 i
->uid
= PTR_TO_UID(z
);
1068 /* Also check NSS */
1070 p
= getpwnam(i
->name
);
1072 log_debug("User %s already exists.", i
->name
);
1076 r
= free_and_strdup(&i
->description
, p
->pw_gecos
);
1082 if (!errno_is_not_exists(errno
))
1083 return log_error_errno(errno
, "Failed to check if user %s already exists: %m", i
->name
);
1086 /* Try to use the suggested numeric UID */
1088 r
= uid_is_ok(i
->uid
, i
->name
, !i
->id_set_strict
);
1090 return log_error_errno(r
, "Failed to verify UID " UID_FMT
": %m", i
->uid
);
1092 log_info("Suggested user ID " UID_FMT
" for %s already used.", i
->uid
, i
->name
);
1097 /* If that didn't work, try to read it from the specified path */
1101 if (read_id_from_file(i
, &c
, NULL
) > 0) {
1103 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1104 log_debug("User ID " UID_FMT
" of file not suitable for %s.", c
, i
->name
);
1106 r
= uid_is_ok(c
, i
->name
, true);
1108 return log_error_errno(r
, "Failed to verify UID " UID_FMT
": %m", i
->uid
);
1113 log_debug("User ID " UID_FMT
" of file for %s is already used.", c
, i
->name
);
1118 /* Otherwise, try to reuse the group ID */
1119 if (!i
->uid_set
&& i
->gid_set
) {
1120 r
= uid_is_ok((uid_t
) i
->gid
, i
->name
, true);
1122 return log_error_errno(r
, "Failed to verify UID " UID_FMT
": %m", i
->uid
);
1124 i
->uid
= (uid_t
) i
->gid
;
1129 /* And if that didn't work either, let's try to find a free one */
1131 maybe_emit_login_defs_warning();
1134 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1136 return log_error_errno(r
, "No free user ID available for %s.", i
->name
);
1138 r
= uid_is_ok(search_uid
, i
->name
, true);
1140 return log_error_errno(r
, "Failed to verify UID " UID_FMT
": %m", i
->uid
);
1146 i
->uid
= search_uid
;
1149 r
= ordered_hashmap_ensure_put(&todo_uids
, NULL
, UID_TO_PTR(i
->uid
), i
);
1151 return log_error_errno(r
, "Requested user %s with UID " UID_FMT
" and gid" GID_FMT
" to be created is duplicated "
1152 "or conflicts with another user.", i
->name
, i
->uid
, i
->gid
);
1156 return log_error_errno(r
, "Failed to store user %s with UID " UID_FMT
" and GID " GID_FMT
" to be created: %m",
1157 i
->name
, i
->uid
, i
->gid
);
1159 i
->todo_user
= true;
1160 log_info("Creating user '%s' (%s) with UID " UID_FMT
" and GID " GID_FMT
".",
1161 i
->name
, strna(i
->description
), i
->uid
, i
->gid
);
1166 static int gid_is_ok(gid_t gid
, bool check_with_uid
) {
1170 if (ordered_hashmap_get(todo_gids
, GID_TO_PTR(gid
)))
1173 /* Avoid reusing gids that are already used by a different user */
1174 if (check_with_uid
&& ordered_hashmap_get(todo_uids
, UID_TO_PTR(gid
)))
1177 if (hashmap_contains(database_by_gid
, GID_TO_PTR(gid
)))
1180 if (check_with_uid
&& hashmap_contains(database_by_uid
, UID_TO_PTR(gid
)))
1188 if (!IN_SET(errno
, 0, ENOENT
))
1191 if (check_with_uid
) {
1193 p
= getpwuid((uid_t
) gid
);
1196 if (!IN_SET(errno
, 0, ENOENT
))
1204 static int get_gid_by_name(const char *name
, gid_t
*gid
) {
1209 /* Check the database directly */
1210 z
= hashmap_get(database_by_groupname
, name
);
1212 *gid
= PTR_TO_GID(z
);
1216 /* Also check NSS */
1226 if (!errno_is_not_exists(errno
))
1227 return log_error_errno(errno
, "Failed to check if group %s already exists: %m", name
);
1233 static int add_group(Item
*i
) {
1238 r
= get_gid_by_name(i
->name
, &i
->gid
);
1242 log_debug("Group %s already exists.", i
->name
);
1247 /* Try to use the suggested numeric GID */
1249 r
= gid_is_ok(i
->gid
, false);
1251 return log_error_errno(r
, "Failed to verify GID " GID_FMT
": %m", i
->gid
);
1252 if (i
->id_set_strict
) {
1253 /* If we require the GID to already exist we can return here:
1254 * r > 0: means the GID does not exist -> fail
1255 * r == 0: means the GID exists -> nothing more to do.
1258 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1259 "Failed to create %s: please create GID " GID_FMT
,
1265 log_info("Suggested group ID " GID_FMT
" for %s already used.", i
->gid
, i
->name
);
1270 /* Try to reuse the numeric uid, if there's one */
1271 if (!i
->gid_set
&& i
->uid_set
) {
1272 r
= gid_is_ok((gid_t
) i
->uid
, true);
1274 return log_error_errno(r
, "Failed to verify GID " GID_FMT
": %m", i
->gid
);
1276 i
->gid
= (gid_t
) i
->uid
;
1281 /* If that didn't work, try to read it from the specified path */
1285 if (read_id_from_file(i
, NULL
, &c
) > 0) {
1287 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1288 log_debug("Group ID " GID_FMT
" of file not suitable for %s.", c
, i
->name
);
1290 r
= gid_is_ok(c
, true);
1292 return log_error_errno(r
, "Failed to verify GID " GID_FMT
": %m", i
->gid
);
1297 log_debug("Group ID " GID_FMT
" of file for %s already used.", c
, i
->name
);
1302 /* And if that didn't work either, let's try to find a free one */
1304 maybe_emit_login_defs_warning();
1307 /* We look for new GIDs in the UID pool! */
1308 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1310 return log_error_errno(r
, "No free group ID available for %s.", i
->name
);
1312 r
= gid_is_ok(search_uid
, true);
1314 return log_error_errno(r
, "Failed to verify GID " GID_FMT
": %m", i
->gid
);
1320 i
->gid
= search_uid
;
1323 r
= ordered_hashmap_ensure_put(&todo_gids
, NULL
, GID_TO_PTR(i
->gid
), i
);
1325 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
);
1329 return log_error_errno(r
, "Failed to store group %s with GID " GID_FMT
" to be created: %m", i
->name
, i
->gid
);
1331 i
->todo_group
= true;
1332 log_info("Creating group '%s' with GID " GID_FMT
".", i
->name
, i
->gid
);
1337 static int process_item(Item
*i
) {
1348 j
= ordered_hashmap_get(groups
, i
->group_name
?: i
->name
);
1350 if (j
&& j
->todo_group
) {
1351 /* When a group with the target name is already in queue,
1352 * use the information about the group and do not create
1353 * duplicated group entry. */
1354 i
->gid_set
= j
->gid_set
;
1356 i
->id_set_strict
= true;
1357 } else if (i
->group_name
) {
1358 /* When a group name was given instead of a GID and it's
1359 * not in queue, then it must already exist. */
1360 r
= get_gid_by_name(i
->group_name
, &i
->gid
);
1362 return log_error_errno(r
, "Group %s not found.", i
->group_name
);
1364 i
->id_set_strict
= true;
1375 return add_group(i
);
1378 assert_not_reached();
1382 static Item
* item_free(Item
*i
) {
1387 free(i
->group_name
);
1390 free(i
->description
);
1396 DEFINE_TRIVIAL_CLEANUP_FUNC(Item
*, item_free
);
1397 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(item_hash_ops
, char, string_hash_func
, string_compare_func
, Item
, item_free
);
1399 static int add_implicit(void) {
1403 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1404 ORDERED_HASHMAP_FOREACH_KEY(l
, g
, members
) {
1406 if (!ordered_hashmap_get(users
, *m
)) {
1407 _cleanup_(item_freep
) Item
*j
= NULL
;
1414 j
->name
= strdup(*m
);
1418 r
= ordered_hashmap_ensure_put(&users
, &item_hash_ops
, j
->name
, j
);
1422 return log_error_errno(r
, "Failed to add implicit user '%s': %m", j
->name
);
1424 log_debug("Adding implicit user '%s' due to m line", j
->name
);
1428 if (!(ordered_hashmap_get(users
, g
) ||
1429 ordered_hashmap_get(groups
, g
))) {
1430 _cleanup_(item_freep
) Item
*j
= NULL
;
1436 j
->type
= ADD_GROUP
;
1437 j
->name
= strdup(g
);
1441 r
= ordered_hashmap_ensure_put(&groups
, &item_hash_ops
, j
->name
, j
);
1445 return log_error_errno(r
, "Failed to add implicit group '%s': %m", j
->name
);
1447 log_debug("Adding implicit group '%s' due to m line", j
->name
);
1455 static int item_equivalent(Item
*a
, Item
*b
) {
1461 if (a
->type
!= b
->type
)
1464 if (!streq_ptr(a
->name
, b
->name
))
1467 /* Paths were simplified previously, so we can use streq. */
1468 if (!streq_ptr(a
->uid_path
, b
->uid_path
))
1471 if (!streq_ptr(a
->gid_path
, b
->gid_path
))
1474 if (!streq_ptr(a
->description
, b
->description
))
1477 if (a
->uid_set
!= b
->uid_set
)
1480 if (a
->uid_set
&& a
->uid
!= b
->uid
)
1483 if (a
->gid_set
!= b
->gid_set
)
1486 if (a
->gid_set
&& a
->gid
!= b
->gid
)
1489 if (!streq_ptr(a
->home
, b
->home
))
1492 /* Check if the two paths refer to the same file.
1493 * If the paths are equal (after normalization), it's obviously the same file.
1494 * If both paths specify a nologin shell, treat them as the same (e.g. /bin/true and /bin/false).
1495 * Otherwise, try to resolve the paths, and see if we get the same result, (e.g. /sbin/nologin and
1496 * /usr/sbin/nologin).
1497 * If we can't resolve something, treat different paths as different. */
1499 const char *a_shell
= pick_shell(a
),
1500 *b_shell
= pick_shell(b
);
1501 if (!path_equal_ptr(a_shell
, b_shell
) &&
1502 !(is_nologin_shell(a_shell
) && is_nologin_shell(b_shell
))) {
1503 _cleanup_free_
char *pa
= NULL
, *pb
= NULL
;
1505 r
= chase_symlinks(a_shell
, arg_root
, CHASE_PREFIX_ROOT
| CHASE_NONEXISTENT
, &pa
, NULL
);
1507 log_full_errno(ERRNO_IS_RESOURCE(r
) ? LOG_ERR
: LOG_DEBUG
,
1508 r
, "Failed to look up path '%s%s%s': %m",
1509 strempty(arg_root
), arg_root
? "/" : "", a_shell
);
1510 return ERRNO_IS_RESOURCE(r
) ? r
: false;
1513 r
= chase_symlinks(b_shell
, arg_root
, CHASE_PREFIX_ROOT
| CHASE_NONEXISTENT
, &pb
, NULL
);
1515 log_full_errno(ERRNO_IS_RESOURCE(r
) ? LOG_ERR
: LOG_DEBUG
,
1516 r
, "Failed to look up path '%s%s%s': %m",
1517 strempty(arg_root
), arg_root
? "/" : "", b_shell
);
1518 return ERRNO_IS_RESOURCE(r
) ? r
: false;
1521 if (!path_equal(pa
, pb
))
1528 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1529 _cleanup_free_
char *action
= NULL
,
1530 *name
= NULL
, *resolved_name
= NULL
,
1531 *id
= NULL
, *resolved_id
= NULL
,
1532 *description
= NULL
, *resolved_description
= NULL
,
1533 *home
= NULL
, *resolved_home
= NULL
,
1534 *shell
= NULL
, *resolved_shell
= NULL
;
1535 _cleanup_(item_freep
) Item
*i
= NULL
;
1547 r
= extract_many_words(&p
, NULL
, EXTRACT_UNQUOTE
,
1548 &action
, &name
, &id
, &description
, &home
, &shell
, NULL
);
1550 return log_syntax(NULL
, LOG_ERR
, fname
, line
, r
, "Syntax error.");
1552 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EINVAL
),
1553 "Missing action and name columns.");
1555 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EINVAL
),
1556 "Trailing garbage.");
1559 if (strlen(action
) != 1)
1560 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EINVAL
),
1561 "Unknown modifier '%s'.", action
);
1563 if (!IN_SET(action
[0], ADD_USER
, ADD_GROUP
, ADD_MEMBER
, ADD_RANGE
))
1564 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EBADMSG
),
1565 "Unknown command type '%c'.", action
[0]);
1568 if (empty_or_dash(name
))
1572 r
= specifier_printf(name
, NAME_MAX
, system_and_tmp_specifier_table
, arg_root
, NULL
, &resolved_name
);
1574 return log_syntax(NULL
, LOG_ERR
, fname
, line
, r
, "Failed to replace specifiers in '%s': %m", name
);
1576 if (!valid_user_group_name(resolved_name
, 0))
1577 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EINVAL
),
1578 "'%s' is not a valid user or group name.", resolved_name
);
1582 if (empty_or_dash(id
))
1586 r
= specifier_printf(id
, PATH_MAX
-1, system_and_tmp_specifier_table
, arg_root
, NULL
, &resolved_id
);
1588 return log_syntax(NULL
, LOG_ERR
, fname
, line
, r
,
1589 "Failed to replace specifiers in '%s': %m", name
);
1592 /* Verify description */
1593 if (empty_or_dash(description
))
1594 description
= mfree(description
);
1597 r
= specifier_printf(description
, LONG_LINE_MAX
, system_and_tmp_specifier_table
, arg_root
, NULL
, &resolved_description
);
1599 return log_syntax(NULL
, LOG_ERR
, fname
, line
, r
,
1600 "Failed to replace specifiers in '%s': %m", description
);
1602 if (!valid_gecos(resolved_description
))
1603 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EINVAL
),
1604 "'%s' is not a valid GECOS field.", resolved_description
);
1608 if (empty_or_dash(home
))
1612 r
= specifier_printf(home
, PATH_MAX
-1, system_and_tmp_specifier_table
, arg_root
, NULL
, &resolved_home
);
1614 return log_syntax(NULL
, LOG_ERR
, fname
, line
, r
,
1615 "Failed to replace specifiers in '%s': %m", home
);
1617 path_simplify(resolved_home
);
1619 if (!valid_home(resolved_home
))
1620 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EINVAL
),
1621 "'%s' is not a valid home directory field.", resolved_home
);
1625 if (empty_or_dash(shell
))
1626 shell
= mfree(shell
);
1629 r
= specifier_printf(shell
, PATH_MAX
-1, system_and_tmp_specifier_table
, arg_root
, NULL
, &resolved_shell
);
1631 return log_syntax(NULL
, LOG_ERR
, fname
, line
, r
,
1632 "Failed to replace specifiers in '%s': %m", shell
);
1634 path_simplify(resolved_shell
);
1636 if (!valid_shell(resolved_shell
))
1637 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EINVAL
),
1638 "'%s' is not a valid login shell field.", resolved_shell
);
1641 switch (action
[0]) {
1645 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EINVAL
),
1646 "Lines of type 'r' don't take a name field.");
1649 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EINVAL
),
1650 "Lines of type 'r' require an ID range in the third field.");
1652 if (description
|| home
|| shell
)
1653 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EINVAL
),
1654 "Lines of type '%c' don't take a %s field.",
1656 description
? "GECOS" : home
? "home directory" : "login shell");
1658 r
= uid_range_add_str(&uid_range
, &n_uid_range
, resolved_id
);
1660 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EINVAL
),
1661 "Invalid UID range %s.", resolved_id
);
1666 /* Try to extend an existing member or group item */
1668 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EINVAL
),
1669 "Lines of type 'm' require a user name in the second field.");
1672 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EINVAL
),
1673 "Lines of type 'm' require a group name in the third field.");
1675 if (!valid_user_group_name(resolved_id
, 0))
1676 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EINVAL
),
1677 "'%s' is not a valid user or group name.", resolved_id
);
1679 if (description
|| home
|| shell
)
1680 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EINVAL
),
1681 "Lines of type '%c' don't take a %s field.",
1683 description
? "GECOS" : home
? "home directory" : "login shell");
1685 r
= string_strv_ordered_hashmap_put(&members
, resolved_id
, resolved_name
);
1687 return log_error_errno(r
, "Failed to store mapping for %s: %m", resolved_id
);
1694 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EINVAL
),
1695 "Lines of type 'u' require a user name in the second field.");
1697 r
= ordered_hashmap_ensure_allocated(&users
, &item_hash_ops
);
1706 if (path_is_absolute(resolved_id
)) {
1707 i
->uid_path
= TAKE_PTR(resolved_id
);
1708 path_simplify(i
->uid_path
);
1710 _cleanup_free_
char *uid
= NULL
, *gid
= NULL
;
1711 if (split_pair(resolved_id
, ":", &uid
, &gid
) == 0) {
1712 r
= parse_gid(gid
, &i
->gid
);
1714 if (valid_user_group_name(gid
, 0))
1715 i
->group_name
= TAKE_PTR(gid
);
1717 return log_syntax(NULL
, LOG_ERR
, fname
, line
, r
,
1718 "Failed to parse GID: '%s': %m", id
);
1721 i
->id_set_strict
= true;
1723 free_and_replace(resolved_id
, uid
);
1725 if (!streq(resolved_id
, "-")) {
1726 r
= parse_uid(resolved_id
, &i
->uid
);
1728 return log_syntax(NULL
, LOG_ERR
, fname
, line
, r
,
1729 "Failed to parse UID: '%s': %m", id
);
1735 i
->description
= TAKE_PTR(resolved_description
);
1736 i
->home
= TAKE_PTR(resolved_home
);
1737 i
->shell
= TAKE_PTR(resolved_shell
);
1744 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EINVAL
),
1745 "Lines of type 'g' require a user name in the second field.");
1747 if (description
|| home
|| shell
)
1748 return log_syntax(NULL
, LOG_ERR
, fname
, line
, SYNTHETIC_ERRNO(EINVAL
),
1749 "Lines of type '%c' don't take a %s field.",
1751 description
? "GECOS" : home
? "home directory" : "login shell");
1753 r
= ordered_hashmap_ensure_allocated(&groups
, &item_hash_ops
);
1762 if (path_is_absolute(resolved_id
)) {
1763 i
->gid_path
= TAKE_PTR(resolved_id
);
1764 path_simplify(i
->gid_path
);
1766 r
= parse_gid(resolved_id
, &i
->gid
);
1768 return log_syntax(NULL
, LOG_ERR
, fname
, line
, r
,
1769 "Failed to parse GID: '%s': %m", id
);
1782 i
->type
= action
[0];
1783 i
->name
= TAKE_PTR(resolved_name
);
1785 existing
= ordered_hashmap_get(h
, i
->name
);
1787 /* Two functionally-equivalent items are fine */
1788 r
= item_equivalent(existing
, i
);
1792 log_syntax(NULL
, LOG_WARNING
, fname
, line
, SYNTHETIC_ERRNO(EUCLEAN
),
1793 "Conflict with earlier configuration for %s '%s', ignoring line.",
1794 item_type_to_string(i
->type
), i
->name
);
1799 r
= ordered_hashmap_put(h
, i
->name
, i
);
1807 static int read_config_file(const char *fn
, bool ignore_enoent
) {
1808 _cleanup_fclose_
FILE *rf
= NULL
;
1809 _cleanup_free_
char *pp
= NULL
;
1819 r
= search_and_fopen(fn
, "re", arg_root
, (const char**) CONF_PATHS_STRV("sysusers.d"), &rf
, &pp
);
1821 if (ignore_enoent
&& r
== -ENOENT
)
1824 return log_error_errno(r
, "Failed to open '%s', ignoring: %m", fn
);
1832 _cleanup_free_
char *line
= NULL
;
1836 k
= read_line(f
, LONG_LINE_MAX
, &line
);
1838 return log_error_errno(k
, "Failed to read '%s': %m", fn
);
1845 if (IN_SET(*l
, 0, '#'))
1848 k
= parse_line(fn
, v
, l
);
1849 if (k
< 0 && r
== 0)
1854 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
1862 static int cat_config(void) {
1863 _cleanup_strv_free_
char **files
= NULL
;
1866 r
= conf_files_list_with_replacement(arg_root
, CONF_PATHS_STRV("sysusers.d"), arg_replace
, &files
, NULL
);
1870 pager_open(arg_pager_flags
);
1872 return cat_files(NULL
, files
, 0);
1875 static int help(void) {
1876 _cleanup_free_
char *link
= NULL
;
1879 r
= terminal_urlify_man("systemd-sysusers.service", "8", &link
);
1883 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1884 "Creates system user accounts.\n\n"
1885 " -h --help Show this help\n"
1886 " --version Show package version\n"
1887 " --cat-config Show configuration files\n"
1888 " --root=PATH Operate on an alternate filesystem root\n"
1889 " --image=PATH Operate on disk image as filesystem root\n"
1890 " --replace=PATH Treat arguments as replacement for PATH\n"
1891 " --dry-run Just print what would be done\n"
1892 " --inline Treat arguments as configuration lines\n"
1893 " --no-pager Do not pipe output into a pager\n"
1894 "\nSee the %s for details.\n",
1895 program_invocation_short_name
,
1901 static int parse_argv(int argc
, char *argv
[]) {
1904 ARG_VERSION
= 0x100,
1914 static const struct option options
[] = {
1915 { "help", no_argument
, NULL
, 'h' },
1916 { "version", no_argument
, NULL
, ARG_VERSION
},
1917 { "cat-config", no_argument
, NULL
, ARG_CAT_CONFIG
},
1918 { "root", required_argument
, NULL
, ARG_ROOT
},
1919 { "image", required_argument
, NULL
, ARG_IMAGE
},
1920 { "replace", required_argument
, NULL
, ARG_REPLACE
},
1921 { "dry-run", no_argument
, NULL
, ARG_DRY_RUN
},
1922 { "inline", no_argument
, NULL
, ARG_INLINE
},
1923 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1932 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
1942 case ARG_CAT_CONFIG
:
1943 arg_cat_config
= true;
1947 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_root
);
1954 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
1955 "This systemd-sysusers version is compiled without support for --image=.");
1957 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_image
);
1964 if (!path_is_absolute(optarg
) ||
1965 !endswith(optarg
, ".conf"))
1966 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1967 "The argument to --replace= must an absolute path to a config file");
1969 arg_replace
= optarg
;
1981 arg_pager_flags
|= PAGER_DISABLE
;
1988 assert_not_reached();
1991 if (arg_replace
&& arg_cat_config
)
1992 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1993 "Option --replace= is not supported with --cat-config");
1995 if (arg_replace
&& optind
>= argc
)
1996 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1997 "When --replace= is given, some configuration items must be specified");
1999 if (arg_image
&& arg_root
)
2000 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Please specify either --root= or --image=, the combination of both is not supported.");
2005 static int parse_arguments(char **args
) {
2009 STRV_FOREACH(arg
, args
) {
2011 /* Use (argument):n, where n==1 for the first positional arg */
2012 r
= parse_line("(argument)", pos
, *arg
);
2014 r
= read_config_file(*arg
, /* ignore_enoent= */ false);
2024 static int read_config_files(char **args
) {
2025 _cleanup_strv_free_
char **files
= NULL
;
2026 _cleanup_free_
char *p
= NULL
;
2029 r
= conf_files_list_with_replacement(arg_root
, CONF_PATHS_STRV("sysusers.d"), arg_replace
, &files
, &p
);
2033 STRV_FOREACH(f
, files
)
2034 if (p
&& path_equal(*f
, p
)) {
2035 log_debug("Parsing arguments at position \"%s\"%s", *f
, special_glyph(SPECIAL_GLYPH_ELLIPSIS
));
2037 r
= parse_arguments(args
);
2041 log_debug("Reading config file \"%s\"%s", *f
, special_glyph(SPECIAL_GLYPH_ELLIPSIS
));
2043 /* Just warn, ignore result otherwise */
2044 (void) read_config_file(*f
, /* ignore_enoent= */ true);
2050 static int read_credential_lines(void) {
2051 _cleanup_free_
char *j
= NULL
;
2055 r
= get_credentials_dir(&d
);
2059 return log_error_errno(r
, "Failed to get credentials directory: %m");
2061 j
= path_join(d
, "sysusers.extra");
2065 (void) read_config_file(j
, /* ignore_enoent= */ true);
2069 static int run(int argc
, char *argv
[]) {
2071 _cleanup_(loop_device_unrefp
) LoopDevice
*loop_device
= NULL
;
2072 _cleanup_(decrypted_image_unrefp
) DecryptedImage
*decrypted_image
= NULL
;
2073 _cleanup_(umount_and_rmdir_and_freep
) char *unlink_dir
= NULL
;
2075 _cleanup_close_
int lock
= -1;
2079 r
= parse_argv(argc
, argv
);
2086 return cat_config();
2090 r
= mac_selinux_init();
2098 r
= mount_image_privately_interactively(
2100 DISSECT_IMAGE_GENERIC_ROOT
|
2101 DISSECT_IMAGE_REQUIRE_ROOT
|
2102 DISSECT_IMAGE_VALIDATE_OS
|
2103 DISSECT_IMAGE_RELAX_VAR_CHECK
|
2104 DISSECT_IMAGE_FSCK
|
2105 DISSECT_IMAGE_GROWFS
,
2112 arg_root
= strdup(unlink_dir
);
2120 /* If command line arguments are specified along with --replace, read all configuration files and
2121 * insert the positional arguments at the specified place. Otherwise, if command line arguments are
2122 * specified, execute just them, and finally, without --replace= or any positional arguments, just
2123 * read configuration and execute it. */
2124 if (arg_replace
|| optind
>= argc
)
2125 r
= read_config_files(argv
+ optind
);
2127 r
= parse_arguments(argv
+ optind
);
2131 r
= read_credential_lines();
2135 /* Let's tell nss-systemd not to synthesize the "root" and "nobody" entries for it, so that our
2136 * detection whether the names or UID/GID area already used otherwise doesn't get confused. After
2137 * all, even though nss-systemd synthesizes these users/groups, they should still appear in
2138 * /etc/passwd and /etc/group, as the synthesizing logic is merely supposed to be fallback for cases
2139 * where we run with a completely unpopulated /etc. */
2140 if (setenv("SYSTEMD_NSS_BYPASS_SYNTHETIC", "1", 1) < 0)
2141 return log_error_errno(errno
, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m");
2144 /* Default to default range of SYSTEMD_UID_MIN..SYSTEM_UID_MAX. */
2145 r
= read_login_defs(&login_defs
, NULL
, arg_root
);
2147 return log_error_errno(r
, "Failed to read %s%s: %m",
2148 strempty(arg_root
), "/etc/login.defs");
2150 login_defs_need_warning
= true;
2152 /* We pick a range that very conservative: we look at compiled-in maximum and the value in
2153 * /etc/login.defs. That way the UIDs/GIDs which we allocate will be interpreted correctly,
2154 * even if /etc/login.defs is removed later. (The bottom bound doesn't matter much, since
2155 * it's only used during allocation, so we use the configured value directly). */
2156 uid_t begin
= login_defs
.system_alloc_uid_min
,
2157 end
= MIN3((uid_t
) SYSTEM_UID_MAX
, login_defs
.system_uid_max
, login_defs
.system_gid_max
);
2159 r
= uid_range_add(&uid_range
, &n_uid_range
, begin
, end
- begin
+ 1);
2170 lock
= take_etc_passwd_lock(arg_root
);
2172 return log_error_errno(lock
, "Failed to take /etc/passwd lock: %m");
2175 r
= load_user_database();
2177 return log_error_errno(r
, "Failed to load user database: %m");
2179 r
= load_group_database();
2181 return log_error_errno(r
, "Failed to read group database: %m");
2183 ORDERED_HASHMAP_FOREACH(i
, groups
)
2184 (void) process_item(i
);
2186 ORDERED_HASHMAP_FOREACH(i
, users
)
2187 (void) process_item(i
);
2189 return write_files();
2192 DEFINE_MAIN_FUNCTION(run
);