]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/sysusers/sysusers.c
sysusers: also add support for NIS entries in /etc/shadow
[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 /* Make sure we keep the NIS entries (if any) at the end. */
433 if (IN_SET(pw->pw_name[0], '+', '-'))
434 break;
435
436 errno = 0;
437 if (putpwent(pw, passwd) < 0)
438 return errno ? -errno : -EIO;
439
440 errno = 0;
441 }
442 if (!IN_SET(errno, 0, ENOENT))
443 return -errno;
444
445 } else {
446 if (errno != ENOENT)
447 return -errno;
448 if (fchmod(fileno(passwd), 0644) < 0)
449 return -errno;
450 }
451
452 ORDERED_HASHMAP_FOREACH(i, todo_uids, iterator) {
453 struct passwd n = {
454 .pw_name = i->name,
455 .pw_uid = i->uid,
456 .pw_gid = i->gid,
457 .pw_gecos = i->description,
458
459 /* "x" means the password is stored in the shadow file */
460 .pw_passwd = (char*) "x",
461
462 /* We default to the root directory as home */
463 .pw_dir = i->home ? i->home : (char*) "/",
464
465 /* Initialize the shell to nologin, with one exception:
466 * for root we patch in something special */
467 .pw_shell = i->shell ?: (char*) default_shell(i->uid),
468 };
469
470 errno = 0;
471 if (putpwent(&n, passwd) != 0)
472 return errno ? -errno : -EIO;
473 }
474
475 /* Append the remaining NIS entries if any */
476 while (pw) {
477 errno = 0;
478 if (putpwent(pw, passwd) < 0)
479 return errno ? -errno : -EIO;
480
481 pw = fgetpwent(original);
482 }
483 if (!IN_SET(errno, 0, ENOENT))
484 return -errno;
485
486 r = fflush_and_check(passwd);
487 if (r < 0)
488 return r;
489
490 *tmpfile = passwd;
491 *tmpfile_path = passwd_tmp;
492 passwd = NULL;
493 passwd_tmp = NULL;
494 return 0;
495 }
496
497 static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char **tmpfile_path) {
498 _cleanup_fclose_ FILE *original = NULL, *shadow = NULL;
499 _cleanup_(unlink_and_freep) char *shadow_tmp = NULL;
500 struct spwd *sp = NULL;
501 Iterator iterator;
502 long lstchg;
503 Item *i;
504 int r;
505
506 if (ordered_hashmap_size(todo_uids) == 0)
507 return 0;
508
509 r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
510 if (r < 0)
511 return r;
512
513 lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
514
515 original = fopen(shadow_path, "re");
516 if (original) {
517
518 r = sync_rights(original, shadow);
519 if (r < 0)
520 return r;
521
522 errno = 0;
523 while ((sp = fgetspent(original))) {
524
525 i = ordered_hashmap_get(users, sp->sp_namp);
526 if (i && i->todo_user) {
527 /* we will update the existing entry */
528 sp->sp_lstchg = lstchg;
529
530 /* only the /etc/shadow stage is left, so we can
531 * safely remove the item from the todo set */
532 i->todo_user = false;
533 ordered_hashmap_remove(todo_uids, UID_TO_PTR(i->uid));
534 }
535
536 errno = 0;
537
538 /* Make sure we keep the NIS entries (if any) at the end. */
539 if (IN_SET(sp->sp_namp[0], '+', '-'))
540 break;
541
542 if (putspent(sp, shadow) < 0)
543 return errno ? -errno : -EIO;
544
545 errno = 0;
546 }
547 if (!IN_SET(errno, 0, ENOENT))
548 return -errno;
549
550 } else {
551 if (errno != ENOENT)
552 return -errno;
553 if (fchmod(fileno(shadow), 0000) < 0)
554 return -errno;
555 }
556
557 ORDERED_HASHMAP_FOREACH(i, todo_uids, iterator) {
558 struct spwd n = {
559 .sp_namp = i->name,
560 .sp_pwdp = (char*) "!!",
561 .sp_lstchg = lstchg,
562 .sp_min = -1,
563 .sp_max = -1,
564 .sp_warn = -1,
565 .sp_inact = -1,
566 .sp_expire = -1,
567 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
568 };
569
570 errno = 0;
571 if (putspent(&n, shadow) != 0)
572 return errno ? -errno : -EIO;
573 }
574 errno = 0;
575
576 /* Append the remaining NIS entries if any */
577 while (sp) {
578 errno = 0;
579 if (putspent(sp, shadow) < 0)
580 return errno ? -errno : -EIO;
581
582 errno = 0;
583 sp = fgetspent(original);
584 }
585 if (!IN_SET(errno, 0, ENOENT))
586 return -errno;
587
588 r = fflush_sync_and_check(shadow);
589 if (r < 0)
590 return r;
591
592 *tmpfile = shadow;
593 *tmpfile_path = shadow_tmp;
594 shadow = NULL;
595 shadow_tmp = NULL;
596 return 0;
597 }
598
599 static int write_temporary_group(const char *group_path, FILE **tmpfile, char **tmpfile_path) {
600 _cleanup_fclose_ FILE *original = NULL, *group = NULL;
601 _cleanup_(unlink_and_freep) char *group_tmp = NULL;
602 bool group_changed = false;
603 struct group *gr = NULL;
604 Iterator iterator;
605 Item *i;
606 int r;
607
608 if (ordered_hashmap_size(todo_gids) == 0 && ordered_hashmap_size(members) == 0)
609 return 0;
610
611 r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
612 if (r < 0)
613 return r;
614
615 original = fopen(group_path, "re");
616 if (original) {
617
618 r = sync_rights(original, group);
619 if (r < 0)
620 return r;
621
622 errno = 0;
623 while ((gr = fgetgrent(original))) {
624 /* Safety checks against name and GID collisions. Normally,
625 * this should be unnecessary, but given that we look at the
626 * entries anyway here, let's make an extra verification
627 * step that we don't generate duplicate entries. */
628
629 i = ordered_hashmap_get(groups, gr->gr_name);
630 if (i && i->todo_group) {
631 log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name);
632 return -EEXIST;
633 }
634
635 if (ordered_hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
636 log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid);
637 return -EEXIST;
638 }
639
640 /* Make sure we keep the NIS entries (if any) at the end. */
641 if (IN_SET(gr->gr_name[0], '+', '-'))
642 break;
643
644 r = putgrent_with_members(gr, group);
645 if (r < 0)
646 return r;
647 if (r > 0)
648 group_changed = true;
649
650 errno = 0;
651 }
652 if (!IN_SET(errno, 0, ENOENT))
653 return -errno;
654
655 } else {
656 if (errno != ENOENT)
657 return -errno;
658 if (fchmod(fileno(group), 0644) < 0)
659 return -errno;
660 }
661
662 ORDERED_HASHMAP_FOREACH(i, todo_gids, iterator) {
663 struct group n = {
664 .gr_name = i->name,
665 .gr_gid = i->gid,
666 .gr_passwd = (char*) "x",
667 };
668
669 r = putgrent_with_members(&n, group);
670 if (r < 0)
671 return r;
672
673 group_changed = true;
674 }
675
676 /* Append the remaining NIS entries if any */
677 while (gr) {
678 errno = 0;
679 if (putgrent(gr, group) != 0)
680 return errno > 0 ? -errno : -EIO;
681
682 gr = fgetgrent(original);
683 }
684 if (!IN_SET(errno, 0, ENOENT))
685 return -errno;
686
687 r = fflush_sync_and_check(group);
688 if (r < 0)
689 return r;
690
691 if (group_changed) {
692 *tmpfile = group;
693 *tmpfile_path = group_tmp;
694 group = NULL;
695 group_tmp = NULL;
696 }
697 return 0;
698 }
699
700 static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, char **tmpfile_path) {
701 #if ENABLE_GSHADOW
702 _cleanup_fclose_ FILE *original = NULL, *gshadow = NULL;
703 _cleanup_(unlink_and_freep) char *gshadow_tmp = NULL;
704 bool group_changed = false;
705 Iterator iterator;
706 Item *i;
707 int r;
708
709 if (ordered_hashmap_size(todo_gids) == 0 && ordered_hashmap_size(members) == 0)
710 return 0;
711
712 r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
713 if (r < 0)
714 return r;
715
716 original = fopen(gshadow_path, "re");
717 if (original) {
718 struct sgrp *sg;
719
720 r = sync_rights(original, gshadow);
721 if (r < 0)
722 return r;
723
724 errno = 0;
725 while ((sg = fgetsgent(original))) {
726
727 i = ordered_hashmap_get(groups, sg->sg_namp);
728 if (i && i->todo_group) {
729 log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp);
730 return -EEXIST;
731 }
732
733 r = putsgent_with_members(sg, gshadow);
734 if (r < 0)
735 return r;
736 if (r > 0)
737 group_changed = true;
738
739 errno = 0;
740 }
741 if (!IN_SET(errno, 0, ENOENT))
742 return -errno;
743
744 } else {
745 if (errno != ENOENT)
746 return -errno;
747 if (fchmod(fileno(gshadow), 0000) < 0)
748 return -errno;
749 }
750
751 ORDERED_HASHMAP_FOREACH(i, todo_gids, iterator) {
752 struct sgrp n = {
753 .sg_namp = i->name,
754 .sg_passwd = (char*) "!!",
755 };
756
757 r = putsgent_with_members(&n, gshadow);
758 if (r < 0)
759 return r;
760
761 group_changed = true;
762 }
763
764 r = fflush_sync_and_check(gshadow);
765 if (r < 0)
766 return r;
767
768 if (group_changed) {
769 *tmpfile = gshadow;
770 *tmpfile_path = gshadow_tmp;
771 gshadow = NULL;
772 gshadow_tmp = NULL;
773 }
774 return 0;
775 #else
776 return 0;
777 #endif
778 }
779
780 static int write_files(void) {
781 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL;
782 _cleanup_(unlink_and_freep) char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL;
783 const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL;
784 int r;
785
786 passwd_path = prefix_roota(arg_root, "/etc/passwd");
787 shadow_path = prefix_roota(arg_root, "/etc/shadow");
788 group_path = prefix_roota(arg_root, "/etc/group");
789 gshadow_path = prefix_roota(arg_root, "/etc/gshadow");
790
791 r = write_temporary_group(group_path, &group, &group_tmp);
792 if (r < 0)
793 return r;
794
795 r = write_temporary_gshadow(gshadow_path, &gshadow, &gshadow_tmp);
796 if (r < 0)
797 return r;
798
799 r = write_temporary_passwd(passwd_path, &passwd, &passwd_tmp);
800 if (r < 0)
801 return r;
802
803 r = write_temporary_shadow(shadow_path, &shadow, &shadow_tmp);
804 if (r < 0)
805 return r;
806
807 /* Make a backup of the old files */
808 if (group) {
809 r = make_backup("/etc/group", group_path);
810 if (r < 0)
811 return r;
812 }
813 if (gshadow) {
814 r = make_backup("/etc/gshadow", gshadow_path);
815 if (r < 0)
816 return r;
817 }
818
819 if (passwd) {
820 r = make_backup("/etc/passwd", passwd_path);
821 if (r < 0)
822 return r;
823 }
824 if (shadow) {
825 r = make_backup("/etc/shadow", shadow_path);
826 if (r < 0)
827 return r;
828 }
829
830 /* And make the new files count */
831 if (group) {
832 r = rename_and_apply_smack(group_tmp, group_path);
833 if (r < 0)
834 return r;
835
836 group_tmp = mfree(group_tmp);
837 }
838 if (gshadow) {
839 r = rename_and_apply_smack(gshadow_tmp, gshadow_path);
840 if (r < 0)
841 return r;
842
843 gshadow_tmp = mfree(gshadow_tmp);
844 }
845
846 if (passwd) {
847 r = rename_and_apply_smack(passwd_tmp, passwd_path);
848 if (r < 0)
849 return r;
850
851 passwd_tmp = mfree(passwd_tmp);
852 }
853 if (shadow) {
854 r = rename_and_apply_smack(shadow_tmp, shadow_path);
855 if (r < 0)
856 return r;
857
858 shadow_tmp = mfree(shadow_tmp);
859 }
860
861 return 0;
862 }
863
864 static int uid_is_ok(uid_t uid, const char *name, bool check_with_gid) {
865 struct passwd *p;
866 struct group *g;
867 const char *n;
868 Item *i;
869
870 /* Let's see if we already have assigned the UID a second time */
871 if (ordered_hashmap_get(todo_uids, UID_TO_PTR(uid)))
872 return 0;
873
874 /* Try to avoid using uids that are already used by a group
875 * that doesn't have the same name as our new user. */
876 if (check_with_gid) {
877 i = ordered_hashmap_get(todo_gids, GID_TO_PTR(uid));
878 if (i && !streq(i->name, name))
879 return 0;
880 }
881
882 /* Let's check the files directly */
883 if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
884 return 0;
885
886 if (check_with_gid) {
887 n = hashmap_get(database_gid, GID_TO_PTR(uid));
888 if (n && !streq(n, name))
889 return 0;
890 }
891
892 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
893 if (!arg_root) {
894 errno = 0;
895 p = getpwuid(uid);
896 if (p)
897 return 0;
898 if (!IN_SET(errno, 0, ENOENT))
899 return -errno;
900
901 if (check_with_gid) {
902 errno = 0;
903 g = getgrgid((gid_t) uid);
904 if (g) {
905 if (!streq(g->gr_name, name))
906 return 0;
907 } else if (!IN_SET(errno, 0, ENOENT))
908 return -errno;
909 }
910 }
911
912 return 1;
913 }
914
915 static int root_stat(const char *p, struct stat *st) {
916 const char *fix;
917
918 fix = prefix_roota(arg_root, p);
919 if (stat(fix, st) < 0)
920 return -errno;
921
922 return 0;
923 }
924
925 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
926 struct stat st;
927 bool found_uid = false, found_gid = false;
928 uid_t uid = 0;
929 gid_t gid = 0;
930
931 assert(i);
932
933 /* First, try to get the gid directly */
934 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
935 gid = st.st_gid;
936 found_gid = true;
937 }
938
939 /* Then, try to get the uid directly */
940 if ((_uid || (_gid && !found_gid))
941 && i->uid_path
942 && root_stat(i->uid_path, &st) >= 0) {
943
944 uid = st.st_uid;
945 found_uid = true;
946
947 /* If we need the gid, but had no success yet, also derive it from the uid path */
948 if (_gid && !found_gid) {
949 gid = st.st_gid;
950 found_gid = true;
951 }
952 }
953
954 /* If that didn't work yet, then let's reuse the gid as uid */
955 if (_uid && !found_uid && i->gid_path) {
956
957 if (found_gid) {
958 uid = (uid_t) gid;
959 found_uid = true;
960 } else if (root_stat(i->gid_path, &st) >= 0) {
961 uid = (uid_t) st.st_gid;
962 found_uid = true;
963 }
964 }
965
966 if (_uid) {
967 if (!found_uid)
968 return 0;
969
970 *_uid = uid;
971 }
972
973 if (_gid) {
974 if (!found_gid)
975 return 0;
976
977 *_gid = gid;
978 }
979
980 return 1;
981 }
982
983 static int add_user(Item *i) {
984 void *z;
985 int r;
986
987 assert(i);
988
989 /* Check the database directly */
990 z = hashmap_get(database_user, i->name);
991 if (z) {
992 log_debug("User %s already exists.", i->name);
993 i->uid = PTR_TO_UID(z);
994 i->uid_set = true;
995 return 0;
996 }
997
998 if (!arg_root) {
999 struct passwd *p;
1000
1001 /* Also check NSS */
1002 errno = 0;
1003 p = getpwnam(i->name);
1004 if (p) {
1005 log_debug("User %s already exists.", i->name);
1006 i->uid = p->pw_uid;
1007 i->uid_set = true;
1008
1009 r = free_and_strdup(&i->description, p->pw_gecos);
1010 if (r < 0)
1011 return log_oom();
1012
1013 return 0;
1014 }
1015 if (!IN_SET(errno, 0, ENOENT))
1016 return log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name);
1017 }
1018
1019 /* Try to use the suggested numeric uid */
1020 if (i->uid_set) {
1021 r = uid_is_ok(i->uid, i->name, !i->id_set_strict);
1022 if (r < 0)
1023 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
1024 if (r == 0) {
1025 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
1026 i->uid_set = false;
1027 }
1028 }
1029
1030 /* If that didn't work, try to read it from the specified path */
1031 if (!i->uid_set) {
1032 uid_t c;
1033
1034 if (read_id_from_file(i, &c, NULL) > 0) {
1035
1036 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
1037 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
1038 else {
1039 r = uid_is_ok(c, i->name, true);
1040 if (r < 0)
1041 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
1042 else if (r > 0) {
1043 i->uid = c;
1044 i->uid_set = true;
1045 } else
1046 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
1047 }
1048 }
1049 }
1050
1051 /* Otherwise, try to reuse the group ID */
1052 if (!i->uid_set && i->gid_set) {
1053 r = uid_is_ok((uid_t) i->gid, i->name, true);
1054 if (r < 0)
1055 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
1056 if (r > 0) {
1057 i->uid = (uid_t) i->gid;
1058 i->uid_set = true;
1059 }
1060 }
1061
1062 /* And if that didn't work either, let's try to find a free one */
1063 if (!i->uid_set) {
1064 for (;;) {
1065 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
1066 if (r < 0) {
1067 log_error("No free user ID available for %s.", i->name);
1068 return r;
1069 }
1070
1071 r = uid_is_ok(search_uid, i->name, true);
1072 if (r < 0)
1073 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
1074 else if (r > 0)
1075 break;
1076 }
1077
1078 i->uid_set = true;
1079 i->uid = search_uid;
1080 }
1081
1082 r = ordered_hashmap_ensure_allocated(&todo_uids, NULL);
1083 if (r < 0)
1084 return log_oom();
1085
1086 r = ordered_hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
1087 if (r < 0)
1088 return log_oom();
1089
1090 i->todo_user = true;
1091 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
1092
1093 return 0;
1094 }
1095
1096 static int gid_is_ok(gid_t gid) {
1097 struct group *g;
1098 struct passwd *p;
1099
1100 if (ordered_hashmap_get(todo_gids, GID_TO_PTR(gid)))
1101 return 0;
1102
1103 /* Avoid reusing gids that are already used by a different user */
1104 if (ordered_hashmap_get(todo_uids, UID_TO_PTR(gid)))
1105 return 0;
1106
1107 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
1108 return 0;
1109
1110 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
1111 return 0;
1112
1113 if (!arg_root) {
1114 errno = 0;
1115 g = getgrgid(gid);
1116 if (g)
1117 return 0;
1118 if (!IN_SET(errno, 0, ENOENT))
1119 return -errno;
1120
1121 errno = 0;
1122 p = getpwuid((uid_t) gid);
1123 if (p)
1124 return 0;
1125 if (!IN_SET(errno, 0, ENOENT))
1126 return -errno;
1127 }
1128
1129 return 1;
1130 }
1131
1132 static int add_group(Item *i) {
1133 void *z;
1134 int r;
1135
1136 assert(i);
1137
1138 /* Check the database directly */
1139 z = hashmap_get(database_group, i->name);
1140 if (z) {
1141 log_debug("Group %s already exists.", i->name);
1142 i->gid = PTR_TO_GID(z);
1143 i->gid_set = true;
1144 return 0;
1145 }
1146
1147 /* Also check NSS */
1148 if (!arg_root) {
1149 struct group *g;
1150
1151 errno = 0;
1152 g = getgrnam(i->name);
1153 if (g) {
1154 log_debug("Group %s already exists.", i->name);
1155 i->gid = g->gr_gid;
1156 i->gid_set = true;
1157 return 0;
1158 }
1159 if (!IN_SET(errno, 0, ENOENT))
1160 return log_error_errno(errno, "Failed to check if group %s already exists: %m", i->name);
1161 }
1162
1163 /* Try to use the suggested numeric gid */
1164 if (i->gid_set) {
1165 r = gid_is_ok(i->gid);
1166 if (r < 0)
1167 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1168 if (i->id_set_strict) {
1169 /* If we require the gid to already exist we can return here:
1170 * r > 0: means the gid does not exist -> fail
1171 * r == 0: means the gid exists -> nothing more to do.
1172 */
1173 if (r > 0) {
1174 log_error("Failed to create %s: please create GID %d", i->name, i->gid);
1175 return -EINVAL;
1176 }
1177 if (r == 0)
1178 return 0;
1179 }
1180 if (r == 0) {
1181 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
1182 i->gid_set = false;
1183 }
1184 }
1185
1186 /* Try to reuse the numeric uid, if there's one */
1187 if (!i->gid_set && i->uid_set) {
1188 r = gid_is_ok((gid_t) i->uid);
1189 if (r < 0)
1190 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1191 if (r > 0) {
1192 i->gid = (gid_t) i->uid;
1193 i->gid_set = true;
1194 }
1195 }
1196
1197 /* If that didn't work, try to read it from the specified path */
1198 if (!i->gid_set) {
1199 gid_t c;
1200
1201 if (read_id_from_file(i, NULL, &c) > 0) {
1202
1203 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
1204 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
1205 else {
1206 r = gid_is_ok(c);
1207 if (r < 0)
1208 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1209 else if (r > 0) {
1210 i->gid = c;
1211 i->gid_set = true;
1212 } else
1213 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
1214 }
1215 }
1216 }
1217
1218 /* And if that didn't work either, let's try to find a free one */
1219 if (!i->gid_set) {
1220 for (;;) {
1221 /* We look for new GIDs in the UID pool! */
1222 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
1223 if (r < 0) {
1224 log_error("No free group ID available for %s.", i->name);
1225 return r;
1226 }
1227
1228 r = gid_is_ok(search_uid);
1229 if (r < 0)
1230 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1231 else if (r > 0)
1232 break;
1233 }
1234
1235 i->gid_set = true;
1236 i->gid = search_uid;
1237 }
1238
1239 r = ordered_hashmap_ensure_allocated(&todo_gids, NULL);
1240 if (r < 0)
1241 return log_oom();
1242
1243 r = ordered_hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
1244 if (r < 0)
1245 return log_oom();
1246
1247 i->todo_group = true;
1248 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
1249
1250 return 0;
1251 }
1252
1253 static int process_item(Item *i) {
1254 int r;
1255
1256 assert(i);
1257
1258 switch (i->type) {
1259
1260 case ADD_USER: {
1261 Item *j;
1262
1263 j = ordered_hashmap_get(groups, i->name);
1264 if (j && j->todo_group) {
1265 /* When the group with the same name is already in queue,
1266 * use the information about the group and do not create
1267 * duplicated group entry. */
1268 i->gid_set = j->gid_set;
1269 i->gid = j->gid;
1270 i->id_set_strict = true;
1271 } else {
1272 r = add_group(i);
1273 if (r < 0)
1274 return r;
1275 }
1276
1277 return add_user(i);
1278 }
1279
1280 case ADD_GROUP:
1281 return add_group(i);
1282
1283 default:
1284 assert_not_reached("Unknown item type");
1285 }
1286 }
1287
1288 static void item_free(Item *i) {
1289
1290 if (!i)
1291 return;
1292
1293 free(i->name);
1294 free(i->uid_path);
1295 free(i->gid_path);
1296 free(i->description);
1297 free(i->home);
1298 free(i->shell);
1299 free(i);
1300 }
1301
1302 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
1303
1304 static int add_implicit(void) {
1305 char *g, **l;
1306 Iterator iterator;
1307 int r;
1308
1309 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1310 ORDERED_HASHMAP_FOREACH_KEY(l, g, members, iterator) {
1311 char **m;
1312
1313 STRV_FOREACH(m, l)
1314 if (!ordered_hashmap_get(users, *m)) {
1315 _cleanup_(item_freep) Item *j = NULL;
1316
1317 r = ordered_hashmap_ensure_allocated(&users, &string_hash_ops);
1318 if (r < 0)
1319 return log_oom();
1320
1321 j = new0(Item, 1);
1322 if (!j)
1323 return log_oom();
1324
1325 j->type = ADD_USER;
1326 j->name = strdup(*m);
1327 if (!j->name)
1328 return log_oom();
1329
1330 r = ordered_hashmap_put(users, j->name, j);
1331 if (r < 0)
1332 return log_oom();
1333
1334 log_debug("Adding implicit user '%s' due to m line", j->name);
1335 j = NULL;
1336 }
1337
1338 if (!(ordered_hashmap_get(users, g) ||
1339 ordered_hashmap_get(groups, g))) {
1340 _cleanup_(item_freep) Item *j = NULL;
1341
1342 r = ordered_hashmap_ensure_allocated(&groups, &string_hash_ops);
1343 if (r < 0)
1344 return log_oom();
1345
1346 j = new0(Item, 1);
1347 if (!j)
1348 return log_oom();
1349
1350 j->type = ADD_GROUP;
1351 j->name = strdup(g);
1352 if (!j->name)
1353 return log_oom();
1354
1355 r = ordered_hashmap_put(groups, j->name, j);
1356 if (r < 0)
1357 return log_oom();
1358
1359 log_debug("Adding implicit group '%s' due to m line", j->name);
1360 j = NULL;
1361 }
1362 }
1363
1364 return 0;
1365 }
1366
1367 static bool item_equal(Item *a, Item *b) {
1368 assert(a);
1369 assert(b);
1370
1371 if (a->type != b->type)
1372 return false;
1373
1374 if (!streq_ptr(a->name, b->name))
1375 return false;
1376
1377 if (!streq_ptr(a->uid_path, b->uid_path))
1378 return false;
1379
1380 if (!streq_ptr(a->gid_path, b->gid_path))
1381 return false;
1382
1383 if (!streq_ptr(a->description, b->description))
1384 return false;
1385
1386 if (a->uid_set != b->uid_set)
1387 return false;
1388
1389 if (a->uid_set && a->uid != b->uid)
1390 return false;
1391
1392 if (a->gid_set != b->gid_set)
1393 return false;
1394
1395 if (a->gid_set && a->gid != b->gid)
1396 return false;
1397
1398 if (!streq_ptr(a->home, b->home))
1399 return false;
1400
1401 if (!streq_ptr(a->shell, b->shell))
1402 return false;
1403
1404 return true;
1405 }
1406
1407 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1408
1409 static const Specifier specifier_table[] = {
1410 { 'm', specifier_machine_id, NULL },
1411 { 'b', specifier_boot_id, NULL },
1412 { 'H', specifier_host_name, NULL },
1413 { 'v', specifier_kernel_release, NULL },
1414 {}
1415 };
1416
1417 _cleanup_free_ char *action = NULL,
1418 *name = NULL, *resolved_name = NULL,
1419 *id = NULL, *resolved_id = NULL,
1420 *description = NULL,
1421 *home = NULL,
1422 *shell, *resolved_shell = NULL;
1423 _cleanup_(item_freep) Item *i = NULL;
1424 Item *existing;
1425 OrderedHashmap *h;
1426 int r;
1427 const char *p;
1428
1429 assert(fname);
1430 assert(line >= 1);
1431 assert(buffer);
1432
1433 /* Parse columns */
1434 p = buffer;
1435 r = extract_many_words(&p, NULL, EXTRACT_QUOTES,
1436 &action, &name, &id, &description, &home, &shell, NULL);
1437 if (r < 0) {
1438 log_error("[%s:%u] Syntax error.", fname, line);
1439 return r;
1440 }
1441 if (r < 2) {
1442 log_error("[%s:%u] Missing action and name columns.", fname, line);
1443 return -EINVAL;
1444 }
1445 if (!isempty(p)) {
1446 log_error("[%s:%u] Trailing garbage.", fname, line);
1447 return -EINVAL;
1448 }
1449
1450 /* Verify action */
1451 if (strlen(action) != 1) {
1452 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1453 return -EINVAL;
1454 }
1455
1456 if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE)) {
1457 log_error("[%s:%u] Unknown command type '%c'.", fname, line, action[0]);
1458 return -EBADMSG;
1459 }
1460
1461 /* Verify name */
1462 if (isempty(name) || streq(name, "-"))
1463 name = mfree(name);
1464
1465 if (name) {
1466 r = specifier_printf(name, specifier_table, NULL, &resolved_name);
1467 if (r < 0) {
1468 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1469 return r;
1470 }
1471
1472 if (!valid_user_group_name(resolved_name)) {
1473 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
1474 return -EINVAL;
1475 }
1476 }
1477
1478 /* Verify id */
1479 if (isempty(id) || streq(id, "-"))
1480 id = mfree(id);
1481
1482 if (id) {
1483 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1484 if (r < 0) {
1485 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1486 return r;
1487 }
1488 }
1489
1490 /* Verify description */
1491 if (isempty(description) || streq(description, "-"))
1492 description = mfree(description);
1493
1494 if (description) {
1495 if (!valid_gecos(description)) {
1496 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, description);
1497 return -EINVAL;
1498 }
1499 }
1500
1501 /* Verify home */
1502 if (isempty(home) || streq(home, "-"))
1503 home = mfree(home);
1504
1505 if (home) {
1506 if (!valid_home(home)) {
1507 log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, home);
1508 return -EINVAL;
1509 }
1510 }
1511
1512 /* Verify shell */
1513 if (isempty(shell) || streq(shell, "-"))
1514 shell = mfree(shell);
1515
1516 if (shell) {
1517 r = specifier_printf(shell, specifier_table, NULL, &resolved_shell);
1518 if (r < 0) {
1519 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, shell);
1520 return r;
1521 }
1522
1523 if (!valid_shell(resolved_shell)) {
1524 log_error("[%s:%u] '%s' is not a valid login shell field.", fname, line, resolved_shell);
1525 return -EINVAL;
1526 }
1527 }
1528
1529
1530 switch (action[0]) {
1531
1532 case ADD_RANGE:
1533 if (resolved_name) {
1534 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname, line);
1535 return -EINVAL;
1536 }
1537
1538 if (!resolved_id) {
1539 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname, line);
1540 return -EINVAL;
1541 }
1542
1543 if (description || home || shell) {
1544 log_error("[%s:%u] Lines of type '%c' don't take a %s field.",
1545 fname, line, action[0],
1546 description ? "GECOS" : home ? "home directory" : "login shell");
1547 return -EINVAL;
1548 }
1549
1550 r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id);
1551 if (r < 0) {
1552 log_error("[%s:%u] Invalid UID range %s.", fname, line, resolved_id);
1553 return -EINVAL;
1554 }
1555
1556 return 0;
1557
1558 case ADD_MEMBER: {
1559 char **l;
1560
1561 /* Try to extend an existing member or group item */
1562 if (!name) {
1563 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname, line);
1564 return -EINVAL;
1565 }
1566
1567 if (!resolved_id) {
1568 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1569 return -EINVAL;
1570 }
1571
1572 if (!valid_user_group_name(resolved_id)) {
1573 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1574 return -EINVAL;
1575 }
1576
1577 if (description || home || shell) {
1578 log_error("[%s:%u] Lines of type '%c' don't take a %s field.",
1579 fname, line, action[0],
1580 description ? "GECOS" : home ? "home directory" : "login shell");
1581 return -EINVAL;
1582 }
1583
1584 r = ordered_hashmap_ensure_allocated(&members, &string_hash_ops);
1585 if (r < 0)
1586 return log_oom();
1587
1588 l = ordered_hashmap_get(members, resolved_id);
1589 if (l) {
1590 /* A list for this group name already exists, let's append to it */
1591 r = strv_push(&l, resolved_name);
1592 if (r < 0)
1593 return log_oom();
1594
1595 resolved_name = NULL;
1596
1597 assert_se(ordered_hashmap_update(members, resolved_id, l) >= 0);
1598 } else {
1599 /* No list for this group name exists yet, create one */
1600
1601 l = new0(char *, 2);
1602 if (!l)
1603 return -ENOMEM;
1604
1605 l[0] = resolved_name;
1606 l[1] = NULL;
1607
1608 r = ordered_hashmap_put(members, resolved_id, l);
1609 if (r < 0) {
1610 free(l);
1611 return log_oom();
1612 }
1613
1614 resolved_id = resolved_name = NULL;
1615 }
1616
1617 return 0;
1618 }
1619
1620 case ADD_USER:
1621 if (!name) {
1622 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname, line);
1623 return -EINVAL;
1624 }
1625
1626 r = ordered_hashmap_ensure_allocated(&users, &string_hash_ops);
1627 if (r < 0)
1628 return log_oom();
1629
1630 i = new0(Item, 1);
1631 if (!i)
1632 return log_oom();
1633
1634 if (resolved_id) {
1635 if (path_is_absolute(resolved_id)) {
1636 i->uid_path = resolved_id;
1637 resolved_id = NULL;
1638
1639 path_kill_slashes(i->uid_path);
1640 } else {
1641 _cleanup_free_ char *uid = NULL, *gid = NULL;
1642 if (split_pair(resolved_id, ":", &uid, &gid) == 0) {
1643 r = parse_gid(gid, &i->gid);
1644 if (r < 0)
1645 return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
1646 i->gid_set = true;
1647 i->id_set_strict = true;
1648 free_and_replace(resolved_id, uid);
1649 }
1650 if (!streq(resolved_id, "-")) {
1651 r = parse_uid(resolved_id, &i->uid);
1652 if (r < 0)
1653 return log_error_errno(r, "Failed to parse UID: '%s': %m", id);
1654 i->uid_set = true;
1655 }
1656 }
1657 }
1658
1659 i->description = description;
1660 description = NULL;
1661
1662 i->home = home;
1663 home = NULL;
1664
1665 i->shell = resolved_shell;
1666 resolved_shell = NULL;
1667
1668 h = users;
1669 break;
1670
1671 case ADD_GROUP:
1672 if (!name) {
1673 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname, line);
1674 return -EINVAL;
1675 }
1676
1677 if (description || home || shell) {
1678 log_error("[%s:%u] Lines of type '%c' don't take a %s field.",
1679 fname, line, action[0],
1680 description ? "GECOS" : home ? "home directory" : "login shell");
1681 return -EINVAL;
1682 }
1683
1684 r = ordered_hashmap_ensure_allocated(&groups, &string_hash_ops);
1685 if (r < 0)
1686 return log_oom();
1687
1688 i = new0(Item, 1);
1689 if (!i)
1690 return log_oom();
1691
1692 if (resolved_id) {
1693 if (path_is_absolute(resolved_id)) {
1694 i->gid_path = resolved_id;
1695 resolved_id = NULL;
1696
1697 path_kill_slashes(i->gid_path);
1698 } else {
1699 r = parse_gid(resolved_id, &i->gid);
1700 if (r < 0)
1701 return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
1702
1703 i->gid_set = true;
1704 }
1705 }
1706
1707 h = groups;
1708 break;
1709
1710 default:
1711 return -EBADMSG;
1712 }
1713
1714 i->type = action[0];
1715 i->name = resolved_name;
1716 resolved_name = NULL;
1717
1718 existing = ordered_hashmap_get(h, i->name);
1719 if (existing) {
1720
1721 /* Two identical items are fine */
1722 if (!item_equal(existing, i))
1723 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1724
1725 return 0;
1726 }
1727
1728 r = ordered_hashmap_put(h, i->name, i);
1729 if (r < 0)
1730 return log_oom();
1731
1732 i = NULL;
1733 return 0;
1734 }
1735
1736 static int read_config_file(const char *fn, bool ignore_enoent) {
1737 _cleanup_fclose_ FILE *rf = NULL;
1738 FILE *f = NULL;
1739 char line[LINE_MAX];
1740 unsigned v = 0;
1741 int r = 0;
1742
1743 assert(fn);
1744
1745 if (streq(fn, "-"))
1746 f = stdin;
1747 else {
1748 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &rf);
1749 if (r < 0) {
1750 if (ignore_enoent && r == -ENOENT)
1751 return 0;
1752
1753 return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
1754 }
1755
1756 f = rf;
1757 }
1758
1759 FOREACH_LINE(line, f, break) {
1760 char *l;
1761 int k;
1762
1763 v++;
1764
1765 l = strstrip(line);
1766 if (IN_SET(*l, 0, '#'))
1767 continue;
1768
1769 k = parse_line(fn, v, l);
1770 if (k < 0 && r == 0)
1771 r = k;
1772 }
1773
1774 if (ferror(f)) {
1775 log_error_errno(errno, "Failed to read from file %s: %m", fn);
1776 if (r == 0)
1777 r = -EIO;
1778 }
1779
1780 return r;
1781 }
1782
1783 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1784 char *name;
1785
1786 for (;;) {
1787 name = hashmap_first(by_id);
1788 if (!name)
1789 break;
1790
1791 hashmap_remove(by_name, name);
1792
1793 hashmap_steal_first_key(by_id);
1794 free(name);
1795 }
1796
1797 while ((name = hashmap_steal_first_key(by_name)))
1798 free(name);
1799
1800 hashmap_free(by_name);
1801 hashmap_free(by_id);
1802 }
1803
1804 static void help(void) {
1805 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1806 "Creates system user accounts.\n\n"
1807 " -h --help Show this help\n"
1808 " --version Show package version\n"
1809 " --root=PATH Operate on an alternate filesystem root\n"
1810 " --replace=PATH Treat arguments as replacement for PATH\n"
1811 " --inline Treat arguments as configuration lines\n"
1812 , program_invocation_short_name);
1813 }
1814
1815 static int parse_argv(int argc, char *argv[]) {
1816
1817 enum {
1818 ARG_VERSION = 0x100,
1819 ARG_ROOT,
1820 ARG_REPLACE,
1821 ARG_INLINE,
1822 };
1823
1824 static const struct option options[] = {
1825 { "help", no_argument, NULL, 'h' },
1826 { "version", no_argument, NULL, ARG_VERSION },
1827 { "root", required_argument, NULL, ARG_ROOT },
1828 { "replace", required_argument, NULL, ARG_REPLACE },
1829 { "inline", no_argument, NULL, ARG_INLINE },
1830 {}
1831 };
1832
1833 int c, r;
1834
1835 assert(argc >= 0);
1836 assert(argv);
1837
1838 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
1839
1840 switch (c) {
1841
1842 case 'h':
1843 help();
1844 return 0;
1845
1846 case ARG_VERSION:
1847 return version();
1848
1849 case ARG_ROOT:
1850 r = parse_path_argument_and_warn(optarg, true, &arg_root);
1851 if (r < 0)
1852 return r;
1853 break;
1854
1855 case ARG_REPLACE:
1856 if (!path_is_absolute(optarg) ||
1857 !endswith(optarg, ".conf")) {
1858 log_error("The argument to --replace= must an absolute path to a config file");
1859 return -EINVAL;
1860 }
1861
1862 arg_replace = optarg;
1863 break;
1864
1865 case ARG_INLINE:
1866 arg_inline = true;
1867 break;
1868
1869 case '?':
1870 return -EINVAL;
1871
1872 default:
1873 assert_not_reached("Unhandled option");
1874 }
1875
1876 if (arg_replace && optind >= argc) {
1877 log_error("When --replace= is given, some configuration items must be specified");
1878 return -EINVAL;
1879 }
1880
1881 return 1;
1882 }
1883
1884 static int parse_arguments(char **args) {
1885 char **arg;
1886 unsigned pos = 1;
1887 int r;
1888
1889 STRV_FOREACH(arg, args) {
1890 if (arg_inline)
1891 /* Use (argument):n, where n==1 for the first positional arg */
1892 r = parse_line("(argument)", pos, *arg);
1893 else
1894 r = read_config_file(*arg, false);
1895 if (r < 0)
1896 return r;
1897
1898 pos++;
1899 }
1900
1901 return 0;
1902 }
1903
1904 static int read_config_files(const char* dirs, char **args) {
1905 _cleanup_strv_free_ char **files = NULL;
1906 _cleanup_free_ char *p = NULL;
1907 char **f;
1908 int r;
1909
1910 r = conf_files_list_nulstr(&files, ".conf", arg_root, 0, dirs);
1911 if (r < 0)
1912 return log_error_errno(r, "Failed to enumerate sysusers.d files: %m");
1913
1914 if (arg_replace) {
1915 r = conf_files_insert_nulstr(&files, arg_root, dirs, arg_replace);
1916 if (r < 0)
1917 return log_error_errno(r, "Failed to extend sysusers.d file list: %m");
1918
1919 p = path_join(arg_root, arg_replace, NULL);
1920 if (!p)
1921 return log_oom();
1922 }
1923
1924 STRV_FOREACH(f, files)
1925 if (p && path_equal(*f, p)) {
1926 log_debug("Parsing arguments at position \"%s\"…", *f);
1927
1928 r = parse_arguments(args);
1929 if (r < 0)
1930 return r;
1931 } else {
1932 log_debug("Reading config file \"%s\"…", *f);
1933
1934 /* Just warn, ignore result otherwise */
1935 (void) read_config_file(*f, true);
1936 }
1937
1938 return 0;
1939 }
1940
1941 int main(int argc, char *argv[]) {
1942
1943 _cleanup_close_ int lock = -1;
1944 Iterator iterator;
1945 int r;
1946 Item *i;
1947 char *n;
1948
1949 r = parse_argv(argc, argv);
1950 if (r <= 0)
1951 goto finish;
1952
1953 log_set_target(LOG_TARGET_AUTO);
1954 log_parse_environment();
1955 log_open();
1956
1957 umask(0022);
1958
1959 r = mac_selinux_init();
1960 if (r < 0) {
1961 log_error_errno(r, "SELinux setup failed: %m");
1962 goto finish;
1963 }
1964
1965 /* If command line arguments are specified along with --replace, read all
1966 * configuration files and insert the positional arguments at the specified
1967 * place. Otherwise, if command line arguments are specified, execute just
1968 * them, and finally, without --replace= or any positional arguments, just
1969 * read configuration and execute it.
1970 */
1971 if (arg_replace || optind >= argc)
1972 r = read_config_files(conf_file_dirs, argv + optind);
1973 else
1974 r = parse_arguments(argv + optind);
1975 if (r < 0)
1976 goto finish;
1977
1978 /* Let's tell nss-systemd not to synthesize the "root" and "nobody" entries for it, so that our detection
1979 * whether the names or UID/GID area already used otherwise doesn't get confused. After all, even though
1980 * nss-systemd synthesizes these users/groups, they should still appear in /etc/passwd and /etc/group, as the
1981 * synthesizing logic is merely supposed to be fallback for cases where we run with a completely unpopulated
1982 * /etc. */
1983 if (setenv("SYSTEMD_NSS_BYPASS_SYNTHETIC", "1", 1) < 0) {
1984 r = log_error_errno(errno, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m");
1985 goto finish;
1986 }
1987
1988 if (!uid_range) {
1989 /* Default to default range of 1..SYSTEM_UID_MAX */
1990 r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX);
1991 if (r < 0) {
1992 log_oom();
1993 goto finish;
1994 }
1995 }
1996
1997 r = add_implicit();
1998 if (r < 0)
1999 goto finish;
2000
2001 lock = take_etc_passwd_lock(arg_root);
2002 if (lock < 0) {
2003 log_error_errno(lock, "Failed to take /etc/passwd lock: %m");
2004 goto finish;
2005 }
2006
2007 r = load_user_database();
2008 if (r < 0) {
2009 log_error_errno(r, "Failed to load user database: %m");
2010 goto finish;
2011 }
2012
2013 r = load_group_database();
2014 if (r < 0) {
2015 log_error_errno(r, "Failed to read group database: %m");
2016 goto finish;
2017 }
2018
2019 ORDERED_HASHMAP_FOREACH(i, groups, iterator)
2020 process_item(i);
2021
2022 ORDERED_HASHMAP_FOREACH(i, users, iterator)
2023 process_item(i);
2024
2025 r = write_files();
2026 if (r < 0)
2027 log_error_errno(r, "Failed to write files: %m");
2028
2029 finish:
2030 ordered_hashmap_free_with_destructor(groups, item_free);
2031 ordered_hashmap_free_with_destructor(users, item_free);
2032
2033 while ((n = ordered_hashmap_first_key(members))) {
2034 strv_free(ordered_hashmap_steal_first(members));
2035 free(n);
2036 }
2037 ordered_hashmap_free(members);
2038
2039 ordered_hashmap_free(todo_uids);
2040 ordered_hashmap_free(todo_gids);
2041
2042 free_database(database_user, database_uid);
2043 free_database(database_group, database_gid);
2044
2045 free(arg_root);
2046
2047 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2048 }