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