app: move wayland mediation to shim package
All checks were successful
test / test (push) Successful in 41s
All checks were successful
test / test (push) Successful in 41s
Values used in the Wayland mediation implementation is stored in various struct fields strewn across multiple app structs and checks are messy and confusing. This commit unifies them into a single struct and access it using much better looking methods. Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
This commit is contained in:
parent
133f23e0de
commit
362ee73893
@ -1,7 +1,6 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
@ -27,9 +26,6 @@ type app struct {
|
|||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
// child process related information
|
// child process related information
|
||||||
seal *appSeal
|
seal *appSeal
|
||||||
|
|
||||||
// wayland connection if wayland mediation is enabled
|
|
||||||
wayland *net.UnixConn
|
|
||||||
// error returned waiting for process
|
// error returned waiting for process
|
||||||
waitErr error
|
waitErr error
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"git.ophivana.moe/security/fortify/internal/shim"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
@ -36,6 +37,43 @@ var (
|
|||||||
ErrMachineCtl = errors.New("machinectl not available")
|
ErrMachineCtl = errors.New("machinectl not available")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// appSeal seals the application with child-related information
|
||||||
|
type appSeal struct {
|
||||||
|
// app unique ID string representation
|
||||||
|
id string
|
||||||
|
// wayland mediation, disabled if nil
|
||||||
|
wl *shim.Wayland
|
||||||
|
|
||||||
|
// freedesktop application ID
|
||||||
|
fid string
|
||||||
|
// argv to start process with in the final confined environment
|
||||||
|
command []string
|
||||||
|
// persistent process state store
|
||||||
|
store state.Store
|
||||||
|
|
||||||
|
// uint8 representation of launch method sealed from config
|
||||||
|
launchOption uint8
|
||||||
|
// process-specific share directory path
|
||||||
|
share string
|
||||||
|
// process-specific share directory path local to XDG_RUNTIME_DIR
|
||||||
|
shareLocal string
|
||||||
|
|
||||||
|
// path to launcher program
|
||||||
|
toolPath string
|
||||||
|
// pass-through enablement tracking from config
|
||||||
|
et system.Enablements
|
||||||
|
|
||||||
|
// prevents sharing from happening twice
|
||||||
|
shared bool
|
||||||
|
// seal system-level component
|
||||||
|
sys *appSealSys
|
||||||
|
|
||||||
|
// used in various sealing operations
|
||||||
|
internal.SystemConstants
|
||||||
|
|
||||||
|
// protected by upstream mutex
|
||||||
|
}
|
||||||
|
|
||||||
// Seal seals the app launch context
|
// Seal seals the app launch context
|
||||||
func (a *app) Seal(config *Config) error {
|
func (a *app) Seal(config *Config) error {
|
||||||
a.lock.Lock()
|
a.lock.Lock()
|
||||||
@ -176,10 +214,10 @@ func (a *app) Seal(config *Config) error {
|
|||||||
seal.sys.bwrap.SetEnv = make(map[string]string)
|
seal.sys.bwrap.SetEnv = make(map[string]string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create wayland client wait channel if mediated wayland is enabled
|
// create wayland struct and client wait channel if mediated wayland is enabled
|
||||||
// this channel being set enables mediated wayland setup later on
|
// this field being set enables mediated wayland setup later on
|
||||||
if config.Confinement.Sandbox.Wayland {
|
if config.Confinement.Sandbox.Wayland {
|
||||||
seal.wlDone = make(chan struct{})
|
seal.wl = shim.NewWayland()
|
||||||
}
|
}
|
||||||
|
|
||||||
// open process state store
|
// open process state store
|
||||||
|
@ -34,7 +34,7 @@ func (seal *appSeal) shareDisplay() error {
|
|||||||
if wd, ok := os.LookupEnv(waylandDisplay); !ok {
|
if wd, ok := os.LookupEnv(waylandDisplay); !ok {
|
||||||
return fmsg.WrapError(ErrWayland,
|
return fmsg.WrapError(ErrWayland,
|
||||||
"WAYLAND_DISPLAY is not set")
|
"WAYLAND_DISPLAY is not set")
|
||||||
} else if seal.wlDone == nil {
|
} else if seal.wl == nil {
|
||||||
// hardlink wayland socket
|
// hardlink wayland socket
|
||||||
wp := path.Join(seal.RuntimePath, wd)
|
wp := path.Join(seal.RuntimePath, wd)
|
||||||
wpi := path.Join(seal.shareLocal, "wayland")
|
wpi := path.Join(seal.shareLocal, "wayland")
|
||||||
@ -46,8 +46,8 @@ func (seal *appSeal) shareDisplay() error {
|
|||||||
// ensure Wayland socket ACL (e.g. `/run/user/%d/wayland-%d`)
|
// ensure Wayland socket ACL (e.g. `/run/user/%d/wayland-%d`)
|
||||||
seal.sys.UpdatePermType(system.EWayland, wp, acl.Read, acl.Write, acl.Execute)
|
seal.sys.UpdatePermType(system.EWayland, wp, acl.Read, acl.Write, acl.Execute)
|
||||||
} else {
|
} else {
|
||||||
// set wayland socket path (e.g. `/run/user/%d/wayland-%d`)
|
// set wayland socket path for mediation (e.g. `/run/user/%d/wayland-%d`)
|
||||||
seal.wl = path.Join(seal.RuntimePath, wd)
|
seal.wl.Path = path.Join(seal.RuntimePath, wd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,23 +62,19 @@ func (a *app) Start() error {
|
|||||||
confSockPath := path.Join(a.seal.share, "shim")
|
confSockPath := path.Join(a.seal.share, "shim")
|
||||||
a.cmd = exec.Command(a.seal.toolPath, commandBuilder(shim.EnvShim+"="+confSockPath)...)
|
a.cmd = exec.Command(a.seal.toolPath, commandBuilder(shim.EnvShim+"="+confSockPath)...)
|
||||||
a.cmd.Env = []string{}
|
a.cmd.Env = []string{}
|
||||||
a.cmd.Stdin = os.Stdin
|
a.cmd.Stdin, a.cmd.Stdout, a.cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||||
a.cmd.Stdout = os.Stdout
|
|
||||||
a.cmd.Stderr = os.Stderr
|
|
||||||
a.cmd.Dir = a.seal.RunDirPath
|
a.cmd.Dir = a.seal.RunDirPath
|
||||||
|
|
||||||
if wls, err := shim.ServeConfig(confSockPath, a.seal.sys.UID(), &shim.Payload{
|
if err := shim.ServeConfig(confSockPath, a.seal.sys.UID(), &shim.Payload{
|
||||||
Argv: a.seal.command,
|
Argv: a.seal.command,
|
||||||
Exec: shimExec,
|
Exec: shimExec,
|
||||||
Bwrap: a.seal.sys.bwrap,
|
Bwrap: a.seal.sys.bwrap,
|
||||||
WL: a.seal.wlDone != nil,
|
WL: a.seal.wl != nil,
|
||||||
|
|
||||||
Verbose: verbose.Get(),
|
Verbose: verbose.Get(),
|
||||||
}, a.seal.wl, a.seal.wlDone); err != nil {
|
}, a.seal.wl); err != nil {
|
||||||
return fmsg.WrapErrorSuffix(err,
|
return fmsg.WrapErrorSuffix(err,
|
||||||
"cannot listen on shim socket:")
|
"cannot serve shim setup:")
|
||||||
} else {
|
|
||||||
a.wayland = wls
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// start shim
|
// start shim
|
||||||
@ -185,9 +181,8 @@ func (a *app) Wait() (int, error) {
|
|||||||
verbose.Println("process", strconv.Itoa(a.cmd.Process.Pid), "exited with exit code", r)
|
verbose.Println("process", strconv.Itoa(a.cmd.Process.Pid), "exited with exit code", r)
|
||||||
|
|
||||||
// close wayland connection
|
// close wayland connection
|
||||||
if a.wayland != nil {
|
if a.seal.wl != nil {
|
||||||
close(a.seal.wlDone)
|
if err := a.seal.wl.Close(); err != nil {
|
||||||
if err := a.wayland.Close(); err != nil {
|
|
||||||
fmt.Println("fortify: cannot close wayland connection:", err)
|
fmt.Println("fortify: cannot close wayland connection:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,51 +5,9 @@ import (
|
|||||||
|
|
||||||
"git.ophivana.moe/security/fortify/dbus"
|
"git.ophivana.moe/security/fortify/dbus"
|
||||||
"git.ophivana.moe/security/fortify/helper/bwrap"
|
"git.ophivana.moe/security/fortify/helper/bwrap"
|
||||||
"git.ophivana.moe/security/fortify/internal"
|
|
||||||
"git.ophivana.moe/security/fortify/internal/state"
|
|
||||||
"git.ophivana.moe/security/fortify/internal/system"
|
"git.ophivana.moe/security/fortify/internal/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
// appSeal seals the application with child-related information
|
|
||||||
type appSeal struct {
|
|
||||||
// wayland socket path if mediated wayland is enabled
|
|
||||||
wl string
|
|
||||||
// wait for wayland client to exit if mediated wayland is enabled,
|
|
||||||
// (wlDone == nil) determines whether mediated wayland setup is performed
|
|
||||||
wlDone chan struct{}
|
|
||||||
|
|
||||||
// app unique ID string representation
|
|
||||||
id string
|
|
||||||
// freedesktop application ID
|
|
||||||
fid string
|
|
||||||
// argv to start process with in the final confined environment
|
|
||||||
command []string
|
|
||||||
// persistent process state store
|
|
||||||
store state.Store
|
|
||||||
|
|
||||||
// uint8 representation of launch method sealed from config
|
|
||||||
launchOption uint8
|
|
||||||
// process-specific share directory path
|
|
||||||
share string
|
|
||||||
// process-specific share directory path local to XDG_RUNTIME_DIR
|
|
||||||
shareLocal string
|
|
||||||
|
|
||||||
// path to launcher program
|
|
||||||
toolPath string
|
|
||||||
// pass-through enablement tracking from config
|
|
||||||
et system.Enablements
|
|
||||||
|
|
||||||
// prevents sharing from happening twice
|
|
||||||
shared bool
|
|
||||||
// seal system-level component
|
|
||||||
sys *appSealSys
|
|
||||||
|
|
||||||
// used in various sealing operations
|
|
||||||
internal.SystemConstants
|
|
||||||
|
|
||||||
// protected by upstream mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// appSealSys encapsulates app seal behaviour with OS interactions
|
// appSealSys encapsulates app seal behaviour with OS interactions
|
||||||
type appSealSys struct {
|
type appSealSys struct {
|
||||||
bwrap *bwrap.Config
|
bwrap *bwrap.Config
|
||||||
|
@ -14,19 +14,18 @@ import (
|
|||||||
|
|
||||||
// called in the parent process
|
// called in the parent process
|
||||||
|
|
||||||
func ServeConfig(socket string, uid int, payload *Payload, wl string, done chan struct{}) (*net.UnixConn, error) {
|
func ServeConfig(socket string, uid int, payload *Payload, wl *Wayland) error {
|
||||||
var ws *net.UnixConn
|
|
||||||
if payload.WL {
|
if payload.WL {
|
||||||
if f, err := net.DialUnix("unix", nil, &net.UnixAddr{Name: wl, Net: "unix"}); err != nil {
|
if f, err := net.DialUnix("unix", nil, &net.UnixAddr{Name: wl.Path, Net: "unix"}); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
} else {
|
} else {
|
||||||
verbose.Println("connected to wayland at", wl)
|
verbose.Println("connected to wayland at", wl)
|
||||||
ws = f
|
wl.UnixConn = f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c, err := net.ListenUnix("unix", &net.UnixAddr{Name: socket, Net: "unix"}); err != nil {
|
if c, err := net.ListenUnix("unix", &net.UnixAddr{Name: socket, Net: "unix"}); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
} else {
|
} else {
|
||||||
verbose.Println("configuring shim on socket", socket)
|
verbose.Println("configuring shim on socket", socket)
|
||||||
if err = acl.UpdatePerm(socket, uid, acl.Read, acl.Write, acl.Execute); err != nil {
|
if err = acl.UpdatePerm(socket, uid, acl.Read, acl.Write, acl.Execute); err != nil {
|
||||||
@ -47,7 +46,7 @@ func ServeConfig(socket string, uid int, payload *Payload, wl string, done chan
|
|||||||
if payload.WL {
|
if payload.WL {
|
||||||
// get raw connection
|
// get raw connection
|
||||||
var rc syscall.RawConn
|
var rc syscall.RawConn
|
||||||
if rc, err = ws.SyscallConn(); err != nil {
|
if rc, err = wl.SyscallConn(); err != nil {
|
||||||
fmt.Println("fortify: cannot obtain raw wayland connection:", err)
|
fmt.Println("fortify: cannot obtain raw wayland connection:", err)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
@ -61,7 +60,7 @@ func ServeConfig(socket string, uid int, payload *Payload, wl string, done chan
|
|||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
|
|
||||||
// block until shim exits
|
// block until shim exits
|
||||||
<-done
|
<-wl.done
|
||||||
verbose.Println("releasing wayland connection")
|
verbose.Println("releasing wayland connection")
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
fmt.Println("fortify: cannot obtain wayland connection fd:", err)
|
fmt.Println("fortify: cannot obtain wayland connection fd:", err)
|
||||||
@ -79,6 +78,6 @@ func ServeConfig(socket string, uid int, payload *Payload, wl string, done chan
|
|||||||
fmt.Println("fortify: cannot remove dangling shim socket:", err)
|
fmt.Println("fortify: cannot remove dangling shim socket:", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return ws, nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
35
internal/shim/wayland.go
Normal file
35
internal/shim/wayland.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package shim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Wayland implements wayland mediation.
|
||||||
|
type Wayland struct {
|
||||||
|
// wayland socket path
|
||||||
|
Path string
|
||||||
|
|
||||||
|
// wayland connection
|
||||||
|
*net.UnixConn
|
||||||
|
|
||||||
|
connErr error
|
||||||
|
sync.Once
|
||||||
|
// wait for wayland client to exit
|
||||||
|
done chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wl *Wayland) Close() error {
|
||||||
|
wl.Do(func() {
|
||||||
|
close(wl.done)
|
||||||
|
wl.connErr = wl.UnixConn.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
return wl.connErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWayland() *Wayland {
|
||||||
|
wl := new(Wayland)
|
||||||
|
wl.done = make(chan struct{})
|
||||||
|
return wl
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user