From 9a239fa1a5ad2ff248ca7a9d39342f66926c9fef Mon Sep 17 00:00:00 2001 From: Ophestra Date: Wed, 22 Jan 2025 01:51:10 +0900 Subject: helper/bwrap: integrate seccomp into helper interface This makes API usage much cleaner, and encapsulates all bwrap arguments in argsWt. Signed-off-by: Ophestra --- dbus/proxy.go | 2 +- dbus/run.go | 2 +- fst/config.go | 10 -- fst/sandbox.go | 7 + helper/bwrap.go | 41 +++--- helper/bwrap/arg.go | 63 ++++++++- helper/bwrap/config.go | 5 +- helper/bwrap/seccomp-export.c | 254 +++++++++++++++++++++++++++++++++++++ helper/bwrap/seccomp-export.h | 22 ++++ helper/bwrap/seccomp-resolve.go | 95 ++++++++++++++ helper/bwrap/seccomp.go | 83 ++++++++++++ helper/bwrap/sequential.go | 6 + helper/bwrap_test.go | 12 +- internal/app/app_pd_test.go | 2 + internal/app/seal.go | 13 +- internal/app/start.go | 9 +- internal/proc/priv/shim/export.c | 254 ------------------------------------- internal/proc/priv/shim/export.h | 22 ---- internal/proc/priv/shim/main.go | 120 +----------------- internal/proc/priv/shim/payload.go | 3 - internal/proc/priv/shim/seccomp.go | 81 ------------ internal/system/dbus.go | 2 +- ldd/exec.go | 3 +- main.go | 2 +- 24 files changed, 575 insertions(+), 538 deletions(-) create mode 100644 helper/bwrap/seccomp-export.c create mode 100644 helper/bwrap/seccomp-export.h create mode 100644 helper/bwrap/seccomp-resolve.go create mode 100644 helper/bwrap/seccomp.go delete mode 100644 internal/proc/priv/shim/export.c delete mode 100644 internal/proc/priv/shim/export.h delete mode 100644 internal/proc/priv/shim/seccomp.go diff --git a/dbus/proxy.go b/dbus/proxy.go index 9f78648c..59b887d2 100644 --- a/dbus/proxy.go +++ b/dbus/proxy.go @@ -66,7 +66,7 @@ func (p *Proxy) String() string { return "(unsealed dbus proxy)" } -func (p *Proxy) Bwrap() []string { +func (p *Proxy) BwrapStatic() []string { return p.bwrap.Args() } diff --git a/dbus/run.go b/dbus/run.go index addf5226..0a554bfb 100644 --- a/dbus/run.go +++ b/dbus/run.go @@ -110,7 +110,7 @@ func (p *Proxy) Start(ready chan error, output io.Writer, sandbox bool) error { bc.Bind(k, k) } - h = helper.MustNewBwrap(bc, toolPath, p.seal, argF, nil) + h = helper.MustNewBwrap(bc, toolPath, p.seal, argF, nil, nil) cmd = h.Unwrap() p.bwrap = bc } diff --git a/fst/config.go b/fst/config.go index ed0ac4cd..72cb2021 100644 --- a/fst/config.go +++ b/fst/config.go @@ -31,8 +31,6 @@ type ConfinementConfig struct { Outer string `json:"home"` // bwrap sandbox confinement configuration Sandbox *SandboxConfig `json:"sandbox"` - // seccomp syscall filter configuration - Syscall *SyscallConfig `json:"syscall"` // extra acl entries to append ExtraPerms []*ExtraPermConfig `json:"extra_perms,omitempty"` @@ -47,14 +45,6 @@ type ConfinementConfig struct { Enablements system.Enablements `json:"enablements"` } -type SyscallConfig struct { - DenyDevel bool `json:"deny_devel"` - Multiarch bool `json:"multiarch"` - Linux32 bool `json:"linux32"` - Can bool `json:"can"` - Bluetooth bool `json:"bluetooth"` -} - type ExtraPermConfig struct { Ensure bool `json:"ensure,omitempty"` Path string `json:"path"` diff --git a/fst/sandbox.go b/fst/sandbox.go index 8def9086..0c3918a9 100644 --- a/fst/sandbox.go +++ b/fst/sandbox.go @@ -22,6 +22,8 @@ type SandboxConfig struct { Net bool `json:"net,omitempty"` // share all devices Dev bool `json:"dev,omitempty"` + // seccomp syscall filter policy + Syscall *bwrap.SyscallPolicy `json:"syscall"` // do not run in new session NoNewSession bool `json:"no_new_session,omitempty"` // map target user uid to privileged user uid in the user namespace @@ -50,6 +52,10 @@ func (s *SandboxConfig) Bwrap(os linux.System) (*bwrap.Config, error) { return nil, errors.New("nil sandbox config") } + if s.Syscall == nil { + fmsg.VPrintln("syscall filter not configured, PROCEED WITH CAUTION") + } + var uid int if !s.MapRealUID { uid = 65534 @@ -69,6 +75,7 @@ func (s *SandboxConfig) Bwrap(os linux.System) (*bwrap.Config, error) { so this capacity should eliminate copies for most setups */ Filesystem: make([]bwrap.FSBuilder, 0, 256), + Syscall: s.Syscall, NewSession: !s.NoNewSession, DieWithParent: true, AsInit: true, diff --git a/helper/bwrap.go b/helper/bwrap.go index ad077312..851f2448 100644 --- a/helper/bwrap.go +++ b/helper/bwrap.go @@ -9,25 +9,17 @@ import ( "sync" "git.gensokyo.uk/security/fortify/helper/bwrap" - "git.gensokyo.uk/security/fortify/internal/proc" ) // BubblewrapName is the file name or path to bubblewrap. var BubblewrapName = "bwrap" -type BwrapExtraFile struct { - Name string - File *os.File -} - type bubblewrap struct { // bwrap child file name name string // bwrap pipes control *pipes - // extra files with fd passed as argument - extra []BwrapExtraFile // returns an array of arguments passed directly // to the child process spawned by bwrap argF func(argsFD, statFD int) []string @@ -54,14 +46,6 @@ func (b *bubblewrap) StartNotify(ready chan error) error { return errors.New("exec: already started") } - // pass extra fd to bwrap - for _, e := range b.extra { - if e.File == nil { - continue - } - b.Cmd.Args = append(b.Cmd.Args, e.Name, strconv.Itoa(int(proc.ExtraFile(b.Cmd, e.File)))) - } - // prepare bwrap pipe and args if argsFD, _, err := b.control.prepareCmd(b.Cmd); err != nil { return err @@ -130,9 +114,10 @@ func (b *bubblewrap) Unwrap() *exec.Cmd { func MustNewBwrap( conf *bwrap.Config, name string, wt io.WriterTo, argF func(argsFD, statFD int) []string, - extra []BwrapExtraFile, + extraFiles []*os.File, + syncFd *os.File, ) Helper { - b, err := NewBwrap(conf, name, wt, argF, extra) + b, err := NewBwrap(conf, name, wt, argF, extraFiles, syncFd) if err != nil { panic(err.Error()) } else { @@ -146,23 +131,27 @@ func MustNewBwrap( func NewBwrap( conf *bwrap.Config, name string, wt io.WriterTo, argF func(argsFD, statFD int) []string, - extra []BwrapExtraFile, + extraFiles []*os.File, + syncFd *os.File, ) (Helper, error) { b := new(bubblewrap) - if args, err := NewCheckedArgs(conf.Args()); err != nil { - return nil, err - } else { - b.control = &pipes{args: args} - } - - b.extra = extra b.argF = argF b.name = name if wt != nil { b.controlPt = &pipes{args: wt} } + b.Cmd = execCommand(BubblewrapName) + b.control = new(pipes) + args := conf.Args() + if fdArgs, err := conf.FDArgs(syncFd, &extraFiles); err != nil { + return nil, err + } else if b.control.args, err = NewCheckedArgs(append(args, fdArgs...)); err != nil { + return nil, err + } else { + b.Cmd.ExtraFiles = extraFiles + } return b, nil } diff --git a/helper/bwrap/arg.go b/helper/bwrap/arg.go index efdab823..d419ba50 100644 --- a/helper/bwrap/arg.go +++ b/helper/bwrap/arg.go @@ -1,6 +1,13 @@ package bwrap -import "encoding/gob" +import ( + "encoding/gob" + "os" + "slices" + "strconv" + + "git.gensokyo.uk/security/fortify/internal/proc" +) type Builder interface { Len() int @@ -12,6 +19,11 @@ type FSBuilder interface { Builder } +type FDBuilder interface { + Len() int + Append(args *[]string, extraFiles *[]*os.File) error +} + func init() { gob.Register(new(pairF)) gob.Register(new(stringF)) @@ -45,6 +57,33 @@ func (s stringF) Append(args *[]string) { *args = append(*args, s[0], s[1]) } +type fileF struct { + name string + file *os.File +} + +func (f *fileF) Len() int { + if f.file == nil { + return 0 + } + return 2 +} + +func (f *fileF) Append(args *[]string, extraFiles *[]*os.File) error { + if f.file == nil { + return nil + } + extraFile(args, extraFiles, f.name, f.file) + return nil +} + +func extraFile(args *[]string, extraFiles *[]*os.File, name string, f *os.File) { + if f == nil { + return + } + *args = append(*args, name, strconv.Itoa(int(proc.ExtraFileSlice(extraFiles, f)))) +} + // Args returns a slice of bwrap args corresponding to c. func (c *Config) Args() (args []string) { builders := []Builder{ @@ -75,3 +114,25 @@ func (c *Config) Args() (args []string) { return } + +func (c *Config) FDArgs(syncFd *os.File, extraFiles *[]*os.File) (args []string, err error) { + builders := []FDBuilder{ + &seccompBuilder{c}, + &fileF{positionalArgs[SyncFd], syncFd}, + } + + argc := 0 + for _, b := range builders { + argc += b.Len() + } + + args = make([]string, 0, argc) + *extraFiles = slices.Grow(*extraFiles, len(builders)) + + for _, b := range builders { + if err = b.Append(&args, extraFiles); err != nil { + break + } + } + return +} diff --git a/helper/bwrap/config.go b/helper/bwrap/config.go index 8def27a6..b9fa0c12 100644 --- a/helper/bwrap/config.go +++ b/helper/bwrap/config.go @@ -47,6 +47,10 @@ type Config struct { // (--chmod OCTAL PATH) Chmod ChmodConfig `json:"chmod,omitempty"` + // load and use seccomp rules from FD (not repeatable) + // (--seccomp FD) + Syscall *SyscallPolicy + // create a new terminal session // (--new-session) NewSession bool `json:"new_session"` @@ -70,7 +74,6 @@ type Config struct { --file FD DEST Copy from FD to destination DEST --bind-data FD DEST Copy from FD to file which is bind-mounted on DEST --ro-bind-data FD DEST Copy from FD to file which is readonly bind-mounted on DEST - --seccomp FD Load and use seccomp rules from FD (not repeatable) --add-seccomp-fd FD Load and use seccomp rules from FD (repeatable) --block-fd FD Block on FD until some data to read is available --userns-block-fd FD Block on FD until the user namespace is ready diff --git a/helper/bwrap/seccomp-export.c b/helper/bwrap/seccomp-export.c new file mode 100644 index 00000000..40e3bea9 --- /dev/null +++ b/helper/bwrap/seccomp-export.c @@ -0,0 +1,254 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // CLONE_NEWUSER +#endif + +#include "seccomp-export.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if (SCMP_VER_MAJOR < 2) || \ + (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 5) || \ + (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR == 5 && SCMP_VER_MICRO < 1) +#error This package requires libseccomp >= v2.5.1 +#endif + +struct f_syscall_act { + int syscall; + int m_errno; + struct scmp_arg_cmp *arg; +}; + +#define LEN(arr) (sizeof(arr) / sizeof((arr)[0])) + +#define SECCOMP_RULESET_ADD(ruleset) do { \ + F_println("adding seccomp ruleset \"" #ruleset "\""); \ + for (int i = 0; i < LEN(ruleset); i++) { \ + assert(ruleset[i].m_errno == EPERM || ruleset[i].m_errno == ENOSYS); \ + \ + if (ruleset[i].arg) \ + ret = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ruleset[i].m_errno), ruleset[i].syscall, 1, *ruleset[i].arg); \ + else \ + ret = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ruleset[i].m_errno), ruleset[i].syscall, 0); \ + \ + if (ret == -EFAULT) { \ + res = 4; \ + goto out; \ + } else if (ret < 0) { \ + res = 5; \ + errno = -ret; \ + goto out; \ + } \ + } \ +} while (0) + + +int f_tmpfile_fd() { + FILE *f = tmpfile(); + if (f == NULL) + return -1; + return fileno(f); +} + +int32_t f_export_bpf(int fd, uint32_t arch, uint32_t multiarch, f_syscall_opts opts) { + int32_t res = 0; // refer to resErr for meaning + int allow_multiarch = opts & F_MULTIARCH; + int allowed_personality = PER_LINUX; + + if (opts & F_LINUX32) + allowed_personality = PER_LINUX32; + + // flatpak commit 4c3bf179e2e4a2a298cd1db1d045adaf3f564532 + + struct f_syscall_act deny_common[] = { + // Block dmesg + {SCMP_SYS(syslog), EPERM}, + // Useless old syscall + {SCMP_SYS(uselib), EPERM}, + // Don't allow disabling accounting + {SCMP_SYS(acct), EPERM}, + // Don't allow reading current quota use + {SCMP_SYS(quotactl), EPERM}, + + // Don't allow access to the kernel keyring + {SCMP_SYS(add_key), EPERM}, + {SCMP_SYS(keyctl), EPERM}, + {SCMP_SYS(request_key), EPERM}, + + // Scary VM/NUMA ops + {SCMP_SYS(move_pages), EPERM}, + {SCMP_SYS(mbind), EPERM}, + {SCMP_SYS(get_mempolicy), EPERM}, + {SCMP_SYS(set_mempolicy), EPERM}, + {SCMP_SYS(migrate_pages), EPERM}, + }; + + struct f_syscall_act deny_ns[] = { + // Don't allow subnamespace setups: + {SCMP_SYS(unshare), EPERM}, + {SCMP_SYS(setns), EPERM}, + {SCMP_SYS(mount), EPERM}, + {SCMP_SYS(umount), EPERM}, + {SCMP_SYS(umount2), EPERM}, + {SCMP_SYS(pivot_root), EPERM}, + {SCMP_SYS(chroot), EPERM}, +#if defined(__s390__) || defined(__s390x__) || defined(__CRIS__) + // Architectures with CONFIG_CLONE_BACKWARDS2: the child stack + // and flags arguments are reversed so the flags come second + {SCMP_SYS(clone), EPERM, &SCMP_A1(SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)}, +#else + // Normally the flags come first + {SCMP_SYS(clone), EPERM, &SCMP_A0(SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)}, +#endif + + // seccomp can't look into clone3()'s struct clone_args to check whether + // the flags are OK, so we have no choice but to block clone3(). + // Return ENOSYS so user-space will fall back to clone(). + // (CVE-2021-41133; see also https://github.com/moby/moby/commit/9f6b562d) + {SCMP_SYS(clone3), ENOSYS}, + + // New mount manipulation APIs can also change our VFS. There's no + // legitimate reason to do these in the sandbox, so block all of them + // rather than thinking about which ones might be dangerous. + // (CVE-2021-41133) + {SCMP_SYS(open_tree), ENOSYS}, + {SCMP_SYS(move_mount), ENOSYS}, + {SCMP_SYS(fsopen), ENOSYS}, + {SCMP_SYS(fsconfig), ENOSYS}, + {SCMP_SYS(fsmount), ENOSYS}, + {SCMP_SYS(fspick), ENOSYS}, + {SCMP_SYS(mount_setattr), ENOSYS}, + }; + + struct f_syscall_act deny_tty[] = { + // Don't allow faking input to the controlling tty (CVE-2017-5226) + {SCMP_SYS(ioctl), EPERM, &SCMP_A1(SCMP_CMP_MASKED_EQ, 0xFFFFFFFFu, (int)TIOCSTI)}, + // In the unlikely event that the controlling tty is a Linux virtual + // console (/dev/tty2 or similar), copy/paste operations have an effect + // similar to TIOCSTI (CVE-2023-28100) + {SCMP_SYS(ioctl), EPERM, &SCMP_A1(SCMP_CMP_MASKED_EQ, 0xFFFFFFFFu, (int)TIOCLINUX)}, + }; + + struct f_syscall_act deny_devel[] = { + // Profiling operations; we expect these to be done by tools from outside + // the sandbox. In particular perf has been the source of many CVEs. + {SCMP_SYS(perf_event_open), EPERM}, + // Don't allow you to switch to bsd emulation or whatnot + {SCMP_SYS(personality), EPERM, &SCMP_A0(SCMP_CMP_NE, allowed_personality)}, + + {SCMP_SYS(ptrace), EPERM} + }; + + // Blocklist all but unix, inet, inet6 and netlink + struct + { + int family; + f_syscall_opts flags_mask; + } socket_family_allowlist[] = { + // NOTE: Keep in numerical order + { AF_UNSPEC, 0 }, + { AF_LOCAL, 0 }, + { AF_INET, 0 }, + { AF_INET6, 0 }, + { AF_NETLINK, 0 }, + { AF_CAN, F_CAN }, + { AF_BLUETOOTH, F_BLUETOOTH }, + }; + + scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW); + if (ctx == NULL) { + res = 1; + goto out; + } else + errno = 0; + + int ret; + + // We only really need to handle arches on multiarch systems. + // If only one arch is supported the default is fine + if (arch != 0) { + // This *adds* the target arch, instead of replacing the + // native one. This is not ideal, because we'd like to only + // allow the target arch, but we can't really disallow the + // native arch at this point, because then bubblewrap + // couldn't continue running. + ret = seccomp_arch_add(ctx, arch); + if (ret < 0 && ret != -EEXIST) { + res = 2; + errno = -ret; + goto out; + } + + if (allow_multiarch && multiarch != 0) { + ret = seccomp_arch_add(ctx, multiarch); + if (ret < 0 && ret != -EEXIST) { + res = 3; + errno = -ret; + goto out; + } + } + } + + SECCOMP_RULESET_ADD(deny_common); + if (opts & F_DENY_NS) SECCOMP_RULESET_ADD(deny_ns); + if (opts & F_DENY_TTY) SECCOMP_RULESET_ADD(deny_tty); + if (opts & F_DENY_DEVEL) SECCOMP_RULESET_ADD(deny_devel); + + if (!allow_multiarch) { + F_println("disabling modify_ldt"); + + // modify_ldt is a historic source of interesting information leaks, + // so it's disabled as a hardening measure. + // However, it is required to run old 16-bit applications + // as well as some Wine patches, so it's allowed in multiarch. + ret = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(modify_ldt), 0); + + // See above for the meaning of EFAULT. + if (ret == -EFAULT) { + // call fmsg here? + res = 4; + goto out; + } else if (ret < 0) { + res = 5; + errno = -ret; + goto out; + } + } + + // Socket filtering doesn't work on e.g. i386, so ignore failures here + // However, we need to user seccomp_rule_add_exact to avoid libseccomp doing + // something else: https://github.com/seccomp/libseccomp/issues/8 + int last_allowed_family = -1; + for (int i = 0; i < LEN(socket_family_allowlist); i++) { + if (socket_family_allowlist[i].flags_mask != 0 && + (socket_family_allowlist[i].flags_mask & opts) != socket_family_allowlist[i].flags_mask) + continue; + + for (int disallowed = last_allowed_family + 1; disallowed < socket_family_allowlist[i].family; disallowed++) { + // Blocklist the in-between valid families + seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EAFNOSUPPORT), SCMP_SYS(socket), 1, SCMP_A0(SCMP_CMP_EQ, disallowed)); + } + last_allowed_family = socket_family_allowlist[i].family; + } + // Blocklist the rest + seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EAFNOSUPPORT), SCMP_SYS(socket), 1, SCMP_A0(SCMP_CMP_GE, last_allowed_family + 1)); + + ret = seccomp_export_bpf(ctx, fd); + if (ret != 0) { + res = 6; + errno = -ret; + goto out; + } + +out: + if (ctx) + seccomp_release(ctx); + + return res; +} diff --git a/helper/bwrap/seccomp-export.h b/helper/bwrap/seccomp-export.h new file mode 100644 index 00000000..5df0dc65 --- /dev/null +++ b/helper/bwrap/seccomp-export.h @@ -0,0 +1,22 @@ +#include +#include + +#if (SCMP_VER_MAJOR < 2) || \ + (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 5) || \ + (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR == 5 && SCMP_VER_MICRO < 1) +#error This package requires libseccomp >= v2.5.1 +#endif + +typedef enum { + F_DENY_NS = 1 << 0, + F_DENY_TTY = 1 << 1, + F_DENY_DEVEL = 1 << 2, + F_MULTIARCH = 1 << 3, + F_LINUX32 = 1 << 4, + F_CAN = 1 << 5, + F_BLUETOOTH = 1 << 6, +} f_syscall_opts; + +extern void F_println(char *v); +int f_tmpfile_fd(); +int32_t f_export_bpf(int fd, uint32_t arch, uint32_t multiarch, f_syscall_opts opts); \ No newline at end of file diff --git a/helper/bwrap/seccomp-resolve.go b/helper/bwrap/seccomp-resolve.go new file mode 100644 index 00000000..995a3719 --- /dev/null +++ b/helper/bwrap/seccomp-resolve.go @@ -0,0 +1,95 @@ +package bwrap + +import ( + "fmt" + "io" + "os" + + "git.gensokyo.uk/security/fortify/internal/fmsg" +) + +type SyscallPolicy struct { + DenyDevel bool `json:"deny_devel"` + Multiarch bool `json:"multiarch"` + Linux32 bool `json:"linux32"` + Can bool `json:"can"` + Bluetooth bool `json:"bluetooth"` +} + +type seccompBuilder struct { + config *Config +} + +func (s *seccompBuilder) Len() int { + if s == nil { + return 0 + } + return 2 +} + +func (s *seccompBuilder) Append(args *[]string, extraFiles *[]*os.File) error { + if s == nil { + return nil + } + if f, err := s.config.resolveSeccomp(); err != nil { + return err + } else { + extraFile(args, extraFiles, positionalArgs[Seccomp], f) + return nil + } +} + +func (c *Config) resolveSeccomp() (*os.File, error) { + if c.Syscall == nil { + return nil, nil + } + + // resolve seccomp filter opts + var ( + opts syscallOpts + optd []string + optCond = [...]struct { + v bool + o syscallOpts + d string + }{ + {!c.UserNS, flagDenyNS, "denyns"}, + {c.NewSession, flagDenyTTY, "denytty"}, + {c.Syscall.DenyDevel, flagDenyDevel, "denydevel"}, + {c.Syscall.Multiarch, flagMultiarch, "multiarch"}, + {c.Syscall.Linux32, flagLinux32, "linux32"}, + {c.Syscall.Can, flagCan, "can"}, + {c.Syscall.Bluetooth, flagBluetooth, "bluetooth"}, + } + ) + if CPrintln != nil { + optd = make([]string, 1, len(optCond)+1) + optd[0] = "common" + } + for _, opt := range optCond { + if opt.v { + opts |= opt.o + if fmsg.Verbose() { + optd = append(optd, opt.d) + } + } + } + if CPrintln != nil { + CPrintln(fmt.Sprintf("seccomp flags: %s", optd)) + } + + // export seccomp filter to tmpfile + if f, err := tmpfile(); err != nil { + return nil, err + } else { + return f, exportAndSeek(f, opts) + } +} + +func exportAndSeek(f *os.File, opts syscallOpts) error { + if err := exportFilter(f.Fd(), opts); err != nil { + return err + } + _, err := f.Seek(0, io.SeekStart) + return err +} diff --git a/helper/bwrap/seccomp.go b/helper/bwrap/seccomp.go new file mode 100644 index 00000000..abc2738b --- /dev/null +++ b/helper/bwrap/seccomp.go @@ -0,0 +1,83 @@ +package bwrap + +/* +#cgo linux pkg-config: --static libseccomp + +#include "seccomp-export.h" +*/ +import "C" +import ( + "errors" + "fmt" + "os" + "runtime" +) + +var CPrintln func(v ...any) + +var resErr = [...]error{ + 0: nil, + 1: errors.New("seccomp_init failed"), + 2: errors.New("seccomp_arch_add failed"), + 3: errors.New("seccomp_arch_add failed (multiarch)"), + 4: errors.New("internal libseccomp failure"), + 5: errors.New("seccomp_rule_add failed"), + 6: errors.New("seccomp_export_bpf failed"), +} + +type ( + syscallOpts = C.f_syscall_opts +) + +const ( + flagDenyNS syscallOpts = C.F_DENY_NS + flagDenyTTY syscallOpts = C.F_DENY_TTY + flagDenyDevel syscallOpts = C.F_DENY_DEVEL + flagMultiarch syscallOpts = C.F_MULTIARCH + flagLinux32 syscallOpts = C.F_LINUX32 + flagCan syscallOpts = C.F_CAN + flagBluetooth syscallOpts = C.F_BLUETOOTH +) + +func tmpfile() (*os.File, error) { + fd, err := C.f_tmpfile_fd() + if err != nil { + return nil, err + } + return os.NewFile(uintptr(fd), "tmpfile"), err +} + +func exportFilter(fd uintptr, opts syscallOpts) error { + var ( + arch C.uint32_t = 0 + multiarch C.uint32_t = 0 + ) + switch runtime.GOARCH { + case "386": + arch = C.SCMP_ARCH_X86 + case "amd64": + arch = C.SCMP_ARCH_X86_64 + multiarch = C.SCMP_ARCH_X86 + case "arm": + arch = C.SCMP_ARCH_ARM + case "arm64": + arch = C.SCMP_ARCH_AARCH64 + multiarch = C.SCMP_ARCH_ARM + } + + res, err := C.f_export_bpf(C.int(fd), arch, multiarch, opts) + if re := resErr[res]; re != nil { + if err == nil { + return re + } + return fmt.Errorf("%s: %v", re.Error(), err) + } + return err +} + +//export F_println +func F_println(v *C.char) { + if CPrintln != nil { + CPrintln(C.GoString(v)) + } +} diff --git a/helper/bwrap/sequential.go b/helper/bwrap/sequential.go index 42a4b2b5..1776c235 100644 --- a/helper/bwrap/sequential.go +++ b/helper/bwrap/sequential.go @@ -43,6 +43,9 @@ const ( Overlay TmpOverlay ROOverlay + + SyncFd + Seccomp ) var positionalArgs = [...]string{ @@ -70,6 +73,9 @@ var positionalArgs = [...]string{ Overlay: "--overlay", TmpOverlay: "--tmp-overlay", ROOverlay: "--ro-overlay", + + SyncFd: "--sync-fd", + Seccomp: "--seccomp", } type PermConfig[T FSBuilder] struct { diff --git a/helper/bwrap_test.go b/helper/bwrap_test.go index 9a565a42..0860d57a 100644 --- a/helper/bwrap_test.go +++ b/helper/bwrap_test.go @@ -34,7 +34,7 @@ func TestBwrap(t *testing.T) { h := helper.MustNewBwrap( sc, "fortify", argsWt, argF, - nil, + nil, nil, ) if err := h.Start(); !errors.Is(err, os.ErrNotExist) { @@ -47,7 +47,7 @@ func TestBwrap(t *testing.T) { if got := helper.MustNewBwrap( sc, "fortify", argsWt, argF, - nil, + nil, nil, ); got == nil { t.Errorf("MustNewBwrap(%#v, %#v, %#v) got nil", sc, argsWt, "fortify") @@ -67,7 +67,7 @@ func TestBwrap(t *testing.T) { helper.MustNewBwrap( &bwrap.Config{Hostname: "\x00"}, "fortify", nil, argF, - nil, + nil, nil, ) }) @@ -84,7 +84,7 @@ func TestBwrap(t *testing.T) { helper.MustNewBwrap( sc, "fortify", nil, argF, - nil, + nil, nil, ).StartNotify(make(chan error)))) }) @@ -94,7 +94,7 @@ func TestBwrap(t *testing.T) { h := helper.MustNewBwrap( sc, "crash-test-dummy", nil, argFChecked, - nil, + nil, nil, ) cmd := h.Unwrap() @@ -127,6 +127,6 @@ func TestBwrap(t *testing.T) { }) t.Run("implementation compliance", func(t *testing.T) { - testHelper(t, func() helper.Helper { return helper.MustNewBwrap(sc, "crash-test-dummy", argsWt, argF, nil) }) + testHelper(t, func() helper.Helper { return helper.MustNewBwrap(sc, "crash-test-dummy", argsWt, argF, nil, nil) }) }) } diff --git a/internal/app/app_pd_test.go b/internal/app/app_pd_test.go index 968a11ab..98ddf31f 100644 --- a/internal/app/app_pd_test.go +++ b/internal/app/app_pd_test.go @@ -39,6 +39,7 @@ var testCasesPd = []sealTestCase{ Net: true, UserNS: true, Clearenv: true, + Syscall: new(bwrap.SyscallPolicy), Chdir: "/home/chronos", SetEnv: map[string]string{ "HOME": "/home/chronos", @@ -258,6 +259,7 @@ var testCasesPd = []sealTestCase{ UserNS: true, Chdir: "/home/chronos", Clearenv: true, + Syscall: new(bwrap.SyscallPolicy), SetEnv: map[string]string{ "DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/65534/bus", "DBUS_SYSTEM_BUS_ADDRESS": "unix:path=/run/dbus/system_bus_socket", diff --git a/internal/app/seal.go b/internal/app/seal.go index bd0522a6..228be5c9 100644 --- a/internal/app/seal.go +++ b/internal/app/seal.go @@ -14,6 +14,7 @@ import ( "git.gensokyo.uk/security/fortify/acl" "git.gensokyo.uk/security/fortify/dbus" "git.gensokyo.uk/security/fortify/fst" + "git.gensokyo.uk/security/fortify/helper/bwrap" "git.gensokyo.uk/security/fortify/internal/fmsg" "git.gensokyo.uk/security/fortify/internal/linux" "git.gensokyo.uk/security/fortify/internal/state" @@ -52,8 +53,6 @@ type appSeal struct { et system.Enablements // initial config gob encoding buffer ct io.WriterTo - // pass-through seccomp config from config - scmp *fst.SyscallConfig // wayland socket direct access directWayland bool // extra UpdatePerm ops @@ -196,6 +195,7 @@ func (a *app) Seal(config *fst.Config) error { conf := &fst.SandboxConfig{ UserNS: true, Net: true, + Syscall: new(bwrap.SyscallPolicy), NoNewSession: true, AutoEtc: true, } @@ -233,12 +233,6 @@ func (a *app) Seal(config *fst.Config) error { conf.Filesystem = append(conf.Filesystem, &fst.FilesystemConfig{Src: "/dev/kvm", Device: true}) config.Confinement.Sandbox = conf - - // ensure syscall filter - if config.Confinement.Syscall == nil { - config.Confinement.Syscall = new(fst.SyscallConfig) - config.Confinement.Syscall.Multiarch = true - } } seal.directWayland = config.Confinement.Sandbox.DirectWayland if b, err := config.Confinement.Sandbox.Bwrap(a.os); err != nil { @@ -259,9 +253,8 @@ func (a *app) Seal(config *fst.Config) error { // initialise system interface with full uid seal.sys.I = system.New(seal.sys.user.uid) - // pass through enablements and seccomp + // pass through enablements seal.et = config.Confinement.Enablements - seal.scmp = config.Confinement.Syscall // this method calls all share methods in sequence if err := seal.setupShares([2]*dbus.Config{config.Confinement.SessionBus, config.Confinement.SystemBus}, a.os); err != nil { diff --git a/internal/app/start.go b/internal/app/start.go index 87bb6fde..d0a0116d 100644 --- a/internal/app/start.go +++ b/internal/app/start.go @@ -76,11 +76,10 @@ func (a *app) Run(ctx context.Context, rs *RunState) error { // send payload if err = a.shim.Serve(shimSetupCtx, &shim.Payload{ - Argv: a.seal.command, - Exec: shimExec, - Bwrap: a.seal.sys.bwrap, - Home: a.seal.sys.user.data, - Syscall: a.seal.scmp, + Argv: a.seal.command, + Exec: shimExec, + Bwrap: a.seal.sys.bwrap, + Home: a.seal.sys.user.data, Verbose: fmsg.Verbose(), }); err != nil { diff --git a/internal/proc/priv/shim/export.c b/internal/proc/priv/shim/export.c deleted file mode 100644 index 404dd135..00000000 --- a/internal/proc/priv/shim/export.c +++ /dev/null @@ -1,254 +0,0 @@ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE // CLONE_NEWUSER -#endif - -#include "export.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if (SCMP_VER_MAJOR < 2) || \ - (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 5) || \ - (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR == 5 && SCMP_VER_MICRO < 1) -#error This package requires libseccomp >= v2.5.1 -#endif - -struct f_syscall_act { - int syscall; - int m_errno; - struct scmp_arg_cmp *arg; -}; - -#define LEN(arr) (sizeof(arr) / sizeof((arr)[0])) - -#define SECCOMP_RULESET_ADD(ruleset) do { \ - F_println("adding seccomp ruleset \"" #ruleset "\""); \ - for (int i = 0; i < LEN(ruleset); i++) { \ - assert(ruleset[i].m_errno == EPERM || ruleset[i].m_errno == ENOSYS); \ - \ - if (ruleset[i].arg) \ - ret = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ruleset[i].m_errno), ruleset[i].syscall, 1, *ruleset[i].arg); \ - else \ - ret = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ruleset[i].m_errno), ruleset[i].syscall, 0); \ - \ - if (ret == -EFAULT) { \ - res = 4; \ - goto out; \ - } else if (ret < 0) { \ - res = 5; \ - errno = -ret; \ - goto out; \ - } \ - } \ -} while (0) - - -int f_tmpfile_fd() { - FILE *f = tmpfile(); - if (f == NULL) - return -1; - return fileno(f); -} - -int32_t f_export_bpf(int fd, uint32_t arch, uint32_t multiarch, f_syscall_opts opts) { - int32_t res = 0; // refer to resErr for meaning - int allow_multiarch = opts & F_MULTIARCH; - int allowed_personality = PER_LINUX; - - if (opts & F_LINUX32) - allowed_personality = PER_LINUX32; - - // flatpak commit 4c3bf179e2e4a2a298cd1db1d045adaf3f564532 - - struct f_syscall_act deny_common[] = { - // Block dmesg - {SCMP_SYS(syslog), EPERM}, - // Useless old syscall - {SCMP_SYS(uselib), EPERM}, - // Don't allow disabling accounting - {SCMP_SYS(acct), EPERM}, - // Don't allow reading current quota use - {SCMP_SYS(quotactl), EPERM}, - - // Don't allow access to the kernel keyring - {SCMP_SYS(add_key), EPERM}, - {SCMP_SYS(keyctl), EPERM}, - {SCMP_SYS(request_key), EPERM}, - - // Scary VM/NUMA ops - {SCMP_SYS(move_pages), EPERM}, - {SCMP_SYS(mbind), EPERM}, - {SCMP_SYS(get_mempolicy), EPERM}, - {SCMP_SYS(set_mempolicy), EPERM}, - {SCMP_SYS(migrate_pages), EPERM}, - }; - - struct f_syscall_act deny_ns[] = { - // Don't allow subnamespace setups: - {SCMP_SYS(unshare), EPERM}, - {SCMP_SYS(setns), EPERM}, - {SCMP_SYS(mount), EPERM}, - {SCMP_SYS(umount), EPERM}, - {SCMP_SYS(umount2), EPERM}, - {SCMP_SYS(pivot_root), EPERM}, - {SCMP_SYS(chroot), EPERM}, -#if defined(__s390__) || defined(__s390x__) || defined(__CRIS__) - // Architectures with CONFIG_CLONE_BACKWARDS2: the child stack - // and flags arguments are reversed so the flags come second - {SCMP_SYS(clone), EPERM, &SCMP_A1(SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)}, -#else - // Normally the flags come first - {SCMP_SYS(clone), EPERM, &SCMP_A0(SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)}, -#endif - - // seccomp can't look into clone3()'s struct clone_args to check whether - // the flags are OK, so we have no choice but to block clone3(). - // Return ENOSYS so user-space will fall back to clone(). - // (CVE-2021-41133; see also https://github.com/moby/moby/commit/9f6b562d) - {SCMP_SYS(clone3), ENOSYS}, - - // New mount manipulation APIs can also change our VFS. There's no - // legitimate reason to do these in the sandbox, so block all of them - // rather than thinking about which ones might be dangerous. - // (CVE-2021-41133) - {SCMP_SYS(open_tree), ENOSYS}, - {SCMP_SYS(move_mount), ENOSYS}, - {SCMP_SYS(fsopen), ENOSYS}, - {SCMP_SYS(fsconfig), ENOSYS}, - {SCMP_SYS(fsmount), ENOSYS}, - {SCMP_SYS(fspick), ENOSYS}, - {SCMP_SYS(mount_setattr), ENOSYS}, - }; - - struct f_syscall_act deny_tty[] = { - // Don't allow faking input to the controlling tty (CVE-2017-5226) - {SCMP_SYS(ioctl), EPERM, &SCMP_A1(SCMP_CMP_MASKED_EQ, 0xFFFFFFFFu, (int)TIOCSTI)}, - // In the unlikely event that the controlling tty is a Linux virtual - // console (/dev/tty2 or similar), copy/paste operations have an effect - // similar to TIOCSTI (CVE-2023-28100) - {SCMP_SYS(ioctl), EPERM, &SCMP_A1(SCMP_CMP_MASKED_EQ, 0xFFFFFFFFu, (int)TIOCLINUX)}, - }; - - struct f_syscall_act deny_devel[] = { - // Profiling operations; we expect these to be done by tools from outside - // the sandbox. In particular perf has been the source of many CVEs. - {SCMP_SYS(perf_event_open), EPERM}, - // Don't allow you to switch to bsd emulation or whatnot - {SCMP_SYS(personality), EPERM, &SCMP_A0(SCMP_CMP_NE, allowed_personality)}, - - {SCMP_SYS(ptrace), EPERM} - }; - - // Blocklist all but unix, inet, inet6 and netlink - struct - { - int family; - f_syscall_opts flags_mask; - } socket_family_allowlist[] = { - // NOTE: Keep in numerical order - { AF_UNSPEC, 0 }, - { AF_LOCAL, 0 }, - { AF_INET, 0 }, - { AF_INET6, 0 }, - { AF_NETLINK, 0 }, - { AF_CAN, F_CAN }, - { AF_BLUETOOTH, F_BLUETOOTH }, - }; - - scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW); - if (ctx == NULL) { - res = 1; - goto out; - } else - errno = 0; - - int ret; - - // We only really need to handle arches on multiarch systems. - // If only one arch is supported the default is fine - if (arch != 0) { - // This *adds* the target arch, instead of replacing the - // native one. This is not ideal, because we'd like to only - // allow the target arch, but we can't really disallow the - // native arch at this point, because then bubblewrap - // couldn't continue running. - ret = seccomp_arch_add(ctx, arch); - if (ret < 0 && ret != -EEXIST) { - res = 2; - errno = -ret; - goto out; - } - - if (allow_multiarch && multiarch != 0) { - ret = seccomp_arch_add(ctx, multiarch); - if (ret < 0 && ret != -EEXIST) { - res = 3; - errno = -ret; - goto out; - } - } - } - - SECCOMP_RULESET_ADD(deny_common); - if (opts & F_DENY_NS) SECCOMP_RULESET_ADD(deny_ns); - if (opts & F_DENY_TTY) SECCOMP_RULESET_ADD(deny_tty); - if (opts & F_DENY_DEVEL) SECCOMP_RULESET_ADD(deny_devel); - - if (!allow_multiarch) { - F_println("disabling modify_ldt"); - - // modify_ldt is a historic source of interesting information leaks, - // so it's disabled as a hardening measure. - // However, it is required to run old 16-bit applications - // as well as some Wine patches, so it's allowed in multiarch. - ret = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(modify_ldt), 0); - - // See above for the meaning of EFAULT. - if (ret == -EFAULT) { - // call fmsg here? - res = 4; - goto out; - } else if (ret < 0) { - res = 5; - errno = -ret; - goto out; - } - } - - // Socket filtering doesn't work on e.g. i386, so ignore failures here - // However, we need to user seccomp_rule_add_exact to avoid libseccomp doing - // something else: https://github.com/seccomp/libseccomp/issues/8 - int last_allowed_family = -1; - for (int i = 0; i < LEN(socket_family_allowlist); i++) { - if (socket_family_allowlist[i].flags_mask != 0 && - (socket_family_allowlist[i].flags_mask & opts) != socket_family_allowlist[i].flags_mask) - continue; - - for (int disallowed = last_allowed_family + 1; disallowed < socket_family_allowlist[i].family; disallowed++) { - // Blocklist the in-between valid families - seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EAFNOSUPPORT), SCMP_SYS(socket), 1, SCMP_A0(SCMP_CMP_EQ, disallowed)); - } - last_allowed_family = socket_family_allowlist[i].family; - } - // Blocklist the rest - seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EAFNOSUPPORT), SCMP_SYS(socket), 1, SCMP_A0(SCMP_CMP_GE, last_allowed_family + 1)); - - ret = seccomp_export_bpf(ctx, fd); - if (ret != 0) { - res = 6; - errno = -ret; - goto out; - } - -out: - if (ctx) - seccomp_release(ctx); - - return res; -} diff --git a/internal/proc/priv/shim/export.h b/internal/proc/priv/shim/export.h deleted file mode 100644 index 5df0dc65..00000000 --- a/internal/proc/priv/shim/export.h +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include - -#if (SCMP_VER_MAJOR < 2) || \ - (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 5) || \ - (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR == 5 && SCMP_VER_MICRO < 1) -#error This package requires libseccomp >= v2.5.1 -#endif - -typedef enum { - F_DENY_NS = 1 << 0, - F_DENY_TTY = 1 << 1, - F_DENY_DEVEL = 1 << 2, - F_MULTIARCH = 1 << 3, - F_LINUX32 = 1 << 4, - F_CAN = 1 << 5, - F_BLUETOOTH = 1 << 6, -} f_syscall_opts; - -extern void F_println(char *v); -int f_tmpfile_fd(); -int32_t f_export_bpf(int fd, uint32_t arch, uint32_t multiarch, f_syscall_opts opts); \ No newline at end of file diff --git a/internal/proc/priv/shim/main.go b/internal/proc/priv/shim/main.go index a92a3cfa..85c5bafe 100644 --- a/internal/proc/priv/shim/main.go +++ b/internal/proc/priv/shim/main.go @@ -2,8 +2,6 @@ package shim import ( "errors" - "flag" - "io" "os" "path" "strconv" @@ -20,7 +18,7 @@ import ( // everything beyond this point runs as unconstrained target user // proceed with caution! -func Main(args []string) { +func Main() { // sharing stdout with fortify // USE WITH CAUTION fmsg.SetPrefix("shim") @@ -31,46 +29,6 @@ func Main(args []string) { panic("unreachable") } - set := flag.NewFlagSet("shim", flag.ExitOnError) - - // debug: export seccomp filter - debugExportSeccomp := set.String("export-seccomp", "", "export the seccomp filter to file") - debugExportSeccompFlags := [...]struct { - o syscallOpts - v *bool - }{ - {flagDenyNS, set.Bool("deny-ns", false, "deny namespace-related syscalls")}, - {flagDenyTTY, set.Bool("deny-tty", false, "deny faking input ioctls")}, - {flagDenyDevel, set.Bool("deny-devel", false, "deny development syscalls")}, - {flagMultiarch, set.Bool("multiarch", false, "allow multiarch")}, - {flagLinux32, set.Bool("linux32", false, "allow PER_LINUX32")}, - {flagCan, set.Bool("can", false, "allow AF_CAN")}, - {flagBluetooth, set.Bool("bluetooth", false, "AF_BLUETOOTH")}, - } - - // Ignore errors; set is set for ExitOnError. - _ = set.Parse(args[1:]) - - // debug: export seccomp filter - if *debugExportSeccomp != "" { - var opts syscallOpts - for _, opt := range debugExportSeccompFlags { - if *opt.v { - opts |= opt.o - } - } - - if f, err := os.Create(*debugExportSeccomp); err != nil { - fmsg.Fatalf("cannot create %q: %v", *debugExportSeccomp, err) - } else { - mustExportFilter(f, opts) - if err = f.Close(); err != nil { - fmsg.Fatalf("cannot close %q: %v", *debugExportSeccomp, err) - } - } - fmsg.Exit(0) - } - // receive setup payload var ( payload Payload @@ -169,23 +127,19 @@ func Main(args []string) { conf.Symlink("fortify", innerInit) helper.BubblewrapName = payload.Exec[0] // resolved bwrap path by parent + if fmsg.Verbose() { + bwrap.CPrintln = fmsg.Println + } if b, err := helper.NewBwrap( conf, innerInit, nil, func(int, int) []string { return make([]string, 0) }, - []helper.BwrapExtraFile{ - // keep this fd open while sandbox is running - // (--sync-fd FD) - {"--sync-fd", syncFd}, - // load and use seccomp rules from FD (not repeatable) - // (--seccomp FD) - {"--seccomp", mustResolveSeccomp(payload.Bwrap, payload.Syscall)}, - }, + extraFiles, + syncFd, ); err != nil { fmsg.Fatalf("malformed sandbox config: %v", err) } else { cmd := b.Unwrap() cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr - cmd.ExtraFiles = extraFiles // run and pass through exit code if err = b.Start(); err != nil { @@ -200,65 +154,3 @@ func Main(args []string) { } } } - -func mustResolveSeccomp(bwrap *bwrap.Config, syscall *fst.SyscallConfig) (seccompFd *os.File) { - if syscall == nil { - fmsg.VPrintln("syscall filter not configured, PROCEED WITH CAUTION") - return - } - - // resolve seccomp filter opts - var ( - opts syscallOpts - optd []string - optCond = [...]struct { - v bool - o syscallOpts - d string - }{ - {!bwrap.UserNS, flagDenyNS, "denyns"}, - {bwrap.NewSession, flagDenyTTY, "denytty"}, - {syscall.DenyDevel, flagDenyDevel, "denydevel"}, - {syscall.Multiarch, flagMultiarch, "multiarch"}, - {syscall.Linux32, flagLinux32, "linux32"}, - {syscall.Can, flagCan, "can"}, - {syscall.Bluetooth, flagBluetooth, "bluetooth"}, - } - ) - if fmsg.Verbose() { - optd = make([]string, 1, len(optCond)+1) - optd[0] = "fortify" - } - for _, opt := range optCond { - if opt.v { - opts |= opt.o - if fmsg.Verbose() { - optd = append(optd, opt.d) - } - } - } - if fmsg.Verbose() { - fmsg.VPrintf("seccomp flags: %s", optd) - } - - // export seccomp filter to tmpfile - if f, err := tmpfile(); err != nil { - fmsg.Fatalf("cannot create tmpfile: %v", err) - panic("unreachable") - } else { - mustExportFilter(f, opts) - seccompFd = f - return - } -} - -func mustExportFilter(f *os.File, opts syscallOpts) { - if err := exportFilter(f.Fd(), opts); err != nil { - fmsg.Fatalf("cannot export seccomp filter: %v", err) - panic("unreachable") - } - if _, err := f.Seek(0, io.SeekStart); err != nil { - fmsg.Fatalf("cannot lseek seccomp file: %v", err) - panic("unreachable") - } -} diff --git a/internal/proc/priv/shim/payload.go b/internal/proc/priv/shim/payload.go index 2d6854f9..e659e3fd 100644 --- a/internal/proc/priv/shim/payload.go +++ b/internal/proc/priv/shim/payload.go @@ -1,7 +1,6 @@ package shim import ( - "git.gensokyo.uk/security/fortify/fst" "git.gensokyo.uk/security/fortify/helper/bwrap" ) @@ -18,8 +17,6 @@ type Payload struct { Home string // sync fd Sync *uintptr - // seccomp opts pass through - Syscall *fst.SyscallConfig // verbosity pass through Verbose bool diff --git a/internal/proc/priv/shim/seccomp.go b/internal/proc/priv/shim/seccomp.go deleted file mode 100644 index bf458742..00000000 --- a/internal/proc/priv/shim/seccomp.go +++ /dev/null @@ -1,81 +0,0 @@ -package shim - -/* -#cgo linux pkg-config: --static libseccomp - -#include "export.h" -*/ -import "C" -import ( - "errors" - "fmt" - "os" - "runtime" - - "git.gensokyo.uk/security/fortify/internal/fmsg" -) - -var resErr = [...]error{ - 0: nil, - 1: errors.New("seccomp_init failed"), - 2: errors.New("seccomp_arch_add failed"), - 3: errors.New("seccomp_arch_add failed (multiarch)"), - 4: errors.New("internal libseccomp failure"), - 5: errors.New("seccomp_rule_add failed"), - 6: errors.New("seccomp_export_bpf failed"), -} - -type ( - syscallOpts = C.f_syscall_opts -) - -const ( - flagDenyNS syscallOpts = C.F_DENY_NS - flagDenyTTY syscallOpts = C.F_DENY_TTY - flagDenyDevel syscallOpts = C.F_DENY_DEVEL - flagMultiarch syscallOpts = C.F_MULTIARCH - flagLinux32 syscallOpts = C.F_LINUX32 - flagCan syscallOpts = C.F_CAN - flagBluetooth syscallOpts = C.F_BLUETOOTH -) - -func tmpfile() (*os.File, error) { - fd, err := C.f_tmpfile_fd() - if err != nil { - return nil, err - } - return os.NewFile(uintptr(fd), "tmpfile"), err -} - -func exportFilter(fd uintptr, opts syscallOpts) error { - var ( - arch C.uint32_t = 0 - multiarch C.uint32_t = 0 - ) - switch runtime.GOARCH { - case "386": - arch = C.SCMP_ARCH_X86 - case "amd64": - arch = C.SCMP_ARCH_X86_64 - multiarch = C.SCMP_ARCH_X86 - case "arm": - arch = C.SCMP_ARCH_ARM - case "arm64": - arch = C.SCMP_ARCH_AARCH64 - multiarch = C.SCMP_ARCH_ARM - } - - res, err := C.f_export_bpf(C.int(fd), arch, multiarch, opts) - if re := resErr[res]; re != nil { - if err == nil { - return re - } - return fmt.Errorf("%s: %v", re.Error(), err) - } - return err -} - -//export F_println -func F_println(v *C.char) { - fmsg.VPrintln(C.GoString(v)) -} diff --git a/internal/system/dbus.go b/internal/system/dbus.go index 3bed540a..b74d5d30 100644 --- a/internal/system/dbus.go +++ b/internal/system/dbus.go @@ -99,7 +99,7 @@ func (d *DBus) apply(_ *I) error { } fmsg.VPrintln("starting message bus proxy:", d.proxy) if fmsg.Verbose() { // save the extra bwrap arg build when verbose logging is off - fmsg.VPrintln("message bus proxy bwrap args:", d.proxy.Bwrap()) + fmsg.VPrintln("message bus proxy bwrap args:", d.proxy.BwrapStatic()) } // background wait for proxy instance and notify completion diff --git a/ldd/exec.go b/ldd/exec.go index eec31e47..23c4e892 100644 --- a/ldd/exec.go +++ b/ldd/exec.go @@ -23,7 +23,8 @@ func Exec(p string) ([]*Entry, error) { NewSession: true, DieWithParent: true, }).Bind("/", "/").DevTmpfs("/dev"), "ldd", - nil, func(_, _ int) []string { return []string{p} }, nil, + nil, func(_, _ int) []string { return []string{p} }, + nil, nil, ); err != nil { return nil, err } else { diff --git a/main.go b/main.go index 8909e5b4..e6e856b6 100644 --- a/main.go +++ b/main.go @@ -291,7 +291,7 @@ func main() { // internal commands case "shim": - shim.Main(args) + shim.Main() fmsg.Exit(0) case "init": init0.Main() -- cgit v1.3.1