/* radare - LGPL - Copyright 2014-2021 - pancake */ /* this helper api is here because it depends on r_util and r_socket */ /* we should find a better place for it. r_io? */ #include #include #include #include #include #include #include #include #include #include #if __APPLE__ && LIBC_HAVE_FORK #if !__POWERPC__ #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #endif #if __UNIX__ #include #include #ifndef __wasi__ #include #endif #include #if defined(__sun) #include #endif #if __linux__ && !__ANDROID__ #include #include #include #endif #if defined(__APPLE__) || defined(__NetBSD__) || defined(__OpenBSD__) #include #elif defined(__FreeBSD__) || defined(__DragonFly__) #include #include #endif #endif #ifdef _MSC_VER #include // to compile chdir in msvc windows #include // to compile execv in msvc windows #define pid_t int #endif #if HAVE_PTY static int (*dyn_openpty)(int *amaster, int *aslave, char *name, struct termios *termp, struct winsize *winp) = NULL; static int (*dyn_login_tty)(int fd) = NULL; static id_t (*dyn_forkpty)(int *amaster, char *name, struct termios *termp, struct winsize *winp) = NULL; static void dyn_init(void) { if (!dyn_openpty) { dyn_openpty = r_lib_dl_sym (NULL, "openpty"); } if (!dyn_login_tty) { dyn_login_tty = r_lib_dl_sym (NULL, "login_tty"); } if (!dyn_forkpty) { dyn_forkpty = r_lib_dl_sym (NULL, "forkpty"); } #if __UNIX__ // attempt to fall back on libutil if we failed to load anything if (!(dyn_openpty && dyn_login_tty && dyn_forkpty)) { void *libutil; if (!(libutil = r_lib_dl_open ("libutil." R_LIB_EXT))) { eprintf ("[ERROR] rarun2: Could not find PTY utils, failed to load %s\n", "libutil." R_LIB_EXT); return; } if (!dyn_openpty) { dyn_openpty = r_lib_dl_sym (libutil, "openpty"); } if (!dyn_login_tty) { dyn_login_tty = r_lib_dl_sym (libutil, "login_tty"); } if (!dyn_forkpty) { dyn_forkpty = r_lib_dl_sym (libutil, "forkpty"); } r_lib_dl_close (libutil); } #endif } #endif R_API RRunProfile *r_run_new(const char *str) { RRunProfile *p = R_NEW0 (RRunProfile); if (p) { r_run_reset (p); if (str) { r_run_parsefile (p, str); } } return p; } R_API void r_run_reset(RRunProfile *p) { r_return_if_fail (p); memset (p, 0, sizeof (RRunProfile)); p->_aslr = -1; } R_API bool r_run_parse(RRunProfile *pf, const char *profile) { r_return_val_if_fail (pf && profile, false); char *p, *o, *str = strdup (profile); if (!str) { return false; } r_str_replace_char (str, '\r',0); p = str; while (p) { if ((o = strchr (p, '\n'))) { *o++ = 0; } r_run_parseline (pf, p); p = o; } free (str); return true; } R_API void r_run_free(RRunProfile *r) { if (r) { free (r->_system); free (r->_program); free (r->_runlib); free (r->_runlib_fcn); free (r->_stdio); free (r->_stdin); free (r->_stdout); free (r->_stderr); free (r->_chgdir); free (r->_chroot); free (r->_libpath); free (r->_preload); free (r); } } #if __UNIX__ static void set_limit(int n, int a, ut64 b) { #ifndef __wasi__ if (n) { struct rlimit cl = {b, b}; setrlimit (RLIMIT_CORE, &cl); } else { struct rlimit cl = {0, 0}; setrlimit (a, &cl); } #endif } #endif static char *getstr(const char *src) { int len; char *ret = NULL; switch (*src) { case '\'': ret = strdup (src+1); if (ret) { len = strlen (ret); if (len > 0) { len--; if (ret[len] == '\'') { ret[len] = 0; return ret; } eprintf ("Missing \"\n"); } free (ret); } return NULL; case '"': ret = strdup (src + 1); if (ret) { len = strlen (ret); if (len > 0) { len--; if (ret[len] == '"') { ret[len] = 0; r_str_unescape (ret); return ret; } eprintf ("Missing \"\n"); } free (ret); } return NULL; case '@': { char *pat = strchr (src + 1, '@'); if (pat) { size_t len; long i, rep; *pat++ = 0; rep = strtol (src + 1, NULL, 10); len = strlen (pat); if (rep > 0) { char *buf = malloc (rep); if (buf) { for (i = 0; i < rep; i++) { buf[i] = pat[i % len]; } } return buf; } } // slurp file return r_file_slurp (src + 1, NULL); } case '`': { char *msg = strdup (src + 1); int msg_len = strlen (msg); if (msg_len > 0) { msg [msg_len - 1] = 0; char *ret = r_sys_cmd_str (msg, NULL, NULL); r_str_trim_tail (ret); free (msg); return ret; } free (msg); return strdup (""); } case '!': { char *a = r_sys_cmd_str (src + 1, NULL, NULL); r_str_trim_tail (a); return a; } case ':': if (src[1] == '!') { ret = r_sys_cmd_str (src + 1, NULL, NULL); r_str_trim_tail (ret); // why no head :? } else { ret = strdup (src); } len = r_hex_str2bin (src + 1, (ut8*)ret); if (len > 0) { ret[len] = 0; return ret; } eprintf ("Invalid hexpair string\n"); free (ret); return NULL; } r_str_unescape ((ret = strdup (src))); return ret; } static int parseBool(const char *e) { return (strcmp (e, "yes")? (strcmp (e, "on")? (strcmp (e, "true")? (strcmp (e, "1")? 0: 1): 1): 1): 1); } // TODO: move into r_util? r_run_... ? with the rest of funcs? static void setASLR(RRunProfile *r, int enabled) { #if __linux__ r_sys_aslr (enabled); #if HAVE_DECL_ADDR_NO_RANDOMIZE && !__ANDROID__ if (personality (ADDR_NO_RANDOMIZE) == -1) { #endif r_sys_aslr (0); #if HAVE_DECL_ADDR_NO_RANDOMIZE && !__ANDROID__ } #endif #elif __APPLE__ // TOO OLD setenv ("DYLD_NO_PIE", "1", 1); // disable this because its const char *argv0 = r->_system ? r->_system : r->_program ? r->_program : r->_args[0] ? r->_args[0] : "/path/to/exec"; eprintf ("To disable aslr patch mach0.hdr.flags with:\n" "r2 -qwnc 'wx 000000 @ 0x18' %s\n", argv0); // f MH_PIE=0x00200000; wB-MH_PIE @ 24\n"); // for osxver>=10.7 // "unset the MH_PIE bit in an already linked executable" with --no-pie flag of the script // the right way is to disable the aslr bit in the spawn call #elif __FreeBSD__ || __NetBSD__ || __DragonFly__ r_sys_aslr (enabled); #else // not supported for this platform #endif } #if __APPLE__ && !__POWERPC__ #else #if HAVE_PTY static void restore_saved_fd(int saved, bool restore, int fd) { if (saved == -1) { return; } if (restore) { dup2 (saved, fd); } close (saved); } #endif static int handle_redirection_proc(const char *cmd, bool in, bool out, bool err) { #if HAVE_PTY if (!dyn_forkpty) { // No forkpty api found, maybe we should fallback to just fork without any pty allocated return -1; } // use PTY to redirect I/O because pipes can be problematic in // case of interactive programs. int saved_stdin = dup (STDIN_FILENO); if (saved_stdin == -1) { return -1; } int saved_stdout = dup (STDOUT_FILENO); if (saved_stdout == -1) { close (saved_stdin); return -1; } int fdm, pid = dyn_forkpty (&fdm, NULL, NULL, NULL); if (pid == -1) { close (saved_stdin); close (saved_stdout); return -1; } const char *tn = ttyname (fdm); if (!tn) { close (saved_stdin); close (saved_stdout); return -1; } int fds = open (tn, O_RDWR); if (fds == -1) { close (saved_stdin); close (saved_stdout); return -1; } if (pid == 0) { close (fdm); // child process if (in) { dup2 (fds, STDIN_FILENO); } if (out) { dup2 (fds, STDOUT_FILENO); } // child - program to run // necessary because otherwise you can read the same thing you // wrote on fdm. struct termios t; tcgetattr (fds, &t); cfmakeraw (&t); tcsetattr (fds, TCSANOW, &t); int code = r_sys_cmd (cmd); restore_saved_fd (saved_stdin, in, STDIN_FILENO); restore_saved_fd (saved_stdout, out, STDOUT_FILENO); exit (code); } else { close (fds); if (in) { dup2 (fdm, STDIN_FILENO); } if (out) { dup2 (fdm, STDOUT_FILENO); } // parent process int status; waitpid (pid, &status, 0); } // parent close (saved_stdin); close (saved_stdout); return 0; #else #ifdef _MSC_VER #pragma message ("TODO: handle_redirection_proc: Not implemented for this platform") #else #warning handle_redirection_proc : unimplemented for this platform #endif return -1; #endif } #endif static int handle_redirection(const char *cmd, bool in, bool out, bool err) { #if __APPLE__ && !__POWERPC__ //XXX handle this in other layer since things changes a little bit //this seems like a really good place to refactor stuff return 0; #else if (!cmd || !*cmd) { return 0; } if (cmd[0] == '"') { #ifdef __wasi__ eprintf ("[ERROR] rarun2: Cannot create pipe\n"); #elif __UNIX__ if (in) { int pipes[2]; if (pipe (pipes) != -1) { size_t cmdl = strlen (cmd)-2; if (write (pipes[1], cmd + 1, cmdl) != cmdl) { eprintf ("[ERROR] rarun2: Cannot write to the pipe\n"); close (0); return 1; } if (write (pipes[1], "\n", 1) != 1) { eprintf ("[ERROR] rarun2: Cannot write to the pipe\n"); close (0); return 1; } close (0); dup2 (pipes[0], 0); } else { eprintf ("[ERROR] rarun2: Cannot create pipe\n"); } } #else #ifdef _MSC_VER #pragma message ("string redirection handle not yet done") #else #warning quoted string redirection handle not yet done #endif #endif } else if (cmd[0] == '!') { // redirection to a process return handle_redirection_proc (cmd + 1, in, out, err); } else { // redirection to a file int f, flag = 0, mode = 0; flag |= in ? O_RDONLY : 0; flag |= out ? O_WRONLY | O_CREAT : 0; flag |= err ? O_WRONLY | O_CREAT : 0; #ifdef __WINDOWS__ mode = _S_IREAD | _S_IWRITE; #else mode = S_IRUSR | S_IWUSR; #endif f = open (cmd, flag, mode); if (f < 0) { eprintf ("[ERROR] rarun2: Cannot open: %s\n", cmd); return 1; } #ifndef __wasi__ #define DUP(x) { close(x); dup2(f,x); } if (in) { DUP(0); } if (out) { DUP(1); } if (err) { DUP(2); } #endif close (f); } return 0; #endif } R_API bool r_run_parsefile(RRunProfile *p, const char *b) { r_return_val_if_fail (p && b, false); char *s = r_file_slurp (b, NULL); if (s) { bool ret = r_run_parse (p, s); free (s); return ret; } return 0; } R_API bool r_run_parseline(RRunProfile *p, const char *b) { int must_free = false; char *e = strchr (b, '='); if (!e || *b == '#') { return 0; } *e++ = 0; if (*e == '$') { must_free = true; e = r_sys_getenv (e); } if (!e) { return 0; } if (!strcmp (b, "program")) { p->_args[0] = p->_program = strdup (e); } else if (!strcmp (b, "daemon")) { p->_daemon = true; } else if (!strcmp (b, "system")) { p->_system = strdup (e); } else if (!strcmp (b, "runlib")) { p->_runlib = strdup (e); } else if (!strcmp (b, "runlib.fcn")) { p->_runlib_fcn = strdup (e); } else if (!strcmp (b, "aslr")) { p->_aslr = parseBool (e); } else if (!strcmp (b, "pid")) { p->_pid = atoi (e); } else if (!strcmp (b, "pidfile")) { p->_pidfile = strdup (e); } else if (!strcmp (b, "connect")) { p->_connect = strdup (e); } else if (!strcmp (b, "listen")) { p->_listen = strdup (e); } else if (!strcmp (b, "pty")) { p->_pty = parseBool (e); } else if (!strcmp (b, "stdio")) { if (e[0] == '!') { p->_stdio = strdup (e); } else { p->_stdout = strdup (e); p->_stderr = strdup (e); p->_stdin = strdup (e); } } else if (!strcmp (b, "stdout")) { p->_stdout = strdup (e); } else if (!strcmp (b, "stdin")) { p->_stdin = strdup (e); } else if (!strcmp (b, "stderr")) { p->_stderr = strdup (e); } else if (!strcmp (b, "input")) { p->_input = strdup (e); } else if (!strcmp (b, "chdir")) { p->_chgdir = strdup (e); } else if (!strcmp (b, "core")) { p->_docore = parseBool (e); } else if (!strcmp (b, "fork")) { p->_dofork = parseBool (e); } else if (!strcmp (b, "sleep")) { p->_r2sleep = atoi (e); } else if (!strcmp (b, "maxstack")) { p->_maxstack = atoi (e); } else if (!strcmp (b, "maxproc")) { p->_maxproc = atoi (e); } else if (!strcmp (b, "maxfd")) { p->_maxfd = atoi (e); } else if (!strcmp (b, "bits")) { p->_bits = atoi (e); } else if (!strcmp (b, "chroot")) { p->_chroot = strdup (e); } else if (!strcmp (b, "libpath")) { p->_libpath = strdup (e); } else if (!strcmp (b, "preload")) { p->_preload = strdup (e); } else if (!strcmp (b, "r2preload")) { p->_r2preload = parseBool (e); } else if (!strcmp (b, "r2preweb")) { r_sys_setenv ("RARUN2_WEB", "yes"); } else if (!strcmp (b, "setuid")) { p->_setuid = strdup (e); } else if (!strcmp (b, "seteuid")) { p->_seteuid = strdup (e); } else if (!strcmp (b, "setgid")) { p->_setgid = strdup (e); } else if (!strcmp (b, "setegid")) { p->_setegid = strdup (e); } else if (!strcmp (b, "nice")) { p->_nice = atoi (e); } else if (!strcmp (b, "timeout")) { p->_timeout = atoi (e); } else if (!strcmp (b, "timeoutsig")) { p->_timeout_sig = r_signal_from_string (e); } else if (!memcmp (b, "arg", 3)) { int n = atoi (b + 3); if (n >= 0 && n < R_RUN_PROFILE_NARGS) { p->_args[n] = getstr (e); p->_argc++; } else { eprintf ("Out of bounds args index: %d\n", n); } } else if (!strcmp (b, "envfile")) { char *p, buf[1024]; size_t len; FILE *fd = r_sandbox_fopen (e, "r"); if (!fd) { eprintf ("Cannot open '%s'\n", e); if (must_free == true) { free (e); } return false; } for (;;) { if (!fgets (buf, sizeof (buf), fd)) { break; } if (feof (fd)) { break; } p = strchr (buf, '='); if (p) { *p++ = 0; len = strlen (p); if (len > 0 && p[len - 1] == '\n') { p[len - 1] = 0; } if (len > 1 && p[len - 2] == '\r') { p[len - 2] = 0; } r_sys_setenv (buf, p); } } fclose (fd); } else if (!strcmp (b, "unsetenv")) { r_sys_setenv (e, NULL); } else if (!strcmp (b, "setenv")) { char *V, *v = strchr (e, '='); if (v) { *v++ = 0; V = getstr (v); r_sys_setenv (e, V); free (V); } } else if (!strcmp(b, "clearenv")) { r_sys_clearenv (); } if (must_free == true) { free (e); } return true; } R_API const char *r_run_help(void) { return "program=/bin/ls\n" "arg1=/bin\n" "# arg2=hello\n" "# arg3=\"hello\\nworld\"\n" "# arg4=:048490184058104849\n" "# arg5=:!ragg2 -p n50 -d 10:0x8048123\n" "# arg6=@arg.txt\n" "# arg7=@300@ABCD # 300 chars filled with ABCD pattern\n" "# system=r2 -\n" "# daemon=false\n" "# aslr=no\n" "setenv=FOO=BAR\n" "# unsetenv=FOO\n" "# clearenv=true\n" "# envfile=environ.txt\n" "timeout=3\n" "# timeoutsig=SIGTERM # or 15\n" "# connect=localhost:8080\n" "# listen=8080\n" "# pty=false\n" "# fork=true\n" "# bits=32\n" "# pid=0\n" "# pidfile=/tmp/foo.pid\n" "# #sleep=0\n" "# #maxfd=0\n" "# #execve=false\n" "# #maxproc=0\n" "# #maxstack=0\n" "# #core=false\n" "# #stdio=blah.txt\n" "# #stderr=foo.txt\n" "# stdout=foo.txt\n" "# stdin=input.txt # or !program to redirect input from another program\n" "# input=input.txt\n" "# chdir=/\n" "# chroot=/mnt/chroot\n" "# libpath=$PWD:/tmp/lib\n" "# r2preload=yes\n" "# preload=/lib/libfoo.so\n" "# setuid=2000\n" "# seteuid=2000\n" "# setgid=2001\n" "# setegid=2001\n" "# nice=5\n"; } #if HAVE_PTY static int fd_forward(int in_fd, int out_fd, char **buff) { int size = 0; if (ioctl (in_fd, FIONREAD, &size) == -1) { perror ("ioctl"); return -1; } if (!size) { // child process exited or socket is closed return -1; } char *new_buff = realloc (*buff, size); if (!new_buff) { eprintf ("Failed to allocate buffer for redirection"); return -1; } *buff = new_buff; if (read (in_fd, *buff, size) != size) { perror ("read"); return -1; } if (write (out_fd, *buff, size) != size) { perror ("write"); return -1; } return 0; } #endif static int redirect_socket_to_stdio(RSocket *sock) { close (0); close (1); close (2); #ifndef __wasi__ dup2 (sock->fd, 0); dup2 (sock->fd, 1); dup2 (sock->fd, 2); #endif return 0; } #if __WINDOWS__ static RThreadFunctionRet exit_process(RThread *th) { // eprintf ("\nrarun2: Interrupted by timeout\n"); exit (0); } #endif static int redirect_socket_to_pty(RSocket *sock) { #if HAVE_PTY // directly duplicating the fds using dup2() creates problems // in case of interactive applications int fdm = -1, fds = -1; if (dyn_openpty && dyn_openpty (&fdm, &fds, NULL, NULL, NULL) == -1) { perror ("opening pty"); return -1; } pid_t child_pid = r_sys_fork (); if (child_pid == -1) { eprintf ("cannot fork\n"); if (fdm != -1) { close (fdm); } if (fds != -1) { close (fds); } return -1; } if (child_pid == 0) { // child process close (fds); char *buff = NULL; int sockfd = sock->fd; int max_fd = fdm > sockfd ? fdm : sockfd; while (true) { fd_set readfds; FD_ZERO (&readfds); FD_SET (fdm, &readfds); FD_SET (sockfd, &readfds); if (select (max_fd + 1, &readfds, NULL, NULL, NULL) == -1) { perror ("select error"); break; } if (FD_ISSET (fdm, &readfds)) { if (fd_forward (fdm, sockfd, &buff) != 0) { break; } } if (FD_ISSET (sockfd, &readfds)) { if (fd_forward (sockfd, fdm, &buff) != 0) { break; } } } free (buff); if (fdm != -1) { close (fdm); fdm = -1; } r_socket_free (sock); exit (0); } // parent r_socket_close_fd (sock); if (dyn_login_tty) { dyn_login_tty (fds); } if (fdm != -1) { close (fdm); } // disable the echo on slave stdin struct termios t; tcgetattr (0, &t); cfmakeraw (&t); tcsetattr (0, TCSANOW, &t); return 0; #else // Fallback to socket to I/O redirection return redirect_socket_to_stdio (sock); #endif } R_API int r_run_config_env(RRunProfile *p) { int ret; #if HAVE_PTY dyn_init (); #endif if (!p->_program && !p->_system && !p->_runlib) { eprintf ("No program, system or runlib rule defined\n"); return 1; } // when IO is redirected to a process, handle them together if (handle_redirection (p->_stdio, true, true, false) != 0) { return 1; } if (handle_redirection (p->_stdin, true, false, false) != 0) { return 1; } if (handle_redirection (p->_stdout, false, true, false) != 0) { return 1; } if (handle_redirection (p->_stderr, false, false, true) != 0) { return 1; } if (p->_aslr != -1) { setASLR (p, p->_aslr); } #if __UNIX__ && !__wasi__ && !defined(serenity) set_limit (p->_docore, RLIMIT_CORE, RLIM_INFINITY); if (p->_maxfd) { set_limit (p->_maxfd, RLIMIT_NOFILE, p->_maxfd); } #ifdef RLIMIT_NPROC if (p->_maxproc) { set_limit (p->_maxproc, RLIMIT_NPROC, p->_maxproc); } #endif if (p->_maxstack) { set_limit (p->_maxstack, RLIMIT_STACK, p->_maxstack); } #else if (p->_docore || p->_maxfd || p->_maxproc || p->_maxstack) eprintf ("Warning: setrlimits not supported for this platform\n"); #endif if (p->_connect) { char *q = strchr (p->_connect, ':'); if (q) { RSocket *fd = r_socket_new (0); *q = 0; if (!r_socket_connect_tcp (fd, p->_connect, q+1, 30)) { eprintf ("Cannot connect\n"); r_socket_free (fd); return 1; } if (p->_pty) { if (redirect_socket_to_pty (fd) != 0) { eprintf ("socket redirection failed\n"); r_socket_free (fd); return 1; } } else { redirect_socket_to_stdio (fd); } } else { eprintf ("Invalid format for connect. missing ':'\n"); return 1; } } if (p->_listen) { RSocket *child, *fd = r_socket_new (0); bool is_child = false; if (!r_socket_listen (fd, p->_listen, NULL)) { eprintf ("rarun2: cannot listen\n"); r_socket_free (fd); return 1; } while (true) { child = r_socket_accept (fd); if (child) { is_child = true; if (p->_dofork && !p->_dodebug) { pid_t child_pid = r_sys_fork (); if (child_pid == -1) { eprintf("rarun2: cannot fork\n"); r_socket_free (child); r_socket_free (fd); return 1; } else if (child_pid != 0){ // parent code is_child = false; } } if (is_child) { r_socket_close_fd (fd); eprintf ("connected\n"); if (p->_pty) { if (redirect_socket_to_pty (child) != 0) { eprintf ("socket redirection failed\n"); r_socket_free (child); r_socket_free (fd); return 1; } } else { redirect_socket_to_stdio (child); } break; } else { r_socket_close_fd (child); } } } if (!is_child) { r_socket_free (child); } r_socket_free (fd); } if (p->_r2sleep != 0) { r_sys_sleep (p->_r2sleep); } #if __UNIX__ if (p->_chroot) { if (chdir (p->_chroot) == -1) { eprintf ("Cannot chdir to chroot in %s\n", p->_chroot); return 1; } else { if (chroot (".") == -1) { eprintf ("Cannot chroot to %s\n", p->_chroot); return 1; } else { // Silenting pedantic meson flags... if (chdir ("/") == -1) { eprintf ("Cannot chdir to /\n"); return 1; } if (p->_chgdir) { if (chdir (p->_chgdir) == -1) { eprintf ("Cannot chdir after chroot to %s\n", p->_chgdir); return 1; } } } } } else if (p->_chgdir) { if (chdir (p->_chgdir) == -1) { eprintf ("Cannot chdir after chroot to %s\n", p->_chgdir); return 1; } } #else if (p->_chgdir) { ret = chdir (p->_chgdir); if (ret < 0) { return 1; } } if (p->_chroot) { ret = chdir (p->_chroot); if (ret < 0) { return 1; } } #endif #if __UNIX__ if (p->_setuid) { ret = setgroups (0, NULL); if (ret < 0) { return 1; } ret = setuid (atoi (p->_setuid)); if (ret < 0) { return 1; } } if (p->_seteuid) { ret = seteuid (atoi (p->_seteuid)); if (ret < 0) { return 1; } } if (p->_setgid) { ret = setgid (atoi (p->_setgid)); if (ret < 0) { return 1; } } if (p->_input) { char *inp; int f2[2]; if (pipe (f2) != -1) { close (0); #if !__wasi__ dup2 (f2[0], 0); #endif } else { eprintf ("[ERROR] rarun2: Cannot create pipe\n"); return 1; } inp = getstr (p->_input); if (inp) { size_t inpl = strlen (inp); if (write (f2[1], inp, inpl) != inpl) { eprintf ("[ERROR] rarun2: Cannot write to the pipe\n"); } close (f2[1]); free (inp); } else { eprintf ("Invalid input\n"); } } #endif if (p->_r2preload) { if (p->_preload) { eprintf ("Warning: Only one library can be opened at a time\n"); } #ifdef __WINDOWS__ p->_preload = r_str_r2_prefix (R_JOIN_2_PATHS (R2_LIBDIR, "libr2."R_LIB_EXT)); #else p->_preload = strdup (R2_LIBDIR"/libr2."R_LIB_EXT); #endif } if (p->_libpath) { #if __WINDOWS__ eprintf ("rarun2: libpath unsupported for this platform\n"); #elif __HAIKU__ char *orig = r_sys_getenv ("LIBRARY_PATH"); char *newlib = r_str_newf ("%s:%s", p->_libpath, orig); r_sys_setenv ("LIBRARY_PATH", newlib); free (newlib); free (orig); #elif __APPLE__ r_sys_setenv ("DYLD_LIBRARY_PATH", p->_libpath); #else r_sys_setenv ("LD_LIBRARY_PATH", p->_libpath); #endif } if (p->_preload) { #if __APPLE__ // 10.6 #ifndef __MAC_10_7 r_sys_setenv ("DYLD_PRELOAD", p->_preload); #endif r_sys_setenv ("DYLD_INSERT_LIBRARIES", p->_preload); // 10.8 r_sys_setenv ("DYLD_FORCE_FLAT_NAMESPACE", "1"); #else r_sys_setenv ("LD_PRELOAD", p->_preload); #endif } if (p->_timeout) { #if __UNIX__ int mypid = r_sys_getpid (); if (!r_sys_fork ()) { int use_signal = p->_timeout_sig; if (use_signal < 1) { use_signal = SIGKILL; } sleep (p->_timeout); if (!kill (mypid, 0)) { // eprintf ("\nrarun2: Interrupted by timeout\n"); } kill (mypid, use_signal); exit (0); } #else if (p->_timeout_sig < 1 || p->_timeout_sig == 9) { r_th_new (exit_process, NULL, p->_timeout); } else { eprintf ("timeout with signal not supported for this platform\n"); } #endif } return 0; } // NOTE: return value is like in unix return code (0 = ok, 1 = not ok) R_API int r_run_start(RRunProfile *p) { #if LIBC_HAVE_FORK if (p->_execve) { exit (execv (p->_program, (char* const*)p->_args)); } #endif #if __APPLE__ && !__POWERPC__ && LIBC_HAVE_FORK posix_spawnattr_t attr = {0}; pid_t pid = -1; int ret; posix_spawnattr_init (&attr); if (p->_args[0]) { char **envp = r_sys_get_environ(); ut32 spflags = 0; //POSIX_SPAWN_START_SUSPENDED; spflags |= POSIX_SPAWN_SETEXEC; if (p->_aslr == 0) { #define _POSIX_SPAWN_DISABLE_ASLR 0x0100 spflags |= _POSIX_SPAWN_DISABLE_ASLR; } (void)posix_spawnattr_setflags (&attr, spflags); if (p->_bits) { size_t copied = 1; cpu_type_t cpu; #if __i386__ || __x86_64__ cpu = CPU_TYPE_I386; if (p->_bits == 64) { cpu |= CPU_ARCH_ABI64; } #else cpu = CPU_TYPE_ANY; #endif posix_spawnattr_setbinpref_np ( &attr, 1, &cpu, &copied); } ret = posix_spawnp (&pid, p->_args[0], NULL, &attr, p->_args, envp); switch (ret) { case 0: break; case 22: eprintf ("posix_spawnp: Invalid argument\n"); break; case 86: eprintf ("posix_spawnp: Unsupported architecture\n"); break; default: eprintf ("posix_spawnp: unknown error %d\n", ret); perror ("posix_spawnp"); break; } exit (ret); } #endif if (p->_system) { if (p->_pid) { eprintf ("PID: Cannot determine pid with 'system' directive. Use 'program'.\n"); } if (p->_daemon) { #if __WINDOWS__ // eprintf ("PID: Cannot determine pid with 'system' directive. Use 'program'.\n"); #else pid_t child = r_sys_fork (); if (child == -1) { perror ("fork"); exit (1); } if (child) { if (p->_pidfile) { char pidstr[32]; snprintf (pidstr, sizeof (pidstr), "%d\n", child); r_file_dump (p->_pidfile, (const ut8*)pidstr, strlen (pidstr), 0); } exit (0); } setsid (); if (p->_timeout) { #if __UNIX__ int mypid = r_sys_getpid (); if (!r_sys_fork ()) { int use_signal = p->_timeout_sig; if (use_signal < 1) { use_signal = SIGKILL; } sleep (p->_timeout); if (!kill (mypid, 0)) { // eprintf ("\nrarun2: Interrupted by timeout\n"); } kill (mypid, use_signal); exit (0); } #else eprintf ("timeout not supported for this platform\n"); #endif } #endif #if __UNIX__ close (0); close (1); char *bin_sh = r_file_binsh (); if (bin_sh) { exit (execl (bin_sh, bin_sh, "-c", p->_system, NULL)); } else { exit (r_sys_cmd (p->_system)); } free (bin_sh); #else exit (r_sys_cmd (p->_system)); #endif } else { if (p->_pidfile) { eprintf ("Warning: pidfile doesnt work with 'system'.\n"); } exit (r_sys_cmd (p->_system)); } } if (p->_program) { if (!r_file_exists (p->_program)) { char *progpath = r_file_path (p->_program); if (progpath && *progpath) { free (p->_program); p->_program = progpath; } else { free (progpath); eprintf ("rarun2: %s: file not found\n", p->_program); return 1; } } #if __UNIX__ // XXX HACK close all non-tty fds { int i; for (i = 3; i < 1024; i++) { close (i); } } // TODO: use posix_spawn if (p->_setgid) { int ret = setgid (atoi (p->_setgid)); if (ret < 0) { return 1; } } if (p->_pid) { eprintf ("PID: %d\n", r_sys_getpid ()); } if (p->_pidfile) { char pidstr[32]; snprintf (pidstr, sizeof (pidstr), "%d\n", r_sys_getpid ()); r_file_dump (p->_pidfile, (const ut8*)pidstr, strlen (pidstr), 0); } #endif if (p->_nice) { #if __UNIX__ && !defined(__HAIKU__) && !defined(__serenity__) if (nice (p->_nice) == -1) { return 1; } #else eprintf ("nice not supported for this platform\n"); #endif } if (p->_daemon) { #if __WINDOWS__ eprintf ("PID: Cannot determine pid with 'system' directive. Use 'program'.\n"); #else pid_t child = r_sys_fork (); if (child == -1) { perror ("fork"); exit (1); } if (child) { if (p->_pidfile) { char pidstr[32]; snprintf (pidstr, sizeof (pidstr), "%d\n", child); r_file_dump (p->_pidfile, (const ut8*)pidstr, strlen (pidstr), 0); exit (0); } } setsid (); #if !LIBC_HAVE_FORK exit (execv (p->_program, (char* const*)p->_args)); #endif #endif } // TODO: must be HAVE_EXECVE #if LIBC_HAVE_FORK exit (execv (p->_program, (char* const*)p->_args)); #endif } if (p->_runlib) { if (!p->_runlib_fcn) { eprintf ("No function specified. Please set runlib.fcn\n"); return 1; } void *addr = r_lib_dl_open (p->_runlib); if (!addr) { eprintf ("Could not load the library '%s'\n", p->_runlib); return 1; } void (*fcn)(void) = r_lib_dl_sym (addr, p->_runlib_fcn); if (!fcn) { eprintf ("Could not find the function '%s'\n", p->_runlib_fcn); return 1; } switch (p->_argc) { case 0: fcn (); break; case 1: r_run_call1 (fcn, p->_args[1]); break; case 2: r_run_call2 (fcn, p->_args[1], p->_args[2]); break; case 3: r_run_call3 (fcn, p->_args[1], p->_args[2], p->_args[3]); break; case 4: r_run_call4 (fcn, p->_args[1], p->_args[2], p->_args[3], p->_args[4]); break; case 5: r_run_call5 (fcn, p->_args[1], p->_args[2], p->_args[3], p->_args[4], p->_args[5]); break; case 6: r_run_call6 (fcn, p->_args[1], p->_args[2], p->_args[3], p->_args[4], p->_args[5], p->_args[6]); break; case 7: r_run_call7 (fcn, p->_args[1], p->_args[2], p->_args[3], p->_args[4], p->_args[5], p->_args[6], p->_args[7]); break; case 8: r_run_call8 (fcn, p->_args[1], p->_args[2], p->_args[3], p->_args[4], p->_args[5], p->_args[6], p->_args[7], p->_args[8]); break; case 9: r_run_call9 (fcn, p->_args[1], p->_args[2], p->_args[3], p->_args[4], p->_args[5], p->_args[6], p->_args[7], p->_args[8], p->_args[9]); break; case 10: r_run_call10 (fcn, p->_args[1], p->_args[2], p->_args[3], p->_args[4], p->_args[5], p->_args[6], p->_args[7], p->_args[8], p->_args[9], p->_args[10]); break; default: eprintf ("Too many arguments.\n"); return 1; } r_lib_dl_close (addr); } return 0; } R_API char *r_run_get_environ_profile(char **env) { if (!env) { return NULL; } RStrBuf *sb = r_strbuf_new (NULL); while (*env) { char *k = strdup (*env); char *v = strchr (k, '='); if (v) { *v++ = 0; v = r_str_escape_latin1 (v, false, true, true); if (v) { r_strbuf_appendf (sb, "setenv=%s=\"%s\"\n", k, v); free (v); } } free (k); env++; } return r_strbuf_drain (sb); }