1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <getopt.h>
4 #include <utmp.h>
5 
6 #include "alloc-util.h"
7 #include "conf-files.h"
8 #include "copy.h"
9 #include "creds-util.h"
10 #include "def.h"
11 #include "dissect-image.h"
12 #include "fd-util.h"
13 #include "fileio.h"
14 #include "format-util.h"
15 #include "fs-util.h"
16 #include "hashmap.h"
17 #include "libcrypt-util.h"
18 #include "main-func.h"
19 #include "memory-util.h"
20 #include "mount-util.h"
21 #include "nscd-flush.h"
22 #include "pager.h"
23 #include "parse-argument.h"
24 #include "path-util.h"
25 #include "pretty-print.h"
26 #include "selinux-util.h"
27 #include "set.h"
28 #include "smack-util.h"
29 #include "specifier.h"
30 #include "string-util.h"
31 #include "strv.h"
32 #include "sync-util.h"
33 #include "tmpfile-util-label.h"
34 #include "uid-alloc-range.h"
35 #include "uid-range.h"
36 #include "user-util.h"
37 #include "utf8.h"
38 #include "util.h"
39 
40 typedef enum ItemType {
41         ADD_USER =   'u',
42         ADD_GROUP =  'g',
43         ADD_MEMBER = 'm',
44         ADD_RANGE =  'r',
45 } ItemType;
46 
item_type_to_string(ItemType t)47 static inline const char* item_type_to_string(ItemType t) {
48         switch (t) {
49         case ADD_USER:
50                 return "user";
51         case ADD_GROUP:
52                 return "group";
53         case ADD_MEMBER:
54                 return "member";
55         case ADD_RANGE:
56                 return "range";
57         default:
58                 assert_not_reached();
59         }
60 }
61 
62 typedef struct Item {
63         ItemType type;
64 
65         char *name;
66         char *group_name;
67         char *uid_path;
68         char *gid_path;
69         char *description;
70         char *home;
71         char *shell;
72 
73         gid_t gid;
74         uid_t uid;
75 
76         bool gid_set:1;
77 
78         /* When set the group with the specified GID must exist
79          * and the check if a UID clashes with the GID is skipped.
80          */
81         bool id_set_strict:1;
82 
83         bool uid_set:1;
84 
85         bool todo_user:1;
86         bool todo_group:1;
87 } Item;
88 
89 static char *arg_root = NULL;
90 static char *arg_image = NULL;
91 static bool arg_cat_config = false;
92 static const char *arg_replace = NULL;
93 static bool arg_dry_run = false;
94 static bool arg_inline = false;
95 static PagerFlags arg_pager_flags = 0;
96 
97 static OrderedHashmap *users = NULL, *groups = NULL;
98 static OrderedHashmap *todo_uids = NULL, *todo_gids = NULL;
99 static OrderedHashmap *members = NULL;
100 
101 static Hashmap *database_by_uid = NULL, *database_by_username = NULL;
102 static Hashmap *database_by_gid = NULL, *database_by_groupname = NULL;
103 static Set *database_users = NULL, *database_groups = NULL;
104 
105 static uid_t search_uid = UID_INVALID;
106 static UidRange *uid_range = NULL;
107 static size_t n_uid_range = 0;
108 
109 static UGIDAllocationRange login_defs = {};
110 static bool login_defs_need_warning = false;
111 
112 STATIC_DESTRUCTOR_REGISTER(groups, ordered_hashmap_freep);
113 STATIC_DESTRUCTOR_REGISTER(users, ordered_hashmap_freep);
114 STATIC_DESTRUCTOR_REGISTER(members, ordered_hashmap_freep);
115 STATIC_DESTRUCTOR_REGISTER(todo_uids, ordered_hashmap_freep);
116 STATIC_DESTRUCTOR_REGISTER(todo_gids, ordered_hashmap_freep);
117 STATIC_DESTRUCTOR_REGISTER(database_by_uid, hashmap_freep);
118 STATIC_DESTRUCTOR_REGISTER(database_by_username, hashmap_freep);
119 STATIC_DESTRUCTOR_REGISTER(database_users, set_free_freep);
120 STATIC_DESTRUCTOR_REGISTER(database_by_gid, hashmap_freep);
121 STATIC_DESTRUCTOR_REGISTER(database_by_groupname, hashmap_freep);
122 STATIC_DESTRUCTOR_REGISTER(database_groups, set_free_freep);
123 STATIC_DESTRUCTOR_REGISTER(uid_range, freep);
124 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
125 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
126 
errno_is_not_exists(int code)127 static int errno_is_not_exists(int code) {
128         /* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are
129          * not found. */
130         return IN_SET(code, 0, ENOENT, ESRCH, EBADF, EPERM);
131 }
132 
maybe_emit_login_defs_warning(void)133 static void maybe_emit_login_defs_warning(void) {
134         if (!login_defs_need_warning)
135                 return;
136 
137         if (login_defs.system_alloc_uid_min != SYSTEM_ALLOC_UID_MIN ||
138             login_defs.system_uid_max != SYSTEM_UID_MAX)
139                 log_warning("login.defs specifies UID allocation range "UID_FMT"–"UID_FMT
140                             " that is different than the built-in defaults ("UID_FMT"–"UID_FMT")",
141                             login_defs.system_alloc_uid_min, login_defs.system_uid_max,
142                             SYSTEM_ALLOC_UID_MIN, SYSTEM_UID_MAX);
143         if (login_defs.system_alloc_gid_min != SYSTEM_ALLOC_GID_MIN ||
144             login_defs.system_gid_max != SYSTEM_GID_MAX)
145                 log_warning("login.defs specifies GID allocation range "GID_FMT"–"GID_FMT
146                             " that is different than the built-in defaults ("GID_FMT"–"GID_FMT")",
147                             login_defs.system_alloc_gid_min, login_defs.system_gid_max,
148                             SYSTEM_ALLOC_GID_MIN, SYSTEM_GID_MAX);
149 
150         login_defs_need_warning = false;
151 }
152 
load_user_database(void)153 static int load_user_database(void) {
154         _cleanup_fclose_ FILE *f = NULL;
155         const char *passwd_path;
156         struct passwd *pw;
157         int r;
158 
159         passwd_path = prefix_roota(arg_root, "/etc/passwd");
160         f = fopen(passwd_path, "re");
161         if (!f)
162                 return errno == ENOENT ? 0 : -errno;
163 
164         r = hashmap_ensure_allocated(&database_by_username, &string_hash_ops);
165         if (r < 0)
166                 return r;
167 
168         r = hashmap_ensure_allocated(&database_by_uid, NULL);
169         if (r < 0)
170                 return r;
171 
172         r = set_ensure_allocated(&database_users, NULL);
173         if (r < 0)
174                 return r;
175 
176         while ((r = fgetpwent_sane(f, &pw)) > 0) {
177                 char *n;
178                 int k, q;
179 
180                 n = strdup(pw->pw_name);
181                 if (!n)
182                         return -ENOMEM;
183 
184                 k = set_put(database_users, n);
185                 if (k < 0) {
186                         free(n);
187                         return k;
188                 }
189 
190                 k = hashmap_put(database_by_username, n, UID_TO_PTR(pw->pw_uid));
191                 if (k < 0 && k != -EEXIST)
192                         return k;
193 
194                 q = hashmap_put(database_by_uid, UID_TO_PTR(pw->pw_uid), n);
195                 if (q < 0 && q != -EEXIST)
196                         return q;
197         }
198         return r;
199 }
200 
load_group_database(void)201 static int load_group_database(void) {
202         _cleanup_fclose_ FILE *f = NULL;
203         const char *group_path;
204         struct group *gr;
205         int r;
206 
207         group_path = prefix_roota(arg_root, "/etc/group");
208         f = fopen(group_path, "re");
209         if (!f)
210                 return errno == ENOENT ? 0 : -errno;
211 
212         r = hashmap_ensure_allocated(&database_by_groupname, &string_hash_ops);
213         if (r < 0)
214                 return r;
215 
216         r = hashmap_ensure_allocated(&database_by_gid, NULL);
217         if (r < 0)
218                 return r;
219 
220         r = set_ensure_allocated(&database_groups, NULL);
221         if (r < 0)
222                 return r;
223 
224         while ((r = fgetgrent_sane(f, &gr)) > 0) {
225                 char *n;
226                 int k, q;
227 
228                 n = strdup(gr->gr_name);
229                 if (!n)
230                         return -ENOMEM;
231 
232                 k = set_put(database_groups, n);
233                 if (k < 0) {
234                         free(n);
235                         return k;
236                 }
237 
238                 k = hashmap_put(database_by_groupname, n, GID_TO_PTR(gr->gr_gid));
239                 if (k < 0 && k != -EEXIST)
240                         return k;
241 
242                 q = hashmap_put(database_by_gid, GID_TO_PTR(gr->gr_gid), n);
243                 if (q < 0 && q != -EEXIST)
244                         return q;
245         }
246         return r;
247 }
248 
make_backup(const char * target,const char * x)249 static int make_backup(const char *target, const char *x) {
250         _cleanup_(unlink_and_freep) char *dst_tmp = NULL;
251         _cleanup_fclose_ FILE *dst = NULL;
252         _cleanup_close_ int src = -1;
253         const char *backup;
254         struct stat st;
255         int r;
256 
257         assert(target);
258         assert(x);
259 
260         src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
261         if (src < 0) {
262                 if (errno == ENOENT) /* No backup necessary... */
263                         return 0;
264 
265                 return -errno;
266         }
267 
268         if (fstat(src, &st) < 0)
269                 return -errno;
270 
271         r = fopen_temporary_label(
272                         target,   /* The path for which to the look up the label */
273                         x,        /* Where we want the file actually to end up */
274                         &dst,     /* The temporary file we write to */
275                         &dst_tmp);
276         if (r < 0)
277                 return r;
278 
279         r = copy_bytes(src, fileno(dst), UINT64_MAX, COPY_REFLINK);
280         if (r < 0)
281                 return r;
282 
283         backup = strjoina(x, "-");
284 
285         /* Copy over the access mask. Don't fail on chmod() or chown(). If it stays owned by us and/or
286          * unreadable by others, then it isn't too bad... */
287         r = fchmod_and_chown_with_fallback(fileno(dst), dst_tmp, st.st_mode & 07777, st.st_uid, st.st_gid);
288         if (r < 0)
289                 log_warning_errno(r, "Failed to change access mode or ownership of %s: %m", backup);
290 
291         if (futimens(fileno(dst), (const struct timespec[2]) { st.st_atim, st.st_mtim }) < 0)
292                 log_warning_errno(errno, "Failed to fix access and modification time of %s: %m", backup);
293 
294         r = fsync_full(fileno(dst));
295         if (r < 0)
296                 return r;
297 
298         if (rename(dst_tmp, backup) < 0)
299                 return errno;
300 
301         dst_tmp = mfree(dst_tmp); /* disable the unlink_and_freep() hook now that the file has been renamed */
302         return 0;
303 }
304 
putgrent_with_members(const struct group * gr,FILE * group)305 static int putgrent_with_members(const struct group *gr, FILE *group) {
306         char **a;
307 
308         assert(gr);
309         assert(group);
310 
311         a = ordered_hashmap_get(members, gr->gr_name);
312         if (a) {
313                 _cleanup_strv_free_ char **l = NULL;
314                 bool added = false;
315 
316                 l = strv_copy(gr->gr_mem);
317                 if (!l)
318                         return -ENOMEM;
319 
320                 STRV_FOREACH(i, a) {
321                         if (strv_contains(l, *i))
322                                 continue;
323 
324                         if (strv_extend(&l, *i) < 0)
325                                 return -ENOMEM;
326 
327                         added = true;
328                 }
329 
330                 if (added) {
331                         struct group t;
332                         int r;
333 
334                         strv_uniq(l);
335                         strv_sort(l);
336 
337                         t = *gr;
338                         t.gr_mem = l;
339 
340                         r = putgrent_sane(&t, group);
341                         return r < 0 ? r : 1;
342                 }
343         }
344 
345         return putgrent_sane(gr, group);
346 }
347 
348 #if ENABLE_GSHADOW
putsgent_with_members(const struct sgrp * sg,FILE * gshadow)349 static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
350         char **a;
351 
352         assert(sg);
353         assert(gshadow);
354 
355         a = ordered_hashmap_get(members, sg->sg_namp);
356         if (a) {
357                 _cleanup_strv_free_ char **l = NULL;
358                 bool added = false;
359 
360                 l = strv_copy(sg->sg_mem);
361                 if (!l)
362                         return -ENOMEM;
363 
364                 STRV_FOREACH(i, a) {
365                         if (strv_contains(l, *i))
366                                 continue;
367 
368                         if (strv_extend(&l, *i) < 0)
369                                 return -ENOMEM;
370 
371                         added = true;
372                 }
373 
374                 if (added) {
375                         struct sgrp t;
376                         int r;
377 
378                         strv_uniq(l);
379                         strv_sort(l);
380 
381                         t = *sg;
382                         t.sg_mem = l;
383 
384                         r = putsgent_sane(&t, gshadow);
385                         return r < 0 ? r : 1;
386                 }
387         }
388 
389         return putsgent_sane(sg, gshadow);
390 }
391 #endif
392 
default_shell(uid_t uid)393 static const char* default_shell(uid_t uid) {
394         return uid == 0 ? "/bin/sh" : NOLOGIN;
395 }
396 
write_temporary_passwd(const char * passwd_path,FILE ** tmpfile,char ** tmpfile_path)397 static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char **tmpfile_path) {
398         _cleanup_fclose_ FILE *original = NULL, *passwd = NULL;
399         _cleanup_(unlink_and_freep) char *passwd_tmp = NULL;
400         struct passwd *pw = NULL;
401         Item *i;
402         int r;
403 
404         if (ordered_hashmap_isempty(todo_uids))
405                 return 0;
406 
407         if (arg_dry_run) {
408                 log_info("Would write /etc/passwd…");
409                 return 0;
410         }
411 
412         r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
413         if (r < 0)
414                 return log_debug_errno(r, "Failed to open temporary copy of %s: %m", passwd_path);
415 
416         original = fopen(passwd_path, "re");
417         if (original) {
418 
419                 /* Allow fallback path for when /proc is not mounted. On any normal system /proc will be
420                  * mounted, but e.g. when 'dnf --installroot' is used, it might not be. There is no security
421                  * relevance here, since the environment is ultimately trusted, and not requiring /proc makes
422                  * it easier to depend on sysusers in packaging scripts and suchlike. */
423                 r = copy_rights_with_fallback(fileno(original), fileno(passwd), passwd_tmp);
424                 if (r < 0)
425                         return log_debug_errno(r, "Failed to copy permissions from %s to %s: %m",
426                                                passwd_path, passwd_tmp);
427 
428                 while ((r = fgetpwent_sane(original, &pw)) > 0) {
429                         i = ordered_hashmap_get(users, pw->pw_name);
430                         if (i && i->todo_user)
431                                 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
432                                                        "%s: User \"%s\" already exists.",
433                                                        passwd_path, pw->pw_name);
434 
435                         if (ordered_hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid)))
436                                 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
437                                                        "%s: Detected collision for UID " UID_FMT ".",
438                                                        passwd_path, pw->pw_uid);
439 
440                         /* Make sure we keep the NIS entries (if any) at the end. */
441                         if (IN_SET(pw->pw_name[0], '+', '-'))
442                                 break;
443 
444                         r = putpwent_sane(pw, passwd);
445                         if (r < 0)
446                                 return log_debug_errno(r, "Failed to add existing user \"%s\" to temporary passwd file: %m",
447                                                        pw->pw_name);
448                 }
449                 if (r < 0)
450                         return log_debug_errno(r, "Failed to read %s: %m", passwd_path);
451 
452         } else {
453                 if (errno != ENOENT)
454                         return log_debug_errno(errno, "Failed to open %s: %m", passwd_path);
455                 if (fchmod(fileno(passwd), 0644) < 0)
456                         return log_debug_errno(errno, "Failed to fchmod %s: %m", passwd_tmp);
457         }
458 
459         ORDERED_HASHMAP_FOREACH(i, todo_uids) {
460                 _cleanup_free_ char *creds_shell = NULL, *cn = NULL;
461 
462                 struct passwd n = {
463                         .pw_name = i->name,
464                         .pw_uid = i->uid,
465                         .pw_gid = i->gid,
466                         .pw_gecos = (char*) strempty(i->description),
467 
468                         /* "x" means the password is stored in the shadow file */
469                         .pw_passwd = (char*) PASSWORD_SEE_SHADOW,
470 
471                         /* We default to the root directory as home */
472                         .pw_dir = i->home ?: (char*) "/",
473 
474                         /* Initialize the shell to nologin, with one exception:
475                          * for root we patch in something special */
476                         .pw_shell = i->shell ?: (char*) default_shell(i->uid),
477                 };
478 
479                 /* Try to pick up the shell for this account via the credentials logic */
480                 cn = strjoin("passwd.shell.", i->name);
481                 if (!cn)
482                         return -ENOMEM;
483 
484                 r = read_credential(cn, (void**) &creds_shell, NULL);
485                 if (r < 0)
486                         log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn);
487                 else
488                         n.pw_shell = creds_shell;
489 
490                 r = putpwent_sane(&n, passwd);
491                 if (r < 0)
492                         return log_debug_errno(r, "Failed to add new user \"%s\" to temporary passwd file: %m",
493                                                pw->pw_name);
494         }
495 
496         /* Append the remaining NIS entries if any */
497         while (pw) {
498                 r = putpwent_sane(pw, passwd);
499                 if (r < 0)
500                         return log_debug_errno(r, "Failed to add existing user \"%s\" to temporary passwd file: %m",
501                                                pw->pw_name);
502 
503                 r = fgetpwent_sane(original, &pw);
504                 if (r < 0)
505                         return log_debug_errno(r, "Failed to read %s: %m", passwd_path);
506                 if (r == 0)
507                         break;
508         }
509 
510         r = fflush_and_check(passwd);
511         if (r < 0)
512                 return log_debug_errno(r, "Failed to flush %s: %m", passwd_tmp);
513 
514         *tmpfile = TAKE_PTR(passwd);
515         *tmpfile_path = TAKE_PTR(passwd_tmp);
516 
517         return 0;
518 }
519 
write_temporary_shadow(const char * shadow_path,FILE ** tmpfile,char ** tmpfile_path)520 static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char **tmpfile_path) {
521         _cleanup_fclose_ FILE *original = NULL, *shadow = NULL;
522         _cleanup_(unlink_and_freep) char *shadow_tmp = NULL;
523         struct spwd *sp = NULL;
524         long lstchg;
525         Item *i;
526         int r;
527 
528         if (ordered_hashmap_isempty(todo_uids))
529                 return 0;
530 
531         if (arg_dry_run) {
532                 log_info("Would write /etc/shadow…");
533                 return 0;
534         }
535 
536         r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
537         if (r < 0)
538                 return log_debug_errno(r, "Failed to open temporary copy of %s: %m", shadow_path);
539 
540         lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
541 
542         original = fopen(shadow_path, "re");
543         if (original) {
544 
545                 r = copy_rights_with_fallback(fileno(original), fileno(shadow), shadow_tmp);
546                 if (r < 0)
547                         return log_debug_errno(r, "Failed to copy permissions from %s to %s: %m",
548                                                shadow_path, shadow_tmp);
549 
550                 while ((r = fgetspent_sane(original, &sp)) > 0) {
551                         i = ordered_hashmap_get(users, sp->sp_namp);
552                         if (i && i->todo_user) {
553                                 /* we will update the existing entry */
554                                 sp->sp_lstchg = lstchg;
555 
556                                 /* only the /etc/shadow stage is left, so we can
557                                  * safely remove the item from the todo set */
558                                 i->todo_user = false;
559                                 ordered_hashmap_remove(todo_uids, UID_TO_PTR(i->uid));
560                         }
561 
562                         /* Make sure we keep the NIS entries (if any) at the end. */
563                         if (IN_SET(sp->sp_namp[0], '+', '-'))
564                                 break;
565 
566                         r = putspent_sane(sp, shadow);
567                         if (r < 0)
568                                 return log_debug_errno(r, "Failed to add existing user \"%s\" to temporary shadow file: %m",
569                                                        sp->sp_namp);
570 
571                 }
572                 if (r < 0)
573                         return log_debug_errno(r, "Failed to read %s: %m", shadow_path);
574 
575         } else {
576                 if (errno != ENOENT)
577                         return log_debug_errno(errno, "Failed to open %s: %m", shadow_path);
578                 if (fchmod(fileno(shadow), 0000) < 0)
579                         return log_debug_errno(errno, "Failed to fchmod %s: %m", shadow_tmp);
580         }
581 
582         ORDERED_HASHMAP_FOREACH(i, todo_uids) {
583                 _cleanup_(erase_and_freep) char *creds_password = NULL;
584                 _cleanup_free_ char *cn = NULL;
585 
586                 struct spwd n = {
587                         .sp_namp = i->name,
588                         .sp_pwdp = (char*) PASSWORD_LOCKED_AND_INVALID,
589                         .sp_lstchg = lstchg,
590                         .sp_min = -1,
591                         .sp_max = -1,
592                         .sp_warn = -1,
593                         .sp_inact = -1,
594                         .sp_expire = -1,
595                         .sp_flag = ULONG_MAX, /* this appears to be what everybody does ... */
596                 };
597 
598                 /* Try to pick up the password for this account via the credentials logic */
599                 cn = strjoin("passwd.hashed-password.", i->name);
600                 if (!cn)
601                         return -ENOMEM;
602 
603                 r = read_credential(cn, (void**) &creds_password, NULL);
604                 if (r == -ENOENT) {
605                         _cleanup_(erase_and_freep) char *plaintext_password = NULL;
606 
607                         free(cn);
608                         cn = strjoin("passwd.plaintext-password.", i->name);
609                         if (!cn)
610                                 return -ENOMEM;
611 
612                         r = read_credential(cn, (void**) &plaintext_password, NULL);
613                         if (r < 0)
614                                 log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn);
615                         else {
616                                 r = hash_password(plaintext_password, &creds_password);
617                                 if (r < 0)
618                                         return log_debug_errno(r, "Failed to hash password: %m");
619                         }
620                 } else if (r < 0)
621                         log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn);
622 
623                 if (creds_password)
624                         n.sp_pwdp = creds_password;
625 
626                 r = putspent_sane(&n, shadow);
627                 if (r < 0)
628                         return log_debug_errno(r, "Failed to add new user \"%s\" to temporary shadow file: %m",
629                                                sp->sp_namp);
630         }
631 
632         /* Append the remaining NIS entries if any */
633         while (sp) {
634                 r = putspent_sane(sp, shadow);
635                 if (r < 0)
636                         return log_debug_errno(r, "Failed to add existing user \"%s\" to temporary shadow file: %m",
637                                                sp->sp_namp);
638 
639                 r = fgetspent_sane(original, &sp);
640                 if (r < 0)
641                         return log_debug_errno(r, "Failed to read %s: %m", shadow_path);
642                 if (r == 0)
643                         break;
644         }
645         if (!IN_SET(errno, 0, ENOENT))
646                 return -errno;
647 
648         r = fflush_sync_and_check(shadow);
649         if (r < 0)
650                 return log_debug_errno(r, "Failed to flush %s: %m", shadow_tmp);
651 
652         *tmpfile = TAKE_PTR(shadow);
653         *tmpfile_path = TAKE_PTR(shadow_tmp);
654 
655         return 0;
656 }
657 
write_temporary_group(const char * group_path,FILE ** tmpfile,char ** tmpfile_path)658 static int write_temporary_group(const char *group_path, FILE **tmpfile, char **tmpfile_path) {
659         _cleanup_fclose_ FILE *original = NULL, *group = NULL;
660         _cleanup_(unlink_and_freep) char *group_tmp = NULL;
661         bool group_changed = false;
662         struct group *gr = NULL;
663         Item *i;
664         int r;
665 
666         if (ordered_hashmap_isempty(todo_gids) && ordered_hashmap_isempty(members))
667                 return 0;
668 
669         if (arg_dry_run) {
670                 log_info("Would write /etc/group…");
671                 return 0;
672         }
673 
674         r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
675         if (r < 0)
676                 return log_debug_errno(r, "Failed to open temporary copy of %s: %m", group_path);
677 
678         original = fopen(group_path, "re");
679         if (original) {
680 
681                 r = copy_rights_with_fallback(fileno(original), fileno(group), group_tmp);
682                 if (r < 0)
683                         return log_debug_errno(r, "Failed to copy permissions from %s to %s: %m",
684                                                group_path, group_tmp);
685 
686                 while ((r = fgetgrent_sane(original, &gr)) > 0) {
687                         /* Safety checks against name and GID collisions. Normally,
688                          * this should be unnecessary, but given that we look at the
689                          * entries anyway here, let's make an extra verification
690                          * step that we don't generate duplicate entries. */
691 
692                         i = ordered_hashmap_get(groups, gr->gr_name);
693                         if (i && i->todo_group)
694                                 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
695                                                        "%s: Group \"%s\" already exists.",
696                                                        group_path, gr->gr_name);
697 
698                         if (ordered_hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid)))
699                                 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
700                                                        "%s: Detected collision for GID " GID_FMT ".",
701                                                        group_path, gr->gr_gid);
702 
703                         /* Make sure we keep the NIS entries (if any) at the end. */
704                         if (IN_SET(gr->gr_name[0], '+', '-'))
705                                 break;
706 
707                         r = putgrent_with_members(gr, group);
708                         if (r < 0)
709                                 return log_debug_errno(r, "Failed to add existing group \"%s\" to temporary group file: %m",
710                                                        gr->gr_name);
711                         if (r > 0)
712                                 group_changed = true;
713                 }
714                 if (r < 0)
715                         return log_debug_errno(r, "Failed to read %s: %m", group_path);
716 
717         } else {
718                 if (errno != ENOENT)
719                         return log_debug_errno(errno, "Failed to open %s: %m", group_path);
720                 if (fchmod(fileno(group), 0644) < 0)
721                         return log_debug_errno(errno, "Failed to fchmod %s: %m", group_tmp);
722         }
723 
724         ORDERED_HASHMAP_FOREACH(i, todo_gids) {
725                 struct group n = {
726                         .gr_name = i->name,
727                         .gr_gid = i->gid,
728                         .gr_passwd = (char*) PASSWORD_SEE_SHADOW,
729                 };
730 
731                 r = putgrent_with_members(&n, group);
732                 if (r < 0)
733                         return log_debug_errno(r, "Failed to add new group \"%s\" to temporary group file: %m",
734                                                gr->gr_name);
735 
736                 group_changed = true;
737         }
738 
739         /* Append the remaining NIS entries if any */
740         while (gr) {
741                 r = putgrent_sane(gr, group);
742                 if (r < 0)
743                         return log_debug_errno(r, "Failed to add existing group \"%s\" to temporary group file: %m",
744                                                gr->gr_name);
745 
746                 r = fgetgrent_sane(original, &gr);
747                 if (r < 0)
748                         return log_debug_errno(r, "Failed to read %s: %m", group_path);
749                 if (r == 0)
750                         break;
751         }
752 
753         r = fflush_sync_and_check(group);
754         if (r < 0)
755                 return log_debug_errno(r, "Failed to flush %s: %m", group_tmp);
756 
757         if (group_changed) {
758                 *tmpfile = TAKE_PTR(group);
759                 *tmpfile_path = TAKE_PTR(group_tmp);
760         }
761         return 0;
762 }
763 
write_temporary_gshadow(const char * gshadow_path,FILE ** tmpfile,char ** tmpfile_path)764 static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, char **tmpfile_path) {
765 #if ENABLE_GSHADOW
766         _cleanup_fclose_ FILE *original = NULL, *gshadow = NULL;
767         _cleanup_(unlink_and_freep) char *gshadow_tmp = NULL;
768         bool group_changed = false;
769         Item *i;
770         int r;
771 
772         if (ordered_hashmap_isempty(todo_gids) && ordered_hashmap_isempty(members))
773                 return 0;
774 
775         if (arg_dry_run) {
776                 log_info("Would write /etc/gshadow…");
777                 return 0;
778         }
779 
780         r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
781         if (r < 0)
782                 return log_debug_errno(r, "Failed to open temporary copy of %s: %m", gshadow_path);
783 
784         original = fopen(gshadow_path, "re");
785         if (original) {
786                 struct sgrp *sg;
787 
788                 r = copy_rights_with_fallback(fileno(original), fileno(gshadow), gshadow_tmp);
789                 if (r < 0)
790                         return log_debug_errno(r, "Failed to copy permissions from %s to %s: %m",
791                                                gshadow_path, gshadow_tmp);
792 
793                 while ((r = fgetsgent_sane(original, &sg)) > 0) {
794 
795                         i = ordered_hashmap_get(groups, sg->sg_namp);
796                         if (i && i->todo_group)
797                                 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
798                                                        "%s: Group \"%s\" already exists.",
799                                                        gshadow_path, sg->sg_namp);
800 
801                         r = putsgent_with_members(sg, gshadow);
802                         if (r < 0)
803                                 return log_debug_errno(r, "Failed to add existing group \"%s\" to temporary gshadow file: %m",
804                                                        sg->sg_namp);
805                         if (r > 0)
806                                 group_changed = true;
807                 }
808                 if (r < 0)
809                         return r;
810 
811         } else {
812                 if (errno != ENOENT)
813                         return log_debug_errno(errno, "Failed to open %s: %m", gshadow_path);
814                 if (fchmod(fileno(gshadow), 0000) < 0)
815                         return log_debug_errno(errno, "Failed to fchmod %s: %m", gshadow_tmp);
816         }
817 
818         ORDERED_HASHMAP_FOREACH(i, todo_gids) {
819                 struct sgrp n = {
820                         .sg_namp = i->name,
821                         .sg_passwd = (char*) PASSWORD_LOCKED_AND_INVALID,
822                 };
823 
824                 r = putsgent_with_members(&n, gshadow);
825                 if (r < 0)
826                         return log_debug_errno(r, "Failed to add new group \"%s\" to temporary gshadow file: %m",
827                                                n.sg_namp);
828 
829                 group_changed = true;
830         }
831 
832         r = fflush_sync_and_check(gshadow);
833         if (r < 0)
834                 return log_debug_errno(r, "Failed to flush %s: %m", gshadow_tmp);
835 
836         if (group_changed) {
837                 *tmpfile = TAKE_PTR(gshadow);
838                 *tmpfile_path = TAKE_PTR(gshadow_tmp);
839         }
840 #endif
841         return 0;
842 }
843 
write_files(void)844 static int write_files(void) {
845         _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL;
846         _cleanup_(unlink_and_freep) char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL;
847         const char *passwd_path, *shadow_path, *group_path, *gshadow_path;
848         int r;
849 
850         passwd_path = prefix_roota(arg_root, "/etc/passwd");
851         shadow_path = prefix_roota(arg_root, "/etc/shadow");
852         group_path = prefix_roota(arg_root, "/etc/group");
853         gshadow_path = prefix_roota(arg_root, "/etc/gshadow");
854 
855         r = write_temporary_group(group_path, &group, &group_tmp);
856         if (r < 0)
857                 return r;
858 
859         r = write_temporary_gshadow(gshadow_path, &gshadow, &gshadow_tmp);
860         if (r < 0)
861                 return r;
862 
863         r = write_temporary_passwd(passwd_path, &passwd, &passwd_tmp);
864         if (r < 0)
865                 return r;
866 
867         r = write_temporary_shadow(shadow_path, &shadow, &shadow_tmp);
868         if (r < 0)
869                 return r;
870 
871         /* Make a backup of the old files */
872         if (group) {
873                 r = make_backup("/etc/group", group_path);
874                 if (r < 0)
875                         return log_debug_errno(r, "Failed to make backup %s: %m", group_path);
876         }
877         if (gshadow) {
878                 r = make_backup("/etc/gshadow", gshadow_path);
879                 if (r < 0)
880                         return log_debug_errno(r, "Failed to make backup %s: %m", gshadow_path);
881         }
882 
883         if (passwd) {
884                 r = make_backup("/etc/passwd", passwd_path);
885                 if (r < 0)
886                         return log_debug_errno(r, "Failed to make backup %s: %m", passwd_path);
887         }
888         if (shadow) {
889                 r = make_backup("/etc/shadow", shadow_path);
890                 if (r < 0)
891                         return log_debug_errno(r, "Failed to make backup %s: %m", shadow_path);
892         }
893 
894         /* And make the new files count */
895         if (group) {
896                 r = rename_and_apply_smack_floor_label(group_tmp, group_path);
897                 if (r < 0)
898                         return log_debug_errno(r, "Failed to rename %s to %s: %m",
899                                                group_tmp, group_path);
900                 group_tmp = mfree(group_tmp);
901 
902                 if (!arg_root && !arg_image)
903                         (void) nscd_flush_cache(STRV_MAKE("group"));
904         }
905         if (gshadow) {
906                 r = rename_and_apply_smack_floor_label(gshadow_tmp, gshadow_path);
907                 if (r < 0)
908                         return log_debug_errno(r, "Failed to rename %s to %s: %m",
909                                                gshadow_tmp, gshadow_path);
910 
911                 gshadow_tmp = mfree(gshadow_tmp);
912         }
913 
914         if (passwd) {
915                 r = rename_and_apply_smack_floor_label(passwd_tmp, passwd_path);
916                 if (r < 0)
917                         return log_debug_errno(r, "Failed to rename %s to %s: %m",
918                                                passwd_tmp, passwd_path);
919 
920                 passwd_tmp = mfree(passwd_tmp);
921 
922                 if (!arg_root && !arg_image)
923                         (void) nscd_flush_cache(STRV_MAKE("passwd"));
924         }
925         if (shadow) {
926                 r = rename_and_apply_smack_floor_label(shadow_tmp, shadow_path);
927                 if (r < 0)
928                         return log_debug_errno(r, "Failed to rename %s to %s: %m",
929                                                shadow_tmp, shadow_path);
930 
931                 shadow_tmp = mfree(shadow_tmp);
932         }
933 
934         return 0;
935 }
936 
uid_is_ok(uid_t uid,const char * name,bool check_with_gid)937 static int uid_is_ok(uid_t uid, const char *name, bool check_with_gid) {
938 
939         /* Let's see if we already have assigned the UID a second time */
940         if (ordered_hashmap_get(todo_uids, UID_TO_PTR(uid)))
941                 return 0;
942 
943         /* Try to avoid using uids that are already used by a group
944          * that doesn't have the same name as our new user. */
945         if (check_with_gid) {
946                 Item *i;
947 
948                 i = ordered_hashmap_get(todo_gids, GID_TO_PTR(uid));
949                 if (i && !streq(i->name, name))
950                         return 0;
951         }
952 
953         /* Let's check the files directly */
954         if (hashmap_contains(database_by_uid, UID_TO_PTR(uid)))
955                 return 0;
956 
957         if (check_with_gid) {
958                 const char *n;
959 
960                 n = hashmap_get(database_by_gid, GID_TO_PTR(uid));
961                 if (n && !streq(n, name))
962                         return 0;
963         }
964 
965         /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
966         if (!arg_root) {
967                 struct passwd *p;
968                 struct group *g;
969 
970                 errno = 0;
971                 p = getpwuid(uid);
972                 if (p)
973                         return 0;
974                 if (!IN_SET(errno, 0, ENOENT))
975                         return -errno;
976 
977                 if (check_with_gid) {
978                         errno = 0;
979                         g = getgrgid((gid_t) uid);
980                         if (g) {
981                                 if (!streq(g->gr_name, name))
982                                         return 0;
983                         } else if (!IN_SET(errno, 0, ENOENT))
984                                 return -errno;
985                 }
986         }
987 
988         return 1;
989 }
990 
root_stat(const char * p,struct stat * st)991 static int root_stat(const char *p, struct stat *st) {
992         const char *fix;
993 
994         fix = prefix_roota(arg_root, p);
995         return RET_NERRNO(stat(fix, st));
996 }
997 
read_id_from_file(Item * i,uid_t * _uid,gid_t * _gid)998 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
999         struct stat st;
1000         bool found_uid = false, found_gid = false;
1001         uid_t uid = 0;
1002         gid_t gid = 0;
1003 
1004         assert(i);
1005 
1006         /* First, try to get the GID directly */
1007         if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
1008                 gid = st.st_gid;
1009                 found_gid = true;
1010         }
1011 
1012         /* Then, try to get the UID directly */
1013         if ((_uid || (_gid && !found_gid))
1014             && i->uid_path
1015             && root_stat(i->uid_path, &st) >= 0) {
1016 
1017                 uid = st.st_uid;
1018                 found_uid = true;
1019 
1020                 /* If we need the gid, but had no success yet, also derive it from the UID path */
1021                 if (_gid && !found_gid) {
1022                         gid = st.st_gid;
1023                         found_gid = true;
1024                 }
1025         }
1026 
1027         /* If that didn't work yet, then let's reuse the GID as UID */
1028         if (_uid && !found_uid && i->gid_path) {
1029 
1030                 if (found_gid) {
1031                         uid = (uid_t) gid;
1032                         found_uid = true;
1033                 } else if (root_stat(i->gid_path, &st) >= 0) {
1034                         uid = (uid_t) st.st_gid;
1035                         found_uid = true;
1036                 }
1037         }
1038 
1039         if (_uid) {
1040                 if (!found_uid)
1041                         return 0;
1042 
1043                 *_uid = uid;
1044         }
1045 
1046         if (_gid) {
1047                 if (!found_gid)
1048                         return 0;
1049 
1050                 *_gid = gid;
1051         }
1052 
1053         return 1;
1054 }
1055 
add_user(Item * i)1056 static int add_user(Item *i) {
1057         void *z;
1058         int r;
1059 
1060         assert(i);
1061 
1062         /* Check the database directly */
1063         z = hashmap_get(database_by_username, i->name);
1064         if (z) {
1065                 log_debug("User %s already exists.", i->name);
1066                 i->uid = PTR_TO_UID(z);
1067                 i->uid_set = true;
1068                 return 0;
1069         }
1070 
1071         if (!arg_root) {
1072                 struct passwd *p;
1073 
1074                 /* Also check NSS */
1075                 errno = 0;
1076                 p = getpwnam(i->name);
1077                 if (p) {
1078                         log_debug("User %s already exists.", i->name);
1079                         i->uid = p->pw_uid;
1080                         i->uid_set = true;
1081 
1082                         r = free_and_strdup(&i->description, p->pw_gecos);
1083                         if (r < 0)
1084                                 return log_oom();
1085 
1086                         return 0;
1087                 }
1088                 if (!errno_is_not_exists(errno))
1089                         return log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name);
1090         }
1091 
1092         /* Try to use the suggested numeric UID */
1093         if (i->uid_set) {
1094                 r = uid_is_ok(i->uid, i->name, !i->id_set_strict);
1095                 if (r < 0)
1096                         return log_error_errno(r, "Failed to verify UID " UID_FMT ": %m", i->uid);
1097                 if (r == 0) {
1098                         log_info("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
1099                         i->uid_set = false;
1100                 }
1101         }
1102 
1103         /* If that didn't work, try to read it from the specified path */
1104         if (!i->uid_set) {
1105                 uid_t c;
1106 
1107                 if (read_id_from_file(i, &c, NULL) > 0) {
1108 
1109                         if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
1110                                 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
1111                         else {
1112                                 r = uid_is_ok(c, i->name, true);
1113                                 if (r < 0)
1114                                         return log_error_errno(r, "Failed to verify UID " UID_FMT ": %m", i->uid);
1115                                 else if (r > 0) {
1116                                         i->uid = c;
1117                                         i->uid_set = true;
1118                                 } else
1119                                         log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
1120                         }
1121                 }
1122         }
1123 
1124         /* Otherwise, try to reuse the group ID */
1125         if (!i->uid_set && i->gid_set) {
1126                 r = uid_is_ok((uid_t) i->gid, i->name, true);
1127                 if (r < 0)
1128                         return log_error_errno(r, "Failed to verify UID " UID_FMT ": %m", i->uid);
1129                 if (r > 0) {
1130                         i->uid = (uid_t) i->gid;
1131                         i->uid_set = true;
1132                 }
1133         }
1134 
1135         /* And if that didn't work either, let's try to find a free one */
1136         if (!i->uid_set) {
1137                 maybe_emit_login_defs_warning();
1138 
1139                 for (;;) {
1140                         r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
1141                         if (r < 0)
1142                                 return log_error_errno(r, "No free user ID available for %s.", i->name);
1143 
1144                         r = uid_is_ok(search_uid, i->name, true);
1145                         if (r < 0)
1146                                 return log_error_errno(r, "Failed to verify UID " UID_FMT ": %m", i->uid);
1147                         else if (r > 0)
1148                                 break;
1149                 }
1150 
1151                 i->uid_set = true;
1152                 i->uid = search_uid;
1153         }
1154 
1155         r = ordered_hashmap_ensure_put(&todo_uids, NULL, UID_TO_PTR(i->uid), i);
1156         if (r == -EEXIST)
1157                 return log_error_errno(r, "Requested user %s with UID " UID_FMT " and gid" GID_FMT " to be created is duplicated "
1158                                        "or conflicts with another user.", i->name, i->uid, i->gid);
1159         if (r == -ENOMEM)
1160                 return log_oom();
1161         if (r < 0)
1162                 return log_error_errno(r, "Failed to store user %s with UID " UID_FMT " and GID " GID_FMT " to be created: %m",
1163                                        i->name, i->uid, i->gid);
1164 
1165         i->todo_user = true;
1166         log_info("Creating user '%s' (%s) with UID " UID_FMT " and GID " GID_FMT ".",
1167                  i->name, strna(i->description), i->uid, i->gid);
1168 
1169         return 0;
1170 }
1171 
gid_is_ok(gid_t gid)1172 static int gid_is_ok(gid_t gid) {
1173         struct group *g;
1174         struct passwd *p;
1175 
1176         if (ordered_hashmap_get(todo_gids, GID_TO_PTR(gid)))
1177                 return 0;
1178 
1179         /* Avoid reusing gids that are already used by a different user */
1180         if (ordered_hashmap_get(todo_uids, UID_TO_PTR(gid)))
1181                 return 0;
1182 
1183         if (hashmap_contains(database_by_gid, GID_TO_PTR(gid)))
1184                 return 0;
1185 
1186         if (hashmap_contains(database_by_uid, UID_TO_PTR(gid)))
1187                 return 0;
1188 
1189         if (!arg_root) {
1190                 errno = 0;
1191                 g = getgrgid(gid);
1192                 if (g)
1193                         return 0;
1194                 if (!IN_SET(errno, 0, ENOENT))
1195                         return -errno;
1196 
1197                 errno = 0;
1198                 p = getpwuid((uid_t) gid);
1199                 if (p)
1200                         return 0;
1201                 if (!IN_SET(errno, 0, ENOENT))
1202                         return -errno;
1203         }
1204 
1205         return 1;
1206 }
1207 
get_gid_by_name(const char * name,gid_t * gid)1208 static int get_gid_by_name(const char *name, gid_t *gid) {
1209         void *z;
1210 
1211         assert(gid);
1212 
1213         /* Check the database directly */
1214         z = hashmap_get(database_by_groupname, name);
1215         if (z) {
1216                 *gid = PTR_TO_GID(z);
1217                 return 0;
1218         }
1219 
1220         /* Also check NSS */
1221         if (!arg_root) {
1222                 struct group *g;
1223 
1224                 errno = 0;
1225                 g = getgrnam(name);
1226                 if (g) {
1227                         *gid = g->gr_gid;
1228                         return 0;
1229                 }
1230                 if (!errno_is_not_exists(errno))
1231                         return log_error_errno(errno, "Failed to check if group %s already exists: %m", name);
1232         }
1233 
1234         return -ENOENT;
1235 }
1236 
add_group(Item * i)1237 static int add_group(Item *i) {
1238         int r;
1239 
1240         assert(i);
1241 
1242         r = get_gid_by_name(i->name, &i->gid);
1243         if (r != -ENOENT) {
1244                 if (r < 0)
1245                         return r;
1246                 log_debug("Group %s already exists.", i->name);
1247                 i->gid_set = true;
1248                 return 0;
1249         }
1250 
1251         /* Try to use the suggested numeric GID */
1252         if (i->gid_set) {
1253                 r = gid_is_ok(i->gid);
1254                 if (r < 0)
1255                         return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
1256                 if (i->id_set_strict) {
1257                         /* If we require the GID to already exist we can return here:
1258                          * r > 0: means the GID does not exist -> fail
1259                          * r == 0: means the GID exists -> nothing more to do.
1260                          */
1261                         if (r > 0)
1262                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1263                                                        "Failed to create %s: please create GID %d",
1264                                                        i->name, i->gid);
1265                         if (r == 0)
1266                                 return 0;
1267                 }
1268                 if (r == 0) {
1269                         log_info("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
1270                         i->gid_set = false;
1271                 }
1272         }
1273 
1274         /* Try to reuse the numeric uid, if there's one */
1275         if (!i->gid_set && i->uid_set) {
1276                 r = gid_is_ok((gid_t) i->uid);
1277                 if (r < 0)
1278                         return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
1279                 if (r > 0) {
1280                         i->gid = (gid_t) i->uid;
1281                         i->gid_set = true;
1282                 }
1283         }
1284 
1285         /* If that didn't work, try to read it from the specified path */
1286         if (!i->gid_set) {
1287                 gid_t c;
1288 
1289                 if (read_id_from_file(i, NULL, &c) > 0) {
1290 
1291                         if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
1292                                 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
1293                         else {
1294                                 r = gid_is_ok(c);
1295                                 if (r < 0)
1296                                         return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
1297                                 else if (r > 0) {
1298                                         i->gid = c;
1299                                         i->gid_set = true;
1300                                 } else
1301                                         log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
1302                         }
1303                 }
1304         }
1305 
1306         /* And if that didn't work either, let's try to find a free one */
1307         if (!i->gid_set) {
1308                 maybe_emit_login_defs_warning();
1309 
1310                 for (;;) {
1311                         /* We look for new GIDs in the UID pool! */
1312                         r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
1313                         if (r < 0)
1314                                 return log_error_errno(r, "No free group ID available for %s.", i->name);
1315 
1316                         r = gid_is_ok(search_uid);
1317                         if (r < 0)
1318                                 return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
1319                         else if (r > 0)
1320                                 break;
1321                 }
1322 
1323                 i->gid_set = true;
1324                 i->gid = search_uid;
1325         }
1326 
1327         r = ordered_hashmap_ensure_put(&todo_gids, NULL, GID_TO_PTR(i->gid), i);
1328         if (r == -EEXIST)
1329                 return log_error_errno(r, "Requested group %s with GID "GID_FMT " to be created is duplicated or conflicts with another user.", i->name, i->gid);
1330         if (r == -ENOMEM)
1331                 return log_oom();
1332         if (r < 0)
1333                 return log_error_errno(r, "Failed to store group %s with GID " GID_FMT " to be created: %m", i->name, i->gid);
1334 
1335         i->todo_group = true;
1336         log_info("Creating group '%s' with GID " GID_FMT ".", i->name, i->gid);
1337 
1338         return 0;
1339 }
1340 
process_item(Item * i)1341 static int process_item(Item *i) {
1342         int r;
1343 
1344         assert(i);
1345 
1346         switch (i->type) {
1347 
1348         case ADD_USER: {
1349                 Item *j;
1350 
1351                 j = ordered_hashmap_get(groups, i->group_name ?: i->name);
1352                 if (j && j->todo_group) {
1353                         /* When a group with the target name is already in queue,
1354                          * use the information about the group and do not create
1355                          * duplicated group entry. */
1356                         i->gid_set = j->gid_set;
1357                         i->gid = j->gid;
1358                         i->id_set_strict = true;
1359                 } else if (i->group_name) {
1360                         /* When a group name was given instead of a GID and it's
1361                          * not in queue, then it must already exist. */
1362                         r = get_gid_by_name(i->group_name, &i->gid);
1363                         if (r < 0)
1364                                 return log_error_errno(r, "Group %s not found.", i->group_name);
1365                         i->gid_set = true;
1366                         i->id_set_strict = true;
1367                 } else {
1368                         r = add_group(i);
1369                         if (r < 0)
1370                                 return r;
1371                 }
1372 
1373                 return add_user(i);
1374         }
1375 
1376         case ADD_GROUP:
1377                 return add_group(i);
1378 
1379         default:
1380                 assert_not_reached();
1381         }
1382 }
1383 
item_free(Item * i)1384 static Item* item_free(Item *i) {
1385         if (!i)
1386                 return NULL;
1387 
1388         free(i->name);
1389         free(i->group_name);
1390         free(i->uid_path);
1391         free(i->gid_path);
1392         free(i->description);
1393         free(i->home);
1394         free(i->shell);
1395         return mfree(i);
1396 }
1397 
1398 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
1399 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(item_hash_ops, char, string_hash_func, string_compare_func, Item, item_free);
1400 
add_implicit(void)1401 static int add_implicit(void) {
1402         char *g, **l;
1403         int r;
1404 
1405         /* Implicitly create additional users and groups, if they were listed in "m" lines */
1406         ORDERED_HASHMAP_FOREACH_KEY(l, g, members) {
1407                 STRV_FOREACH(m, l)
1408                         if (!ordered_hashmap_get(users, *m)) {
1409                                 _cleanup_(item_freep) Item *j = NULL;
1410 
1411                                 j = new0(Item, 1);
1412                                 if (!j)
1413                                         return log_oom();
1414 
1415                                 j->type = ADD_USER;
1416                                 j->name = strdup(*m);
1417                                 if (!j->name)
1418                                         return log_oom();
1419 
1420                                 r = ordered_hashmap_ensure_put(&users, &item_hash_ops, j->name, j);
1421                                 if (r == -ENOMEM)
1422                                         return log_oom();
1423                                 if (r < 0)
1424                                         return log_error_errno(r, "Failed to add implicit user '%s': %m", j->name);
1425 
1426                                 log_debug("Adding implicit user '%s' due to m line", j->name);
1427                                 TAKE_PTR(j);
1428                         }
1429 
1430                 if (!(ordered_hashmap_get(users, g) ||
1431                       ordered_hashmap_get(groups, g))) {
1432                         _cleanup_(item_freep) Item *j = NULL;
1433 
1434                         j = new0(Item, 1);
1435                         if (!j)
1436                                 return log_oom();
1437 
1438                         j->type = ADD_GROUP;
1439                         j->name = strdup(g);
1440                         if (!j->name)
1441                                 return log_oom();
1442 
1443                         r = ordered_hashmap_ensure_put(&groups, &item_hash_ops, j->name, j);
1444                         if (r == -ENOMEM)
1445                                 return log_oom();
1446                         if (r < 0)
1447                                 return log_error_errno(r, "Failed to add implicit group '%s': %m", j->name);
1448 
1449                         log_debug("Adding implicit group '%s' due to m line", j->name);
1450                         TAKE_PTR(j);
1451                 }
1452         }
1453 
1454         return 0;
1455 }
1456 
item_equal(Item * a,Item * b)1457 static bool item_equal(Item *a, Item *b) {
1458         assert(a);
1459         assert(b);
1460 
1461         if (a->type != b->type)
1462                 return false;
1463 
1464         if (!streq_ptr(a->name, b->name))
1465                 return false;
1466 
1467         if (!streq_ptr(a->uid_path, b->uid_path))
1468                 return false;
1469 
1470         if (!streq_ptr(a->gid_path, b->gid_path))
1471                 return false;
1472 
1473         if (!streq_ptr(a->description, b->description))
1474                 return false;
1475 
1476         if (a->uid_set != b->uid_set)
1477                 return false;
1478 
1479         if (a->uid_set && a->uid != b->uid)
1480                 return false;
1481 
1482         if (a->gid_set != b->gid_set)
1483                 return false;
1484 
1485         if (a->gid_set && a->gid != b->gid)
1486                 return false;
1487 
1488         if (!streq_ptr(a->home, b->home))
1489                 return false;
1490 
1491         if (!streq_ptr(a->shell, b->shell))
1492                 return false;
1493 
1494         return true;
1495 }
1496 
parse_line(const char * fname,unsigned line,const char * buffer)1497 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1498         _cleanup_free_ char *action = NULL,
1499                 *name = NULL, *resolved_name = NULL,
1500                 *id = NULL, *resolved_id = NULL,
1501                 *description = NULL, *resolved_description = NULL,
1502                 *home = NULL, *resolved_home = NULL,
1503                 *shell = NULL, *resolved_shell = NULL;
1504         _cleanup_(item_freep) Item *i = NULL;
1505         Item *existing;
1506         OrderedHashmap *h;
1507         int r;
1508         const char *p;
1509 
1510         assert(fname);
1511         assert(line >= 1);
1512         assert(buffer);
1513 
1514         /* Parse columns */
1515         p = buffer;
1516         r = extract_many_words(&p, NULL, EXTRACT_UNQUOTE,
1517                                &action, &name, &id, &description, &home, &shell, NULL);
1518         if (r < 0)
1519                 return log_error_errno(r, "[%s:%u] Syntax error.", fname, line);
1520         if (r < 2)
1521                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1522                                        "[%s:%u] Missing action and name columns.", fname, line);
1523         if (!isempty(p))
1524                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1525                                        "[%s:%u] Trailing garbage.", fname, line);
1526 
1527         /* Verify action */
1528         if (strlen(action) != 1)
1529                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1530                                        "[%s:%u] Unknown modifier '%s'", fname, line, action);
1531 
1532         if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE))
1533                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
1534                                        "[%s:%u] Unknown command type '%c'.", fname, line, action[0]);
1535 
1536         /* Verify name */
1537         if (empty_or_dash(name))
1538                 name = mfree(name);
1539 
1540         if (name) {
1541                 r = specifier_printf(name, NAME_MAX, system_and_tmp_specifier_table, arg_root, NULL, &resolved_name);
1542                 if (r < 0)
1543                         return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m", fname, line, name);
1544 
1545                 if (!valid_user_group_name(resolved_name, 0))
1546                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1547                                                "[%s:%u] '%s' is not a valid user or group name.",
1548                                                fname, line, resolved_name);
1549         }
1550 
1551         /* Verify id */
1552         if (empty_or_dash(id))
1553                 id = mfree(id);
1554 
1555         if (id) {
1556                 r = specifier_printf(id, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_id);
1557                 if (r < 0)
1558                         return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
1559                                                fname, line, name);
1560         }
1561 
1562         /* Verify description */
1563         if (empty_or_dash(description))
1564                 description = mfree(description);
1565 
1566         if (description) {
1567                 r = specifier_printf(description, LONG_LINE_MAX, system_and_tmp_specifier_table, arg_root, NULL, &resolved_description);
1568                 if (r < 0)
1569                         return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
1570                                                fname, line, description);
1571 
1572                 if (!valid_gecos(resolved_description))
1573                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1574                                                "[%s:%u] '%s' is not a valid GECOS field.",
1575                                                fname, line, resolved_description);
1576         }
1577 
1578         /* Verify home */
1579         if (empty_or_dash(home))
1580                 home = mfree(home);
1581 
1582         if (home) {
1583                 r = specifier_printf(home, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_home);
1584                 if (r < 0)
1585                         return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
1586                                                fname, line, home);
1587 
1588                 if (!valid_home(resolved_home))
1589                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1590                                                "[%s:%u] '%s' is not a valid home directory field.",
1591                                                fname, line, resolved_home);
1592         }
1593 
1594         /* Verify shell */
1595         if (empty_or_dash(shell))
1596                 shell = mfree(shell);
1597 
1598         if (shell) {
1599                 r = specifier_printf(shell, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_shell);
1600                 if (r < 0)
1601                         return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
1602                                                fname, line, shell);
1603 
1604                 if (!valid_shell(resolved_shell))
1605                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1606                                                "[%s:%u] '%s' is not a valid login shell field.",
1607                                                fname, line, resolved_shell);
1608         }
1609 
1610         switch (action[0]) {
1611 
1612         case ADD_RANGE:
1613                 if (resolved_name)
1614                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1615                                                "[%s:%u] Lines of type 'r' don't take a name field.",
1616                                                fname, line);
1617 
1618                 if (!resolved_id)
1619                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1620                                                "[%s:%u] Lines of type 'r' require an ID range in the third field.",
1621                                                fname, line);
1622 
1623                 if (description || home || shell)
1624                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1625                                                "[%s:%u] Lines of type '%c' don't take a %s field.",
1626                                                fname, line, action[0],
1627                                                description ? "GECOS" : home ? "home directory" : "login shell");
1628 
1629                 r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id);
1630                 if (r < 0)
1631                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1632                                                "[%s:%u] Invalid UID range %s.", fname, line, resolved_id);
1633 
1634                 return 0;
1635 
1636         case ADD_MEMBER: {
1637                 /* Try to extend an existing member or group item */
1638                 if (!name)
1639                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1640                                                "[%s:%u] Lines of type 'm' require a user name in the second field.",
1641                                                fname, line);
1642 
1643                 if (!resolved_id)
1644                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1645                                                "[%s:%u] Lines of type 'm' require a group name in the third field.",
1646                                                fname, line);
1647 
1648                 if (!valid_user_group_name(resolved_id, 0))
1649                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1650                                                "[%s:%u] '%s' is not a valid user or group name.",
1651                                                fname, line, resolved_id);
1652 
1653                 if (description || home || shell)
1654                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1655                                                "[%s:%u] Lines of type '%c' don't take a %s field.",
1656                                                fname, line, action[0],
1657                                                description ? "GECOS" : home ? "home directory" : "login shell");
1658 
1659                 r = string_strv_ordered_hashmap_put(&members, resolved_id, resolved_name);
1660                 if (r < 0)
1661                         return log_error_errno(r, "Failed to store mapping for %s: %m", resolved_id);
1662 
1663                 return 0;
1664         }
1665 
1666         case ADD_USER:
1667                 if (!name)
1668                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1669                                                "[%s:%u] Lines of type 'u' require a user name in the second field.",
1670                                                fname, line);
1671 
1672                 r = ordered_hashmap_ensure_allocated(&users, &item_hash_ops);
1673                 if (r < 0)
1674                         return log_oom();
1675 
1676                 i = new0(Item, 1);
1677                 if (!i)
1678                         return log_oom();
1679 
1680                 if (resolved_id) {
1681                         if (path_is_absolute(resolved_id)) {
1682                                 i->uid_path = TAKE_PTR(resolved_id);
1683                                 path_simplify(i->uid_path);
1684                         } else {
1685                                 _cleanup_free_ char *uid = NULL, *gid = NULL;
1686                                 if (split_pair(resolved_id, ":", &uid, &gid) == 0) {
1687                                         r = parse_gid(gid, &i->gid);
1688                                         if (r < 0) {
1689                                                 if (valid_user_group_name(gid, 0))
1690                                                         i->group_name = TAKE_PTR(gid);
1691                                                 else
1692                                                         return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
1693                                         } else {
1694                                                 i->gid_set = true;
1695                                                 i->id_set_strict = true;
1696                                         }
1697                                         free_and_replace(resolved_id, uid);
1698                                 }
1699                                 if (!streq(resolved_id, "-")) {
1700                                         r = parse_uid(resolved_id, &i->uid);
1701                                         if (r < 0)
1702                                                 return log_error_errno(r, "Failed to parse UID: '%s': %m", id);
1703                                         i->uid_set = true;
1704                                 }
1705                         }
1706                 }
1707 
1708                 i->description = TAKE_PTR(resolved_description);
1709                 i->home = TAKE_PTR(resolved_home);
1710                 i->shell = TAKE_PTR(resolved_shell);
1711 
1712                 h = users;
1713                 break;
1714 
1715         case ADD_GROUP:
1716                 if (!name)
1717                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1718                                                "[%s:%u] Lines of type 'g' require a user name in the second field.",
1719                                                fname, line);
1720 
1721                 if (description || home || shell)
1722                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1723                                                "[%s:%u] Lines of type '%c' don't take a %s field.",
1724                                                fname, line, action[0],
1725                                                description ? "GECOS" : home ? "home directory" : "login shell");
1726 
1727                 r = ordered_hashmap_ensure_allocated(&groups, &item_hash_ops);
1728                 if (r < 0)
1729                         return log_oom();
1730 
1731                 i = new0(Item, 1);
1732                 if (!i)
1733                         return log_oom();
1734 
1735                 if (resolved_id) {
1736                         if (path_is_absolute(resolved_id)) {
1737                                 i->gid_path = TAKE_PTR(resolved_id);
1738                                 path_simplify(i->gid_path);
1739                         } else {
1740                                 r = parse_gid(resolved_id, &i->gid);
1741                                 if (r < 0)
1742                                         return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
1743 
1744                                 i->gid_set = true;
1745                         }
1746                 }
1747 
1748                 h = groups;
1749                 break;
1750 
1751         default:
1752                 return -EBADMSG;
1753         }
1754 
1755         i->type = action[0];
1756         i->name = TAKE_PTR(resolved_name);
1757 
1758         existing = ordered_hashmap_get(h, i->name);
1759         if (existing) {
1760                 /* Two identical items are fine */
1761                 if (!item_equal(existing, i))
1762                         log_warning("%s:%u: conflict with earlier configuration for %s '%s', ignoring line.",
1763                                     fname, line,
1764                                     item_type_to_string(i->type), i->name);
1765 
1766                 return 0;
1767         }
1768 
1769         r = ordered_hashmap_put(h, i->name, i);
1770         if (r < 0)
1771                 return log_oom();
1772 
1773         i = NULL;
1774         return 0;
1775 }
1776 
read_config_file(const char * fn,bool ignore_enoent)1777 static int read_config_file(const char *fn, bool ignore_enoent) {
1778         _cleanup_fclose_ FILE *rf = NULL;
1779         _cleanup_free_ char *pp = NULL;
1780         FILE *f = NULL;
1781         unsigned v = 0;
1782         int r = 0;
1783 
1784         assert(fn);
1785 
1786         if (streq(fn, "-"))
1787                 f = stdin;
1788         else {
1789                 r = search_and_fopen(fn, "re", arg_root, (const char**) CONF_PATHS_STRV("sysusers.d"), &rf, &pp);
1790                 if (r < 0) {
1791                         if (ignore_enoent && r == -ENOENT)
1792                                 return 0;
1793 
1794                         return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
1795                 }
1796 
1797                 f = rf;
1798                 fn = pp;
1799         }
1800 
1801         for (;;) {
1802                 _cleanup_free_ char *line = NULL;
1803                 char *l;
1804                 int k;
1805 
1806                 k = read_line(f, LONG_LINE_MAX, &line);
1807                 if (k < 0)
1808                         return log_error_errno(k, "Failed to read '%s': %m", fn);
1809                 if (k == 0)
1810                         break;
1811 
1812                 v++;
1813 
1814                 l = strstrip(line);
1815                 if (IN_SET(*l, 0, '#'))
1816                         continue;
1817 
1818                 k = parse_line(fn, v, l);
1819                 if (k < 0 && r == 0)
1820                         r = k;
1821         }
1822 
1823         if (ferror(f)) {
1824                 log_error_errno(errno, "Failed to read from file %s: %m", fn);
1825                 if (r == 0)
1826                         r = -EIO;
1827         }
1828 
1829         return r;
1830 }
1831 
cat_config(void)1832 static int cat_config(void) {
1833         _cleanup_strv_free_ char **files = NULL;
1834         int r;
1835 
1836         r = conf_files_list_with_replacement(arg_root, CONF_PATHS_STRV("sysusers.d"), arg_replace, &files, NULL);
1837         if (r < 0)
1838                 return r;
1839 
1840         pager_open(arg_pager_flags);
1841 
1842         return cat_files(NULL, files, 0);
1843 }
1844 
help(void)1845 static int help(void) {
1846         _cleanup_free_ char *link = NULL;
1847         int r;
1848 
1849         r = terminal_urlify_man("systemd-sysusers.service", "8", &link);
1850         if (r < 0)
1851                 return log_oom();
1852 
1853         printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1854                "Creates system user accounts.\n\n"
1855                "  -h --help                 Show this help\n"
1856                "     --version              Show package version\n"
1857                "     --cat-config           Show configuration files\n"
1858                "     --root=PATH            Operate on an alternate filesystem root\n"
1859                "     --image=PATH           Operate on disk image as filesystem root\n"
1860                "     --replace=PATH         Treat arguments as replacement for PATH\n"
1861                "     --dry-run              Just print what would be done\n"
1862                "     --inline               Treat arguments as configuration lines\n"
1863                "     --no-pager             Do not pipe output into a pager\n"
1864                "\nSee the %s for details.\n",
1865                program_invocation_short_name,
1866                link);
1867 
1868         return 0;
1869 }
1870 
parse_argv(int argc,char * argv[])1871 static int parse_argv(int argc, char *argv[]) {
1872 
1873         enum {
1874                 ARG_VERSION = 0x100,
1875                 ARG_CAT_CONFIG,
1876                 ARG_ROOT,
1877                 ARG_IMAGE,
1878                 ARG_REPLACE,
1879                 ARG_DRY_RUN,
1880                 ARG_INLINE,
1881                 ARG_NO_PAGER,
1882         };
1883 
1884         static const struct option options[] = {
1885                 { "help",       no_argument,       NULL, 'h'            },
1886                 { "version",    no_argument,       NULL, ARG_VERSION    },
1887                 { "cat-config", no_argument,       NULL, ARG_CAT_CONFIG },
1888                 { "root",       required_argument, NULL, ARG_ROOT       },
1889                 { "image",      required_argument, NULL, ARG_IMAGE      },
1890                 { "replace",    required_argument, NULL, ARG_REPLACE    },
1891                 { "dry-run",    no_argument,       NULL, ARG_DRY_RUN    },
1892                 { "inline",     no_argument,       NULL, ARG_INLINE     },
1893                 { "no-pager",   no_argument,       NULL, ARG_NO_PAGER   },
1894                 {}
1895         };
1896 
1897         int c, r;
1898 
1899         assert(argc >= 0);
1900         assert(argv);
1901 
1902         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
1903 
1904                 switch (c) {
1905 
1906                 case 'h':
1907                         return help();
1908 
1909                 case ARG_VERSION:
1910                         return version();
1911 
1912                 case ARG_CAT_CONFIG:
1913                         arg_cat_config = true;
1914                         break;
1915 
1916                 case ARG_ROOT:
1917                         r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_root);
1918                         if (r < 0)
1919                                 return r;
1920                         break;
1921 
1922                 case ARG_IMAGE:
1923 #ifdef STANDALONE
1924                         return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1925                                                "This systemd-sysusers version is compiled without support for --image=.");
1926 #else
1927                         r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
1928                         if (r < 0)
1929                                 return r;
1930                         break;
1931 #endif
1932 
1933                 case ARG_REPLACE:
1934                         if (!path_is_absolute(optarg) ||
1935                             !endswith(optarg, ".conf"))
1936                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1937                                                        "The argument to --replace= must an absolute path to a config file");
1938 
1939                         arg_replace = optarg;
1940                         break;
1941 
1942                 case ARG_DRY_RUN:
1943                         arg_dry_run = true;
1944                         break;
1945 
1946                 case ARG_INLINE:
1947                         arg_inline = true;
1948                         break;
1949 
1950                 case ARG_NO_PAGER:
1951                         arg_pager_flags |= PAGER_DISABLE;
1952                         break;
1953 
1954                 case '?':
1955                         return -EINVAL;
1956 
1957                 default:
1958                         assert_not_reached();
1959                 }
1960 
1961         if (arg_replace && arg_cat_config)
1962                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1963                                        "Option --replace= is not supported with --cat-config");
1964 
1965         if (arg_replace && optind >= argc)
1966                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1967                                        "When --replace= is given, some configuration items must be specified");
1968 
1969         if (arg_image && arg_root)
1970                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
1971 
1972         return 1;
1973 }
1974 
parse_arguments(char ** args)1975 static int parse_arguments(char **args) {
1976         unsigned pos = 1;
1977         int r;
1978 
1979         STRV_FOREACH(arg, args) {
1980                 if (arg_inline)
1981                         /* Use (argument):n, where n==1 for the first positional arg */
1982                         r = parse_line("(argument)", pos, *arg);
1983                 else
1984                         r = read_config_file(*arg, false);
1985                 if (r < 0)
1986                         return r;
1987 
1988                 pos++;
1989         }
1990 
1991         return 0;
1992 }
1993 
read_config_files(char ** args)1994 static int read_config_files(char **args) {
1995         _cleanup_strv_free_ char **files = NULL;
1996         _cleanup_free_ char *p = NULL;
1997         int r;
1998 
1999         r = conf_files_list_with_replacement(arg_root, CONF_PATHS_STRV("sysusers.d"), arg_replace, &files, &p);
2000         if (r < 0)
2001                 return r;
2002 
2003         STRV_FOREACH(f, files)
2004                 if (p && path_equal(*f, p)) {
2005                         log_debug("Parsing arguments at position \"%s\"…", *f);
2006 
2007                         r = parse_arguments(args);
2008                         if (r < 0)
2009                                 return r;
2010                 } else {
2011                         log_debug("Reading config file \"%s\"…", *f);
2012 
2013                         /* Just warn, ignore result otherwise */
2014                         (void) read_config_file(*f, true);
2015                 }
2016 
2017         return 0;
2018 }
2019 
run(int argc,char * argv[])2020 static int run(int argc, char *argv[]) {
2021 #ifndef STANDALONE
2022         _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
2023         _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
2024         _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
2025 #endif
2026         _cleanup_close_ int lock = -1;
2027         Item *i;
2028         int r;
2029 
2030         r = parse_argv(argc, argv);
2031         if (r <= 0)
2032                 return r;
2033 
2034         log_setup();
2035 
2036         if (arg_cat_config)
2037                 return cat_config();
2038 
2039         umask(0022);
2040 
2041         r = mac_selinux_init();
2042         if (r < 0)
2043                 return r;
2044 
2045 #ifndef STANDALONE
2046         if (arg_image) {
2047                 assert(!arg_root);
2048 
2049                 r = mount_image_privately_interactively(
2050                                 arg_image,
2051                                 DISSECT_IMAGE_GENERIC_ROOT |
2052                                 DISSECT_IMAGE_REQUIRE_ROOT |
2053                                 DISSECT_IMAGE_VALIDATE_OS |
2054                                 DISSECT_IMAGE_RELAX_VAR_CHECK |
2055                                 DISSECT_IMAGE_FSCK |
2056                                 DISSECT_IMAGE_GROWFS,
2057                                 &unlink_dir,
2058                                 &loop_device,
2059                                 &decrypted_image);
2060                 if (r < 0)
2061                         return r;
2062 
2063                 arg_root = strdup(unlink_dir);
2064                 if (!arg_root)
2065                         return log_oom();
2066         }
2067 #else
2068         assert(!arg_image);
2069 #endif
2070 
2071         /* If command line arguments are specified along with --replace, read all
2072          * configuration files and insert the positional arguments at the specified
2073          * place. Otherwise, if command line arguments are specified, execute just
2074          * them, and finally, without --replace= or any positional arguments, just
2075          * read configuration and execute it.
2076          */
2077         if (arg_replace || optind >= argc)
2078                 r = read_config_files(argv + optind);
2079         else
2080                 r = parse_arguments(argv + optind);
2081         if (r < 0)
2082                 return r;
2083 
2084         /* Let's tell nss-systemd not to synthesize the "root" and "nobody" entries for it, so that our detection
2085          * whether the names or UID/GID area already used otherwise doesn't get confused. After all, even though
2086          * nss-systemd synthesizes these users/groups, they should still appear in /etc/passwd and /etc/group, as the
2087          * synthesizing logic is merely supposed to be fallback for cases where we run with a completely unpopulated
2088          * /etc. */
2089         if (setenv("SYSTEMD_NSS_BYPASS_SYNTHETIC", "1", 1) < 0)
2090                 return log_error_errno(errno, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m");
2091 
2092         if (!uid_range) {
2093                 /* Default to default range of SYSTEMD_UID_MIN..SYSTEM_UID_MAX. */
2094                 r = read_login_defs(&login_defs, NULL, arg_root);
2095                 if (r < 0)
2096                         return log_error_errno(r, "Failed to read %s%s: %m",
2097                                                strempty(arg_root), "/etc/login.defs");
2098 
2099                 login_defs_need_warning = true;
2100 
2101                 /* We pick a range that very conservative: we look at compiled-in maximum and the value in
2102                  * /etc/login.defs. That way the UIDs/GIDs which we allocate will be interpreted correctly,
2103                  * even if /etc/login.defs is removed later. (The bottom bound doesn't matter much, since
2104                  * it's only used during allocation, so we use the configured value directly). */
2105                 uid_t begin = login_defs.system_alloc_uid_min,
2106                       end = MIN3((uid_t) SYSTEM_UID_MAX, login_defs.system_uid_max, login_defs.system_gid_max);
2107                 if (begin < end) {
2108                         r = uid_range_add(&uid_range, &n_uid_range, begin, end - begin + 1);
2109                         if (r < 0)
2110                                 return log_oom();
2111                 }
2112         }
2113 
2114         r = add_implicit();
2115         if (r < 0)
2116                 return r;
2117 
2118         if (!arg_dry_run) {
2119                 lock = take_etc_passwd_lock(arg_root);
2120                 if (lock < 0)
2121                         return log_error_errno(lock, "Failed to take /etc/passwd lock: %m");
2122         }
2123 
2124         r = load_user_database();
2125         if (r < 0)
2126                 return log_error_errno(r, "Failed to load user database: %m");
2127 
2128         r = load_group_database();
2129         if (r < 0)
2130                 return log_error_errno(r, "Failed to read group database: %m");
2131 
2132         ORDERED_HASHMAP_FOREACH(i, groups)
2133                 (void) process_item(i);
2134 
2135         ORDERED_HASHMAP_FOREACH(i, users)
2136                 (void) process_item(i);
2137 
2138         r = write_files();
2139         if (r < 0)
2140                 return log_error_errno(r, "Failed to write files: %m");
2141 
2142         return 0;
2143 }
2144 
2145 DEFINE_MAIN_FUNCTION(run);
2146