diff options
| author | Ophestra Umiker <cat@ophivana.moe> | 2024-10-23 21:46:21 +0900 |
|---|---|---|
| committer | Ophestra Umiker <cat@ophivana.moe> | 2024-10-23 21:46:21 +0900 |
| commit | 6bc5be7e5a2334274adf2945ad6d29d063a7086b (patch) | |
| tree | d99223898a282b7ea40c41330b5748a3b5f2b310 /internal/app | |
| parent | e35c5fe3ed995d352c001bd7e35bd1214ca583da (diff) | |
internal: wrap calls to os standard library functions
This change helps tests stub out and simulate OS behaviour during the sealing process. This also removes dependency on XDG_RUNTIME_DIR as the internal.System implementation provided to App provides a compat directory inside the tmpdir-based share when XDG_RUNTIME_DIR is unavailable.
Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
Diffstat (limited to 'internal/app')
| -rw-r--r-- | internal/app/app.go | 7 | ||||
| -rw-r--r-- | internal/app/launch.machinectl.go | 3 | ||||
| -rw-r--r-- | internal/app/launch.sudo.go | 4 | ||||
| -rw-r--r-- | internal/app/seal.go | 26 | ||||
| -rw-r--r-- | internal/app/share.display.go | 4 | ||||
| -rw-r--r-- | internal/app/share.pulse.go | 8 | ||||
| -rw-r--r-- | internal/app/share.runtime.go | 1 | ||||
| -rw-r--r-- | internal/app/share.system.go | 4 | ||||
| -rw-r--r-- | internal/app/system.go | 9 |
9 files changed, 34 insertions, 32 deletions
diff --git a/internal/app/app.go b/internal/app/app.go index 0055dd76..de2c461f 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -3,6 +3,8 @@ package app import ( "os/exec" "sync" + + "git.ophivana.moe/security/fortify/internal" ) type App interface { @@ -22,6 +24,8 @@ type App interface { type app struct { // application unique identifier id *ID + // operating system interface + os internal.System // underlying user switcher process cmd *exec.Cmd // shim setup abort reason and completion @@ -61,8 +65,9 @@ func (a *app) WaitErr() error { return a.waitErr } -func New() (App, error) { +func New(os internal.System) (App, error) { a := new(app) a.id = new(ID) + a.os = os return a, newAppID(a.id) } diff --git a/internal/app/launch.machinectl.go b/internal/app/launch.machinectl.go index 32f06740..c1d26101 100644 --- a/internal/app/launch.machinectl.go +++ b/internal/app/launch.machinectl.go @@ -1,7 +1,6 @@ package app import ( - "os/exec" "strings" "git.ophivana.moe/security/fortify/internal/fmsg" @@ -31,7 +30,7 @@ func (a *app) commandBuilderMachineCtl(shimEnv string) (args []string) { args = append(args, "--", ".host") // /bin/sh -c - if sh, err := exec.LookPath("sh"); err != nil { + if sh, err := a.os.LookPath("sh"); err != nil { // hardcode /bin/sh path since it exists more often than not args = append(args, "/bin/sh", "-c") } else { diff --git a/internal/app/launch.sudo.go b/internal/app/launch.sudo.go index 2df7be0f..e7a20672 100644 --- a/internal/app/launch.sudo.go +++ b/internal/app/launch.sudo.go @@ -1,8 +1,6 @@ package app import ( - "os" - "git.ophivana.moe/security/fortify/internal/fmsg" ) @@ -17,7 +15,7 @@ func (a *app) commandBuilderSudo(shimEnv string) (args []string) { args = append(args, "-Hiu", a.seal.sys.user.Username) // -A? - if _, ok := os.LookupEnv(sudoAskPass); ok { + if _, ok := a.os.LookupEnv(sudoAskPass); ok { fmsg.VPrintln(sudoAskPass, "set, adding askpass flag") args = append(args, "-A") } diff --git a/internal/app/seal.go b/internal/app/seal.go index f0fe5582..45010607 100644 --- a/internal/app/seal.go +++ b/internal/app/seal.go @@ -2,8 +2,7 @@ package app import ( "errors" - "os" - "os/exec" + "io/fs" "os/user" "path" "strconv" @@ -67,8 +66,7 @@ type appSeal struct { // seal system-level component sys *appSealSys - // used in various sealing operations - internal.SystemConstants + internal.Paths // protected by upstream mutex } @@ -91,7 +89,7 @@ func (a *app) Seal(config *Config) error { seal := new(appSeal) // fetch system constants - seal.SystemConstants = internal.GetSC() + seal.Paths = a.os.Paths() // pass through config values seal.id = a.id.String() @@ -102,7 +100,7 @@ func (a *app) Seal(config *Config) error { switch config.Method { case method[LaunchMethodSudo]: seal.launchOption = LaunchMethodSudo - if sudoPath, err := exec.LookPath("sudo"); err != nil { + if sudoPath, err := a.os.LookPath("sudo"); err != nil { return fmsg.WrapError(ErrSudo, "sudo not found") } else { @@ -115,7 +113,7 @@ func (a *app) Seal(config *Config) error { "system has not been booted with systemd as init system") } - if machineCtlPath, err := exec.LookPath("machinectl"); err != nil { + if machineCtlPath, err := a.os.LookPath("machinectl"); err != nil { return fmsg.WrapError(ErrMachineCtl, "machinectl not found") } else { @@ -130,14 +128,14 @@ func (a *app) Seal(config *Config) error { seal.sys = new(appSealSys) // look up fortify executable path - if p, err := os.Executable(); err != nil { + if p, err := a.os.Executable(); err != nil { return fmsg.WrapErrorSuffix(err, "cannot look up fortify executable path:") } else { seal.sys.executable = p } // look up user from system - if u, err := user.Lookup(config.User); err != nil { + if u, err := a.os.Lookup(config.User); err != nil { if errors.As(err, new(user.UnknownUserError)) { return fmsg.WrapError(ErrUser, "unknown user", config.User) } else { @@ -160,7 +158,7 @@ func (a *app) Seal(config *Config) error { NoNewSession: true, } // bind entries in / - if d, err := os.ReadDir("/"); err != nil { + if d, err := a.os.ReadDir("/"); err != nil { return err } else { b := make([]*FilesystemConfig, 0, len(d)) @@ -180,7 +178,7 @@ func (a *app) Seal(config *Config) error { conf.Filesystem = append(conf.Filesystem, b...) } // bind entries in /run - if d, err := os.ReadDir("/run"); err != nil { + if d, err := a.os.ReadDir("/run"); err != nil { return err } else { b := make([]*FilesystemConfig, 0, len(d)) @@ -198,7 +196,7 @@ func (a *app) Seal(config *Config) error { } // hide nscd from sandbox if present nscd := "/var/run/nscd" - if _, err := os.Stat(nscd); !errors.Is(err, os.ErrNotExist) { + if _, err := a.os.Stat(nscd); !errors.Is(err, fs.ErrNotExist) { conf.Override = append(conf.Override, nscd) } // bind GPU stuff @@ -222,7 +220,7 @@ func (a *app) Seal(config *Config) error { // 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 - seal.store = state.NewSimple(seal.SystemConstants.RunDirPath, seal.sys.user.Uid) + seal.store = state.NewSimple(seal.RunDirPath, seal.sys.user.Uid) // parse string UID if u, err := strconv.Atoi(seal.sys.user.Uid); err != nil { @@ -236,7 +234,7 @@ func (a *app) Seal(config *Config) error { seal.et = config.Confinement.Enablements // this method calls all share methods in sequence - if err := seal.shareAll([2]*dbus.Config{config.Confinement.SessionBus, config.Confinement.SystemBus}); err != nil { + if err := seal.shareAll([2]*dbus.Config{config.Confinement.SessionBus, config.Confinement.SystemBus}, a.os); err != nil { return err } diff --git a/internal/app/share.display.go b/internal/app/share.display.go index 18fad7a7..95132119 100644 --- a/internal/app/share.display.go +++ b/internal/app/share.display.go @@ -2,10 +2,10 @@ package app import ( "errors" - "os" "path" "git.ophivana.moe/security/fortify/acl" + "git.ophivana.moe/security/fortify/internal" "git.ophivana.moe/security/fortify/internal/fmsg" "git.ophivana.moe/security/fortify/internal/system" ) @@ -23,7 +23,7 @@ var ( ErrXDisplay = errors.New(display + " unset") ) -func (seal *appSeal) shareDisplay() error { +func (seal *appSeal) shareDisplay(os internal.System) error { // pass $TERM to launcher if t, ok := os.LookupEnv(term); ok { seal.sys.bwrap.SetEnv[term] = t diff --git a/internal/app/share.pulse.go b/internal/app/share.pulse.go index 83c16fde..fa5c0a7a 100644 --- a/internal/app/share.pulse.go +++ b/internal/app/share.pulse.go @@ -4,9 +4,9 @@ import ( "errors" "fmt" "io/fs" - "os" "path" + "git.ophivana.moe/security/fortify/internal" "git.ophivana.moe/security/fortify/internal/fmsg" "git.ophivana.moe/security/fortify/internal/system" ) @@ -25,7 +25,7 @@ var ( ErrPulseMode = errors.New("unexpected pulse socket mode") ) -func (seal *appSeal) sharePulse() error { +func (seal *appSeal) sharePulse(os internal.System) error { if !seal.et.Has(system.EPulse) { return nil } @@ -65,7 +65,7 @@ func (seal *appSeal) sharePulse() error { seal.sys.bwrap.SetEnv[pulseServer] = "unix:" + p // publish current user's pulse cookie for target user - if src, err := discoverPulseCookie(); err != nil { + if src, err := discoverPulseCookie(os); err != nil { return err } else { dst := path.Join(seal.share, "pulse-cookie") @@ -78,7 +78,7 @@ func (seal *appSeal) sharePulse() error { } // discoverPulseCookie attempts various standard methods to discover the current user's PulseAudio authentication cookie -func discoverPulseCookie() (string, error) { +func discoverPulseCookie(os internal.System) (string, error) { if p, ok := os.LookupEnv(pulseCookie); ok { return p, nil } diff --git a/internal/app/share.runtime.go b/internal/app/share.runtime.go index 1c65aa77..dd3a6b13 100644 --- a/internal/app/share.runtime.go +++ b/internal/app/share.runtime.go @@ -29,6 +29,7 @@ func (seal *appSeal) shareRuntime() { seal.sys.UpdatePermType(system.User, seal.RunDirPath, acl.Execute) // ensure runtime directory ACL (e.g. `/run/user/%d`) + seal.sys.Ensure(seal.RuntimePath, 0700) // ensure this dir in case XDG_RUNTIME_DIR is unset seal.sys.UpdatePermType(system.User, seal.RuntimePath, acl.Execute) // ensure process-specific share local to XDG_RUNTIME_DIR (e.g. `/run/user/%d/fortify/%s`) diff --git a/internal/app/share.system.go b/internal/app/share.system.go index b8c5f063..600119ee 100644 --- a/internal/app/share.system.go +++ b/internal/app/share.system.go @@ -1,10 +1,10 @@ package app import ( - "os" "path" "git.ophivana.moe/security/fortify/acl" + "git.ophivana.moe/security/fortify/internal" "git.ophivana.moe/security/fortify/internal/system" ) @@ -38,7 +38,7 @@ func (seal *appSeal) shareSystem() { seal.sys.bwrap.Tmpfs(seal.SharePath, 1*1024*1024) } -func (seal *appSeal) sharePasswd() { +func (seal *appSeal) sharePasswd(os internal.System) { // look up shell sh := "/bin/sh" if s, ok := os.LookupEnv(shell); ok { diff --git a/internal/app/system.go b/internal/app/system.go index 2fc413e7..7ccc2eeb 100644 --- a/internal/app/system.go +++ b/internal/app/system.go @@ -5,6 +5,7 @@ import ( "git.ophivana.moe/security/fortify/dbus" "git.ophivana.moe/security/fortify/helper/bwrap" + "git.ophivana.moe/security/fortify/internal" "git.ophivana.moe/security/fortify/internal/system" ) @@ -27,7 +28,7 @@ type appSealSys struct { } // shareAll calls all share methods in sequence -func (seal *appSeal) shareAll(bus [2]*dbus.Config) error { +func (seal *appSeal) shareAll(bus [2]*dbus.Config, os internal.System) error { if seal.shared { panic("seal shared twice") } @@ -35,11 +36,11 @@ func (seal *appSeal) shareAll(bus [2]*dbus.Config) error { seal.shareSystem() seal.shareRuntime() - seal.sharePasswd() - if err := seal.shareDisplay(); err != nil { + seal.sharePasswd(os) + if err := seal.shareDisplay(os); err != nil { return err } - if err := seal.sharePulse(); err != nil { + if err := seal.sharePulse(os); err != nil { return err } |
