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