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