Bug Summary

File:build-scan/../src/nspawn/nspawn-setuid.c
Warning:line 171, column 24
Potential leak of memory pointed to by 'home'

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 nspawn-setuid.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/nspawn/libnspawn-core.a.p -I src/nspawn -I ../src/nspawn -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/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 .. -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 hidden -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/nspawn/nspawn-setuid.c
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include <grp.h>
4#include <sys/types.h>
5#include <unistd.h>
6
7#include "alloc-util.h"
8#include "def.h"
9#include "errno.h"
10#include "fd-util.h"
11#include "fileio.h"
12#include "mkdir.h"
13#include "nspawn-setuid.h"
14#include "process-util.h"
15#include "signal-util.h"
16#include "string-util.h"
17#include "strv.h"
18#include "user-util.h"
19#include "util.h"
20
21static int spawn_getent(const char *database, const char *key, pid_t *rpid) {
22 int pipe_fds[2], r;
23 pid_t pid;
24
25 assert(database)do { if ((__builtin_expect(!!(!(database)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("database"), "../src/nspawn/nspawn-setuid.c"
, 25, __PRETTY_FUNCTION__); } while (0)
;
26 assert(key)do { if ((__builtin_expect(!!(!(key)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("key"), "../src/nspawn/nspawn-setuid.c",
26, __PRETTY_FUNCTION__); } while (0)
;
27 assert(rpid)do { if ((__builtin_expect(!!(!(rpid)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("rpid"), "../src/nspawn/nspawn-setuid.c"
, 27, __PRETTY_FUNCTION__); } while (0)
;
28
29 if (pipe2(pipe_fds, O_CLOEXEC02000000) < 0)
30 return log_error_errno(errno, "Failed to allocate pipe: %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/nspawn/nspawn-setuid.c", 30, __func__
, "Failed to allocate pipe: %m") : -abs(_e); })
;
31
32 r = safe_fork("(getent)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
33 if (r < 0) {
34 safe_close_pair(pipe_fds);
35 return r;
36 }
37 if (r == 0) {
38 char *empty_env = NULL((void*)0);
39
40 safe_close(pipe_fds[0]);
41
42 if (rearrange_stdio(-1, pipe_fds[1], -1) < 0)
43 _exit(EXIT_FAILURE1);
44
45 close_all_fds(NULL((void*)0), 0);
46
47 execle("/usr/bin/getent", "getent", database, key, NULL((void*)0), &empty_env);
48 execle("/bin/getent", "getent", database, key, NULL((void*)0), &empty_env);
49 _exit(EXIT_FAILURE1);
50 }
51
52 pipe_fds[1] = safe_close(pipe_fds[1]);
53
54 *rpid = pid;
55
56 return pipe_fds[0];
57}
58
59int change_uid_gid(const char *user, char **_home) {
60 char *x, *u, *g, *h;
61 const char *word, *state;
62 _cleanup_free___attribute__((cleanup(freep))) uid_t *uids = NULL((void*)0);
63 _cleanup_free___attribute__((cleanup(freep))) char *home = NULL((void*)0), *line = NULL((void*)0);
64 _cleanup_fclose___attribute__((cleanup(fclosep))) FILE *f = NULL((void*)0);
65 _cleanup_close___attribute__((cleanup(closep))) int fd = -1;
66 unsigned n_uids = 0;
67 size_t sz = 0, l;
68 uid_t uid;
69 gid_t gid;
70 pid_t pid;
71 int r;
72
73 assert(_home)do { if ((__builtin_expect(!!(!(_home)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("_home"), "../src/nspawn/nspawn-setuid.c"
, 73, __PRETTY_FUNCTION__); } while (0)
;
1
Assuming '_home' is non-null
2
Taking false branch
3
Loop condition is false. Exiting loop
74
75 if (!user || STR_IN_SET(user, "root", "0")(!!strv_find((((char**) ((const char*[]) { "root", "0", ((void
*)0) }))), (user)))
) {
4
Assuming 'user' is non-null
5
Assuming the condition is true
6
Taking false branch
76 /* Reset everything fully to 0, just in case */
77
78 r = reset_uid_gid();
79 if (r < 0)
80 return log_error_errno(r, "Failed to become root: %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/nspawn/nspawn-setuid.c", 80, __func__, "Failed to become root: %m"
) : -abs(_e); })
;
81
82 *_home = NULL((void*)0);
83 return 0;
84 }
85
86 /* First, get user credentials */
87 fd = spawn_getent("passwd", user, &pid);
88 if (fd < 0)
7
Assuming 'fd' is >= 0
8
Taking false branch
89 return fd;
90
91 f = fdopen(fd, "re");
92 if (!f)
9
Assuming 'f' is non-null
10
Taking false branch
93 return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-setuid.c"
, 93, __func__)
;
94 fd = -1;
95
96 r = read_line(f, LONG_LINE_MAX(1U*1024U*1024U), &line);
97 if (r == 0) {
11
Assuming 'r' is not equal to 0
12
Taking false branch
98 log_error("Failed to resolve user %s.", user)({ 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/nspawn/nspawn-setuid.c", 98, __func__, "Failed to resolve user %s."
, user) : -abs(_e); })
;
99 return -ESRCH3;
100 }
101 if (r < 0)
13
Assuming 'r' is >= 0
14
Taking false branch
102 return log_error_errno(r, "Failed to read from getent: %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/nspawn/nspawn-setuid.c", 102, __func__, "Failed to read from getent: %m"
) : -abs(_e); })
;
103
104 (void) wait_for_terminate_and_check("getent passwd", pid, WAIT_LOG);
105
106 x = strchr(line, ':');
107 if (!x) {
15
Assuming 'x' is non-null
16
Taking false branch
108 log_error("/etc/passwd entry has invalid user 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/nspawn/nspawn-setuid.c", 108, __func__, "/etc/passwd entry has invalid user field."
) : -abs(_e); })
;
109 return -EIO5;
110 }
111
112 u = strchr(x+1, ':');
113 if (!u) {
17
Assuming 'u' is non-null
18
Taking false branch
114 log_error("/etc/passwd entry has invalid password 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/nspawn/nspawn-setuid.c", 114, __func__, "/etc/passwd entry has invalid password field."
) : -abs(_e); })
;
115 return -EIO5;
116 }
117
118 u++;
119 g = strchr(u, ':');
120 if (!g) {
19
Assuming 'g' is non-null
20
Taking false branch
121 log_error("/etc/passwd entry has invalid UID 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/nspawn/nspawn-setuid.c", 121, __func__, "/etc/passwd entry has invalid UID field."
) : -abs(_e); })
;
122 return -EIO5;
123 }
124
125 *g = 0;
126 g++;
127 x = strchr(g, ':');
128 if (!x) {
21
Assuming 'x' is non-null
22
Taking false branch
129 log_error("/etc/passwd entry has invalid GID 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/nspawn/nspawn-setuid.c", 129, __func__, "/etc/passwd entry has invalid GID field."
) : -abs(_e); })
;
130 return -EIO5;
131 }
132
133 *x = 0;
134 h = strchr(x+1, ':');
135 if (!h) {
23
Assuming 'h' is non-null
24
Taking false branch
136 log_error("/etc/passwd entry has invalid GECOS 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/nspawn/nspawn-setuid.c", 136, __func__, "/etc/passwd entry has invalid GECOS field."
) : -abs(_e); })
;
137 return -EIO5;
138 }
139
140 h++;
141 x = strchr(h, ':');
142 if (!x) {
25
Assuming 'x' is non-null
26
Taking false branch
143 log_error("/etc/passwd entry has invalid home directory 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/nspawn/nspawn-setuid.c", 143, __func__, "/etc/passwd entry has invalid home directory field."
) : -abs(_e); })
;
144 return -EIO5;
145 }
146
147 *x = 0;
148
149 r = parse_uid(u, &uid);
150 if (r < 0) {
27
Assuming 'r' is >= 0
28
Taking false branch
151 log_error("Failed to parse UID of user.")({ 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/nspawn/nspawn-setuid.c", 151, __func__, "Failed to parse UID of user."
) : -abs(_e); })
;
152 return -EIO5;
153 }
154
155 r = parse_gid(g, &gid);
156 if (r < 0) {
29
Assuming 'r' is >= 0
30
Taking false branch
157 log_error("Failed to parse GID of user.")({ 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/nspawn/nspawn-setuid.c", 157, __func__, "Failed to parse GID of user."
) : -abs(_e); })
;
158 return -EIO5;
159 }
160
161 home = strdup(h);
31
Memory is allocated
162 if (!home)
32
Assuming 'home' is non-null
33
Taking false branch
163 return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-setuid.c"
, 163, __func__)
;
164
165 f = safe_fclose(f);
166 line = mfree(line);
167
168 /* Second, get group memberships */
169 fd = spawn_getent("initgroups", user, &pid);
170 if (fd < 0)
34
Assuming 'fd' is < 0
35
Taking true branch
171 return fd;
36
Potential leak of memory pointed to by 'home'
172
173 f = fdopen(fd, "re");
174 if (!f)
175 return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-setuid.c"
, 175, __func__)
;
176 fd = -1;
177
178 r = read_line(f, LONG_LINE_MAX(1U*1024U*1024U), &line);
179 if (r == 0) {
180 log_error("Failed to resolve user %s.", user)({ 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/nspawn/nspawn-setuid.c", 180, __func__, "Failed to resolve user %s."
, user) : -abs(_e); })
;
181 return -ESRCH3;
182 }
183 if (r < 0)
184 return log_error_errno(r, "Failed to read from getent: %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/nspawn/nspawn-setuid.c", 184, __func__, "Failed to read from getent: %m"
) : -abs(_e); })
;
185
186 (void) wait_for_terminate_and_check("getent initgroups", pid, WAIT_LOG);
187
188 /* Skip over the username and subsequent separator whitespace */
189 x = line;
190 x += strcspn(x, WHITESPACE" \t\n\r");
191 x += strspn(x, WHITESPACE" \t\n\r");
192
193 FOREACH_WORD(word, l, x, state)for ((state) = (x), (word) = split(&(state), &(l), (" \t\n\r"
), (0)); (word); (word) = split(&(state), &(l), (" \t\n\r"
), (0)))
{
194 char c[l+1];
195
196 memcpy(c, word, l);
197 c[l] = 0;
198
199 if (!GREEDY_REALLOC(uids, sz, n_uids+1)greedy_realloc((void**) &(uids), &(sz), (n_uids+1), sizeof
((uids)[0]))
)
200 return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-setuid.c"
, 200, __func__)
;
201
202 r = parse_uid(c, &uids[n_uids++]);
203 if (r < 0)
204 return log_error_errno(r, "Failed to parse group data from getent: %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/nspawn/nspawn-setuid.c", 204, __func__, "Failed to parse group data from getent: %m"
) : -abs(_e); })
;
205 }
206
207 r = mkdir_parents(home, 0775);
208 if (r < 0)
209 return log_error_errno(r, "Failed to make home root directory: %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/nspawn/nspawn-setuid.c", 209, __func__, "Failed to make home root directory: %m"
) : -abs(_e); })
;
210
211 r = mkdir_safe(home, 0755, uid, gid, 0);
212 if (r < 0 && !IN_SET(r, -EEXIST, -ENOTDIR)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){-17, -20})/sizeof(int)]; switch(r) { case
-17: case -20: _found = 1; break; default: break; } _found; }
)
)
213 return log_error_errno(r, "Failed to make home directory: %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/nspawn/nspawn-setuid.c", 213, __func__, "Failed to make home directory: %m"
) : -abs(_e); })
;
214
215 (void) fchown(STDIN_FILENO0, uid, gid);
216 (void) fchown(STDOUT_FILENO1, uid, gid);
217 (void) fchown(STDERR_FILENO2, uid, gid);
218
219 if (setgroups(n_uids, uids) < 0)
220 return log_error_errno(errno, "Failed to set auxiliary groups: %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/nspawn/nspawn-setuid.c", 220, __func__
, "Failed to set auxiliary groups: %m") : -abs(_e); })
;
221
222 if (setresgid(gid, gid, gid) < 0)
223 return log_error_errno(errno, "setresgid() failed: %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/nspawn/nspawn-setuid.c", 223, __func__
, "setresgid() failed: %m") : -abs(_e); })
;
224
225 if (setresuid(uid, uid, uid) < 0)
226 return log_error_errno(errno, "setresuid() failed: %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/nspawn/nspawn-setuid.c", 226, __func__
, "setresuid() failed: %m") : -abs(_e); })
;
227
228 if (_home)
229 *_home = TAKE_PTR(home)({ typeof(home) _ptr_ = (home); (home) = ((void*)0); _ptr_; }
)
;
230
231 return 0;
232}