File: | build-scan/../src/nspawn/nspawn-setuid.c |
Warning: | line 171, column 24 Potential leak of memory pointed to by 'home' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
21 | static 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 | ||||
59 | int 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); | |||
| ||||
74 | ||||
75 | if (!user || STR_IN_SET(user, "root", "0")(!!strv_find((((char**) ((const char*[]) { "root", "0", ((void *)0) }))), (user)))) { | |||
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) | |||
89 | return fd; | |||
90 | ||||
91 | f = fdopen(fd, "re"); | |||
92 | if (!f) | |||
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) { | |||
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) | |||
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) { | |||
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) { | |||
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) { | |||
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) { | |||
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) { | |||
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) { | |||
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) { | |||
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) { | |||
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); | |||
162 | if (!home) | |||
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) | |||
171 | return fd; | |||
| ||||
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 | } |