aboutsummaryrefslogtreecommitdiffhomepage
path: root/dbus/proc.go
diff options
context:
space:
mode:
Diffstat (limited to 'dbus/proc.go')
-rw-r--r--dbus/proc.go201
1 files changed, 201 insertions, 0 deletions
diff --git a/dbus/proc.go b/dbus/proc.go
new file mode 100644
index 00000000..379f9fd3
--- /dev/null
+++ b/dbus/proc.go
@@ -0,0 +1,201 @@
+package dbus
+
+import (
+ "context"
+ "errors"
+ "io"
+ "os"
+ "os/exec"
+ "path"
+ "path/filepath"
+ "slices"
+ "strconv"
+ "strings"
+ "syscall"
+
+ "git.gensokyo.uk/security/fortify/helper"
+ "git.gensokyo.uk/security/fortify/helper/bwrap"
+ "git.gensokyo.uk/security/fortify/ldd"
+)
+
+// Start launches the D-Bus proxy.
+func (p *Proxy) Start(ctx context.Context, output io.Writer, sandbox bool) error {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ if p.seal == nil {
+ return errors.New("proxy not sealed")
+ }
+
+ var h helper.Helper
+
+ c, cancel := context.WithCancelCause(ctx)
+ if !sandbox {
+ h = helper.NewDirect(c, p.name, p.seal, true, argF, func(cmd *exec.Cmd) {
+ cmdF(cmd, output, p.CmdF)
+
+ // xdg-dbus-proxy does not need to inherit the environment
+ cmd.Env = make([]string, 0)
+ }, nil)
+ } else {
+ // look up absolute path if name is just a file name
+ toolPath := p.name
+ if filepath.Base(p.name) == p.name {
+ if s, err := exec.LookPath(p.name); err != nil {
+ return err
+ } else {
+ toolPath = s
+ }
+ }
+
+ // resolve libraries by parsing ldd output
+ var proxyDeps []*ldd.Entry
+ if toolPath != os.Args[0] {
+ if l, err := ldd.Exec(ctx, toolPath); err != nil {
+ return err
+ } else {
+ proxyDeps = l
+ }
+ }
+
+ bc := &bwrap.Config{
+ Hostname: "fortify-dbus",
+ Chdir: "/",
+ Syscall: &bwrap.SyscallPolicy{DenyDevel: true, Multiarch: true},
+ Clearenv: true,
+ NewSession: true,
+ DieWithParent: true,
+ }
+
+ // resolve proxy socket directories
+ bindTargetM := make(map[string]struct{}, 2)
+
+ for _, ps := range []string{p.session[1], p.system[1]} {
+ if pd := path.Dir(ps); len(pd) > 0 {
+ if pd[0] == '/' {
+ bindTargetM[pd] = struct{}{}
+ }
+ }
+ }
+
+ bindTarget := make([]string, 0, len(bindTargetM))
+ for k := range bindTargetM {
+ bindTarget = append(bindTarget, k)
+ }
+ slices.Sort(bindTarget)
+ for _, name := range bindTarget {
+ bc.Bind(name, name, false, true)
+ }
+
+ roBindTargetM := make(map[string]struct{}, 2+1+len(proxyDeps))
+
+ // xdb-dbus-proxy bin and dependencies
+ roBindTargetM[path.Dir(toolPath)] = struct{}{}
+ for _, ent := range proxyDeps {
+ if path.IsAbs(ent.Path) {
+ roBindTargetM[path.Dir(ent.Path)] = struct{}{}
+ }
+ if path.IsAbs(ent.Name) {
+ roBindTargetM[path.Dir(ent.Name)] = struct{}{}
+ }
+ }
+
+ // resolve upstream bus directories
+ for _, as := range []string{p.session[0], p.system[0]} {
+ if len(as) > 0 && strings.HasPrefix(as, "unix:path=/") {
+ // leave / intact
+ roBindTargetM[path.Dir(as[10:])] = struct{}{}
+ }
+ }
+
+ roBindTarget := make([]string, 0, len(roBindTargetM))
+ for k := range roBindTargetM {
+ roBindTarget = append(roBindTarget, k)
+ }
+ slices.Sort(roBindTarget)
+ for _, name := range roBindTarget {
+ bc.Bind(name, name)
+ }
+
+ h = helper.MustNewBwrap(c, toolPath,
+ p.seal, true,
+ argF, func(cmd *exec.Cmd) { cmdF(cmd, output, p.CmdF) },
+ nil,
+ bc, nil,
+ )
+ p.bwrap = bc
+ }
+
+ if err := h.Start(); err != nil {
+ cancel(err)
+ return err
+ }
+
+ p.helper = h
+ p.ctx = c
+ p.cancel = cancel
+ return nil
+}
+
+var proxyClosed = errors.New("proxy closed")
+
+// Wait blocks until xdg-dbus-proxy exits and releases resources.
+func (p *Proxy) Wait() error {
+ p.lock.RLock()
+ defer p.lock.RUnlock()
+
+ if p.helper == nil {
+ return errors.New("dbus: not started")
+ }
+
+ errs := make([]error, 3)
+
+ errs[0] = p.helper.Wait()
+ if p.cancel == nil &&
+ errors.Is(errs[0], context.Canceled) &&
+ errors.Is(context.Cause(p.ctx), proxyClosed) {
+ errs[0] = nil
+ }
+
+ // ensure socket removal so ephemeral directory is empty at revert
+ if err := os.Remove(p.session[1]); err != nil && !errors.Is(err, os.ErrNotExist) {
+ errs[1] = err
+ }
+ if p.sysP {
+ if err := os.Remove(p.system[1]); err != nil && !errors.Is(err, os.ErrNotExist) {
+ errs[2] = err
+ }
+ }
+
+ return errors.Join(errs...)
+}
+
+// Close cancels the context passed to the helper instance attached to xdg-dbus-proxy.
+func (p *Proxy) Close() {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ if p.cancel == nil {
+ panic("dbus: not started")
+ }
+ p.cancel(proxyClosed)
+ p.cancel = nil
+}
+
+func argF(argsFd, statFd int) []string {
+ if statFd == -1 {
+ return []string{"--args=" + strconv.Itoa(argsFd)}
+ } else {
+ return []string{"--args=" + strconv.Itoa(argsFd), "--fd=" + strconv.Itoa(statFd)}
+ }
+}
+
+func cmdF(cmd *exec.Cmd, output io.Writer, cmdF func(cmd *exec.Cmd)) {
+ cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
+ if output != nil {
+ cmd.Stdout, cmd.Stderr = output, output
+ }
+ if cmdF != nil {
+ cmdF(cmd)
+ }
+}