mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-09-17 23:22:27 +02:00
181 lines
4.0 KiB
Go
181 lines
4.0 KiB
Go
//go:build !windows
|
|
|
|
package processguard
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
type unixGroup struct {
|
|
mu sync.Mutex
|
|
watcher *watchdog
|
|
pids map[int]struct{}
|
|
closed bool
|
|
}
|
|
|
|
func newUnixGroup() (*unixGroup, error) {
|
|
g := &unixGroup{pids: make(map[int]struct{})}
|
|
w, err := startWatchdog(watchRequest{Name: "process_group"}, func() {
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
g.closed = true
|
|
for pid := range g.pids {
|
|
_ = syscall.Kill(-pid, syscall.SIGKILL)
|
|
}
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
g.watcher = w
|
|
return g, nil
|
|
}
|
|
func (g *unixGroup) Name() string { return "process_group_watchdog" }
|
|
func configureGuardian(cmd *exec.Cmd) { cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true} }
|
|
|
|
type childSpec struct {
|
|
Path string
|
|
Args []string
|
|
}
|
|
|
|
func (g *unixGroup) Prepare(cmd *exec.Cmd) (*Launch, error) {
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
if g.closed {
|
|
return nil, fmt.Errorf("process group is closed")
|
|
}
|
|
// A dead guardian rejects subsequent launches before user code is executed.
|
|
if _, err := g.watcher.send(watchRequest{Op: "ping"}); err != nil {
|
|
return nil, err
|
|
}
|
|
read, write, err := os.Pipe()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
spec, _ := json.Marshal(childSpec{Path: cmd.Path, Args: cmd.Args})
|
|
exe, err := os.Executable()
|
|
if err != nil {
|
|
read.Close()
|
|
write.Close()
|
|
return nil, err
|
|
}
|
|
fd := 3 + len(cmd.ExtraFiles)
|
|
cmd.ExtraFiles = append(cmd.ExtraFiles, read)
|
|
cmd.Path = exe
|
|
cmd.Args = []string{exe, childArg, strconv.Itoa(fd), base64.RawStdEncoding.EncodeToString(spec)}
|
|
return &Launch{Dispose: func() { read.Close(); write.Close() }, Commit: func() error {
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
if g.closed {
|
|
return fmt.Errorf("process group is closed")
|
|
}
|
|
pid := cmd.Process.Pid
|
|
if _, err := g.watcher.send(watchRequest{Op: "add", PID: pid}); err != nil {
|
|
return err
|
|
}
|
|
g.pids[pid] = struct{}{}
|
|
_, err := write.Write([]byte{1})
|
|
return err
|
|
}}, nil
|
|
}
|
|
func (g *unixGroup) Release(pid int) error {
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
if _, ok := g.pids[pid]; !ok {
|
|
return nil
|
|
}
|
|
select {
|
|
case <-g.watcher.done:
|
|
delete(g.pids, pid)
|
|
return nil
|
|
default:
|
|
}
|
|
if _, err := g.watcher.send(watchRequest{Op: "release", PID: pid}); err != nil {
|
|
return err
|
|
}
|
|
delete(g.pids, pid)
|
|
return nil
|
|
}
|
|
func (g *unixGroup) Close(ctx context.Context) error {
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
g.closed = true
|
|
for pid := range g.pids {
|
|
_ = syscall.Kill(-pid, syscall.SIGKILL)
|
|
}
|
|
for {
|
|
for pid := range g.pids {
|
|
if syscall.Kill(-pid, 0) == syscall.ESRCH {
|
|
delete(g.pids, pid)
|
|
}
|
|
}
|
|
if len(g.pids) == 0 {
|
|
return g.watcher.close()
|
|
}
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-time.After(10 * time.Millisecond):
|
|
}
|
|
}
|
|
}
|
|
func gatedChildMain(args []string) error {
|
|
if len(args) != 2 {
|
|
return fmt.Errorf("invalid internal launch")
|
|
}
|
|
fd, err := strconv.Atoi(args[0])
|
|
if err != nil || fd < 3 {
|
|
return fmt.Errorf("invalid launch gate")
|
|
}
|
|
gate := os.NewFile(uintptr(fd), "launch-gate")
|
|
var token [1]byte
|
|
if _, err = io.ReadFull(gate, token[:]); err != nil {
|
|
return fmt.Errorf("owner exited before launch: %w", err)
|
|
}
|
|
gate.Close()
|
|
if token[0] != 1 {
|
|
return fmt.Errorf("invalid launch token")
|
|
}
|
|
b, err := base64.RawStdEncoding.DecodeString(args[1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var spec childSpec
|
|
if err = json.Unmarshal(b, &spec); err != nil {
|
|
return err
|
|
}
|
|
return syscall.Exec(spec.Path, spec.Args, os.Environ())
|
|
}
|
|
func groupGuardian(dec *json.Decoder, enc *json.Encoder) error {
|
|
pids := make(map[int]struct{})
|
|
return serveGuardian(dec, enc, func(req watchRequest) error {
|
|
switch req.Op {
|
|
case "ping":
|
|
case "add":
|
|
if req.PID <= 1 {
|
|
return fmt.Errorf("invalid PID")
|
|
}
|
|
pids[req.PID] = struct{}{}
|
|
case "release":
|
|
delete(pids, req.PID)
|
|
default:
|
|
return fmt.Errorf("unknown guardian command")
|
|
}
|
|
return nil
|
|
}, func() error {
|
|
for pid := range pids {
|
|
_ = syscall.Kill(-pid, syscall.SIGKILL)
|
|
}
|
|
return nil
|
|
})
|
|
}
|