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