1 /* radare - LGPL - Copyright 2012-2020 - pancake */
2
3 #include <r_util.h>
4 #include <signal.h>
5 #if _MSC_VER
6 #include <process.h> // to compile execl under msvc windows
7 #include <direct.h> // to compile chdir under msvc windows
8 #endif
9
10 #if HAVE_CAPSICUM
11 #include <sys/capsicum.h>
12 #endif
13
14 #if LIBC_HAVE_PRIV_SET
15 #include <priv.h>
16 #endif
17
18 static bool enabled = false;
19 static bool disabled = false;
20
inHomeWww(const char * path)21 static bool inHomeWww(const char *path) {
22 r_return_val_if_fail (path, false);
23 bool ret = false;
24 char *homeWww = r_str_home (R2_HOME_WWWROOT R_SYS_DIR);
25 if (homeWww) {
26 if (!strncmp (path, homeWww, strlen (homeWww))) {
27 ret = true;
28 }
29 free (homeWww);
30 }
31 return ret;
32 }
33
34 /**
35 * This function verifies that the given path is allowed. Paths are allowed only if they don't
36 * contain .. components (which would indicate directory traversal) and they are relative.
37 * Paths pointing into the webroot are an exception: For reaching the webroot, .. and absolute
38 * path are ok.
39 */
r_sandbox_check_path(const char * path)40 R_API bool r_sandbox_check_path (const char *path) {
41 r_return_val_if_fail (path, false);
42 size_t root_len;
43 char *p;
44 /* XXX: the sandbox can be bypassed if a directory is symlink */
45 root_len = strlen (R2_LIBDIR"/radare2");
46 if (!strncmp (path, R2_LIBDIR"/radare2", root_len)) {
47 return true;
48 }
49 root_len = strlen (R2_DATDIR"/radare2");
50 if (!strncmp (path, R2_DATDIR"/radare2", root_len)) {
51 return true;
52 }
53 if (inHomeWww (path)) {
54 return true;
55 }
56 // Accessing stuff inside the webroot is ok even if we need .. or leading / for that
57 root_len = strlen (R2_WWWROOT);
58 if (R2_WWWROOT[0] && !strncmp (path, R2_WWWROOT, root_len) && (
59 R2_WWWROOT[root_len-1] == '/' || path[root_len] == '/' || path[root_len] == '\0')) {
60 path += strlen (R2_WWWROOT);
61 while (*path == '/') {
62 path++;
63 }
64 }
65
66 // ./ path is not allowed
67 if (path[0]=='.' && path[1]=='/') {
68 return false;
69 }
70 // Properly check for directory traversal using "..". First, does it start with a .. part?
71 if (path[0] == '.' && path[1] == '.' && (path[2] == '\0' || path[2] == '/')) {
72 return 0;
73 }
74
75 // Or does it have .. in some other position?
76 for (p = strstr (path, "/.."); p; p = strstr(p, "/..")) {
77 if (p[3] == '\0' || p[3] == '/') {
78 return false;
79 }
80 }
81 // Absolute paths are forbidden.
82 if (*path == '/') {
83 return false;
84 }
85 #if __UNIX__
86 char ch;
87 if (readlink (path, &ch, 1) != -1) {
88 return false;
89 }
90 #endif
91 return true;
92 }
93
r_sandbox_disable(bool e)94 R_API bool r_sandbox_disable (bool e) {
95 if (e) {
96 #if LIBC_HAVE_PLEDGE
97 if (enabled) {
98 eprintf ("sandbox mode couldn't be disabled when pledged\n");
99 return enabled;
100 }
101 #endif
102 #if HAVE_CAPSICUM
103 if (enabled) {
104 eprintf ("sandbox mode couldn't be disabled in capability mode\n");
105 return enabled;
106 }
107 #endif
108 #if LIBC_HAVE_PRIV_SET
109 if (enabled) {
110 eprintf ("sandbox mode couldn't be disabled in priv mode\n");
111 return enabled;
112 }
113 #endif
114 disabled = enabled;
115 enabled = false;
116 } else {
117 enabled = disabled;
118 disabled = false;
119 }
120 return enabled;
121 }
122
r_sandbox_enable(bool e)123 R_API bool r_sandbox_enable (bool e) {
124 if (enabled) {
125 if (!e) {
126 // eprintf ("Can't disable sandbox\n");
127 }
128 return true;
129 }
130 enabled = e;
131 #if LIBC_HAVE_PLEDGE
132 if (enabled && pledge ("stdio rpath tty prot_exec inet", NULL) == -1) {
133 eprintf ("sandbox: pledge call failed\n");
134 return false;
135 }
136 #endif
137 #if HAVE_CAPSICUM
138 if (enabled) {
139 #if __FreeBSD_version >= 1000000
140 cap_rights_t wrt, rdr;
141
142 if (!cap_rights_init (&wrt, CAP_READ, CAP_WRITE)) {
143 eprintf ("sandbox: write descriptor failed\n");
144 return false;
145 }
146
147 if (!cap_rights_init (&rdr, CAP_READ, CAP_EVENT, CAP_FCNTL)) {
148 eprintf ("sandbox: read descriptor failed\n");
149 return false;
150 }
151
152 if (cap_rights_limit (STDIN_FILENO, &rdr) == -1) {
153 eprintf ("sandbox: stdin protection failed\n");
154 return false;
155 }
156
157 if (cap_rights_limit (STDOUT_FILENO, &wrt) == -1) {
158 eprintf ("sandbox: stdout protection failed\n");
159 return false;
160 }
161
162 if (cap_rights_limit (STDERR_FILENO, &wrt) == -1) {
163 eprintf ("sandbox: stderr protection failed\n");
164 return false;
165 }
166 #endif
167
168 if (cap_enter () != 0) {
169 eprintf ("sandbox: call_enter failed\n");
170 return false;
171 }
172 }
173 #endif
174 #if LIBC_HAVE_PRIV_SET
175 if (enabled) {
176 priv_set_t *priv = priv_allocset();
177 const char *const privrules[] = {
178 PRIV_PROC_INFO,
179 PRIV_PROC_SESSION,
180 PRIV_PROC_ZONE,
181 PRIV_NET_OBSERVABILITY
182 };
183
184 size_t i, privrulescnt = sizeof (privrules) / sizeof (privrules[0]);
185
186 if (!priv) {
187 eprintf ("sandbox: priv_allocset failed\n");
188 return false;
189 }
190 priv_basicset(priv);
191
192 for (i = 0; i < privrulescnt; i ++) {
193 if (priv_delset (priv, privrules[i]) != 0) {
194 priv_emptyset (priv);
195 priv_freeset (priv);
196 eprintf ("sandbox: priv_delset failed\n");
197 return false;
198 }
199 }
200
201 priv_freeset (priv);
202 }
203 #endif
204 return enabled;
205 }
206
r_sandbox_system(const char * x,int n)207 R_API int r_sandbox_system(const char *x, int n) {
208 r_return_val_if_fail (x, -1);
209 if (enabled) {
210 eprintf ("sandbox: system call disabled\n");
211 return -1;
212 }
213 #if LIBC_HAVE_FORK
214 #if LIBC_HAVE_SYSTEM
215 if (n) {
216 #if APPLE_SDK_IPHONEOS
217 #include <spawn.h>
218 int argc;
219 char *cmd = strdup (x);
220 char **argv = r_str_argv (cmd, &argc);
221 if (argv) {
222 char *argv0 = r_file_path (argv[0]);
223 pid_t pid = 0;
224 int r = posix_spawn (&pid, argv0, NULL, NULL, argv, NULL);
225 if (r != 0) {
226 return -1;
227 }
228 int status;
229 int s = waitpid (pid, &status, 0);
230 return WEXITSTATUS (s);
231 }
232 #else
233 return system (x);
234 #endif
235 }
236 return execl ("/bin/sh", "sh", "-c", x, (const char*)NULL);
237 #else
238 #include <spawn.h>
239 if (n && !strchr (x, '|')) {
240 char **argv, *cmd = strdup (x);
241 int rc, pid, argc;
242 char *isbg = strchr (cmd, '&');
243 // XXX this is hacky
244 if (isbg) {
245 *isbg = 0;
246 }
247 argv = r_str_argv (cmd, &argc);
248 if (argv) {
249 char *argv0 = r_file_path (argv[0]);
250 if (!argv0) {
251 eprintf ("Cannot find '%s'\n", argv[0]);
252 return -1;
253 }
254 pid = 0;
255 posix_spawn (&pid, argv0, NULL, NULL, argv, NULL);
256 if (isbg) {
257 // XXX. wait for children
258 rc = 0;
259 } else {
260 rc = waitpid (pid, NULL, 0);
261 }
262 r_str_argv_free (argv);
263 free (argv0);
264 return rc;
265 }
266 eprintf ("Error parsing command arguments\n");
267 return -1;
268 }
269 int child = fork ();
270 if (child == -1) {
271 return -1;
272 }
273 if (child) {
274 return waitpid (child, NULL, 0);
275 }
276 if (execl ("/bin/sh", "sh", "-c", x, (const char*)NULL) == -1) {
277 perror ("execl");
278 }
279 exit (1);
280 #endif
281 #endif
282 return -1;
283 }
284
r_sandbox_creat(const char * path,int mode)285 R_API bool r_sandbox_creat (const char *path, int mode) {
286 if (enabled) {
287 return false;
288 #if 0
289 if (mode & O_CREAT) return -1;
290 if (mode & O_RDWR) return -1;
291 if (!r_sandbox_check_path (path))
292 return -1;
293 #endif
294 }
295 int fd = open (path, O_CREAT | O_TRUNC | O_WRONLY, mode);
296 if (fd != -1) {
297 close (fd);
298 return true;
299 }
300 return false;
301 }
302
expand_home(const char * p)303 static inline char *expand_home(const char *p) {
304 return (*p == '~')? r_str_home (p): strdup (p);
305 }
306
r_sandbox_lseek(int fd,ut64 addr,int whence)307 R_API int r_sandbox_lseek(int fd, ut64 addr, int whence) {
308 if (enabled) {
309 return -1;
310 }
311 return lseek (fd, (off_t)addr, whence);
312 }
313
r_sandbox_truncate(int fd,ut64 length)314 R_API int r_sandbox_truncate(int fd, ut64 length) {
315 if (enabled) {
316 return -1;
317 }
318 #ifdef _MSC_VER
319 return _chsize_s (fd, length);
320 #else
321 return ftruncate (fd, (off_t)length);
322 #endif
323 }
324
r_sandbox_read(int fd,ut8 * buf,int len)325 R_API int r_sandbox_read(int fd, ut8 *buf, int len) {
326 return enabled? -1: read (fd, buf, len);
327 }
328
r_sandbox_write(int fd,const ut8 * buf,int len)329 R_API int r_sandbox_write(int fd, const ut8* buf, int len) {
330 return enabled? -1: write (fd, buf, len);
331 }
332
r_sandbox_close(int fd)333 R_API int r_sandbox_close(int fd) {
334 return enabled? -1: close (fd);
335 }
336
337 /* perm <-> mode */
r_sandbox_open(const char * path,int perm,int mode)338 R_API int r_sandbox_open(const char *path, int perm, int mode) {
339 r_return_val_if_fail (path, -1);
340 char *epath = expand_home (path);
341 int ret = -1;
342 #if __WINDOWS__
343 if (!strcmp (path, "/dev/null")) {
344 path = "NUL";
345 }
346 #endif
347 if (enabled) {
348 if ((perm & O_CREAT) || (perm & O_RDWR)
349 || (!r_sandbox_check_path (epath))) {
350 free (epath);
351 return -1;
352 }
353 }
354 #if __WINDOWS__
355 {
356 DWORD flags = 0;
357 if (perm & O_RANDOM) {
358 flags = FILE_FLAG_RANDOM_ACCESS;
359 } else if (perm & O_SEQUENTIAL) {
360 flags = FILE_FLAG_SEQUENTIAL_SCAN;
361 }
362 if (perm & O_TEMPORARY) {
363 flags |= FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY;
364 } else if (perm & _O_SHORT_LIVED) {
365 flags |= FILE_ATTRIBUTE_TEMPORARY;
366 } else {
367 flags |= FILE_ATTRIBUTE_NORMAL;
368 }
369 DWORD creation = 0;
370 bool read_only = false;
371 if (perm & O_CREAT) {
372 if (perm & O_EXCL) {
373 creation = CREATE_NEW;
374 } else {
375 creation = OPEN_ALWAYS;
376 }
377 if (mode & S_IREAD && !(mode & S_IWRITE)) {
378 flags = FILE_ATTRIBUTE_READONLY;
379 read_only = true;
380 }
381 } else if (perm & O_TRUNC) {
382 creation = TRUNCATE_EXISTING;
383 }
384 if (!creation || !strcasecmp ("NUL", path)) {
385 creation = OPEN_EXISTING;
386 }
387 DWORD permission = 0;
388 if (perm & O_WRONLY) {
389 permission = GENERIC_WRITE;
390 } else if (perm & O_RDWR) {
391 permission = GENERIC_WRITE | GENERIC_READ;
392 } else {
393 permission = GENERIC_READ;
394 }
395 if (perm & O_APPEND) {
396 permission |= FILE_APPEND_DATA;
397 }
398
399 wchar_t *wepath = r_utf8_to_utf16 (epath);
400 if (!wepath) {
401 free (epath);
402 return -1;
403 }
404 HANDLE h = CreateFileW (wepath, permission, FILE_SHARE_READ | (read_only ? 0 : FILE_SHARE_WRITE), NULL, creation, flags, NULL);
405 if (h != INVALID_HANDLE_VALUE) {
406 ret = _open_osfhandle ((intptr_t)h, perm);
407 }
408 free (wepath);
409 }
410 #else // __WINDOWS__
411 ret = open (epath, perm, mode);
412 #endif // __WINDOWS__
413 free (epath);
414 return ret;
415 }
416
r_sandbox_fopen(const char * path,const char * mode)417 R_API FILE *r_sandbox_fopen (const char *path, const char *mode) {
418 r_return_val_if_fail (path && mode, NULL);
419 FILE *ret = NULL;
420 char *epath = NULL;
421 if (enabled) {
422 if (strchr (mode, 'w') || strchr (mode, 'a') || strchr (mode, '+')) {
423 return NULL;
424 }
425 epath = expand_home (path);
426 if (!r_sandbox_check_path (epath)) {
427 free (epath);
428 return NULL;
429 }
430 }
431 if (!epath) {
432 epath = expand_home (path);
433 }
434 if ((strchr (mode, 'w') || strchr (mode, 'a') || r_file_is_regular (epath))) {
435 #if __WINDOWS__
436 wchar_t *wepath = r_utf8_to_utf16 (epath);
437 if (!wepath) {
438 free (epath);
439 return ret;
440 }
441 wchar_t *wmode = r_utf8_to_utf16 (mode);
442 if (!wmode) {
443 free (wepath);
444 free (epath);
445 return ret;
446 }
447 ret = _wfopen (wepath, wmode);
448 free (wmode);
449 free (wepath);
450 #else // __WINDOWS__
451 ret = fopen (epath, mode);
452 #endif // __WINDOWS__
453 }
454 free (epath);
455 return ret;
456 }
457
r_sandbox_chdir(const char * path)458 R_API int r_sandbox_chdir(const char *path) {
459 r_return_val_if_fail (path, -1);
460 if (enabled) {
461 // TODO: check path
462 if (strstr (path, "../")) {
463 return -1;
464 }
465 if (*path == '/') {
466 return -1;
467 }
468 return -1;
469 }
470 return chdir (path);
471 }
472
r_sandbox_kill(int pid,int sig)473 R_API int r_sandbox_kill(int pid, int sig) {
474 r_return_val_if_fail (pid != -1, -1);
475 // XXX: fine-tune. maybe we want to enable kill for child?
476 if (enabled) {
477 return -1;
478 }
479 #if __UNIX__
480 return kill (pid, sig);
481 #endif
482 return -1;
483 }
484 #if __WINDOWS__
r_sandbox_opendir(const char * path,WIN32_FIND_DATAW * entry)485 R_API HANDLE r_sandbox_opendir (const char *path, WIN32_FIND_DATAW *entry) {
486 r_return_val_if_fail (path, NULL);
487 wchar_t dir[MAX_PATH];
488 wchar_t *wcpath = 0;
489 if (r_sandbox_enable (0)) {
490 if (path && !r_sandbox_check_path (path)) {
491 return NULL;
492 }
493 }
494 if (!(wcpath = r_utf8_to_utf16 (path))) {
495 return NULL;
496 }
497 swprintf (dir, MAX_PATH, L"%ls\\*.*", wcpath);
498 free (wcpath);
499 return FindFirstFileW (dir, entry);
500 }
501 #else
r_sandbox_opendir(const char * path)502 R_API DIR* r_sandbox_opendir (const char *path) {
503 r_return_val_if_fail (path, NULL);
504 if (r_sandbox_enable (0)) {
505 if (path && !r_sandbox_check_path (path)) {
506 return NULL;
507 }
508 }
509 return opendir (path);
510 }
511 #endif
r_sys_stop(void)512 R_API bool r_sys_stop (void) {
513 if (enabled) {
514 return false;
515 }
516 #if __UNIX__
517 return !r_sandbox_kill (0, SIGTSTP);
518 #else
519 return false;
520 #endif
521 }
522