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