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