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