aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--internal/app/share.display.go2
-rw-r--r--internal/app/share.runtime.go9
-rw-r--r--internal/app/start.go42
-rw-r--r--internal/app/system.go47
4 files changed, 72 insertions, 28 deletions
diff --git a/internal/app/share.display.go b/internal/app/share.display.go
index 9dbb6c3f..51ac54d3 100644
--- a/internal/app/share.display.go
+++ b/internal/app/share.display.go
@@ -42,7 +42,7 @@ func (seal *appSeal) shareDisplay() error {
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)
+ seal.sys.updatePermTag(state.EnableWayland, wp, acl.Read, acl.Write, acl.Execute)
}
}
diff --git a/internal/app/share.runtime.go b/internal/app/share.runtime.go
index 444d85a2..b015d45c 100644
--- a/internal/app/share.runtime.go
+++ b/internal/app/share.runtime.go
@@ -4,6 +4,7 @@ import (
"path"
"git.ophivana.moe/cat/fortify/acl"
+ "git.ophivana.moe/cat/fortify/internal/state"
)
const (
@@ -16,10 +17,10 @@ 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)
+ seal.sys.updatePermTag(state.EnableLength, seal.RunDirPath, acl.Execute)
// ensure runtime directory ACL (e.g. `/run/user/%d`)
- seal.sys.updatePerm(seal.RuntimePath, acl.Execute)
+ seal.sys.updatePermTag(state.EnableLength, seal.RuntimePath, acl.Execute)
// ensure Share (e.g. `/tmp/fortify.%d`)
// acl is unnecessary as this directory is world executable
@@ -40,12 +41,12 @@ func (seal *appSeal) shareRuntimeChild() string {
// ensure child runtime parent directory (e.g. `/tmp/fortify.%d/runtime`)
targetRuntimeParent := path.Join(seal.SharePath, "runtime")
seal.sys.ensure(targetRuntimeParent, 0700)
- seal.sys.updatePerm(targetRuntimeParent, acl.Execute)
+ seal.sys.updatePermTag(state.EnableLength, targetRuntimeParent, acl.Execute)
// ensure child runtime directory (e.g. `/tmp/fortify.%d/runtime/%d`)
targetRuntime := path.Join(targetRuntimeParent, seal.sys.Uid)
seal.sys.ensure(targetRuntime, 0700)
- seal.sys.updatePerm(targetRuntime, acl.Read, acl.Write, acl.Execute)
+ seal.sys.updatePermTag(state.EnableLength, targetRuntime, acl.Read, acl.Write, acl.Execute)
// point to ensured runtime path
seal.appendEnv(xdgRuntimeDir, targetRuntime)
diff --git a/internal/app/start.go b/internal/app/start.go
index db6c5f14..02fad973 100644
--- a/internal/app/start.go
+++ b/internal/app/start.go
@@ -155,24 +155,42 @@ func (a *app) Wait() (int, error) {
return err
}
- var global bool
-
- // measure remaining state entries
- if l, err := b.Len(); err != nil {
+ // enablements of remaining launchers
+ rt, tags := new(state.Enablements), new(state.Enablements)
+ tags.Set(state.EnableLength + 1)
+ if states, err := b.Load(); err != nil {
return err
} else {
- // clean up global modifications if we're the last launcher alive
- global = l == 0
-
- if !global {
- verbose.Printf("found %d active launchers, cleaning up without globals\n", l)
- } else {
+ if l := len(states); l == 0 {
+ // cleanup globals as the final launcher
verbose.Println("no other launchers active, will clean up globals")
+ tags.Set(state.EnableLength)
+ } else {
+ verbose.Printf("found %d active launchers, cleaning up without globals\n", l)
+ }
+
+ // accumulate capabilities of other launchers
+ for _, s := range states {
+ *rt |= s.Capability
+ }
+ }
+ // invert accumulated enablements for cleanup
+ for i := state.Enablement(0); i < state.EnableLength; i++ {
+ if !rt.Has(i) {
+ tags.Set(i)
+ }
+ }
+ if verbose.Get() {
+ ct := make([]state.Enablement, 0, state.EnableLength)
+ for i := state.Enablement(0); i < state.EnableLength; i++ {
+ if tags.Has(i) {
+ ct = append(ct, i)
+ }
}
+ verbose.Println("will revert operations tagged", ct, "as no remaining launchers hold these enablements")
}
- // FIXME: depending on exit sequence, some parts of the transaction never gets reverted
- if err := a.seal.sys.revert(global); err != nil {
+ if err := a.seal.sys.revert(tags); err != nil {
return err.(RevertCompoundError)
}
diff --git a/internal/app/system.go b/internal/app/system.go
index f3285027..aa62cfb0 100644
--- a/internal/app/system.go
+++ b/internal/app/system.go
@@ -112,10 +112,22 @@ func (tx *appSealTx) ensureEphemeral(path string, perm os.FileMode) {
// appACLEntry contains information for applying/reverting an ACL entry
type appACLEntry struct {
+ tag state.Enablement
path string
perms []acl.Perm
}
+func (e *appACLEntry) ts() string {
+ switch e.tag {
+ case state.EnableLength:
+ return "Global"
+ case state.EnableLength + 1:
+ return "Process"
+ default:
+ return e.tag.String()
+ }
+}
+
func (e *appACLEntry) String() string {
var s = []byte("---")
for _, p := range e.perms {
@@ -131,9 +143,16 @@ func (e *appACLEntry) String() string {
return string(s)
}
-// updatePerm appends an acl update action
+// updatePerm appends an untagged acl update action
func (tx *appSealTx) updatePerm(path string, perms ...acl.Perm) {
- tx.acl = append(tx.acl, &appACLEntry{path, perms})
+ tx.updatePermTag(state.EnableLength+1, path, perms...)
+}
+
+// updatePermTag appends an acl update action
+// Tagging with state.EnableLength sets cleanup to happen at final active launcher exit,
+// while tagging with state.EnableLength+1 will unconditionally clean up on exit.
+func (tx *appSealTx) updatePermTag(tag state.Enablement, path string, perms ...acl.Perm) {
+ tx.acl = append(tx.acl, &appACLEntry{tag, path, perms})
}
// changeHosts appends target username of an X11 ChangeHosts action
@@ -175,7 +194,11 @@ func (tx *appSealTx) commit() error {
// global changes (x11, ACLs) are always repeated and check for other launchers cannot happen here
// attempting cleanup here will cause other fortified processes to lose access to them
// a better (and more secure) fix is to proxy access to these resources and eliminate the ACLs altogether
- if err := txp.revert(false); err != nil {
+ tags := new(state.Enablements)
+ for e := state.Enablement(0); e < state.EnableLength+2; e++ {
+ tags.Set(e)
+ }
+ if err := txp.revert(tags); err != nil {
fmt.Println("fortify: errors returned reverting partial commit:", err)
}
}
@@ -250,13 +273,13 @@ func (tx *appSealTx) commit() error {
// apply ACLs
for _, e := range tx.acl {
- verbose.Println("applying ACL", e, "uid:", tx.Uid, "path:", e.path)
+ verbose.Println("applying ACL", e, "uid:", tx.Uid, "tag:", e.ts(), "path:", e.path)
if err := acl.UpdatePerm(e.path, tx.uid, e.perms...); err != nil {
return (*ACLUpdateError)(wrapError(err,
fmt.Sprintf("cannot apply ACL to '%s': %s", e.path, err)))
} else {
// register partial commit
- txp.updatePerm(e.path, e.perms...)
+ txp.updatePermTag(e.tag, e.path, e.perms...)
}
}
@@ -268,7 +291,7 @@ func (tx *appSealTx) commit() error {
// revert rolls back recorded actions
// order: acl, dbus, hardlinks, tmpfiles, mkdir, xhost
// errors are printed but not treated as fatal
-func (tx *appSealTx) revert(global bool) error {
+func (tx *appSealTx) revert(tags *state.Enablements) error {
if tx.closed {
panic("seal transaction reverted twice")
}
@@ -284,12 +307,14 @@ func (tx *appSealTx) revert(global bool) error {
errs = append(errs, e)
}
- if global {
- // revert ACLs
- for _, e := range tx.acl {
- verbose.Println("stripping ACL", e, "uid:", tx.Uid, "path:", e.path)
+ // revert ACLs
+ for _, e := range tx.acl {
+ if tags.Has(e.tag) {
+ verbose.Println("stripping ACL", e, "uid:", tx.Uid, "tag:", e.ts(), "path:", e.path)
err := acl.UpdatePerm(e.path, tx.uid)
joinError(err, fmt.Sprintf("cannot strip ACL entry from '%s': %s", e.path, err))
+ } else {
+ verbose.Println("skipping ACL", e, "uid:", tx.Uid, "tag:", e.ts(), "path:", e.path)
}
}
@@ -326,7 +351,7 @@ func (tx *appSealTx) revert(global bool) error {
joinError(err, fmt.Sprintf("cannot remove ephemeral directory '%s': %s", dir.path, err))
}
- if global {
+ if tags.Has(state.EnableX) {
// rollback xhost insertions
for _, username := range tx.xhost {
verbose.Printf("deleting XHost entry SI:localuser:%s\n", username)