chiark / gitweb /
sysusers: don't allow user names longer than UT_NAMESIZE
[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 (strpbrk(d, ":\n"))
1111                 return false;
1112
1113         return true;
1114 }
1115
1116 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1117
1118         static const Specifier specifier_table[] = {
1119                 { 'm', specifier_machine_id, NULL },
1120                 { 'b', specifier_boot_id, NULL },
1121                 { 'H', specifier_host_name, NULL },
1122                 { 'v', specifier_kernel_release, NULL },
1123                 {}
1124         };
1125
1126         _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL;
1127         _cleanup_(item_freep) Item *i = NULL;
1128         Item *existing;
1129         Hashmap *h;
1130         int r, n = -1;
1131
1132         assert(fname);
1133         assert(line >= 1);
1134         assert(buffer);
1135
1136         r = sscanf(buffer,
1137                    "%ms %ms %ms %n",
1138                    &action,
1139                    &name,
1140                    &id,
1141                    &n);
1142         if (r < 2) {
1143                 log_error("[%s:%u] Syntax error.", fname, line);
1144                 return -EIO;
1145         }
1146
1147         if (strlen(action) != 1) {
1148                 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1149                 return -EINVAL;
1150         }
1151
1152         if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER)) {
1153                 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
1154                 return -EBADMSG;
1155         }
1156
1157         r = specifier_printf(name, specifier_table, NULL, &resolved_name);
1158         if (r < 0) {
1159                 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1160                 return r;
1161         }
1162
1163         if (!valid_user_group_name(resolved_name)) {
1164                 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
1165                 return -EINVAL;
1166         }
1167
1168         if (n >= 0) {
1169                 n += strspn(buffer+n, WHITESPACE);
1170
1171                 if (STR_IN_SET(buffer + n, "", "-"))
1172                         n = -1;
1173         }
1174
1175         switch (action[0]) {
1176
1177         case ADD_MEMBER: {
1178                 _cleanup_free_ char *resolved_id = NULL;
1179                 char **l;
1180
1181                 r = hashmap_ensure_allocated(&members, string_hash_func, string_compare_func);
1182                 if (r < 0)
1183                         return log_oom();
1184
1185                 /* Try to extend an existing member or group item */
1186
1187                 if (!id || streq(id, "-")) {
1188                         log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1189                         return -EINVAL;
1190                 }
1191
1192                 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1193                 if (r < 0) {
1194                         log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1195                         return r;
1196                 }
1197
1198                 if (!valid_user_group_name(resolved_id)) {
1199                         log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1200                         return -EINVAL;
1201                 }
1202
1203                 if (n >= 0) {
1204                         log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
1205                         return -EINVAL;
1206                 }
1207
1208                 l = hashmap_get(members, resolved_id);
1209                 if (l) {
1210                         /* A list for this group name already exists, let's append to it */
1211                         r = strv_push(&l, resolved_name);
1212                         if (r < 0)
1213                                 return log_oom();
1214
1215                         resolved_name = NULL;
1216
1217                         assert_se(hashmap_update(members, resolved_id, l) >= 0);
1218                 } else {
1219                         /* No list for this group name exists yet, create one */
1220
1221                         l = new0(char *, 2);
1222                         if (!l)
1223                                 return -ENOMEM;
1224
1225                         l[0] = resolved_name;
1226                         l[1] = NULL;
1227
1228                         r = hashmap_put(members, resolved_id, l);
1229                         if (r < 0) {
1230                                 free(l);
1231                                 return log_oom();
1232                         }
1233
1234                         resolved_id = resolved_name = NULL;
1235                 }
1236
1237                 return 0;
1238         }
1239
1240         case ADD_USER:
1241                 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1242                 if (r < 0)
1243                         return log_oom();
1244
1245                 i = new0(Item, 1);
1246                 if (!i)
1247                         return log_oom();
1248
1249                 if (id && !streq(id, "-")) {
1250
1251                         if (path_is_absolute(id)) {
1252                                 i->uid_path = strdup(id);
1253                                 if (!i->uid_path)
1254                                         return log_oom();
1255
1256                                 path_kill_slashes(i->uid_path);
1257
1258                         } else {
1259                                 r = parse_uid(id, &i->uid);
1260                                 if (r < 0) {
1261                                         log_error("Failed to parse UID: %s", id);
1262                                         return -EBADMSG;
1263                                 }
1264
1265                                 i->uid_set = true;
1266                         }
1267                 }
1268
1269                 if (n >= 0) {
1270                         i->description = unquote(buffer+n, "\"");
1271                         if (!i->description)
1272                                 return log_oom();
1273
1274                         if (!valid_gecos(i->description)) {
1275                                 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, i->description);
1276                                 return -EINVAL;
1277                         }
1278                 }
1279
1280                 h = users;
1281                 break;
1282
1283         case ADD_GROUP:
1284                 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1285                 if (r < 0)
1286                         return log_oom();
1287
1288                 if (n >= 0) {
1289                         log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
1290                         return -EINVAL;
1291                 }
1292
1293                 i = new0(Item, 1);
1294                 if (!i)
1295                         return log_oom();
1296
1297                 if (id && !streq(id, "-")) {
1298
1299                         if (path_is_absolute(id)) {
1300                                 i->gid_path = strdup(id);
1301                                 if (!i->gid_path)
1302                                         return log_oom();
1303
1304                                 path_kill_slashes(i->gid_path);
1305                         } else {
1306                                 r = parse_gid(id, &i->gid);
1307                                 if (r < 0) {
1308                                         log_error("Failed to parse GID: %s", id);
1309                                         return -EBADMSG;
1310                                 }
1311
1312                                 i->gid_set = true;
1313                         }
1314                 }
1315
1316
1317                 h = groups;
1318                 break;
1319         default:
1320                 return -EBADMSG;
1321         }
1322
1323         i->type = action[0];
1324         i->name = resolved_name;
1325         resolved_name = NULL;
1326
1327         existing = hashmap_get(h, i->name);
1328         if (existing) {
1329
1330                 /* Two identical items are fine */
1331                 if (!item_equal(existing, i))
1332                         log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1333
1334                 return 0;
1335         }
1336
1337         r = hashmap_put(h, i->name, i);
1338         if (r < 0)
1339                 return log_oom();
1340
1341         i = NULL;
1342         return 0;
1343 }
1344
1345 static int read_config_file(const char *fn, bool ignore_enoent) {
1346         _cleanup_fclose_ FILE *f = NULL;
1347         char line[LINE_MAX];
1348         unsigned v = 0;
1349         int r;
1350
1351         assert(fn);
1352
1353         r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
1354         if (r < 0) {
1355                 if (ignore_enoent && r == -ENOENT)
1356                         return 0;
1357
1358                 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1359                 return r;
1360         }
1361
1362         FOREACH_LINE(line, f, break) {
1363                 char *l;
1364                 int k;
1365
1366                 v++;
1367
1368                 l = strstrip(line);
1369                 if (*l == '#' || *l == 0)
1370                         continue;
1371
1372                 k = parse_line(fn, v, l);
1373                 if (k < 0 && r == 0)
1374                         r = k;
1375         }
1376
1377         if (ferror(f)) {
1378                 log_error("Failed to read from file %s: %m", fn);
1379                 if (r == 0)
1380                         r = -EIO;
1381         }
1382
1383         return r;
1384 }
1385
1386 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1387         char *name;
1388
1389         for (;;) {
1390                 name = hashmap_first(by_id);
1391                 if (!name)
1392                         break;
1393
1394                 hashmap_remove(by_name, name);
1395
1396                 hashmap_steal_first_key(by_id);
1397                 free(name);
1398         }
1399
1400         while ((name = hashmap_steal_first_key(by_name)))
1401                 free(name);
1402
1403         hashmap_free(by_name);
1404         hashmap_free(by_id);
1405 }
1406
1407 static int help(void) {
1408
1409         printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1410                "Creates system user accounts.\n\n"
1411                "  -h --help                 Show this help\n"
1412                "     --version              Show package version\n"
1413                "     --root=PATH            Operate on an alternate filesystem root\n",
1414                program_invocation_short_name);
1415
1416         return 0;
1417 }
1418
1419 static int parse_argv(int argc, char *argv[]) {
1420
1421         enum {
1422                 ARG_VERSION = 0x100,
1423                 ARG_ROOT,
1424         };
1425
1426         static const struct option options[] = {
1427                 { "help",    no_argument,       NULL, 'h'         },
1428                 { "version", no_argument,       NULL, ARG_VERSION },
1429                 { "root",    required_argument, NULL, ARG_ROOT    },
1430                 {}
1431         };
1432
1433         int c;
1434
1435         assert(argc >= 0);
1436         assert(argv);
1437
1438         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
1439
1440                 switch (c) {
1441
1442                 case 'h':
1443                         return help();
1444
1445                 case ARG_VERSION:
1446                         puts(PACKAGE_STRING);
1447                         puts(SYSTEMD_FEATURES);
1448                         return 0;
1449
1450                 case ARG_ROOT:
1451                         free(arg_root);
1452                         arg_root = path_make_absolute_cwd(optarg);
1453                         if (!arg_root)
1454                                 return log_oom();
1455
1456                         path_kill_slashes(arg_root);
1457                         break;
1458
1459                 case '?':
1460                         return -EINVAL;
1461
1462                 default:
1463                         assert_not_reached("Unhandled option");
1464                 }
1465         }
1466
1467         return 1;
1468 }
1469
1470 int main(int argc, char *argv[]) {
1471
1472         _cleanup_close_ int lock = -1;
1473         Iterator iterator;
1474         int r, k;
1475         Item *i;
1476         char *n;
1477
1478         r = parse_argv(argc, argv);
1479         if (r <= 0)
1480                 goto finish;
1481
1482         log_set_target(LOG_TARGET_AUTO);
1483         log_parse_environment();
1484         log_open();
1485
1486         umask(0022);
1487
1488         r = 0;
1489
1490         if (optind < argc) {
1491                 int j;
1492
1493                 for (j = optind; j < argc; j++) {
1494                         k = read_config_file(argv[j], false);
1495                         if (k < 0 && r == 0)
1496                                 r = k;
1497                 }
1498         } else {
1499                 _cleanup_strv_free_ char **files = NULL;
1500                 char **f;
1501
1502                 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1503                 if (r < 0) {
1504                         log_error("Failed to enumerate sysusers.d files: %s", strerror(-r));
1505                         goto finish;
1506                 }
1507
1508                 STRV_FOREACH(f, files) {
1509                         k = read_config_file(*f, true);
1510                         if (k < 0 && r == 0)
1511                                 r = k;
1512                 }
1513         }
1514
1515         r = add_implicit();
1516         if (r < 0)
1517                 goto finish;
1518
1519         lock = take_password_lock(arg_root);
1520         if (lock < 0) {
1521                 log_error("Failed to take lock: %s", strerror(-lock));
1522                 goto finish;
1523         }
1524
1525         r = load_user_database();
1526         if (r < 0) {
1527                 log_error("Failed to load user database: %s", strerror(-r));
1528                 goto finish;
1529         }
1530
1531         r = load_group_database();
1532         if (r < 0) {
1533                 log_error("Failed to read group database: %s", strerror(-r));
1534                 goto finish;
1535         }
1536
1537         HASHMAP_FOREACH(i, groups, iterator)
1538                 process_item(i);
1539
1540         HASHMAP_FOREACH(i, users, iterator)
1541                 process_item(i);
1542
1543         r = write_files();
1544         if (r < 0)
1545                 log_error("Failed to write files: %s", strerror(-r));
1546
1547 finish:
1548         while ((i = hashmap_steal_first(groups)))
1549                 item_free(i);
1550
1551         while ((i = hashmap_steal_first(users)))
1552                 item_free(i);
1553
1554         while ((n = hashmap_first_key(members))) {
1555                 strv_free(hashmap_steal_first(members));
1556                 free(n);
1557         }
1558
1559         hashmap_free(groups);
1560         hashmap_free(users);
1561         hashmap_free(members);
1562         hashmap_free(todo_uids);
1563         hashmap_free(todo_gids);
1564
1565         free_database(database_user, database_uid);
1566         free_database(database_group, database_gid);
1567
1568         free(arg_root);
1569
1570         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1571 }