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