]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/sysusers/sysusers.c
Merge pull request #7933 from keszybz/fuzz-regression
[thirdparty/systemd.git] / src / sysusers / sysusers.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2014 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <getopt.h>
22 #include <grp.h>
23 #include <gshadow.h>
24 #include <pwd.h>
25 #include <shadow.h>
26 #include <utmp.h>
27
28 #include "alloc-util.h"
29 #include "conf-files.h"
30 #include "copy.h"
31 #include "def.h"
32 #include "fd-util.h"
33 #include "fs-util.h"
34 #include "fileio-label.h"
35 #include "format-util.h"
36 #include "hashmap.h"
37 #include "path-util.h"
38 #include "selinux-util.h"
39 #include "smack-util.h"
40 #include "specifier.h"
41 #include "string-util.h"
42 #include "strv.h"
43 #include "uid-range.h"
44 #include "user-util.h"
45 #include "utf8.h"
46 #include "util.h"
47
48 typedef enum ItemType {
49 ADD_USER = 'u',
50 ADD_GROUP = 'g',
51 ADD_MEMBER = 'm',
52 ADD_RANGE = 'r',
53 } ItemType;
54 typedef struct Item {
55 ItemType type;
56
57 char *name;
58 char *uid_path;
59 char *gid_path;
60 char *description;
61 char *home;
62
63 gid_t gid;
64 uid_t uid;
65
66 bool gid_set:1;
67 bool gid_must_exist:1;
68 bool uid_set:1;
69
70 bool todo_user:1;
71 bool todo_group:1;
72 } Item;
73
74 static char *arg_root = NULL;
75
76 static const char conf_file_dirs[] = CONF_PATHS_NULSTR("sysusers.d");
77
78 static OrderedHashmap *users = NULL, *groups = NULL;
79 static OrderedHashmap *todo_uids = NULL, *todo_gids = NULL;
80 static OrderedHashmap *members = NULL;
81
82 static Hashmap *database_uid = NULL, *database_user = NULL;
83 static Hashmap *database_gid = NULL, *database_group = NULL;
84
85 static uid_t search_uid = UID_INVALID;
86 static UidRange *uid_range = NULL;
87 static unsigned n_uid_range = 0;
88
89 static int load_user_database(void) {
90 _cleanup_fclose_ FILE *f = NULL;
91 const char *passwd_path;
92 struct passwd *pw;
93 int r;
94
95 passwd_path = prefix_roota(arg_root, "/etc/passwd");
96 f = fopen(passwd_path, "re");
97 if (!f)
98 return errno == ENOENT ? 0 : -errno;
99
100 r = hashmap_ensure_allocated(&database_user, &string_hash_ops);
101 if (r < 0)
102 return r;
103
104 r = hashmap_ensure_allocated(&database_uid, NULL);
105 if (r < 0)
106 return r;
107
108 errno = 0;
109 while ((pw = fgetpwent(f))) {
110 char *n;
111 int k, q;
112
113 n = strdup(pw->pw_name);
114 if (!n)
115 return -ENOMEM;
116
117 k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
118 if (k < 0 && k != -EEXIST) {
119 free(n);
120 return k;
121 }
122
123 q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
124 if (q < 0 && q != -EEXIST) {
125 if (k < 0)
126 free(n);
127 return q;
128 }
129
130 if (q < 0 && k < 0)
131 free(n);
132
133 errno = 0;
134 }
135 if (!IN_SET(errno, 0, ENOENT))
136 return -errno;
137
138 return 0;
139 }
140
141 static int load_group_database(void) {
142 _cleanup_fclose_ FILE *f = NULL;
143 const char *group_path;
144 struct group *gr;
145 int r;
146
147 group_path = prefix_roota(arg_root, "/etc/group");
148 f = fopen(group_path, "re");
149 if (!f)
150 return errno == ENOENT ? 0 : -errno;
151
152 r = hashmap_ensure_allocated(&database_group, &string_hash_ops);
153 if (r < 0)
154 return r;
155
156 r = hashmap_ensure_allocated(&database_gid, NULL);
157 if (r < 0)
158 return r;
159
160 errno = 0;
161 while ((gr = fgetgrent(f))) {
162 char *n;
163 int k, q;
164
165 n = strdup(gr->gr_name);
166 if (!n)
167 return -ENOMEM;
168
169 k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
170 if (k < 0 && k != -EEXIST) {
171 free(n);
172 return k;
173 }
174
175 q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
176 if (q < 0 && q != -EEXIST) {
177 if (k < 0)
178 free(n);
179 return q;
180 }
181
182 if (q < 0 && k < 0)
183 free(n);
184
185 errno = 0;
186 }
187 if (!IN_SET(errno, 0, ENOENT))
188 return -errno;
189
190 return 0;
191 }
192
193 static int make_backup(const char *target, const char *x) {
194 _cleanup_close_ int src = -1;
195 _cleanup_fclose_ FILE *dst = NULL;
196 _cleanup_free_ char *temp = NULL;
197 char *backup;
198 struct timespec ts[2];
199 struct stat st;
200 int r;
201
202 src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
203 if (src < 0) {
204 if (errno == ENOENT) /* No backup necessary... */
205 return 0;
206
207 return -errno;
208 }
209
210 if (fstat(src, &st) < 0)
211 return -errno;
212
213 r = fopen_temporary_label(target, x, &dst, &temp);
214 if (r < 0)
215 return r;
216
217 r = copy_bytes(src, fileno(dst), (uint64_t) -1, COPY_REFLINK);
218 if (r < 0)
219 goto fail;
220
221 /* Don't fail on chmod() or chown(). If it stays owned by us
222 * and/or unreadable by others, then it isn't too bad... */
223
224 backup = strjoina(x, "-");
225
226 /* Copy over the access mask */
227 if (fchmod(fileno(dst), st.st_mode & 07777) < 0)
228 log_warning_errno(errno, "Failed to change mode on %s: %m", backup);
229
230 if (fchown(fileno(dst), st.st_uid, st.st_gid)< 0)
231 log_warning_errno(errno, "Failed to change ownership of %s: %m", backup);
232
233 ts[0] = st.st_atim;
234 ts[1] = st.st_mtim;
235 if (futimens(fileno(dst), ts) < 0)
236 log_warning_errno(errno, "Failed to fix access and modification time of %s: %m", backup);
237
238 r = fflush_sync_and_check(dst);
239 if (r < 0)
240 goto fail;
241
242 if (rename(temp, backup) < 0) {
243 r = -errno;
244 goto fail;
245 }
246
247 return 0;
248
249 fail:
250 unlink(temp);
251 return r;
252 }
253
254 static int putgrent_with_members(const struct group *gr, FILE *group) {
255 char **a;
256
257 assert(gr);
258 assert(group);
259
260 a = ordered_hashmap_get(members, gr->gr_name);
261 if (a) {
262 _cleanup_strv_free_ char **l = NULL;
263 bool added = false;
264 char **i;
265
266 l = strv_copy(gr->gr_mem);
267 if (!l)
268 return -ENOMEM;
269
270 STRV_FOREACH(i, a) {
271 if (strv_find(l, *i))
272 continue;
273
274 if (strv_extend(&l, *i) < 0)
275 return -ENOMEM;
276
277 added = true;
278 }
279
280 if (added) {
281 struct group t;
282
283 strv_uniq(l);
284 strv_sort(l);
285
286 t = *gr;
287 t.gr_mem = l;
288
289 errno = 0;
290 if (putgrent(&t, group) != 0)
291 return errno > 0 ? -errno : -EIO;
292
293 return 1;
294 }
295 }
296
297 errno = 0;
298 if (putgrent(gr, group) != 0)
299 return errno > 0 ? -errno : -EIO;
300
301 return 0;
302 }
303
304 #if ENABLE_GSHADOW
305 static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
306 char **a;
307
308 assert(sg);
309 assert(gshadow);
310
311 a = ordered_hashmap_get(members, sg->sg_namp);
312 if (a) {
313 _cleanup_strv_free_ char **l = NULL;
314 bool added = false;
315 char **i;
316
317 l = strv_copy(sg->sg_mem);
318 if (!l)
319 return -ENOMEM;
320
321 STRV_FOREACH(i, a) {
322 if (strv_find(l, *i))
323 continue;
324
325 if (strv_extend(&l, *i) < 0)
326 return -ENOMEM;
327
328 added = true;
329 }
330
331 if (added) {
332 struct sgrp t;
333
334 strv_uniq(l);
335 strv_sort(l);
336
337 t = *sg;
338 t.sg_mem = l;
339
340 errno = 0;
341 if (putsgent(&t, gshadow) != 0)
342 return errno > 0 ? -errno : -EIO;
343
344 return 1;
345 }
346 }
347
348 errno = 0;
349 if (putsgent(sg, gshadow) != 0)
350 return errno > 0 ? -errno : -EIO;
351
352 return 0;
353 }
354 #endif
355
356 static int sync_rights(FILE *from, FILE *to) {
357 struct stat st;
358
359 if (fstat(fileno(from), &st) < 0)
360 return -errno;
361
362 if (fchmod(fileno(to), st.st_mode & 07777) < 0)
363 return -errno;
364
365 if (fchown(fileno(to), st.st_uid, st.st_gid) < 0)
366 return -errno;
367
368 return 0;
369 }
370
371 static int rename_and_apply_smack(const char *temp_path, const char *dest_path) {
372 int r = 0;
373 if (rename(temp_path, dest_path) < 0)
374 return -errno;
375
376 #ifdef SMACK_RUN_LABEL
377 r = mac_smack_apply(dest_path, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL);
378 if (r < 0)
379 return r;
380 #endif
381 return r;
382 }
383
384 static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char **tmpfile_path) {
385 _cleanup_fclose_ FILE *original = NULL, *passwd = NULL;
386 _cleanup_(unlink_and_freep) char *passwd_tmp = NULL;
387 Iterator iterator;
388 Item *i;
389 int r;
390
391 if (ordered_hashmap_size(todo_uids) == 0)
392 return 0;
393
394 r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
395 if (r < 0)
396 return r;
397
398 original = fopen(passwd_path, "re");
399 if (original) {
400 struct passwd *pw;
401
402 r = sync_rights(original, passwd);
403 if (r < 0)
404 return r;
405
406 errno = 0;
407 while ((pw = fgetpwent(original))) {
408
409 i = ordered_hashmap_get(users, pw->pw_name);
410 if (i && i->todo_user) {
411 log_error("%s: User \"%s\" already exists.", passwd_path, pw->pw_name);
412 return -EEXIST;
413 }
414
415 if (ordered_hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
416 log_error("%s: Detected collision for UID " UID_FMT ".", passwd_path, pw->pw_uid);
417 return -EEXIST;
418 }
419
420 errno = 0;
421 if (putpwent(pw, passwd) < 0)
422 return errno ? -errno : -EIO;
423
424 errno = 0;
425 }
426 if (!IN_SET(errno, 0, ENOENT))
427 return -errno;
428
429 } else {
430 if (errno != ENOENT)
431 return -errno;
432 if (fchmod(fileno(passwd), 0644) < 0)
433 return -errno;
434 }
435
436 ORDERED_HASHMAP_FOREACH(i, todo_uids, iterator) {
437 struct passwd n = {
438 .pw_name = i->name,
439 .pw_uid = i->uid,
440 .pw_gid = i->gid,
441 .pw_gecos = i->description,
442
443 /* "x" means the password is stored in the shadow file */
444 .pw_passwd = (char*) "x",
445
446 /* We default to the root directory as home */
447 .pw_dir = i->home ? i->home : (char*) "/",
448
449 /* Initialize the shell to nologin, with one exception:
450 * for root we patch in something special */
451 .pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
452 };
453
454 errno = 0;
455 if (putpwent(&n, passwd) != 0)
456 return errno ? -errno : -EIO;
457 }
458
459 r = fflush_and_check(passwd);
460 if (r < 0)
461 return r;
462
463 *tmpfile = passwd;
464 *tmpfile_path = passwd_tmp;
465 passwd = NULL;
466 passwd_tmp = NULL;
467 return 0;
468 }
469
470 static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char **tmpfile_path) {
471 _cleanup_fclose_ FILE *original = NULL, *shadow = NULL;
472 _cleanup_(unlink_and_freep) char *shadow_tmp = NULL;
473 Iterator iterator;
474 long lstchg;
475 Item *i;
476 int r;
477
478 if (ordered_hashmap_size(todo_uids) == 0)
479 return 0;
480
481 r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
482 if (r < 0)
483 return r;
484
485 lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
486
487 original = fopen(shadow_path, "re");
488 if (original) {
489 struct spwd *sp;
490
491 r = sync_rights(original, shadow);
492 if (r < 0)
493 return r;
494
495 errno = 0;
496 while ((sp = fgetspent(original))) {
497
498 i = ordered_hashmap_get(users, sp->sp_namp);
499 if (i && i->todo_user) {
500 /* we will update the existing entry */
501 sp->sp_lstchg = lstchg;
502
503 /* only the /etc/shadow stage is left, so we can
504 * safely remove the item from the todo set */
505 i->todo_user = false;
506 ordered_hashmap_remove(todo_uids, UID_TO_PTR(i->uid));
507 }
508
509 errno = 0;
510 if (putspent(sp, shadow) < 0)
511 return errno ? -errno : -EIO;
512
513 errno = 0;
514 }
515 if (!IN_SET(errno, 0, ENOENT))
516 return -errno;
517
518 } else {
519 if (errno != ENOENT)
520 return -errno;
521 if (fchmod(fileno(shadow), 0000) < 0)
522 return -errno;
523 }
524
525 ORDERED_HASHMAP_FOREACH(i, todo_uids, iterator) {
526 struct spwd n = {
527 .sp_namp = i->name,
528 .sp_pwdp = (char*) "!!",
529 .sp_lstchg = lstchg,
530 .sp_min = -1,
531 .sp_max = -1,
532 .sp_warn = -1,
533 .sp_inact = -1,
534 .sp_expire = -1,
535 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
536 };
537
538 errno = 0;
539 if (putspent(&n, shadow) != 0)
540 return errno ? -errno : -EIO;
541 }
542
543 r = fflush_sync_and_check(shadow);
544 if (r < 0)
545 return r;
546
547 *tmpfile = shadow;
548 *tmpfile_path = shadow_tmp;
549 shadow = NULL;
550 shadow_tmp = NULL;
551 return 0;
552 }
553
554 static int write_temporary_group(const char *group_path, FILE **tmpfile, char **tmpfile_path) {
555 _cleanup_fclose_ FILE *original = NULL, *group = NULL;
556 _cleanup_(unlink_and_freep) char *group_tmp = NULL;
557 bool group_changed = false;
558 Iterator iterator;
559 Item *i;
560 int r;
561
562 if (ordered_hashmap_size(todo_gids) == 0 && ordered_hashmap_size(members) == 0)
563 return 0;
564
565 r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
566 if (r < 0)
567 return r;
568
569 original = fopen(group_path, "re");
570 if (original) {
571 struct group *gr;
572
573 r = sync_rights(original, group);
574 if (r < 0)
575 return r;
576
577 errno = 0;
578 while ((gr = fgetgrent(original))) {
579 /* Safety checks against name and GID collisions. Normally,
580 * this should be unnecessary, but given that we look at the
581 * entries anyway here, let's make an extra verification
582 * step that we don't generate duplicate entries. */
583
584 i = ordered_hashmap_get(groups, gr->gr_name);
585 if (i && i->todo_group) {
586 log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name);
587 return -EEXIST;
588 }
589
590 if (ordered_hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
591 log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid);
592 return -EEXIST;
593 }
594
595 r = putgrent_with_members(gr, group);
596 if (r < 0)
597 return r;
598 if (r > 0)
599 group_changed = true;
600
601 errno = 0;
602 }
603 if (!IN_SET(errno, 0, ENOENT))
604 return -errno;
605
606 } else {
607 if (errno != ENOENT)
608 return -errno;
609 if (fchmod(fileno(group), 0644) < 0)
610 return -errno;
611 }
612
613 ORDERED_HASHMAP_FOREACH(i, todo_gids, iterator) {
614 struct group n = {
615 .gr_name = i->name,
616 .gr_gid = i->gid,
617 .gr_passwd = (char*) "x",
618 };
619
620 r = putgrent_with_members(&n, group);
621 if (r < 0)
622 return r;
623
624 group_changed = true;
625 }
626
627 r = fflush_sync_and_check(group);
628 if (r < 0)
629 return r;
630
631 if (group_changed) {
632 *tmpfile = group;
633 *tmpfile_path = group_tmp;
634 group = NULL;
635 group_tmp = NULL;
636 }
637 return 0;
638 }
639
640 static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, char **tmpfile_path) {
641 #if ENABLE_GSHADOW
642 _cleanup_fclose_ FILE *original = NULL, *gshadow = NULL;
643 _cleanup_(unlink_and_freep) char *gshadow_tmp = NULL;
644 bool group_changed = false;
645 Iterator iterator;
646 Item *i;
647 int r;
648
649 if (ordered_hashmap_size(todo_gids) == 0 && ordered_hashmap_size(members) == 0)
650 return 0;
651
652 r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
653 if (r < 0)
654 return r;
655
656 original = fopen(gshadow_path, "re");
657 if (original) {
658 struct sgrp *sg;
659
660 r = sync_rights(original, gshadow);
661 if (r < 0)
662 return r;
663
664 errno = 0;
665 while ((sg = fgetsgent(original))) {
666
667 i = ordered_hashmap_get(groups, sg->sg_namp);
668 if (i && i->todo_group) {
669 log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp);
670 return -EEXIST;
671 }
672
673 r = putsgent_with_members(sg, gshadow);
674 if (r < 0)
675 return r;
676 if (r > 0)
677 group_changed = true;
678
679 errno = 0;
680 }
681 if (!IN_SET(errno, 0, ENOENT))
682 return -errno;
683
684 } else {
685 if (errno != ENOENT)
686 return -errno;
687 if (fchmod(fileno(gshadow), 0000) < 0)
688 return -errno;
689 }
690
691 ORDERED_HASHMAP_FOREACH(i, todo_gids, iterator) {
692 struct sgrp n = {
693 .sg_namp = i->name,
694 .sg_passwd = (char*) "!!",
695 };
696
697 r = putsgent_with_members(&n, gshadow);
698 if (r < 0)
699 return r;
700
701 group_changed = true;
702 }
703
704 r = fflush_sync_and_check(gshadow);
705 if (r < 0)
706 return r;
707
708 if (group_changed) {
709 *tmpfile = gshadow;
710 *tmpfile_path = gshadow_tmp;
711 gshadow = NULL;
712 gshadow_tmp = NULL;
713 }
714 return 0;
715 #else
716 return 0;
717 #endif
718 }
719
720 static int write_files(void) {
721 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL;
722 _cleanup_(unlink_and_freep) char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL;
723 const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL;
724 int r;
725
726 passwd_path = prefix_roota(arg_root, "/etc/passwd");
727 shadow_path = prefix_roota(arg_root, "/etc/shadow");
728 group_path = prefix_roota(arg_root, "/etc/group");
729 gshadow_path = prefix_roota(arg_root, "/etc/gshadow");
730
731 r = write_temporary_group(group_path, &group, &group_tmp);
732 if (r < 0)
733 return r;
734
735 r = write_temporary_gshadow(gshadow_path, &gshadow, &gshadow_tmp);
736 if (r < 0)
737 return r;
738
739 r = write_temporary_passwd(passwd_path, &passwd, &passwd_tmp);
740 if (r < 0)
741 return r;
742
743 r = write_temporary_shadow(shadow_path, &shadow, &shadow_tmp);
744 if (r < 0)
745 return r;
746
747 /* Make a backup of the old files */
748 if (group) {
749 r = make_backup("/etc/group", group_path);
750 if (r < 0)
751 return r;
752 }
753 if (gshadow) {
754 r = make_backup("/etc/gshadow", gshadow_path);
755 if (r < 0)
756 return r;
757 }
758
759 if (passwd) {
760 r = make_backup("/etc/passwd", passwd_path);
761 if (r < 0)
762 return r;
763 }
764 if (shadow) {
765 r = make_backup("/etc/shadow", shadow_path);
766 if (r < 0)
767 return r;
768 }
769
770 /* And make the new files count */
771 if (group) {
772 r = rename_and_apply_smack(group_tmp, group_path);
773 if (r < 0)
774 return r;
775
776 group_tmp = mfree(group_tmp);
777 }
778 if (gshadow) {
779 r = rename_and_apply_smack(gshadow_tmp, gshadow_path);
780 if (r < 0)
781 return r;
782
783 gshadow_tmp = mfree(gshadow_tmp);
784 }
785
786 if (passwd) {
787 r = rename_and_apply_smack(passwd_tmp, passwd_path);
788 if (r < 0)
789 return r;
790
791 passwd_tmp = mfree(passwd_tmp);
792 }
793 if (shadow) {
794 r = rename_and_apply_smack(shadow_tmp, shadow_path);
795 if (r < 0)
796 return r;
797
798 shadow_tmp = mfree(shadow_tmp);
799 }
800
801 return 0;
802 }
803
804 static int uid_is_ok(uid_t uid, const char *name) {
805 struct passwd *p;
806 struct group *g;
807 const char *n;
808 Item *i;
809
810 /* Let's see if we already have assigned the UID a second time */
811 if (ordered_hashmap_get(todo_uids, UID_TO_PTR(uid)))
812 return 0;
813
814 /* Try to avoid using uids that are already used by a group
815 * that doesn't have the same name as our new user. */
816 i = ordered_hashmap_get(todo_gids, GID_TO_PTR(uid));
817 if (i && !streq(i->name, name))
818 return 0;
819
820 /* Let's check the files directly */
821 if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
822 return 0;
823
824 n = hashmap_get(database_gid, GID_TO_PTR(uid));
825 if (n && !streq(n, name))
826 return 0;
827
828 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
829 if (!arg_root) {
830 errno = 0;
831 p = getpwuid(uid);
832 if (p)
833 return 0;
834 if (!IN_SET(errno, 0, ENOENT))
835 return -errno;
836
837 errno = 0;
838 g = getgrgid((gid_t) uid);
839 if (g) {
840 if (!streq(g->gr_name, name))
841 return 0;
842 } else if (!IN_SET(errno, 0, ENOENT))
843 return -errno;
844 }
845
846 return 1;
847 }
848
849 static int root_stat(const char *p, struct stat *st) {
850 const char *fix;
851
852 fix = prefix_roota(arg_root, p);
853 if (stat(fix, st) < 0)
854 return -errno;
855
856 return 0;
857 }
858
859 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
860 struct stat st;
861 bool found_uid = false, found_gid = false;
862 uid_t uid = 0;
863 gid_t gid = 0;
864
865 assert(i);
866
867 /* First, try to get the gid directly */
868 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
869 gid = st.st_gid;
870 found_gid = true;
871 }
872
873 /* Then, try to get the uid directly */
874 if ((_uid || (_gid && !found_gid))
875 && i->uid_path
876 && root_stat(i->uid_path, &st) >= 0) {
877
878 uid = st.st_uid;
879 found_uid = true;
880
881 /* If we need the gid, but had no success yet, also derive it from the uid path */
882 if (_gid && !found_gid) {
883 gid = st.st_gid;
884 found_gid = true;
885 }
886 }
887
888 /* If that didn't work yet, then let's reuse the gid as uid */
889 if (_uid && !found_uid && i->gid_path) {
890
891 if (found_gid) {
892 uid = (uid_t) gid;
893 found_uid = true;
894 } else if (root_stat(i->gid_path, &st) >= 0) {
895 uid = (uid_t) st.st_gid;
896 found_uid = true;
897 }
898 }
899
900 if (_uid) {
901 if (!found_uid)
902 return 0;
903
904 *_uid = uid;
905 }
906
907 if (_gid) {
908 if (!found_gid)
909 return 0;
910
911 *_gid = gid;
912 }
913
914 return 1;
915 }
916
917 static int add_user(Item *i) {
918 void *z;
919 int r;
920
921 assert(i);
922
923 /* Check the database directly */
924 z = hashmap_get(database_user, i->name);
925 if (z) {
926 log_debug("User %s already exists.", i->name);
927 i->uid = PTR_TO_UID(z);
928 i->uid_set = true;
929 return 0;
930 }
931
932 if (!arg_root) {
933 struct passwd *p;
934
935 /* Also check NSS */
936 errno = 0;
937 p = getpwnam(i->name);
938 if (p) {
939 log_debug("User %s already exists.", i->name);
940 i->uid = p->pw_uid;
941 i->uid_set = true;
942
943 r = free_and_strdup(&i->description, p->pw_gecos);
944 if (r < 0)
945 return log_oom();
946
947 return 0;
948 }
949 if (!IN_SET(errno, 0, ENOENT))
950 return log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name);
951 }
952
953 /* Try to use the suggested numeric uid */
954 if (i->uid_set) {
955 r = uid_is_ok(i->uid, i->name);
956 if (r < 0)
957 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
958 if (r == 0) {
959 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
960 i->uid_set = false;
961 }
962 }
963
964 /* If that didn't work, try to read it from the specified path */
965 if (!i->uid_set) {
966 uid_t c;
967
968 if (read_id_from_file(i, &c, NULL) > 0) {
969
970 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
971 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
972 else {
973 r = uid_is_ok(c, i->name);
974 if (r < 0)
975 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
976 else if (r > 0) {
977 i->uid = c;
978 i->uid_set = true;
979 } else
980 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
981 }
982 }
983 }
984
985 /* Otherwise, try to reuse the group ID */
986 if (!i->uid_set && i->gid_set) {
987 r = uid_is_ok((uid_t) i->gid, i->name);
988 if (r < 0)
989 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
990 if (r > 0) {
991 i->uid = (uid_t) i->gid;
992 i->uid_set = true;
993 }
994 }
995
996 /* And if that didn't work either, let's try to find a free one */
997 if (!i->uid_set) {
998 for (;;) {
999 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
1000 if (r < 0) {
1001 log_error("No free user ID available for %s.", i->name);
1002 return r;
1003 }
1004
1005 r = uid_is_ok(search_uid, i->name);
1006 if (r < 0)
1007 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
1008 else if (r > 0)
1009 break;
1010 }
1011
1012 i->uid_set = true;
1013 i->uid = search_uid;
1014 }
1015
1016 r = ordered_hashmap_ensure_allocated(&todo_uids, NULL);
1017 if (r < 0)
1018 return log_oom();
1019
1020 r = ordered_hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
1021 if (r < 0)
1022 return log_oom();
1023
1024 i->todo_user = true;
1025 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
1026
1027 return 0;
1028 }
1029
1030 static int gid_is_ok(gid_t gid) {
1031 struct group *g;
1032 struct passwd *p;
1033
1034 if (ordered_hashmap_get(todo_gids, GID_TO_PTR(gid)))
1035 return 0;
1036
1037 /* Avoid reusing gids that are already used by a different user */
1038 if (ordered_hashmap_get(todo_uids, UID_TO_PTR(gid)))
1039 return 0;
1040
1041 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
1042 return 0;
1043
1044 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
1045 return 0;
1046
1047 if (!arg_root) {
1048 errno = 0;
1049 g = getgrgid(gid);
1050 if (g)
1051 return 0;
1052 if (!IN_SET(errno, 0, ENOENT))
1053 return -errno;
1054
1055 errno = 0;
1056 p = getpwuid((uid_t) gid);
1057 if (p)
1058 return 0;
1059 if (!IN_SET(errno, 0, ENOENT))
1060 return -errno;
1061 }
1062
1063 return 1;
1064 }
1065
1066 static int add_group(Item *i) {
1067 void *z;
1068 int r;
1069
1070 assert(i);
1071
1072 /* Check the database directly */
1073 z = hashmap_get(database_group, i->name);
1074 if (z) {
1075 log_debug("Group %s already exists.", i->name);
1076 i->gid = PTR_TO_GID(z);
1077 i->gid_set = true;
1078 return 0;
1079 }
1080
1081 /* Also check NSS */
1082 if (!arg_root) {
1083 struct group *g;
1084
1085 errno = 0;
1086 g = getgrnam(i->name);
1087 if (g) {
1088 log_debug("Group %s already exists.", i->name);
1089 i->gid = g->gr_gid;
1090 i->gid_set = true;
1091 return 0;
1092 }
1093 if (!IN_SET(errno, 0, ENOENT))
1094 return log_error_errno(errno, "Failed to check if group %s already exists: %m", i->name);
1095 }
1096
1097 /* Try to use the suggested numeric gid */
1098 if (i->gid_set) {
1099 r = gid_is_ok(i->gid);
1100 if (r < 0)
1101 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1102 if (i->gid_must_exist) {
1103 /* If we require the gid to already exist we can return here:
1104 * r > 0: means the gid does not exist -> fail
1105 * r == 0: means the gid exists -> nothing more to do.
1106 */
1107 if (r > 0) {
1108 log_error("Failed to create %s: please create GID %d", i->name, i->gid);
1109 return -EINVAL;
1110 }
1111 if (r == 0)
1112 return 0;
1113 }
1114 if (r == 0) {
1115 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
1116 i->gid_set = false;
1117 }
1118 }
1119
1120 /* Try to reuse the numeric uid, if there's one */
1121 if (!i->gid_set && i->uid_set) {
1122 r = gid_is_ok((gid_t) i->uid);
1123 if (r < 0)
1124 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1125 if (r > 0) {
1126 i->gid = (gid_t) i->uid;
1127 i->gid_set = true;
1128 }
1129 }
1130
1131 /* If that didn't work, try to read it from the specified path */
1132 if (!i->gid_set) {
1133 gid_t c;
1134
1135 if (read_id_from_file(i, NULL, &c) > 0) {
1136
1137 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
1138 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
1139 else {
1140 r = gid_is_ok(c);
1141 if (r < 0)
1142 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1143 else if (r > 0) {
1144 i->gid = c;
1145 i->gid_set = true;
1146 } else
1147 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
1148 }
1149 }
1150 }
1151
1152 /* And if that didn't work either, let's try to find a free one */
1153 if (!i->gid_set) {
1154 for (;;) {
1155 /* We look for new GIDs in the UID pool! */
1156 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
1157 if (r < 0) {
1158 log_error("No free group ID available for %s.", i->name);
1159 return r;
1160 }
1161
1162 r = gid_is_ok(search_uid);
1163 if (r < 0)
1164 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1165 else if (r > 0)
1166 break;
1167 }
1168
1169 i->gid_set = true;
1170 i->gid = search_uid;
1171 }
1172
1173 r = ordered_hashmap_ensure_allocated(&todo_gids, NULL);
1174 if (r < 0)
1175 return log_oom();
1176
1177 r = ordered_hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
1178 if (r < 0)
1179 return log_oom();
1180
1181 i->todo_group = true;
1182 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
1183
1184 return 0;
1185 }
1186
1187 static int process_item(Item *i) {
1188 int r;
1189
1190 assert(i);
1191
1192 switch (i->type) {
1193
1194 case ADD_USER:
1195 r = add_group(i);
1196 if (r < 0)
1197 return r;
1198
1199 return add_user(i);
1200
1201 case ADD_GROUP:
1202 return add_group(i);
1203
1204 default:
1205 assert_not_reached("Unknown item type");
1206 }
1207 }
1208
1209 static void item_free(Item *i) {
1210
1211 if (!i)
1212 return;
1213
1214 free(i->name);
1215 free(i->uid_path);
1216 free(i->gid_path);
1217 free(i->description);
1218 free(i->home);
1219 free(i);
1220 }
1221
1222 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
1223
1224 static int add_implicit(void) {
1225 char *g, **l;
1226 Iterator iterator;
1227 int r;
1228
1229 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1230
1231 ORDERED_HASHMAP_FOREACH_KEY(l, g, members, iterator) {
1232 Item *i;
1233 char **m;
1234
1235 i = ordered_hashmap_get(groups, g);
1236 if (!i) {
1237 _cleanup_(item_freep) Item *j = NULL;
1238
1239 r = ordered_hashmap_ensure_allocated(&groups, &string_hash_ops);
1240 if (r < 0)
1241 return log_oom();
1242
1243 j = new0(Item, 1);
1244 if (!j)
1245 return log_oom();
1246
1247 j->type = ADD_GROUP;
1248 j->name = strdup(g);
1249 if (!j->name)
1250 return log_oom();
1251
1252 r = ordered_hashmap_put(groups, j->name, j);
1253 if (r < 0)
1254 return log_oom();
1255
1256 log_debug("Adding implicit group '%s' due to m line", j->name);
1257 j = NULL;
1258 }
1259
1260 STRV_FOREACH(m, l) {
1261
1262 i = ordered_hashmap_get(users, *m);
1263 if (!i) {
1264 _cleanup_(item_freep) Item *j = NULL;
1265
1266 r = ordered_hashmap_ensure_allocated(&users, &string_hash_ops);
1267 if (r < 0)
1268 return log_oom();
1269
1270 j = new0(Item, 1);
1271 if (!j)
1272 return log_oom();
1273
1274 j->type = ADD_USER;
1275 j->name = strdup(*m);
1276 if (!j->name)
1277 return log_oom();
1278
1279 r = ordered_hashmap_put(users, j->name, j);
1280 if (r < 0)
1281 return log_oom();
1282
1283 log_debug("Adding implicit user '%s' due to m line", j->name);
1284 j = NULL;
1285 }
1286 }
1287 }
1288
1289 return 0;
1290 }
1291
1292 static bool item_equal(Item *a, Item *b) {
1293 assert(a);
1294 assert(b);
1295
1296 if (a->type != b->type)
1297 return false;
1298
1299 if (!streq_ptr(a->name, b->name))
1300 return false;
1301
1302 if (!streq_ptr(a->uid_path, b->uid_path))
1303 return false;
1304
1305 if (!streq_ptr(a->gid_path, b->gid_path))
1306 return false;
1307
1308 if (!streq_ptr(a->description, b->description))
1309 return false;
1310
1311 if (a->uid_set != b->uid_set)
1312 return false;
1313
1314 if (a->uid_set && a->uid != b->uid)
1315 return false;
1316
1317 if (a->gid_set != b->gid_set)
1318 return false;
1319
1320 if (a->gid_set && a->gid != b->gid)
1321 return false;
1322
1323 if (!streq_ptr(a->home, b->home))
1324 return false;
1325
1326 return true;
1327 }
1328
1329 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1330
1331 static const Specifier specifier_table[] = {
1332 { 'm', specifier_machine_id, NULL },
1333 { 'b', specifier_boot_id, NULL },
1334 { 'H', specifier_host_name, NULL },
1335 { 'v', specifier_kernel_release, NULL },
1336 {}
1337 };
1338
1339 _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL, *resolved_id = NULL, *description = NULL, *home = NULL;
1340 _cleanup_(item_freep) Item *i = NULL;
1341 Item *existing;
1342 OrderedHashmap *h;
1343 int r;
1344 const char *p;
1345
1346 assert(fname);
1347 assert(line >= 1);
1348 assert(buffer);
1349
1350 /* Parse columns */
1351 p = buffer;
1352 r = extract_many_words(&p, NULL, EXTRACT_QUOTES, &action, &name, &id, &description, &home, NULL);
1353 if (r < 0) {
1354 log_error("[%s:%u] Syntax error.", fname, line);
1355 return r;
1356 }
1357 if (r < 2) {
1358 log_error("[%s:%u] Missing action and name columns.", fname, line);
1359 return -EINVAL;
1360 }
1361 if (!isempty(p)) {
1362 log_error("[%s:%u] Trailing garbage.", fname, line);
1363 return -EINVAL;
1364 }
1365
1366 /* Verify action */
1367 if (strlen(action) != 1) {
1368 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1369 return -EINVAL;
1370 }
1371
1372 if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE)) {
1373 log_error("[%s:%u] Unknown command type '%c'.", fname, line, action[0]);
1374 return -EBADMSG;
1375 }
1376
1377 /* Verify name */
1378 if (isempty(name) || streq(name, "-"))
1379 name = mfree(name);
1380
1381 if (name) {
1382 r = specifier_printf(name, specifier_table, NULL, &resolved_name);
1383 if (r < 0) {
1384 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1385 return r;
1386 }
1387
1388 if (!valid_user_group_name(resolved_name)) {
1389 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
1390 return -EINVAL;
1391 }
1392 }
1393
1394 /* Verify id */
1395 if (isempty(id) || streq(id, "-"))
1396 id = mfree(id);
1397
1398 if (id) {
1399 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1400 if (r < 0) {
1401 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1402 return r;
1403 }
1404 }
1405
1406 /* Verify description */
1407 if (isempty(description) || streq(description, "-"))
1408 description = mfree(description);
1409
1410 if (description) {
1411 if (!valid_gecos(description)) {
1412 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, description);
1413 return -EINVAL;
1414 }
1415 }
1416
1417 /* Verify home */
1418 if (isempty(home) || streq(home, "-"))
1419 home = mfree(home);
1420
1421 if (home) {
1422 if (!valid_home(home)) {
1423 log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, home);
1424 return -EINVAL;
1425 }
1426 }
1427
1428 switch (action[0]) {
1429
1430 case ADD_RANGE:
1431 if (resolved_name) {
1432 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname, line);
1433 return -EINVAL;
1434 }
1435
1436 if (!resolved_id) {
1437 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname, line);
1438 return -EINVAL;
1439 }
1440
1441 if (description) {
1442 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname, line);
1443 return -EINVAL;
1444 }
1445
1446 if (home) {
1447 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname, line);
1448 return -EINVAL;
1449 }
1450
1451 r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id);
1452 if (r < 0) {
1453 log_error("[%s:%u] Invalid UID range %s.", fname, line, resolved_id);
1454 return -EINVAL;
1455 }
1456
1457 return 0;
1458
1459 case ADD_MEMBER: {
1460 char **l;
1461
1462 /* Try to extend an existing member or group item */
1463 if (!name) {
1464 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname, line);
1465 return -EINVAL;
1466 }
1467
1468 if (!resolved_id) {
1469 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1470 return -EINVAL;
1471 }
1472
1473 if (!valid_user_group_name(resolved_id)) {
1474 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1475 return -EINVAL;
1476 }
1477
1478 if (description) {
1479 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
1480 return -EINVAL;
1481 }
1482
1483 if (home) {
1484 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname, line);
1485 return -EINVAL;
1486 }
1487
1488 r = ordered_hashmap_ensure_allocated(&members, &string_hash_ops);
1489 if (r < 0)
1490 return log_oom();
1491
1492 l = ordered_hashmap_get(members, resolved_id);
1493 if (l) {
1494 /* A list for this group name already exists, let's append to it */
1495 r = strv_push(&l, resolved_name);
1496 if (r < 0)
1497 return log_oom();
1498
1499 resolved_name = NULL;
1500
1501 assert_se(ordered_hashmap_update(members, resolved_id, l) >= 0);
1502 } else {
1503 /* No list for this group name exists yet, create one */
1504
1505 l = new0(char *, 2);
1506 if (!l)
1507 return -ENOMEM;
1508
1509 l[0] = resolved_name;
1510 l[1] = NULL;
1511
1512 r = ordered_hashmap_put(members, resolved_id, l);
1513 if (r < 0) {
1514 free(l);
1515 return log_oom();
1516 }
1517
1518 resolved_id = resolved_name = NULL;
1519 }
1520
1521 return 0;
1522 }
1523
1524 case ADD_USER:
1525 if (!name) {
1526 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname, line);
1527 return -EINVAL;
1528 }
1529
1530 r = ordered_hashmap_ensure_allocated(&users, &string_hash_ops);
1531 if (r < 0)
1532 return log_oom();
1533
1534 i = new0(Item, 1);
1535 if (!i)
1536 return log_oom();
1537
1538 if (resolved_id) {
1539 if (path_is_absolute(resolved_id)) {
1540 i->uid_path = resolved_id;
1541 resolved_id = NULL;
1542
1543 path_kill_slashes(i->uid_path);
1544 } else {
1545 _cleanup_free_ char *uid = NULL, *gid = NULL;
1546 if (split_pair(resolved_id, ":", &uid, &gid) == 0) {
1547 r = parse_gid(gid, &i->gid);
1548 if (r < 0)
1549 return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
1550 i->gid_set = true;
1551 i->gid_must_exist = true;
1552 free_and_replace(resolved_id, uid);
1553 }
1554 r = parse_uid(resolved_id, &i->uid);
1555 if (r < 0)
1556 return log_error_errno(r, "Failed to parse UID: '%s': %m", id);
1557
1558 i->uid_set = true;
1559 }
1560 }
1561
1562 i->description = description;
1563 description = NULL;
1564
1565 i->home = home;
1566 home = NULL;
1567
1568 h = users;
1569 break;
1570
1571 case ADD_GROUP:
1572 if (!name) {
1573 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname, line);
1574 return -EINVAL;
1575 }
1576
1577 if (description) {
1578 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
1579 return -EINVAL;
1580 }
1581
1582 if (home) {
1583 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname, line);
1584 return -EINVAL;
1585 }
1586
1587 r = ordered_hashmap_ensure_allocated(&groups, &string_hash_ops);
1588 if (r < 0)
1589 return log_oom();
1590
1591 i = new0(Item, 1);
1592 if (!i)
1593 return log_oom();
1594
1595 if (resolved_id) {
1596 if (path_is_absolute(resolved_id)) {
1597 i->gid_path = resolved_id;
1598 resolved_id = NULL;
1599
1600 path_kill_slashes(i->gid_path);
1601 } else {
1602 r = parse_gid(resolved_id, &i->gid);
1603 if (r < 0)
1604 return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
1605
1606 i->gid_set = true;
1607 }
1608 }
1609
1610 h = groups;
1611 break;
1612
1613 default:
1614 return -EBADMSG;
1615 }
1616
1617 i->type = action[0];
1618 i->name = resolved_name;
1619 resolved_name = NULL;
1620
1621 existing = ordered_hashmap_get(h, i->name);
1622 if (existing) {
1623
1624 /* Two identical items are fine */
1625 if (!item_equal(existing, i))
1626 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1627
1628 return 0;
1629 }
1630
1631 r = ordered_hashmap_put(h, i->name, i);
1632 if (r < 0)
1633 return log_oom();
1634
1635 i = NULL;
1636 return 0;
1637 }
1638
1639 static int read_config_file(const char *fn, bool ignore_enoent) {
1640 _cleanup_fclose_ FILE *rf = NULL;
1641 FILE *f = NULL;
1642 char line[LINE_MAX];
1643 unsigned v = 0;
1644 int r = 0;
1645
1646 assert(fn);
1647
1648 if (streq(fn, "-"))
1649 f = stdin;
1650 else {
1651 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &rf);
1652 if (r < 0) {
1653 if (ignore_enoent && r == -ENOENT)
1654 return 0;
1655
1656 return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
1657 }
1658
1659 f = rf;
1660 }
1661
1662 FOREACH_LINE(line, f, break) {
1663 char *l;
1664 int k;
1665
1666 v++;
1667
1668 l = strstrip(line);
1669 if (IN_SET(*l, 0, '#'))
1670 continue;
1671
1672 k = parse_line(fn, v, l);
1673 if (k < 0 && r == 0)
1674 r = k;
1675 }
1676
1677 if (ferror(f)) {
1678 log_error_errno(errno, "Failed to read from file %s: %m", fn);
1679 if (r == 0)
1680 r = -EIO;
1681 }
1682
1683 return r;
1684 }
1685
1686 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1687 char *name;
1688
1689 for (;;) {
1690 name = hashmap_first(by_id);
1691 if (!name)
1692 break;
1693
1694 hashmap_remove(by_name, name);
1695
1696 hashmap_steal_first_key(by_id);
1697 free(name);
1698 }
1699
1700 while ((name = hashmap_steal_first_key(by_name)))
1701 free(name);
1702
1703 hashmap_free(by_name);
1704 hashmap_free(by_id);
1705 }
1706
1707 static void help(void) {
1708 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1709 "Creates system user accounts.\n\n"
1710 " -h --help Show this help\n"
1711 " --version Show package version\n"
1712 " --root=PATH Operate on an alternate filesystem root\n"
1713 , program_invocation_short_name);
1714 }
1715
1716 static int parse_argv(int argc, char *argv[]) {
1717
1718 enum {
1719 ARG_VERSION = 0x100,
1720 ARG_ROOT,
1721 };
1722
1723 static const struct option options[] = {
1724 { "help", no_argument, NULL, 'h' },
1725 { "version", no_argument, NULL, ARG_VERSION },
1726 { "root", required_argument, NULL, ARG_ROOT },
1727 {}
1728 };
1729
1730 int c, r;
1731
1732 assert(argc >= 0);
1733 assert(argv);
1734
1735 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
1736
1737 switch (c) {
1738
1739 case 'h':
1740 help();
1741 return 0;
1742
1743 case ARG_VERSION:
1744 return version();
1745
1746 case ARG_ROOT:
1747 r = parse_path_argument_and_warn(optarg, true, &arg_root);
1748 if (r < 0)
1749 return r;
1750 break;
1751
1752 case '?':
1753 return -EINVAL;
1754
1755 default:
1756 assert_not_reached("Unhandled option");
1757 }
1758
1759 return 1;
1760 }
1761
1762 int main(int argc, char *argv[]) {
1763
1764 _cleanup_close_ int lock = -1;
1765 Iterator iterator;
1766 int r, k;
1767 Item *i;
1768 char *n;
1769
1770 r = parse_argv(argc, argv);
1771 if (r <= 0)
1772 goto finish;
1773
1774 log_set_target(LOG_TARGET_AUTO);
1775 log_parse_environment();
1776 log_open();
1777
1778 umask(0022);
1779
1780 r = mac_selinux_init();
1781 if (r < 0) {
1782 log_error_errno(r, "SELinux setup failed: %m");
1783 goto finish;
1784 }
1785
1786 if (optind < argc) {
1787 int j;
1788
1789 for (j = optind; j < argc; j++) {
1790 k = read_config_file(argv[j], false);
1791 if (k < 0 && r == 0)
1792 r = k;
1793 }
1794 } else {
1795 _cleanup_strv_free_ char **files = NULL;
1796 char **f;
1797
1798 r = conf_files_list_nulstr(&files, ".conf", arg_root, 0, conf_file_dirs);
1799 if (r < 0) {
1800 log_error_errno(r, "Failed to enumerate sysusers.d files: %m");
1801 goto finish;
1802 }
1803
1804 STRV_FOREACH(f, files) {
1805 k = read_config_file(*f, true);
1806 if (k < 0 && r == 0)
1807 r = k;
1808 }
1809 }
1810
1811 /* Let's tell nss-systemd not to synthesize the "root" and "nobody" entries for it, so that our detection
1812 * whether the names or UID/GID area already used otherwise doesn't get confused. After all, even though
1813 * nss-systemd synthesizes these users/groups, they should still appear in /etc/passwd and /etc/group, as the
1814 * synthesizing logic is merely supposed to be fallback for cases where we run with a completely unpopulated
1815 * /etc. */
1816 if (setenv("SYSTEMD_NSS_BYPASS_SYNTHETIC", "1", 1) < 0) {
1817 r = log_error_errno(errno, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m");
1818 goto finish;
1819 }
1820
1821 if (!uid_range) {
1822 /* Default to default range of 1..SYSTEMD_UID_MAX */
1823 r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX);
1824 if (r < 0) {
1825 log_oom();
1826 goto finish;
1827 }
1828 }
1829
1830 r = add_implicit();
1831 if (r < 0)
1832 goto finish;
1833
1834 lock = take_etc_passwd_lock(arg_root);
1835 if (lock < 0) {
1836 log_error_errno(lock, "Failed to take lock: %m");
1837 goto finish;
1838 }
1839
1840 r = load_user_database();
1841 if (r < 0) {
1842 log_error_errno(r, "Failed to load user database: %m");
1843 goto finish;
1844 }
1845
1846 r = load_group_database();
1847 if (r < 0) {
1848 log_error_errno(r, "Failed to read group database: %m");
1849 goto finish;
1850 }
1851
1852 ORDERED_HASHMAP_FOREACH(i, groups, iterator)
1853 process_item(i);
1854
1855 ORDERED_HASHMAP_FOREACH(i, users, iterator)
1856 process_item(i);
1857
1858 r = write_files();
1859 if (r < 0)
1860 log_error_errno(r, "Failed to write files: %m");
1861
1862 finish:
1863 ordered_hashmap_free_with_destructor(groups, item_free);
1864 ordered_hashmap_free_with_destructor(users, item_free);
1865
1866 while ((n = ordered_hashmap_first_key(members))) {
1867 strv_free(ordered_hashmap_steal_first(members));
1868 free(n);
1869 }
1870 ordered_hashmap_free(members);
1871
1872 ordered_hashmap_free(todo_uids);
1873 ordered_hashmap_free(todo_gids);
1874
1875 free_database(database_user, database_uid);
1876 free_database(database_group, database_gid);
1877
1878 free(arg_root);
1879
1880 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1881 }