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