diff --git a/internal/app/app_pd_test.go b/internal/app/app_pd_test.go index 4d90d7f..b5cf46b 100644 --- a/internal/app/app_pd_test.go +++ b/internal/app/app_pd_test.go @@ -1,6 +1,8 @@ package app_test import ( + "os" + "git.gensokyo.uk/security/fortify/acl" "git.gensokyo.uk/security/fortify/dbus" "git.gensokyo.uk/security/fortify/fst" @@ -217,7 +219,7 @@ var testCasesPd = []sealTestCase{ Ensure("/run/user/1971", 0700).UpdatePermType(system.User, "/run/user/1971", acl.Execute). // this is ordered as is because the previous Ensure only calls mkdir if XDG_RUNTIME_DIR is unset Ephemeral(system.Process, "/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c", 0700).UpdatePermType(system.Process, "/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c", acl.Execute). Ensure("/tmp/fortify.1971/wayland", 0711). - Wayland("/tmp/fortify.1971/wayland/ebf083d1b175911782d413369b64ce7c", "/run/user/1971/wayland-0", "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c"). + Wayland(new(*os.File), "/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(new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 256, 256). MustProxyDBus("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/bus", &dbus.Config{ diff --git a/internal/app/share.go b/internal/app/share.go index 1862fb2..beed4f0 100644 --- a/internal/app/share.go +++ b/internal/app/share.go @@ -163,7 +163,7 @@ func (seal *appSeal) setupShares(bus [2]*dbus.Config, os linux.System) error { // use instance ID in case app id is not set appID = "uk.gensokyo.fortify." + seal.id } - seal.sys.Wayland(outerPath, socketPath, appID, seal.id) + seal.sys.Wayland(&seal.sys.sp, outerPath, socketPath, appID, seal.id) seal.sys.bwrap.Bind(outerPath, innerPath) } else { // bind mount wayland socket (insecure) fmsg.Verbose("direct wayland access, PROCEED WITH CAUTION") diff --git a/internal/app/start.go b/internal/app/start.go index 467735e..8390b6b 100644 --- a/internal/app/start.go +++ b/internal/app/start.go @@ -58,7 +58,7 @@ func (a *app) Run(ctx context.Context, rs *RunState) error { if startTime, err := a.shim.Start( a.seal.sys.user.as, a.seal.sys.user.supp, - a.seal.sys.Sync(), + a.seal.sys.sp, ); err != nil { return err } else { diff --git a/internal/app/system.go b/internal/app/system.go index 241eafa..f11efc8 100644 --- a/internal/app/system.go +++ b/internal/app/system.go @@ -1,6 +1,8 @@ package app import ( + "os" + "git.gensokyo.uk/security/fortify/helper/bwrap" "git.gensokyo.uk/security/fortify/internal/system" ) @@ -8,6 +10,8 @@ import ( // appSealSys encapsulates app seal behaviour with OS interactions type appSealSys struct { bwrap *bwrap.Config + // bwrap sync fd + sp *os.File // paths to override by mounting tmpfs over them override []string diff --git a/internal/system/op.go b/internal/system/op.go index 08b5794..42f93d4 100644 --- a/internal/system/op.go +++ b/internal/system/op.go @@ -4,7 +4,6 @@ import ( "context" "errors" "log" - "os" "sync" "git.gensokyo.uk/security/fortify/internal/fmsg" @@ -60,8 +59,6 @@ type I struct { uid int ops []Op ctx context.Context - // sync fd passed to bwrap - sp *os.File // whether sys has been reverted state bool @@ -73,10 +70,6 @@ func (sys *I) UID() int { return sys.uid } -func (sys *I) Sync() *os.File { - return sys.sp -} - func (sys *I) Equal(v *I) bool { if v == nil || sys.uid != v.uid || len(sys.ops) != len(v.ops) { return false diff --git a/internal/system/wayland.go b/internal/system/wayland.go index ac3634a..f381bd2 100644 --- a/internal/system/wayland.go +++ b/internal/system/wayland.go @@ -11,79 +11,80 @@ import ( ) // Wayland sets up a wayland socket with a security context attached. -func (sys *I) Wayland(dst, src, appID, instanceID string) *I { +func (sys *I) Wayland(syncFd **os.File, dst, src, appID, instanceID string) *I { sys.lock.Lock() defer sys.lock.Unlock() - sys.ops = append(sys.ops, Wayland{[2]string{dst, src}, new(wl.Conn), appID, instanceID}) + sys.ops = append(sys.ops, &Wayland{syncFd, dst, src, appID, instanceID, wl.Conn{}}) return sys } type Wayland struct { - pair [2]string - conn *wl.Conn - + sync **os.File + dst, src string appID, instanceID string + + conn wl.Conn } -func (w Wayland) Type() Enablement { - return Process -} +func (w *Wayland) Type() Enablement { return Process } + +func (w *Wayland) apply(sys *I) error { + if w.sync == nil { + // this is a misuse of the API; do not return an error message + return errors.New("invalid sync") + } -func (w Wayland) apply(sys *I) error { // the Wayland op is not repeatable - if sys.sp != nil { + if *w.sync != nil { + // this is a misuse of the API; do not return an error message return errors.New("attempted to attach multiple wayland sockets") } - if err := w.conn.Attach(w.pair[1]); err != nil { + if err := w.conn.Attach(w.src); err != nil { // make console output less nasty if errors.Is(err, os.ErrNotExist) { err = os.ErrNotExist } return fmsg.WrapErrorSuffix(err, - fmt.Sprintf("cannot attach to wayland on %q:", w.pair[1])) + fmt.Sprintf("cannot attach to wayland on %q:", w.src)) } else { - fmsg.Verbosef("wayland attached on %q", w.pair[1]) + fmsg.Verbosef("wayland attached on %q", w.src) } - if sp, err := w.conn.Bind(w.pair[0], w.appID, w.instanceID); err != nil { + if sp, err := w.conn.Bind(w.dst, w.appID, w.instanceID); err != nil { return fmsg.WrapErrorSuffix(err, - fmt.Sprintf("cannot bind to socket on %q:", w.pair[0])) + fmt.Sprintf("cannot bind to socket on %q:", w.dst)) } else { - sys.sp = sp - fmsg.Verbosef("wayland listening on %q", w.pair[0]) - return fmsg.WrapErrorSuffix(errors.Join(os.Chmod(w.pair[0], 0), acl.UpdatePerm(w.pair[0], sys.uid, acl.Read, acl.Write, acl.Execute)), - fmt.Sprintf("cannot chmod socket on %q:", w.pair[0])) + *w.sync = sp + fmsg.Verbosef("wayland listening on %q", w.dst) + return fmsg.WrapErrorSuffix(errors.Join(os.Chmod(w.dst, 0), acl.UpdatePerm(w.dst, sys.uid, acl.Read, acl.Write, acl.Execute)), + fmt.Sprintf("cannot chmod socket on %q:", w.dst)) } } -func (w Wayland) revert(_ *I, ec *Criteria) error { +func (w *Wayland) revert(_ *I, ec *Criteria) error { if ec.hasType(w) { - fmsg.Verbosef("removing wayland socket on %q", w.pair[0]) - if err := os.Remove(w.pair[0]); err != nil && !errors.Is(err, os.ErrNotExist) { + fmsg.Verbosef("removing wayland socket on %q", w.dst) + if err := os.Remove(w.dst); err != nil && !errors.Is(err, os.ErrNotExist) { return err } - fmsg.Verbosef("detaching from wayland on %q", w.pair[1]) + fmsg.Verbosef("detaching from wayland on %q", w.src) return fmsg.WrapErrorSuffix(w.conn.Close(), - fmt.Sprintf("cannot detach from wayland on %q:", w.pair[1])) + fmt.Sprintf("cannot detach from wayland on %q:", w.src)) } else { - fmsg.Verbosef("skipping wayland cleanup on %q", w.pair[0]) + fmsg.Verbosef("skipping wayland cleanup on %q", w.dst) return nil } } -func (w Wayland) Is(o Op) bool { - w0, ok := o.(Wayland) - return ok && w.pair == w0.pair +func (w *Wayland) Is(o Op) bool { + w0, ok := o.(*Wayland) + return ok && w.dst == w0.dst && w.src == w0.src && + w.appID == w0.appID && w.instanceID == w0.instanceID } -func (w Wayland) Path() string { - return w.pair[0] -} - -func (w Wayland) String() string { - return fmt.Sprintf("wayland socket at %q", w.pair[0]) -} +func (w *Wayland) Path() string { return w.dst } +func (w *Wayland) String() string { return fmt.Sprintf("wayland socket at %q", w.dst) }