chiark / gitweb /
c0af69300ac9bc6a4141585e6ee0572d9b2146d2
[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         default:
1316                 return -EBADMSG;
1317         }
1318
1319         i->type = action[0];
1320         i->name = resolved_name;
1321         resolved_name = NULL;
1322
1323         existing = hashmap_get(h, i->name);
1324         if (existing) {
1325
1326                 /* Two identical items are fine */
1327                 if (!item_equal(existing, i))
1328                         log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1329
1330                 return 0;
1331         }
1332
1333         r = hashmap_put(h, i->name, i);
1334         if (r < 0)
1335                 return log_oom();
1336
1337         i = NULL;
1338         return 0;
1339 }
1340
1341 static int read_config_file(const char *fn, bool ignore_enoent) {
1342         _cleanup_fclose_ FILE *f = NULL;
1343         char line[LINE_MAX];
1344         unsigned v = 0;
1345         int r;
1346
1347         assert(fn);
1348
1349         r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
1350         if (r < 0) {
1351                 if (ignore_enoent && r == -ENOENT)
1352                         return 0;
1353
1354                 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1355                 return r;
1356         }
1357
1358         FOREACH_LINE(line, f, break) {
1359                 char *l;
1360                 int k;
1361
1362                 v++;
1363
1364                 l = strstrip(line);
1365                 if (*l == '#' || *l == 0)
1366                         continue;
1367
1368                 k = parse_line(fn, v, l);
1369                 if (k < 0 && r == 0)
1370                         r = k;
1371         }
1372
1373         if (ferror(f)) {
1374                 log_error("Failed to read from file %s: %m", fn);
1375                 if (r == 0)
1376                         r = -EIO;
1377         }
1378
1379         return r;
1380 }
1381
1382 static int take_lock(void) {
1383
1384         struct flock flock = {
1385                 .l_type = F_WRLCK,
1386                 .l_whence = SEEK_SET,
1387                 .l_start = 0,
1388                 .l_len = 0,
1389         };
1390
1391         const char *path;
1392         int fd, r;
1393
1394         /* This is roughly the same as lckpwdf(), but not as awful. We
1395          * don't want to use alarm() and signals, hence we implement
1396          * our own trivial version of this.
1397          *
1398          * Note that shadow-utils also takes per-database locks in
1399          * addition to lckpwdf(). However, we don't given that they
1400          * are redundant as they they invoke lckpwdf() first and keep
1401          * it during everything they do. The per-database locks are
1402          * awfully racy, and thus we just won't do them. */
1403
1404         path = fix_root("/etc/.pwd.lock");
1405         fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
1406         if (fd < 0)
1407                 return -errno;
1408
1409         r = fcntl(fd, F_SETLKW, &flock);
1410         if (r < 0) {
1411                 safe_close(fd);
1412                 return -errno;
1413         }
1414
1415         return fd;
1416 }
1417
1418 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1419         char *name;
1420
1421         for (;;) {
1422                 name = hashmap_first(by_id);
1423                 if (!name)
1424                         break;
1425
1426                 hashmap_remove(by_name, name);
1427
1428                 hashmap_steal_first_key(by_id);
1429                 free(name);
1430         }
1431
1432         while ((name = hashmap_steal_first_key(by_name)))
1433                 free(name);
1434
1435         hashmap_free(by_name);
1436         hashmap_free(by_id);
1437 }
1438
1439 static int help(void) {
1440
1441         printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1442                "Creates system user accounts.\n\n"
1443                "  -h --help                 Show this help\n"
1444                "     --version              Show package version\n"
1445                "     --root=PATH            Operate on an alternate filesystem root\n",
1446                program_invocation_short_name);
1447
1448         return 0;
1449 }
1450
1451 static int parse_argv(int argc, char *argv[]) {
1452
1453         enum {
1454                 ARG_VERSION = 0x100,
1455                 ARG_ROOT,
1456         };
1457
1458         static const struct option options[] = {
1459                 { "help",    no_argument,       NULL, 'h'         },
1460                 { "version", no_argument,       NULL, ARG_VERSION },
1461                 { "root",    required_argument, NULL, ARG_ROOT    },
1462                 {}
1463         };
1464
1465         int c;
1466
1467         assert(argc >= 0);
1468         assert(argv);
1469
1470         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
1471
1472                 switch (c) {
1473
1474                 case 'h':
1475                         return help();
1476
1477                 case ARG_VERSION:
1478                         puts(PACKAGE_STRING);
1479                         puts(SYSTEMD_FEATURES);
1480                         return 0;
1481
1482                 case ARG_ROOT:
1483                         free(arg_root);
1484                         arg_root = path_make_absolute_cwd(optarg);
1485                         if (!arg_root)
1486                                 return log_oom();
1487
1488                         path_kill_slashes(arg_root);
1489                         break;
1490
1491                 case '?':
1492                         return -EINVAL;
1493
1494                 default:
1495                         assert_not_reached("Unhandled option");
1496                 }
1497         }
1498
1499         return 1;
1500 }
1501
1502 int main(int argc, char *argv[]) {
1503
1504         _cleanup_close_ int lock = -1;
1505         Iterator iterator;
1506         int r, k;
1507         Item *i;
1508         char *n;
1509
1510         r = parse_argv(argc, argv);
1511         if (r <= 0)
1512                 goto finish;
1513
1514         log_set_target(LOG_TARGET_AUTO);
1515         log_parse_environment();
1516         log_open();
1517
1518         umask(0022);
1519
1520         r = 0;
1521
1522         if (optind < argc) {
1523                 int j;
1524
1525                 for (j = optind; j < argc; j++) {
1526                         k = read_config_file(argv[j], false);
1527                         if (k < 0 && r == 0)
1528                                 r = k;
1529                 }
1530         } else {
1531                 _cleanup_strv_free_ char **files = NULL;
1532                 char **f;
1533
1534                 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1535                 if (r < 0) {
1536                         log_error("Failed to enumerate sysusers.d files: %s", strerror(-r));
1537                         goto finish;
1538                 }
1539
1540                 STRV_FOREACH(f, files) {
1541                         k = read_config_file(*f, true);
1542                         if (k < 0 && r == 0)
1543                                 r = k;
1544                 }
1545         }
1546
1547         r = add_implicit();
1548         if (r < 0)
1549                 goto finish;
1550
1551         lock = take_lock();
1552         if (lock < 0) {
1553                 log_error("Failed to take lock: %s", strerror(-lock));
1554                 goto finish;
1555         }
1556
1557         r = load_user_database();
1558         if (r < 0) {
1559                 log_error("Failed to load user database: %s", strerror(-r));
1560                 goto finish;
1561         }
1562
1563         r = load_group_database();
1564         if (r < 0) {
1565                 log_error("Failed to read group database: %s", strerror(-r));
1566                 goto finish;
1567         }
1568
1569         HASHMAP_FOREACH(i, groups, iterator)
1570                 process_item(i);
1571
1572         HASHMAP_FOREACH(i, users, iterator)
1573                 process_item(i);
1574
1575         r = write_files();
1576         if (r < 0)
1577                 log_error("Failed to write files: %s", strerror(-r));
1578
1579 finish:
1580         while ((i = hashmap_steal_first(groups)))
1581                 item_free(i);
1582
1583         while ((i = hashmap_steal_first(users)))
1584                 item_free(i);
1585
1586         while ((n = hashmap_first_key(members))) {
1587                 strv_free(hashmap_steal_first(members));
1588                 free(n);
1589         }
1590
1591         hashmap_free(groups);
1592         hashmap_free(users);
1593         hashmap_free(members);
1594         hashmap_free(todo_uids);
1595         hashmap_free(todo_gids);
1596
1597         free_database(database_user, database_uid);
1598         free_database(database_group, database_gid);
1599
1600         free(arg_root);
1601
1602         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1603 }