int restore(pink_easy_process_t *current) { pid_t pid = pink_easy_process_get_pid(current); pink_bitness_t bit = pink_easy_process_get_bitness(current); proc_data_t *data = pink_easy_process_get_userdata(current); /* Restore system call number */ if (!pink_util_set_syscall(pid, bit, data->sno)) { if (errno == ESRCH) return PINK_EASY_CFLAG_DROP; warning("pink_util_set_syscall(%lu, %s, %s): errno:%d (%s)", (unsigned long)pid, pink_bitness_name(bit), pink_name_syscall(data->sno, bit), errno, strerror(errno)); } /* Return the saved return value */ if (!pink_util_set_return(pid, data->ret)) { if (errno == ESRCH) return PINK_EASY_CFLAG_DROP; warning("pink_util_set_return(%lu, %s, %s): errno:%d (%s)", (unsigned long)pid, pink_bitness_name(bit), pink_name_syscall(data->sno, bit), errno, strerror(errno)); } return 0; }
static void handle_sigtrap(struct child *son) { long scno, ret; const char *scname; /* We get this event twice, one at entering a * system call and one at exiting a system * call. */ if (son->insyscall) { if (!pink_util_get_return(son->pid, &ret)) err(1, "pink_util_get_return"); if (son->inexecve) { son->inexecve = false; if (ret == 0) { /* execve was successful */ /* Update bitness */ son->bitness = pink_bitness_get(son->pid); if (son->bitness == PINK_BITNESS_UNKNOWN) err(1, "pink_bitness_get"); printf(" = 0 (Updating the bitness of child %i to %s mode)\n", son->pid, pink_bitness_name(son->bitness)); son->printret = false; return; } } son->insyscall = false; if (!son->printret) { son->printret = true; return; } /* Exiting the system call, print the * return value. */ putchar(' '); print_ret(ret); putchar('\n'); } else { son->insyscall = true; /* Get the system call number and call * the appropriate decoder. */ if (!pink_util_get_syscall(son->pid, son->bitness, &scno)) err(1, "pink_util_get_syscall"); scname = pink_name_syscall(scno, son->bitness); assert(scname != NULL); if (!strcmp(scname, "execve")) son->inexecve = true; if (!strcmp(scname, "open")) decode_open(son->pid, son->bitness); else if (!strcmp(scname, "execve")) decode_execve(son->pid, son->bitness); else if (!strcmp(scname, "bind") || !strcmp(scname, "connect")) decode_socketcall(son->pid, son->bitness, scname); else printf("%s()", scname); } }
static void report(pink_easy_process_t *current, const char *fmt, va_list ap) { char *cmdline; pid_t pid = pink_easy_process_get_pid(current); pink_bitness_t bit = pink_easy_process_get_bitness(current); proc_data_t *data = pink_easy_process_get_userdata(current); warning("-- Access Violation! --"); warning("process id:%lu (%s name:\"%s\")", (unsigned long)pid, pink_bitness_name(bit), data->comm); warning("cwd: `%s'", data->cwd); if (!proc_cmdline(pid, 128, &cmdline)) { warning("cmdline: `%s'", cmdline); free(cmdline); } log_msg_va(1, fmt, ap); }
int sys_linkat(pink_easy_process_t *current, const char *name) { int r; long flags; pid_t pid = pink_easy_process_get_pid(current); pink_bitness_t bit = pink_easy_process_get_bitness(current); proc_data_t *data = pink_easy_process_get_userdata(current); sys_info_t info; if (data->config.sandbox_write == SANDBOX_OFF) return 0; /* Check for AT_SYMLINK_FOLLOW */ if (!pink_util_get_arg(pid, bit, 4, &flags)) { if (errno != ESRCH) { warning("pink_util_get_arg(%lu, \"%s\", 4): %d(%s)", (unsigned long)pid, pink_bitness_name(bit), errno, strerror(errno)); return panic(current); } return PINK_EASY_CFLAG_DROP; } memset(&info, 0, sizeof(sys_info_t)); info.at = true; info.resolv = !!(flags & AT_SYMLINK_FOLLOW); info.index = 1; info.whitelisting = data->config.sandbox_write == SANDBOX_DENY; r = box_check_path(current, name, &info); if (!r && !data->deny) { info.create = MAY_CREATE; info.index = 3; return box_check_path(current, name, &info); } return r; }
int deny(pink_easy_process_t *current) { pid_t pid = pink_easy_process_get_pid(current); pink_bitness_t bit = pink_easy_process_get_bitness(current); proc_data_t *data = pink_easy_process_get_userdata(current); data->deny = true; data->ret = errno2retval(); if (!pink_util_set_syscall(pid, bit, PINKTRACE_INVALID_SYSCALL)) { if (errno != ESRCH) { warning("pink_util_set_syscall(%d, \"%s\", 0xbadca11): %d(%s)", pid, pink_bitness_name(bit), errno, strerror(errno)); return panic(current); } return PINK_EASY_CFLAG_DROP; } return 0; }
int main(int argc, char **argv) { int sig, status, exit_code; pink_event_t event; struct child son; /* Parse arguments */ if (argc < 2) { fprintf(stderr, "Usage: %s program [argument...]\n", argv[0]); return EXIT_FAILURE; } /* Fork */ if ((son.pid = fork()) < 0) { perror("fork"); return EXIT_FAILURE; } else if (!son.pid) { /* child */ /* Set up for tracing */ if (!pink_trace_me()) { perror("pink_trace_me"); _exit(EXIT_FAILURE); } /* Stop and let the parent continue the execution. */ kill(getpid(), SIGSTOP); ++argv; execvp(argv[0], argv); perror("execvp"); _exit(-1); } else { waitpid(son.pid, &status, 0); event = pink_event_decide(status); assert(event == PINK_EVENT_STOP); /* Set up the tracing options. */ if (!pink_trace_setup(son.pid, PINK_TRACE_OPTION_SYSGOOD | PINK_TRACE_OPTION_EXEC)) { perror("pink_trace_setup"); pink_trace_kill(son.pid); return EXIT_FAILURE; } /* Figure out the bitness of the traced child. */ son.bitness = pink_bitness_get(son.pid); if (son.bitness == PINK_BITNESS_UNKNOWN) err(1, "pink_bitness_get"); printf("Child %i runs in %s mode\n", son.pid, pink_bitness_name(son.bitness)); son.dead = son.insyscall = false; sig = exit_code = 0; for (;;) { /* At this point the traced child is stopped and needs * to be resumed. */ if (!pink_trace_syscall(son.pid, sig)) { perror("pink_trace_syscall"); return (errno == ESRCH) ? 0 : 1; } sig = 0; /* Wait for the child */ if ((son.pid = waitpid(son.pid, &status, 0)) < 0) { perror("waitpid"); return (errno == ECHILD) ? 0 : 1; } /* Check the event. */ event = pink_event_decide(status); switch (event) { case PINK_EVENT_SYSCALL: handle_syscall(&son); break; break; case PINK_EVENT_EXEC: /* Update bitness */ son.bitness = pink_bitness_get(son.pid); if (son.bitness == PINK_BITNESS_UNKNOWN) err(1, "pink_bitness_get"); else printf(" (Updating the bitness of child %i to %s mode)\n", son.pid, pink_bitness_name(son.bitness)); break; case PINK_EVENT_GENUINE: case PINK_EVENT_UNKNOWN: /* Send the signal to the traced child as it * was a genuine signal. */ sig = WSTOPSIG(status); break; case PINK_EVENT_EXIT_GENUINE: exit_code = WEXITSTATUS(status); printf("Child %i exited normally with return code %d\n", son.pid, exit_code); son.dead = true; break; case PINK_EVENT_EXIT_SIGNAL: exit_code = 128 + WTERMSIG(status); printf("Child %i exited with signal %d\n", son.pid, WTERMSIG(status)); son.dead = true; break; default: /* Nothing */ ; } if (son.dead) break; } return exit_code; } }
int main(int argc, char **argv) { int sig, status, exit_code; struct child son; /* Parse arguments */ if (argc < 2) { fprintf(stderr, "Usage: %s program [argument...]\n", argv[0]); return EXIT_FAILURE; } /* Fork */ if ((son.pid = fork()) < 0) { perror("fork"); return EXIT_FAILURE; } else if (!son.pid) { /* child */ /* Set up for tracing */ if (!pink_trace_me()) { perror("pink_trace_me"); _exit(EXIT_FAILURE); } /* Stop and let the parent continue the execution. */ kill(getpid(), SIGSTOP); ++argv; execvp(argv[0], argv); perror("execvp"); _exit(-1); } else { waitpid(son.pid, &status, 0); assert(WIFSTOPPED(status)); assert(WSTOPSIG(status) == SIGSTOP); /* Figure out the bitness of the traced child. */ son.bitness = pink_bitness_get(son.pid); if (son.bitness == PINK_BITNESS_UNKNOWN) err(1, "pink_bitness_get"); printf("Child %i runs in %s mode\n", son.pid, pink_bitness_name(son.bitness)); son.inexecve = son.insyscall = false; son.printret = true; sig = exit_code = 0; for (;;) { /* At this point the traced child is stopped and needs * to be resumed. */ if (!pink_trace_syscall(son.pid, sig)) { perror("pink_trace_syscall"); return (errno == ESRCH) ? 0 : 1; } sig = 0; /* Wait for the child */ if ((son.pid = waitpid(son.pid, &status, 0)) < 0) { perror("waitpid"); return (errno == ECHILD) ? 0 : 1; } /* Check the event */ if (WIFSTOPPED(status)) { if (WSTOPSIG(status) == SIGTRAP) handle_sigtrap(&son); else { /* Child received a genuine signal. * Send it to the child. */ sig = WSTOPSIG(status); } } else if (WIFEXITED(status)) { exit_code = WEXITSTATUS(status); printf("Child %i exited normally with return code %d\n", son.pid, exit_code); break; } else if (WIFSIGNALED(status)) { exit_code = 128 + WTERMSIG(status); printf("Child %i exited with signal %d\n", son.pid, WTERMSIG(status)); break; } } return exit_code; } }
int sys_execve(pink_easy_process_t *current, const char *name) { int r; char *path, *abspath; pid_t pid = pink_easy_process_get_pid(current); pink_bitness_t bit = pink_easy_process_get_bitness(current); proc_data_t *data = pink_easy_process_get_userdata(current); path = abspath = NULL; r = path_decode(current, 0, &path); if (r < 0) return deny(current); else if (r /* > 0 */) return r; if ((r = box_resolve_path(path, data->cwd, pid, 0, 1, &abspath)) < 0) { info("resolving path:\"%s\" [%s() index:0] failed for process:%lu [%s name:\"%s\" cwd:\"%s\"] (errno:%d %s)", path, name, (unsigned long)pid, pink_bitness_name(bit), data->comm, data->cwd, -r, strerror(-r)); errno = -r; r = deny(current); if (pandora->config.violation_raise_fail) violation(current, "%s(\"%s\")", name, path); free(path); return r; } free(path); /* Handling exec.kill_if_match and exec.resume_if_match: * * Resolve and save the path argument in data->abspath. * When we receive a PINK_EVENT_EXEC which means execve() was * successful, we'll check for kill_if_match and resume_if_match lists * and kill or resume the process as necessary. */ data->abspath = abspath; switch (data->config.sandbox_exec) { case SANDBOX_OFF: return 0; case SANDBOX_DENY: if (box_match_path(abspath, &data->config.whitelist_exec, NULL)) return 0; break; case SANDBOX_ALLOW: if (!box_match_path(abspath, &data->config.blacklist_exec, NULL)) return 0; break; default: abort(); } errno = EACCES; r = deny(current); if (!box_match_path(abspath, &pandora->config.filter_exec, NULL)) violation(current, "%s(\"%s\")", name, abspath); free(abspath); data->abspath = NULL; return r; }