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