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