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