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