mirror of
https://github.com/criyle/go-sandbox.git
synced 2025-09-26 23:19:11 +08:00
164 lines
3.8 KiB
Go
164 lines
3.8 KiB
Go
package ptrace
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"syscall"
|
|
|
|
"github.com/criyle/go-sandbox/pkg/seccomp/libseccomp"
|
|
"github.com/criyle/go-sandbox/ptracer"
|
|
)
|
|
|
|
type tracerHandler struct {
|
|
ShowDetails, Unsafe bool
|
|
Handler Handler
|
|
}
|
|
|
|
func (h *tracerHandler) Debug(v ...interface{}) {
|
|
if h.ShowDetails {
|
|
fmt.Fprintln(os.Stderr, v...)
|
|
}
|
|
}
|
|
|
|
func (h *tracerHandler) getString(ctx *ptracer.Context, addr uint) string {
|
|
return absPath(ctx.Pid, ctx.GetString(uintptr(addr)))
|
|
}
|
|
|
|
func (h *tracerHandler) checkOpen(ctx *ptracer.Context, addr uint, flags uint) ptracer.TraceAction {
|
|
fn := h.getString(ctx, addr)
|
|
isReadOnly := (flags&syscall.O_ACCMODE == syscall.O_RDONLY) &&
|
|
(flags&syscall.O_CREAT == 0) &&
|
|
(flags&syscall.O_EXCL == 0) &&
|
|
(flags&syscall.O_TRUNC == 0)
|
|
|
|
h.Debug("open: ", fn, getFileMode(flags))
|
|
if isReadOnly {
|
|
return h.Handler.CheckRead(fn)
|
|
}
|
|
return h.Handler.CheckWrite(fn)
|
|
}
|
|
|
|
func (h *tracerHandler) checkRead(ctx *ptracer.Context, addr uint) ptracer.TraceAction {
|
|
fn := h.getString(ctx, addr)
|
|
h.Debug("check read: ", fn)
|
|
return h.Handler.CheckRead(fn)
|
|
}
|
|
|
|
func (h *tracerHandler) checkWrite(ctx *ptracer.Context, addr uint) ptracer.TraceAction {
|
|
fn := h.getString(ctx, addr)
|
|
h.Debug("check write: ", fn)
|
|
return h.Handler.CheckWrite(fn)
|
|
}
|
|
|
|
func (h *tracerHandler) checkStat(ctx *ptracer.Context, addr uint) ptracer.TraceAction {
|
|
fn := h.getString(ctx, addr)
|
|
h.Debug("check stat: ", fn)
|
|
return h.Handler.CheckStat(fn)
|
|
}
|
|
|
|
func (h *tracerHandler) Handle(ctx *ptracer.Context) ptracer.TraceAction {
|
|
syscallNo := ctx.SyscallNo()
|
|
syscallName, err := libseccomp.ToSyscallName(syscallNo)
|
|
h.Debug("syscall:", syscallNo, syscallName, err)
|
|
if err != nil {
|
|
h.Debug("invalid syscall no")
|
|
return ptracer.TraceKill
|
|
}
|
|
|
|
action := ptracer.TraceKill
|
|
switch syscallName {
|
|
case "open":
|
|
action = h.checkOpen(ctx, ctx.Arg0(), ctx.Arg1())
|
|
case "openat":
|
|
action = h.checkOpen(ctx, ctx.Arg1(), ctx.Arg2())
|
|
|
|
case "readlink":
|
|
action = h.checkRead(ctx, ctx.Arg0())
|
|
case "readlinkat":
|
|
action = h.checkRead(ctx, ctx.Arg1())
|
|
|
|
case "unlink":
|
|
action = h.checkWrite(ctx, ctx.Arg0())
|
|
case "unlinkat":
|
|
action = h.checkWrite(ctx, ctx.Arg1())
|
|
|
|
case "access":
|
|
action = h.checkStat(ctx, ctx.Arg0())
|
|
case "faccessat", "newfstatat":
|
|
action = h.checkStat(ctx, ctx.Arg1())
|
|
|
|
case "stat", "stat64":
|
|
action = h.checkStat(ctx, ctx.Arg0())
|
|
case "lstat", "lstat64":
|
|
action = h.checkStat(ctx, ctx.Arg0())
|
|
|
|
case "execve":
|
|
action = h.checkRead(ctx, ctx.Arg0())
|
|
case "execveat":
|
|
action = h.checkRead(ctx, ctx.Arg1())
|
|
|
|
case "chmod":
|
|
action = h.checkWrite(ctx, ctx.Arg0())
|
|
case "rename":
|
|
action = h.checkWrite(ctx, ctx.Arg0())
|
|
|
|
default:
|
|
action = h.Handler.CheckSyscall(syscallName)
|
|
if h.Unsafe && action == ptracer.TraceKill {
|
|
action = ptracer.TraceBan
|
|
}
|
|
}
|
|
|
|
switch action {
|
|
case ptracer.TraceAllow:
|
|
return ptracer.TraceAllow
|
|
case ptracer.TraceBan:
|
|
h.Debug("<soft ban syscall>")
|
|
return softBanSyscall(ctx)
|
|
default:
|
|
return ptracer.TraceKill
|
|
}
|
|
}
|
|
|
|
func softBanSyscall(ctx *ptracer.Context) ptracer.TraceAction {
|
|
ctx.SetReturnValue(-int(BanRet))
|
|
return ptracer.TraceBan
|
|
}
|
|
|
|
func getFileMode(flags uint) string {
|
|
switch flags & syscall.O_ACCMODE {
|
|
case syscall.O_RDONLY:
|
|
return "r "
|
|
case syscall.O_WRONLY:
|
|
return "w "
|
|
case syscall.O_RDWR:
|
|
return "wr"
|
|
default:
|
|
return "??"
|
|
}
|
|
}
|
|
|
|
// getProcCwd gets the process CWD
|
|
func getProcCwd(pid int) string {
|
|
fileName := "/proc/self/cwd"
|
|
if pid > 0 {
|
|
fileName = fmt.Sprintf("/proc/%d/cwd", pid)
|
|
}
|
|
s, err := os.Readlink(fileName)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return s
|
|
}
|
|
|
|
// absPath calculates the absolute path for a process
|
|
// built-in function did the dirty works to resolve relative paths
|
|
func absPath(pid int, p string) string {
|
|
// if relative path
|
|
if !filepath.IsAbs(p) {
|
|
return filepath.Join(getProcCwd(pid), p)
|
|
}
|
|
return filepath.Clean(p)
|
|
}
|