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