| 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 | } |