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