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