Bug Summary

File:build-scan/../src/basic/user-util.c
Warning:line 354, column 27
Potential leak of memory pointed to by 'buf'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name user-util.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -menable-no-infs -menable-no-nans -menable-unsafe-fp-math -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/12.0.0 -include config.h -I src/basic/libbasic.a.p -I src/basic -I ../src/basic -I src/shared -I ../src/shared -I src/systemd -I ../src/systemd -I src/journal -I ../src/journal -I src/journal-remote -I ../src/journal-remote -I src/nspawn -I ../src/nspawn -I src/resolve -I ../src/resolve -I src/timesync -I ../src/timesync -I ../src/time-wait-sync -I src/login -I ../src/login -I src/udev -I ../src/udev -I src/libudev -I ../src/libudev -I src/core -I ../src/core -I ../src/libsystemd/sd-bus -I ../src/libsystemd/sd-device -I ../src/libsystemd/sd-hwdb -I ../src/libsystemd/sd-id128 -I ../src/libsystemd/sd-netlink -I ../src/libsystemd/sd-network -I src/libsystemd-network -I ../src/libsystemd-network -I . -I .. -I /usr/include/blkid -I /usr/include/libmount -D _FILE_OFFSET_BITS=64 -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Wno-format-signedness -Wno-error=nonnull -std=gnu99 -fconst-strings -fdebug-compilation-dir /home/mrc0mmand/repos/@redhat-plumbers/systemd-rhel8/build-scan -ferror-limit 19 -fvisibility default -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-07-16-221226-1465241-1 -x c ../src/basic/user-util.c
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
32bool_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
47int 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_10 { 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
78char* 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
90char *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
100int 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
192static 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
211int 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
235int 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
287char* 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
329char* gid_to_name(gid_t gid) {
330 char *ret;
331 int r;
332
333 if (gid == 0)
1
Assuming 'gid' is not equal to 0
2
Taking false branch
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)) {
3
Taking true branch
340 long bufsize;
341
342 bufsize = sysconf(_SC_GETGR_R_SIZE_MAX_SC_GETGR_R_SIZE_MAX);
343 if (bufsize <= 0)
4
Assuming 'bufsize' is > 0
5
Taking false branch
344 bufsize = 4096;
345
346 for (;;) {
6
Loop condition is true. Entering loop body
347 struct group grbuf, *gr = NULL((void*)0);
348 _cleanup_free___attribute__((cleanup(freep))) char *buf = NULL((void*)0);
349
350 buf = malloc(bufsize);
7
Memory is allocated
351 if (!buf)
8
Assuming 'buf' is non-null
9
Taking false branch
352 return NULL((void*)0);
353
354 r = getgrgid_r(gid, &grbuf, buf, (size_t) bufsize, &gr);
10
Potential leak of memory pointed to by 'buf'
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
370int 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
402int 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
413int 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
469int 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
525int 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
541int 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
581bool_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
700bool_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
718bool_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
744int 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
773bool_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
794int 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
805int 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
816int 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
828int 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
840int 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
855int 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
870int 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
886int 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