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