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