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