1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <getopt.h>
4 #include <utmp.h>
5
6 #include "alloc-util.h"
7 #include "conf-files.h"
8 #include "copy.h"
9 #include "creds-util.h"
10 #include "def.h"
11 #include "dissect-image.h"
12 #include "fd-util.h"
13 #include "fileio.h"
14 #include "format-util.h"
15 #include "fs-util.h"
16 #include "hashmap.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"
22 #include "pager.h"
23 #include "parse-argument.h"
24 #include "path-util.h"
25 #include "pretty-print.h"
26 #include "selinux-util.h"
27 #include "set.h"
28 #include "smack-util.h"
29 #include "specifier.h"
30 #include "string-util.h"
31 #include "strv.h"
32 #include "sync-util.h"
33 #include "tmpfile-util-label.h"
34 #include "uid-alloc-range.h"
35 #include "uid-range.h"
36 #include "user-util.h"
37 #include "utf8.h"
38 #include "util.h"
39
40 typedef enum ItemType {
41 ADD_USER = 'u',
42 ADD_GROUP = 'g',
43 ADD_MEMBER = 'm',
44 ADD_RANGE = 'r',
45 } ItemType;
46
item_type_to_string(ItemType t)47 static inline const char* item_type_to_string(ItemType t) {
48 switch (t) {
49 case ADD_USER:
50 return "user";
51 case ADD_GROUP:
52 return "group";
53 case ADD_MEMBER:
54 return "member";
55 case ADD_RANGE:
56 return "range";
57 default:
58 assert_not_reached();
59 }
60 }
61
62 typedef struct Item {
63 ItemType type;
64
65 char *name;
66 char *group_name;
67 char *uid_path;
68 char *gid_path;
69 char *description;
70 char *home;
71 char *shell;
72
73 gid_t gid;
74 uid_t uid;
75
76 bool gid_set:1;
77
78 /* When set the group with the specified GID must exist
79 * and the check if a UID clashes with the GID is skipped.
80 */
81 bool id_set_strict:1;
82
83 bool uid_set:1;
84
85 bool todo_user:1;
86 bool todo_group:1;
87 } Item;
88
89 static char *arg_root = NULL;
90 static char *arg_image = NULL;
91 static bool arg_cat_config = false;
92 static const char *arg_replace = NULL;
93 static bool arg_dry_run = false;
94 static bool arg_inline = false;
95 static PagerFlags arg_pager_flags = 0;
96
97 static OrderedHashmap *users = NULL, *groups = NULL;
98 static OrderedHashmap *todo_uids = NULL, *todo_gids = NULL;
99 static OrderedHashmap *members = NULL;
100
101 static Hashmap *database_by_uid = NULL, *database_by_username = NULL;
102 static Hashmap *database_by_gid = NULL, *database_by_groupname = NULL;
103 static Set *database_users = NULL, *database_groups = NULL;
104
105 static uid_t search_uid = UID_INVALID;
106 static UidRange *uid_range = NULL;
107 static size_t n_uid_range = 0;
108
109 static UGIDAllocationRange login_defs = {};
110 static bool login_defs_need_warning = false;
111
112 STATIC_DESTRUCTOR_REGISTER(groups, ordered_hashmap_freep);
113 STATIC_DESTRUCTOR_REGISTER(users, ordered_hashmap_freep);
114 STATIC_DESTRUCTOR_REGISTER(members, ordered_hashmap_freep);
115 STATIC_DESTRUCTOR_REGISTER(todo_uids, ordered_hashmap_freep);
116 STATIC_DESTRUCTOR_REGISTER(todo_gids, ordered_hashmap_freep);
117 STATIC_DESTRUCTOR_REGISTER(database_by_uid, hashmap_freep);
118 STATIC_DESTRUCTOR_REGISTER(database_by_username, hashmap_freep);
119 STATIC_DESTRUCTOR_REGISTER(database_users, set_free_freep);
120 STATIC_DESTRUCTOR_REGISTER(database_by_gid, hashmap_freep);
121 STATIC_DESTRUCTOR_REGISTER(database_by_groupname, hashmap_freep);
122 STATIC_DESTRUCTOR_REGISTER(database_groups, set_free_freep);
123 STATIC_DESTRUCTOR_REGISTER(uid_range, freep);
124 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
125 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
126
errno_is_not_exists(int code)127 static int errno_is_not_exists(int code) {
128 /* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are
129 * not found. */
130 return IN_SET(code, 0, ENOENT, ESRCH, EBADF, EPERM);
131 }
132
maybe_emit_login_defs_warning(void)133 static void maybe_emit_login_defs_warning(void) {
134 if (!login_defs_need_warning)
135 return;
136
137 if (login_defs.system_alloc_uid_min != SYSTEM_ALLOC_UID_MIN ||
138 login_defs.system_uid_max != SYSTEM_UID_MAX)
139 log_warning("login.defs specifies UID allocation range "UID_FMT"–"UID_FMT
140 " that is different than the built-in defaults ("UID_FMT"–"UID_FMT")",
141 login_defs.system_alloc_uid_min, login_defs.system_uid_max,
142 SYSTEM_ALLOC_UID_MIN, SYSTEM_UID_MAX);
143 if (login_defs.system_alloc_gid_min != SYSTEM_ALLOC_GID_MIN ||
144 login_defs.system_gid_max != SYSTEM_GID_MAX)
145 log_warning("login.defs specifies GID allocation range "GID_FMT"–"GID_FMT
146 " that is different than the built-in defaults ("GID_FMT"–"GID_FMT")",
147 login_defs.system_alloc_gid_min, login_defs.system_gid_max,
148 SYSTEM_ALLOC_GID_MIN, SYSTEM_GID_MAX);
149
150 login_defs_need_warning = false;
151 }
152
load_user_database(void)153 static int load_user_database(void) {
154 _cleanup_fclose_ FILE *f = NULL;
155 const char *passwd_path;
156 struct passwd *pw;
157 int r;
158
159 passwd_path = prefix_roota(arg_root, "/etc/passwd");
160 f = fopen(passwd_path, "re");
161 if (!f)
162 return errno == ENOENT ? 0 : -errno;
163
164 r = hashmap_ensure_allocated(&database_by_username, &string_hash_ops);
165 if (r < 0)
166 return r;
167
168 r = hashmap_ensure_allocated(&database_by_uid, NULL);
169 if (r < 0)
170 return r;
171
172 r = set_ensure_allocated(&database_users, NULL);
173 if (r < 0)
174 return r;
175
176 while ((r = fgetpwent_sane(f, &pw)) > 0) {
177 char *n;
178 int k, q;
179
180 n = strdup(pw->pw_name);
181 if (!n)
182 return -ENOMEM;
183
184 k = set_put(database_users, n);
185 if (k < 0) {
186 free(n);
187 return k;
188 }
189
190 k = hashmap_put(database_by_username, n, UID_TO_PTR(pw->pw_uid));
191 if (k < 0 && k != -EEXIST)
192 return k;
193
194 q = hashmap_put(database_by_uid, UID_TO_PTR(pw->pw_uid), n);
195 if (q < 0 && q != -EEXIST)
196 return q;
197 }
198 return r;
199 }
200
load_group_database(void)201 static int load_group_database(void) {
202 _cleanup_fclose_ FILE *f = NULL;
203 const char *group_path;
204 struct group *gr;
205 int r;
206
207 group_path = prefix_roota(arg_root, "/etc/group");
208 f = fopen(group_path, "re");
209 if (!f)
210 return errno == ENOENT ? 0 : -errno;
211
212 r = hashmap_ensure_allocated(&database_by_groupname, &string_hash_ops);
213 if (r < 0)
214 return r;
215
216 r = hashmap_ensure_allocated(&database_by_gid, NULL);
217 if (r < 0)
218 return r;
219
220 r = set_ensure_allocated(&database_groups, NULL);
221 if (r < 0)
222 return r;
223
224 while ((r = fgetgrent_sane(f, &gr)) > 0) {
225 char *n;
226 int k, q;
227
228 n = strdup(gr->gr_name);
229 if (!n)
230 return -ENOMEM;
231
232 k = set_put(database_groups, n);
233 if (k < 0) {
234 free(n);
235 return k;
236 }
237
238 k = hashmap_put(database_by_groupname, n, GID_TO_PTR(gr->gr_gid));
239 if (k < 0 && k != -EEXIST)
240 return k;
241
242 q = hashmap_put(database_by_gid, GID_TO_PTR(gr->gr_gid), n);
243 if (q < 0 && q != -EEXIST)
244 return q;
245 }
246 return r;
247 }
248
make_backup(const char * target,const char * x)249 static int make_backup(const char *target, const char *x) {
250 _cleanup_(unlink_and_freep) char *dst_tmp = NULL;
251 _cleanup_fclose_ FILE *dst = NULL;
252 _cleanup_close_ int src = -1;
253 const char *backup;
254 struct stat st;
255 int r;
256
257 assert(target);
258 assert(x);
259
260 src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
261 if (src < 0) {
262 if (errno == ENOENT) /* No backup necessary... */
263 return 0;
264
265 return -errno;
266 }
267
268 if (fstat(src, &st) < 0)
269 return -errno;
270
271 r = fopen_temporary_label(
272 target, /* The path for which to the look up the label */
273 x, /* Where we want the file actually to end up */
274 &dst, /* The temporary file we write to */
275 &dst_tmp);
276 if (r < 0)
277 return r;
278
279 r = copy_bytes(src, fileno(dst), UINT64_MAX, COPY_REFLINK);
280 if (r < 0)
281 return r;
282
283 backup = strjoina(x, "-");
284
285 /* Copy over the access mask. Don't fail on chmod() or chown(). If it stays owned by us and/or
286 * unreadable by others, then it isn't too bad... */
287 r = fchmod_and_chown_with_fallback(fileno(dst), dst_tmp, st.st_mode & 07777, st.st_uid, st.st_gid);
288 if (r < 0)
289 log_warning_errno(r, "Failed to change access mode or ownership of %s: %m", backup);
290
291 if (futimens(fileno(dst), (const struct timespec[2]) { st.st_atim, st.st_mtim }) < 0)
292 log_warning_errno(errno, "Failed to fix access and modification time of %s: %m", backup);
293
294 r = fsync_full(fileno(dst));
295 if (r < 0)
296 return r;
297
298 if (rename(dst_tmp, backup) < 0)
299 return errno;
300
301 dst_tmp = mfree(dst_tmp); /* disable the unlink_and_freep() hook now that the file has been renamed */
302 return 0;
303 }
304
putgrent_with_members(const struct group * gr,FILE * group)305 static int putgrent_with_members(const struct group *gr, FILE *group) {
306 char **a;
307
308 assert(gr);
309 assert(group);
310
311 a = ordered_hashmap_get(members, gr->gr_name);
312 if (a) {
313 _cleanup_strv_free_ char **l = NULL;
314 bool added = false;
315
316 l = strv_copy(gr->gr_mem);
317 if (!l)
318 return -ENOMEM;
319
320 STRV_FOREACH(i, a) {
321 if (strv_contains(l, *i))
322 continue;
323
324 if (strv_extend(&l, *i) < 0)
325 return -ENOMEM;
326
327 added = true;
328 }
329
330 if (added) {
331 struct group t;
332 int r;
333
334 strv_uniq(l);
335 strv_sort(l);
336
337 t = *gr;
338 t.gr_mem = l;
339
340 r = putgrent_sane(&t, group);
341 return r < 0 ? r : 1;
342 }
343 }
344
345 return putgrent_sane(gr, group);
346 }
347
348 #if ENABLE_GSHADOW
putsgent_with_members(const struct sgrp * sg,FILE * gshadow)349 static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
350 char **a;
351
352 assert(sg);
353 assert(gshadow);
354
355 a = ordered_hashmap_get(members, sg->sg_namp);
356 if (a) {
357 _cleanup_strv_free_ char **l = NULL;
358 bool added = false;
359
360 l = strv_copy(sg->sg_mem);
361 if (!l)
362 return -ENOMEM;
363
364 STRV_FOREACH(i, a) {
365 if (strv_contains(l, *i))
366 continue;
367
368 if (strv_extend(&l, *i) < 0)
369 return -ENOMEM;
370
371 added = true;
372 }
373
374 if (added) {
375 struct sgrp t;
376 int r;
377
378 strv_uniq(l);
379 strv_sort(l);
380
381 t = *sg;
382 t.sg_mem = l;
383
384 r = putsgent_sane(&t, gshadow);
385 return r < 0 ? r : 1;
386 }
387 }
388
389 return putsgent_sane(sg, gshadow);
390 }
391 #endif
392
default_shell(uid_t uid)393 static const char* default_shell(uid_t uid) {
394 return uid == 0 ? "/bin/sh" : NOLOGIN;
395 }
396
write_temporary_passwd(const char * passwd_path,FILE ** tmpfile,char ** tmpfile_path)397 static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char **tmpfile_path) {
398 _cleanup_fclose_ FILE *original = NULL, *passwd = NULL;
399 _cleanup_(unlink_and_freep) char *passwd_tmp = NULL;
400 struct passwd *pw = NULL;
401 Item *i;
402 int r;
403
404 if (ordered_hashmap_isempty(todo_uids))
405 return 0;
406
407 if (arg_dry_run) {
408 log_info("Would write /etc/passwd…");
409 return 0;
410 }
411
412 r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
413 if (r < 0)
414 return log_debug_errno(r, "Failed to open temporary copy of %s: %m", passwd_path);
415
416 original = fopen(passwd_path, "re");
417 if (original) {
418
419 /* Allow fallback path for when /proc is not mounted. On any normal system /proc will be
420 * mounted, but e.g. when 'dnf --installroot' is used, it might not be. There is no security
421 * relevance here, since the environment is ultimately trusted, and not requiring /proc makes
422 * it easier to depend on sysusers in packaging scripts and suchlike. */
423 r = copy_rights_with_fallback(fileno(original), fileno(passwd), passwd_tmp);
424 if (r < 0)
425 return log_debug_errno(r, "Failed to copy permissions from %s to %s: %m",
426 passwd_path, passwd_tmp);
427
428 while ((r = fgetpwent_sane(original, &pw)) > 0) {
429 i = ordered_hashmap_get(users, pw->pw_name);
430 if (i && i->todo_user)
431 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
432 "%s: User \"%s\" already exists.",
433 passwd_path, pw->pw_name);
434
435 if (ordered_hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid)))
436 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
437 "%s: Detected collision for UID " UID_FMT ".",
438 passwd_path, pw->pw_uid);
439
440 /* Make sure we keep the NIS entries (if any) at the end. */
441 if (IN_SET(pw->pw_name[0], '+', '-'))
442 break;
443
444 r = putpwent_sane(pw, passwd);
445 if (r < 0)
446 return log_debug_errno(r, "Failed to add existing user \"%s\" to temporary passwd file: %m",
447 pw->pw_name);
448 }
449 if (r < 0)
450 return log_debug_errno(r, "Failed to read %s: %m", passwd_path);
451
452 } else {
453 if (errno != ENOENT)
454 return log_debug_errno(errno, "Failed to open %s: %m", passwd_path);
455 if (fchmod(fileno(passwd), 0644) < 0)
456 return log_debug_errno(errno, "Failed to fchmod %s: %m", passwd_tmp);
457 }
458
459 ORDERED_HASHMAP_FOREACH(i, todo_uids) {
460 _cleanup_free_ char *creds_shell = NULL, *cn = NULL;
461
462 struct passwd n = {
463 .pw_name = i->name,
464 .pw_uid = i->uid,
465 .pw_gid = i->gid,
466 .pw_gecos = (char*) strempty(i->description),
467
468 /* "x" means the password is stored in the shadow file */
469 .pw_passwd = (char*) PASSWORD_SEE_SHADOW,
470
471 /* We default to the root directory as home */
472 .pw_dir = i->home ?: (char*) "/",
473
474 /* Initialize the shell to nologin, with one exception:
475 * for root we patch in something special */
476 .pw_shell = i->shell ?: (char*) default_shell(i->uid),
477 };
478
479 /* Try to pick up the shell for this account via the credentials logic */
480 cn = strjoin("passwd.shell.", i->name);
481 if (!cn)
482 return -ENOMEM;
483
484 r = read_credential(cn, (void**) &creds_shell, NULL);
485 if (r < 0)
486 log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn);
487 else
488 n.pw_shell = creds_shell;
489
490 r = putpwent_sane(&n, passwd);
491 if (r < 0)
492 return log_debug_errno(r, "Failed to add new user \"%s\" to temporary passwd file: %m",
493 pw->pw_name);
494 }
495
496 /* Append the remaining NIS entries if any */
497 while (pw) {
498 r = putpwent_sane(pw, passwd);
499 if (r < 0)
500 return log_debug_errno(r, "Failed to add existing user \"%s\" to temporary passwd file: %m",
501 pw->pw_name);
502
503 r = fgetpwent_sane(original, &pw);
504 if (r < 0)
505 return log_debug_errno(r, "Failed to read %s: %m", passwd_path);
506 if (r == 0)
507 break;
508 }
509
510 r = fflush_and_check(passwd);
511 if (r < 0)
512 return log_debug_errno(r, "Failed to flush %s: %m", passwd_tmp);
513
514 *tmpfile = TAKE_PTR(passwd);
515 *tmpfile_path = TAKE_PTR(passwd_tmp);
516
517 return 0;
518 }
519
write_temporary_shadow(const char * shadow_path,FILE ** tmpfile,char ** tmpfile_path)520 static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char **tmpfile_path) {
521 _cleanup_fclose_ FILE *original = NULL, *shadow = NULL;
522 _cleanup_(unlink_and_freep) char *shadow_tmp = NULL;
523 struct spwd *sp = NULL;
524 long lstchg;
525 Item *i;
526 int r;
527
528 if (ordered_hashmap_isempty(todo_uids))
529 return 0;
530
531 if (arg_dry_run) {
532 log_info("Would write /etc/shadow…");
533 return 0;
534 }
535
536 r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
537 if (r < 0)
538 return log_debug_errno(r, "Failed to open temporary copy of %s: %m", shadow_path);
539
540 lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
541
542 original = fopen(shadow_path, "re");
543 if (original) {
544
545 r = copy_rights_with_fallback(fileno(original), fileno(shadow), shadow_tmp);
546 if (r < 0)
547 return log_debug_errno(r, "Failed to copy permissions from %s to %s: %m",
548 shadow_path, shadow_tmp);
549
550 while ((r = fgetspent_sane(original, &sp)) > 0) {
551 i = ordered_hashmap_get(users, sp->sp_namp);
552 if (i && i->todo_user) {
553 /* we will update the existing entry */
554 sp->sp_lstchg = lstchg;
555
556 /* only the /etc/shadow stage is left, so we can
557 * safely remove the item from the todo set */
558 i->todo_user = false;
559 ordered_hashmap_remove(todo_uids, UID_TO_PTR(i->uid));
560 }
561
562 /* Make sure we keep the NIS entries (if any) at the end. */
563 if (IN_SET(sp->sp_namp[0], '+', '-'))
564 break;
565
566 r = putspent_sane(sp, shadow);
567 if (r < 0)
568 return log_debug_errno(r, "Failed to add existing user \"%s\" to temporary shadow file: %m",
569 sp->sp_namp);
570
571 }
572 if (r < 0)
573 return log_debug_errno(r, "Failed to read %s: %m", shadow_path);
574
575 } else {
576 if (errno != ENOENT)
577 return log_debug_errno(errno, "Failed to open %s: %m", shadow_path);
578 if (fchmod(fileno(shadow), 0000) < 0)
579 return log_debug_errno(errno, "Failed to fchmod %s: %m", shadow_tmp);
580 }
581
582 ORDERED_HASHMAP_FOREACH(i, todo_uids) {
583 _cleanup_(erase_and_freep) char *creds_password = NULL;
584 _cleanup_free_ char *cn = NULL;
585
586 struct spwd n = {
587 .sp_namp = i->name,
588 .sp_pwdp = (char*) PASSWORD_LOCKED_AND_INVALID,
589 .sp_lstchg = lstchg,
590 .sp_min = -1,
591 .sp_max = -1,
592 .sp_warn = -1,
593 .sp_inact = -1,
594 .sp_expire = -1,
595 .sp_flag = ULONG_MAX, /* this appears to be what everybody does ... */
596 };
597
598 /* Try to pick up the password for this account via the credentials logic */
599 cn = strjoin("passwd.hashed-password.", i->name);
600 if (!cn)
601 return -ENOMEM;
602
603 r = read_credential(cn, (void**) &creds_password, NULL);
604 if (r == -ENOENT) {
605 _cleanup_(erase_and_freep) char *plaintext_password = NULL;
606
607 free(cn);
608 cn = strjoin("passwd.plaintext-password.", i->name);
609 if (!cn)
610 return -ENOMEM;
611
612 r = read_credential(cn, (void**) &plaintext_password, NULL);
613 if (r < 0)
614 log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn);
615 else {
616 r = hash_password(plaintext_password, &creds_password);
617 if (r < 0)
618 return log_debug_errno(r, "Failed to hash password: %m");
619 }
620 } else if (r < 0)
621 log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn);
622
623 if (creds_password)
624 n.sp_pwdp = creds_password;
625
626 r = putspent_sane(&n, shadow);
627 if (r < 0)
628 return log_debug_errno(r, "Failed to add new user \"%s\" to temporary shadow file: %m",
629 sp->sp_namp);
630 }
631
632 /* Append the remaining NIS entries if any */
633 while (sp) {
634 r = putspent_sane(sp, shadow);
635 if (r < 0)
636 return log_debug_errno(r, "Failed to add existing user \"%s\" to temporary shadow file: %m",
637 sp->sp_namp);
638
639 r = fgetspent_sane(original, &sp);
640 if (r < 0)
641 return log_debug_errno(r, "Failed to read %s: %m", shadow_path);
642 if (r == 0)
643 break;
644 }
645 if (!IN_SET(errno, 0, ENOENT))
646 return -errno;
647
648 r = fflush_sync_and_check(shadow);
649 if (r < 0)
650 return log_debug_errno(r, "Failed to flush %s: %m", shadow_tmp);
651
652 *tmpfile = TAKE_PTR(shadow);
653 *tmpfile_path = TAKE_PTR(shadow_tmp);
654
655 return 0;
656 }
657
write_temporary_group(const char * group_path,FILE ** tmpfile,char ** tmpfile_path)658 static int write_temporary_group(const char *group_path, FILE **tmpfile, char **tmpfile_path) {
659 _cleanup_fclose_ FILE *original = NULL, *group = NULL;
660 _cleanup_(unlink_and_freep) char *group_tmp = NULL;
661 bool group_changed = false;
662 struct group *gr = NULL;
663 Item *i;
664 int r;
665
666 if (ordered_hashmap_isempty(todo_gids) && ordered_hashmap_isempty(members))
667 return 0;
668
669 if (arg_dry_run) {
670 log_info("Would write /etc/group…");
671 return 0;
672 }
673
674 r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
675 if (r < 0)
676 return log_debug_errno(r, "Failed to open temporary copy of %s: %m", group_path);
677
678 original = fopen(group_path, "re");
679 if (original) {
680
681 r = copy_rights_with_fallback(fileno(original), fileno(group), group_tmp);
682 if (r < 0)
683 return log_debug_errno(r, "Failed to copy permissions from %s to %s: %m",
684 group_path, group_tmp);
685
686 while ((r = fgetgrent_sane(original, &gr)) > 0) {
687 /* Safety checks against name and GID collisions. Normally,
688 * this should be unnecessary, but given that we look at the
689 * entries anyway here, let's make an extra verification
690 * step that we don't generate duplicate entries. */
691
692 i = ordered_hashmap_get(groups, gr->gr_name);
693 if (i && i->todo_group)
694 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
695 "%s: Group \"%s\" already exists.",
696 group_path, gr->gr_name);
697
698 if (ordered_hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid)))
699 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
700 "%s: Detected collision for GID " GID_FMT ".",
701 group_path, gr->gr_gid);
702
703 /* Make sure we keep the NIS entries (if any) at the end. */
704 if (IN_SET(gr->gr_name[0], '+', '-'))
705 break;
706
707 r = putgrent_with_members(gr, group);
708 if (r < 0)
709 return log_debug_errno(r, "Failed to add existing group \"%s\" to temporary group file: %m",
710 gr->gr_name);
711 if (r > 0)
712 group_changed = true;
713 }
714 if (r < 0)
715 return log_debug_errno(r, "Failed to read %s: %m", group_path);
716
717 } else {
718 if (errno != ENOENT)
719 return log_debug_errno(errno, "Failed to open %s: %m", group_path);
720 if (fchmod(fileno(group), 0644) < 0)
721 return log_debug_errno(errno, "Failed to fchmod %s: %m", group_tmp);
722 }
723
724 ORDERED_HASHMAP_FOREACH(i, todo_gids) {
725 struct group n = {
726 .gr_name = i->name,
727 .gr_gid = i->gid,
728 .gr_passwd = (char*) PASSWORD_SEE_SHADOW,
729 };
730
731 r = putgrent_with_members(&n, group);
732 if (r < 0)
733 return log_debug_errno(r, "Failed to add new group \"%s\" to temporary group file: %m",
734 gr->gr_name);
735
736 group_changed = true;
737 }
738
739 /* Append the remaining NIS entries if any */
740 while (gr) {
741 r = putgrent_sane(gr, group);
742 if (r < 0)
743 return log_debug_errno(r, "Failed to add existing group \"%s\" to temporary group file: %m",
744 gr->gr_name);
745
746 r = fgetgrent_sane(original, &gr);
747 if (r < 0)
748 return log_debug_errno(r, "Failed to read %s: %m", group_path);
749 if (r == 0)
750 break;
751 }
752
753 r = fflush_sync_and_check(group);
754 if (r < 0)
755 return log_debug_errno(r, "Failed to flush %s: %m", group_tmp);
756
757 if (group_changed) {
758 *tmpfile = TAKE_PTR(group);
759 *tmpfile_path = TAKE_PTR(group_tmp);
760 }
761 return 0;
762 }
763
write_temporary_gshadow(const char * gshadow_path,FILE ** tmpfile,char ** tmpfile_path)764 static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, char **tmpfile_path) {
765 #if ENABLE_GSHADOW
766 _cleanup_fclose_ FILE *original = NULL, *gshadow = NULL;
767 _cleanup_(unlink_and_freep) char *gshadow_tmp = NULL;
768 bool group_changed = false;
769 Item *i;
770 int r;
771
772 if (ordered_hashmap_isempty(todo_gids) && ordered_hashmap_isempty(members))
773 return 0;
774
775 if (arg_dry_run) {
776 log_info("Would write /etc/gshadow…");
777 return 0;
778 }
779
780 r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
781 if (r < 0)
782 return log_debug_errno(r, "Failed to open temporary copy of %s: %m", gshadow_path);
783
784 original = fopen(gshadow_path, "re");
785 if (original) {
786 struct sgrp *sg;
787
788 r = copy_rights_with_fallback(fileno(original), fileno(gshadow), gshadow_tmp);
789 if (r < 0)
790 return log_debug_errno(r, "Failed to copy permissions from %s to %s: %m",
791 gshadow_path, gshadow_tmp);
792
793 while ((r = fgetsgent_sane(original, &sg)) > 0) {
794
795 i = ordered_hashmap_get(groups, sg->sg_namp);
796 if (i && i->todo_group)
797 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
798 "%s: Group \"%s\" already exists.",
799 gshadow_path, sg->sg_namp);
800
801 r = putsgent_with_members(sg, gshadow);
802 if (r < 0)
803 return log_debug_errno(r, "Failed to add existing group \"%s\" to temporary gshadow file: %m",
804 sg->sg_namp);
805 if (r > 0)
806 group_changed = true;
807 }
808 if (r < 0)
809 return r;
810
811 } else {
812 if (errno != ENOENT)
813 return log_debug_errno(errno, "Failed to open %s: %m", gshadow_path);
814 if (fchmod(fileno(gshadow), 0000) < 0)
815 return log_debug_errno(errno, "Failed to fchmod %s: %m", gshadow_tmp);
816 }
817
818 ORDERED_HASHMAP_FOREACH(i, todo_gids) {
819 struct sgrp n = {
820 .sg_namp = i->name,
821 .sg_passwd = (char*) PASSWORD_LOCKED_AND_INVALID,
822 };
823
824 r = putsgent_with_members(&n, gshadow);
825 if (r < 0)
826 return log_debug_errno(r, "Failed to add new group \"%s\" to temporary gshadow file: %m",
827 n.sg_namp);
828
829 group_changed = true;
830 }
831
832 r = fflush_sync_and_check(gshadow);
833 if (r < 0)
834 return log_debug_errno(r, "Failed to flush %s: %m", gshadow_tmp);
835
836 if (group_changed) {
837 *tmpfile = TAKE_PTR(gshadow);
838 *tmpfile_path = TAKE_PTR(gshadow_tmp);
839 }
840 #endif
841 return 0;
842 }
843
write_files(void)844 static int write_files(void) {
845 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL;
846 _cleanup_(unlink_and_freep) char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL;
847 const char *passwd_path, *shadow_path, *group_path, *gshadow_path;
848 int r;
849
850 passwd_path = prefix_roota(arg_root, "/etc/passwd");
851 shadow_path = prefix_roota(arg_root, "/etc/shadow");
852 group_path = prefix_roota(arg_root, "/etc/group");
853 gshadow_path = prefix_roota(arg_root, "/etc/gshadow");
854
855 r = write_temporary_group(group_path, &group, &group_tmp);
856 if (r < 0)
857 return r;
858
859 r = write_temporary_gshadow(gshadow_path, &gshadow, &gshadow_tmp);
860 if (r < 0)
861 return r;
862
863 r = write_temporary_passwd(passwd_path, &passwd, &passwd_tmp);
864 if (r < 0)
865 return r;
866
867 r = write_temporary_shadow(shadow_path, &shadow, &shadow_tmp);
868 if (r < 0)
869 return r;
870
871 /* Make a backup of the old files */
872 if (group) {
873 r = make_backup("/etc/group", group_path);
874 if (r < 0)
875 return log_debug_errno(r, "Failed to make backup %s: %m", group_path);
876 }
877 if (gshadow) {
878 r = make_backup("/etc/gshadow", gshadow_path);
879 if (r < 0)
880 return log_debug_errno(r, "Failed to make backup %s: %m", gshadow_path);
881 }
882
883 if (passwd) {
884 r = make_backup("/etc/passwd", passwd_path);
885 if (r < 0)
886 return log_debug_errno(r, "Failed to make backup %s: %m", passwd_path);
887 }
888 if (shadow) {
889 r = make_backup("/etc/shadow", shadow_path);
890 if (r < 0)
891 return log_debug_errno(r, "Failed to make backup %s: %m", shadow_path);
892 }
893
894 /* And make the new files count */
895 if (group) {
896 r = rename_and_apply_smack_floor_label(group_tmp, group_path);
897 if (r < 0)
898 return log_debug_errno(r, "Failed to rename %s to %s: %m",
899 group_tmp, group_path);
900 group_tmp = mfree(group_tmp);
901
902 if (!arg_root && !arg_image)
903 (void) nscd_flush_cache(STRV_MAKE("group"));
904 }
905 if (gshadow) {
906 r = rename_and_apply_smack_floor_label(gshadow_tmp, gshadow_path);
907 if (r < 0)
908 return log_debug_errno(r, "Failed to rename %s to %s: %m",
909 gshadow_tmp, gshadow_path);
910
911 gshadow_tmp = mfree(gshadow_tmp);
912 }
913
914 if (passwd) {
915 r = rename_and_apply_smack_floor_label(passwd_tmp, passwd_path);
916 if (r < 0)
917 return log_debug_errno(r, "Failed to rename %s to %s: %m",
918 passwd_tmp, passwd_path);
919
920 passwd_tmp = mfree(passwd_tmp);
921
922 if (!arg_root && !arg_image)
923 (void) nscd_flush_cache(STRV_MAKE("passwd"));
924 }
925 if (shadow) {
926 r = rename_and_apply_smack_floor_label(shadow_tmp, shadow_path);
927 if (r < 0)
928 return log_debug_errno(r, "Failed to rename %s to %s: %m",
929 shadow_tmp, shadow_path);
930
931 shadow_tmp = mfree(shadow_tmp);
932 }
933
934 return 0;
935 }
936
uid_is_ok(uid_t uid,const char * name,bool check_with_gid)937 static int uid_is_ok(uid_t uid, const char *name, bool check_with_gid) {
938
939 /* Let's see if we already have assigned the UID a second time */
940 if (ordered_hashmap_get(todo_uids, UID_TO_PTR(uid)))
941 return 0;
942
943 /* Try to avoid using uids that are already used by a group
944 * that doesn't have the same name as our new user. */
945 if (check_with_gid) {
946 Item *i;
947
948 i = ordered_hashmap_get(todo_gids, GID_TO_PTR(uid));
949 if (i && !streq(i->name, name))
950 return 0;
951 }
952
953 /* Let's check the files directly */
954 if (hashmap_contains(database_by_uid, UID_TO_PTR(uid)))
955 return 0;
956
957 if (check_with_gid) {
958 const char *n;
959
960 n = hashmap_get(database_by_gid, GID_TO_PTR(uid));
961 if (n && !streq(n, name))
962 return 0;
963 }
964
965 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
966 if (!arg_root) {
967 struct passwd *p;
968 struct group *g;
969
970 errno = 0;
971 p = getpwuid(uid);
972 if (p)
973 return 0;
974 if (!IN_SET(errno, 0, ENOENT))
975 return -errno;
976
977 if (check_with_gid) {
978 errno = 0;
979 g = getgrgid((gid_t) uid);
980 if (g) {
981 if (!streq(g->gr_name, name))
982 return 0;
983 } else if (!IN_SET(errno, 0, ENOENT))
984 return -errno;
985 }
986 }
987
988 return 1;
989 }
990
root_stat(const char * p,struct stat * st)991 static int root_stat(const char *p, struct stat *st) {
992 const char *fix;
993
994 fix = prefix_roota(arg_root, p);
995 return RET_NERRNO(stat(fix, st));
996 }
997
read_id_from_file(Item * i,uid_t * _uid,gid_t * _gid)998 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
999 struct stat st;
1000 bool found_uid = false, found_gid = false;
1001 uid_t uid = 0;
1002 gid_t gid = 0;
1003
1004 assert(i);
1005
1006 /* First, try to get the GID directly */
1007 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
1008 gid = st.st_gid;
1009 found_gid = true;
1010 }
1011
1012 /* Then, try to get the UID directly */
1013 if ((_uid || (_gid && !found_gid))
1014 && i->uid_path
1015 && root_stat(i->uid_path, &st) >= 0) {
1016
1017 uid = st.st_uid;
1018 found_uid = true;
1019
1020 /* If we need the gid, but had no success yet, also derive it from the UID path */
1021 if (_gid && !found_gid) {
1022 gid = st.st_gid;
1023 found_gid = true;
1024 }
1025 }
1026
1027 /* If that didn't work yet, then let's reuse the GID as UID */
1028 if (_uid && !found_uid && i->gid_path) {
1029
1030 if (found_gid) {
1031 uid = (uid_t) gid;
1032 found_uid = true;
1033 } else if (root_stat(i->gid_path, &st) >= 0) {
1034 uid = (uid_t) st.st_gid;
1035 found_uid = true;
1036 }
1037 }
1038
1039 if (_uid) {
1040 if (!found_uid)
1041 return 0;
1042
1043 *_uid = uid;
1044 }
1045
1046 if (_gid) {
1047 if (!found_gid)
1048 return 0;
1049
1050 *_gid = gid;
1051 }
1052
1053 return 1;
1054 }
1055
add_user(Item * i)1056 static int add_user(Item *i) {
1057 void *z;
1058 int r;
1059
1060 assert(i);
1061
1062 /* Check the database directly */
1063 z = hashmap_get(database_by_username, i->name);
1064 if (z) {
1065 log_debug("User %s already exists.", i->name);
1066 i->uid = PTR_TO_UID(z);
1067 i->uid_set = true;
1068 return 0;
1069 }
1070
1071 if (!arg_root) {
1072 struct passwd *p;
1073
1074 /* Also check NSS */
1075 errno = 0;
1076 p = getpwnam(i->name);
1077 if (p) {
1078 log_debug("User %s already exists.", i->name);
1079 i->uid = p->pw_uid;
1080 i->uid_set = true;
1081
1082 r = free_and_strdup(&i->description, p->pw_gecos);
1083 if (r < 0)
1084 return log_oom();
1085
1086 return 0;
1087 }
1088 if (!errno_is_not_exists(errno))
1089 return log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name);
1090 }
1091
1092 /* Try to use the suggested numeric UID */
1093 if (i->uid_set) {
1094 r = uid_is_ok(i->uid, i->name, !i->id_set_strict);
1095 if (r < 0)
1096 return log_error_errno(r, "Failed to verify UID " UID_FMT ": %m", i->uid);
1097 if (r == 0) {
1098 log_info("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
1099 i->uid_set = false;
1100 }
1101 }
1102
1103 /* If that didn't work, try to read it from the specified path */
1104 if (!i->uid_set) {
1105 uid_t c;
1106
1107 if (read_id_from_file(i, &c, NULL) > 0) {
1108
1109 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
1110 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
1111 else {
1112 r = uid_is_ok(c, i->name, true);
1113 if (r < 0)
1114 return log_error_errno(r, "Failed to verify UID " UID_FMT ": %m", i->uid);
1115 else if (r > 0) {
1116 i->uid = c;
1117 i->uid_set = true;
1118 } else
1119 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
1120 }
1121 }
1122 }
1123
1124 /* Otherwise, try to reuse the group ID */
1125 if (!i->uid_set && i->gid_set) {
1126 r = uid_is_ok((uid_t) i->gid, i->name, true);
1127 if (r < 0)
1128 return log_error_errno(r, "Failed to verify UID " UID_FMT ": %m", i->uid);
1129 if (r > 0) {
1130 i->uid = (uid_t) i->gid;
1131 i->uid_set = true;
1132 }
1133 }
1134
1135 /* And if that didn't work either, let's try to find a free one */
1136 if (!i->uid_set) {
1137 maybe_emit_login_defs_warning();
1138
1139 for (;;) {
1140 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
1141 if (r < 0)
1142 return log_error_errno(r, "No free user ID available for %s.", i->name);
1143
1144 r = uid_is_ok(search_uid, i->name, true);
1145 if (r < 0)
1146 return log_error_errno(r, "Failed to verify UID " UID_FMT ": %m", i->uid);
1147 else if (r > 0)
1148 break;
1149 }
1150
1151 i->uid_set = true;
1152 i->uid = search_uid;
1153 }
1154
1155 r = ordered_hashmap_ensure_put(&todo_uids, NULL, UID_TO_PTR(i->uid), i);
1156 if (r == -EEXIST)
1157 return log_error_errno(r, "Requested user %s with UID " UID_FMT " and gid" GID_FMT " to be created is duplicated "
1158 "or conflicts with another user.", i->name, i->uid, i->gid);
1159 if (r == -ENOMEM)
1160 return log_oom();
1161 if (r < 0)
1162 return log_error_errno(r, "Failed to store user %s with UID " UID_FMT " and GID " GID_FMT " to be created: %m",
1163 i->name, i->uid, i->gid);
1164
1165 i->todo_user = true;
1166 log_info("Creating user '%s' (%s) with UID " UID_FMT " and GID " GID_FMT ".",
1167 i->name, strna(i->description), i->uid, i->gid);
1168
1169 return 0;
1170 }
1171
gid_is_ok(gid_t gid)1172 static int gid_is_ok(gid_t gid) {
1173 struct group *g;
1174 struct passwd *p;
1175
1176 if (ordered_hashmap_get(todo_gids, GID_TO_PTR(gid)))
1177 return 0;
1178
1179 /* Avoid reusing gids that are already used by a different user */
1180 if (ordered_hashmap_get(todo_uids, UID_TO_PTR(gid)))
1181 return 0;
1182
1183 if (hashmap_contains(database_by_gid, GID_TO_PTR(gid)))
1184 return 0;
1185
1186 if (hashmap_contains(database_by_uid, UID_TO_PTR(gid)))
1187 return 0;
1188
1189 if (!arg_root) {
1190 errno = 0;
1191 g = getgrgid(gid);
1192 if (g)
1193 return 0;
1194 if (!IN_SET(errno, 0, ENOENT))
1195 return -errno;
1196
1197 errno = 0;
1198 p = getpwuid((uid_t) gid);
1199 if (p)
1200 return 0;
1201 if (!IN_SET(errno, 0, ENOENT))
1202 return -errno;
1203 }
1204
1205 return 1;
1206 }
1207
get_gid_by_name(const char * name,gid_t * gid)1208 static int get_gid_by_name(const char *name, gid_t *gid) {
1209 void *z;
1210
1211 assert(gid);
1212
1213 /* Check the database directly */
1214 z = hashmap_get(database_by_groupname, name);
1215 if (z) {
1216 *gid = PTR_TO_GID(z);
1217 return 0;
1218 }
1219
1220 /* Also check NSS */
1221 if (!arg_root) {
1222 struct group *g;
1223
1224 errno = 0;
1225 g = getgrnam(name);
1226 if (g) {
1227 *gid = g->gr_gid;
1228 return 0;
1229 }
1230 if (!errno_is_not_exists(errno))
1231 return log_error_errno(errno, "Failed to check if group %s already exists: %m", name);
1232 }
1233
1234 return -ENOENT;
1235 }
1236
add_group(Item * i)1237 static int add_group(Item *i) {
1238 int r;
1239
1240 assert(i);
1241
1242 r = get_gid_by_name(i->name, &i->gid);
1243 if (r != -ENOENT) {
1244 if (r < 0)
1245 return r;
1246 log_debug("Group %s already exists.", i->name);
1247 i->gid_set = true;
1248 return 0;
1249 }
1250
1251 /* Try to use the suggested numeric GID */
1252 if (i->gid_set) {
1253 r = gid_is_ok(i->gid);
1254 if (r < 0)
1255 return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
1256 if (i->id_set_strict) {
1257 /* If we require the GID to already exist we can return here:
1258 * r > 0: means the GID does not exist -> fail
1259 * r == 0: means the GID exists -> nothing more to do.
1260 */
1261 if (r > 0)
1262 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1263 "Failed to create %s: please create GID %d",
1264 i->name, i->gid);
1265 if (r == 0)
1266 return 0;
1267 }
1268 if (r == 0) {
1269 log_info("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
1270 i->gid_set = false;
1271 }
1272 }
1273
1274 /* Try to reuse the numeric uid, if there's one */
1275 if (!i->gid_set && i->uid_set) {
1276 r = gid_is_ok((gid_t) i->uid);
1277 if (r < 0)
1278 return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
1279 if (r > 0) {
1280 i->gid = (gid_t) i->uid;
1281 i->gid_set = true;
1282 }
1283 }
1284
1285 /* If that didn't work, try to read it from the specified path */
1286 if (!i->gid_set) {
1287 gid_t c;
1288
1289 if (read_id_from_file(i, NULL, &c) > 0) {
1290
1291 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
1292 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
1293 else {
1294 r = gid_is_ok(c);
1295 if (r < 0)
1296 return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
1297 else if (r > 0) {
1298 i->gid = c;
1299 i->gid_set = true;
1300 } else
1301 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
1302 }
1303 }
1304 }
1305
1306 /* And if that didn't work either, let's try to find a free one */
1307 if (!i->gid_set) {
1308 maybe_emit_login_defs_warning();
1309
1310 for (;;) {
1311 /* We look for new GIDs in the UID pool! */
1312 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
1313 if (r < 0)
1314 return log_error_errno(r, "No free group ID available for %s.", i->name);
1315
1316 r = gid_is_ok(search_uid);
1317 if (r < 0)
1318 return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
1319 else if (r > 0)
1320 break;
1321 }
1322
1323 i->gid_set = true;
1324 i->gid = search_uid;
1325 }
1326
1327 r = ordered_hashmap_ensure_put(&todo_gids, NULL, GID_TO_PTR(i->gid), i);
1328 if (r == -EEXIST)
1329 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);
1330 if (r == -ENOMEM)
1331 return log_oom();
1332 if (r < 0)
1333 return log_error_errno(r, "Failed to store group %s with GID " GID_FMT " to be created: %m", i->name, i->gid);
1334
1335 i->todo_group = true;
1336 log_info("Creating group '%s' with GID " GID_FMT ".", i->name, i->gid);
1337
1338 return 0;
1339 }
1340
process_item(Item * i)1341 static int process_item(Item *i) {
1342 int r;
1343
1344 assert(i);
1345
1346 switch (i->type) {
1347
1348 case ADD_USER: {
1349 Item *j;
1350
1351 j = ordered_hashmap_get(groups, i->group_name ?: i->name);
1352 if (j && j->todo_group) {
1353 /* When a group with the target name is already in queue,
1354 * use the information about the group and do not create
1355 * duplicated group entry. */
1356 i->gid_set = j->gid_set;
1357 i->gid = j->gid;
1358 i->id_set_strict = true;
1359 } else if (i->group_name) {
1360 /* When a group name was given instead of a GID and it's
1361 * not in queue, then it must already exist. */
1362 r = get_gid_by_name(i->group_name, &i->gid);
1363 if (r < 0)
1364 return log_error_errno(r, "Group %s not found.", i->group_name);
1365 i->gid_set = true;
1366 i->id_set_strict = true;
1367 } else {
1368 r = add_group(i);
1369 if (r < 0)
1370 return r;
1371 }
1372
1373 return add_user(i);
1374 }
1375
1376 case ADD_GROUP:
1377 return add_group(i);
1378
1379 default:
1380 assert_not_reached();
1381 }
1382 }
1383
item_free(Item * i)1384 static Item* item_free(Item *i) {
1385 if (!i)
1386 return NULL;
1387
1388 free(i->name);
1389 free(i->group_name);
1390 free(i->uid_path);
1391 free(i->gid_path);
1392 free(i->description);
1393 free(i->home);
1394 free(i->shell);
1395 return mfree(i);
1396 }
1397
1398 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
1399 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(item_hash_ops, char, string_hash_func, string_compare_func, Item, item_free);
1400
add_implicit(void)1401 static int add_implicit(void) {
1402 char *g, **l;
1403 int r;
1404
1405 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1406 ORDERED_HASHMAP_FOREACH_KEY(l, g, members) {
1407 STRV_FOREACH(m, l)
1408 if (!ordered_hashmap_get(users, *m)) {
1409 _cleanup_(item_freep) Item *j = NULL;
1410
1411 j = new0(Item, 1);
1412 if (!j)
1413 return log_oom();
1414
1415 j->type = ADD_USER;
1416 j->name = strdup(*m);
1417 if (!j->name)
1418 return log_oom();
1419
1420 r = ordered_hashmap_ensure_put(&users, &item_hash_ops, j->name, j);
1421 if (r == -ENOMEM)
1422 return log_oom();
1423 if (r < 0)
1424 return log_error_errno(r, "Failed to add implicit user '%s': %m", j->name);
1425
1426 log_debug("Adding implicit user '%s' due to m line", j->name);
1427 TAKE_PTR(j);
1428 }
1429
1430 if (!(ordered_hashmap_get(users, g) ||
1431 ordered_hashmap_get(groups, g))) {
1432 _cleanup_(item_freep) Item *j = NULL;
1433
1434 j = new0(Item, 1);
1435 if (!j)
1436 return log_oom();
1437
1438 j->type = ADD_GROUP;
1439 j->name = strdup(g);
1440 if (!j->name)
1441 return log_oom();
1442
1443 r = ordered_hashmap_ensure_put(&groups, &item_hash_ops, j->name, j);
1444 if (r == -ENOMEM)
1445 return log_oom();
1446 if (r < 0)
1447 return log_error_errno(r, "Failed to add implicit group '%s': %m", j->name);
1448
1449 log_debug("Adding implicit group '%s' due to m line", j->name);
1450 TAKE_PTR(j);
1451 }
1452 }
1453
1454 return 0;
1455 }
1456
item_equal(Item * a,Item * b)1457 static bool item_equal(Item *a, Item *b) {
1458 assert(a);
1459 assert(b);
1460
1461 if (a->type != b->type)
1462 return false;
1463
1464 if (!streq_ptr(a->name, b->name))
1465 return false;
1466
1467 if (!streq_ptr(a->uid_path, b->uid_path))
1468 return false;
1469
1470 if (!streq_ptr(a->gid_path, b->gid_path))
1471 return false;
1472
1473 if (!streq_ptr(a->description, b->description))
1474 return false;
1475
1476 if (a->uid_set != b->uid_set)
1477 return false;
1478
1479 if (a->uid_set && a->uid != b->uid)
1480 return false;
1481
1482 if (a->gid_set != b->gid_set)
1483 return false;
1484
1485 if (a->gid_set && a->gid != b->gid)
1486 return false;
1487
1488 if (!streq_ptr(a->home, b->home))
1489 return false;
1490
1491 if (!streq_ptr(a->shell, b->shell))
1492 return false;
1493
1494 return true;
1495 }
1496
parse_line(const char * fname,unsigned line,const char * buffer)1497 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1498 _cleanup_free_ char *action = NULL,
1499 *name = NULL, *resolved_name = NULL,
1500 *id = NULL, *resolved_id = NULL,
1501 *description = NULL, *resolved_description = NULL,
1502 *home = NULL, *resolved_home = NULL,
1503 *shell = NULL, *resolved_shell = NULL;
1504 _cleanup_(item_freep) Item *i = NULL;
1505 Item *existing;
1506 OrderedHashmap *h;
1507 int r;
1508 const char *p;
1509
1510 assert(fname);
1511 assert(line >= 1);
1512 assert(buffer);
1513
1514 /* Parse columns */
1515 p = buffer;
1516 r = extract_many_words(&p, NULL, EXTRACT_UNQUOTE,
1517 &action, &name, &id, &description, &home, &shell, NULL);
1518 if (r < 0)
1519 return log_error_errno(r, "[%s:%u] Syntax error.", fname, line);
1520 if (r < 2)
1521 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1522 "[%s:%u] Missing action and name columns.", fname, line);
1523 if (!isempty(p))
1524 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1525 "[%s:%u] Trailing garbage.", fname, line);
1526
1527 /* Verify action */
1528 if (strlen(action) != 1)
1529 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1530 "[%s:%u] Unknown modifier '%s'", fname, line, action);
1531
1532 if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE))
1533 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
1534 "[%s:%u] Unknown command type '%c'.", fname, line, action[0]);
1535
1536 /* Verify name */
1537 if (empty_or_dash(name))
1538 name = mfree(name);
1539
1540 if (name) {
1541 r = specifier_printf(name, NAME_MAX, system_and_tmp_specifier_table, arg_root, NULL, &resolved_name);
1542 if (r < 0)
1543 return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m", fname, line, name);
1544
1545 if (!valid_user_group_name(resolved_name, 0))
1546 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1547 "[%s:%u] '%s' is not a valid user or group name.",
1548 fname, line, resolved_name);
1549 }
1550
1551 /* Verify id */
1552 if (empty_or_dash(id))
1553 id = mfree(id);
1554
1555 if (id) {
1556 r = specifier_printf(id, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_id);
1557 if (r < 0)
1558 return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
1559 fname, line, name);
1560 }
1561
1562 /* Verify description */
1563 if (empty_or_dash(description))
1564 description = mfree(description);
1565
1566 if (description) {
1567 r = specifier_printf(description, LONG_LINE_MAX, system_and_tmp_specifier_table, arg_root, NULL, &resolved_description);
1568 if (r < 0)
1569 return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
1570 fname, line, description);
1571
1572 if (!valid_gecos(resolved_description))
1573 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1574 "[%s:%u] '%s' is not a valid GECOS field.",
1575 fname, line, resolved_description);
1576 }
1577
1578 /* Verify home */
1579 if (empty_or_dash(home))
1580 home = mfree(home);
1581
1582 if (home) {
1583 r = specifier_printf(home, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_home);
1584 if (r < 0)
1585 return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
1586 fname, line, home);
1587
1588 if (!valid_home(resolved_home))
1589 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1590 "[%s:%u] '%s' is not a valid home directory field.",
1591 fname, line, resolved_home);
1592 }
1593
1594 /* Verify shell */
1595 if (empty_or_dash(shell))
1596 shell = mfree(shell);
1597
1598 if (shell) {
1599 r = specifier_printf(shell, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_shell);
1600 if (r < 0)
1601 return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
1602 fname, line, shell);
1603
1604 if (!valid_shell(resolved_shell))
1605 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1606 "[%s:%u] '%s' is not a valid login shell field.",
1607 fname, line, resolved_shell);
1608 }
1609
1610 switch (action[0]) {
1611
1612 case ADD_RANGE:
1613 if (resolved_name)
1614 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1615 "[%s:%u] Lines of type 'r' don't take a name field.",
1616 fname, line);
1617
1618 if (!resolved_id)
1619 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1620 "[%s:%u] Lines of type 'r' require an ID range in the third field.",
1621 fname, line);
1622
1623 if (description || home || shell)
1624 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1625 "[%s:%u] Lines of type '%c' don't take a %s field.",
1626 fname, line, action[0],
1627 description ? "GECOS" : home ? "home directory" : "login shell");
1628
1629 r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id);
1630 if (r < 0)
1631 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1632 "[%s:%u] Invalid UID range %s.", fname, line, resolved_id);
1633
1634 return 0;
1635
1636 case ADD_MEMBER: {
1637 /* Try to extend an existing member or group item */
1638 if (!name)
1639 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1640 "[%s:%u] Lines of type 'm' require a user name in the second field.",
1641 fname, line);
1642
1643 if (!resolved_id)
1644 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1645 "[%s:%u] Lines of type 'm' require a group name in the third field.",
1646 fname, line);
1647
1648 if (!valid_user_group_name(resolved_id, 0))
1649 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1650 "[%s:%u] '%s' is not a valid user or group name.",
1651 fname, line, resolved_id);
1652
1653 if (description || home || shell)
1654 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1655 "[%s:%u] Lines of type '%c' don't take a %s field.",
1656 fname, line, action[0],
1657 description ? "GECOS" : home ? "home directory" : "login shell");
1658
1659 r = string_strv_ordered_hashmap_put(&members, resolved_id, resolved_name);
1660 if (r < 0)
1661 return log_error_errno(r, "Failed to store mapping for %s: %m", resolved_id);
1662
1663 return 0;
1664 }
1665
1666 case ADD_USER:
1667 if (!name)
1668 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1669 "[%s:%u] Lines of type 'u' require a user name in the second field.",
1670 fname, line);
1671
1672 r = ordered_hashmap_ensure_allocated(&users, &item_hash_ops);
1673 if (r < 0)
1674 return log_oom();
1675
1676 i = new0(Item, 1);
1677 if (!i)
1678 return log_oom();
1679
1680 if (resolved_id) {
1681 if (path_is_absolute(resolved_id)) {
1682 i->uid_path = TAKE_PTR(resolved_id);
1683 path_simplify(i->uid_path);
1684 } else {
1685 _cleanup_free_ char *uid = NULL, *gid = NULL;
1686 if (split_pair(resolved_id, ":", &uid, &gid) == 0) {
1687 r = parse_gid(gid, &i->gid);
1688 if (r < 0) {
1689 if (valid_user_group_name(gid, 0))
1690 i->group_name = TAKE_PTR(gid);
1691 else
1692 return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
1693 } else {
1694 i->gid_set = true;
1695 i->id_set_strict = true;
1696 }
1697 free_and_replace(resolved_id, uid);
1698 }
1699 if (!streq(resolved_id, "-")) {
1700 r = parse_uid(resolved_id, &i->uid);
1701 if (r < 0)
1702 return log_error_errno(r, "Failed to parse UID: '%s': %m", id);
1703 i->uid_set = true;
1704 }
1705 }
1706 }
1707
1708 i->description = TAKE_PTR(resolved_description);
1709 i->home = TAKE_PTR(resolved_home);
1710 i->shell = TAKE_PTR(resolved_shell);
1711
1712 h = users;
1713 break;
1714
1715 case ADD_GROUP:
1716 if (!name)
1717 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1718 "[%s:%u] Lines of type 'g' require a user name in the second field.",
1719 fname, line);
1720
1721 if (description || home || shell)
1722 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1723 "[%s:%u] Lines of type '%c' don't take a %s field.",
1724 fname, line, action[0],
1725 description ? "GECOS" : home ? "home directory" : "login shell");
1726
1727 r = ordered_hashmap_ensure_allocated(&groups, &item_hash_ops);
1728 if (r < 0)
1729 return log_oom();
1730
1731 i = new0(Item, 1);
1732 if (!i)
1733 return log_oom();
1734
1735 if (resolved_id) {
1736 if (path_is_absolute(resolved_id)) {
1737 i->gid_path = TAKE_PTR(resolved_id);
1738 path_simplify(i->gid_path);
1739 } else {
1740 r = parse_gid(resolved_id, &i->gid);
1741 if (r < 0)
1742 return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
1743
1744 i->gid_set = true;
1745 }
1746 }
1747
1748 h = groups;
1749 break;
1750
1751 default:
1752 return -EBADMSG;
1753 }
1754
1755 i->type = action[0];
1756 i->name = TAKE_PTR(resolved_name);
1757
1758 existing = ordered_hashmap_get(h, i->name);
1759 if (existing) {
1760 /* Two identical items are fine */
1761 if (!item_equal(existing, i))
1762 log_warning("%s:%u: conflict with earlier configuration for %s '%s', ignoring line.",
1763 fname, line,
1764 item_type_to_string(i->type), i->name);
1765
1766 return 0;
1767 }
1768
1769 r = ordered_hashmap_put(h, i->name, i);
1770 if (r < 0)
1771 return log_oom();
1772
1773 i = NULL;
1774 return 0;
1775 }
1776
read_config_file(const char * fn,bool ignore_enoent)1777 static int read_config_file(const char *fn, bool ignore_enoent) {
1778 _cleanup_fclose_ FILE *rf = NULL;
1779 _cleanup_free_ char *pp = NULL;
1780 FILE *f = NULL;
1781 unsigned v = 0;
1782 int r = 0;
1783
1784 assert(fn);
1785
1786 if (streq(fn, "-"))
1787 f = stdin;
1788 else {
1789 r = search_and_fopen(fn, "re", arg_root, (const char**) CONF_PATHS_STRV("sysusers.d"), &rf, &pp);
1790 if (r < 0) {
1791 if (ignore_enoent && r == -ENOENT)
1792 return 0;
1793
1794 return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
1795 }
1796
1797 f = rf;
1798 fn = pp;
1799 }
1800
1801 for (;;) {
1802 _cleanup_free_ char *line = NULL;
1803 char *l;
1804 int k;
1805
1806 k = read_line(f, LONG_LINE_MAX, &line);
1807 if (k < 0)
1808 return log_error_errno(k, "Failed to read '%s': %m", fn);
1809 if (k == 0)
1810 break;
1811
1812 v++;
1813
1814 l = strstrip(line);
1815 if (IN_SET(*l, 0, '#'))
1816 continue;
1817
1818 k = parse_line(fn, v, l);
1819 if (k < 0 && r == 0)
1820 r = k;
1821 }
1822
1823 if (ferror(f)) {
1824 log_error_errno(errno, "Failed to read from file %s: %m", fn);
1825 if (r == 0)
1826 r = -EIO;
1827 }
1828
1829 return r;
1830 }
1831
cat_config(void)1832 static int cat_config(void) {
1833 _cleanup_strv_free_ char **files = NULL;
1834 int r;
1835
1836 r = conf_files_list_with_replacement(arg_root, CONF_PATHS_STRV("sysusers.d"), arg_replace, &files, NULL);
1837 if (r < 0)
1838 return r;
1839
1840 pager_open(arg_pager_flags);
1841
1842 return cat_files(NULL, files, 0);
1843 }
1844
help(void)1845 static int help(void) {
1846 _cleanup_free_ char *link = NULL;
1847 int r;
1848
1849 r = terminal_urlify_man("systemd-sysusers.service", "8", &link);
1850 if (r < 0)
1851 return log_oom();
1852
1853 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1854 "Creates system user accounts.\n\n"
1855 " -h --help Show this help\n"
1856 " --version Show package version\n"
1857 " --cat-config Show configuration files\n"
1858 " --root=PATH Operate on an alternate filesystem root\n"
1859 " --image=PATH Operate on disk image as filesystem root\n"
1860 " --replace=PATH Treat arguments as replacement for PATH\n"
1861 " --dry-run Just print what would be done\n"
1862 " --inline Treat arguments as configuration lines\n"
1863 " --no-pager Do not pipe output into a pager\n"
1864 "\nSee the %s for details.\n",
1865 program_invocation_short_name,
1866 link);
1867
1868 return 0;
1869 }
1870
parse_argv(int argc,char * argv[])1871 static int parse_argv(int argc, char *argv[]) {
1872
1873 enum {
1874 ARG_VERSION = 0x100,
1875 ARG_CAT_CONFIG,
1876 ARG_ROOT,
1877 ARG_IMAGE,
1878 ARG_REPLACE,
1879 ARG_DRY_RUN,
1880 ARG_INLINE,
1881 ARG_NO_PAGER,
1882 };
1883
1884 static const struct option options[] = {
1885 { "help", no_argument, NULL, 'h' },
1886 { "version", no_argument, NULL, ARG_VERSION },
1887 { "cat-config", no_argument, NULL, ARG_CAT_CONFIG },
1888 { "root", required_argument, NULL, ARG_ROOT },
1889 { "image", required_argument, NULL, ARG_IMAGE },
1890 { "replace", required_argument, NULL, ARG_REPLACE },
1891 { "dry-run", no_argument, NULL, ARG_DRY_RUN },
1892 { "inline", no_argument, NULL, ARG_INLINE },
1893 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1894 {}
1895 };
1896
1897 int c, r;
1898
1899 assert(argc >= 0);
1900 assert(argv);
1901
1902 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
1903
1904 switch (c) {
1905
1906 case 'h':
1907 return help();
1908
1909 case ARG_VERSION:
1910 return version();
1911
1912 case ARG_CAT_CONFIG:
1913 arg_cat_config = true;
1914 break;
1915
1916 case ARG_ROOT:
1917 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_root);
1918 if (r < 0)
1919 return r;
1920 break;
1921
1922 case ARG_IMAGE:
1923 #ifdef STANDALONE
1924 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1925 "This systemd-sysusers version is compiled without support for --image=.");
1926 #else
1927 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
1928 if (r < 0)
1929 return r;
1930 break;
1931 #endif
1932
1933 case ARG_REPLACE:
1934 if (!path_is_absolute(optarg) ||
1935 !endswith(optarg, ".conf"))
1936 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1937 "The argument to --replace= must an absolute path to a config file");
1938
1939 arg_replace = optarg;
1940 break;
1941
1942 case ARG_DRY_RUN:
1943 arg_dry_run = true;
1944 break;
1945
1946 case ARG_INLINE:
1947 arg_inline = true;
1948 break;
1949
1950 case ARG_NO_PAGER:
1951 arg_pager_flags |= PAGER_DISABLE;
1952 break;
1953
1954 case '?':
1955 return -EINVAL;
1956
1957 default:
1958 assert_not_reached();
1959 }
1960
1961 if (arg_replace && arg_cat_config)
1962 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1963 "Option --replace= is not supported with --cat-config");
1964
1965 if (arg_replace && optind >= argc)
1966 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1967 "When --replace= is given, some configuration items must be specified");
1968
1969 if (arg_image && arg_root)
1970 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
1971
1972 return 1;
1973 }
1974
parse_arguments(char ** args)1975 static int parse_arguments(char **args) {
1976 unsigned pos = 1;
1977 int r;
1978
1979 STRV_FOREACH(arg, args) {
1980 if (arg_inline)
1981 /* Use (argument):n, where n==1 for the first positional arg */
1982 r = parse_line("(argument)", pos, *arg);
1983 else
1984 r = read_config_file(*arg, false);
1985 if (r < 0)
1986 return r;
1987
1988 pos++;
1989 }
1990
1991 return 0;
1992 }
1993
read_config_files(char ** args)1994 static int read_config_files(char **args) {
1995 _cleanup_strv_free_ char **files = NULL;
1996 _cleanup_free_ char *p = NULL;
1997 int r;
1998
1999 r = conf_files_list_with_replacement(arg_root, CONF_PATHS_STRV("sysusers.d"), arg_replace, &files, &p);
2000 if (r < 0)
2001 return r;
2002
2003 STRV_FOREACH(f, files)
2004 if (p && path_equal(*f, p)) {
2005 log_debug("Parsing arguments at position \"%s\"…", *f);
2006
2007 r = parse_arguments(args);
2008 if (r < 0)
2009 return r;
2010 } else {
2011 log_debug("Reading config file \"%s\"…", *f);
2012
2013 /* Just warn, ignore result otherwise */
2014 (void) read_config_file(*f, true);
2015 }
2016
2017 return 0;
2018 }
2019
run(int argc,char * argv[])2020 static int run(int argc, char *argv[]) {
2021 #ifndef STANDALONE
2022 _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
2023 _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
2024 _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
2025 #endif
2026 _cleanup_close_ int lock = -1;
2027 Item *i;
2028 int r;
2029
2030 r = parse_argv(argc, argv);
2031 if (r <= 0)
2032 return r;
2033
2034 log_setup();
2035
2036 if (arg_cat_config)
2037 return cat_config();
2038
2039 umask(0022);
2040
2041 r = mac_selinux_init();
2042 if (r < 0)
2043 return r;
2044
2045 #ifndef STANDALONE
2046 if (arg_image) {
2047 assert(!arg_root);
2048
2049 r = mount_image_privately_interactively(
2050 arg_image,
2051 DISSECT_IMAGE_GENERIC_ROOT |
2052 DISSECT_IMAGE_REQUIRE_ROOT |
2053 DISSECT_IMAGE_VALIDATE_OS |
2054 DISSECT_IMAGE_RELAX_VAR_CHECK |
2055 DISSECT_IMAGE_FSCK |
2056 DISSECT_IMAGE_GROWFS,
2057 &unlink_dir,
2058 &loop_device,
2059 &decrypted_image);
2060 if (r < 0)
2061 return r;
2062
2063 arg_root = strdup(unlink_dir);
2064 if (!arg_root)
2065 return log_oom();
2066 }
2067 #else
2068 assert(!arg_image);
2069 #endif
2070
2071 /* If command line arguments are specified along with --replace, read all
2072 * configuration files and insert the positional arguments at the specified
2073 * place. Otherwise, if command line arguments are specified, execute just
2074 * them, and finally, without --replace= or any positional arguments, just
2075 * read configuration and execute it.
2076 */
2077 if (arg_replace || optind >= argc)
2078 r = read_config_files(argv + optind);
2079 else
2080 r = parse_arguments(argv + optind);
2081 if (r < 0)
2082 return r;
2083
2084 /* Let's tell nss-systemd not to synthesize the "root" and "nobody" entries for it, so that our detection
2085 * whether the names or UID/GID area already used otherwise doesn't get confused. After all, even though
2086 * nss-systemd synthesizes these users/groups, they should still appear in /etc/passwd and /etc/group, as the
2087 * synthesizing logic is merely supposed to be fallback for cases where we run with a completely unpopulated
2088 * /etc. */
2089 if (setenv("SYSTEMD_NSS_BYPASS_SYNTHETIC", "1", 1) < 0)
2090 return log_error_errno(errno, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m");
2091
2092 if (!uid_range) {
2093 /* Default to default range of SYSTEMD_UID_MIN..SYSTEM_UID_MAX. */
2094 r = read_login_defs(&login_defs, NULL, arg_root);
2095 if (r < 0)
2096 return log_error_errno(r, "Failed to read %s%s: %m",
2097 strempty(arg_root), "/etc/login.defs");
2098
2099 login_defs_need_warning = true;
2100
2101 /* We pick a range that very conservative: we look at compiled-in maximum and the value in
2102 * /etc/login.defs. That way the UIDs/GIDs which we allocate will be interpreted correctly,
2103 * even if /etc/login.defs is removed later. (The bottom bound doesn't matter much, since
2104 * it's only used during allocation, so we use the configured value directly). */
2105 uid_t begin = login_defs.system_alloc_uid_min,
2106 end = MIN3((uid_t) SYSTEM_UID_MAX, login_defs.system_uid_max, login_defs.system_gid_max);
2107 if (begin < end) {
2108 r = uid_range_add(&uid_range, &n_uid_range, begin, end - begin + 1);
2109 if (r < 0)
2110 return log_oom();
2111 }
2112 }
2113
2114 r = add_implicit();
2115 if (r < 0)
2116 return r;
2117
2118 if (!arg_dry_run) {
2119 lock = take_etc_passwd_lock(arg_root);
2120 if (lock < 0)
2121 return log_error_errno(lock, "Failed to take /etc/passwd lock: %m");
2122 }
2123
2124 r = load_user_database();
2125 if (r < 0)
2126 return log_error_errno(r, "Failed to load user database: %m");
2127
2128 r = load_group_database();
2129 if (r < 0)
2130 return log_error_errno(r, "Failed to read group database: %m");
2131
2132 ORDERED_HASHMAP_FOREACH(i, groups)
2133 (void) process_item(i);
2134
2135 ORDERED_HASHMAP_FOREACH(i, users)
2136 (void) process_item(i);
2137
2138 r = write_files();
2139 if (r < 0)
2140 return log_error_errno(r, "Failed to write files: %m");
2141
2142 return 0;
2143 }
2144
2145 DEFINE_MAIN_FUNCTION(run);
2146