File: | build-scan/../src/basic/user-util.c |
Warning: | line 313, column 27 Potential leak of memory pointed to by 'buf' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |||
2 | ||||
3 | #include <alloca.h> | |||
4 | #include <errno(*__errno_location ()).h> | |||
5 | #include <fcntl.h> | |||
6 | #include <grp.h> | |||
7 | #include <pwd.h> | |||
8 | #include <stddef.h> | |||
9 | #include <stdint.h> | |||
10 | #include <stdio.h> | |||
11 | #include <stdlib.h> | |||
12 | #include <string.h> | |||
13 | #include <sys/stat.h> | |||
14 | #include <unistd.h> | |||
15 | #include <utmp.h> | |||
16 | ||||
17 | #include "sd-messages.h" | |||
18 | ||||
19 | #include "alloc-util.h" | |||
20 | #include "fd-util.h" | |||
21 | #include "fileio.h" | |||
22 | #include "format-util.h" | |||
23 | #include "macro.h" | |||
24 | #include "missing.h" | |||
25 | #include "parse-util.h" | |||
26 | #include "path-util.h" | |||
27 | #include "string-util.h" | |||
28 | #include "strv.h" | |||
29 | #include "user-util.h" | |||
30 | #include "utf8.h" | |||
31 | ||||
32 | bool_Bool uid_is_valid(uid_t uid) { | |||
33 | ||||
34 | /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.436. */ | |||
35 | ||||
36 | /* Some libc APIs use UID_INVALID as special placeholder */ | |||
37 | if (uid == (uid_t) UINT32_C(0xFFFFFFFF)0xFFFFFFFFU) | |||
38 | return false0; | |||
39 | ||||
40 | /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */ | |||
41 | if (uid == (uid_t) UINT32_C(0xFFFF)0xFFFFU) | |||
42 | return false0; | |||
43 | ||||
44 | return true1; | |||
45 | } | |||
46 | ||||
47 | int parse_uid(const char *s, uid_t *ret) { | |||
48 | uint32_t uid = 0; | |||
49 | int r; | |||
50 | ||||
51 | assert(s)do { if ((__builtin_expect(!!(!(s)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("s"), "../src/basic/user-util.c", 51, __PRETTY_FUNCTION__ ); } while (0); | |||
52 | ||||
53 | assert_cc(sizeof(uid_t) == sizeof(uint32_t))GCC diagnostic push
; GCC diagnostic ignored "-Wdeclaration-after-statement" ; struct _assert_struct_9 { char x[(sizeof(uid_t) == sizeof( uint32_t)) ? 0 : -1]; }; GCC diagnostic pop ; | |||
54 | ||||
55 | /* We are very strict when parsing UIDs, and prohibit +/- as prefix, leading zero as prefix, and | |||
56 | * whitespace. We do this, since this call is often used in a context where we parse things as UID | |||
57 | * first, and if that doesn't work we fall back to NSS. Thus we really want to make sure that UIDs | |||
58 | * are parsed as UIDs only if they really really look like UIDs. */ | |||
59 | r = safe_atou32_full(s, 10 | |||
60 | | SAFE_ATO_REFUSE_PLUS_MINUS(1U << 30) | |||
61 | | SAFE_ATO_REFUSE_LEADING_ZERO(1U << 29) | |||
62 | | SAFE_ATO_REFUSE_LEADING_WHITESPACE(1U << 28), &uid); | |||
63 | if (r < 0) | |||
64 | return r; | |||
65 | ||||
66 | if (!uid_is_valid(uid)) | |||
67 | return -ENXIO6; /* we return ENXIO instead of EINVAL | |||
68 | * here, to make it easy to distuingish | |||
69 | * invalid numeric uids from invalid | |||
70 | * strings. */ | |||
71 | ||||
72 | if (ret) | |||
73 | *ret = uid; | |||
74 | ||||
75 | return 0; | |||
76 | } | |||
77 | ||||
78 | char* getlogname_malloc(void) { | |||
79 | uid_t uid; | |||
80 | struct stat st; | |||
81 | ||||
82 | if (isatty(STDIN_FILENO0) && fstat(STDIN_FILENO0, &st) >= 0) | |||
83 | uid = st.st_uid; | |||
84 | else | |||
85 | uid = getuid(); | |||
86 | ||||
87 | return uid_to_name(uid); | |||
88 | } | |||
89 | ||||
90 | char *getusername_malloc(void) { | |||
91 | const char *e; | |||
92 | ||||
93 | e = getenv("USER"); | |||
94 | if (e) | |||
| ||||
95 | return strdup(e); | |||
96 | ||||
97 | return uid_to_name(getuid()); | |||
98 | } | |||
99 | ||||
100 | int get_user_creds( | |||
101 | const char **username, | |||
102 | uid_t *uid, gid_t *gid, | |||
103 | const char **home, | |||
104 | const char **shell) { | |||
105 | ||||
106 | struct passwd *p; | |||
107 | uid_t u; | |||
108 | ||||
109 | assert(username)do { if ((__builtin_expect(!!(!(username)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("username"), "../src/basic/user-util.c", 109, __PRETTY_FUNCTION__); } while (0); | |||
110 | assert(*username)do { if ((__builtin_expect(!!(!(*username)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("*username"), "../src/basic/user-util.c" , 110, __PRETTY_FUNCTION__); } while (0); | |||
111 | ||||
112 | /* We enforce some special rules for uid=0 and uid=65534: in order to avoid NSS lookups for root we hardcode | |||
113 | * their user record data. */ | |||
114 | ||||
115 | if (STR_IN_SET(*username, "root", "0")(!!strv_find((((char**) ((const char*[]) { "root", "0", ((void *)0) }))), (*username)))) { | |||
116 | *username = "root"; | |||
117 | ||||
118 | if (uid) | |||
119 | *uid = 0; | |||
120 | if (gid) | |||
121 | *gid = 0; | |||
122 | ||||
123 | if (home) | |||
124 | *home = "/root"; | |||
125 | ||||
126 | if (shell) | |||
127 | *shell = "/bin/sh"; | |||
128 | ||||
129 | return 0; | |||
130 | } | |||
131 | ||||
132 | if (synthesize_nobody() && | |||
133 | STR_IN_SET(*username, NOBODY_USER_NAME, "65534")(!!strv_find((((char**) ((const char*[]) { "nobody", "65534", ((void*)0) }))), (*username)))) { | |||
134 | *username = NOBODY_USER_NAME"nobody"; | |||
135 | ||||
136 | if (uid) | |||
137 | *uid = UID_NOBODY((uid_t) 65534U); | |||
138 | if (gid) | |||
139 | *gid = GID_NOBODY((gid_t) 65534U); | |||
140 | ||||
141 | if (home) | |||
142 | *home = "/"; | |||
143 | ||||
144 | if (shell) | |||
145 | *shell = "/sbin/nologin"; | |||
146 | ||||
147 | return 0; | |||
148 | } | |||
149 | ||||
150 | if (parse_uid(*username, &u) >= 0) { | |||
151 | errno(*__errno_location ()) = 0; | |||
152 | p = getpwuid(u); | |||
153 | ||||
154 | /* If there are multiple users with the same id, make | |||
155 | * sure to leave $USER to the configured value instead | |||
156 | * of the first occurrence in the database. However if | |||
157 | * the uid was configured by a numeric uid, then let's | |||
158 | * pick the real username from /etc/passwd. */ | |||
159 | if (p) | |||
160 | *username = p->pw_name; | |||
161 | } else { | |||
162 | errno(*__errno_location ()) = 0; | |||
163 | p = getpwnam(*username); | |||
164 | } | |||
165 | ||||
166 | if (!p) | |||
167 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -ESRCH3; | |||
168 | ||||
169 | if (uid) { | |||
170 | if (!uid_is_valid(p->pw_uid)) | |||
171 | return -EBADMSG74; | |||
172 | ||||
173 | *uid = p->pw_uid; | |||
174 | } | |||
175 | ||||
176 | if (gid) { | |||
177 | if (!gid_is_valid(p->pw_gid)) | |||
178 | return -EBADMSG74; | |||
179 | ||||
180 | *gid = p->pw_gid; | |||
181 | } | |||
182 | ||||
183 | if (home) | |||
184 | *home = p->pw_dir; | |||
185 | ||||
186 | if (shell) | |||
187 | *shell = p->pw_shell; | |||
188 | ||||
189 | return 0; | |||
190 | } | |||
191 | ||||
192 | static inline bool_Bool is_nologin_shell(const char *shell) { | |||
193 | ||||
194 | return PATH_IN_SET(shell,({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
195 | /* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
196 | * message and exits. Different distributions place the binary at different places though,({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
197 | * hence let's list them all. */({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
198 | "/bin/nologin",({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
199 | "/sbin/nologin",({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
200 | "/usr/bin/nologin",({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
201 | "/usr/sbin/nologin",({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
202 | /* 'true' and 'false' work too for the same purpose, but are less friendly as they don't do({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
203 | * any message printing. Different distributions place the binary at various places but at({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
204 | * least not in the 'sbin' directory. */({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
205 | "/bin/false",({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
206 | "/usr/bin/false",({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
207 | "/bin/true",({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }) | |||
208 | "/usr/bin/true")({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/bin/nologin", "/sbin/nologin", "/usr/bin/nologin", "/usr/sbin/nologin" , "/bin/false", "/usr/bin/false", "/bin/true", "/usr/bin/true" , ((void*)0) }))); (s) && *(s); (s)++) if (path_equal (shell, *s)) { _found = 1; break; } _found; }); | |||
209 | } | |||
210 | ||||
211 | int get_user_creds_clean( | |||
212 | const char **username, | |||
213 | uid_t *uid, gid_t *gid, | |||
214 | const char **home, | |||
215 | const char **shell) { | |||
216 | ||||
217 | int r; | |||
218 | ||||
219 | /* Like get_user_creds(), but resets home/shell to NULL if they don't contain anything relevant. */ | |||
220 | ||||
221 | r = get_user_creds(username, uid, gid, home, shell); | |||
222 | if (r < 0) | |||
223 | return r; | |||
224 | ||||
225 | if (shell && | |||
226 | (isempty(*shell) || is_nologin_shell(*shell))) | |||
227 | *shell = NULL((void*)0); | |||
228 | ||||
229 | if (home && empty_or_root(*home)) | |||
230 | *home = NULL((void*)0); | |||
231 | ||||
232 | return 0; | |||
233 | } | |||
234 | ||||
235 | int get_group_creds(const char **groupname, gid_t *gid) { | |||
236 | struct group *g; | |||
237 | gid_t id; | |||
238 | ||||
239 | assert(groupname)do { if ((__builtin_expect(!!(!(groupname)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("groupname"), "../src/basic/user-util.c" , 239, __PRETTY_FUNCTION__); } while (0); | |||
240 | ||||
241 | /* We enforce some special rules for gid=0: in order to avoid | |||
242 | * NSS lookups for root we hardcode its data. */ | |||
243 | ||||
244 | if (STR_IN_SET(*groupname, "root", "0")(!!strv_find((((char**) ((const char*[]) { "root", "0", ((void *)0) }))), (*groupname)))) { | |||
245 | *groupname = "root"; | |||
246 | ||||
247 | if (gid) | |||
248 | *gid = 0; | |||
249 | ||||
250 | return 0; | |||
251 | } | |||
252 | ||||
253 | if (synthesize_nobody() && | |||
254 | STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534")(!!strv_find((((char**) ((const char*[]) { "nobody", "65534", ((void*)0) }))), (*groupname)))) { | |||
255 | *groupname = NOBODY_GROUP_NAME"nobody"; | |||
256 | ||||
257 | if (gid) | |||
258 | *gid = GID_NOBODY((gid_t) 65534U); | |||
259 | ||||
260 | return 0; | |||
261 | } | |||
262 | ||||
263 | if (parse_gid(*groupname, &id) >= 0) { | |||
264 | errno(*__errno_location ()) = 0; | |||
265 | g = getgrgid(id); | |||
266 | ||||
267 | if (g) | |||
268 | *groupname = g->gr_name; | |||
269 | } else { | |||
270 | errno(*__errno_location ()) = 0; | |||
271 | g = getgrnam(*groupname); | |||
272 | } | |||
273 | ||||
274 | if (!g) | |||
275 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -ESRCH3; | |||
276 | ||||
277 | if (gid) { | |||
278 | if (!gid_is_valid(g->gr_gid)) | |||
279 | return -EBADMSG74; | |||
280 | ||||
281 | *gid = g->gr_gid; | |||
282 | } | |||
283 | ||||
284 | return 0; | |||
285 | } | |||
286 | ||||
287 | char* uid_to_name(uid_t uid) { | |||
288 | char *ret; | |||
289 | int r; | |||
290 | ||||
291 | /* Shortcut things to avoid NSS lookups */ | |||
292 | if (uid == 0) | |||
293 | return strdup("root"); | |||
294 | if (synthesize_nobody() && | |||
295 | uid == UID_NOBODY((uid_t) 65534U)) | |||
296 | return strdup(NOBODY_USER_NAME"nobody"); | |||
297 | ||||
298 | if (uid_is_valid(uid)) { | |||
299 | long bufsize; | |||
300 | ||||
301 | bufsize = sysconf(_SC_GETPW_R_SIZE_MAX_SC_GETPW_R_SIZE_MAX); | |||
302 | if (bufsize <= 0) | |||
303 | bufsize = 4096; | |||
304 | ||||
305 | for (;;) { | |||
306 | struct passwd pwbuf, *pw = NULL((void*)0); | |||
307 | _cleanup_free___attribute__((cleanup(freep))) char *buf = NULL((void*)0); | |||
308 | ||||
309 | buf = malloc(bufsize); | |||
310 | if (!buf) | |||
311 | return NULL((void*)0); | |||
312 | ||||
313 | r = getpwuid_r(uid, &pwbuf, buf, (size_t) bufsize, &pw); | |||
| ||||
314 | if (r == 0 && pw) | |||
315 | return strdup(pw->pw_name); | |||
316 | if (r != ERANGE34) | |||
317 | break; | |||
318 | ||||
319 | bufsize *= 2; | |||
320 | } | |||
321 | } | |||
322 | ||||
323 | if (asprintf(&ret, UID_FMT"%" "u", uid) < 0) | |||
324 | return NULL((void*)0); | |||
325 | ||||
326 | return ret; | |||
327 | } | |||
328 | ||||
329 | char* gid_to_name(gid_t gid) { | |||
330 | char *ret; | |||
331 | int r; | |||
332 | ||||
333 | if (gid == 0) | |||
334 | return strdup("root"); | |||
335 | if (synthesize_nobody() && | |||
336 | gid == GID_NOBODY((gid_t) 65534U)) | |||
337 | return strdup(NOBODY_GROUP_NAME"nobody"); | |||
338 | ||||
339 | if (gid_is_valid(gid)) { | |||
340 | long bufsize; | |||
341 | ||||
342 | bufsize = sysconf(_SC_GETGR_R_SIZE_MAX_SC_GETGR_R_SIZE_MAX); | |||
343 | if (bufsize <= 0) | |||
344 | bufsize = 4096; | |||
345 | ||||
346 | for (;;) { | |||
347 | struct group grbuf, *gr = NULL((void*)0); | |||
348 | _cleanup_free___attribute__((cleanup(freep))) char *buf = NULL((void*)0); | |||
349 | ||||
350 | buf = malloc(bufsize); | |||
351 | if (!buf) | |||
352 | return NULL((void*)0); | |||
353 | ||||
354 | r = getgrgid_r(gid, &grbuf, buf, (size_t) bufsize, &gr); | |||
355 | if (r == 0 && gr) | |||
356 | return strdup(gr->gr_name); | |||
357 | if (r != ERANGE34) | |||
358 | break; | |||
359 | ||||
360 | bufsize *= 2; | |||
361 | } | |||
362 | } | |||
363 | ||||
364 | if (asprintf(&ret, GID_FMT"%" "u", gid) < 0) | |||
365 | return NULL((void*)0); | |||
366 | ||||
367 | return ret; | |||
368 | } | |||
369 | ||||
370 | int in_gid(gid_t gid) { | |||
371 | gid_t *gids; | |||
372 | int ngroups, r, i; | |||
373 | ||||
374 | if (getgid() == gid) | |||
375 | return 1; | |||
376 | ||||
377 | if (getegid() == gid) | |||
378 | return 1; | |||
379 | ||||
380 | if (!gid_is_valid(gid)) | |||
381 | return -EINVAL22; | |||
382 | ||||
383 | ngroups = getgroups(0, NULL((void*)0)); | |||
384 | if (ngroups < 0) | |||
385 | return -errno(*__errno_location ()); | |||
386 | if (ngroups == 0) | |||
387 | return 0; | |||
388 | ||||
389 | gids = newa(gid_t, ngroups)({ do { if ((__builtin_expect(!!(!(!size_multiply_overflow(sizeof (gid_t), ngroups))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD , ("!size_multiply_overflow(sizeof(gid_t), ngroups)"), "../src/basic/user-util.c" , 389, __PRETTY_FUNCTION__); } while (0); (gid_t*) __builtin_alloca (sizeof(gid_t)*(ngroups)); }); | |||
390 | ||||
391 | r = getgroups(ngroups, gids); | |||
392 | if (r < 0) | |||
393 | return -errno(*__errno_location ()); | |||
394 | ||||
395 | for (i = 0; i < r; i++) | |||
396 | if (gids[i] == gid) | |||
397 | return 1; | |||
398 | ||||
399 | return 0; | |||
400 | } | |||
401 | ||||
402 | int in_group(const char *name) { | |||
403 | int r; | |||
404 | gid_t gid; | |||
405 | ||||
406 | r = get_group_creds(&name, &gid); | |||
407 | if (r < 0) | |||
408 | return r; | |||
409 | ||||
410 | return in_gid(gid); | |||
411 | } | |||
412 | ||||
413 | int get_home_dir(char **_h) { | |||
414 | struct passwd *p; | |||
415 | const char *e; | |||
416 | char *h; | |||
417 | uid_t u; | |||
418 | ||||
419 | assert(_h)do { if ((__builtin_expect(!!(!(_h)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("_h"), "../src/basic/user-util.c", 419, __PRETTY_FUNCTION__ ); } while (0); | |||
420 | ||||
421 | /* Take the user specified one */ | |||
422 | e = secure_getenv("HOME"); | |||
423 | if (e && path_is_absolute(e)) { | |||
424 | h = strdup(e); | |||
425 | if (!h) | |||
426 | return -ENOMEM12; | |||
427 | ||||
428 | *_h = h; | |||
429 | return 0; | |||
430 | } | |||
431 | ||||
432 | /* Hardcode home directory for root and nobody to avoid NSS */ | |||
433 | u = getuid(); | |||
434 | if (u == 0) { | |||
435 | h = strdup("/root"); | |||
436 | if (!h) | |||
437 | return -ENOMEM12; | |||
438 | ||||
439 | *_h = h; | |||
440 | return 0; | |||
441 | } | |||
442 | if (synthesize_nobody() && | |||
443 | u == UID_NOBODY((uid_t) 65534U)) { | |||
444 | h = strdup("/"); | |||
445 | if (!h) | |||
446 | return -ENOMEM12; | |||
447 | ||||
448 | *_h = h; | |||
449 | return 0; | |||
450 | } | |||
451 | ||||
452 | /* Check the database... */ | |||
453 | errno(*__errno_location ()) = 0; | |||
454 | p = getpwuid(u); | |||
455 | if (!p) | |||
456 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -ESRCH3; | |||
457 | ||||
458 | if (!path_is_absolute(p->pw_dir)) | |||
459 | return -EINVAL22; | |||
460 | ||||
461 | h = strdup(p->pw_dir); | |||
462 | if (!h) | |||
463 | return -ENOMEM12; | |||
464 | ||||
465 | *_h = h; | |||
466 | return 0; | |||
467 | } | |||
468 | ||||
469 | int get_shell(char **_s) { | |||
470 | struct passwd *p; | |||
471 | const char *e; | |||
472 | char *s; | |||
473 | uid_t u; | |||
474 | ||||
475 | assert(_s)do { if ((__builtin_expect(!!(!(_s)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("_s"), "../src/basic/user-util.c", 475, __PRETTY_FUNCTION__ ); } while (0); | |||
476 | ||||
477 | /* Take the user specified one */ | |||
478 | e = getenv("SHELL"); | |||
479 | if (e) { | |||
480 | s = strdup(e); | |||
481 | if (!s) | |||
482 | return -ENOMEM12; | |||
483 | ||||
484 | *_s = s; | |||
485 | return 0; | |||
486 | } | |||
487 | ||||
488 | /* Hardcode shell for root and nobody to avoid NSS */ | |||
489 | u = getuid(); | |||
490 | if (u == 0) { | |||
491 | s = strdup("/bin/sh"); | |||
492 | if (!s) | |||
493 | return -ENOMEM12; | |||
494 | ||||
495 | *_s = s; | |||
496 | return 0; | |||
497 | } | |||
498 | if (synthesize_nobody() && | |||
499 | u == UID_NOBODY((uid_t) 65534U)) { | |||
500 | s = strdup("/sbin/nologin"); | |||
501 | if (!s) | |||
502 | return -ENOMEM12; | |||
503 | ||||
504 | *_s = s; | |||
505 | return 0; | |||
506 | } | |||
507 | ||||
508 | /* Check the database... */ | |||
509 | errno(*__errno_location ()) = 0; | |||
510 | p = getpwuid(u); | |||
511 | if (!p) | |||
512 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -ESRCH3; | |||
513 | ||||
514 | if (!path_is_absolute(p->pw_shell)) | |||
515 | return -EINVAL22; | |||
516 | ||||
517 | s = strdup(p->pw_shell); | |||
518 | if (!s) | |||
519 | return -ENOMEM12; | |||
520 | ||||
521 | *_s = s; | |||
522 | return 0; | |||
523 | } | |||
524 | ||||
525 | int reset_uid_gid(void) { | |||
526 | int r; | |||
527 | ||||
528 | r = maybe_setgroups(0, NULL((void*)0)); | |||
529 | if (r < 0) | |||
530 | return r; | |||
531 | ||||
532 | if (setresgid(0, 0, 0) < 0) | |||
533 | return -errno(*__errno_location ()); | |||
534 | ||||
535 | if (setresuid(0, 0, 0) < 0) | |||
536 | return -errno(*__errno_location ()); | |||
537 | ||||
538 | return 0; | |||
539 | } | |||
540 | ||||
541 | int take_etc_passwd_lock(const char *root) { | |||
542 | ||||
543 | struct flock flock = { | |||
544 | .l_type = F_WRLCK1, | |||
545 | .l_whence = SEEK_SET0, | |||
546 | .l_start = 0, | |||
547 | .l_len = 0, | |||
548 | }; | |||
549 | ||||
550 | const char *path; | |||
551 | int fd, r; | |||
552 | ||||
553 | /* This is roughly the same as lckpwdf(), but not as awful. We | |||
554 | * don't want to use alarm() and signals, hence we implement | |||
555 | * our own trivial version of this. | |||
556 | * | |||
557 | * Note that shadow-utils also takes per-database locks in | |||
558 | * addition to lckpwdf(). However, we don't given that they | |||
559 | * are redundant as they invoke lckpwdf() first and keep | |||
560 | * it during everything they do. The per-database locks are | |||
561 | * awfully racy, and thus we just won't do them. */ | |||
562 | ||||
563 | if (root) | |||
564 | path = prefix_roota(root, ETC_PASSWD_LOCK_PATH)({ const char* _path = ("/etc/.pwd.lock"), *_root = (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; }); | |||
565 | else | |||
566 | path = ETC_PASSWD_LOCK_PATH"/etc/.pwd.lock"; | |||
567 | ||||
568 | fd = open(path, O_WRONLY01|O_CREAT0100|O_CLOEXEC02000000|O_NOCTTY0400|O_NOFOLLOW0400000, 0600); | |||
569 | if (fd < 0) | |||
570 | return log_debug_errno(errno, "Cannot open %s: %m", path)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/basic/user-util.c", 570, __func__ , "Cannot open %s: %m", path) : -abs(_e); }); | |||
571 | ||||
572 | r = fcntl(fd, F_SETLKW7, &flock); | |||
573 | if (r < 0) { | |||
574 | safe_close(fd); | |||
575 | return log_debug_errno(errno, "Locking %s failed: %m", path)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/basic/user-util.c", 575, __func__ , "Locking %s failed: %m", path) : -abs(_e); }); | |||
576 | } | |||
577 | ||||
578 | return fd; | |||
579 | } | |||
580 | ||||
581 | bool_Bool valid_user_group_name(const char *u, ValidUserFlags flags) { | |||
582 | const char *i; | |||
583 | ||||
584 | /* Checks if the specified name is a valid user/group name. There are two flavours of this call: | |||
585 | * strict mode is the default which is POSIX plus some extra rules; and relaxed mode where we accept | |||
586 | * pretty much everything except the really worst offending names. | |||
587 | * | |||
588 | * Whenever we synthesize users ourselves we should use the strict mode. But when we process users | |||
589 | * created by other stuff, let's be more liberal. */ | |||
590 | ||||
591 | if (isempty(u)) /* An empty user name is never valid */ | |||
592 | return false0; | |||
593 | ||||
594 | if (parse_uid(u, NULL((void*)0)) >= 0) /* Something that parses as numeric UID string is valid exactly when the | |||
595 | * flag for it is set */ | |||
596 | return FLAGS_SET(flags, VALID_USER_ALLOW_NUMERIC)(((flags) & (VALID_USER_ALLOW_NUMERIC)) == (VALID_USER_ALLOW_NUMERIC )); | |||
597 | ||||
598 | if (FLAGS_SET(flags, VALID_USER_RELAX)(((flags) & (VALID_USER_RELAX)) == (VALID_USER_RELAX))) { | |||
599 | ||||
600 | /* In relaxed mode we just check very superficially. Apparently SSSD and other stuff is | |||
601 | * extremely liberal (way too liberal if you ask me, even inserting "@" in user names, which | |||
602 | * is bound to cause problems for example when used with an MTA), hence only filter the most | |||
603 | * obvious cases, or where things would result in an invalid entry if such a user name would | |||
604 | * show up in /etc/passwd (or equivalent getent output). | |||
605 | * | |||
606 | * Note that we stepped far out of POSIX territory here. It's not our fault though, but | |||
607 | * SSSD's, Samba's and everybody else who ignored POSIX on this. (I mean, I am happy to step | |||
608 | * outside of POSIX' bounds any day, but I must say in this case I probably wouldn't | |||
609 | * have...) */ | |||
610 | ||||
611 | if (startswith(u, " ") || endswith(u, " ")) /* At least expect whitespace padding is removed | |||
612 | * at front and back (accept in the middle, since | |||
613 | * that's apparently a thing on Windows). Note | |||
614 | * that this also blocks usernames consisting of | |||
615 | * whitespace only. */ | |||
616 | return false0; | |||
617 | ||||
618 | if (!utf8_is_valid(u)) /* We want to synthesize JSON from this, hence insist on UTF-8 */ | |||
619 | return false0; | |||
620 | ||||
621 | if (string_has_cc(u, NULL((void*)0))) /* CC characters are just dangerous (and \n in particular is the | |||
622 | * record separator in /etc/passwd), so we can't allow that. */ | |||
623 | return false0; | |||
624 | ||||
625 | if (strpbrk(u, ":/")) /* Colons are the field separator in /etc/passwd, we can't allow | |||
626 | * that. Slashes are special to file systems paths and user names | |||
627 | * typically show up in the file system as home directories, hence | |||
628 | * don't allow slashes. */ | |||
629 | return false0; | |||
630 | ||||
631 | if (in_charset(u, "0123456789")) /* Don't allow fully numeric strings, they might be confused | |||
632 | * with with UIDs (note that this test is more broad than | |||
633 | * the parse_uid() test above, as it will cover more than | |||
634 | * the 32bit range, and it will detect 65535 (which is in | |||
635 | * invalid UID, even though in the unsigned 32 bit range) */ | |||
636 | return false0; | |||
637 | ||||
638 | if (u[0] == '-' && in_charset(u + 1, "0123456789")) /* Don't allow negative fully numeric | |||
639 | * strings either. After all some people | |||
640 | * write 65535 as -1 (even though that's | |||
641 | * not even true on 32bit uid_t | |||
642 | * anyway) */ | |||
643 | return false0; | |||
644 | ||||
645 | if (dot_or_dot_dot(u)) /* User names typically become home directory names, and these two are | |||
646 | * special in that context, don't allow that. */ | |||
647 | return false0; | |||
648 | ||||
649 | /* Compare with strict result and warn if result doesn't match */ | |||
650 | if (FLAGS_SET(flags, VALID_USER_WARN)(((flags) & (VALID_USER_WARN)) == (VALID_USER_WARN)) && !valid_user_group_name(u, 0)) | |||
651 | log_struct(LOG_NOTICE,log_struct_internal(((LOG_REALM_SYSTEMD) << 10 | (5)), 0 , "../src/basic/user-util.c", 654, __func__, "MESSAGE=Accepting user/group name '%s', which does not match strict user/group name rules." , u, "USER_GROUP_NAME=%s", u, "MESSAGE_ID=" "b6" "1f" "da" "c6" "12" "e9" "4b" "91" "82" "28" "5b" "99" "88" "43" "06" "1f", ((void*)0)) | |||
652 | "MESSAGE=Accepting user/group name '%s', which does not match strict user/group name rules.", u,log_struct_internal(((LOG_REALM_SYSTEMD) << 10 | (5)), 0 , "../src/basic/user-util.c", 654, __func__, "MESSAGE=Accepting user/group name '%s', which does not match strict user/group name rules." , u, "USER_GROUP_NAME=%s", u, "MESSAGE_ID=" "b6" "1f" "da" "c6" "12" "e9" "4b" "91" "82" "28" "5b" "99" "88" "43" "06" "1f", ((void*)0)) | |||
653 | "USER_GROUP_NAME=%s", u,log_struct_internal(((LOG_REALM_SYSTEMD) << 10 | (5)), 0 , "../src/basic/user-util.c", 654, __func__, "MESSAGE=Accepting user/group name '%s', which does not match strict user/group name rules." , u, "USER_GROUP_NAME=%s", u, "MESSAGE_ID=" "b6" "1f" "da" "c6" "12" "e9" "4b" "91" "82" "28" "5b" "99" "88" "43" "06" "1f", ((void*)0)) | |||
654 | "MESSAGE_ID=" SD_MESSAGE_UNSAFE_USER_NAME_STR)log_struct_internal(((LOG_REALM_SYSTEMD) << 10 | (5)), 0 , "../src/basic/user-util.c", 654, __func__, "MESSAGE=Accepting user/group name '%s', which does not match strict user/group name rules." , u, "USER_GROUP_NAME=%s", u, "MESSAGE_ID=" "b6" "1f" "da" "c6" "12" "e9" "4b" "91" "82" "28" "5b" "99" "88" "43" "06" "1f", ((void*)0)); | |||
655 | ||||
656 | /* Note that we make no restrictions on the length in relaxed mode! */ | |||
657 | } else { | |||
658 | long sz; | |||
659 | size_t l; | |||
660 | ||||
661 | /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.437. We are a bit stricter here | |||
662 | * however. Specifically we deviate from POSIX rules: | |||
663 | * | |||
664 | * - We don't allow empty user names (see above) | |||
665 | * - We require that names fit into the appropriate utmp field | |||
666 | * - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator) | |||
667 | * - We don't allow dashes or digit as the first character | |||
668 | * | |||
669 | * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters. | |||
670 | */ | |||
671 | ||||
672 | if (!(u[0] >= 'a' && u[0] <= 'z') && | |||
673 | !(u[0] >= 'A' && u[0] <= 'Z') && | |||
674 | u[0] != '_') | |||
675 | return false0; | |||
676 | ||||
677 | for (i = u+1; *i; i++) | |||
678 | if (!(*i >= 'a' && *i <= 'z') && | |||
679 | !(*i >= 'A' && *i <= 'Z') && | |||
680 | !(*i >= '0' && *i <= '9') && | |||
681 | !IN_SET(*i, '_', '-')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){'_', '-'})/sizeof(int)]; switch(*i) { case '_': case '-': _found = 1; break; default: break; } _found; } )) | |||
682 | return false0; | |||
683 | ||||
684 | l = i - u; | |||
685 | ||||
686 | sz = sysconf(_SC_LOGIN_NAME_MAX_SC_LOGIN_NAME_MAX); | |||
687 | assert_se(sz > 0)do { if ((__builtin_expect(!!(!(sz > 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("sz > 0"), "../src/basic/user-util.c" , 687, __PRETTY_FUNCTION__); } while (0); | |||
688 | ||||
689 | if (l > (size_t) sz) | |||
690 | return false0; | |||
691 | if (l > FILENAME_MAX4096) | |||
692 | return false0; | |||
693 | if (l > UT_NAMESIZE32 - 1) | |||
694 | return false0; | |||
695 | } | |||
696 | ||||
697 | return true1; | |||
698 | } | |||
699 | ||||
700 | bool_Bool valid_gecos(const char *d) { | |||
701 | ||||
702 | if (!d) | |||
703 | return false0; | |||
704 | ||||
705 | if (!utf8_is_valid(d)) | |||
706 | return false0; | |||
707 | ||||
708 | if (string_has_cc(d, NULL((void*)0))) | |||
709 | return false0; | |||
710 | ||||
711 | /* Colons are used as field separators, and hence not OK */ | |||
712 | if (strchr(d, ':')) | |||
713 | return false0; | |||
714 | ||||
715 | return true1; | |||
716 | } | |||
717 | ||||
718 | bool_Bool valid_home(const char *p) { | |||
719 | /* Note that this function is also called by valid_shell(), any | |||
720 | * changes must account for that. */ | |||
721 | ||||
722 | if (isempty(p)) | |||
723 | return false0; | |||
724 | ||||
725 | if (!utf8_is_valid(p)) | |||
726 | return false0; | |||
727 | ||||
728 | if (string_has_cc(p, NULL((void*)0))) | |||
729 | return false0; | |||
730 | ||||
731 | if (!path_is_absolute(p)) | |||
732 | return false0; | |||
733 | ||||
734 | if (!path_is_normalized(p)) | |||
735 | return false0; | |||
736 | ||||
737 | /* Colons are used as field separators, and hence not OK */ | |||
738 | if (strchr(p, ':')) | |||
739 | return false0; | |||
740 | ||||
741 | return true1; | |||
742 | } | |||
743 | ||||
744 | int maybe_setgroups(size_t size, const gid_t *list) { | |||
745 | int r; | |||
746 | ||||
747 | /* Check if setgroups is allowed before we try to drop all the auxiliary groups */ | |||
748 | if (size == 0) { /* Dropping all aux groups? */ | |||
749 | _cleanup_free___attribute__((cleanup(freep))) char *setgroups_content = NULL((void*)0); | |||
750 | bool_Bool can_setgroups; | |||
751 | ||||
752 | r = read_one_line_file("/proc/self/setgroups", &setgroups_content); | |||
753 | if (r == -ENOENT2) | |||
754 | /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */ | |||
755 | can_setgroups = true1; | |||
756 | else if (r < 0) | |||
757 | return r; | |||
758 | else | |||
759 | can_setgroups = streq(setgroups_content, "allow")(strcmp((setgroups_content),("allow")) == 0); | |||
760 | ||||
761 | if (!can_setgroups) { | |||
762 | log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'")({ 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/basic/user-util.c", 762, __func__, "Skipping setgroups(), /proc/self/setgroups is set to 'deny'" ) : -abs(_e); }); | |||
763 | return 0; | |||
764 | } | |||
765 | } | |||
766 | ||||
767 | if (setgroups(size, list) < 0) | |||
768 | return -errno(*__errno_location ()); | |||
769 | ||||
770 | return 0; | |||
771 | } | |||
772 | ||||
773 | bool_Bool synthesize_nobody(void) { | |||
774 | ||||
775 | #ifdef NOLEGACY | |||
776 | return true1; | |||
777 | #else | |||
778 | /* Returns true when we shall synthesize the "nobody" user (which we do by default). This can be turned off by | |||
779 | * touching /etc/systemd/dont-synthesize-nobody in order to provide upgrade compatibility with legacy systems | |||
780 | * that used the "nobody" user name and group name for other UIDs/GIDs than 65534. | |||
781 | * | |||
782 | * Note that we do not employ any kind of synchronization on the following caching variable. If the variable is | |||
783 | * accessed in multi-threaded programs in the worst case it might happen that we initialize twice, but that | |||
784 | * shouldn't matter as each initialization should come to the same result. */ | |||
785 | static int cache = -1; | |||
786 | ||||
787 | if (cache < 0) | |||
788 | cache = access("/etc/systemd/dont-synthesize-nobody", F_OK0) < 0; | |||
789 | ||||
790 | return cache; | |||
791 | #endif | |||
792 | } | |||
793 | ||||
794 | int putpwent_sane(const struct passwd *pw, FILE *stream) { | |||
795 | assert(pw)do { if ((__builtin_expect(!!(!(pw)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("pw"), "../src/basic/user-util.c", 795, __PRETTY_FUNCTION__ ); } while (0); | |||
796 | assert(stream)do { if ((__builtin_expect(!!(!(stream)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("stream"), "../src/basic/user-util.c", 796 , __PRETTY_FUNCTION__); } while (0); | |||
797 | ||||
798 | errno(*__errno_location ()) = 0; | |||
799 | if (putpwent(pw, stream) != 0) | |||
800 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -EIO5; | |||
801 | ||||
802 | return 0; | |||
803 | } | |||
804 | ||||
805 | int putspent_sane(const struct spwd *sp, FILE *stream) { | |||
806 | assert(sp)do { if ((__builtin_expect(!!(!(sp)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("sp"), "../src/basic/user-util.c", 806, __PRETTY_FUNCTION__ ); } while (0); | |||
807 | assert(stream)do { if ((__builtin_expect(!!(!(stream)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("stream"), "../src/basic/user-util.c", 807 , __PRETTY_FUNCTION__); } while (0); | |||
808 | ||||
809 | errno(*__errno_location ()) = 0; | |||
810 | if (putspent(sp, stream) != 0) | |||
811 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -EIO5; | |||
812 | ||||
813 | return 0; | |||
814 | } | |||
815 | ||||
816 | int putgrent_sane(const struct group *gr, FILE *stream) { | |||
817 | assert(gr)do { if ((__builtin_expect(!!(!(gr)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("gr"), "../src/basic/user-util.c", 817, __PRETTY_FUNCTION__ ); } while (0); | |||
818 | assert(stream)do { if ((__builtin_expect(!!(!(stream)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("stream"), "../src/basic/user-util.c", 818 , __PRETTY_FUNCTION__); } while (0); | |||
819 | ||||
820 | errno(*__errno_location ()) = 0; | |||
821 | if (putgrent(gr, stream) != 0) | |||
822 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -EIO5; | |||
823 | ||||
824 | return 0; | |||
825 | } | |||
826 | ||||
827 | #if ENABLE_GSHADOW1 | |||
828 | int putsgent_sane(const struct sgrp *sg, FILE *stream) { | |||
829 | assert(sg)do { if ((__builtin_expect(!!(!(sg)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("sg"), "../src/basic/user-util.c", 829, __PRETTY_FUNCTION__ ); } while (0); | |||
830 | assert(stream)do { if ((__builtin_expect(!!(!(stream)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("stream"), "../src/basic/user-util.c", 830 , __PRETTY_FUNCTION__); } while (0); | |||
831 | ||||
832 | errno(*__errno_location ()) = 0; | |||
833 | if (putsgent(sg, stream) != 0) | |||
834 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -EIO5; | |||
835 | ||||
836 | return 0; | |||
837 | } | |||
838 | #endif | |||
839 | ||||
840 | int fgetpwent_sane(FILE *stream, struct passwd **pw) { | |||
841 | struct passwd *p; | |||
842 | ||||
843 | assert(pw)do { if ((__builtin_expect(!!(!(pw)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("pw"), "../src/basic/user-util.c", 843, __PRETTY_FUNCTION__ ); } while (0); | |||
844 | assert(stream)do { if ((__builtin_expect(!!(!(stream)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("stream"), "../src/basic/user-util.c", 844 , __PRETTY_FUNCTION__); } while (0); | |||
845 | ||||
846 | errno(*__errno_location ()) = 0; | |||
847 | p = fgetpwent(stream); | |||
848 | if (!p && errno(*__errno_location ()) != ENOENT2) | |||
849 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -EIO5; | |||
850 | ||||
851 | *pw = p; | |||
852 | return !!p; | |||
853 | } | |||
854 | ||||
855 | int fgetspent_sane(FILE *stream, struct spwd **sp) { | |||
856 | struct spwd *s; | |||
857 | ||||
858 | assert(sp)do { if ((__builtin_expect(!!(!(sp)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("sp"), "../src/basic/user-util.c", 858, __PRETTY_FUNCTION__ ); } while (0); | |||
859 | assert(stream)do { if ((__builtin_expect(!!(!(stream)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("stream"), "../src/basic/user-util.c", 859 , __PRETTY_FUNCTION__); } while (0); | |||
860 | ||||
861 | errno(*__errno_location ()) = 0; | |||
862 | s = fgetspent(stream); | |||
863 | if (!s && errno(*__errno_location ()) != ENOENT2) | |||
864 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -EIO5; | |||
865 | ||||
866 | *sp = s; | |||
867 | return !!s; | |||
868 | } | |||
869 | ||||
870 | int fgetgrent_sane(FILE *stream, struct group **gr) { | |||
871 | struct group *g; | |||
872 | ||||
873 | assert(gr)do { if ((__builtin_expect(!!(!(gr)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("gr"), "../src/basic/user-util.c", 873, __PRETTY_FUNCTION__ ); } while (0); | |||
874 | assert(stream)do { if ((__builtin_expect(!!(!(stream)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("stream"), "../src/basic/user-util.c", 874 , __PRETTY_FUNCTION__); } while (0); | |||
875 | ||||
876 | errno(*__errno_location ()) = 0; | |||
877 | g = fgetgrent(stream); | |||
878 | if (!g && errno(*__errno_location ()) != ENOENT2) | |||
879 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -EIO5; | |||
880 | ||||
881 | *gr = g; | |||
882 | return !!g; | |||
883 | } | |||
884 | ||||
885 | #if ENABLE_GSHADOW1 | |||
886 | int fgetsgent_sane(FILE *stream, struct sgrp **sg) { | |||
887 | struct sgrp *s; | |||
888 | ||||
889 | assert(sg)do { if ((__builtin_expect(!!(!(sg)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("sg"), "../src/basic/user-util.c", 889, __PRETTY_FUNCTION__ ); } while (0); | |||
890 | assert(stream)do { if ((__builtin_expect(!!(!(stream)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("stream"), "../src/basic/user-util.c", 890 , __PRETTY_FUNCTION__); } while (0); | |||
891 | ||||
892 | errno(*__errno_location ()) = 0; | |||
893 | s = fgetsgent(stream); | |||
894 | if (!s && errno(*__errno_location ()) != ENOENT2) | |||
895 | return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -EIO5; | |||
896 | ||||
897 | *sg = s; | |||
898 | return !!s; | |||
899 | } | |||
900 | #endif |