]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/sysusers/sysusers.c
cryptsetup: check that password is not null
[thirdparty/systemd.git] / src / sysusers / sysusers.c
CommitLineData
1b992147
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <sys/types.h>
23#include <pwd.h>
24#include <grp.h>
25#include <shadow.h>
26#include <getopt.h>
27
28#include "util.h"
29#include "hashmap.h"
30#include "specifier.h"
31#include "path-util.h"
32#include "build.h"
33#include "strv.h"
34#include "conf-files.h"
35#include "copy.h"
36#include "utf8.h"
37
38typedef enum ItemType {
39 ADD_USER = 'u',
40 ADD_GROUP = 'g',
41} ItemType;
42typedef struct Item {
43 ItemType type;
44
45 char *name;
46 char *uid_path;
47 char *gid_path;
48 char *description;
49
50 gid_t gid;
51 uid_t uid;
52
53 bool gid_set:1;
54 bool uid_set:1;
55
56 bool todo:1;
57} Item;
58
59static char *arg_root = NULL;
60
61static const char conf_file_dirs[] =
62 "/usr/local/lib/sysusers.d\0"
63 "/usr/lib/sysusers.d\0"
64#ifdef HAVE_SPLIT_USR
65 "/lib/sysusers.d\0"
66#endif
67 ;
68
69static Hashmap *users = NULL, *groups = NULL;
70static Hashmap *todo_uids = NULL, *todo_gids = NULL;
71
72static Hashmap *database_uid = NULL, *database_user = NULL;
73static Hashmap *database_gid = NULL, *database_group = NULL;
74
75static uid_t search_uid = SYSTEM_UID_MAX;
76static gid_t search_gid = SYSTEM_GID_MAX;
77
78#define UID_TO_PTR(u) (ULONG_TO_PTR(u+1))
79#define PTR_TO_UID(u) ((uid_t) (PTR_TO_ULONG(u)-1))
80
81#define GID_TO_PTR(g) (ULONG_TO_PTR(g+1))
82#define PTR_TO_GID(g) ((gid_t) (PTR_TO_ULONG(g)-1))
83
84#define fix_root(x) (arg_root ? strappenda(arg_root, x) : x)
85
86static int load_user_database(void) {
87 _cleanup_fclose_ FILE *f = NULL;
88 const char *passwd_path;
89 struct passwd *pw;
90 int r;
91
92 passwd_path = fix_root("/etc/passwd");
93 f = fopen(passwd_path, "re");
94 if (!f)
95 return errno == ENOENT ? 0 : -errno;
96
97 r = hashmap_ensure_allocated(&database_user, string_hash_func, string_compare_func);
98 if (r < 0)
99 return r;
100
101 r = hashmap_ensure_allocated(&database_uid, trivial_hash_func, trivial_compare_func);
102 if (r < 0)
103 return r;
104
105 errno = 0;
106 while ((pw = fgetpwent(f))) {
107 char *n;
108 int k, q;
109
110 n = strdup(pw->pw_name);
111 if (!n)
112 return -ENOMEM;
113
114 k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
115 if (k < 0 && k != -EEXIST) {
116 free(n);
117 return k;
118 }
119
120 q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
121 if (q < 0 && q != -EEXIST) {
122 if (k < 0)
123 free(n);
124 return q;
125 }
126
127 if (q < 0 && k < 0)
128 free(n);
129
130 errno = 0;
131 }
132 if (!IN_SET(errno, 0, ENOENT))
133 return -errno;
134
135 return 0;
136}
137
138static int load_group_database(void) {
139 _cleanup_fclose_ FILE *f = NULL;
140 const char *group_path;
141 struct group *gr;
142 int r;
143
144 group_path = fix_root("/etc/group");
145 f = fopen(group_path, "re");
146 if (!f)
147 return errno == ENOENT ? 0 : -errno;
148
149 r = hashmap_ensure_allocated(&database_group, string_hash_func, string_compare_func);
150 if (r < 0)
151 return r;
152
153 r = hashmap_ensure_allocated(&database_gid, trivial_hash_func, trivial_compare_func);
154 if (r < 0)
155 return r;
156
157 errno = 0;
158 while ((gr = fgetgrent(f))) {
159 char *n;
160 int k, q;
161
162 n = strdup(gr->gr_name);
163 if (!n)
164 return -ENOMEM;
165
166 k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
167 if (k < 0 && k != -EEXIST) {
168 free(n);
169 return k;
170 }
171
172 q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
173 if (q < 0 && q != -EEXIST) {
174 if (k < 0)
175 free(n);
176 return q;
177 }
178
179 if (q < 0 && k < 0)
180 free(n);
181
182 errno = 0;
183 }
184 if (!IN_SET(errno, 0, ENOENT))
185 return -errno;
186
187 return 0;
188}
189
190static int make_backup(const char *x) {
191 _cleanup_close_ int src = -1, dst = -1;
192 char *backup, *temp;
193 struct timespec ts[2];
194 struct stat st;
195 int r;
196
197 src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
198 if (src < 0) {
199 if (errno == ENOENT) /* No backup necessary... */
200 return 0;
201
202 return -errno;
203 }
204
205 if (fstat(src, &st) < 0)
206 return -errno;
207
208 temp = strappenda(x, ".XXXXXX");
209 dst = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC|O_NOCTTY);
210 if (dst < 0)
211 return dst;
212
213 r = copy_bytes(src, dst);
214 if (r < 0)
215 goto fail;
216
217 /* Copy over the access mask */
218 if (fchmod(dst, st.st_mode & 07777) < 0) {
219 r = -errno;
220 goto fail;
221 }
222
223 /* Don't fail on chmod(). If it stays owned by us, then it
224 * isn't too bad... */
225 fchown(dst, st.st_uid, st.st_gid);
226
227 ts[0] = st.st_atim;
228 ts[1] = st.st_mtim;
229 futimens(dst, ts);
230
231 backup = strappenda(x, "-");
232 if (rename(temp, backup) < 0)
233 goto fail;
234
235 return 0;
236
237fail:
238 unlink(temp);
239 return r;
240}
241
242static int write_files(void) {
243
244 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL;
245 _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL;
246 const char *passwd_path = NULL, *group_path = NULL;
247 Iterator iterator;
248 Item *i;
249 int r;
250
251 /* We don't patch /etc/shadow or /etc/gshadow here, since we
252 * only create user accounts without passwords anyway. */
253
254 if (hashmap_size(todo_gids) > 0) {
255 _cleanup_fclose_ FILE *original = NULL;
256
257 group_path = fix_root("/etc/group");
258 r = fopen_temporary(group_path, &group, &group_tmp);
259 if (r < 0)
260 goto finish;
261
262 if (fchmod(fileno(group), 0644) < 0) {
263 r = -errno;
264 goto finish;
265 }
266
267 original = fopen(group_path, "re");
268 if (original) {
269 struct group *gr;
270
271 errno = 0;
272 while ((gr = fgetgrent(original))) {
273 /* Safety checks against name and GID
274 * collisions. Normally, this should
275 * be unnecessary, but given that we
276 * look at the entries anyway here,
277 * let's make an extra verification
278 * step that we don't generate
279 * duplicate entries. */
280
281 i = hashmap_get(groups, gr->gr_name);
282 if (i && i->todo) {
283 r = -EEXIST;
284 goto finish;
285 }
286
287 if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
288 r = -EEXIST;
289 goto finish;
290 }
291
292 if (putgrent(gr, group) < 0) {
293 r = -errno;
294 goto finish;
295 }
296
297 errno = 0;
298 }
299 if (!IN_SET(errno, 0, ENOENT)) {
300 r = -errno;
301 goto finish;
302 }
303
304 } else if (errno != ENOENT) {
305 r = -errno;
306 goto finish;
307 }
308
309 HASHMAP_FOREACH(i, todo_gids, iterator) {
310 struct group n = {
311 .gr_name = i->name,
312 .gr_gid = i->gid,
313 .gr_passwd = (char*) "x",
314 };
315
316 if (putgrent(&n, group) < 0) {
317 r = -errno;
318 goto finish;
319 }
320 }
321
322 r = fflush_and_check(group);
323 if (r < 0)
324 goto finish;
325 }
326
327 if (hashmap_size(todo_uids) > 0) {
328 _cleanup_fclose_ FILE *original = NULL;
329
330 passwd_path = fix_root("/etc/passwd");
331 r = fopen_temporary(passwd_path, &passwd, &passwd_tmp);
332 if (r < 0)
333 goto finish;
334
335 if (fchmod(fileno(passwd), 0644) < 0) {
336 r = -errno;
337 goto finish;
338 }
339
340 original = fopen(passwd_path, "re");
341 if (original) {
342 struct passwd *pw;
343
344 errno = 0;
345 while ((pw = fgetpwent(original))) {
346
347 i = hashmap_get(users, pw->pw_name);
348 if (i && i->todo) {
349 r = -EEXIST;
350 goto finish;
351 }
352
353 if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
354 r = -EEXIST;
355 goto finish;
356 }
357
358 if (putpwent(pw, passwd) < 0) {
359 r = -errno;
360 goto finish;
361 }
362
363 errno = 0;
364 }
365 if (!IN_SET(errno, 0, ENOENT)) {
366 r = -errno;
367 goto finish;
368 }
369
370 } else if (errno != ENOENT) {
371 r = -errno;
372 goto finish;
373 }
374
375 HASHMAP_FOREACH(i, todo_uids, iterator) {
376 struct passwd n = {
377 .pw_name = i->name,
378 .pw_uid = i->uid,
379 .pw_gid = i->gid,
380 .pw_gecos = i->description,
381 .pw_passwd = (char*) "x",
382 };
383
384 /* Initialize the home directory and the shell
385 * to nologin, with one exception: for root we
386 * patch in something special */
387 if (i->uid == 0) {
388 n.pw_shell = (char*) "/bin/sh";
389 n.pw_dir = (char*) "/root";
390 } else {
391 n.pw_shell = (char*) "/sbin/nologin";
392 n.pw_dir = (char*) "/";
393 }
394
395 if (putpwent(&n, passwd) < 0) {
396 r = -r;
397 goto finish;
398 }
399 }
400
401 r = fflush_and_check(passwd);
402 if (r < 0)
403 goto finish;
404 }
405
406 /* Make a backup of the old files */
407 if (group) {
408 r = make_backup(group_path);
409 if (r < 0)
410 goto finish;
411 }
412
413 if (passwd) {
414 r = make_backup(passwd_path);
415 if (r < 0)
416 goto finish;
417 }
418
419 /* And make the new files count */
420 if (group) {
421 if (rename(group_tmp, group_path) < 0) {
422 r = -errno;
423 goto finish;
424 }
425
426 free(group_tmp);
427 group_tmp = NULL;
428 }
429
430 if (passwd) {
431 if (rename(passwd_tmp, passwd_path) < 0) {
432 r = -errno;
433 goto finish;
434 }
435
436 free(passwd_tmp);
437 passwd_tmp = NULL;
438 }
439
440 return 0;
441
442finish:
443 if (r < 0) {
444 if (passwd_tmp)
445 unlink(passwd_tmp);
446 if (group_tmp)
447 unlink(group_tmp);
448 }
449
450 return r;
451}
452
453static int uid_is_ok(uid_t uid, const char *name) {
454 struct passwd *p;
455 struct group *g;
456 const char *n;
457 Item *i;
458
459 /* Let's see if we already have assigned the UID a second time */
460 if (hashmap_get(todo_uids, UID_TO_PTR(uid)))
461 return 0;
462
463 /* Try to avoid using uids that are already used by a group
464 * that doesn't have the same name as our new user. */
465 i = hashmap_get(todo_gids, GID_TO_PTR(uid));
466 if (i && !streq(i->name, name))
467 return 0;
468
469 /* Let's check the files directly */
470 if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
471 return 0;
472
473 n = hashmap_get(database_gid, GID_TO_PTR(uid));
474 if (n && !streq(n, name))
475 return 0;
476
477 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
478 if (!arg_root) {
479 errno = 0;
480 p = getpwuid(uid);
481 if (p)
482 return 0;
483 if (errno != 0)
484 return -errno;
485
486 errno = 0;
487 g = getgrgid((gid_t) uid);
488 if (g) {
489 if (!streq(g->gr_name, name))
490 return 0;
491 } else if (errno != 0)
492 return -errno;
493 }
494
495 return 1;
496}
497
498static int root_stat(const char *p, struct stat *st) {
499 const char *fix;
500
501 fix = fix_root(p);
502 if (stat(fix, st) < 0)
503 return -errno;
504
505 return 0;
506}
507
508static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
509 struct stat st;
510 bool found_uid = false, found_gid = false;
511 uid_t uid;
512 gid_t gid;
513
514 assert(i);
515
516 /* First, try to get the gid directly */
517 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
518 gid = st.st_gid;
519 found_gid = true;
520 }
521
522 /* Then, try to get the uid directly */
523 if ((_uid || (_gid && !found_gid))
524 && i->uid_path
525 && root_stat(i->uid_path, &st) >= 0) {
526
527 uid = st.st_uid;
528 found_uid = true;
529
530 /* If we need the gid, but had no success yet, also derive it from the uid path */
531 if (_gid && !found_gid) {
532 gid = st.st_gid;
533 found_gid = true;
534 }
535 }
536
537 /* If that didn't work yet, then let's reuse the gid as uid */
538 if (_uid && !found_uid && i->gid_path) {
539
540 if (found_gid) {
541 uid = (uid_t) gid;
542 found_uid = true;
543 } else if (root_stat(i->gid_path, &st) >= 0) {
544 uid = (uid_t) st.st_gid;
545 found_uid = true;
546 }
547 }
548
549 if (_uid) {
550 if (!found_uid)
551 return 0;
552
553 *_uid = uid;
554 }
555
556 if (_gid) {
557 if (!found_gid)
558 return 0;
559
560 *_gid = gid;
561 }
562
563 return 1;
564}
565
566static int add_user(Item *i) {
567 void *z;
568 int r;
569
570 assert(i);
571
572 /* Check the database directly */
573 z = hashmap_get(database_user, i->name);
574 if (z) {
575 log_debug("User %s already exists.", i->name);
576 i->uid = PTR_TO_GID(z);
577 i->uid_set = true;
578 return 0;
579 }
580
581 if (!arg_root) {
582 struct passwd *p;
583 struct spwd *sp;
584
585 /* Also check NSS */
586 errno = 0;
587 p = getpwnam(i->name);
588 if (p) {
589 log_debug("User %s already exists.", i->name);
590 i->uid = p->pw_uid;
591 i->uid_set = true;
592
593 free(i->description);
594 i->description = strdup(p->pw_gecos);
595 return 0;
596 }
597 if (errno != 0) {
598 log_error("Failed to check if user %s already exists: %m", i->name);
599 return -errno;
600 }
601
602 /* And shadow too, just to be sure */
603 errno = 0;
604 sp = getspnam(i->name);
605 if (sp) {
606 log_error("User %s already exists in shadow database, but not in user database.", i->name);
607 return -EBADMSG;
608 }
609 if (errno != 0) {
610 log_error("Failed to check if user %s already exists in shadow database: %m", i->name);
611 return -errno;
612 }
613 }
614
615 /* Try to use the suggested numeric uid */
616 if (i->uid_set) {
617 r = uid_is_ok(i->uid, i->name);
618 if (r < 0) {
619 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
620 return r;
621 }
622 if (r == 0) {
623 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
624 i->uid_set = false;
625 }
626 }
627
628 /* If that didn't work, try to read it from the specified path */
629 if (!i->uid_set) {
630 uid_t c;
631
632 if (read_id_from_file(i, &c, NULL) > 0) {
633
634 if (c <= 0 || c > SYSTEM_UID_MAX)
635 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
636 else {
637 r = uid_is_ok(c, i->name);
638 if (r < 0) {
639 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
640 return r;
641 } else if (r > 0) {
642 i->uid = c;
643 i->uid_set = true;
644 } else
645 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
646 }
647 }
648 }
649
650 /* Otherwise try to reuse the group ID */
651 if (!i->uid_set && i->gid_set) {
652 r = uid_is_ok((uid_t) i->gid, i->name);
653 if (r < 0) {
654 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
655 return r;
656 }
657 if (r > 0) {
658 i->uid = (uid_t) i->gid;
659 i->uid_set = true;
660 }
661 }
662
663 /* And if that didn't work either, let's try to find a free one */
664 if (!i->uid_set) {
665 for (; search_uid > 0; search_uid--) {
666
667 r = uid_is_ok(search_uid, i->name);
668 if (r < 0) {
669 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
670 return r;
671 } else if (r > 0)
672 break;
673 }
674
675 if (search_uid <= 0) {
676 log_error("No free user ID available for %s.", i->name);
677 return -E2BIG;
678 }
679
680 i->uid_set = true;
681 i->uid = search_uid;
682
683 search_uid--;
684 }
685
686 r = hashmap_ensure_allocated(&todo_uids, trivial_hash_func, trivial_compare_func);
687 if (r < 0)
688 return log_oom();
689
690 r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
691 if (r < 0)
692 return log_oom();
693
694 i->todo = true;
695 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
696
697 return 0;
698}
699
700static int gid_is_ok(gid_t gid) {
701 struct group *g;
702 struct passwd *p;
703
704 if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
705 return 0;
706
707 /* Avoid reusing gids that are already used by a different user */
708 if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
709 return 0;
710
711 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
712 return 0;
713
714 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
715 return 0;
716
717 if (!arg_root) {
718 errno = 0;
719 g = getgrgid(gid);
720 if (g)
721 return 0;
722 if (errno != 0)
723 return -errno;
724
725 errno = 0;
726 p = getpwuid((uid_t) gid);
727 if (p)
728 return 0;
729 if (errno != 0)
730 return -errno;
731 }
732
733 return 1;
734}
735
736static int add_group(Item *i) {
737 void *z;
738 int r;
739
740 assert(i);
741
742 /* Check the database directly */
743 z = hashmap_get(database_group, i->name);
744 if (z) {
745 log_debug("Group %s already exists.", i->name);
746 i->gid = PTR_TO_GID(z);
747 i->gid_set = true;
748 return 0;
749 }
750
751 /* Also check NSS */
752 if (!arg_root) {
753 struct group *g;
754
755 errno = 0;
756 g = getgrnam(i->name);
757 if (g) {
758 log_debug("Group %s already exists.", i->name);
759 i->gid = g->gr_gid;
760 i->gid_set = true;
761 return 0;
762 }
763 if (errno != 0) {
764 log_error("Failed to check if group %s already exists: %m", i->name);
765 return -errno;
766 }
767 }
768
769 /* Try to use the suggested numeric gid */
770 if (i->gid_set) {
771 r = gid_is_ok(i->gid);
772 if (r < 0) {
773 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
774 return r;
775 }
776 if (r == 0) {
777 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
778 i->gid_set = false;
779 }
780 }
781
782 /* Try to reuse the numeric uid, if there's one */
783 if (!i->gid_set && i->uid_set) {
784 r = gid_is_ok((gid_t) i->uid);
785 if (r < 0) {
786 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
787 return r;
788 }
789 if (r > 0) {
790 i->gid = (gid_t) i->uid;
791 i->gid_set = true;
792 }
793 }
794
795 /* If that didn't work, try to read it from the specified path */
796 if (!i->gid_set) {
797 gid_t c;
798
799 if (read_id_from_file(i, NULL, &c) > 0) {
800
801 if (c <= 0 || c > SYSTEM_GID_MAX)
802 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
803 else {
804 r = gid_is_ok(c);
805 if (r < 0) {
806 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
807 return r;
808 } else if (r > 0) {
809 i->gid = c;
810 i->gid_set = true;
811 } else
812 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
813 }
814 }
815 }
816
817 /* And if that didn't work either, let's try to find a free one */
818 if (!i->gid_set) {
819 for (; search_gid > 0; search_gid--) {
820 r = gid_is_ok(search_gid);
821 if (r < 0) {
822 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
823 return r;
824 } else if (r > 0)
825 break;
826 }
827
828 if (search_gid <= 0) {
829 log_error("No free group ID available for %s.", i->name);
830 return -E2BIG;
831 }
832
833 i->gid_set = true;
834 i->gid = search_gid;
835
836 search_gid--;
837 }
838
839 r = hashmap_ensure_allocated(&todo_gids, trivial_hash_func, trivial_compare_func);
840 if (r < 0)
841 return log_oom();
842
843 r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
844 if (r < 0)
845 return log_oom();
846
847 i->todo = true;
848 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
849
850 return 0;
851}
852
853static int process_item(Item *i) {
854 int r;
855
856 assert(i);
857
858 switch (i->type) {
859
860 case ADD_USER:
861 r = add_group(i);
862 if (r < 0)
863 return r;
864
865 return add_user(i);
866
867 case ADD_GROUP: {
868 Item *j;
869
870 j = hashmap_get(users, i->name);
871 if (j) {
872 /* There's already user to be created for this
873 * name, let's process that in one step */
874
875 if (i->gid_set) {
876 j->gid = i->gid;
877 j->gid_set = true;
878 }
879
880 if (i->gid_path) {
881 free(j->gid_path);
882 j->gid_path = strdup(i->gid_path);
883 if (!j->gid_path)
884 return log_oom();
885 }
886
887 return 0;
888 }
889
890 return add_group(i);
891 }
892 }
893
894 assert_not_reached("Unknown item type");
895}
896
897static void item_free(Item *i) {
898
899 if (!i)
900 return;
901
902 free(i->name);
903 free(i->uid_path);
904 free(i->gid_path);
905 free(i->description);
906 free(i);
907}
908
909DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
910
911static bool item_equal(Item *a, Item *b) {
912 assert(a);
913 assert(b);
914
915 if (a->type != b->type)
916 return false;
917
918 if (!streq_ptr(a->name, b->name))
919 return false;
920
921 if (!streq_ptr(a->uid_path, b->uid_path))
922 return false;
923
924 if (!streq_ptr(a->gid_path, b->gid_path))
925 return false;
926
927 if (!streq_ptr(a->description, b->description))
928 return false;
929
930 if (a->uid_set != b->uid_set)
931 return false;
932
933 if (a->uid_set && a->uid != b->uid)
934 return false;
935
936 if (a->gid_set != b->gid_set)
937 return false;
938
939 if (a->gid_set && a->gid != b->gid)
940 return false;
941
942 return true;
943}
944
945static bool valid_user_group_name(const char *u) {
946 const char *i;
947 long sz;
948
949 if (isempty(u) < 0)
950 return false;
951
952 if (!(u[0] >= 'a' && u[0] <= 'z') &&
953 !(u[0] >= 'A' && u[0] <= 'Z') &&
954 u[0] != '_')
955 return false;
956
957 for (i = u+1; *i; i++) {
958 if (!(*i >= 'a' && *i <= 'z') &&
959 !(*i >= 'A' && *i <= 'Z') &&
960 !(*i >= '0' && *i <= '9') &&
961 *i != '_' &&
962 *i != '-')
963 return false;
964 }
965
966 sz = sysconf(_SC_LOGIN_NAME_MAX);
967 assert_se(sz > 0);
968
969 if ((size_t) (i-u) > (size_t) sz)
970 return false;
971
972 return true;
973}
974
975static bool valid_gecos(const char *d) {
976
977 if (!utf8_is_valid(d))
978 return false;
979
980 if (strpbrk(d, ":\n"))
981 return false;
982
983 return true;
984}
985
986static int parse_line(const char *fname, unsigned line, const char *buffer) {
987
988 static const Specifier specifier_table[] = {
989 { 'm', specifier_machine_id, NULL },
990 { 'b', specifier_boot_id, NULL },
991 { 'H', specifier_host_name, NULL },
992 { 'v', specifier_kernel_release, NULL },
993 {}
994 };
995
996 _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL;
997 _cleanup_(item_freep) Item *i = NULL;
998 Item *existing;
999 Hashmap *h;
1000 int r, n = -1;
1001
1002 assert(fname);
1003 assert(line >= 1);
1004 assert(buffer);
1005
1006 r = sscanf(buffer,
1007 "%ms %ms %ms %n",
1008 &action,
1009 &name,
1010 &id,
1011 &n);
1012 if (r < 2) {
1013 log_error("[%s:%u] Syntax error.", fname, line);
1014 return -EIO;
1015 }
1016
1017 if (strlen(action) != 1) {
1018 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1019 return -EINVAL;
1020 }
1021
1022 if (!IN_SET(action[0], ADD_USER, ADD_GROUP)) {
1023 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
1024 return -EBADMSG;
1025 }
1026
1027 i = new0(Item, 1);
1028 if (!i)
1029 return log_oom();
1030
1031 i->type = action[0];
1032
1033 r = specifier_printf(name, specifier_table, NULL, &i->name);
1034 if (r < 0) {
1035 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1036 return r;
1037 }
1038
1039 if (!valid_user_group_name(i->name)) {
1040 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, i->name);
1041 return -EINVAL;
1042 }
1043
1044 if (n >= 0) {
1045 n += strspn(buffer+n, WHITESPACE);
1046 if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) {
1047 i->description = unquote(buffer+n, "\"");
1048 if (!i->description)
1049 return log_oom();
1050
1051 if (!valid_gecos(i->description)) {
1052 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, i->description);
1053 return -EINVAL;
1054 }
1055 }
1056 }
1057
1058 if (id && !streq(id, "-")) {
1059
1060 if (path_is_absolute(id)) {
1061 char *p;
1062
1063 p = strdup(id);
1064 if (!p)
1065 return log_oom();
1066
1067 path_kill_slashes(p);
1068
1069 if (i->type == ADD_USER)
1070 i->uid_path = p;
1071 else
1072 i->gid_path = p;
1073
1074 } else if (i->type == ADD_USER) {
1075 r = parse_uid(id, &i->uid);
1076 if (r < 0) {
1077 log_error("Failed to parse UID: %s", id);
1078 return -EBADMSG;
1079 }
1080
1081 i->uid_set = true;
1082
1083 } else {
1084 assert(i->type == ADD_GROUP);
1085
1086 r = parse_gid(id, &i->gid);
1087 if (r < 0) {
1088 log_error("Failed to parse GID: %s", id);
1089 return -EBADMSG;
1090 }
1091
1092 i->gid_set = true;
1093 }
1094 }
1095
1096 if (i->type == ADD_USER) {
1097 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1098 h = users;
1099 } else {
1100 assert(i->type == ADD_GROUP);
1101 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1102 h = groups;
1103 }
1104 if (r < 0)
1105 return log_oom();
1106
1107 existing = hashmap_get(h, i->name);
1108 if (existing) {
1109
1110 /* Two identical items are fine */
1111 if (!item_equal(existing, i))
1112 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1113
1114 return 0;
1115 }
1116
1117 r = hashmap_put(h, i->name, i);
1118 if (r < 0) {
1119 log_error("Failed to insert item %s: %s", i->name, strerror(-r));
1120 return r;
1121 }
1122
1123 i = NULL;
1124 return 0;
1125}
1126
1127static int read_config_file(const char *fn, bool ignore_enoent) {
1128 _cleanup_fclose_ FILE *f = NULL;
1129 char line[LINE_MAX];
1130 unsigned v = 0;
1131 int r;
1132
1133 assert(fn);
1134
1135 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
1136 if (r < 0) {
1137 if (ignore_enoent && r == -ENOENT)
1138 return 0;
1139
1140 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1141 return r;
1142 }
1143
1144 FOREACH_LINE(line, f, break) {
1145 char *l;
1146 int k;
1147
1148 v++;
1149
1150 l = strstrip(line);
1151 if (*l == '#' || *l == 0)
1152 continue;
1153
1154 k = parse_line(fn, v, l);
1155 if (k < 0 && r == 0)
1156 r = k;
1157 }
1158
1159 if (ferror(f)) {
1160 log_error("Failed to read from file %s: %m", fn);
1161 if (r == 0)
1162 r = -EIO;
1163 }
1164
1165 return r;
1166}
1167
1168static int take_lock(void) {
1169
1170 struct flock flock = {
1171 .l_type = F_WRLCK,
1172 .l_whence = SEEK_SET,
1173 .l_start = 0,
1174 .l_len = 0,
1175 };
1176
1177 const char *path;
1178 int fd, r;
1179
1180 /* This is roughly the same as lckpwdf(), but not as awful. We
1181 * don't want to use alarm() and signals, hence we implement
1182 * our own trivial version of this.
1183 *
1184 * Note that shadow-utils also takes per-database locks in
1185 * addition to lckpwdf(). However, we don't given that they
1186 * are redundant as they they invoke lckpwdf() first and keep
1187 * it during everything they do. The per-database locks are
1188 * awfully racy, and thus we just won't do them. */
1189
1190 path = fix_root("/etc/.pwd.lock");
1191 fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
1192 if (fd < 0)
1193 return -errno;
1194
1195 r = fcntl(fd, F_SETLKW, &flock);
1196 if (r < 0) {
1197 safe_close(fd);
1198 return -errno;
1199 }
1200
1201 return fd;
1202}
1203
1204static void free_database(Hashmap *by_name, Hashmap *by_id) {
1205 char *name;
1206
1207 for (;;) {
1208 name = hashmap_first(by_id);
1209 if (!name)
1210 break;
1211
1212 hashmap_remove(by_name, name);
1213
1214 hashmap_steal_first_key(by_id);
1215 free(name);
1216 }
1217
1218 while ((name = hashmap_steal_first_key(by_name)))
1219 free(name);
1220
1221 hashmap_free(by_name);
1222 hashmap_free(by_id);
1223}
1224
1225static int help(void) {
1226
1227 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1228 "Creates system user accounts.\n\n"
1229 " -h --help Show this help\n"
1230 " --version Show package version\n"
1231 " --root=PATH Operate on an alternate filesystem root\n",
1232 program_invocation_short_name);
1233
1234 return 0;
1235}
1236
1237static int parse_argv(int argc, char *argv[]) {
1238
1239 enum {
1240 ARG_VERSION = 0x100,
1241 ARG_ROOT,
1242 };
1243
1244 static const struct option options[] = {
1245 { "help", no_argument, NULL, 'h' },
1246 { "version", no_argument, NULL, ARG_VERSION },
1247 { "root", required_argument, NULL, ARG_ROOT },
1248 {}
1249 };
1250
1251 int c;
1252
1253 assert(argc >= 0);
1254 assert(argv);
1255
1256 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
1257
1258 switch (c) {
1259
1260 case 'h':
1261 return help();
1262
1263 case ARG_VERSION:
1264 puts(PACKAGE_STRING);
1265 puts(SYSTEMD_FEATURES);
1266 return 0;
1267
1268 case ARG_ROOT:
1269 free(arg_root);
1270 arg_root = path_make_absolute_cwd(optarg);
1271 if (!arg_root)
1272 return log_oom();
1273
1274 path_kill_slashes(arg_root);
1275 break;
1276
1277 case '?':
1278 return -EINVAL;
1279
1280 default:
1281 assert_not_reached("Unhandled option");
1282 }
1283 }
1284
1285 return 1;
1286}
1287
1288int main(int argc, char *argv[]) {
1289
1290 _cleanup_close_ int lock = -1;
1291 Iterator iterator;
1292 int r, k;
1293 Item *i;
1294
1295 r = parse_argv(argc, argv);
1296 if (r <= 0)
1297 goto finish;
1298
1299 log_set_target(LOG_TARGET_AUTO);
1300 log_parse_environment();
1301 log_open();
1302
1303 umask(0022);
1304
1305 r = 0;
1306
1307 if (optind < argc) {
1308 int j;
1309
1310 for (j = optind; j < argc; j++) {
1311 k = read_config_file(argv[j], false);
1312 if (k < 0 && r == 0)
1313 r = k;
1314 }
1315 } else {
1316 _cleanup_strv_free_ char **files = NULL;
1317 char **f;
1318
1319 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1320 if (r < 0) {
1321 log_error("Failed to enumerate sysusers.d files: %s", strerror(-r));
1322 goto finish;
1323 }
1324
1325 STRV_FOREACH(f, files) {
1326 k = read_config_file(*f, true);
1327 if (k < 0 && r == 0)
1328 r = k;
1329 }
1330 }
1331
1332 lock = take_lock();
1333 if (lock < 0) {
1334 log_error("Failed to take lock: %s", strerror(-lock));
1335 goto finish;
1336 }
1337
1338 r = load_user_database();
1339 if (r < 0) {
1340 log_error("Failed to load user database: %s", strerror(-r));
1341 goto finish;
1342 }
1343
1344 r = load_group_database();
1345 if (r < 0) {
1346 log_error("Failed to read group database: %s", strerror(-r));
1347 goto finish;
1348 }
1349
1350 HASHMAP_FOREACH(i, groups, iterator)
1351 process_item(i);
1352
1353 HASHMAP_FOREACH(i, users, iterator)
1354 process_item(i);
1355
1356 r = write_files();
1357 if (r < 0)
1358 log_error("Failed to write files: %s", strerror(-r));
1359
1360finish:
1361 while ((i = hashmap_steal_first(groups)))
1362 item_free(i);
1363
1364 while ((i = hashmap_steal_first(users)))
1365 item_free(i);
1366
1367 hashmap_free(groups);
1368 hashmap_free(users);
1369 hashmap_free(todo_uids);
1370 hashmap_free(todo_gids);
1371
1372 free_database(database_user, database_uid);
1373 free_database(database_group, database_gid);
1374
1375 free(arg_root);
1376
1377 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1378}