aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOphestra Umiker <cat@ophivana.moe>2024-10-10 12:44:08 +0900
committerOphestra Umiker <cat@ophivana.moe>2024-10-10 12:44:08 +0900
commit86cb5ac1dbd581078b10f5ec1eb723549c662a1b (patch)
tree9198789d0a47ebeda49b4e000013cfbf009b1b45
parent2220055e26bff8b426ebf086d09ba242e5180e76 (diff)
app: hardlink sockets to process-specific share local to XDG_RUNTIME_DIR
This avoids adding ACLs to the PulseAudio directory. Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
-rw-r--r--internal/app/share.display.go6
-rw-r--r--internal/app/share.pulse.go13
-rw-r--r--internal/app/share.runtime.go6
-rw-r--r--internal/app/system.go32
4 files changed, 47 insertions, 10 deletions
diff --git a/internal/app/share.display.go b/internal/app/share.display.go
index c1891876..9dbb6c3f 100644
--- a/internal/app/share.display.go
+++ b/internal/app/share.display.go
@@ -35,9 +35,11 @@ func (seal *appSeal) shareDisplay() error {
if wd, ok := os.LookupEnv(waylandDisplay); !ok {
return (*ErrDisplayEnv)(wrapError(ErrWayland, "WAYLAND_DISPLAY is not set"))
} else {
- // wayland socket path
+ // hardlink wayland socket
wp := path.Join(seal.RuntimePath, wd)
- seal.appendEnv(waylandDisplay, wp)
+ wpi := path.Join(seal.shareLocal, "wayland")
+ seal.sys.link(wp, wpi)
+ seal.appendEnv(waylandDisplay, wpi)
// ensure Wayland socket ACL (e.g. `/run/user/%d/wayland-%d`)
seal.sys.updatePerm(wp, acl.Read, acl.Write, acl.Execute)
diff --git a/internal/app/share.pulse.go b/internal/app/share.pulse.go
index 633b859d..b432345f 100644
--- a/internal/app/share.pulse.go
+++ b/internal/app/share.pulse.go
@@ -7,7 +7,6 @@ import (
"os"
"path"
- "git.ophivana.moe/cat/fortify/acl"
"git.ophivana.moe/cat/fortify/internal/state"
)
@@ -35,7 +34,7 @@ func (seal *appSeal) sharePulse() error {
return nil
}
- // ensure PulseAudio directory ACL (e.g. `/run/user/%d/pulse`)
+ // 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 {
@@ -47,10 +46,7 @@ func (seal *appSeal) sharePulse() error {
fmt.Sprintf("PulseAudio directory '%s' not found", pd)))
}
- seal.appendEnv(pulseServer, "unix:"+ps)
- seal.sys.updatePerm(pd, acl.Execute)
-
- // ensure PulseAudio socket permission (e.g. `/run/user/%d/pulse/native`)
+ // 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 (*PulseSocketAccessError)(wrapError(err,
@@ -65,6 +61,11 @@ func (seal *appSeal) sharePulse() error {
}
}
+ // hard link pulse socket into target-executable share
+ psi := path.Join(seal.shareLocal, "pulse")
+ seal.sys.link(ps, psi)
+ seal.appendEnv(pulseServer, "unix:"+psi)
+
// publish current user's pulse cookie for target user
if src, err := discoverPulseCookie(); err != nil {
return err
diff --git a/internal/app/share.runtime.go b/internal/app/share.runtime.go
index e43effe2..444d85a2 100644
--- a/internal/app/share.runtime.go
+++ b/internal/app/share.runtime.go
@@ -16,6 +16,7 @@ const (
func (seal *appSeal) shareRuntime() {
// ensure RunDir (e.g. `/run/user/%d/fortify`)
seal.sys.ensure(seal.RunDirPath, 0700)
+ seal.sys.updatePerm(seal.RunDirPath, acl.Execute)
// ensure runtime directory ACL (e.g. `/run/user/%d`)
seal.sys.updatePerm(seal.RuntimePath, acl.Execute)
@@ -28,6 +29,11 @@ func (seal *appSeal) shareRuntime() {
// acl is unnecessary as this directory is world executable
seal.share = path.Join(seal.SharePath, seal.id.String())
seal.sys.ensureEphemeral(seal.share, 0701)
+
+ // ensure process-specific share local to XDG_RUNTIME_DIR (e.g. `/run/user/%d/fortify/%s`)
+ seal.shareLocal = path.Join(seal.RunDirPath, seal.id.String())
+ seal.sys.ensureEphemeral(seal.shareLocal, 0700)
+ seal.sys.updatePerm(seal.shareLocal, acl.Execute)
}
func (seal *appSeal) shareRuntimeChild() string {
diff --git a/internal/app/system.go b/internal/app/system.go
index 86d97d9a..f3285027 100644
--- a/internal/app/system.go
+++ b/internal/app/system.go
@@ -33,6 +33,8 @@ type appSeal struct {
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
@@ -74,6 +76,8 @@ type appSealTx struct {
mkdir []appEnsureEntry
// dst, src pairs of temporarily shared files
tmpfiles [][2]string
+ // dst, src pairs of temporarily hard linked files
+ hardlinks [][2]string
// sealed path to fortify executable, used by shim
executable string
@@ -143,6 +147,11 @@ func (tx *appSealTx) copyFile(dst, src string) {
tx.updatePerm(dst, acl.Read)
}
+// link appends a hardlink action
+func (tx *appSealTx) link(oldname, newname string) {
+ tx.hardlinks = append(tx.hardlinks, [2]string{oldname, newname})
+}
+
type (
ChangeHostsError BaseError
EnsureDirError BaseError
@@ -152,7 +161,7 @@ type (
)
// commit applies recorded actions
-// order: xhost, mkdir, tmpfiles, dbus, acl
+// order: xhost, mkdir, tmpfiles, hardlinks, dbus, acl
func (tx *appSealTx) commit() error {
if tx.complete {
panic("seal transaction committed twice")
@@ -211,6 +220,18 @@ func (tx *appSealTx) commit() error {
}
}
+ // create hardlinks
+ for _, link := range tx.hardlinks {
+ verbose.Println("creating hardlink", link[1], "from", link[0])
+ if err := os.Link(link[0], link[1]); err != nil {
+ return (*TmpfileError)(wrapError(err,
+ fmt.Sprintf("cannot create hardlink '%s' from '%s': %s", link[1], link[0], err)))
+ } else {
+ // register partial commit
+ txp.link(link[0], link[1])
+ }
+ }
+
if tx.dbus != nil {
// start dbus proxy
verbose.Printf("session bus proxy on '%s' for upstream '%s'\n", tx.dbusAddr[0][1], tx.dbusAddr[0][0])
@@ -245,7 +266,7 @@ func (tx *appSealTx) commit() error {
}
// revert rolls back recorded actions
-// order: acl, dbus, tmpfiles, mkdir, xhost
+// order: acl, dbus, hardlinks, tmpfiles, mkdir, xhost
// errors are printed but not treated as fatal
func (tx *appSealTx) revert(global bool) error {
if tx.closed {
@@ -279,6 +300,13 @@ func (tx *appSealTx) revert(global bool) error {
joinError(err, "cannot stop message bus proxy:", err)
}
+ // remove hardlinks
+ for _, link := range tx.hardlinks {
+ verbose.Println("removing hardlink", link[1])
+ err := os.Remove(link[1])
+ joinError(err, fmt.Sprintf("cannot remove hardlink '%s': %s", link[1], err))
+ }
+
// remove tmpfiles
for _, tmpfile := range tx.tmpfiles {
verbose.Println("removing tmpfile", tmpfile[0])