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