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