github.com/criyle/go-sandbox@v0.10.3/runner/ptrace/handle_linux.go (about)

     1  package ptrace
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path"
     7  	"syscall"
     8  
     9  	"github.com/criyle/go-sandbox/pkg/seccomp/libseccomp"
    10  	"github.com/criyle/go-sandbox/ptracer"
    11  )
    12  
    13  type tracerHandler struct {
    14  	ShowDetails, Unsafe bool
    15  	Handler             Handler
    16  }
    17  
    18  func (h *tracerHandler) Debug(v ...interface{}) {
    19  	if h.ShowDetails {
    20  		fmt.Fprintln(os.Stderr, v...)
    21  	}
    22  }
    23  
    24  func (h *tracerHandler) getString(ctx *ptracer.Context, addr uint) string {
    25  	return absPath(ctx.Pid, ctx.GetString(uintptr(addr)))
    26  }
    27  
    28  func (h *tracerHandler) checkOpen(ctx *ptracer.Context, addr uint, flags uint) ptracer.TraceAction {
    29  	fn := h.getString(ctx, addr)
    30  	isReadOnly := (flags&syscall.O_ACCMODE == syscall.O_RDONLY) &&
    31  		(flags&syscall.O_CREAT == 0) &&
    32  		(flags&syscall.O_EXCL == 0) &&
    33  		(flags&syscall.O_TRUNC == 0)
    34  
    35  	h.Debug("open: ", fn, getFileMode(flags))
    36  	if isReadOnly {
    37  		return h.Handler.CheckRead(fn)
    38  	}
    39  	return h.Handler.CheckWrite(fn)
    40  }
    41  
    42  func (h *tracerHandler) checkRead(ctx *ptracer.Context, addr uint) ptracer.TraceAction {
    43  	fn := h.getString(ctx, addr)
    44  	h.Debug("check read: ", fn)
    45  	return h.Handler.CheckRead(fn)
    46  }
    47  
    48  func (h *tracerHandler) checkWrite(ctx *ptracer.Context, addr uint) ptracer.TraceAction {
    49  	fn := h.getString(ctx, addr)
    50  	h.Debug("check write: ", fn)
    51  	return h.Handler.CheckWrite(fn)
    52  }
    53  
    54  func (h *tracerHandler) checkStat(ctx *ptracer.Context, addr uint) ptracer.TraceAction {
    55  	fn := h.getString(ctx, addr)
    56  	h.Debug("check stat: ", fn)
    57  	return h.Handler.CheckStat(fn)
    58  }
    59  
    60  func (h *tracerHandler) Handle(ctx *ptracer.Context) ptracer.TraceAction {
    61  	syscallNo := ctx.SyscallNo()
    62  	syscallName, err := libseccomp.ToSyscallName(syscallNo)
    63  	h.Debug("syscall:", syscallNo, syscallName, err)
    64  	if err != nil {
    65  		h.Debug("invalid syscall no")
    66  		return ptracer.TraceKill
    67  	}
    68  
    69  	action := ptracer.TraceKill
    70  	switch syscallName {
    71  	case "open":
    72  		action = h.checkOpen(ctx, ctx.Arg0(), ctx.Arg1())
    73  	case "openat":
    74  		action = h.checkOpen(ctx, ctx.Arg1(), ctx.Arg2())
    75  
    76  	case "readlink":
    77  		action = h.checkRead(ctx, ctx.Arg0())
    78  	case "readlinkat":
    79  		action = h.checkRead(ctx, ctx.Arg1())
    80  
    81  	case "unlink":
    82  		action = h.checkWrite(ctx, ctx.Arg0())
    83  	case "unlinkat":
    84  		action = h.checkWrite(ctx, ctx.Arg1())
    85  
    86  	case "access":
    87  		action = h.checkStat(ctx, ctx.Arg0())
    88  	case "faccessat", "newfstatat":
    89  		action = h.checkStat(ctx, ctx.Arg1())
    90  
    91  	case "stat", "stat64":
    92  		action = h.checkStat(ctx, ctx.Arg0())
    93  	case "lstat", "lstat64":
    94  		action = h.checkStat(ctx, ctx.Arg0())
    95  
    96  	case "execve":
    97  		action = h.checkRead(ctx, ctx.Arg0())
    98  	case "execveat":
    99  		action = h.checkRead(ctx, ctx.Arg1())
   100  
   101  	case "chmod":
   102  		action = h.checkWrite(ctx, ctx.Arg0())
   103  	case "rename":
   104  		action = h.checkWrite(ctx, ctx.Arg0())
   105  
   106  	default:
   107  		action = h.Handler.CheckSyscall(syscallName)
   108  		if h.Unsafe && action == ptracer.TraceKill {
   109  			action = ptracer.TraceBan
   110  		}
   111  	}
   112  
   113  	switch action {
   114  	case ptracer.TraceAllow:
   115  		return ptracer.TraceAllow
   116  	case ptracer.TraceBan:
   117  		h.Debug("<soft ban syscall>")
   118  		return softBanSyscall(ctx)
   119  	default:
   120  		return ptracer.TraceKill
   121  	}
   122  }
   123  
   124  func softBanSyscall(ctx *ptracer.Context) ptracer.TraceAction {
   125  	ctx.SetReturnValue(-int(BanRet))
   126  	return ptracer.TraceBan
   127  }
   128  
   129  func getFileMode(flags uint) string {
   130  	switch flags & syscall.O_ACCMODE {
   131  	case syscall.O_RDONLY:
   132  		return "r "
   133  	case syscall.O_WRONLY:
   134  		return "w "
   135  	case syscall.O_RDWR:
   136  		return "wr"
   137  	default:
   138  		return "??"
   139  	}
   140  }
   141  
   142  // getProcCwd gets the process CWD
   143  func getProcCwd(pid int) string {
   144  	fileName := "/proc/self/cwd"
   145  	if pid > 0 {
   146  		fileName = fmt.Sprintf("/proc/%d/cwd", pid)
   147  	}
   148  	s, err := os.Readlink(fileName)
   149  	if err != nil {
   150  		return ""
   151  	}
   152  	return s
   153  }
   154  
   155  // absPath calculates the absolute path for a process
   156  // built-in function did the dirty works to resolve relative paths
   157  func absPath(pid int, p string) string {
   158  	// if relative path
   159  	if !path.IsAbs(p) {
   160  		return path.Join(getProcCwd(pid), p)
   161  	}
   162  	return path.Clean(p)
   163  }