From 0107620d8c174dcf9237c16d4b4b7b2f6955a214 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Sat, 28 Dec 2024 11:12:35 +0900 Subject: [PATCH] app: merge share methods This significantly increases readability and makes order of ops more obvious. Signed-off-by: Ophestra --- internal/app/seal.go | 2 +- internal/app/share.dbus.go | 44 ----- internal/app/share.display.go | 81 -------- internal/app/share.go | 335 ++++++++++++++++++++++++++++++++++ internal/app/share.pulse.go | 119 ------------ internal/app/share.runtime.go | 39 ---- internal/app/share.system.go | 74 -------- internal/app/system.go | 36 ---- 8 files changed, 336 insertions(+), 394 deletions(-) delete mode 100644 internal/app/share.dbus.go delete mode 100644 internal/app/share.display.go create mode 100644 internal/app/share.go delete mode 100644 internal/app/share.pulse.go delete mode 100644 internal/app/share.runtime.go delete mode 100644 internal/app/share.system.go diff --git a/internal/app/seal.go b/internal/app/seal.go index f3dbfbd..87e62b3 100644 --- a/internal/app/seal.go +++ b/internal/app/seal.go @@ -229,7 +229,7 @@ func (a *app) Seal(config *fst.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}, a.os); err != nil { + if err := seal.setupShares([2]*dbus.Config{config.Confinement.SessionBus, config.Confinement.SystemBus}, a.os); err != nil { return err } diff --git a/internal/app/share.dbus.go b/internal/app/share.dbus.go deleted file mode 100644 index 4fd947d..0000000 --- a/internal/app/share.dbus.go +++ /dev/null @@ -1,44 +0,0 @@ -package app - -import ( - "path" - - "git.gensokyo.uk/security/fortify/acl" - "git.gensokyo.uk/security/fortify/dbus" - "git.gensokyo.uk/security/fortify/internal/system" -) - -const ( - dbusSessionBusAddress = "DBUS_SESSION_BUS_ADDRESS" - dbusSystemBusAddress = "DBUS_SYSTEM_BUS_ADDRESS" -) - -func (seal *appSeal) shareDBus(config [2]*dbus.Config) error { - if !seal.et.Has(system.EDBus) { - return nil - } - - // downstream socket paths - sessionPath, systemPath := path.Join(seal.share, "bus"), path.Join(seal.share, "system_bus_socket") - - // configure dbus proxy - if f, err := seal.sys.ProxyDBus(config[0], config[1], sessionPath, systemPath); err != nil { - return err - } else { - seal.dbusMsg = f - } - - // share proxy sockets - sessionInner := path.Join(seal.sys.runtime, "bus") - seal.sys.bwrap.SetEnv[dbusSessionBusAddress] = "unix:path=" + sessionInner - seal.sys.bwrap.Bind(sessionPath, sessionInner) - seal.sys.UpdatePerm(sessionPath, acl.Read, acl.Write) - if config[1] != nil { - systemInner := "/run/dbus/system_bus_socket" - seal.sys.bwrap.SetEnv[dbusSystemBusAddress] = "unix:path=" + systemInner - seal.sys.bwrap.Bind(systemPath, systemInner) - seal.sys.UpdatePerm(systemPath, acl.Read, acl.Write) - } - - return nil -} diff --git a/internal/app/share.display.go b/internal/app/share.display.go deleted file mode 100644 index f09efe6..0000000 --- a/internal/app/share.display.go +++ /dev/null @@ -1,81 +0,0 @@ -package app - -import ( - "errors" - "path" - - "git.gensokyo.uk/security/fortify/acl" - "git.gensokyo.uk/security/fortify/internal/fmsg" - "git.gensokyo.uk/security/fortify/internal/linux" - "git.gensokyo.uk/security/fortify/internal/system" -) - -const ( - term = "TERM" - display = "DISPLAY" - - // https://manpages.debian.org/experimental/libwayland-doc/wl_display_connect.3.en.html - waylandDisplay = "WAYLAND_DISPLAY" -) - -var ( - ErrWayland = errors.New(waylandDisplay + " unset") - ErrXDisplay = errors.New(display + " unset") -) - -func (seal *appSeal) shareDisplay(os linux.System) error { - // pass $TERM to launcher - if t, ok := os.LookupEnv(term); ok { - seal.sys.bwrap.SetEnv[term] = t - } - - // 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 { - 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 - wpi := path.Join(seal.shareLocal, "wayland") - seal.sys.Link(wp, wpi) - 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 { - 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) - } - } - - // set up X11 - if seal.et.Has(system.EX11) { - // discover X11 and grant user permission via the `ChangeHosts` command - if d, ok := os.LookupEnv(display); !ok { - return fmsg.WrapError(ErrXDisplay, - "DISPLAY is not set") - } else { - seal.sys.ChangeHosts("#" + seal.sys.user.us) - seal.sys.bwrap.SetEnv[display] = d - seal.sys.bwrap.Bind("/tmp/.X11-unix", "/tmp/.X11-unix") - } - } - - return nil -} diff --git a/internal/app/share.go b/internal/app/share.go new file mode 100644 index 0000000..dea55a8 --- /dev/null +++ b/internal/app/share.go @@ -0,0 +1,335 @@ +package app + +import ( + "errors" + "fmt" + "io/fs" + "path" + + "git.gensokyo.uk/security/fortify/acl" + "git.gensokyo.uk/security/fortify/dbus" + "git.gensokyo.uk/security/fortify/fst" + "git.gensokyo.uk/security/fortify/internal/fmsg" + "git.gensokyo.uk/security/fortify/internal/linux" + "git.gensokyo.uk/security/fortify/internal/system" +) + +const ( + home = "HOME" + shell = "SHELL" + + xdgConfigHome = "XDG_CONFIG_HOME" + xdgRuntimeDir = "XDG_RUNTIME_DIR" + xdgSessionClass = "XDG_SESSION_CLASS" + xdgSessionType = "XDG_SESSION_TYPE" + + term = "TERM" + display = "DISPLAY" + + // https://manpages.debian.org/experimental/libwayland-doc/wl_display_connect.3.en.html + waylandDisplay = "WAYLAND_DISPLAY" + + pulseServer = "PULSE_SERVER" + pulseCookie = "PULSE_COOKIE" + + dbusSessionBusAddress = "DBUS_SESSION_BUS_ADDRESS" + dbusSystemBusAddress = "DBUS_SYSTEM_BUS_ADDRESS" +) + +var ( + ErrWayland = errors.New(waylandDisplay + " unset") + ErrXDisplay = errors.New(display + " unset") + + ErrPulseCookie = errors.New("pulse cookie not present") + ErrPulseSocket = errors.New("pulse socket not present") + ErrPulseMode = errors.New("unexpected pulse socket mode") +) + +func (seal *appSeal) setupShares(bus [2]*dbus.Config, os linux.System) error { + if seal.shared { + panic("seal shared twice") + } + seal.shared = true + + /* + Tmpdir-based share directory + */ + + // ensure Share (e.g. `/tmp/fortify.%d`) + // acl is unnecessary as this directory is world executable + seal.sys.Ensure(seal.SharePath, 0711) + + // ensure process-specific share (e.g. `/tmp/fortify.%d/%s`) + // acl is unnecessary as this directory is world executable + seal.share = path.Join(seal.SharePath, seal.id) + seal.sys.Ephemeral(system.Process, seal.share, 0711) + + // ensure child tmpdir parent directory (e.g. `/tmp/fortify.%d/tmpdir`) + targetTmpdirParent := path.Join(seal.SharePath, "tmpdir") + seal.sys.Ensure(targetTmpdirParent, 0700) + seal.sys.UpdatePermType(system.User, targetTmpdirParent, acl.Execute) + + // ensure child tmpdir (e.g. `/tmp/fortify.%d/tmpdir/%d`) + targetTmpdir := path.Join(targetTmpdirParent, seal.sys.user.as) + seal.sys.Ensure(targetTmpdir, 01700) + seal.sys.UpdatePermType(system.User, targetTmpdir, acl.Read, acl.Write, acl.Execute) + seal.sys.bwrap.Bind(targetTmpdir, "/tmp", false, true) + + /* + XDG runtime directory + */ + + // mount tmpfs on inner runtime (e.g. `/run/user/%d`) + seal.sys.bwrap.Tmpfs("/run/user", 1*1024*1024) + seal.sys.bwrap.Tmpfs(seal.sys.runtime, 8*1024*1024) + + // point to inner runtime path `/run/user/%d` + seal.sys.bwrap.SetEnv[xdgRuntimeDir] = seal.sys.runtime + seal.sys.bwrap.SetEnv[xdgSessionClass] = "user" + seal.sys.bwrap.SetEnv[xdgSessionType] = "tty" + + // ensure RunDir (e.g. `/run/user/%d/fortify`) + seal.sys.Ensure(seal.RunDirPath, 0700) + 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`) + seal.shareLocal = path.Join(seal.RunDirPath, seal.id) + seal.sys.Ephemeral(system.Process, seal.shareLocal, 0700) + seal.sys.UpdatePerm(seal.shareLocal, acl.Execute) + + /* + Inner passwd database + */ + + // look up shell + sh := "/bin/sh" + if s, ok := os.LookupEnv(shell); ok { + seal.sys.bwrap.SetEnv[shell] = s + sh = s + } + + // generate /etc/passwd + passwdPath := path.Join(seal.share, "passwd") + username := "chronos" + if seal.sys.user.username != "" { + username = seal.sys.user.username + } + homeDir := "/var/empty" + if seal.sys.user.home != "" { + homeDir = seal.sys.user.home + } + + // bind home directory + seal.sys.bwrap.Bind(seal.sys.user.data, homeDir, false, true) + seal.sys.bwrap.Chdir = homeDir + + seal.sys.bwrap.SetEnv["USER"] = username + seal.sys.bwrap.SetEnv["HOME"] = homeDir + + passwd := username + ":x:" + seal.sys.mappedIDString + ":" + seal.sys.mappedIDString + ":Fortify:" + homeDir + ":" + sh + "\n" + seal.sys.Write(passwdPath, passwd) + + // write /etc/group + groupPath := path.Join(seal.share, "group") + seal.sys.Write(groupPath, "fortify:x:"+seal.sys.mappedIDString+":\n") + + // bind /etc/passwd and /etc/group + seal.sys.bwrap.Bind(passwdPath, "/etc/passwd") + seal.sys.bwrap.Bind(groupPath, "/etc/group") + + /* + Display servers + */ + + // pass $TERM to launcher + if t, ok := os.LookupEnv(term); ok { + seal.sys.bwrap.SetEnv[term] = t + } + + // 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 { + wp = path.Join(seal.RuntimePath, wd) + } + + w := path.Join(seal.sys.runtime, "wayland-0") + seal.sys.bwrap.SetEnv[waylandDisplay] = w + + if !seal.directWayland { // set up security-context-v1 + 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) + } else { // bind mount wayland socket (insecure) + // hardlink wayland socket + wpi := path.Join(seal.shareLocal, "wayland") + seal.sys.Link(wp, wpi) + 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) + } + } + + // set up X11 + if seal.et.Has(system.EX11) { + // discover X11 and grant user permission via the `ChangeHosts` command + if d, ok := os.LookupEnv(display); !ok { + return fmsg.WrapError(ErrXDisplay, + "DISPLAY is not set") + } else { + seal.sys.ChangeHosts("#" + seal.sys.user.us) + seal.sys.bwrap.SetEnv[display] = d + seal.sys.bwrap.Bind("/tmp/.X11-unix", "/tmp/.X11-unix") + } + } + + /* + PulseAudio server and authentication + */ + + if seal.et.Has(system.EPulse) { + // check PulseAudio directory presence (e.g. `/run/user/%d/pulse`) + pd := path.Join(seal.RuntimePath, "pulse") + ps := path.Join(pd, "native") + if _, err := os.Stat(pd); err != nil { + if !errors.Is(err, fs.ErrNotExist) { + return fmsg.WrapErrorSuffix(err, + fmt.Sprintf("cannot access PulseAudio directory %q:", pd)) + } + return fmsg.WrapError(ErrPulseSocket, + fmt.Sprintf("PulseAudio directory %q not found", pd)) + } + + // check PulseAudio socket permission (e.g. `/run/user/%d/pulse/native`) + if s, err := os.Stat(ps); err != nil { + if !errors.Is(err, fs.ErrNotExist) { + return fmsg.WrapErrorSuffix(err, + fmt.Sprintf("cannot access PulseAudio socket %q:", ps)) + } + return fmsg.WrapError(ErrPulseSocket, + fmt.Sprintf("PulseAudio directory %q found but socket does not exist", pd)) + } else { + if m := s.Mode(); m&0o006 != 0o006 { + return fmsg.WrapError(ErrPulseMode, + fmt.Sprintf("unexpected permissions on %q:", ps), m) + } + } + + // hard link pulse socket into target-executable share + psi := path.Join(seal.shareLocal, "pulse") + p := path.Join(seal.sys.runtime, "pulse", "native") + seal.sys.Link(ps, psi) + seal.sys.bwrap.Bind(psi, p) + seal.sys.bwrap.SetEnv[pulseServer] = "unix:" + p + + // publish current user's pulse cookie for target user + if src, err := discoverPulseCookie(os); err != nil { + // not fatal + fmsg.VPrintln(err.(*fmsg.BaseError).Message()) + } else { + dst := path.Join(seal.share, "pulse-cookie") + innerDst := fst.Tmp + "/pulse-cookie" + seal.sys.bwrap.SetEnv[pulseCookie] = innerDst + seal.sys.CopyFile(dst, src) + seal.sys.bwrap.Bind(dst, innerDst) + } + } + + /* + D-Bus proxy + */ + + if seal.et.Has(system.EDBus) { + // ensure dbus session bus defaults + if bus[0] == nil { + bus[0] = dbus.NewConfig(seal.fid, true, true) + } + + // downstream socket paths + sessionPath, systemPath := path.Join(seal.share, "bus"), path.Join(seal.share, "system_bus_socket") + + // configure dbus proxy + if f, err := seal.sys.ProxyDBus(bus[0], bus[1], sessionPath, systemPath); err != nil { + return err + } else { + seal.dbusMsg = f + } + + // share proxy sockets + sessionInner := path.Join(seal.sys.runtime, "bus") + seal.sys.bwrap.SetEnv[dbusSessionBusAddress] = "unix:path=" + sessionInner + seal.sys.bwrap.Bind(sessionPath, sessionInner) + seal.sys.UpdatePerm(sessionPath, acl.Read, acl.Write) + if bus[1] != nil { + systemInner := "/run/dbus/system_bus_socket" + seal.sys.bwrap.SetEnv[dbusSystemBusAddress] = "unix:path=" + systemInner + seal.sys.bwrap.Bind(systemPath, systemInner) + seal.sys.UpdatePerm(systemPath, acl.Read, acl.Write) + } + } + + /* + Miscellaneous + */ + + // queue overriding tmpfs at the end of seal.sys.bwrap.Filesystem + for _, dest := range seal.sys.override { + seal.sys.bwrap.Tmpfs(dest, 8*1024) + } + + return nil +} + +// discoverPulseCookie attempts various standard methods to discover the current user's PulseAudio authentication cookie +func discoverPulseCookie(os linux.System) (string, error) { + if p, ok := os.LookupEnv(pulseCookie); ok { + return p, nil + } + + // dotfile $HOME/.pulse-cookie + if p, ok := os.LookupEnv(home); ok { + p = path.Join(p, ".pulse-cookie") + if s, err := os.Stat(p); err != nil { + if !errors.Is(err, fs.ErrNotExist) { + return p, fmsg.WrapErrorSuffix(err, + fmt.Sprintf("cannot access PulseAudio cookie %q:", p)) + } + // not found, try next method + } else if !s.IsDir() { + return p, nil + } + } + + // $XDG_CONFIG_HOME/pulse/cookie + if p, ok := os.LookupEnv(xdgConfigHome); ok { + p = path.Join(p, "pulse", "cookie") + if s, err := os.Stat(p); err != nil { + if !errors.Is(err, fs.ErrNotExist) { + return p, fmsg.WrapErrorSuffix(err, + fmt.Sprintf("cannot access PulseAudio cookie %q:", p)) + } + // not found, try next method + } else if !s.IsDir() { + return p, nil + } + } + + return "", fmsg.WrapError(ErrPulseCookie, + fmt.Sprintf("cannot locate PulseAudio cookie (tried $%s, $%s/pulse/cookie, $%s/.pulse-cookie)", + pulseCookie, xdgConfigHome, home)) +} diff --git a/internal/app/share.pulse.go b/internal/app/share.pulse.go deleted file mode 100644 index 7d4b354..0000000 --- a/internal/app/share.pulse.go +++ /dev/null @@ -1,119 +0,0 @@ -package app - -import ( - "errors" - "fmt" - "io/fs" - "path" - - "git.gensokyo.uk/security/fortify/fst" - "git.gensokyo.uk/security/fortify/internal/fmsg" - "git.gensokyo.uk/security/fortify/internal/linux" - "git.gensokyo.uk/security/fortify/internal/system" -) - -const ( - pulseServer = "PULSE_SERVER" - pulseCookie = "PULSE_COOKIE" - - home = "HOME" - xdgConfigHome = "XDG_CONFIG_HOME" -) - -var ( - ErrPulseCookie = errors.New("pulse cookie not present") - ErrPulseSocket = errors.New("pulse socket not present") - ErrPulseMode = errors.New("unexpected pulse socket mode") -) - -func (seal *appSeal) sharePulse(os linux.System) error { - if !seal.et.Has(system.EPulse) { - return nil - } - - // check PulseAudio directory presence (e.g. `/run/user/%d/pulse`) - pd := path.Join(seal.RuntimePath, "pulse") - ps := path.Join(pd, "native") - if _, err := os.Stat(pd); err != nil { - if !errors.Is(err, fs.ErrNotExist) { - return fmsg.WrapErrorSuffix(err, - fmt.Sprintf("cannot access PulseAudio directory %q:", pd)) - } - return fmsg.WrapError(ErrPulseSocket, - fmt.Sprintf("PulseAudio directory %q not found", pd)) - } - - // check PulseAudio socket permission (e.g. `/run/user/%d/pulse/native`) - if s, err := os.Stat(ps); err != nil { - if !errors.Is(err, fs.ErrNotExist) { - return fmsg.WrapErrorSuffix(err, - fmt.Sprintf("cannot access PulseAudio socket %q:", ps)) - } - return fmsg.WrapError(ErrPulseSocket, - fmt.Sprintf("PulseAudio directory %q found but socket does not exist", pd)) - } else { - if m := s.Mode(); m&0o006 != 0o006 { - return fmsg.WrapError(ErrPulseMode, - fmt.Sprintf("unexpected permissions on %q:", ps), m) - } - } - - // hard link pulse socket into target-executable share - psi := path.Join(seal.shareLocal, "pulse") - p := path.Join(seal.sys.runtime, "pulse", "native") - seal.sys.Link(ps, psi) - seal.sys.bwrap.Bind(psi, p) - seal.sys.bwrap.SetEnv[pulseServer] = "unix:" + p - - // publish current user's pulse cookie for target user - if src, err := discoverPulseCookie(os); err != nil { - fmsg.VPrintln(err.(*fmsg.BaseError).Message()) - } else { - dst := path.Join(seal.share, "pulse-cookie") - innerDst := fst.Tmp + "/pulse-cookie" - seal.sys.bwrap.SetEnv[pulseCookie] = innerDst - seal.sys.CopyFile(dst, src) - seal.sys.bwrap.Bind(dst, innerDst) - } - - return nil -} - -// discoverPulseCookie attempts various standard methods to discover the current user's PulseAudio authentication cookie -func discoverPulseCookie(os linux.System) (string, error) { - if p, ok := os.LookupEnv(pulseCookie); ok { - return p, nil - } - - // dotfile $HOME/.pulse-cookie - if p, ok := os.LookupEnv(home); ok { - p = path.Join(p, ".pulse-cookie") - if s, err := os.Stat(p); err != nil { - if !errors.Is(err, fs.ErrNotExist) { - return p, fmsg.WrapErrorSuffix(err, - fmt.Sprintf("cannot access PulseAudio cookie %q:", p)) - } - // not found, try next method - } else if !s.IsDir() { - return p, nil - } - } - - // $XDG_CONFIG_HOME/pulse/cookie - if p, ok := os.LookupEnv(xdgConfigHome); ok { - p = path.Join(p, "pulse", "cookie") - if s, err := os.Stat(p); err != nil { - if !errors.Is(err, fs.ErrNotExist) { - return p, fmsg.WrapErrorSuffix(err, - fmt.Sprintf("cannot access PulseAudio cookie %q:", p)) - } - // not found, try next method - } else if !s.IsDir() { - return p, nil - } - } - - return "", fmsg.WrapError(ErrPulseCookie, - fmt.Sprintf("cannot locate PulseAudio cookie (tried $%s, $%s/pulse/cookie, $%s/.pulse-cookie)", - pulseCookie, xdgConfigHome, home)) -} diff --git a/internal/app/share.runtime.go b/internal/app/share.runtime.go deleted file mode 100644 index 720c47f..0000000 --- a/internal/app/share.runtime.go +++ /dev/null @@ -1,39 +0,0 @@ -package app - -import ( - "path" - - "git.gensokyo.uk/security/fortify/acl" - "git.gensokyo.uk/security/fortify/internal/system" -) - -const ( - xdgRuntimeDir = "XDG_RUNTIME_DIR" - xdgSessionClass = "XDG_SESSION_CLASS" - xdgSessionType = "XDG_SESSION_TYPE" -) - -// shareRuntime queues actions for sharing/ensuring the runtime and share directories -func (seal *appSeal) shareRuntime() { - // mount tmpfs on inner runtime (e.g. `/run/user/%d`) - seal.sys.bwrap.Tmpfs("/run/user", 1*1024*1024) - seal.sys.bwrap.Tmpfs(seal.sys.runtime, 8*1024*1024) - - // point to inner runtime path `/run/user/%d` - seal.sys.bwrap.SetEnv[xdgRuntimeDir] = seal.sys.runtime - seal.sys.bwrap.SetEnv[xdgSessionClass] = "user" - seal.sys.bwrap.SetEnv[xdgSessionType] = "tty" - - // ensure RunDir (e.g. `/run/user/%d/fortify`) - seal.sys.Ensure(seal.RunDirPath, 0700) - 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`) - seal.shareLocal = path.Join(seal.RunDirPath, seal.id) - seal.sys.Ephemeral(system.Process, seal.shareLocal, 0700) - seal.sys.UpdatePerm(seal.shareLocal, acl.Execute) -} diff --git a/internal/app/share.system.go b/internal/app/share.system.go deleted file mode 100644 index 2e65ac6..0000000 --- a/internal/app/share.system.go +++ /dev/null @@ -1,74 +0,0 @@ -package app - -import ( - "path" - - "git.gensokyo.uk/security/fortify/acl" - "git.gensokyo.uk/security/fortify/internal/linux" - "git.gensokyo.uk/security/fortify/internal/system" -) - -const ( - shell = "SHELL" -) - -// shareSystem queues various system-related actions -func (seal *appSeal) shareSystem() { - // ensure Share (e.g. `/tmp/fortify.%d`) - // acl is unnecessary as this directory is world executable - seal.sys.Ensure(seal.SharePath, 0711) - - // ensure process-specific share (e.g. `/tmp/fortify.%d/%s`) - // acl is unnecessary as this directory is world executable - seal.share = path.Join(seal.SharePath, seal.id) - seal.sys.Ephemeral(system.Process, seal.share, 0711) - - // ensure child tmpdir parent directory (e.g. `/tmp/fortify.%d/tmpdir`) - targetTmpdirParent := path.Join(seal.SharePath, "tmpdir") - seal.sys.Ensure(targetTmpdirParent, 0700) - seal.sys.UpdatePermType(system.User, targetTmpdirParent, acl.Execute) - - // ensure child tmpdir (e.g. `/tmp/fortify.%d/tmpdir/%d`) - targetTmpdir := path.Join(targetTmpdirParent, seal.sys.user.as) - seal.sys.Ensure(targetTmpdir, 01700) - seal.sys.UpdatePermType(system.User, targetTmpdir, acl.Read, acl.Write, acl.Execute) - seal.sys.bwrap.Bind(targetTmpdir, "/tmp", false, true) -} - -func (seal *appSeal) sharePasswd(os linux.System) { - // look up shell - sh := "/bin/sh" - if s, ok := os.LookupEnv(shell); ok { - seal.sys.bwrap.SetEnv[shell] = s - sh = s - } - - // generate /etc/passwd - passwdPath := path.Join(seal.share, "passwd") - username := "chronos" - if seal.sys.user.username != "" { - username = seal.sys.user.username - } - homeDir := "/var/empty" - if seal.sys.user.home != "" { - homeDir = seal.sys.user.home - } - - // bind home directory - seal.sys.bwrap.Bind(seal.sys.user.data, homeDir, false, true) - seal.sys.bwrap.Chdir = homeDir - - seal.sys.bwrap.SetEnv["USER"] = username - seal.sys.bwrap.SetEnv["HOME"] = homeDir - - passwd := username + ":x:" + seal.sys.mappedIDString + ":" + seal.sys.mappedIDString + ":Fortify:" + homeDir + ":" + sh + "\n" - seal.sys.Write(passwdPath, passwd) - - // write /etc/group - groupPath := path.Join(seal.share, "group") - seal.sys.Write(groupPath, "fortify:x:"+seal.sys.mappedIDString+":\n") - - // bind /etc/passwd and /etc/group - seal.sys.bwrap.Bind(passwdPath, "/etc/passwd") - seal.sys.bwrap.Bind(groupPath, "/etc/group") -} diff --git a/internal/app/system.go b/internal/app/system.go index 8402854..241eafa 100644 --- a/internal/app/system.go +++ b/internal/app/system.go @@ -1,9 +1,7 @@ package app import ( - "git.gensokyo.uk/security/fortify/dbus" "git.gensokyo.uk/security/fortify/helper/bwrap" - "git.gensokyo.uk/security/fortify/internal/linux" "git.gensokyo.uk/security/fortify/internal/system" ) @@ -51,37 +49,3 @@ type appUser struct { // passwd database username username string } - -// shareAll calls all share methods in sequence -func (seal *appSeal) shareAll(bus [2]*dbus.Config, os linux.System) error { - if seal.shared { - panic("seal shared twice") - } - seal.shared = true - - seal.shareSystem() - seal.shareRuntime() - seal.sharePasswd(os) - if err := seal.shareDisplay(os); err != nil { - return err - } - if err := seal.sharePulse(os); err != nil { - return err - } - - // ensure dbus session bus defaults - if bus[0] == nil { - bus[0] = dbus.NewConfig(seal.fid, true, true) - } - - if err := seal.shareDBus(bus); err != nil { - return err - } - - // queue overriding tmpfs at the end of seal.sys.bwrap.Filesystem - for _, dest := range seal.sys.override { - seal.sys.bwrap.Tmpfs(dest, 8*1024) - } - - return nil -}