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