]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/sysusers/sysusers.c
tree-wide: use free_and_strdup()
[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 r = free_and_strdup(&i->description, p->pw_gecos);
895 if (r < 0)
896 return log_oom();
897
898 return 0;
899 }
900 if (!IN_SET(errno, 0, ENOENT))
901 return log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name);
902 }
903
904 /* Try to use the suggested numeric uid */
905 if (i->uid_set) {
906 r = uid_is_ok(i->uid, i->name);
907 if (r < 0)
908 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
909 if (r == 0) {
910 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
911 i->uid_set = false;
912 }
913 }
914
915 /* If that didn't work, try to read it from the specified path */
916 if (!i->uid_set) {
917 uid_t c;
918
919 if (read_id_from_file(i, &c, NULL) > 0) {
920
921 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
922 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
923 else {
924 r = uid_is_ok(c, i->name);
925 if (r < 0)
926 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
927 else if (r > 0) {
928 i->uid = c;
929 i->uid_set = true;
930 } else
931 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
932 }
933 }
934 }
935
936 /* Otherwise try to reuse the group ID */
937 if (!i->uid_set && i->gid_set) {
938 r = uid_is_ok((uid_t) i->gid, i->name);
939 if (r < 0)
940 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
941 if (r > 0) {
942 i->uid = (uid_t) i->gid;
943 i->uid_set = true;
944 }
945 }
946
947 /* And if that didn't work either, let's try to find a free one */
948 if (!i->uid_set) {
949 for (;;) {
950 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
951 if (r < 0) {
952 log_error("No free user ID available for %s.", i->name);
953 return r;
954 }
955
956 r = uid_is_ok(search_uid, i->name);
957 if (r < 0)
958 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
959 else if (r > 0)
960 break;
961 }
962
963 i->uid_set = true;
964 i->uid = search_uid;
965 }
966
967 r = hashmap_ensure_allocated(&todo_uids, NULL);
968 if (r < 0)
969 return log_oom();
970
971 r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
972 if (r < 0)
973 return log_oom();
974
975 i->todo_user = true;
976 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
977
978 return 0;
979 }
980
981 static int gid_is_ok(gid_t gid) {
982 struct group *g;
983 struct passwd *p;
984
985 if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
986 return 0;
987
988 /* Avoid reusing gids that are already used by a different user */
989 if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
990 return 0;
991
992 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
993 return 0;
994
995 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
996 return 0;
997
998 if (!arg_root) {
999 errno = 0;
1000 g = getgrgid(gid);
1001 if (g)
1002 return 0;
1003 if (!IN_SET(errno, 0, ENOENT))
1004 return -errno;
1005
1006 errno = 0;
1007 p = getpwuid((uid_t) gid);
1008 if (p)
1009 return 0;
1010 if (!IN_SET(errno, 0, ENOENT))
1011 return -errno;
1012 }
1013
1014 return 1;
1015 }
1016
1017 static int add_group(Item *i) {
1018 void *z;
1019 int r;
1020
1021 assert(i);
1022
1023 /* Check the database directly */
1024 z = hashmap_get(database_group, i->name);
1025 if (z) {
1026 log_debug("Group %s already exists.", i->name);
1027 i->gid = PTR_TO_GID(z);
1028 i->gid_set = true;
1029 return 0;
1030 }
1031
1032 /* Also check NSS */
1033 if (!arg_root) {
1034 struct group *g;
1035
1036 errno = 0;
1037 g = getgrnam(i->name);
1038 if (g) {
1039 log_debug("Group %s already exists.", i->name);
1040 i->gid = g->gr_gid;
1041 i->gid_set = true;
1042 return 0;
1043 }
1044 if (!IN_SET(errno, 0, ENOENT))
1045 return log_error_errno(errno, "Failed to check if group %s already exists: %m", i->name);
1046 }
1047
1048 /* Try to use the suggested numeric gid */
1049 if (i->gid_set) {
1050 r = gid_is_ok(i->gid);
1051 if (r < 0)
1052 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1053 if (r == 0) {
1054 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
1055 i->gid_set = false;
1056 }
1057 }
1058
1059 /* Try to reuse the numeric uid, if there's one */
1060 if (!i->gid_set && i->uid_set) {
1061 r = gid_is_ok((gid_t) i->uid);
1062 if (r < 0)
1063 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1064 if (r > 0) {
1065 i->gid = (gid_t) i->uid;
1066 i->gid_set = true;
1067 }
1068 }
1069
1070 /* If that didn't work, try to read it from the specified path */
1071 if (!i->gid_set) {
1072 gid_t c;
1073
1074 if (read_id_from_file(i, NULL, &c) > 0) {
1075
1076 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
1077 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
1078 else {
1079 r = gid_is_ok(c);
1080 if (r < 0)
1081 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1082 else if (r > 0) {
1083 i->gid = c;
1084 i->gid_set = true;
1085 } else
1086 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
1087 }
1088 }
1089 }
1090
1091 /* And if that didn't work either, let's try to find a free one */
1092 if (!i->gid_set) {
1093 for (;;) {
1094 /* We look for new GIDs in the UID pool! */
1095 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
1096 if (r < 0) {
1097 log_error("No free group ID available for %s.", i->name);
1098 return r;
1099 }
1100
1101 r = gid_is_ok(search_uid);
1102 if (r < 0)
1103 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1104 else if (r > 0)
1105 break;
1106 }
1107
1108 i->gid_set = true;
1109 i->gid = search_uid;
1110 }
1111
1112 r = hashmap_ensure_allocated(&todo_gids, NULL);
1113 if (r < 0)
1114 return log_oom();
1115
1116 r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
1117 if (r < 0)
1118 return log_oom();
1119
1120 i->todo_group = true;
1121 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
1122
1123 return 0;
1124 }
1125
1126 static int process_item(Item *i) {
1127 int r;
1128
1129 assert(i);
1130
1131 switch (i->type) {
1132
1133 case ADD_USER:
1134 r = add_group(i);
1135 if (r < 0)
1136 return r;
1137
1138 return add_user(i);
1139
1140 case ADD_GROUP: {
1141 Item *j;
1142
1143 j = hashmap_get(users, i->name);
1144 if (j) {
1145 /* There's already user to be created for this
1146 * name, let's process that in one step */
1147
1148 if (i->gid_set) {
1149 j->gid = i->gid;
1150 j->gid_set = true;
1151 }
1152
1153 if (i->gid_path) {
1154 r = free_and_strdup(&j->gid_path, i->gid_path);
1155 if (r < 0)
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, 0, &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 }