aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--cmd/finit/ipc/payload.go2
-rw-r--r--cmd/finit/main.go8
-rw-r--r--cmd/fshim/ipc/payload.go13
-rw-r--r--cmd/fshim/ipc/shim/shim.go18
-rw-r--r--cmd/fshim/ipc/wayland.go75
-rw-r--r--cmd/fshim/main.go49
-rw-r--r--internal/app/app_nixos_test.go4
-rw-r--r--internal/app/app_pd_test.go6
-rw-r--r--internal/app/config.go18
-rw-r--r--internal/app/seal.go12
-rw-r--r--internal/app/share.display.go25
-rw-r--r--internal/app/start.go12
12 files changed, 57 insertions, 185 deletions
diff --git a/cmd/finit/ipc/payload.go b/cmd/finit/ipc/payload.go
index 535a0e70..d1dc9ec3 100644
--- a/cmd/finit/ipc/payload.go
+++ b/cmd/finit/ipc/payload.go
@@ -7,8 +7,6 @@ type Payload struct {
Argv0 string
// child full argv
Argv []string
- // wayland fd, -1 to disable
- WL int
// verbosity pass through
Verbose bool
diff --git a/cmd/finit/main.go b/cmd/finit/main.go
index da3eaea3..b9744e0f 100644
--- a/cmd/finit/main.go
+++ b/cmd/finit/main.go
@@ -92,14 +92,6 @@ func main() {
cmd.Args = payload.Argv
cmd.Env = os.Environ()
- // pass wayland fd
- if payload.WL != -1 {
- if f := os.NewFile(uintptr(payload.WL), "wayland"); f != nil {
- cmd.Env = append(cmd.Env, "WAYLAND_SOCKET="+strconv.Itoa(3+len(cmd.ExtraFiles)))
- cmd.ExtraFiles = append(cmd.ExtraFiles, f)
- }
- }
-
if err := cmd.Start(); err != nil {
fmsg.Fatalf("cannot start %q: %v", payload.Argv0, err)
}
diff --git a/cmd/fshim/ipc/payload.go b/cmd/fshim/ipc/payload.go
index 1aaddb1f..f6b0c926 100644
--- a/cmd/fshim/ipc/payload.go
+++ b/cmd/fshim/ipc/payload.go
@@ -2,7 +2,6 @@ package shim0
import (
"encoding/gob"
- "errors"
"net"
"git.ophivana.moe/security/fortify/helper/bwrap"
@@ -18,25 +17,19 @@ type Payload struct {
Exec [2]string
// bwrap config
Bwrap *bwrap.Config
- // whether to pass wayland fd
- WL bool
+ // sync fd
+ Sync *uintptr
// verbosity pass through
Verbose bool
}
-func (p *Payload) Serve(conn *net.UnixConn, wl *Wayland) error {
+func (p *Payload) Serve(conn *net.UnixConn) error {
if err := gob.NewEncoder(conn).Encode(*p); err != nil {
return fmsg.WrapErrorSuffix(err,
"cannot stream shim payload:")
}
- if wl != nil {
- if err := wl.WriteUnix(conn); err != nil {
- return errors.Join(err, conn.Close())
- }
- }
-
return fmsg.WrapErrorSuffix(conn.Close(),
"cannot close setup connection:")
}
diff --git a/cmd/fshim/ipc/shim/shim.go b/cmd/fshim/ipc/shim/shim.go
index db4a9b09..d01d02e8 100644
--- a/cmd/fshim/ipc/shim/shim.go
+++ b/cmd/fshim/ipc/shim/shim.go
@@ -39,14 +39,12 @@ type Shim struct {
abortOnce sync.Once
// fallback exit notifier with error returned killing the process
killFallback chan error
- // wayland mediation, nil if disabled
- wl *shim0.Wayland
// shim setup payload
payload *shim0.Payload
}
-func New(uid uint32, aid string, supp []string, socket string, wl *shim0.Wayland, payload *shim0.Payload) *Shim {
- return &Shim{uid: uid, aid: aid, supp: supp, socket: socket, wl: wl, payload: payload}
+func New(uid uint32, aid string, supp []string, socket string, payload *shim0.Payload) *Shim {
+ return &Shim{uid: uid, aid: aid, supp: supp, socket: socket, payload: payload}
}
func (s *Shim) String() string {
@@ -112,6 +110,14 @@ func (s *Shim) Start() (*time.Time, error) {
}
s.cmd.Stdin, s.cmd.Stdout, s.cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
s.cmd.Dir = "/"
+
+ // pass sync fd if set
+ if s.payload.Bwrap.Sync() != nil {
+ fd := uintptr(3 + len(s.cmd.ExtraFiles))
+ s.payload.Sync = &fd
+ s.cmd.ExtraFiles = append(s.cmd.ExtraFiles, s.payload.Bwrap.Sync())
+ }
+
fmsg.VPrintln("starting shim via fsu:", s.cmd)
fmsg.Suspend() // withhold messages to stderr
if err := s.cmd.Start(); err != nil {
@@ -172,9 +178,9 @@ func (s *Shim) Start() (*time.Time, error) {
return &startTime, err
}
- // serve payload and wayland fd if enabled
+ // serve payload
// this also closes the connection
- err := s.payload.Serve(conn, s.wl)
+ err := s.payload.Serve(conn)
if err == nil {
killShim = func() {}
}
diff --git a/cmd/fshim/ipc/wayland.go b/cmd/fshim/ipc/wayland.go
deleted file mode 100644
index 132e74f4..00000000
--- a/cmd/fshim/ipc/wayland.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package shim0
-
-import (
- "fmt"
- "net"
- "sync"
- "syscall"
-
- "git.ophivana.moe/security/fortify/internal/fmsg"
-)
-
-// Wayland implements wayland mediation.
-type Wayland struct {
- // wayland socket path
- Path string
-
- // wayland connection
- conn *net.UnixConn
-
- connErr error
- sync.Once
- // wait for wayland client to exit
- done chan struct{}
-}
-
-func (wl *Wayland) WriteUnix(conn *net.UnixConn) error {
- // connect to host wayland socket
- if f, err := net.DialUnix("unix", nil, &net.UnixAddr{Name: wl.Path, Net: "unix"}); err != nil {
- return fmsg.WrapErrorSuffix(err,
- fmt.Sprintf("cannot connect to wayland at %q:", wl.Path))
- } else {
- fmsg.VPrintf("connected to wayland at %q", wl.Path)
- wl.conn = f
- }
-
- // set up for passing wayland socket
- if rc, err := wl.conn.SyscallConn(); err != nil {
- return fmsg.WrapErrorSuffix(err, "cannot obtain raw wayland connection:")
- } else {
- ec := make(chan error)
- go func() {
- // pass wayland connection fd
- if err = rc.Control(func(fd uintptr) {
- if _, _, err = conn.WriteMsgUnix(nil, syscall.UnixRights(int(fd)), nil); err != nil {
- ec <- fmsg.WrapErrorSuffix(err, "cannot pass wayland connection to shim:")
- return
- }
- ec <- nil
-
- // block until shim exits
- <-wl.done
- fmsg.VPrintln("releasing wayland connection")
- }); err != nil {
- ec <- fmsg.WrapErrorSuffix(err, "cannot obtain wayland connection fd:")
- return
- }
- }()
- return <-ec
- }
-}
-
-func (wl *Wayland) Close() error {
- wl.Do(func() {
- close(wl.done)
- wl.connErr = wl.conn.Close()
- })
-
- return wl.connErr
-}
-
-func NewWayland() *Wayland {
- wl := new(Wayland)
- wl.done = make(chan struct{})
- return wl
-}
diff --git a/cmd/fshim/main.go b/cmd/fshim/main.go
index 2322984a..222419e3 100644
--- a/cmd/fshim/main.go
+++ b/cmd/fshim/main.go
@@ -2,7 +2,6 @@ package main
import (
"encoding/gob"
- "errors"
"net"
"os"
"path"
@@ -76,14 +75,9 @@ func main() {
fmsg.Fatal("bwrap config not supplied")
}
- // receive wayland fd over socket
- wfd := -1
- if payload.WL {
- if fd, err := receiveWLfd(conn); err != nil {
- fmsg.Fatalf("cannot receive wayland fd: %v", err)
- } else {
- wfd = fd
- }
+ // restore bwrap sync fd
+ if payload.Sync != nil {
+ payload.Bwrap.SetSync(os.NewFile(*payload.Sync, "sync"))
}
// close setup socket
@@ -116,16 +110,6 @@ func main() {
var extraFiles []*os.File
- // pass wayland fd
- if wfd != -1 {
- if f := os.NewFile(uintptr(wfd), "wayland"); f != nil {
- ic.WL = 3 + len(extraFiles)
- extraFiles = append(extraFiles, f)
- }
- } else {
- ic.WL = -1
- }
-
// share config pipe
if r, w, err := os.Pipe(); err != nil {
fmsg.Fatalf("cannot pipe: %v", err)
@@ -168,30 +152,3 @@ func main() {
}
}
}
-
-func receiveWLfd(conn *net.UnixConn) (int, error) {
- oob := make([]byte, syscall.CmsgSpace(4)) // single fd
-
- if _, oobn, _, _, err := conn.ReadMsgUnix(nil, oob); err != nil {
- return -1, err
- } else if len(oob) != oobn {
- return -1, errors.New("invalid message length")
- }
-
- var msg syscall.SocketControlMessage
- if messages, err := syscall.ParseSocketControlMessage(oob); err != nil {
- return -1, err
- } else if len(messages) != 1 {
- return -1, errors.New("unexpected message count")
- } else {
- msg = messages[0]
- }
-
- if fds, err := syscall.ParseUnixRights(&msg); err != nil {
- return -1, err
- } else if len(fds) != 1 {
- return -1, errors.New("unexpected fd count")
- } else {
- return fds[0], nil
- }
-}
diff --git a/internal/app/app_nixos_test.go b/internal/app/app_nixos_test.go
index 06bde05c..33380793 100644
--- a/internal/app/app_nixos_test.go
+++ b/internal/app/app_nixos_test.go
@@ -10,7 +10,7 @@ import (
var testCasesNixos = []sealTestCase{
{
- "nixos chromium", new(stubNixOS),
+ "nixos chromium direct wayland", new(stubNixOS),
&app.Config{
ID: "org.chromium.Chromium",
Command: []string{"/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"},
@@ -18,7 +18,7 @@ var testCasesNixos = []sealTestCase{
AppID: 1, Groups: []string{}, Username: "u0_a1",
Outer: "/var/lib/persist/module/fortify/0/1",
Sandbox: &app.SandboxConfig{
- UserNS: true, Net: true, MapRealUID: true, Env: nil,
+ UserNS: true, Net: true, MapRealUID: true, DirectWayland: true, Env: nil,
Filesystem: []*app.FilesystemConfig{
{Src: "/bin", Must: true}, {Src: "/usr/bin", Must: true},
{Src: "/nix/store", Must: true}, {Src: "/run/current-system", Must: true},
diff --git a/internal/app/app_pd_test.go b/internal/app/app_pd_test.go
index 224d2636..a97bbcf7 100644
--- a/internal/app/app_pd_test.go
+++ b/internal/app/app_pd_test.go
@@ -248,8 +248,8 @@ var testCasesPd = []sealTestCase{
Ephemeral(system.Process, "/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c", 0700).UpdatePermType(system.Process, "/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c", acl.Execute).
WriteType(system.Process, "/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/passwd", "chronos:x:65534:65534:Fortify:/home/chronos:/run/current-system/sw/bin/zsh\n").
WriteType(system.Process, "/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/group", "fortify:x:65534:\n").
- Link("/run/user/1971/wayland-0", "/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c/wayland").
- UpdatePermType(system.EWayland, "/run/user/1971/wayland-0", acl.Read, acl.Write, acl.Execute).
+ Ensure("/tmp/fortify.1971/wayland", 0711).
+ Wayland("/tmp/fortify.1971/wayland/ebf083d1b175911782d413369b64ce7c", "/run/user/1971/wayland-0", "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c").
Link("/run/user/1971/pulse/native", "/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c/pulse").
CopyFile("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/pulse-cookie", "/home/ophestra/xdg/config/pulse/cookie").
MustProxyDBus("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/bus", &dbus.Config{
@@ -442,7 +442,7 @@ var testCasesPd = []sealTestCase{
Bind("/home/chronos", "/home/chronos", false, true).
Bind("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/passwd", "/etc/passwd").
Bind("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/group", "/etc/group").
- Bind("/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c/wayland", "/run/user/65534/wayland-0").
+ Bind("/tmp/fortify.1971/wayland/ebf083d1b175911782d413369b64ce7c", "/run/user/65534/wayland-0").
Bind("/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c/pulse", "/run/user/65534/pulse/native").
Bind("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/pulse-cookie", "/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/pulse-cookie").
Bind("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/bus", "/run/user/65534/bus").
diff --git a/internal/app/config.go b/internal/app/config.go
index 298925de..758dcd08 100644
--- a/internal/app/config.go
+++ b/internal/app/config.go
@@ -62,8 +62,8 @@ type SandboxConfig struct {
NoNewSession bool `json:"no_new_session,omitempty"`
// map target user uid to privileged user uid in the user namespace
MapRealUID bool `json:"map_real_uid"`
- // mediated access to wayland socket
- Wayland bool `json:"wayland,omitempty"`
+ // direct access to wayland socket
+ DirectWayland bool `json:"direct_wayland,omitempty"`
// final environment variables
Env map[string]string `json:"env"`
@@ -190,13 +190,13 @@ func Template() *Config {
Outer: "/var/lib/persist/home/org.chromium.Chromium",
Inner: "/var/lib/fortify",
Sandbox: &SandboxConfig{
- Hostname: "localhost",
- UserNS: true,
- Net: true,
- NoNewSession: true,
- MapRealUID: true,
- Dev: true,
- Wayland: false,
+ Hostname: "localhost",
+ UserNS: true,
+ Net: true,
+ NoNewSession: true,
+ MapRealUID: true,
+ Dev: true,
+ DirectWayland: false,
// example API credentials pulled from Google Chrome
// DO NOT USE THESE IN A REAL BROWSER
Env: map[string]string{
diff --git a/internal/app/seal.go b/internal/app/seal.go
index d3c7a875..94c9e061 100644
--- a/internal/app/seal.go
+++ b/internal/app/seal.go
@@ -8,7 +8,6 @@ import (
"regexp"
"strconv"
- shim "git.ophivana.moe/security/fortify/cmd/fshim/ipc"
"git.ophivana.moe/security/fortify/dbus"
"git.ophivana.moe/security/fortify/internal/fmsg"
"git.ophivana.moe/security/fortify/internal/linux"
@@ -29,8 +28,6 @@ var posixUsername = regexp.MustCompilePOSIX("^[a-z_]([A-Za-z0-9_-]{0,31}|[A-Za-z
type appSeal struct {
// app unique ID string representation
id string
- // wayland mediation, disabled if nil
- wl *shim.Wayland
// dbus proxy message buffer retriever
dbusMsg func(f func(msgbuf []string))
@@ -48,6 +45,8 @@ type appSeal struct {
// pass-through enablement tracking from config
et system.Enablements
+ // wayland socket direct access
+ directWayland bool
// prevents sharing from happening twice
shared bool
@@ -204,6 +203,7 @@ func (a *app) Seal(config *Config) error {
config.Confinement.Sandbox = conf
}
+ seal.directWayland = config.Confinement.Sandbox.DirectWayland
if b, err := config.Confinement.Sandbox.Bwrap(a.os); err != nil {
return err
} else {
@@ -214,12 +214,6 @@ func (a *app) Seal(config *Config) error {
seal.sys.bwrap.SetEnv = make(map[string]string)
}
- // create wayland struct and client wait channel if mediated wayland is enabled
- // this field being set enables mediated wayland setup later on
- if config.Confinement.Sandbox.Wayland {
- seal.wl = shim.NewWayland()
- }
-
// open process state store
// the simple store only starts holding an open file after first action
// store activity begins after Start is called and must end before Wait
diff --git a/internal/app/share.display.go b/internal/app/share.display.go
index 801ca54c..b1d98342 100644
--- a/internal/app/share.display.go
+++ b/internal/app/share.display.go
@@ -31,23 +31,36 @@ func (seal *appSeal) shareDisplay(os linux.System) error {
// set up wayland
if seal.et.Has(system.EWayland) {
+ var wp string
if wd, ok := os.LookupEnv(waylandDisplay); !ok {
return fmsg.WrapError(ErrWayland,
"WAYLAND_DISPLAY is not set")
- } else if seal.wl == nil {
+ } else {
+ wp = path.Join(seal.RuntimePath, wd)
+ }
+
+ w := path.Join(seal.sys.runtime, "wayland-0")
+ seal.sys.bwrap.SetEnv[waylandDisplay] = w
+
+ if seal.directWayland {
// hardlink wayland socket
- wp := path.Join(seal.RuntimePath, wd)
wpi := path.Join(seal.shareLocal, "wayland")
- w := path.Join(seal.sys.runtime, "wayland-0")
seal.sys.Link(wp, wpi)
- seal.sys.bwrap.SetEnv[waylandDisplay] = w
seal.sys.bwrap.Bind(wpi, w)
// ensure Wayland socket ACL (e.g. `/run/user/%d/wayland-%d`)
seal.sys.UpdatePermType(system.EWayland, wp, acl.Read, acl.Write, acl.Execute)
} else {
- // set wayland socket path for mediation (e.g. `/run/user/%d/wayland-%d`)
- seal.wl.Path = path.Join(seal.RuntimePath, wd)
+ wc := path.Join(seal.SharePath, "wayland")
+ wt := path.Join(wc, seal.id)
+ seal.sys.Ensure(wc, 0711)
+ appID := seal.fid
+ if appID == "" {
+ // use instance ID in case app id is not set
+ appID = "moe.ophivana.fortify." + seal.id
+ }
+ seal.sys.Wayland(wt, wp, appID, seal.id)
+ seal.sys.bwrap.Bind(wt, w)
}
}
diff --git a/internal/app/start.go b/internal/app/start.go
index c33d26fb..e1247d4c 100644
--- a/internal/app/start.go
+++ b/internal/app/start.go
@@ -47,12 +47,10 @@ func (a *app) Start() error {
a.seal.sys.user.as,
a.seal.sys.user.supp,
path.Join(a.seal.share, "shim"),
- a.seal.wl,
&shim0.Payload{
Argv: a.seal.command,
Exec: shimExec,
Bwrap: a.seal.sys.bwrap,
- WL: a.seal.wl != nil,
Verbose: fmsg.Verbose(),
},
@@ -64,6 +62,9 @@ func (a *app) Start() error {
}
a.seal.sys.needRevert = true
+ // export sync pipe from sys
+ a.seal.sys.bwrap.SetSync(a.seal.sys.Sync())
+
if startTime, err := a.shim.Start(); err != nil {
return err
} else {
@@ -199,13 +200,6 @@ func (a *app) Wait() (int, error) {
})
}
- // close wayland connection
- if a.seal.wl != nil {
- if err := a.seal.wl.Close(); err != nil {
- fmsg.Println("cannot close wayland connection:", err)
- }
- }
-
// update store and revert app setup transaction
e := new(StateStoreError)
e.Inner, e.DoErr = a.seal.store.Do(func(b state.Backend) {