]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/sysusers/sysusers.c
a348ec497aeca33ba16176fecf07fdf59d4a2183
[thirdparty/systemd.git] / src / sysusers / sysusers.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <getopt.h>
4 #include <utmp.h>
5
6 #include "alloc-util.h"
7 #include "conf-files.h"
8 #include "copy.h"
9 #include "def.h"
10 #include "fd-util.h"
11 #include "fileio-label.h"
12 #include "format-util.h"
13 #include "fs-util.h"
14 #include "hashmap.h"
15 #include "pager.h"
16 #include "path-util.h"
17 #include "pretty-print.h"
18 #include "selinux-util.h"
19 #include "smack-util.h"
20 #include "specifier.h"
21 #include "string-util.h"
22 #include "strv.h"
23 #include "uid-range.h"
24 #include "user-util.h"
25 #include "utf8.h"
26 #include "util.h"
27
28 typedef enum ItemType {
29 ADD_USER = 'u',
30 ADD_GROUP = 'g',
31 ADD_MEMBER = 'm',
32 ADD_RANGE = 'r',
33 } ItemType;
34
35 typedef struct Item {
36 ItemType type;
37
38 char *name;
39 char *uid_path;
40 char *gid_path;
41 char *description;
42 char *home;
43 char *shell;
44
45 gid_t gid;
46 uid_t uid;
47
48 bool gid_set:1;
49
50 /* When set the group with the specified gid must exist
51 * and the check if a uid clashes with the gid is skipped.
52 */
53 bool id_set_strict:1;
54
55 bool uid_set:1;
56
57 bool todo_user:1;
58 bool todo_group:1;
59 } Item;
60
61 static char *arg_root = NULL;
62 static bool arg_cat_config = false;
63 static const char *arg_replace = NULL;
64 static bool arg_inline = false;
65 static PagerFlags arg_pager_flags = 0;
66
67 static OrderedHashmap *users = NULL, *groups = NULL;
68 static OrderedHashmap *todo_uids = NULL, *todo_gids = NULL;
69 static OrderedHashmap *members = NULL;
70
71 static Hashmap *database_uid = NULL, *database_user = NULL;
72 static Hashmap *database_gid = NULL, *database_group = NULL;
73
74 static uid_t search_uid = UID_INVALID;
75 static UidRange *uid_range = NULL;
76 static unsigned n_uid_range = 0;
77
78 static int load_user_database(void) {
79 _cleanup_fclose_ FILE *f = NULL;
80 const char *passwd_path;
81 struct passwd *pw;
82 int r;
83
84 passwd_path = prefix_roota(arg_root, "/etc/passwd");
85 f = fopen(passwd_path, "re");
86 if (!f)
87 return errno == ENOENT ? 0 : -errno;
88
89 r = hashmap_ensure_allocated(&database_user, &string_hash_ops);
90 if (r < 0)
91 return r;
92
93 r = hashmap_ensure_allocated(&database_uid, NULL);
94 if (r < 0)
95 return r;
96
97 while ((r = fgetpwent_sane(f, &pw)) > 0) {
98 char *n;
99 int k, q;
100
101 n = strdup(pw->pw_name);
102 if (!n)
103 return -ENOMEM;
104
105 k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
106 if (k < 0 && k != -EEXIST) {
107 free(n);
108 return k;
109 }
110
111 q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
112 if (q < 0 && q != -EEXIST) {
113 if (k <= 0)
114 free(n);
115 return q;
116 }
117
118 if (k <= 0 && q <= 0)
119 free(n);
120 }
121 return r;
122 }
123
124 static int load_group_database(void) {
125 _cleanup_fclose_ FILE *f = NULL;
126 const char *group_path;
127 struct group *gr;
128 int r;
129
130 group_path = prefix_roota(arg_root, "/etc/group");
131 f = fopen(group_path, "re");
132 if (!f)
133 return errno == ENOENT ? 0 : -errno;
134
135 r = hashmap_ensure_allocated(&database_group, &string_hash_ops);
136 if (r < 0)
137 return r;
138
139 r = hashmap_ensure_allocated(&database_gid, NULL);
140 if (r < 0)
141 return r;
142
143 errno = 0;
144 while ((gr = fgetgrent(f))) {
145 char *n;
146 int k, q;
147
148 n = strdup(gr->gr_name);
149 if (!n)
150 return -ENOMEM;
151
152 k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
153 if (k < 0 && k != -EEXIST) {
154 free(n);
155 return k;
156 }
157
158 q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
159 if (q < 0 && q != -EEXIST) {
160 if (k <= 0)
161 free(n);
162 return q;
163 }
164
165 if (k <= 0 && q <= 0)
166 free(n);
167
168 errno = 0;
169 }
170 if (!IN_SET(errno, 0, ENOENT))
171 return -errno;
172
173 return 0;
174 }
175
176 static int make_backup(const char *target, const char *x) {
177 _cleanup_close_ int src = -1;
178 _cleanup_fclose_ FILE *dst = NULL;
179 _cleanup_free_ char *temp = NULL;
180 char *backup;
181 struct timespec ts[2];
182 struct stat st;
183 int r;
184
185 src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
186 if (src < 0) {
187 if (errno == ENOENT) /* No backup necessary... */
188 return 0;
189
190 return -errno;
191 }
192
193 if (fstat(src, &st) < 0)
194 return -errno;
195
196 r = fopen_temporary_label(target, x, &dst, &temp);
197 if (r < 0)
198 return r;
199
200 r = copy_bytes(src, fileno(dst), (uint64_t) -1, COPY_REFLINK);
201 if (r < 0)
202 goto fail;
203
204 /* Don't fail on chmod() or chown(). If it stays owned by us
205 * and/or unreadable by others, then it isn't too bad... */
206
207 backup = strjoina(x, "-");
208
209 /* Copy over the access mask */
210 r = fchmod_and_chown(fileno(dst), st.st_mode & 07777, st.st_uid, st.st_gid);
211 if (r < 0)
212 log_warning_errno(r, "Failed to change access mode or ownership of %s: %m", backup);
213
214 ts[0] = st.st_atim;
215 ts[1] = st.st_mtim;
216 if (futimens(fileno(dst), ts) < 0)
217 log_warning_errno(errno, "Failed to fix access and modification time of %s: %m", backup);
218
219 r = fflush_sync_and_check(dst);
220 if (r < 0)
221 goto fail;
222
223 if (rename(temp, backup) < 0) {
224 r = -errno;
225 goto fail;
226 }
227
228 return 0;
229
230 fail:
231 unlink(temp);
232 return r;
233 }
234
235 static int putgrent_with_members(const struct group *gr, FILE *group) {
236 char **a;
237
238 assert(gr);
239 assert(group);
240
241 a = ordered_hashmap_get(members, gr->gr_name);
242 if (a) {
243 _cleanup_strv_free_ char **l = NULL;
244 bool added = false;
245 char **i;
246
247 l = strv_copy(gr->gr_mem);
248 if (!l)
249 return -ENOMEM;
250
251 STRV_FOREACH(i, a) {
252 if (strv_find(l, *i))
253 continue;
254
255 if (strv_extend(&l, *i) < 0)
256 return -ENOMEM;
257
258 added = true;
259 }
260
261 if (added) {
262 struct group t;
263 int r;
264
265 strv_uniq(l);
266 strv_sort(l);
267
268 t = *gr;
269 t.gr_mem = l;
270
271 r = putgrent_sane(&t, group);
272 return r < 0 ? r : 1;
273 }
274 }
275
276 return putgrent_sane(gr, group);
277 }
278
279 #if ENABLE_GSHADOW
280 static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
281 char **a;
282
283 assert(sg);
284 assert(gshadow);
285
286 a = ordered_hashmap_get(members, sg->sg_namp);
287 if (a) {
288 _cleanup_strv_free_ char **l = NULL;
289 bool added = false;
290 char **i;
291
292 l = strv_copy(sg->sg_mem);
293 if (!l)
294 return -ENOMEM;
295
296 STRV_FOREACH(i, a) {
297 if (strv_find(l, *i))
298 continue;
299
300 if (strv_extend(&l, *i) < 0)
301 return -ENOMEM;
302
303 added = true;
304 }
305
306 if (added) {
307 struct sgrp t;
308 int r;
309
310 strv_uniq(l);
311 strv_sort(l);
312
313 t = *sg;
314 t.sg_mem = l;
315
316 r = putsgent_sane(&t, gshadow);
317 return r < 0 ? r : 1;
318 }
319 }
320
321 return putsgent_sane(sg, gshadow);
322 }
323 #endif
324
325 static int sync_rights(FILE *from, FILE *to) {
326 struct stat st;
327
328 if (fstat(fileno(from), &st) < 0)
329 return -errno;
330
331 return fchmod_and_chown(fileno(to), st.st_mode & 07777, st.st_uid, st.st_gid);
332 }
333
334 static int rename_and_apply_smack(const char *temp_path, const char *dest_path) {
335 int r = 0;
336 if (rename(temp_path, dest_path) < 0)
337 return -errno;
338
339 #ifdef SMACK_RUN_LABEL
340 r = mac_smack_apply(dest_path, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL);
341 if (r < 0)
342 return r;
343 #endif
344 return r;
345 }
346
347 static const char* default_shell(uid_t uid) {
348 return uid == 0 ? "/bin/sh" : "/sbin/nologin";
349 }
350
351 static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char **tmpfile_path) {
352 _cleanup_fclose_ FILE *original = NULL, *passwd = NULL;
353 _cleanup_(unlink_and_freep) char *passwd_tmp = NULL;
354 struct passwd *pw = NULL;
355 Iterator iterator;
356 Item *i;
357 int r;
358
359 if (ordered_hashmap_size(todo_uids) == 0)
360 return 0;
361
362 r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
363 if (r < 0)
364 return r;
365
366 original = fopen(passwd_path, "re");
367 if (original) {
368
369 r = sync_rights(original, passwd);
370 if (r < 0)
371 return r;
372
373 while ((r = fgetpwent_sane(original, &pw)) > 0) {
374
375 i = ordered_hashmap_get(users, pw->pw_name);
376 if (i && i->todo_user) {
377 log_error("%s: User \"%s\" already exists.", passwd_path, pw->pw_name);
378 return -EEXIST;
379 }
380
381 if (ordered_hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
382 log_error("%s: Detected collision for UID " UID_FMT ".", passwd_path, pw->pw_uid);
383 return -EEXIST;
384 }
385
386 /* Make sure we keep the NIS entries (if any) at the end. */
387 if (IN_SET(pw->pw_name[0], '+', '-'))
388 break;
389
390 r = putpwent_sane(pw, passwd);
391 if (r < 0)
392 return r;
393 }
394 if (r < 0)
395 return r;
396
397 } else {
398 if (errno != ENOENT)
399 return -errno;
400 if (fchmod(fileno(passwd), 0644) < 0)
401 return -errno;
402 }
403
404 ORDERED_HASHMAP_FOREACH(i, todo_uids, iterator) {
405 struct passwd n = {
406 .pw_name = i->name,
407 .pw_uid = i->uid,
408 .pw_gid = i->gid,
409 .pw_gecos = i->description,
410
411 /* "x" means the password is stored in the shadow file */
412 .pw_passwd = (char*) "x",
413
414 /* We default to the root directory as home */
415 .pw_dir = i->home ? i->home : (char*) "/",
416
417 /* Initialize the shell to nologin, with one exception:
418 * for root we patch in something special */
419 .pw_shell = i->shell ?: (char*) default_shell(i->uid),
420 };
421
422 r = putpwent_sane(&n, passwd);
423 if (r < 0)
424 return r;
425 }
426
427 /* Append the remaining NIS entries if any */
428 while (pw) {
429 r = putpwent_sane(pw, passwd);
430 if (r < 0)
431 return r;
432
433 r = fgetpwent_sane(original, &pw);
434 if (r < 0)
435 return r;
436 if (r == 0)
437 break;
438 }
439
440 r = fflush_and_check(passwd);
441 if (r < 0)
442 return r;
443
444 *tmpfile = TAKE_PTR(passwd);
445 *tmpfile_path = TAKE_PTR(passwd_tmp);
446
447 return 0;
448 }
449
450 static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char **tmpfile_path) {
451 _cleanup_fclose_ FILE *original = NULL, *shadow = NULL;
452 _cleanup_(unlink_and_freep) char *shadow_tmp = NULL;
453 struct spwd *sp = NULL;
454 Iterator iterator;
455 long lstchg;
456 Item *i;
457 int r;
458
459 if (ordered_hashmap_size(todo_uids) == 0)
460 return 0;
461
462 r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
463 if (r < 0)
464 return r;
465
466 lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
467
468 original = fopen(shadow_path, "re");
469 if (original) {
470
471 r = sync_rights(original, shadow);
472 if (r < 0)
473 return r;
474
475 while ((r = fgetspent_sane(original, &sp)) > 0) {
476
477 i = ordered_hashmap_get(users, sp->sp_namp);
478 if (i && i->todo_user) {
479 /* we will update the existing entry */
480 sp->sp_lstchg = lstchg;
481
482 /* only the /etc/shadow stage is left, so we can
483 * safely remove the item from the todo set */
484 i->todo_user = false;
485 ordered_hashmap_remove(todo_uids, UID_TO_PTR(i->uid));
486 }
487
488 /* Make sure we keep the NIS entries (if any) at the end. */
489 if (IN_SET(sp->sp_namp[0], '+', '-'))
490 break;
491
492 r = putspent_sane(sp, shadow);
493 if (r < 0)
494 return r;
495 }
496 if (r < 0)
497 return r;
498
499 } else {
500 if (errno != ENOENT)
501 return -errno;
502 if (fchmod(fileno(shadow), 0000) < 0)
503 return -errno;
504 }
505
506 ORDERED_HASHMAP_FOREACH(i, todo_uids, iterator) {
507 struct spwd n = {
508 .sp_namp = i->name,
509 .sp_pwdp = (char*) "!!",
510 .sp_lstchg = lstchg,
511 .sp_min = -1,
512 .sp_max = -1,
513 .sp_warn = -1,
514 .sp_inact = -1,
515 .sp_expire = -1,
516 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
517 };
518
519 r = putspent_sane(&n, shadow);
520 if (r < 0)
521 return r;
522 }
523
524 /* Append the remaining NIS entries if any */
525 while (sp) {
526 r = putspent_sane(sp, shadow);
527 if (r < 0)
528 return r;
529
530 r = fgetspent_sane(original, &sp);
531 if (r < 0)
532 return r;
533 if (r == 0)
534 break;
535 }
536 if (!IN_SET(errno, 0, ENOENT))
537 return -errno;
538
539 r = fflush_sync_and_check(shadow);
540 if (r < 0)
541 return r;
542
543 *tmpfile = TAKE_PTR(shadow);
544 *tmpfile_path = TAKE_PTR(shadow_tmp);
545
546 return 0;
547 }
548
549 static int write_temporary_group(const char *group_path, FILE **tmpfile, char **tmpfile_path) {
550 _cleanup_fclose_ FILE *original = NULL, *group = NULL;
551 _cleanup_(unlink_and_freep) char *group_tmp = NULL;
552 bool group_changed = false;
553 struct group *gr = NULL;
554 Iterator iterator;
555 Item *i;
556 int r;
557
558 if (ordered_hashmap_size(todo_gids) == 0 && ordered_hashmap_size(members) == 0)
559 return 0;
560
561 r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
562 if (r < 0)
563 return r;
564
565 original = fopen(group_path, "re");
566 if (original) {
567
568 r = sync_rights(original, group);
569 if (r < 0)
570 return r;
571
572 while ((r = fgetgrent_sane(original, &gr)) > 0) {
573 /* Safety checks against name and GID collisions. Normally,
574 * this should be unnecessary, but given that we look at the
575 * entries anyway here, let's make an extra verification
576 * step that we don't generate duplicate entries. */
577
578 i = ordered_hashmap_get(groups, gr->gr_name);
579 if (i && i->todo_group) {
580 log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name);
581 return -EEXIST;
582 }
583
584 if (ordered_hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
585 log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid);
586 return -EEXIST;
587 }
588
589 /* Make sure we keep the NIS entries (if any) at the end. */
590 if (IN_SET(gr->gr_name[0], '+', '-'))
591 break;
592
593 r = putgrent_with_members(gr, group);
594 if (r < 0)
595 return r;
596 if (r > 0)
597 group_changed = true;
598 }
599 if (r < 0)
600 return r;
601
602 } else {
603 if (errno != ENOENT)
604 return -errno;
605 if (fchmod(fileno(group), 0644) < 0)
606 return -errno;
607 }
608
609 ORDERED_HASHMAP_FOREACH(i, todo_gids, iterator) {
610 struct group n = {
611 .gr_name = i->name,
612 .gr_gid = i->gid,
613 .gr_passwd = (char*) "x",
614 };
615
616 r = putgrent_with_members(&n, group);
617 if (r < 0)
618 return r;
619
620 group_changed = true;
621 }
622
623 /* Append the remaining NIS entries if any */
624 while (gr) {
625 r = putgrent_sane(gr, group);
626 if (r < 0)
627 return r;
628
629 r = fgetgrent_sane(original, &gr);
630 if (r < 0)
631 return r;
632 if (r == 0)
633 break;
634 }
635
636 r = fflush_sync_and_check(group);
637 if (r < 0)
638 return r;
639
640 if (group_changed) {
641 *tmpfile = TAKE_PTR(group);
642 *tmpfile_path = TAKE_PTR(group_tmp);
643 }
644 return 0;
645 }
646
647 static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, char **tmpfile_path) {
648 #if ENABLE_GSHADOW
649 _cleanup_fclose_ FILE *original = NULL, *gshadow = NULL;
650 _cleanup_(unlink_and_freep) char *gshadow_tmp = NULL;
651 bool group_changed = false;
652 Iterator iterator;
653 Item *i;
654 int r;
655
656 if (ordered_hashmap_size(todo_gids) == 0 && ordered_hashmap_size(members) == 0)
657 return 0;
658
659 r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
660 if (r < 0)
661 return r;
662
663 original = fopen(gshadow_path, "re");
664 if (original) {
665 struct sgrp *sg;
666
667 r = sync_rights(original, gshadow);
668 if (r < 0)
669 return r;
670
671 while ((r = fgetsgent_sane(original, &sg)) > 0) {
672
673 i = ordered_hashmap_get(groups, sg->sg_namp);
674 if (i && i->todo_group) {
675 log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp);
676 return -EEXIST;
677 }
678
679 r = putsgent_with_members(sg, gshadow);
680 if (r < 0)
681 return r;
682 if (r > 0)
683 group_changed = true;
684 }
685 if (r < 0)
686 return r;
687
688 } else {
689 if (errno != ENOENT)
690 return -errno;
691 if (fchmod(fileno(gshadow), 0000) < 0)
692 return -errno;
693 }
694
695 ORDERED_HASHMAP_FOREACH(i, todo_gids, iterator) {
696 struct sgrp n = {
697 .sg_namp = i->name,
698 .sg_passwd = (char*) "!!",
699 };
700
701 r = putsgent_with_members(&n, gshadow);
702 if (r < 0)
703 return r;
704
705 group_changed = true;
706 }
707
708 r = fflush_sync_and_check(gshadow);
709 if (r < 0)
710 return r;
711
712 if (group_changed) {
713 *tmpfile = TAKE_PTR(gshadow);
714 *tmpfile_path = TAKE_PTR(gshadow_tmp);
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 Item *j;
1204
1205 j = ordered_hashmap_get(groups, i->name);
1206 if (j && j->todo_group) {
1207 /* When the group with the same name is already in queue,
1208 * use the information about the group and do not create
1209 * duplicated group entry. */
1210 i->gid_set = j->gid_set;
1211 i->gid = j->gid;
1212 i->id_set_strict = true;
1213 } else {
1214 r = add_group(i);
1215 if (r < 0)
1216 return r;
1217 }
1218
1219 return add_user(i);
1220 }
1221
1222 case ADD_GROUP:
1223 return add_group(i);
1224
1225 default:
1226 assert_not_reached("Unknown item type");
1227 }
1228 }
1229
1230 static void item_free(Item *i) {
1231
1232 if (!i)
1233 return;
1234
1235 free(i->name);
1236 free(i->uid_path);
1237 free(i->gid_path);
1238 free(i->description);
1239 free(i->home);
1240 free(i->shell);
1241 free(i);
1242 }
1243
1244 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
1245
1246 static int add_implicit(void) {
1247 char *g, **l;
1248 Iterator iterator;
1249 int r;
1250
1251 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1252 ORDERED_HASHMAP_FOREACH_KEY(l, g, members, iterator) {
1253 char **m;
1254
1255 STRV_FOREACH(m, l)
1256 if (!ordered_hashmap_get(users, *m)) {
1257 _cleanup_(item_freep) Item *j = NULL;
1258
1259 r = ordered_hashmap_ensure_allocated(&users, &string_hash_ops);
1260 if (r < 0)
1261 return log_oom();
1262
1263 j = new0(Item, 1);
1264 if (!j)
1265 return log_oom();
1266
1267 j->type = ADD_USER;
1268 j->name = strdup(*m);
1269 if (!j->name)
1270 return log_oom();
1271
1272 r = ordered_hashmap_put(users, j->name, j);
1273 if (r < 0)
1274 return log_oom();
1275
1276 log_debug("Adding implicit user '%s' due to m line", j->name);
1277 j = NULL;
1278 }
1279
1280 if (!(ordered_hashmap_get(users, g) ||
1281 ordered_hashmap_get(groups, g))) {
1282 _cleanup_(item_freep) Item *j = NULL;
1283
1284 r = ordered_hashmap_ensure_allocated(&groups, &string_hash_ops);
1285 if (r < 0)
1286 return log_oom();
1287
1288 j = new0(Item, 1);
1289 if (!j)
1290 return log_oom();
1291
1292 j->type = ADD_GROUP;
1293 j->name = strdup(g);
1294 if (!j->name)
1295 return log_oom();
1296
1297 r = ordered_hashmap_put(groups, j->name, j);
1298 if (r < 0)
1299 return log_oom();
1300
1301 log_debug("Adding implicit group '%s' due to m line", j->name);
1302 j = NULL;
1303 }
1304 }
1305
1306 return 0;
1307 }
1308
1309 static bool item_equal(Item *a, Item *b) {
1310 assert(a);
1311 assert(b);
1312
1313 if (a->type != b->type)
1314 return false;
1315
1316 if (!streq_ptr(a->name, b->name))
1317 return false;
1318
1319 if (!streq_ptr(a->uid_path, b->uid_path))
1320 return false;
1321
1322 if (!streq_ptr(a->gid_path, b->gid_path))
1323 return false;
1324
1325 if (!streq_ptr(a->description, b->description))
1326 return false;
1327
1328 if (a->uid_set != b->uid_set)
1329 return false;
1330
1331 if (a->uid_set && a->uid != b->uid)
1332 return false;
1333
1334 if (a->gid_set != b->gid_set)
1335 return false;
1336
1337 if (a->gid_set && a->gid != b->gid)
1338 return false;
1339
1340 if (!streq_ptr(a->home, b->home))
1341 return false;
1342
1343 if (!streq_ptr(a->shell, b->shell))
1344 return false;
1345
1346 return true;
1347 }
1348
1349 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1350
1351 static const Specifier specifier_table[] = {
1352 { 'm', specifier_machine_id, NULL },
1353 { 'b', specifier_boot_id, NULL },
1354 { 'H', specifier_host_name, NULL },
1355 { 'v', specifier_kernel_release, NULL },
1356 { 'T', specifier_tmp_dir, NULL },
1357 { 'V', specifier_var_tmp_dir, 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, *resolved_description = NULL,
1365 *home = NULL, *resolved_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 r = specifier_printf(description, specifier_table, NULL, &resolved_description);
1440 if (r < 0) {
1441 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, description);
1442 return r;
1443 }
1444
1445 if (!valid_gecos(resolved_description)) {
1446 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, resolved_description);
1447 return -EINVAL;
1448 }
1449 }
1450
1451 /* Verify home */
1452 if (isempty(home) || streq(home, "-"))
1453 home = mfree(home);
1454
1455 if (home) {
1456 r = specifier_printf(home, specifier_table, NULL, &resolved_home);
1457 if (r < 0) {
1458 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, home);
1459 return r;
1460 }
1461
1462 if (!valid_home(resolved_home)) {
1463 log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, resolved_home);
1464 return -EINVAL;
1465 }
1466 }
1467
1468 /* Verify shell */
1469 if (isempty(shell) || streq(shell, "-"))
1470 shell = mfree(shell);
1471
1472 if (shell) {
1473 r = specifier_printf(shell, specifier_table, NULL, &resolved_shell);
1474 if (r < 0) {
1475 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, shell);
1476 return r;
1477 }
1478
1479 if (!valid_shell(resolved_shell)) {
1480 log_error("[%s:%u] '%s' is not a valid login shell field.", fname, line, resolved_shell);
1481 return -EINVAL;
1482 }
1483 }
1484
1485 switch (action[0]) {
1486
1487 case ADD_RANGE:
1488 if (resolved_name) {
1489 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname, line);
1490 return -EINVAL;
1491 }
1492
1493 if (!resolved_id) {
1494 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname, line);
1495 return -EINVAL;
1496 }
1497
1498 if (description || home || shell) {
1499 log_error("[%s:%u] Lines of type '%c' don't take a %s field.",
1500 fname, line, action[0],
1501 description ? "GECOS" : home ? "home directory" : "login shell");
1502 return -EINVAL;
1503 }
1504
1505 r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id);
1506 if (r < 0) {
1507 log_error("[%s:%u] Invalid UID range %s.", fname, line, resolved_id);
1508 return -EINVAL;
1509 }
1510
1511 return 0;
1512
1513 case ADD_MEMBER: {
1514 char **l;
1515
1516 /* Try to extend an existing member or group item */
1517 if (!name) {
1518 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname, line);
1519 return -EINVAL;
1520 }
1521
1522 if (!resolved_id) {
1523 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1524 return -EINVAL;
1525 }
1526
1527 if (!valid_user_group_name(resolved_id)) {
1528 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1529 return -EINVAL;
1530 }
1531
1532 if (description || home || shell) {
1533 log_error("[%s:%u] Lines of type '%c' don't take a %s field.",
1534 fname, line, action[0],
1535 description ? "GECOS" : home ? "home directory" : "login shell");
1536 return -EINVAL;
1537 }
1538
1539 r = ordered_hashmap_ensure_allocated(&members, &string_hash_ops);
1540 if (r < 0)
1541 return log_oom();
1542
1543 l = ordered_hashmap_get(members, resolved_id);
1544 if (l) {
1545 /* A list for this group name already exists, let's append to it */
1546 r = strv_push(&l, resolved_name);
1547 if (r < 0)
1548 return log_oom();
1549
1550 resolved_name = NULL;
1551
1552 assert_se(ordered_hashmap_update(members, resolved_id, l) >= 0);
1553 } else {
1554 /* No list for this group name exists yet, create one */
1555
1556 l = new0(char *, 2);
1557 if (!l)
1558 return -ENOMEM;
1559
1560 l[0] = resolved_name;
1561 l[1] = NULL;
1562
1563 r = ordered_hashmap_put(members, resolved_id, l);
1564 if (r < 0) {
1565 free(l);
1566 return log_oom();
1567 }
1568
1569 resolved_id = resolved_name = NULL;
1570 }
1571
1572 return 0;
1573 }
1574
1575 case ADD_USER:
1576 if (!name) {
1577 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname, line);
1578 return -EINVAL;
1579 }
1580
1581 r = ordered_hashmap_ensure_allocated(&users, &string_hash_ops);
1582 if (r < 0)
1583 return log_oom();
1584
1585 i = new0(Item, 1);
1586 if (!i)
1587 return log_oom();
1588
1589 if (resolved_id) {
1590 if (path_is_absolute(resolved_id)) {
1591 i->uid_path = TAKE_PTR(resolved_id);
1592 path_simplify(i->uid_path, false);
1593 } else {
1594 _cleanup_free_ char *uid = NULL, *gid = NULL;
1595 if (split_pair(resolved_id, ":", &uid, &gid) == 0) {
1596 r = parse_gid(gid, &i->gid);
1597 if (r < 0)
1598 return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
1599 i->gid_set = true;
1600 i->id_set_strict = true;
1601 free_and_replace(resolved_id, uid);
1602 }
1603 if (!streq(resolved_id, "-")) {
1604 r = parse_uid(resolved_id, &i->uid);
1605 if (r < 0)
1606 return log_error_errno(r, "Failed to parse UID: '%s': %m", id);
1607 i->uid_set = true;
1608 }
1609 }
1610 }
1611
1612 i->description = TAKE_PTR(resolved_description);
1613 i->home = TAKE_PTR(resolved_home);
1614 i->shell = TAKE_PTR(resolved_shell);
1615
1616 h = users;
1617 break;
1618
1619 case ADD_GROUP:
1620 if (!name) {
1621 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname, line);
1622 return -EINVAL;
1623 }
1624
1625 if (description || home || shell) {
1626 log_error("[%s:%u] Lines of type '%c' don't take a %s field.",
1627 fname, line, action[0],
1628 description ? "GECOS" : home ? "home directory" : "login shell");
1629 return -EINVAL;
1630 }
1631
1632 r = ordered_hashmap_ensure_allocated(&groups, &string_hash_ops);
1633 if (r < 0)
1634 return log_oom();
1635
1636 i = new0(Item, 1);
1637 if (!i)
1638 return log_oom();
1639
1640 if (resolved_id) {
1641 if (path_is_absolute(resolved_id)) {
1642 i->gid_path = TAKE_PTR(resolved_id);
1643 path_simplify(i->gid_path, false);
1644 } else {
1645 r = parse_gid(resolved_id, &i->gid);
1646 if (r < 0)
1647 return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
1648
1649 i->gid_set = true;
1650 }
1651 }
1652
1653 h = groups;
1654 break;
1655
1656 default:
1657 return -EBADMSG;
1658 }
1659
1660 i->type = action[0];
1661 i->name = TAKE_PTR(resolved_name);
1662
1663 existing = ordered_hashmap_get(h, i->name);
1664 if (existing) {
1665
1666 /* Two identical items are fine */
1667 if (!item_equal(existing, i))
1668 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1669
1670 return 0;
1671 }
1672
1673 r = ordered_hashmap_put(h, i->name, i);
1674 if (r < 0)
1675 return log_oom();
1676
1677 i = NULL;
1678 return 0;
1679 }
1680
1681 static int read_config_file(const char *fn, bool ignore_enoent) {
1682 _cleanup_fclose_ FILE *rf = NULL;
1683 FILE *f = NULL;
1684 unsigned v = 0;
1685 int r = 0;
1686
1687 assert(fn);
1688
1689 if (streq(fn, "-"))
1690 f = stdin;
1691 else {
1692 r = search_and_fopen(fn, "re", arg_root, (const char**) CONF_PATHS_STRV("sysusers.d"), &rf);
1693 if (r < 0) {
1694 if (ignore_enoent && r == -ENOENT)
1695 return 0;
1696
1697 return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
1698 }
1699
1700 f = rf;
1701 }
1702
1703 for (;;) {
1704 _cleanup_free_ char *line = NULL;
1705 char *l;
1706 int k;
1707
1708 k = read_line(f, LONG_LINE_MAX, &line);
1709 if (k < 0)
1710 return log_error_errno(k, "Failed to read '%s': %m", fn);
1711 if (k == 0)
1712 break;
1713
1714 v++;
1715
1716 l = strstrip(line);
1717 if (IN_SET(*l, 0, '#'))
1718 continue;
1719
1720 k = parse_line(fn, v, l);
1721 if (k < 0 && r == 0)
1722 r = k;
1723 }
1724
1725 if (ferror(f)) {
1726 log_error_errno(errno, "Failed to read from file %s: %m", fn);
1727 if (r == 0)
1728 r = -EIO;
1729 }
1730
1731 return r;
1732 }
1733
1734 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1735 char *name;
1736
1737 for (;;) {
1738 name = hashmap_first(by_id);
1739 if (!name)
1740 break;
1741
1742 hashmap_remove(by_name, name);
1743
1744 hashmap_steal_first_key(by_id);
1745 free(name);
1746 }
1747
1748 while ((name = hashmap_steal_first_key(by_name)))
1749 free(name);
1750
1751 hashmap_free(by_name);
1752 hashmap_free(by_id);
1753 }
1754
1755 static int cat_config(void) {
1756 _cleanup_strv_free_ char **files = NULL;
1757 int r;
1758
1759 r = conf_files_list_with_replacement(arg_root, CONF_PATHS_STRV("sysusers.d"), arg_replace, &files, NULL);
1760 if (r < 0)
1761 return r;
1762
1763 (void) pager_open(arg_pager_flags);
1764
1765 return cat_files(NULL, files, 0);
1766 }
1767
1768 static int help(void) {
1769 _cleanup_free_ char *link = NULL;
1770 int r;
1771
1772 r = terminal_urlify_man("systemd-sysusers.service", "8", &link);
1773 if (r < 0)
1774 return log_oom();
1775
1776 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1777 "Creates system user accounts.\n\n"
1778 " -h --help Show this help\n"
1779 " --version Show package version\n"
1780 " --cat-config Show configuration files\n"
1781 " --root=PATH Operate on an alternate filesystem root\n"
1782 " --replace=PATH Treat arguments as replacement for PATH\n"
1783 " --inline Treat arguments as configuration lines\n"
1784 " --no-pager Do not pipe output into a pager\n"
1785 "\nSee the %s for details.\n"
1786 , program_invocation_short_name
1787 , link
1788 );
1789
1790 return 0;
1791 }
1792
1793 static int parse_argv(int argc, char *argv[]) {
1794
1795 enum {
1796 ARG_VERSION = 0x100,
1797 ARG_CAT_CONFIG,
1798 ARG_ROOT,
1799 ARG_REPLACE,
1800 ARG_INLINE,
1801 ARG_NO_PAGER,
1802 };
1803
1804 static const struct option options[] = {
1805 { "help", no_argument, NULL, 'h' },
1806 { "version", no_argument, NULL, ARG_VERSION },
1807 { "cat-config", no_argument, NULL, ARG_CAT_CONFIG },
1808 { "root", required_argument, NULL, ARG_ROOT },
1809 { "replace", required_argument, NULL, ARG_REPLACE },
1810 { "inline", no_argument, NULL, ARG_INLINE },
1811 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1812 {}
1813 };
1814
1815 int c, r;
1816
1817 assert(argc >= 0);
1818 assert(argv);
1819
1820 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
1821
1822 switch (c) {
1823
1824 case 'h':
1825 return help();
1826
1827 case ARG_VERSION:
1828 return version();
1829
1830 case ARG_CAT_CONFIG:
1831 arg_cat_config = true;
1832 break;
1833
1834 case ARG_ROOT:
1835 r = parse_path_argument_and_warn(optarg, true, &arg_root);
1836 if (r < 0)
1837 return r;
1838 break;
1839
1840 case ARG_REPLACE:
1841 if (!path_is_absolute(optarg) ||
1842 !endswith(optarg, ".conf")) {
1843 log_error("The argument to --replace= must an absolute path to a config file");
1844 return -EINVAL;
1845 }
1846
1847 arg_replace = optarg;
1848 break;
1849
1850 case ARG_INLINE:
1851 arg_inline = true;
1852 break;
1853
1854 case ARG_NO_PAGER:
1855 arg_pager_flags |= PAGER_DISABLE;
1856 break;
1857
1858 case '?':
1859 return -EINVAL;
1860
1861 default:
1862 assert_not_reached("Unhandled option");
1863 }
1864
1865 if (arg_replace && arg_cat_config) {
1866 log_error("Option --replace= is not supported with --cat-config");
1867 return -EINVAL;
1868 }
1869
1870 if (arg_replace && optind >= argc) {
1871 log_error("When --replace= is given, some configuration items must be specified");
1872 return -EINVAL;
1873 }
1874
1875 return 1;
1876 }
1877
1878 static int parse_arguments(char **args) {
1879 char **arg;
1880 unsigned pos = 1;
1881 int r;
1882
1883 STRV_FOREACH(arg, args) {
1884 if (arg_inline)
1885 /* Use (argument):n, where n==1 for the first positional arg */
1886 r = parse_line("(argument)", pos, *arg);
1887 else
1888 r = read_config_file(*arg, false);
1889 if (r < 0)
1890 return r;
1891
1892 pos++;
1893 }
1894
1895 return 0;
1896 }
1897
1898 static int read_config_files(char **args) {
1899 _cleanup_strv_free_ char **files = NULL;
1900 _cleanup_free_ char *p = NULL;
1901 char **f;
1902 int r;
1903
1904 r = conf_files_list_with_replacement(arg_root, CONF_PATHS_STRV("sysusers.d"), arg_replace, &files, &p);
1905 if (r < 0)
1906 return r;
1907
1908 STRV_FOREACH(f, files)
1909 if (p && path_equal(*f, p)) {
1910 log_debug("Parsing arguments at position \"%s\"…", *f);
1911
1912 r = parse_arguments(args);
1913 if (r < 0)
1914 return r;
1915 } else {
1916 log_debug("Reading config file \"%s\"…", *f);
1917
1918 /* Just warn, ignore result otherwise */
1919 (void) read_config_file(*f, true);
1920 }
1921
1922 return 0;
1923 }
1924
1925 int main(int argc, char *argv[]) {
1926 _cleanup_close_ int lock = -1;
1927 Iterator iterator;
1928 int r;
1929 Item *i;
1930 char *n;
1931
1932 r = parse_argv(argc, argv);
1933 if (r <= 0)
1934 goto finish;
1935
1936 log_setup_service();
1937
1938 if (arg_cat_config) {
1939 r = cat_config();
1940 goto finish;
1941 }
1942
1943 umask(0022);
1944
1945 r = mac_selinux_init();
1946 if (r < 0) {
1947 log_error_errno(r, "SELinux setup failed: %m");
1948 goto finish;
1949 }
1950
1951 /* If command line arguments are specified along with --replace, read all
1952 * configuration files and insert the positional arguments at the specified
1953 * place. Otherwise, if command line arguments are specified, execute just
1954 * them, and finally, without --replace= or any positional arguments, just
1955 * read configuration and execute it.
1956 */
1957 if (arg_replace || optind >= argc)
1958 r = read_config_files(argv + optind);
1959 else
1960 r = parse_arguments(argv + optind);
1961 if (r < 0)
1962 goto finish;
1963
1964 /* Let's tell nss-systemd not to synthesize the "root" and "nobody" entries for it, so that our detection
1965 * whether the names or UID/GID area already used otherwise doesn't get confused. After all, even though
1966 * nss-systemd synthesizes these users/groups, they should still appear in /etc/passwd and /etc/group, as the
1967 * synthesizing logic is merely supposed to be fallback for cases where we run with a completely unpopulated
1968 * /etc. */
1969 if (setenv("SYSTEMD_NSS_BYPASS_SYNTHETIC", "1", 1) < 0) {
1970 r = log_error_errno(errno, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m");
1971 goto finish;
1972 }
1973
1974 if (!uid_range) {
1975 /* Default to default range of 1..SYSTEM_UID_MAX */
1976 r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX);
1977 if (r < 0) {
1978 log_oom();
1979 goto finish;
1980 }
1981 }
1982
1983 r = add_implicit();
1984 if (r < 0)
1985 goto finish;
1986
1987 lock = take_etc_passwd_lock(arg_root);
1988 if (lock < 0) {
1989 log_error_errno(lock, "Failed to take /etc/passwd lock: %m");
1990 goto finish;
1991 }
1992
1993 r = load_user_database();
1994 if (r < 0) {
1995 log_error_errno(r, "Failed to load user database: %m");
1996 goto finish;
1997 }
1998
1999 r = load_group_database();
2000 if (r < 0) {
2001 log_error_errno(r, "Failed to read group database: %m");
2002 goto finish;
2003 }
2004
2005 ORDERED_HASHMAP_FOREACH(i, groups, iterator)
2006 (void) process_item(i);
2007
2008 ORDERED_HASHMAP_FOREACH(i, users, iterator)
2009 (void) process_item(i);
2010
2011 r = write_files();
2012 if (r < 0)
2013 log_error_errno(r, "Failed to write files: %m");
2014
2015 finish:
2016 pager_close();
2017
2018 ordered_hashmap_free_with_destructor(groups, item_free);
2019 ordered_hashmap_free_with_destructor(users, item_free);
2020
2021 while ((n = ordered_hashmap_first_key(members))) {
2022 strv_free(ordered_hashmap_steal_first(members));
2023 free(n);
2024 }
2025 ordered_hashmap_free(members);
2026
2027 ordered_hashmap_free(todo_uids);
2028 ordered_hashmap_free(todo_gids);
2029
2030 free_database(database_user, database_uid);
2031 free_database(database_group, database_gid);
2032
2033 free(uid_range);
2034
2035 free(arg_root);
2036
2037 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2038 }