From 1931b5460034b63dcb1c89bd0c8825a751829f45 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Sun, 7 Dec 2025 22:33:45 +0900 Subject: [PATCH] hst: add pipewire flag These are for #26. None of them are implemented yet. This fixes up test cases for the change to happen. Existing source code and JSON configuration continue to have the same effect. Existing flags get its EPulse bit replaced by EPipeWire. Signed-off-by: Ophestra --- cmd/hakurei/print_test.go | 11 +++++++---- hst/config.go | 11 +++++++++++ hst/enablement.go | 25 +++++++++++++++++-------- hst/enablement_test.go | 4 +++- hst/hst.go | 2 +- hst/hst_test.go | 1 + internal/outcome/outcome.go | 5 ++++- internal/store/data_test.go | 4 ++-- internal/store/header_test.go | 2 +- 9 files changed, 47 insertions(+), 18 deletions(-) diff --git a/cmd/hakurei/print_test.go b/cmd/hakurei/print_test.go index 1d6f511..d0c12d1 100644 --- a/cmd/hakurei/print_test.go +++ b/cmd/hakurei/print_test.go @@ -32,7 +32,7 @@ var ( PID: 0xbeef, ShimPID: 0xcafe, Config: &hst.Config{ - Enablements: hst.NewEnablements(hst.EWayland | hst.EPulse), + Enablements: hst.NewEnablements(hst.EWayland | hst.EPipeWire), Identity: 1, Container: &hst.ContainerConfig{ Shell: check.MustAbs("/bin/sh"), @@ -62,7 +62,7 @@ func TestPrintShowInstance(t *testing.T) { {"nil", nil, nil, false, false, "Error: invalid configuration!\n\n", false}, {"config", nil, hst.Template(), false, false, `App Identity: 9 (org.chromium.Chromium) - Enablements: wayland, dbus, pulseaudio + Enablements: wayland, dbus, pipewire, pulseaudio Groups: video, dialout, plugdev Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir Home: /data/data/org.chromium.Chromium @@ -159,7 +159,7 @@ Session bus App Identity: 9 (org.chromium.Chromium) - Enablements: wayland, dbus, pulseaudio + Enablements: wayland, dbus, pipewire, pulseaudio Groups: video, dialout, plugdev Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir Home: /data/data/org.chromium.Chromium @@ -215,6 +215,7 @@ App "enablements": { "wayland": true, "dbus": true, + "pipewire": true, "pulse": true }, "session_bus": { @@ -366,6 +367,7 @@ App "enablements": { "wayland": true, "dbus": true, + "pipewire": true, "pulse": true }, "session_bus": { @@ -564,6 +566,7 @@ func TestPrintPs(t *testing.T) { "enablements": { "wayland": true, "dbus": true, + "pipewire": true, "pulse": true }, "session_bus": { @@ -715,7 +718,7 @@ func TestPrintPs(t *testing.T) { "shim_pid": 51966, "enablements": { "wayland": true, - "pulse": true + "pipewire": true }, "identity": 1, "groups": null, diff --git a/hst/config.go b/hst/config.go index d707855..c6cd790 100644 --- a/hst/config.go +++ b/hst/config.go @@ -23,9 +23,20 @@ type Config struct { // System D-Bus proxy configuration. // If set to nil, system bus proxy is disabled. SystemBus *BusConfig `json:"system_bus,omitempty"` + // Direct access to wayland socket, no attempt is made to attach security-context-v1 // and the bare socket is made available to the container. + // + // This option is unsupported and most likely enables full control over the Wayland + // session. Do not set this to true unless you are sure you know what you are doing. DirectWayland bool `json:"direct_wayland,omitempty"` + // Direct access to PulseAudio socket, no attempt is made to establish pipewire-pulse + // server via a PipeWire socket with a SecurityContext attached and the bare socket + // is made available to the container. + // + // This option is unsupported and enables arbitrary code execution as the PulseAudio + // server. Do not set this to true, this is insecure under any configuration. + DirectPulse bool `json:"direct_pulse,omitempty"` // Extra acl updates to perform before setuid. ExtraPerms []ExtraPermConfig `json:"extra_perms,omitempty"` diff --git a/hst/enablement.go b/hst/enablement.go index 7a1056d..9024078 100644 --- a/hst/enablement.go +++ b/hst/enablement.go @@ -17,6 +17,8 @@ const ( EX11 // EDBus enables the per-container xdg-dbus-proxy daemon. EDBus + // EPipeWire exposes a pipewire pathname socket via SecurityContext. + EPipeWire // EPulse copies the PulseAudio cookie to [hst.PrivateTmp] and exposes the PulseAudio socket. EPulse @@ -35,6 +37,8 @@ func (e Enablement) String() string { return "x11" case EDBus: return "dbus" + case EPipeWire: + return "pipewire" case EPulse: return "pulseaudio" default: @@ -62,10 +66,11 @@ type Enablements Enablement // enablementsJSON is the [json] representation of [Enablements]. type enablementsJSON = struct { - Wayland bool `json:"wayland,omitempty"` - X11 bool `json:"x11,omitempty"` - DBus bool `json:"dbus,omitempty"` - Pulse bool `json:"pulse,omitempty"` + Wayland bool `json:"wayland,omitempty"` + X11 bool `json:"x11,omitempty"` + DBus bool `json:"dbus,omitempty"` + PipeWire bool `json:"pipewire,omitempty"` + Pulse bool `json:"pulse,omitempty"` } // Unwrap returns the underlying [Enablement]. @@ -81,10 +86,11 @@ func (e *Enablements) MarshalJSON() ([]byte, error) { return nil, syscall.EINVAL } return json.Marshal(&enablementsJSON{ - Wayland: Enablement(*e)&EWayland != 0, - X11: Enablement(*e)&EX11 != 0, - DBus: Enablement(*e)&EDBus != 0, - Pulse: Enablement(*e)&EPulse != 0, + Wayland: Enablement(*e)&EWayland != 0, + X11: Enablement(*e)&EX11 != 0, + DBus: Enablement(*e)&EDBus != 0, + PipeWire: Enablement(*e)&EPipeWire != 0, + Pulse: Enablement(*e)&EPulse != 0, }) } @@ -108,6 +114,9 @@ func (e *Enablements) UnmarshalJSON(data []byte) error { if v.DBus { ve |= EDBus } + if v.PipeWire { + ve |= EPipeWire + } if v.Pulse { ve |= EPulse } diff --git a/hst/enablement_test.go b/hst/enablement_test.go index 75d8d94..5ba9b71 100644 --- a/hst/enablement_test.go +++ b/hst/enablement_test.go @@ -32,6 +32,7 @@ func TestEnablementString(t *testing.T) { {hst.EWayland | hst.EDBus | hst.EPulse, "wayland, dbus, pulseaudio"}, {hst.EX11 | hst.EDBus | hst.EPulse, "x11, dbus, pulseaudio"}, {hst.EWayland | hst.EX11 | hst.EDBus | hst.EPulse, "wayland, x11, dbus, pulseaudio"}, + {hst.EM - 1, "wayland, x11, dbus, pipewire, pulseaudio"}, {1 << 5, "e20"}, {1 << 6, "e40"}, @@ -62,8 +63,9 @@ func TestEnablements(t *testing.T) { {"wayland", hst.NewEnablements(hst.EWayland), `{"wayland":true}`, `{"value":{"wayland":true},"magic":3236757504}`}, {"x11", hst.NewEnablements(hst.EX11), `{"x11":true}`, `{"value":{"x11":true},"magic":3236757504}`}, {"dbus", hst.NewEnablements(hst.EDBus), `{"dbus":true}`, `{"value":{"dbus":true},"magic":3236757504}`}, + {"pipewire", hst.NewEnablements(hst.EPipeWire), `{"pipewire":true}`, `{"value":{"pipewire":true},"magic":3236757504}`}, {"pulse", hst.NewEnablements(hst.EPulse), `{"pulse":true}`, `{"value":{"pulse":true},"magic":3236757504}`}, - {"all", hst.NewEnablements(hst.EWayland | hst.EX11 | hst.EDBus | hst.EPulse), `{"wayland":true,"x11":true,"dbus":true,"pulse":true}`, `{"value":{"wayland":true,"x11":true,"dbus":true,"pulse":true},"magic":3236757504}`}, + {"all", hst.NewEnablements(hst.EM - 1), `{"wayland":true,"x11":true,"dbus":true,"pipewire":true,"pulse":true}`, `{"value":{"wayland":true,"x11":true,"dbus":true,"pipewire":true,"pulse":true},"magic":3236757504}`}, } for _, tc := range testCases { diff --git a/hst/hst.go b/hst/hst.go index f48b9f8..a7f9092 100644 --- a/hst/hst.go +++ b/hst/hst.go @@ -70,7 +70,7 @@ func Template() *Config { return &Config{ ID: "org.chromium.Chromium", - Enablements: NewEnablements(EWayland | EDBus | EPulse), + Enablements: NewEnablements(EWayland | EDBus | EPipeWire | EPulse), SessionBus: &BusConfig{ See: nil, diff --git a/hst/hst_test.go b/hst/hst_test.go index b487dd5..01d229f 100644 --- a/hst/hst_test.go +++ b/hst/hst_test.go @@ -105,6 +105,7 @@ func TestTemplate(t *testing.T) { "enablements": { "wayland": true, "dbus": true, + "pipewire": true, "pulse": true }, "session_bus": { diff --git a/internal/outcome/outcome.go b/internal/outcome/outcome.go index e256355..3252db9 100644 --- a/internal/outcome/outcome.go +++ b/internal/outcome/outcome.go @@ -172,6 +172,8 @@ type outcomeStateSys struct { // Copied from [hst.Config]. Safe for read by spWaylandOp.toSystem only. directWayland bool + // Copied from [hst.Config]. Safe for read by spPulseOp.toSystem only. + directPulse bool // Copied header from [hst.Config]. Safe for read by spFilesystemOp.toSystem only. extraPerms []hst.ExtraPermConfig // Copied address from [hst.Config]. Safe for read by spDBusOp.toSystem only. @@ -185,7 +187,8 @@ type outcomeStateSys struct { func (s *outcomeState) newSys(config *hst.Config, sys *system.I) *outcomeStateSys { return &outcomeStateSys{ appId: config.ID, et: config.Enablements.Unwrap(), - directWayland: config.DirectWayland, extraPerms: config.ExtraPerms, + directWayland: config.DirectWayland, directPulse: config.DirectPulse, + extraPerms: config.ExtraPerms, sessionBus: config.SessionBus, systemBus: config.SystemBus, sys: sys, outcomeState: s, } diff --git a/internal/store/data_test.go b/internal/store/data_test.go index fe0e212..a2eb92c 100644 --- a/internal/store/data_test.go +++ b/internal/store/data_test.go @@ -47,9 +47,9 @@ func TestEntryData(t *testing.T) { {"inconsistent enablement", "\x00\xff\xca\xfe\x00\x00\xff\x00" + templateStateGob, NewTemplateState(), &hst.AppError{ Step: "validate state enablement", Err: os.ErrInvalid, - Msg: "state entry aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa has unexpected enablement byte 0xd, 0xff"}}, + Msg: "state entry aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa has unexpected enablement byte 0x1d, 0xff"}}, - {"template", "\x00\xff\xca\xfe\x00\x00\x0d\xf2" + templateStateGob, NewTemplateState(), nil}, + {"template", "\x00\xff\xca\xfe\x00\x00\x1d\xe2" + templateStateGob, NewTemplateState(), nil}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { diff --git a/internal/store/header_test.go b/internal/store/header_test.go index 51a6939..9da547f 100644 --- a/internal/store/header_test.go +++ b/internal/store/header_test.go @@ -34,7 +34,7 @@ func TestEntryHeader(t *testing.T) { {"success high", [entryHeaderSize]byte{0x00, 0xff, 0xca, 0xfe, 0x00, 0x00, 0xff, 0x00}, 0xff, nil}, {"success", [entryHeaderSize]byte{0x00, 0xff, 0xca, 0xfe, 0x00, 0x00, - 0x09, 0xf6}, hst.EWayland | hst.EPulse, nil}, + 0x09, 0xf6}, hst.EWayland | hst.EPipeWire, nil}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) {