/* radare - LGPL - Copyright 2012-2017 - pancake */ #include #include #if _MSC_VER #include // to compile execl under msvc windows #include // to compile chdir under msvc windows #endif static bool enabled = false; static bool disabled = false; static bool inHomeWww(const char *path) { bool ret = false; char *homeWww = r_str_home (".config/radare2/www/"); if (homeWww) { if (!strncmp (path, homeWww, strlen (homeWww))) { ret = true; } free (homeWww); } return ret; } /** * This function verifies that the given path is allowed. Paths are allowed only if they don't * contain .. components (which would indicate directory traversal) and they are relative. * Paths pointing into the webroot are an exception: For reaching the webroot, .. and absolute * path are ok. */ R_API bool r_sandbox_check_path (const char *path) { size_t root_len; char *p; /* XXX: the sandbox can be bypassed if a directory is symlink */ if (!path) { return false; } root_len = strlen (R2_LIBDIR"/radare2"); if (!strncmp (path, R2_LIBDIR"/radare2", root_len)) { return true; } root_len = strlen (R2_DATDIR"/radare2"); if (!strncmp (path, R2_DATDIR"/radare2", root_len)) { return true; } if (inHomeWww (path)) { return true; } // Accessing stuff inside the webroot is ok even if we need .. or leading / for that root_len = strlen (R2_WWWROOT); if (R2_WWWROOT[0] && !strncmp (path, R2_WWWROOT, root_len) && ( R2_WWWROOT[root_len-1] == '/' || path[root_len] == '/' || path[root_len] == '\0')) { path += strlen (R2_WWWROOT); while (*path == '/') { path++; } } // ./ path is not allowed if (path[0]=='.' && path[1]=='/') { return false; } // Properly check for directrory traversal using "..". First, does it start with a .. part? if (path[0]=='.' && path[1]=='.' && (path[2]=='\0' || path[2]=='/')) return 0; // Or does it have .. in some other position? for (p = strstr (path, "/.."); p; p = strstr(p, "/..")) { if (p[3] == '\0' || p[3] == '/') { return false; } } // Absolute paths are forbidden. if (*path == '/') { return false; } #if __UNIX__ char ch; if (readlink (path, &ch, 1) != -1) { return false; } #endif return true; } R_API bool r_sandbox_disable (bool e) { if (e) { #if LIBC_HAVE_PLEDGE if (enabled) { eprintf ("sandbox mode couldn't be disabled when pledged\n"); return enabled; } #endif disabled = enabled; enabled = false; } else { enabled = disabled; } return enabled; } R_API bool r_sandbox_enable (bool e) { if (enabled) { if (!e) { // eprintf ("Cant disable sandbox\n"); } return true; } enabled = e; #if LIBC_HAVE_PLEDGE if (enabled && pledge ("stdio rpath tty prot_exec", NULL) == -1) { eprintf ("sandbox: pledge call failed\n"); return false; } #endif return enabled; } R_API int r_sandbox_system (const char *x, int n) { if (enabled) { eprintf ("sandbox: system call disabled\n"); return -1; } #if LIBC_HAVE_FORK #if LIBC_HAVE_SYSTEM if (n) { #if APPLE_SDK_IPHONEOS #include int (*__system)(const char *cmd) = dlsym (NULL, "system"); if (__system) { return __system (x); } return -1; #else return system (x); #endif } return execl ("/bin/sh", "sh", "-c", x, (const char*)NULL); #else #include if (n && !strchr (x, '|')) { char **argv, *cmd = strdup (x); int rc, pid, argc; char *isbg = strchr (cmd, '&'); // XXX this is hacky if (isbg) { *isbg = 0; } argv = r_str_argv (cmd, &argc); if (argv) { char *argv0 = r_file_path (argv[0]); if (!argv0) { eprintf ("Cannot find '%s'\n", argv[0]); return -1; } pid = 0; posix_spawn (&pid, argv0, NULL, NULL, argv, NULL); if (isbg) { // XXX. wait for children rc = 0; } else { rc = waitpid (pid, NULL, 0); } r_str_argv_free (argv); free (argv0); return rc; } eprintf ("Error parsing command arguments\n"); return -1; } int child = fork(); if (child == -1) return -1; if (child) { return waitpid (child, NULL, 0); } execl ("/bin/sh", "sh", "-c", x, (const char*)NULL); exit (1); #endif #endif return -1; } R_API bool r_sandbox_creat (const char *path, int mode) { if (enabled) { return false; #if 0 if (mode & O_CREAT) return -1; if (mode & O_RDWR) return -1; if (!r_sandbox_check_path (path)) return -1; #endif } int fd = open (path, O_CREAT | O_TRUNC | O_WRONLY, mode); if (fd != -1) { close (fd); return true; } return false; } static char *expand_home(const char *p) { if (*p == '~') { return r_str_home (p); } return strdup (p); } R_API int r_sandbox_lseek (int fd, ut64 addr, int whence) { if (enabled) { return -1; } return lseek (fd, (off_t)addr, whence); } R_API int r_sandbox_read (int fd, ut8* buf, int len) { return enabled? -1 : read (fd, buf, len); } R_API int r_sandbox_write (int fd, const ut8* buf, int len) { return enabled? -1 : write (fd, buf, len); } R_API int r_sandbox_close (int fd) { return enabled? -1 : close (fd); } /* perm <-> mode */ R_API int r_sandbox_open (const char *path, int mode, int perm) { if (!path) { return -1; } char *epath = expand_home (path); #if __WINDOWS__ mode |= O_BINARY; #endif if (enabled) { if ((mode & O_CREAT) || (mode & O_RDWR) || (!r_sandbox_check_path (epath))) { free (epath); return -1; } } int ret = open (epath, mode, perm); free (epath); return ret; } R_API FILE *r_sandbox_fopen (const char *path, const char *mode) { FILE *ret = NULL; char *epath = NULL; if (!path) { return NULL; } if (enabled) { if (strchr (mode, 'w') || strchr (mode, 'a') || strchr (mode, '+')) { return NULL; } epath = expand_home (path); if (!r_sandbox_check_path (epath)) { free (epath); return NULL; } } if (!epath) { epath = expand_home (path); } if ((strchr (mode, 'w') || r_file_is_regular (epath))) { ret = fopen (epath, mode); } free (epath); return ret; } R_API int r_sandbox_chdir (const char *path) { if (enabled) { // TODO: check path if (strstr (path, "../")) return -1; if (*path == '/') return -1; return -1; } return chdir (path); } R_API int r_sandbox_kill(int pid, int sig) { // XXX: fine-tune. maybe we want to enable kill for child? if (enabled) return -1; #if __UNIX__ return kill (pid, sig); #endif return -1; } #if __WINDOWS__ && !defined(__CYGWIN__) R_API HANDLE r_sandbox_opendir (const char *path, WIN32_FIND_DATAW *entry) { wchar_t dir[MAX_PATH]; wchar_t *wcpath = 0; if (!path) { return NULL; } if (r_sandbox_enable (0)) { if (path && !r_sandbox_check_path (path)) { return NULL; } } if (!(wcpath = r_utf8_to_utf16 (path))) { return NULL; } #if __MINGW32__ swprintf (dir, L"%ls\\*.*", wcpath); #else swprintf (dir, MAX_PATH, L"%ls\\*.*", wcpath); #endif free (wcpath); return FindFirstFileW (dir, entry); } #else R_API DIR* r_sandbox_opendir (const char *path) { if (!path) return NULL; if (r_sandbox_enable (0)) { if (path && !r_sandbox_check_path (path)) { return NULL; } } return opendir (path); } #endif R_API bool r_sys_stop () { if (enabled) { return false; } #if __UNIX__ return !r_sandbox_kill (0, SIGTSTP); #else return false; #endif }