Bug Summary

File:lwan-straitjacket.c
Warning:line 214, column 21
No call of chdir("/") immediately after chroot

Annotated Source Code

1/*
2 * lwan - simple web server
3 * Copyright (c) 2015 Leandro A. F. Pereira <leandro@hardinfo.org>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20#define _GNU_SOURCE
21#include <dirent.h>
22#include <errno(*__errno_location ()).h>
23#include <grp.h>
24#include <pwd.h>
25#include <stdlib.h>
26#include <string.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <unistd.h>
30
31#include "lwan-private.h"
32
33#include "lwan-config.h"
34#include "lwan-status.h"
35
36static bool_Bool get_user_uid_gid(const char *user, uid_t *uid, gid_t *gid)
37{
38 struct passwd pwd = { };
39 struct passwd *result;
40 char *buf;
41 long pw_size_max = sysconf(_SC_GETPW_R_SIZE_MAX_SC_GETPW_R_SIZE_MAX);
42 int r;
43
44 if (!user || !*user) {
45 lwan_status_error("Username should be provided")lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 45, __FUNCTION__, "Username should be provided")
;
46 return false0;
47 }
48
49 if (pw_size_max < 0)
50 pw_size_max = 16384;
51
52 buf = malloc((size_t)pw_size_max);
53 if (!buf) {
54 lwan_status_error("Could not allocate buffer for passwd struct")lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 54, __FUNCTION__, "Could not allocate buffer for passwd struct"
)
;
55 return false0;
56 }
57
58 r = getpwnam_r(user, &pwd, buf, (size_t)pw_size_max, &result);
59 *uid = pwd.pw_uid;
60 *gid = pwd.pw_gid;
61 free(buf);
62
63 if (result)
64 return true1;
65
66 if (!r) {
67 lwan_status_error("Username not found: %s", user)lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 67, __FUNCTION__, "Username not found: %s", user)
;
68 } else {
69 errno(*__errno_location ()) = r;
70 lwan_status_perror("Could not obtain uid/gid for user %s", user)lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 70, __FUNCTION__, "Could not obtain uid/gid for user %s", user
)
;
71 }
72
73 return false0;
74}
75
76static bool_Bool switch_to_user(uid_t uid, gid_t gid, const char *username)
77{
78 uid_t ruid, euid, suid;
79 gid_t rgid, egid, sgid;
80
81 lwan_status_info("Dropping privileges to UID %d, GID %d (%s)",lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 82, __FUNCTION__, "Dropping privileges to UID %d, GID %d (%s)"
, uid, gid, username)
82 uid, gid, username)lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 82, __FUNCTION__, "Dropping privileges to UID %d, GID %d (%s)"
, uid, gid, username)
;
83
84 if (setresgid(gid, gid, gid) < 0)
85 return false0;
86#if defined(__APPLE__)
87 if (initgroups(username, (int)gid) < 0)
88 return false0;
89#else
90 if (initgroups(username, gid) < 0)
91 return false0;
92#endif
93 if (setresuid(uid, uid, uid) < 0)
94 return false0;
95
96 if (getresuid(&ruid, &euid, &suid) < 0)
97 return false0;
98 if (ruid != euid || euid != suid || suid != uid)
99 return false0;
100
101 if (getresgid(&rgid, &egid, &sgid) < 0)
102 return false0;
103 if (rgid != egid || egid != sgid || sgid != gid)
104 return false0;
105
106 return true1;
107}
108
109#ifdef __linux__1
110static void abort_on_open_directories(void)
111{
112 /* This is racy, of course, but right now I see no other way of ensuring
113 * that there are no file descriptors to directories open. */
114 DIR *dir = opendir("/proc/self/fd");
115 struct dirent *ent;
116 char own_fd[3 * sizeof(int)];
117 int ret;
118
119 if (!dir) {
120 lwan_status_critical_perror(lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 121, __FUNCTION__, "Could not determine if there are open directory fds"
)
121 "Could not determine if there are open directory fds")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 121, __FUNCTION__, "Could not determine if there are open directory fds"
)
;
122 }
123
124 ret = snprintf(own_fd, sizeof(own_fd), "%d", dirfd(dir));
125 if (ret < 0 || ret >= (int)sizeof(own_fd)) {
126 lwan_status_critical("Could not get descriptor of /proc/self/fd")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 126, __FUNCTION__, "Could not get descriptor of /proc/self/fd"
)
;
127 }
128
129 while ((ent = readdir(dir))) {
130 char path[PATH_MAX4096];
131 struct stat st;
132 ssize_t len;
133
134 if (!strcmp(ent->d_name, own_fd))
135 continue;
136 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
137 continue;
138
139 len = readlinkat(dirfd(dir), ent->d_name, path, sizeof(path));
140 if (len < 0) {
141 lwan_status_critical_perror(lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 142, __FUNCTION__, "Could not get information about fd %s",
ent->d_name)
142 "Could not get information about fd %s", ent->d_name)lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 142, __FUNCTION__, "Could not get information about fd %s",
ent->d_name)
;
143 }
144 path[len] = '\0';
145
146 if (path[0] != '/') {
147 /* readlink() there will point to the realpath() of a file, so
148 * if it's on a filesystem, it starts with '/'. Sockets, for
149 * instance, begin with "socket:" instead... so no need for
150 * stat(). */
151 continue;
152 }
153
154 if (stat(path, &st) < 0) {
155 lwan_status_critical_perror(lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 156, __FUNCTION__, "Could not get information about open file: %s"
, path)
156 "Could not get information about open file: %s", path)lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 156, __FUNCTION__, "Could not get information about open file: %s"
, path)
;
157 }
158
159 if (S_ISDIR(st.st_mode)((((st.st_mode)) & 0170000) == (0040000))) {
160 closedir(dir);
161
162 lwan_status_critical(lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 164, __FUNCTION__, "The directory '%s' is open (fd %s), can't chroot"
, path, ent->d_name)
163 "The directory '%s' is open (fd %s), can't chroot",lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 164, __FUNCTION__, "The directory '%s' is open (fd %s), can't chroot"
, path, ent->d_name)
164 path, ent->d_name)lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 164, __FUNCTION__, "The directory '%s' is open (fd %s), can't chroot"
, path, ent->d_name)
;
165 return;
166 }
167 }
168
169 closedir(dir);
170}
171#else
172static void abort_on_open_directories(void)
173{
174}
175#endif
176
177void lwan_straitjacket_enforce(struct config *c, struct config_line *l)
178{
179 char *user_name = NULL((void*)0);
180 char *chroot_path = NULL((void*)0);
181 uid_t uid;
182 gid_t gid;
183
184 if (geteuid() != 0) {
1
Assuming the condition is false
2
Taking false branch
185 config_error(c, "Straitjacket requires root privileges");
186 return;
187 }
188
189 while (config_read_line(c, l)) {
3
Loop condition is true. Entering loop body
8
Loop condition is true. Entering loop body
12
Loop condition is true. Entering loop body
190 switch (l->type) {
4
Control jumps to 'case CONFIG_LINE_TYPE_LINE:' at line 191
9
Control jumps to 'case CONFIG_LINE_TYPE_LINE:' at line 191
13
Control jumps to 'case CONFIG_LINE_TYPE_SECTION_END:' at line 205
191 case CONFIG_LINE_TYPE_LINE:
192 /* TODO: limit_syscalls */
193 if (streq(l->key, "user")) {
5
Taking false branch
10
Taking true branch
194 user_name = strdupa(l->value)(__extension__ ({ const char *__old = (l->value); size_t __len
= strlen (__old) + 1; char *__new = (char *) __builtin_alloca
(__len); (char *) memcpy (__new, __old, __len); }))
;
195 } else if (streq(l->key, "chroot")) {
6
Taking true branch
196 chroot_path = strdupa(l->value)(__extension__ ({ const char *__old = (l->value); size_t __len
= strlen (__old) + 1; char *__new = (char *) __builtin_alloca
(__len); (char *) memcpy (__new, __old, __len); }))
;
197 } else {
198 config_error(c, "Invalid key: %s", l->key);
199 return;
200 }
201 break;
7
Execution continues on line 189
11
Execution continues on line 189
202 case CONFIG_LINE_TYPE_SECTION:
203 config_error(c, "Straitjacket accepts no sections");
204 return;
205 case CONFIG_LINE_TYPE_SECTION_END:
206 if (!get_user_uid_gid(user_name, &uid, &gid)) {
14
Taking false branch
207 config_error(c, "Unknown user: %s", user_name);
208 return;
209 }
210
211 if (chroot_path) {
15
Taking true branch
212 abort_on_open_directories();
213 if (chroot(chroot_path) < 0) {
16
Taking true branch
214 lwan_status_critical_perror("Could not chroot() to %s",lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 215, __FUNCTION__, "Could not chroot() to %s", chroot_path)
17
Within the expansion of the macro 'lwan_status_critical_perror':
a
No call of chdir("/") immediately after chroot
215 chroot_path)lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 215, __FUNCTION__, "Could not chroot() to %s", chroot_path)
;
216 }
217 if (chdir("/") < 0)
218 lwan_status_critical_perror("Could not chdir() to /")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 218, __FUNCTION__, "Could not chdir() to /")
;
219 lwan_status_debug("Jailed to %s", chroot_path)lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 219, __FUNCTION__, "Jailed to %s", chroot_path)
;
220 }
221
222 if (!switch_to_user(uid, gid, user_name)) {
223 lwan_status_critical("Could not drop privileges to %s, aborting",lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 224, __FUNCTION__, "Could not drop privileges to %s, aborting"
, user_name)
224 user_name)lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/common/lwan-straitjacket.c"
, 224, __FUNCTION__, "Could not drop privileges to %s, aborting"
, user_name)
;
225 }
226 return;
227 }
228 }
229
230 config_error(c, "Expecting section end while parsing straitjacket");
231}