]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/sysusers/sysusers.c
util-lib: split out fd-related operations into fd-util.[ch]
[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 "conf-files.h"
30 #include "copy.h"
31 #include "fileio-label.h"
32 #include "formats-util.h"
33 #include "hashmap.h"
34 #include "path-util.h"
35 #include "selinux-util.h"
36 #include "smack-util.h"
37 #include "specifier.h"
38 #include "string-util.h"
39 #include "strv.h"
40 #include "uid-range.h"
41 #include "utf8.h"
42 #include "util.h"
43 #include "fd-util.h"
44
45 typedef enum ItemType {
46 ADD_USER = 'u',
47 ADD_GROUP = 'g',
48 ADD_MEMBER = 'm',
49 ADD_RANGE = 'r',
50 } ItemType;
51 typedef struct Item {
52 ItemType type;
53
54 char *name;
55 char *uid_path;
56 char *gid_path;
57 char *description;
58 char *home;
59
60 gid_t gid;
61 uid_t uid;
62
63 bool gid_set:1;
64 bool uid_set:1;
65
66 bool todo_user:1;
67 bool todo_group:1;
68 } Item;
69
70 static char *arg_root = NULL;
71
72 static const char conf_file_dirs[] = CONF_DIRS_NULSTR("sysusers");
73
74 static Hashmap *users = NULL, *groups = NULL;
75 static Hashmap *todo_uids = NULL, *todo_gids = NULL;
76 static Hashmap *members = NULL;
77
78 static Hashmap *database_uid = NULL, *database_user = NULL;
79 static Hashmap *database_gid = NULL, *database_group = NULL;
80
81 static uid_t search_uid = UID_INVALID;
82 static UidRange *uid_range = NULL;
83 static unsigned n_uid_range = 0;
84
85 static int load_user_database(void) {
86 _cleanup_fclose_ FILE *f = NULL;
87 const char *passwd_path;
88 struct passwd *pw;
89 int r;
90
91 passwd_path = prefix_roota(arg_root, "/etc/passwd");
92 f = fopen(passwd_path, "re");
93 if (!f)
94 return errno == ENOENT ? 0 : -errno;
95
96 r = hashmap_ensure_allocated(&database_user, &string_hash_ops);
97 if (r < 0)
98 return r;
99
100 r = hashmap_ensure_allocated(&database_uid, NULL);
101 if (r < 0)
102 return r;
103
104 errno = 0;
105 while ((pw = fgetpwent(f))) {
106 char *n;
107 int k, q;
108
109 n = strdup(pw->pw_name);
110 if (!n)
111 return -ENOMEM;
112
113 k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
114 if (k < 0 && k != -EEXIST) {
115 free(n);
116 return k;
117 }
118
119 q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
120 if (q < 0 && q != -EEXIST) {
121 if (k < 0)
122 free(n);
123 return q;
124 }
125
126 if (q < 0 && k < 0)
127 free(n);
128
129 errno = 0;
130 }
131 if (!IN_SET(errno, 0, ENOENT))
132 return -errno;
133
134 return 0;
135 }
136
137 static int load_group_database(void) {
138 _cleanup_fclose_ FILE *f = NULL;
139 const char *group_path;
140 struct group *gr;
141 int r;
142
143 group_path = prefix_roota(arg_root, "/etc/group");
144 f = fopen(group_path, "re");
145 if (!f)
146 return errno == ENOENT ? 0 : -errno;
147
148 r = hashmap_ensure_allocated(&database_group, &string_hash_ops);
149 if (r < 0)
150 return r;
151
152 r = hashmap_ensure_allocated(&database_gid, NULL);
153 if (r < 0)
154 return r;
155
156 errno = 0;
157 while ((gr = fgetgrent(f))) {
158 char *n;
159 int k, q;
160
161 n = strdup(gr->gr_name);
162 if (!n)
163 return -ENOMEM;
164
165 k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
166 if (k < 0 && k != -EEXIST) {
167 free(n);
168 return k;
169 }
170
171 q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
172 if (q < 0 && q != -EEXIST) {
173 if (k < 0)
174 free(n);
175 return q;
176 }
177
178 if (q < 0 && k < 0)
179 free(n);
180
181 errno = 0;
182 }
183 if (!IN_SET(errno, 0, ENOENT))
184 return -errno;
185
186 return 0;
187 }
188
189 static int make_backup(const char *target, const char *x) {
190 _cleanup_close_ int src = -1;
191 _cleanup_fclose_ FILE *dst = NULL;
192 char *backup, *temp;
193 struct timespec ts[2];
194 struct stat st;
195 int r;
196
197 src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
198 if (src < 0) {
199 if (errno == ENOENT) /* No backup necessary... */
200 return 0;
201
202 return -errno;
203 }
204
205 if (fstat(src, &st) < 0)
206 return -errno;
207
208 r = fopen_temporary_label(target, x, &dst, &temp);
209 if (r < 0)
210 return r;
211
212 r = copy_bytes(src, fileno(dst), (uint64_t) -1, true);
213 if (r < 0)
214 goto fail;
215
216 /* Don't fail on chmod() or chown(). If it stays owned by us
217 * and/or unreadable by others, then it isn't too bad... */
218
219 backup = strjoina(x, "-");
220
221 /* Copy over the access mask */
222 if (fchmod(fileno(dst), st.st_mode & 07777) < 0)
223 log_warning_errno(errno, "Failed to change mode on %s: %m", backup);
224
225 if (fchown(fileno(dst), st.st_uid, st.st_gid)< 0)
226 log_warning_errno(errno, "Failed to change ownership of %s: %m", backup);
227
228 ts[0] = st.st_atim;
229 ts[1] = st.st_mtim;
230 if (futimens(fileno(dst), ts) < 0)
231 log_warning_errno(errno, "Failed to fix access and modification time of %s: %m", backup);
232
233 if (rename(temp, backup) < 0)
234 goto fail;
235
236 return 0;
237
238 fail:
239 unlink(temp);
240 return r;
241 }
242
243 static int putgrent_with_members(const struct group *gr, FILE *group) {
244 char **a;
245
246 assert(gr);
247 assert(group);
248
249 a = hashmap_get(members, gr->gr_name);
250 if (a) {
251 _cleanup_strv_free_ char **l = NULL;
252 bool added = false;
253 char **i;
254
255 l = strv_copy(gr->gr_mem);
256 if (!l)
257 return -ENOMEM;
258
259 STRV_FOREACH(i, a) {
260 if (strv_find(l, *i))
261 continue;
262
263 if (strv_extend(&l, *i) < 0)
264 return -ENOMEM;
265
266 added = true;
267 }
268
269 if (added) {
270 struct group t;
271
272 strv_uniq(l);
273 strv_sort(l);
274
275 t = *gr;
276 t.gr_mem = l;
277
278 errno = 0;
279 if (putgrent(&t, group) != 0)
280 return errno ? -errno : -EIO;
281
282 return 1;
283 }
284 }
285
286 errno = 0;
287 if (putgrent(gr, group) != 0)
288 return errno ? -errno : -EIO;
289
290 return 0;
291 }
292
293 static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
294 char **a;
295
296 assert(sg);
297 assert(gshadow);
298
299 a = hashmap_get(members, sg->sg_namp);
300 if (a) {
301 _cleanup_strv_free_ char **l = NULL;
302 bool added = false;
303 char **i;
304
305 l = strv_copy(sg->sg_mem);
306 if (!l)
307 return -ENOMEM;
308
309 STRV_FOREACH(i, a) {
310 if (strv_find(l, *i))
311 continue;
312
313 if (strv_extend(&l, *i) < 0)
314 return -ENOMEM;
315
316 added = true;
317 }
318
319 if (added) {
320 struct sgrp t;
321
322 strv_uniq(l);
323 strv_sort(l);
324
325 t = *sg;
326 t.sg_mem = l;
327
328 errno = 0;
329 if (putsgent(&t, gshadow) != 0)
330 return errno ? -errno : -EIO;
331
332 return 1;
333 }
334 }
335
336 errno = 0;
337 if (putsgent(sg, gshadow) != 0)
338 return errno ? -errno : -EIO;
339
340 return 0;
341 }
342
343 static int sync_rights(FILE *from, FILE *to) {
344 struct stat st;
345
346 if (fstat(fileno(from), &st) < 0)
347 return -errno;
348
349 if (fchmod(fileno(to), st.st_mode & 07777) < 0)
350 return -errno;
351
352 if (fchown(fileno(to), st.st_uid, st.st_gid) < 0)
353 return -errno;
354
355 return 0;
356 }
357
358 static int rename_and_apply_smack(const char *temp_path, const char *dest_path) {
359 int r = 0;
360 if (rename(temp_path, dest_path) < 0)
361 return -errno;
362
363 #ifdef SMACK_RUN_LABEL
364 r = mac_smack_apply(dest_path, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL);
365 if (r < 0)
366 return r;
367 #endif
368 return r;
369 }
370
371 static int write_files(void) {
372
373 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL;
374 _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL;
375 const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL;
376 bool group_changed = false;
377 Iterator iterator;
378 Item *i;
379 int r;
380
381 if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) {
382 _cleanup_fclose_ FILE *original = NULL;
383
384 /* First we update the actual group list file */
385 group_path = prefix_roota(arg_root, "/etc/group");
386 r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
387 if (r < 0)
388 goto finish;
389
390 original = fopen(group_path, "re");
391 if (original) {
392 struct group *gr;
393
394 r = sync_rights(original, group);
395 if (r < 0)
396 goto finish;
397
398 errno = 0;
399 while ((gr = fgetgrent(original))) {
400 /* Safety checks against name and GID
401 * collisions. Normally, this should
402 * be unnecessary, but given that we
403 * look at the entries anyway here,
404 * let's make an extra verification
405 * step that we don't generate
406 * duplicate entries. */
407
408 i = hashmap_get(groups, gr->gr_name);
409 if (i && i->todo_group) {
410 r = -EEXIST;
411 goto finish;
412 }
413
414 if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
415 r = -EEXIST;
416 goto finish;
417 }
418
419 r = putgrent_with_members(gr, group);
420 if (r < 0)
421 goto finish;
422 if (r > 0)
423 group_changed = true;
424
425 errno = 0;
426 }
427 if (!IN_SET(errno, 0, ENOENT)) {
428 r = -errno;
429 goto finish;
430 }
431
432 } else if (errno != ENOENT) {
433 r = -errno;
434 goto finish;
435 } else if (fchmod(fileno(group), 0644) < 0) {
436 r = -errno;
437 goto finish;
438 }
439
440 HASHMAP_FOREACH(i, todo_gids, iterator) {
441 struct group n = {
442 .gr_name = i->name,
443 .gr_gid = i->gid,
444 .gr_passwd = (char*) "x",
445 };
446
447 r = putgrent_with_members(&n, group);
448 if (r < 0)
449 goto finish;
450
451 group_changed = true;
452 }
453
454 r = fflush_and_check(group);
455 if (r < 0)
456 goto finish;
457
458 if (original) {
459 fclose(original);
460 original = NULL;
461 }
462
463 /* OK, now also update the shadow file for the group list */
464 gshadow_path = prefix_roota(arg_root, "/etc/gshadow");
465 r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
466 if (r < 0)
467 goto finish;
468
469 original = fopen(gshadow_path, "re");
470 if (original) {
471 struct sgrp *sg;
472
473 r = sync_rights(original, gshadow);
474 if (r < 0)
475 goto finish;
476
477 errno = 0;
478 while ((sg = fgetsgent(original))) {
479
480 i = hashmap_get(groups, sg->sg_namp);
481 if (i && i->todo_group) {
482 r = -EEXIST;
483 goto finish;
484 }
485
486 r = putsgent_with_members(sg, gshadow);
487 if (r < 0)
488 goto finish;
489 if (r > 0)
490 group_changed = true;
491
492 errno = 0;
493 }
494 if (!IN_SET(errno, 0, ENOENT)) {
495 r = -errno;
496 goto finish;
497 }
498
499 } else if (errno != ENOENT) {
500 r = -errno;
501 goto finish;
502 } else if (fchmod(fileno(gshadow), 0000) < 0) {
503 r = -errno;
504 goto finish;
505 }
506
507 HASHMAP_FOREACH(i, todo_gids, iterator) {
508 struct sgrp n = {
509 .sg_namp = i->name,
510 .sg_passwd = (char*) "!!",
511 };
512
513 r = putsgent_with_members(&n, gshadow);
514 if (r < 0)
515 goto finish;
516
517 group_changed = true;
518 }
519
520 r = fflush_and_check(gshadow);
521 if (r < 0)
522 goto finish;
523 }
524
525 if (hashmap_size(todo_uids) > 0) {
526 _cleanup_fclose_ FILE *original = NULL;
527 long lstchg;
528
529 /* First we update the user database itself */
530 passwd_path = prefix_roota(arg_root, "/etc/passwd");
531 r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
532 if (r < 0)
533 goto finish;
534
535 original = fopen(passwd_path, "re");
536 if (original) {
537 struct passwd *pw;
538
539 r = sync_rights(original, passwd);
540 if (r < 0)
541 goto finish;
542
543 errno = 0;
544 while ((pw = fgetpwent(original))) {
545
546 i = hashmap_get(users, pw->pw_name);
547 if (i && i->todo_user) {
548 r = -EEXIST;
549 goto finish;
550 }
551
552 if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
553 r = -EEXIST;
554 goto finish;
555 }
556
557 errno = 0;
558 if (putpwent(pw, passwd) < 0) {
559 r = errno ? -errno : -EIO;
560 goto finish;
561 }
562
563 errno = 0;
564 }
565 if (!IN_SET(errno, 0, ENOENT)) {
566 r = -errno;
567 goto finish;
568 }
569
570 } else if (errno != ENOENT) {
571 r = -errno;
572 goto finish;
573 } else if (fchmod(fileno(passwd), 0644) < 0) {
574 r = -errno;
575 goto finish;
576 }
577
578 HASHMAP_FOREACH(i, todo_uids, iterator) {
579 struct passwd n = {
580 .pw_name = i->name,
581 .pw_uid = i->uid,
582 .pw_gid = i->gid,
583 .pw_gecos = i->description,
584
585 /* "x" means the password is stored in
586 * the shadow file */
587 .pw_passwd = (char*) "x",
588
589 /* We default to the root directory as home */
590 .pw_dir = i->home ? i->home : (char*) "/",
591
592 /* Initialize the shell to nologin,
593 * with one exception: for root we
594 * patch in something special */
595 .pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
596 };
597
598 errno = 0;
599 if (putpwent(&n, passwd) != 0) {
600 r = errno ? -errno : -EIO;
601 goto finish;
602 }
603 }
604
605 r = fflush_and_check(passwd);
606 if (r < 0)
607 goto finish;
608
609 if (original) {
610 fclose(original);
611 original = NULL;
612 }
613
614 /* The we update the shadow database */
615 shadow_path = prefix_roota(arg_root, "/etc/shadow");
616 r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
617 if (r < 0)
618 goto finish;
619
620 lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
621
622 original = fopen(shadow_path, "re");
623 if (original) {
624 struct spwd *sp;
625
626 r = sync_rights(original, shadow);
627 if (r < 0)
628 goto finish;
629
630 errno = 0;
631 while ((sp = fgetspent(original))) {
632
633 i = hashmap_get(users, sp->sp_namp);
634 if (i && i->todo_user) {
635 /* we will update the existing entry */
636 sp->sp_lstchg = lstchg;
637
638 /* only the /etc/shadow stage is left, so we can
639 * safely remove the item from the todo set */
640 i->todo_user = false;
641 hashmap_remove(todo_uids, UID_TO_PTR(i->uid));
642 }
643
644 errno = 0;
645 if (putspent(sp, shadow) < 0) {
646 r = errno ? -errno : -EIO;
647 goto finish;
648 }
649
650 errno = 0;
651 }
652 if (!IN_SET(errno, 0, ENOENT)) {
653 r = -errno;
654 goto finish;
655 }
656 } else if (errno != ENOENT) {
657 r = -errno;
658 goto finish;
659 } else if (fchmod(fileno(shadow), 0000) < 0) {
660 r = -errno;
661 goto finish;
662 }
663
664 HASHMAP_FOREACH(i, todo_uids, iterator) {
665 struct spwd n = {
666 .sp_namp = i->name,
667 .sp_pwdp = (char*) "!!",
668 .sp_lstchg = lstchg,
669 .sp_min = -1,
670 .sp_max = -1,
671 .sp_warn = -1,
672 .sp_inact = -1,
673 .sp_expire = -1,
674 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
675 };
676
677 errno = 0;
678 if (putspent(&n, shadow) != 0) {
679 r = errno ? -errno : -EIO;
680 goto finish;
681 }
682 }
683
684 r = fflush_and_check(shadow);
685 if (r < 0)
686 goto finish;
687 }
688
689 /* Make a backup of the old files */
690 if (group_changed) {
691 if (group) {
692 r = make_backup("/etc/group", group_path);
693 if (r < 0)
694 goto finish;
695 }
696 if (gshadow) {
697 r = make_backup("/etc/gshadow", gshadow_path);
698 if (r < 0)
699 goto finish;
700 }
701 }
702
703 if (passwd) {
704 r = make_backup("/etc/passwd", passwd_path);
705 if (r < 0)
706 goto finish;
707 }
708 if (shadow) {
709 r = make_backup("/etc/shadow", shadow_path);
710 if (r < 0)
711 goto finish;
712 }
713
714 /* And make the new files count */
715 if (group_changed) {
716 if (group) {
717 r = rename_and_apply_smack(group_tmp, group_path);
718 if (r < 0)
719 goto finish;
720
721 group_tmp = mfree(group_tmp);
722 }
723 if (gshadow) {
724 r = rename_and_apply_smack(gshadow_tmp, gshadow_path);
725 if (r < 0)
726 goto finish;
727
728 gshadow_tmp = mfree(gshadow_tmp);
729 }
730 }
731
732 if (passwd) {
733 r = rename_and_apply_smack(passwd_tmp, passwd_path);
734 if (r < 0)
735 goto finish;
736
737 passwd_tmp = mfree(passwd_tmp);
738 }
739 if (shadow) {
740 r = rename_and_apply_smack(shadow_tmp, shadow_path);
741 if (r < 0)
742 goto finish;
743
744 shadow_tmp = mfree(shadow_tmp);
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 = prefix_roota(arg_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
893 /* Also check NSS */
894 errno = 0;
895 p = getpwnam(i->name);
896 if (p) {
897 log_debug("User %s already exists.", i->name);
898 i->uid = p->pw_uid;
899 i->uid_set = true;
900
901 r = free_and_strdup(&i->description, p->pw_gecos);
902 if (r < 0)
903 return log_oom();
904
905 return 0;
906 }
907 if (!IN_SET(errno, 0, ENOENT))
908 return log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name);
909 }
910
911 /* Try to use the suggested numeric uid */
912 if (i->uid_set) {
913 r = uid_is_ok(i->uid, i->name);
914 if (r < 0)
915 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
916 if (r == 0) {
917 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
918 i->uid_set = false;
919 }
920 }
921
922 /* If that didn't work, try to read it from the specified path */
923 if (!i->uid_set) {
924 uid_t c;
925
926 if (read_id_from_file(i, &c, NULL) > 0) {
927
928 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
929 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
930 else {
931 r = uid_is_ok(c, i->name);
932 if (r < 0)
933 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
934 else if (r > 0) {
935 i->uid = c;
936 i->uid_set = true;
937 } else
938 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
939 }
940 }
941 }
942
943 /* Otherwise try to reuse the group ID */
944 if (!i->uid_set && i->gid_set) {
945 r = uid_is_ok((uid_t) i->gid, i->name);
946 if (r < 0)
947 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
948 if (r > 0) {
949 i->uid = (uid_t) i->gid;
950 i->uid_set = true;
951 }
952 }
953
954 /* And if that didn't work either, let's try to find a free one */
955 if (!i->uid_set) {
956 for (;;) {
957 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
958 if (r < 0) {
959 log_error("No free user ID available for %s.", i->name);
960 return r;
961 }
962
963 r = uid_is_ok(search_uid, i->name);
964 if (r < 0)
965 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
966 else if (r > 0)
967 break;
968 }
969
970 i->uid_set = true;
971 i->uid = search_uid;
972 }
973
974 r = hashmap_ensure_allocated(&todo_uids, NULL);
975 if (r < 0)
976 return log_oom();
977
978 r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
979 if (r < 0)
980 return log_oom();
981
982 i->todo_user = true;
983 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
984
985 return 0;
986 }
987
988 static int gid_is_ok(gid_t gid) {
989 struct group *g;
990 struct passwd *p;
991
992 if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
993 return 0;
994
995 /* Avoid reusing gids that are already used by a different user */
996 if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
997 return 0;
998
999 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
1000 return 0;
1001
1002 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
1003 return 0;
1004
1005 if (!arg_root) {
1006 errno = 0;
1007 g = getgrgid(gid);
1008 if (g)
1009 return 0;
1010 if (!IN_SET(errno, 0, ENOENT))
1011 return -errno;
1012
1013 errno = 0;
1014 p = getpwuid((uid_t) gid);
1015 if (p)
1016 return 0;
1017 if (!IN_SET(errno, 0, ENOENT))
1018 return -errno;
1019 }
1020
1021 return 1;
1022 }
1023
1024 static int add_group(Item *i) {
1025 void *z;
1026 int r;
1027
1028 assert(i);
1029
1030 /* Check the database directly */
1031 z = hashmap_get(database_group, i->name);
1032 if (z) {
1033 log_debug("Group %s already exists.", i->name);
1034 i->gid = PTR_TO_GID(z);
1035 i->gid_set = true;
1036 return 0;
1037 }
1038
1039 /* Also check NSS */
1040 if (!arg_root) {
1041 struct group *g;
1042
1043 errno = 0;
1044 g = getgrnam(i->name);
1045 if (g) {
1046 log_debug("Group %s already exists.", i->name);
1047 i->gid = g->gr_gid;
1048 i->gid_set = true;
1049 return 0;
1050 }
1051 if (!IN_SET(errno, 0, ENOENT))
1052 return log_error_errno(errno, "Failed to check if group %s already exists: %m", i->name);
1053 }
1054
1055 /* Try to use the suggested numeric gid */
1056 if (i->gid_set) {
1057 r = gid_is_ok(i->gid);
1058 if (r < 0)
1059 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1060 if (r == 0) {
1061 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
1062 i->gid_set = false;
1063 }
1064 }
1065
1066 /* Try to reuse the numeric uid, if there's one */
1067 if (!i->gid_set && i->uid_set) {
1068 r = gid_is_ok((gid_t) i->uid);
1069 if (r < 0)
1070 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1071 if (r > 0) {
1072 i->gid = (gid_t) i->uid;
1073 i->gid_set = true;
1074 }
1075 }
1076
1077 /* If that didn't work, try to read it from the specified path */
1078 if (!i->gid_set) {
1079 gid_t c;
1080
1081 if (read_id_from_file(i, NULL, &c) > 0) {
1082
1083 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
1084 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
1085 else {
1086 r = gid_is_ok(c);
1087 if (r < 0)
1088 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1089 else if (r > 0) {
1090 i->gid = c;
1091 i->gid_set = true;
1092 } else
1093 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
1094 }
1095 }
1096 }
1097
1098 /* And if that didn't work either, let's try to find a free one */
1099 if (!i->gid_set) {
1100 for (;;) {
1101 /* We look for new GIDs in the UID pool! */
1102 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
1103 if (r < 0) {
1104 log_error("No free group ID available for %s.", i->name);
1105 return r;
1106 }
1107
1108 r = gid_is_ok(search_uid);
1109 if (r < 0)
1110 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1111 else if (r > 0)
1112 break;
1113 }
1114
1115 i->gid_set = true;
1116 i->gid = search_uid;
1117 }
1118
1119 r = hashmap_ensure_allocated(&todo_gids, NULL);
1120 if (r < 0)
1121 return log_oom();
1122
1123 r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
1124 if (r < 0)
1125 return log_oom();
1126
1127 i->todo_group = true;
1128 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
1129
1130 return 0;
1131 }
1132
1133 static int process_item(Item *i) {
1134 int r;
1135
1136 assert(i);
1137
1138 switch (i->type) {
1139
1140 case ADD_USER:
1141 r = add_group(i);
1142 if (r < 0)
1143 return r;
1144
1145 return add_user(i);
1146
1147 case ADD_GROUP: {
1148 Item *j;
1149
1150 j = hashmap_get(users, i->name);
1151 if (j) {
1152 /* There's already user to be created for this
1153 * name, let's process that in one step */
1154
1155 if (i->gid_set) {
1156 j->gid = i->gid;
1157 j->gid_set = true;
1158 }
1159
1160 if (i->gid_path) {
1161 r = free_and_strdup(&j->gid_path, i->gid_path);
1162 if (r < 0)
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 = extract_many_words(&p, NULL, EXTRACT_QUOTES, &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 (!isempty(p)) {
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 name = mfree(name);
1422
1423 if (name) {
1424 r = specifier_printf(name, specifier_table, NULL, &resolved_name);
1425 if (r < 0) {
1426 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1427 return r;
1428 }
1429
1430 if (!valid_user_group_name(resolved_name)) {
1431 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
1432 return -EINVAL;
1433 }
1434 }
1435
1436 /* Verify id */
1437 if (isempty(id) || streq(id, "-"))
1438 id = mfree(id);
1439
1440 if (id) {
1441 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1442 if (r < 0) {
1443 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1444 return r;
1445 }
1446 }
1447
1448 /* Verify description */
1449 if (isempty(description) || streq(description, "-"))
1450 description = mfree(description);
1451
1452 if (description) {
1453 if (!valid_gecos(description)) {
1454 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, description);
1455 return -EINVAL;
1456 }
1457 }
1458
1459 /* Verify home */
1460 if (isempty(home) || streq(home, "-"))
1461 home = mfree(home);
1462
1463 if (home) {
1464 if (!valid_home(home)) {
1465 log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, home);
1466 return -EINVAL;
1467 }
1468 }
1469
1470 switch (action[0]) {
1471
1472 case ADD_RANGE:
1473 if (resolved_name) {
1474 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname, line);
1475 return -EINVAL;
1476 }
1477
1478 if (!resolved_id) {
1479 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname, line);
1480 return -EINVAL;
1481 }
1482
1483 if (description) {
1484 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname, line);
1485 return -EINVAL;
1486 }
1487
1488 if (home) {
1489 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname, line);
1490 return -EINVAL;
1491 }
1492
1493 r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id);
1494 if (r < 0) {
1495 log_error("[%s:%u] Invalid UID range %s.", fname, line, resolved_id);
1496 return -EINVAL;
1497 }
1498
1499 return 0;
1500
1501 case ADD_MEMBER: {
1502 char **l;
1503
1504 /* Try to extend an existing member or group item */
1505 if (!name) {
1506 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname, line);
1507 return -EINVAL;
1508 }
1509
1510 if (!resolved_id) {
1511 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1512 return -EINVAL;
1513 }
1514
1515 if (!valid_user_group_name(resolved_id)) {
1516 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1517 return -EINVAL;
1518 }
1519
1520 if (description) {
1521 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
1522 return -EINVAL;
1523 }
1524
1525 if (home) {
1526 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname, line);
1527 return -EINVAL;
1528 }
1529
1530 r = hashmap_ensure_allocated(&members, &string_hash_ops);
1531 if (r < 0)
1532 return log_oom();
1533
1534 l = hashmap_get(members, resolved_id);
1535 if (l) {
1536 /* A list for this group name already exists, let's append to it */
1537 r = strv_push(&l, resolved_name);
1538 if (r < 0)
1539 return log_oom();
1540
1541 resolved_name = NULL;
1542
1543 assert_se(hashmap_update(members, resolved_id, l) >= 0);
1544 } else {
1545 /* No list for this group name exists yet, create one */
1546
1547 l = new0(char *, 2);
1548 if (!l)
1549 return -ENOMEM;
1550
1551 l[0] = resolved_name;
1552 l[1] = NULL;
1553
1554 r = hashmap_put(members, resolved_id, l);
1555 if (r < 0) {
1556 free(l);
1557 return log_oom();
1558 }
1559
1560 resolved_id = resolved_name = NULL;
1561 }
1562
1563 return 0;
1564 }
1565
1566 case ADD_USER:
1567 if (!name) {
1568 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname, line);
1569 return -EINVAL;
1570 }
1571
1572 r = hashmap_ensure_allocated(&users, &string_hash_ops);
1573 if (r < 0)
1574 return log_oom();
1575
1576 i = new0(Item, 1);
1577 if (!i)
1578 return log_oom();
1579
1580 if (resolved_id) {
1581 if (path_is_absolute(resolved_id)) {
1582 i->uid_path = resolved_id;
1583 resolved_id = NULL;
1584
1585 path_kill_slashes(i->uid_path);
1586 } else {
1587 r = parse_uid(resolved_id, &i->uid);
1588 if (r < 0) {
1589 log_error("Failed to parse UID: %s", id);
1590 return -EBADMSG;
1591 }
1592
1593 i->uid_set = true;
1594 }
1595 }
1596
1597 i->description = description;
1598 description = NULL;
1599
1600 i->home = home;
1601 home = NULL;
1602
1603 h = users;
1604 break;
1605
1606 case ADD_GROUP:
1607 if (!name) {
1608 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname, line);
1609 return -EINVAL;
1610 }
1611
1612 if (description) {
1613 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
1614 return -EINVAL;
1615 }
1616
1617 if (home) {
1618 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname, line);
1619 return -EINVAL;
1620 }
1621
1622 r = hashmap_ensure_allocated(&groups, &string_hash_ops);
1623 if (r < 0)
1624 return log_oom();
1625
1626 i = new0(Item, 1);
1627 if (!i)
1628 return log_oom();
1629
1630 if (resolved_id) {
1631 if (path_is_absolute(resolved_id)) {
1632 i->gid_path = resolved_id;
1633 resolved_id = NULL;
1634
1635 path_kill_slashes(i->gid_path);
1636 } else {
1637 r = parse_gid(resolved_id, &i->gid);
1638 if (r < 0) {
1639 log_error("Failed to parse GID: %s", id);
1640 return -EBADMSG;
1641 }
1642
1643 i->gid_set = true;
1644 }
1645 }
1646
1647 h = groups;
1648 break;
1649
1650 default:
1651 return -EBADMSG;
1652 }
1653
1654 i->type = action[0];
1655 i->name = resolved_name;
1656 resolved_name = NULL;
1657
1658 existing = hashmap_get(h, i->name);
1659 if (existing) {
1660
1661 /* Two identical items are fine */
1662 if (!item_equal(existing, i))
1663 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1664
1665 return 0;
1666 }
1667
1668 r = hashmap_put(h, i->name, i);
1669 if (r < 0)
1670 return log_oom();
1671
1672 i = NULL;
1673 return 0;
1674 }
1675
1676 static int read_config_file(const char *fn, bool ignore_enoent) {
1677 _cleanup_fclose_ FILE *rf = NULL;
1678 FILE *f = NULL;
1679 char line[LINE_MAX];
1680 unsigned v = 0;
1681 int r = 0;
1682
1683 assert(fn);
1684
1685 if (streq(fn, "-"))
1686 f = stdin;
1687 else {
1688 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &rf);
1689 if (r < 0) {
1690 if (ignore_enoent && r == -ENOENT)
1691 return 0;
1692
1693 return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
1694 }
1695
1696 f = rf;
1697 }
1698
1699 FOREACH_LINE(line, f, break) {
1700 char *l;
1701 int k;
1702
1703 v++;
1704
1705 l = strstrip(line);
1706 if (*l == '#' || *l == 0)
1707 continue;
1708
1709 k = parse_line(fn, v, l);
1710 if (k < 0 && r == 0)
1711 r = k;
1712 }
1713
1714 if (ferror(f)) {
1715 log_error_errno(errno, "Failed to read from file %s: %m", fn);
1716 if (r == 0)
1717 r = -EIO;
1718 }
1719
1720 return r;
1721 }
1722
1723 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1724 char *name;
1725
1726 for (;;) {
1727 name = hashmap_first(by_id);
1728 if (!name)
1729 break;
1730
1731 hashmap_remove(by_name, name);
1732
1733 hashmap_steal_first_key(by_id);
1734 free(name);
1735 }
1736
1737 while ((name = hashmap_steal_first_key(by_name)))
1738 free(name);
1739
1740 hashmap_free(by_name);
1741 hashmap_free(by_id);
1742 }
1743
1744 static void help(void) {
1745 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1746 "Creates system user accounts.\n\n"
1747 " -h --help Show this help\n"
1748 " --version Show package version\n"
1749 " --root=PATH Operate on an alternate filesystem root\n"
1750 , program_invocation_short_name);
1751 }
1752
1753 static int parse_argv(int argc, char *argv[]) {
1754
1755 enum {
1756 ARG_VERSION = 0x100,
1757 ARG_ROOT,
1758 };
1759
1760 static const struct option options[] = {
1761 { "help", no_argument, NULL, 'h' },
1762 { "version", no_argument, NULL, ARG_VERSION },
1763 { "root", required_argument, NULL, ARG_ROOT },
1764 {}
1765 };
1766
1767 int c, r;
1768
1769 assert(argc >= 0);
1770 assert(argv);
1771
1772 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
1773
1774 switch (c) {
1775
1776 case 'h':
1777 help();
1778 return 0;
1779
1780 case ARG_VERSION:
1781 return version();
1782
1783 case ARG_ROOT:
1784 r = parse_path_argument_and_warn(optarg, true, &arg_root);
1785 if (r < 0)
1786 return r;
1787 break;
1788
1789 case '?':
1790 return -EINVAL;
1791
1792 default:
1793 assert_not_reached("Unhandled option");
1794 }
1795
1796 return 1;
1797 }
1798
1799 int main(int argc, char *argv[]) {
1800
1801 _cleanup_close_ int lock = -1;
1802 Iterator iterator;
1803 int r, k;
1804 Item *i;
1805 char *n;
1806
1807 r = parse_argv(argc, argv);
1808 if (r <= 0)
1809 goto finish;
1810
1811 log_set_target(LOG_TARGET_AUTO);
1812 log_parse_environment();
1813 log_open();
1814
1815 umask(0022);
1816
1817 r = mac_selinux_init(NULL);
1818 if (r < 0) {
1819 log_error_errno(r, "SELinux setup failed: %m");
1820 goto finish;
1821 }
1822
1823 if (optind < argc) {
1824 int j;
1825
1826 for (j = optind; j < argc; j++) {
1827 k = read_config_file(argv[j], false);
1828 if (k < 0 && r == 0)
1829 r = k;
1830 }
1831 } else {
1832 _cleanup_strv_free_ char **files = NULL;
1833 char **f;
1834
1835 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1836 if (r < 0) {
1837 log_error_errno(r, "Failed to enumerate sysusers.d files: %m");
1838 goto finish;
1839 }
1840
1841 STRV_FOREACH(f, files) {
1842 k = read_config_file(*f, true);
1843 if (k < 0 && r == 0)
1844 r = k;
1845 }
1846 }
1847
1848 if (!uid_range) {
1849 /* Default to default range of 1..SYSTEMD_UID_MAX */
1850 r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX);
1851 if (r < 0) {
1852 log_oom();
1853 goto finish;
1854 }
1855 }
1856
1857 r = add_implicit();
1858 if (r < 0)
1859 goto finish;
1860
1861 lock = take_password_lock(arg_root);
1862 if (lock < 0) {
1863 log_error_errno(lock, "Failed to take lock: %m");
1864 goto finish;
1865 }
1866
1867 r = load_user_database();
1868 if (r < 0) {
1869 log_error_errno(r, "Failed to load user database: %m");
1870 goto finish;
1871 }
1872
1873 r = load_group_database();
1874 if (r < 0) {
1875 log_error_errno(r, "Failed to read group database: %m");
1876 goto finish;
1877 }
1878
1879 HASHMAP_FOREACH(i, groups, iterator)
1880 process_item(i);
1881
1882 HASHMAP_FOREACH(i, users, iterator)
1883 process_item(i);
1884
1885 r = write_files();
1886 if (r < 0)
1887 log_error_errno(r, "Failed to write files: %m");
1888
1889 finish:
1890 while ((i = hashmap_steal_first(groups)))
1891 item_free(i);
1892
1893 while ((i = hashmap_steal_first(users)))
1894 item_free(i);
1895
1896 while ((n = hashmap_first_key(members))) {
1897 strv_free(hashmap_steal_first(members));
1898 free(n);
1899 }
1900
1901 hashmap_free(groups);
1902 hashmap_free(users);
1903 hashmap_free(members);
1904 hashmap_free(todo_uids);
1905 hashmap_free(todo_gids);
1906
1907 free_database(database_user, database_uid);
1908 free_database(database_group, database_gid);
1909
1910 free(arg_root);
1911
1912 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1913 }