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