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