aboutsummaryrefslogtreecommitdiffhomepage
path: root/dbus/run.go
blob: addf5226a02c78fe200cbd928ea9def2eb7aa86f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package dbus

import (
	"errors"
	"io"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"strings"

	"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 and sets up the Wait method.
// ready should be buffered and must only be received from once.
func (p *Proxy) Start(ready chan error, 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
		cmd *exec.Cmd

		argF = func(argsFD, statFD int) []string {
			if statFD == -1 {
				return []string{"--args=" + strconv.Itoa(argsFD)}
			} else {
				return []string{"--args=" + strconv.Itoa(argsFD), "--fd=" + strconv.Itoa(statFD)}
			}
		}
	)

	if !sandbox {
		h = helper.New(p.seal, p.name, argF)
		cmd = h.Unwrap()
		// xdg-dbus-proxy does not need to inherit the environment
		cmd.Env = []string{}
	} 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 != "/nonexistent-xdg-dbus-proxy" {
			if l, err := ldd.Exec(toolPath); err != nil {
				return err
			} else {
				proxyDeps = l
			}
		}

		bc := &bwrap.Config{
			Unshare:       nil,
			Hostname:      "fortify-dbus",
			Chdir:         "/",
			Clearenv:      true,
			NewSession:    true,
			DieWithParent: true,
		}

		// resolve proxy socket directories
		bindTarget := 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] == '/' {
					bindTarget[pd] = struct{}{}
				}
			}
		}
		for k := range bindTarget {
			bc.Bind(k, k, false, true)
		}

		roBindTarget := make(map[string]struct{}, 2+1+len(proxyDeps))

		// xdb-dbus-proxy bin and dependencies
		roBindTarget[path.Dir(toolPath)] = struct{}{}
		for _, ent := range proxyDeps {
			if path.IsAbs(ent.Path) {
				roBindTarget[path.Dir(ent.Path)] = struct{}{}
			}
			if path.IsAbs(ent.Name) {
				roBindTarget[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
				roBindTarget[path.Dir(as[10:])] = struct{}{}
			}
		}

		for k := range roBindTarget {
			bc.Bind(k, k)
		}

		h = helper.MustNewBwrap(bc, toolPath, p.seal, argF, nil)
		cmd = h.Unwrap()
		p.bwrap = bc
	}

	if output != nil {
		cmd.Stdout = output
		cmd.Stderr = output
	}
	if err := h.StartNotify(ready); err != nil {
		return err
	}

	p.helper = h
	return nil
}

// Wait waits for xdg-dbus-proxy to exit or fault.
func (p *Proxy) Wait() error {
	p.lock.RLock()
	defer p.lock.RUnlock()

	if p.helper == nil {
		return errors.New("proxy not started")
	}

	return p.helper.Wait()
}

// Close closes the status file descriptor passed to xdg-dbus-proxy, causing it to stop.
func (p *Proxy) Close() error {
	return p.helper.Close()
}