⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ptracesandbox.c

📁 文件传输协议linux 下vsftpd2.1.0.tar.gz
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Part of Very Secure FTPd * Licence: GPL v2 * Author: Chris Evans * ptracesandbox.c * * Generic routines to setup and run a process under a restrictive ptrace() * based sandbox. * Note that the style in this file is to not go via the helper functions in * sysutil.c, but instead hit the system APIs directly. This is because I may * very well release just this file to the public domain, and do not want * dependencies on other parts of vsftpd. */#include "ptracesandbox.h"#if defined(__linux__) && defined(__i386__)#include <sys/mman.h>#include <sys/prctl.h>#include <sys/ptrace.h>/* For AF_MAX (NPROTO is defined to this) */#include <sys/socket.h>#include <sys/types.h>#include <sys/user.h>#include <sys/wait.h>#include <err.h>#include <errno.h>#include <fcntl.h>#include <signal.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <syslog.h>#include <asm/unistd.h>/* For the socketcall() multiplex args. */#include <linux/net.h>#ifndef PTRACE_SETOPTIONS  #define PTRACE_SETOPTIONS 0x4200#endif#ifndef PTRACE_O_TRACESYSGOOD  #define PTRACE_O_TRACESYSGOOD 1#endif#ifndef PTRACE_O_TRACEFORK  #define PTRACE_O_TRACEFORK 2#endif#ifndef PTRACE_O_TRACEVFORK  #define PTRACE_O_TRACEVFORK 4#endif#ifndef PTRACE_O_TRACECLONE  #define PTRACE_O_TRACECLONE 8#endif#ifndef O_DIRECT  #define O_DIRECT 040000#endifstatic void sanitize_child();static int get_action(struct pt_sandbox* p_sandbox);static int validate_mmap2(struct pt_sandbox* p_sandbox, void* p_arg);static int validate_open_default(struct pt_sandbox* p_sandbox, void* p_arg);static int validate_open_readonly(struct pt_sandbox* p_sandbox, void* p_arg);static int validate_fcntl(struct pt_sandbox* p_sandbox, void* p_arg);static int validate_socketcall(struct pt_sandbox* p_sandbox, void* p_arg);static void install_socketcall(struct pt_sandbox* p_sandbox);#define MAX_SYSCALL 300struct pt_sandbox{  int read_event_fd;  int write_event_fd;  pid_t pid;  int is_allowed[MAX_SYSCALL];  ptrace_sandbox_validator_t validator[MAX_SYSCALL];  void* validator_arg[MAX_SYSCALL];  int is_exit;  struct user_regs_struct regs;  int is_socketcall_allowed[NPROTO];  ptrace_sandbox_validator_t socketcall_validator[NPROTO];  void* socketcall_validator_arg[NPROTO];};static int s_sigchld_fd = -1;voidhandle_sigchld(int sig){  int ret;  if (sig != SIGCHLD)  {    _exit(1);  }  if (s_sigchld_fd != -1)  {    do    {      static const char zero = '\0';      ret = write(s_sigchld_fd, &zero, sizeof(zero));    } while (ret == -1 && errno == EINTR);    if (ret != 1)    {      _exit(2);    }  }}struct pt_sandbox*ptrace_sandbox_alloc(){  int i;  struct sigaction sigact;  struct pt_sandbox* ret = malloc(sizeof(struct pt_sandbox));  if (ret == NULL)  {    return NULL;  }  ret->pid = -1;  ret->read_event_fd = -1;  ret->write_event_fd = -1;  ret->is_exit = 0;  memset(&ret->regs, '\0', sizeof(ret->regs));  for (i = 0; i < MAX_SYSCALL; ++i)  {    ret->is_allowed[i] = 0;    ret->validator[i] = 0;    ret->validator_arg[i] = 0;  }  for (i = 0; i < NPROTO; ++i)  {    ret->is_socketcall_allowed[i] = 0;    ret->socketcall_validator[i] = 0;    ret->socketcall_validator_arg[i] = 0;  }  memset((void*) &sigact, '\0', sizeof(sigact));  sigact.sa_handler = handle_sigchld;  if (sigaction(SIGCHLD, &sigact, NULL) != 0)  {    goto err_out;  }  return ret;err_out:  ptrace_sandbox_free(ret);  return NULL;}voidptrace_sandbox_free(struct pt_sandbox* p_sandbox){  if (p_sandbox->pid != -1)  {    warnx("bug: pid active in ptrace_sandbox_free");    /* We'll kill it for you so it doesn't escape the sandbox totally, but     * we won't reap the zombie.     * Killing it like this is a risk: if it's stopped in syscall entry,     * that syscall will execute before the pending kill takes effect.     * If that pending syscall were to be a fork(), there could be trouble.     */    (void) kill(p_sandbox->pid, SIGKILL);  }  if (p_sandbox->read_event_fd != -1)  {    s_sigchld_fd = -1;    close(p_sandbox->read_event_fd);    close(p_sandbox->write_event_fd);  }  free(p_sandbox);}voidptrace_sandbox_attach_point(){  long pt_ret;  int ret;  pid_t pid = getpid();  if (pid <= 1)  {    warnx("weird pid");    _exit(1);  }  /* You don't have to use PTRACE_TRACEME, but if you don't, a rogue SIGCONT   * might wake you up from the STOP below before the tracer has attached.   */  pt_ret = ptrace(PTRACE_TRACEME, 0, 0, 0);  if (pt_ret != 0)  {    warn("PTRACE_TRACEME failed");    _exit(2);  }  ret = kill(pid, SIGSTOP);  if (ret != 0)  {    warn("kill SIGSTOP failed");    _exit(3);  }}intptrace_sandbox_launch_process(struct pt_sandbox* p_sandbox,                              void (*p_func)(void*),                              void* p_arg){  long pt_ret;  pid_t ret;  int status;  if (p_sandbox->pid != -1)  {    warnx("bug: process already active");    return -1;  }  ret = fork();  if (ret < 0)  {    return -1;  }  else if (ret == 0)  {    /* Child context. */    sanitize_child();    (*p_func)(p_arg);    _exit(0);  }  /* Parent context */  p_sandbox->pid = ret;  do  {    ret = waitpid(p_sandbox->pid, &status, 0);  } while (ret == -1 && errno == EINTR);  if (ret == -1)  {    warn("waitpid failed");    goto kill_out;  }  else if (ret != p_sandbox->pid)  {    warnx("unknown pid %d", ret);    goto kill_out;  }  if (!WIFSTOPPED(status))  {    warnx("not stopped status %d\n", status);    goto kill_out;  }  if (WSTOPSIG(status) != SIGSTOP)  {    warnx("not SIGSTOP status %d\n", status);    goto kill_out;  }  /* The fork, etc. tracing options are worth a bit of explanation. We don't   * permit process launching syscalls at all as they are dangerous. But   * there's a small race if the untrusted process attempts a denied fork()   * and then takes a rouge SIGKILL before the supervisor gets a chance to   * clear the orig_eax register. In this case the syscall will still execute.   * (Policies may not include signal sending capabilities, thus mitigating this   * direct attack, however a rogue SIGKILL may come from a non-malicious   * source). Therefore, we'd rather any fork()ed process starts off traced,   * just in case this tiny race condition triggers.   */  pt_ret = ptrace(PTRACE_SETOPTIONS,                  p_sandbox->pid,                  0,                  PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK |                      PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE);  if (pt_ret != 0)  {    warn("PTRACE_SETOPTIONS failure");    goto kill_out;  }  return p_sandbox->pid;kill_out:  (void) kill(p_sandbox->pid, SIGKILL);  p_sandbox->pid = -1;  return -1;}intptrace_sandbox_continue_process(struct pt_sandbox* p_sandbox, int sig){  long pt_ret = ptrace(PTRACE_SYSCALL, p_sandbox->pid, 0, sig);  if (pt_ret != 0)  {    warn("PTRACE_SYSCALL failure");    if (errno == ESRCH)    {      return PTRACE_SANDBOX_ERR_DEAD;    }    return PTRACE_SANDBOX_ERR_PTRACE;  }  return 0;}intptrace_sandbox_get_event_fd(struct pt_sandbox* p_sandbox){  /* TODO: allocate pipe fds */  (void) p_sandbox;  return -1;}intptrace_sandbox_get_event(struct pt_sandbox* p_sandbox, int* status, int block){  pid_t pid;  int options = 0;  if (!block)  {    options = WNOHANG;  }  do  {    pid = waitpid(p_sandbox->pid, status, options);  } while (pid == -1 && errno == EINTR);  if (pid == -1)  {    warn("waitpid failure");    if (errno == ECHILD)    {      return PTRACE_SANDBOX_ERR_DEAD;    }    return PTRACE_SANDBOX_ERR_WAITPID;  }  return pid;}intptrace_sandbox_handle_event(struct pt_sandbox* p_sandbox, int status){  int sig;  int action;  if (WIFEXITED(status) || WIFSIGNALED(status))  {    p_sandbox->pid = -1;    return 1;  }  if (!WIFSTOPPED(status))  {    warnx("weird status: %d\n", status);    return PTRACE_SANDBOX_ERR_WAIT_STATUS;  }  sig = WSTOPSIG(status);  if (sig >= 0 && sig < 0x80)  {    /* It's a normal signal; deliver it right on. SIGSTOP / SIGCONT handling     * are buggy in the kernel and I'm not sure it's safe to pass either on,     * so the signal becomes a little more... robust :)     */    if (sig == SIGSTOP || sig == SIGCONT)    {      sig = SIGKILL;    }    return ptrace_sandbox_continue_process(p_sandbox, sig);  }  if (!(sig & 0x80))  {    warnx("weird status: %d\n", status);    return PTRACE_SANDBOX_ERR_WAIT_STATUS;  }  /* Syscall trap. */  if (p_sandbox->is_exit)  {    p_sandbox->is_exit = 0;  }  else  {    p_sandbox->is_exit = 1;    action = get_action(p_sandbox);    if (action != 0)    {      return action;    }  }  return ptrace_sandbox_continue_process(p_sandbox, 0);}intptrace_sandbox_run_processes(struct pt_sandbox* p_sandbox){  if (ptrace_sandbox_continue_process(p_sandbox, 0) != 0)  {    goto kill_out;  }  while (1)  {    int status;    int ret = ptrace_sandbox_get_event(p_sandbox, &status, 1);    if (ret <= 0)    {      goto kill_out;    }    ret = ptrace_sandbox_handle_event(p_sandbox, status);    if (ret < 0)    {      warnx("couldn't handle sandbox event");      goto kill_out;    }    if (ret == 1)    {      return 0;    }  }kill_out:  ptrace_sandbox_kill_processes(p_sandbox);  return -1;}voidptrace_sandbox_kill_processes(struct pt_sandbox* p_sandbox){  long pt_ret;  struct user_regs_struct regs;  pid_t pid = p_sandbox->pid;  if (pid == -1)  {    return;  }  p_sandbox->pid = -1;  pt_ret = ptrace(PTRACE_GETREGS, pid, 0, &regs);  if (pt_ret != 0)  {    warn("PTRACE_GETREGS failure");    /* This API is supposed to be called with the process stopped; but if it     * is still running, we can at least help a bit. See security related     * comment in ptrace_sandbox_free(), though.     */    (void) kill(pid, SIGKILL);    return;  }  /* Kind of nasty, but the only way of stopping a started syscall from   * executing is to rewrite the registers to execute a different syscall.   */  regs.orig_eax = __NR_exit_group;  regs.eip = 0xffffffff;  pt_ret = ptrace(PTRACE_SETREGS, pid, 0, &regs);  if (pt_ret != 0)  {    warn("PTRACE_SETREGS failure");    /* Deliberate fall-thru. */  }  pt_ret = ptrace(PTRACE_KILL, pid, 0, 0);  if (pt_ret != 0)  {    warn("PTRACE_KILL failure");    /* Deliberate fall-thru. */  }  /* Just to make ourselves clear. */  (void) kill(pid, SIGKILL);  /* So the GETREGS succeeded, so the process definitely _was_ there. We can   * safely wait for it to reap the zombie.   */  (void) waitpid(pid, NULL, 0);}intptrace_sandbox_get_arg(struct pt_sandbox* p_sandbox,                       int arg,                       unsigned long* p_out){  long ret = 0;  struct user_regs_struct* p_regs = &p_sandbox->regs;  if (p_regs->orig_eax == 0)  {    return PTRACE_SANDBOX_ERR_API_ABUSE_STOPIT;  }  if (arg < 0 || arg > 5)  {    return PTRACE_SANDBOX_ERR_API_ABUSE_STOPIT;  }  switch (arg)  {  case 0:    ret = p_regs->ebx;    break;  case 1:    ret = p_regs->ecx;    break;  case 2:    ret = p_regs->edx;    break;  case 3:    ret = p_regs->esi;    break;  case 4:    ret = p_regs->edi;    break;  case 5:    ret = p_regs->ebp;    break;  }  *p_out = ret;  return 0;}intptrace_sandbox_get_socketcall_arg(struct pt_sandbox* p_sandbox,                                  int arg,                                  unsigned long* p_out){  unsigned long ptr;  int ret;  struct user_regs_struct* p_regs = &p_sandbox->regs;  if (p_regs->orig_eax == 0)  {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -