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