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