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