dbus: clean up wrapper implementation
All checks were successful
Test / Create distribution (push) Successful in 27s
Test / Sandbox (push) Successful in 1m50s
Test / Fortify (push) Successful in 2m49s
Test / Sandbox (race detector) (push) Successful in 3m4s
Test / Fpkg (push) Successful in 3m35s
Test / Fortify (race detector) (push) Successful in 4m13s
Test / Flake checks (push) Successful in 1m3s
All checks were successful
Test / Create distribution (push) Successful in 27s
Test / Sandbox (push) Successful in 1m50s
Test / Fortify (push) Successful in 2m49s
Test / Sandbox (race detector) (push) Successful in 3m4s
Test / Fpkg (push) Successful in 3m35s
Test / Fortify (race detector) (push) Successful in 4m13s
Test / Flake checks (push) Successful in 1m3s
The dbus proxy wrapper haven't been updated much ever since the helper interface was introduced. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
e587112e63
commit
5979d8b1e0
@ -7,6 +7,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ProxyPair is an upstream dbus address and a downstream socket path.
|
||||||
|
type ProxyPair [2]string
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// See set 'see' policy for NAME (--see=NAME)
|
// See set 'see' policy for NAME (--see=NAME)
|
||||||
See []string `json:"see"`
|
See []string `json:"see"`
|
||||||
@ -24,7 +27,7 @@ type Config struct {
|
|||||||
Filter bool `json:"filter"`
|
Filter bool `json:"filter"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) Args(bus [2]string) (args []string) {
|
func (c *Config) Args(bus ProxyPair) (args []string) {
|
||||||
argc := 2 + len(c.See) + len(c.Talk) + len(c.Own) + len(c.Call) + len(c.Broadcast)
|
argc := 2 + len(c.See) + len(c.Talk) + len(c.Own) + len(c.Call) + len(c.Broadcast)
|
||||||
if c.Log {
|
if c.Log {
|
||||||
argc++
|
argc++
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
@ -19,64 +20,21 @@ import (
|
|||||||
"git.gensokyo.uk/security/fortify/sandbox"
|
"git.gensokyo.uk/security/fortify/sandbox"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNew(t *testing.T) {
|
func TestFinalise(t *testing.T) {
|
||||||
for _, tc := range [][2][2]string{
|
if _, err := dbus.Finalise(dbus.ProxyPair{}, dbus.ProxyPair{}, nil, nil); !errors.Is(err, syscall.EBADE) {
|
||||||
{
|
t.Errorf("Finalise: error = %v, want %v",
|
||||||
{"unix:path=/run/user/1971/bus", "/tmp/fortify.1971/1ca5d183ef4c99e74c3e544715f32702/bus"},
|
err, syscall.EBADE)
|
||||||
{"unix:path=/run/dbus/system_bus_socket", "/tmp/fortify.1971/1ca5d183ef4c99e74c3e544715f32702/system_bus_socket"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{"unix:path=/run/user/1971/bus", "/tmp/fortify.1971/881ac3796ff3f3bf0a773824383187a0/bus"},
|
|
||||||
{"unix:path=/run/dbus/system_bus_socket", "/tmp/fortify.1971/881ac3796ff3f3bf0a773824383187a0/system_bus_socket"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{"unix:path=/run/user/1971/bus", "/tmp/fortify.1971/3d1a5084520ef79c0c6a49a675bac701/bus"},
|
|
||||||
{"unix:path=/run/dbus/system_bus_socket", "/tmp/fortify.1971/3d1a5084520ef79c0c6a49a675bac701/system_bus_socket"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{"unix:path=/run/user/1971/bus", "/tmp/fortify.1971/2a1639bab712799788ea0ff7aa280c35/bus"},
|
|
||||||
{"unix:path=/run/dbus/system_bus_socket", "/tmp/fortify.1971/2a1639bab712799788ea0ff7aa280c35/system_bus_socket"},
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
t.Run("create instance for "+tc[0][0]+" and "+tc[1][0], func(t *testing.T) {
|
|
||||||
if got := dbus.New(tc[0], tc[1]); !got.CompareTestNew(tc[0], tc[1]) {
|
|
||||||
t.Errorf("New(%q, %q) = %v",
|
|
||||||
tc[0], tc[1],
|
|
||||||
got)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProxy_Seal(t *testing.T) {
|
|
||||||
t.Run("double seal panic", func(t *testing.T) {
|
|
||||||
defer func() {
|
|
||||||
want := "dbus proxy sealed twice"
|
|
||||||
if r := recover(); r != want {
|
|
||||||
t.Errorf("Seal: panic = %q, want %q",
|
|
||||||
r, want)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
p := dbus.New([2]string{}, [2]string{})
|
|
||||||
_ = p.Seal(dbus.NewConfig("", true, false), nil)
|
|
||||||
_ = p.Seal(dbus.NewConfig("", true, false), nil)
|
|
||||||
})
|
|
||||||
|
|
||||||
ep := dbus.New([2]string{}, [2]string{})
|
|
||||||
if err := ep.Seal(nil, nil); !errors.Is(err, dbus.ErrConfig) {
|
|
||||||
t.Errorf("Seal(nil, nil) error = %v, want %v",
|
|
||||||
err, dbus.ErrConfig)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for id, tc := range testCasePairs() {
|
for id, tc := range testCasePairs() {
|
||||||
t.Run("create seal for "+id, func(t *testing.T) {
|
t.Run("create final for "+id, func(t *testing.T) {
|
||||||
p := dbus.New(tc[0].bus, tc[1].bus)
|
var wt io.WriterTo
|
||||||
if err := p.Seal(tc[0].c, tc[1].c); (errors.Is(err, syscall.EINVAL)) != tc[0].wantErr {
|
if v, err := dbus.Finalise(tc[0].bus, tc[1].bus, tc[0].c, tc[1].c); (errors.Is(err, syscall.EINVAL)) != tc[0].wantErr {
|
||||||
t.Errorf("Seal(%p, %p) error = %v, wantErr %v",
|
t.Errorf("Finalise: error = %v, wantErr %v",
|
||||||
tc[0].c, tc[1].c,
|
|
||||||
err, tc[0].wantErr)
|
err, tc[0].wantErr)
|
||||||
return
|
return
|
||||||
|
} else {
|
||||||
|
wt = v
|
||||||
}
|
}
|
||||||
|
|
||||||
// rest of the tests happen for sealed instances
|
// rest of the tests happen for sealed instances
|
||||||
@ -89,25 +47,23 @@ func TestProxy_Seal(t *testing.T) {
|
|||||||
args := append(tc[0].want, tc[1].want...)
|
args := append(tc[0].want, tc[1].want...)
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
want.WriteString(arg)
|
want.WriteString(arg)
|
||||||
want.WriteByte('\x00')
|
want.WriteByte(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
wt := p.AccessTestProxySeal()
|
|
||||||
got := new(strings.Builder)
|
got := new(strings.Builder)
|
||||||
if _, err := wt.WriteTo(got); err != nil {
|
if _, err := wt.WriteTo(got); err != nil {
|
||||||
t.Errorf("p.seal.WriteTo(): %v", err)
|
t.Errorf("WriteTo: error = %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if want.String() != got.String() {
|
if want.String() != got.String() {
|
||||||
t.Errorf("Seal(%p, %p) seal = %v, want %v",
|
t.Errorf("Seal: %q, want %q",
|
||||||
tc[0].c, tc[1].c,
|
|
||||||
got.String(), want.String())
|
got.String(), want.String())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProxy_Start_Wait_Close_String(t *testing.T) {
|
func TestProxyStartWaitCloseString(t *testing.T) {
|
||||||
oldWaitDelay := helper.WaitDelay
|
oldWaitDelay := helper.WaitDelay
|
||||||
helper.WaitDelay = 16 * time.Second
|
helper.WaitDelay = 16 * time.Second
|
||||||
t.Cleanup(func() { helper.WaitDelay = oldWaitDelay })
|
t.Cleanup(func() { helper.WaitDelay = oldWaitDelay })
|
||||||
@ -116,29 +72,62 @@ func TestProxy_Start_Wait_Close_String(t *testing.T) {
|
|||||||
proxyName := dbus.ProxyName
|
proxyName := dbus.ProxyName
|
||||||
dbus.ProxyName = os.Args[0]
|
dbus.ProxyName = os.Args[0]
|
||||||
t.Cleanup(func() { dbus.ProxyName = proxyName })
|
t.Cleanup(func() { dbus.ProxyName = proxyName })
|
||||||
testProxyStartWaitCloseString(t, true)
|
testProxyFinaliseStartWaitCloseString(t, true)
|
||||||
})
|
})
|
||||||
t.Run("direct", func(t *testing.T) { testProxyStartWaitCloseString(t, false) })
|
t.Run("direct", func(t *testing.T) { testProxyFinaliseStartWaitCloseString(t, false) })
|
||||||
}
|
}
|
||||||
|
|
||||||
func testProxyStartWaitCloseString(t *testing.T, useSandbox bool) {
|
func testProxyFinaliseStartWaitCloseString(t *testing.T, useSandbox bool) {
|
||||||
|
var p *dbus.Proxy
|
||||||
|
|
||||||
|
t.Run("string for nil proxy", func(t *testing.T) {
|
||||||
|
want := "(invalid dbus proxy)"
|
||||||
|
if got := p.String(); got != want {
|
||||||
|
t.Errorf("String: %q, want %q",
|
||||||
|
got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("invalid start", func(t *testing.T) {
|
||||||
|
if !useSandbox {
|
||||||
|
p = dbus.NewDirect(context.TODO(), nil, nil)
|
||||||
|
} else {
|
||||||
|
p = dbus.New(context.TODO(), nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.Start(); !errors.Is(err, syscall.ENOTRECOVERABLE) {
|
||||||
|
t.Errorf("Start: error = %q, wantErr %q",
|
||||||
|
err, syscall.ENOTRECOVERABLE)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
for id, tc := range testCasePairs() {
|
for id, tc := range testCasePairs() {
|
||||||
// this test does not test errors
|
// this test does not test errors
|
||||||
if tc[0].wantErr {
|
if tc[0].wantErr {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("string for nil proxy", func(t *testing.T) {
|
t.Run("proxy for "+id, func(t *testing.T) {
|
||||||
var p *dbus.Proxy
|
var final *dbus.Final
|
||||||
want := "(invalid dbus proxy)"
|
t.Run("finalise", func(t *testing.T) {
|
||||||
if got := p.String(); got != want {
|
if v, err := dbus.Finalise(tc[0].bus, tc[1].bus, tc[0].c, tc[1].c); err != nil {
|
||||||
t.Errorf("String() = %v, want %v",
|
t.Errorf("Finalise: error = %v, wantErr %v",
|
||||||
got, want)
|
err, tc[0].wantErr)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
final = v
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("proxy for "+id, func(t *testing.T) {
|
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
|
||||||
p := dbus.New(tc[0].bus, tc[1].bus)
|
defer cancel()
|
||||||
|
if !useSandbox {
|
||||||
|
p = dbus.NewDirect(ctx, final, nil)
|
||||||
|
} else {
|
||||||
|
p = dbus.New(ctx, final, nil)
|
||||||
|
}
|
||||||
|
|
||||||
p.CommandContext = func(ctx context.Context) (cmd *exec.Cmd) {
|
p.CommandContext = func(ctx context.Context) (cmd *exec.Cmd) {
|
||||||
return exec.CommandContext(ctx, os.Args[0], "-test.v",
|
return exec.CommandContext(ctx, os.Args[0], "-test.v",
|
||||||
"-test.run=TestHelperInit", "--", "init")
|
"-test.run=TestHelperInit", "--", "init")
|
||||||
@ -163,57 +152,26 @@ func testProxyStartWaitCloseString(t *testing.T, useSandbox bool) {
|
|||||||
p.FilterF = func(v []byte) []byte { return bytes.SplitN(v, []byte("TestHelperInit\n"), 2)[1] }
|
p.FilterF = func(v []byte) []byte { return bytes.SplitN(v, []byte("TestHelperInit\n"), 2)[1] }
|
||||||
output := new(strings.Builder)
|
output := new(strings.Builder)
|
||||||
|
|
||||||
t.Run("unsealed", func(t *testing.T) {
|
t.Run("invalid wait", func(t *testing.T) {
|
||||||
t.Run("string", func(t *testing.T) {
|
|
||||||
want := "(unsealed dbus proxy)"
|
|
||||||
if got := p.String(); got != want {
|
|
||||||
t.Errorf("String() = %v, want %v",
|
|
||||||
got, want)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("start", func(t *testing.T) {
|
|
||||||
want := "proxy not sealed"
|
|
||||||
if err := p.Start(context.Background(), nil, useSandbox); err == nil || err.Error() != want {
|
|
||||||
t.Errorf("Start() error = %v, wantErr %q",
|
|
||||||
err, errors.New(want))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("wait", func(t *testing.T) {
|
|
||||||
wantErr := "dbus: not started"
|
wantErr := "dbus: not started"
|
||||||
if err := p.Wait(); err == nil || err.Error() != wantErr {
|
if err := p.Wait(); err == nil || err.Error() != wantErr {
|
||||||
t.Errorf("Wait() error = %v, wantErr %v",
|
t.Errorf("Wait: error = %v, wantErr %v",
|
||||||
err, wantErr)
|
err, wantErr)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("seal with "+id, func(t *testing.T) {
|
t.Run("string", func(t *testing.T) {
|
||||||
if err := p.Seal(tc[0].c, tc[1].c); err != nil {
|
want := "(unused dbus proxy)"
|
||||||
t.Errorf("Seal(%p, %p) error = %v, wantErr %v",
|
|
||||||
tc[0].c, tc[1].c,
|
|
||||||
err, tc[0].wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("sealed", func(t *testing.T) {
|
|
||||||
want := strings.Join(append(tc[0].want, tc[1].want...), " ")
|
|
||||||
if got := p.String(); got != want {
|
if got := p.String(); got != want {
|
||||||
t.Errorf("String() = %v, want %v",
|
t.Errorf("String: %q, want %q",
|
||||||
got, want)
|
got, want)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("start", func(t *testing.T) {
|
t.Run("start", func(t *testing.T) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
if err := p.Start(); err != nil {
|
||||||
defer cancel()
|
t.Fatalf("Start: error = %v",
|
||||||
|
|
||||||
if err := p.Start(ctx, output, useSandbox); err != nil {
|
|
||||||
t.Fatalf("Start(nil, nil) error = %v",
|
|
||||||
err)
|
err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,19 +181,23 @@ func testProxyStartWaitCloseString(t *testing.T, useSandbox bool) {
|
|||||||
wantSubstr = fmt.Sprintf(`argv: ["%s" "-test.run=TestHelperStub" "--" "--args=3" "--fd=4"], flags: 0x0, seccomp: 0x3e`, os.Args[0])
|
wantSubstr = fmt.Sprintf(`argv: ["%s" "-test.run=TestHelperStub" "--" "--args=3" "--fd=4"], flags: 0x0, seccomp: 0x3e`, os.Args[0])
|
||||||
}
|
}
|
||||||
if got := p.String(); !strings.Contains(got, wantSubstr) {
|
if got := p.String(); !strings.Contains(got, wantSubstr) {
|
||||||
t.Errorf("String() = %v, want %v",
|
t.Errorf("String: %q, want %q",
|
||||||
p.String(), wantSubstr)
|
got, wantSubstr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("wait", func(t *testing.T) {
|
t.Run("wait", func(t *testing.T) {
|
||||||
p.Close()
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
if err := p.Wait(); err != nil {
|
if err := p.Wait(); err != nil {
|
||||||
t.Errorf("Wait() error = %v\noutput: %s",
|
t.Errorf("Wait: error = %v\noutput: %s",
|
||||||
err, output.String())
|
err, output.String())
|
||||||
}
|
}
|
||||||
})
|
close(done)
|
||||||
|
}()
|
||||||
|
p.Close()
|
||||||
|
<-done
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package dbus
|
package dbus
|
||||||
|
|
||||||
import "io"
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
// CompareTestNew provides TestNew with comparison access to unexported Proxy fields.
|
// NewDirect returns a new instance of [Proxy] with its sandbox disabled.
|
||||||
func (p *Proxy) CompareTestNew(session, system [2]string) bool {
|
func NewDirect(ctx context.Context, final *Final, output io.Writer) *Proxy {
|
||||||
return session == p.session && system == p.system
|
p := New(ctx, final, output)
|
||||||
}
|
p.useSandbox = false
|
||||||
|
return p
|
||||||
// AccessTestProxySeal provides TestProxy_Seal with access to unexported Proxy seal field.
|
|
||||||
func (p *Proxy) AccessTestProxySeal() io.WriterTo {
|
|
||||||
return p.seal
|
|
||||||
}
|
}
|
||||||
|
94
dbus/proc.go
94
dbus/proc.go
@ -3,14 +3,12 @@ package dbus
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"git.gensokyo.uk/security/fortify/helper"
|
"git.gensokyo.uk/security/fortify/helper"
|
||||||
@ -19,25 +17,30 @@ import (
|
|||||||
"git.gensokyo.uk/security/fortify/sandbox/seccomp"
|
"git.gensokyo.uk/security/fortify/sandbox/seccomp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Start launches the D-Bus proxy.
|
// Start starts and configures a D-Bus proxy process.
|
||||||
func (p *Proxy) Start(ctx context.Context, output io.Writer, useSandbox bool) error {
|
func (p *Proxy) Start() error {
|
||||||
p.lock.Lock()
|
if p.final == nil || p.final.WriterTo == nil {
|
||||||
defer p.lock.Unlock()
|
return syscall.ENOTRECOVERABLE
|
||||||
|
|
||||||
if p.seal == nil {
|
|
||||||
return errors.New("proxy not sealed")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var h helper.Helper
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
p.pmu.Lock()
|
||||||
|
defer p.pmu.Unlock()
|
||||||
|
|
||||||
c, cancel := context.WithCancelCause(ctx)
|
if p.cancel != nil || p.cause != nil {
|
||||||
if !useSandbox {
|
return errors.New("dbus: already started")
|
||||||
h = helper.NewDirect(c, p.name, p.seal, true, argF, func(cmd *exec.Cmd) {
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancelCause(p.ctx)
|
||||||
|
|
||||||
|
if !p.useSandbox {
|
||||||
|
p.helper = helper.NewDirect(ctx, p.name, p.final, true, argF, func(cmd *exec.Cmd) {
|
||||||
if p.CmdF != nil {
|
if p.CmdF != nil {
|
||||||
p.CmdF(cmd)
|
p.CmdF(cmd)
|
||||||
}
|
}
|
||||||
if output != nil {
|
if p.output != nil {
|
||||||
cmd.Stdout, cmd.Stderr = output, output
|
cmd.Stdout, cmd.Stderr = p.output, p.output
|
||||||
}
|
}
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||||
cmd.Env = make([]string, 0)
|
cmd.Env = make([]string, 0)
|
||||||
@ -59,15 +62,15 @@ func (p *Proxy) Start(ctx context.Context, output io.Writer, useSandbox bool) er
|
|||||||
libPaths = ldd.Path(entries)
|
libPaths = ldd.Path(entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
h = helper.New(
|
p.helper = helper.New(
|
||||||
c, toolPath,
|
ctx, toolPath,
|
||||||
p.seal, true,
|
p.final, true,
|
||||||
argF, func(container *sandbox.Container) {
|
argF, func(container *sandbox.Container) {
|
||||||
container.Seccomp |= seccomp.FilterMultiarch
|
container.Seccomp |= seccomp.FilterMultiarch
|
||||||
container.Hostname = "fortify-dbus"
|
container.Hostname = "fortify-dbus"
|
||||||
container.CommandContext = p.CommandContext
|
container.CommandContext = p.CommandContext
|
||||||
if output != nil {
|
if p.output != nil {
|
||||||
container.Stdout, container.Stderr = output, output
|
container.Stdout, container.Stderr = p.output, p.output
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.CmdF != nil {
|
if p.CmdF != nil {
|
||||||
@ -81,10 +84,17 @@ func (p *Proxy) Start(ctx context.Context, output io.Writer, useSandbox bool) er
|
|||||||
|
|
||||||
// upstream bus directories
|
// upstream bus directories
|
||||||
upstreamPaths := make([]string, 0, 2)
|
upstreamPaths := make([]string, 0, 2)
|
||||||
for _, as := range []string{p.session[0], p.system[0]} {
|
for _, addr := range [][]AddrEntry{p.final.SessionUpstream, p.final.SystemUpstream} {
|
||||||
if len(as) > 0 && strings.HasPrefix(as, "unix:path=/") {
|
for _, ent := range addr {
|
||||||
// leave / intact
|
if ent.Method != "unix" {
|
||||||
upstreamPaths = append(upstreamPaths, path.Dir(as[10:]))
|
continue
|
||||||
|
}
|
||||||
|
for _, pair := range ent.Values {
|
||||||
|
if pair[0] != "path" || !path.IsAbs(pair[1]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
upstreamPaths = append(upstreamPaths, path.Dir(pair[1]))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slices.Sort(upstreamPaths)
|
slices.Sort(upstreamPaths)
|
||||||
@ -95,10 +105,10 @@ func (p *Proxy) Start(ctx context.Context, output io.Writer, useSandbox bool) er
|
|||||||
|
|
||||||
// parent directories of bind paths
|
// parent directories of bind paths
|
||||||
sockDirPaths := make([]string, 0, 2)
|
sockDirPaths := make([]string, 0, 2)
|
||||||
if d := path.Dir(p.session[1]); path.IsAbs(d) {
|
if d := path.Dir(p.final.Session[1]); path.IsAbs(d) {
|
||||||
sockDirPaths = append(sockDirPaths, d)
|
sockDirPaths = append(sockDirPaths, d)
|
||||||
}
|
}
|
||||||
if d := path.Dir(p.system[1]); path.IsAbs(d) {
|
if d := path.Dir(p.final.System[1]); path.IsAbs(d) {
|
||||||
sockDirPaths = append(sockDirPaths, d)
|
sockDirPaths = append(sockDirPaths, d)
|
||||||
}
|
}
|
||||||
slices.Sort(sockDirPaths)
|
slices.Sort(sockDirPaths)
|
||||||
@ -113,14 +123,13 @@ func (p *Proxy) Start(ctx context.Context, output io.Writer, useSandbox bool) er
|
|||||||
}, nil)
|
}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.Start(); err != nil {
|
if err := p.helper.Start(); err != nil {
|
||||||
cancel(err)
|
cancel(err)
|
||||||
|
p.helper = nil
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
p.helper = h
|
p.cancel, p.cause = cancel, func() error { return context.Cause(ctx) }
|
||||||
p.ctx = c
|
|
||||||
p.cancel = cancel
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,28 +137,30 @@ var proxyClosed = errors.New("proxy closed")
|
|||||||
|
|
||||||
// Wait blocks until xdg-dbus-proxy exits and releases resources.
|
// Wait blocks until xdg-dbus-proxy exits and releases resources.
|
||||||
func (p *Proxy) Wait() error {
|
func (p *Proxy) Wait() error {
|
||||||
p.lock.RLock()
|
p.mu.RLock()
|
||||||
defer p.lock.RUnlock()
|
defer p.mu.RUnlock()
|
||||||
|
|
||||||
if p.helper == nil {
|
p.pmu.RLock()
|
||||||
|
if p.helper == nil || p.cancel == nil || p.cause == nil {
|
||||||
|
p.pmu.RUnlock()
|
||||||
return errors.New("dbus: not started")
|
return errors.New("dbus: not started")
|
||||||
}
|
}
|
||||||
|
|
||||||
errs := make([]error, 3)
|
errs := make([]error, 3)
|
||||||
|
|
||||||
errs[0] = p.helper.Wait()
|
errs[0] = p.helper.Wait()
|
||||||
if p.cancel == nil &&
|
if errors.Is(errs[0], context.Canceled) &&
|
||||||
errors.Is(errs[0], context.Canceled) &&
|
errors.Is(p.cause(), proxyClosed) {
|
||||||
errors.Is(context.Cause(p.ctx), proxyClosed) {
|
|
||||||
errs[0] = nil
|
errs[0] = nil
|
||||||
}
|
}
|
||||||
|
p.pmu.RUnlock()
|
||||||
|
|
||||||
// ensure socket removal so ephemeral directory is empty at revert
|
// ensure socket removal so ephemeral directory is empty at revert
|
||||||
if err := os.Remove(p.session[1]); err != nil && !errors.Is(err, os.ErrNotExist) {
|
if err := os.Remove(p.final.Session[1]); err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||||
errs[1] = err
|
errs[1] = err
|
||||||
}
|
}
|
||||||
if p.sysP {
|
if p.final.System[1] != "" {
|
||||||
if err := os.Remove(p.system[1]); err != nil && !errors.Is(err, os.ErrNotExist) {
|
if err := os.Remove(p.final.System[1]); err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||||
errs[2] = err
|
errs[2] = err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,14 +170,13 @@ func (p *Proxy) Wait() error {
|
|||||||
|
|
||||||
// Close cancels the context passed to the helper instance attached to xdg-dbus-proxy.
|
// Close cancels the context passed to the helper instance attached to xdg-dbus-proxy.
|
||||||
func (p *Proxy) Close() {
|
func (p *Proxy) Close() {
|
||||||
p.lock.Lock()
|
p.pmu.Lock()
|
||||||
defer p.lock.Unlock()
|
defer p.pmu.Unlock()
|
||||||
|
|
||||||
if p.cancel == nil {
|
if p.cancel == nil {
|
||||||
panic("dbus: not started")
|
panic("dbus: not started")
|
||||||
}
|
}
|
||||||
p.cancel(proxyClosed)
|
p.cancel(proxyClosed)
|
||||||
p.cancel = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func argF(argsFd, statFd int) []string {
|
func argF(argsFd, statFd int) []string {
|
||||||
|
@ -2,11 +2,10 @@ package dbus
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"git.gensokyo.uk/security/fortify/helper"
|
"git.gensokyo.uk/security/fortify/helper"
|
||||||
)
|
)
|
||||||
@ -15,84 +14,88 @@ import (
|
|||||||
// Overriding ProxyName will only affect Proxy instance created after the change.
|
// Overriding ProxyName will only affect Proxy instance created after the change.
|
||||||
var ProxyName = "xdg-dbus-proxy"
|
var ProxyName = "xdg-dbus-proxy"
|
||||||
|
|
||||||
// Proxy holds references to a xdg-dbus-proxy process, and should never be copied.
|
// Proxy holds the state of a xdg-dbus-proxy process, and should never be copied.
|
||||||
// Once sealed, configuration changes will no longer be possible and attempting to do so will result in a panic.
|
|
||||||
type Proxy struct {
|
type Proxy struct {
|
||||||
helper helper.Helper
|
helper helper.Helper
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
|
||||||
cancel context.CancelCauseFunc
|
cancel context.CancelCauseFunc
|
||||||
|
cause func() error
|
||||||
|
|
||||||
|
final *Final
|
||||||
|
output io.Writer
|
||||||
|
useSandbox bool
|
||||||
|
|
||||||
name string
|
name string
|
||||||
session [2]string
|
|
||||||
system [2]string
|
|
||||||
CmdF func(any)
|
CmdF func(any)
|
||||||
sysP bool
|
|
||||||
|
|
||||||
CommandContext func(ctx context.Context) (cmd *exec.Cmd)
|
CommandContext func(ctx context.Context) (cmd *exec.Cmd)
|
||||||
FilterF func([]byte) []byte
|
FilterF func([]byte) []byte
|
||||||
|
|
||||||
seal io.WriterTo
|
mu, pmu sync.RWMutex
|
||||||
lock sync.RWMutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Proxy) Session() [2]string { return p.session }
|
|
||||||
func (p *Proxy) System() [2]string { return p.system }
|
|
||||||
func (p *Proxy) Sealed() bool { p.lock.RLock(); defer p.lock.RUnlock(); return p.seal != nil }
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrConfig = errors.New("no configuration to seal")
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p *Proxy) String() string {
|
func (p *Proxy) String() string {
|
||||||
if p == nil {
|
if p == nil {
|
||||||
return "(invalid dbus proxy)"
|
return "(invalid dbus proxy)"
|
||||||
}
|
}
|
||||||
|
|
||||||
p.lock.RLock()
|
p.mu.RLock()
|
||||||
defer p.lock.RUnlock()
|
defer p.mu.RUnlock()
|
||||||
|
|
||||||
if p.helper != nil {
|
if p.helper != nil {
|
||||||
return p.helper.String()
|
return p.helper.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.seal != nil {
|
return "(unused dbus proxy)"
|
||||||
return p.seal.(fmt.Stringer).String()
|
|
||||||
}
|
|
||||||
|
|
||||||
return "(unsealed dbus proxy)"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seal seals the Proxy instance.
|
// Final describes the outcome of a proxy configuration.
|
||||||
func (p *Proxy) Seal(session, system *Config) error {
|
type Final struct {
|
||||||
p.lock.Lock()
|
Session, System ProxyPair
|
||||||
defer p.lock.Unlock()
|
// parsed upstream address
|
||||||
|
SessionUpstream, SystemUpstream []AddrEntry
|
||||||
if p.seal != nil {
|
io.WriterTo
|
||||||
panic("dbus proxy sealed twice")
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// Finalise creates a checked argument writer for [Proxy].
|
||||||
|
func Finalise(sessionBus, systemBus ProxyPair, session, system *Config) (final *Final, err error) {
|
||||||
if session == nil && system == nil {
|
if session == nil && system == nil {
|
||||||
return ErrConfig
|
return nil, syscall.EBADE
|
||||||
}
|
}
|
||||||
|
|
||||||
var args []string
|
var args []string
|
||||||
if session != nil {
|
if session != nil {
|
||||||
args = append(args, session.Args(p.session)...)
|
args = append(args, session.Args(sessionBus)...)
|
||||||
}
|
}
|
||||||
if system != nil {
|
if system != nil {
|
||||||
args = append(args, system.Args(p.system)...)
|
args = append(args, system.Args(systemBus)...)
|
||||||
p.sysP = true
|
|
||||||
}
|
|
||||||
if seal, err := helper.NewCheckedArgs(args); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
p.seal = seal
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
final = &Final{Session: sessionBus, System: systemBus}
|
||||||
|
|
||||||
|
final.WriterTo, err = helper.NewCheckedArgs(args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if session != nil {
|
||||||
|
final.SessionUpstream, err = Parse([]byte(final.Session[0]))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if system != nil {
|
||||||
|
final.SystemUpstream, err = Parse([]byte(final.System[0]))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a reference to a new unsealed Proxy.
|
// New returns a new instance of [Proxy].
|
||||||
func New(session, system [2]string) *Proxy {
|
func New(ctx context.Context, final *Final, output io.Writer) *Proxy {
|
||||||
return &Proxy{name: ProxyName, session: session, system: system}
|
return &Proxy{name: ProxyName, ctx: ctx, final: final, output: output, useSandbox: true}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"git.gensokyo.uk/security/fortify/dbus"
|
"git.gensokyo.uk/security/fortify/dbus"
|
||||||
)
|
)
|
||||||
@ -26,64 +27,62 @@ func (sys *I) MustProxyDBus(sessionPath string, session *dbus.Config, systemPath
|
|||||||
func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath string) (func(), error) {
|
func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath string) (func(), error) {
|
||||||
d := new(DBus)
|
d := new(DBus)
|
||||||
|
|
||||||
// session bus is mandatory
|
// session bus is required as otherwise this is effectively a very expensive noop
|
||||||
if session == nil {
|
if session == nil {
|
||||||
return nil, msg.WrapErr(ErrDBusConfig,
|
return nil, msg.WrapErr(ErrDBusConfig,
|
||||||
"attempted to seal message bus proxy without session bus config")
|
"attempted to create message bus proxy args without session bus config")
|
||||||
}
|
}
|
||||||
|
|
||||||
// system bus is optional
|
// system bus is optional
|
||||||
d.system = system != nil
|
d.system = system != nil
|
||||||
|
|
||||||
// upstream address, downstream socket path
|
d.sessionBus[0], d.systemBus[0] = dbus.Address()
|
||||||
var sessionBus, systemBus [2]string
|
d.sessionBus[1], d.systemBus[1] = sessionPath, systemPath
|
||||||
|
|
||||||
// resolve upstream bus addresses
|
|
||||||
sessionBus[0], systemBus[0] = dbus.Address()
|
|
||||||
|
|
||||||
// set paths from caller
|
|
||||||
sessionBus[1], systemBus[1] = sessionPath, systemPath
|
|
||||||
|
|
||||||
// create proxy instance
|
|
||||||
d.proxy = dbus.New(sessionBus, systemBus)
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if msg.IsVerbose() && d.proxy.Sealed() {
|
|
||||||
msg.Verbose("sealed session proxy", session.Args(sessionBus))
|
|
||||||
if system != nil {
|
|
||||||
msg.Verbose("sealed system proxy", system.Args(systemBus))
|
|
||||||
}
|
|
||||||
msg.Verbose("message bus proxy final args:", d.proxy)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// queue operation
|
|
||||||
sys.ops = append(sys.ops, d)
|
|
||||||
|
|
||||||
// seal dbus proxy
|
|
||||||
d.out = &scanToFmsg{msg: new(strings.Builder)}
|
d.out = &scanToFmsg{msg: new(strings.Builder)}
|
||||||
return d.out.Dump, wrapErrSuffix(d.proxy.Seal(session, system),
|
if final, err := dbus.Finalise(d.sessionBus, d.systemBus, session, system); err != nil {
|
||||||
"cannot seal message bus proxy:")
|
if errors.Is(err, syscall.EINVAL) {
|
||||||
|
return nil, msg.WrapErr(err, "message bus proxy configuration contains NUL byte")
|
||||||
|
}
|
||||||
|
return nil, wrapErrSuffix(err, "cannot finalise message bus proxy:")
|
||||||
|
} else {
|
||||||
|
if msg.IsVerbose() {
|
||||||
|
msg.Verbose("session bus proxy:", session.Args(d.sessionBus))
|
||||||
|
if system != nil {
|
||||||
|
msg.Verbose("system bus proxy:", system.Args(d.systemBus))
|
||||||
|
}
|
||||||
|
|
||||||
|
// this calls the argsWt String method
|
||||||
|
msg.Verbose("message bus proxy final args:", final.WriterTo)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.final = final
|
||||||
|
}
|
||||||
|
|
||||||
|
sys.ops = append(sys.ops, d)
|
||||||
|
return d.out.Dump, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type DBus struct {
|
type DBus struct {
|
||||||
proxy *dbus.Proxy
|
proxy *dbus.Proxy // populated during apply
|
||||||
|
|
||||||
|
final *dbus.Final
|
||||||
out *scanToFmsg
|
out *scanToFmsg
|
||||||
// whether system bus proxy is enabled
|
// whether system bus proxy is enabled
|
||||||
system bool
|
system bool
|
||||||
|
|
||||||
|
sessionBus, systemBus dbus.ProxyPair
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DBus) Type() Enablement { return Process }
|
func (d *DBus) Type() Enablement { return Process }
|
||||||
|
|
||||||
func (d *DBus) apply(sys *I) error {
|
func (d *DBus) apply(sys *I) error {
|
||||||
msg.Verbosef("session bus proxy on %q for upstream %q", d.proxy.Session()[1], d.proxy.Session()[0])
|
msg.Verbosef("session bus proxy on %q for upstream %q", d.sessionBus[1], d.sessionBus[0])
|
||||||
if d.system {
|
if d.system {
|
||||||
msg.Verbosef("system bus proxy on %q for upstream %q", d.proxy.System()[1], d.proxy.System()[0])
|
msg.Verbosef("system bus proxy on %q for upstream %q", d.systemBus[1], d.systemBus[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
// this starts the process and blocks until ready
|
d.proxy = dbus.New(sys.ctx, d.final, d.out)
|
||||||
if err := d.proxy.Start(sys.ctx, d.out, true); err != nil {
|
if err := d.proxy.Start(); err != nil {
|
||||||
d.out.Dump()
|
d.out.Dump()
|
||||||
return wrapErrSuffix(err,
|
return wrapErrSuffix(err,
|
||||||
"cannot start message bus proxy:")
|
"cannot start message bus proxy:")
|
||||||
|
Loading…
Reference in New Issue
Block a user