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