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