system: optimise string formatting
Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
ee51320abf
commit
ec5e91b8c9
@ -41,7 +41,7 @@ type appInfo struct {
|
|||||||
// passed through to [fst.Config]
|
// passed through to [fst.Config]
|
||||||
SessionBus *dbus.Config `json:"session_bus,omitempty"`
|
SessionBus *dbus.Config `json:"session_bus,omitempty"`
|
||||||
// passed through to [fst.Config]
|
// passed through to [fst.Config]
|
||||||
Enablements system.Enablements `json:"enablements"`
|
Enablements system.Enablement `json:"enablements"`
|
||||||
|
|
||||||
// passed through to [fst.Config]
|
// passed through to [fst.Config]
|
||||||
Multiarch bool `json:"multiarch,omitempty"`
|
Multiarch bool `json:"multiarch,omitempty"`
|
||||||
|
@ -48,7 +48,7 @@ type ConfinementConfig struct {
|
|||||||
SessionBus *dbus.Config `json:"session_bus,omitempty"`
|
SessionBus *dbus.Config `json:"session_bus,omitempty"`
|
||||||
|
|
||||||
// system resources to expose to the container
|
// system resources to expose to the container
|
||||||
Enablements system.Enablements `json:"enablements"`
|
Enablements system.Enablement `json:"enablements"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExtraPermConfig struct {
|
type ExtraPermConfig struct {
|
||||||
@ -153,7 +153,7 @@ func Template() *Config {
|
|||||||
Log: false,
|
Log: false,
|
||||||
Filter: true,
|
Filter: true,
|
||||||
},
|
},
|
||||||
Enablements: system.EWayland.Mask() | system.EDBus.Mask() | system.EPulse.Mask(),
|
Enablements: system.EWayland | system.EDBus | system.EPulse,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ var testCasesNixos = []sealTestCase{
|
|||||||
Call: map[string]string{}, Broadcast: map[string]string{},
|
Call: map[string]string{}, Broadcast: map[string]string{},
|
||||||
Filter: true,
|
Filter: true,
|
||||||
},
|
},
|
||||||
Enablements: system.EWayland.Mask() | system.EDBus.Mask() | system.EPulse.Mask(),
|
Enablements: system.EWayland | system.EDBus | system.EPulse,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
fst.ID{
|
fst.ID{
|
||||||
|
@ -196,7 +196,7 @@ var testCasesPd = []sealTestCase{
|
|||||||
},
|
},
|
||||||
Filter: true,
|
Filter: true,
|
||||||
},
|
},
|
||||||
Enablements: system.EWayland.Mask() | system.EDBus.Mask() | system.EPulse.Mask(),
|
Enablements: system.EWayland | system.EDBus | system.EPulse,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
fst.ID{
|
fst.ID{
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.gensokyo.uk/security/fortify/fst"
|
"git.gensokyo.uk/security/fortify/fst"
|
||||||
@ -54,17 +53,16 @@ func (seal *outcome) Run(rs *fst.RunState) error {
|
|||||||
revert app setup transaction
|
revert app setup transaction
|
||||||
*/
|
*/
|
||||||
|
|
||||||
rt, ec := new(system.Enablements), new(system.Criteria)
|
var rt system.Enablement
|
||||||
ec.Enablements = new(system.Enablements)
|
ec := system.Process
|
||||||
ec.Set(system.Process)
|
|
||||||
if states, err := c.Load(); err != nil {
|
if states, err := c.Load(); err != nil {
|
||||||
// revert per-process state here to limit damage
|
// revert per-process state here to limit damage
|
||||||
storeErr.OpErr = err
|
storeErr.OpErr = err
|
||||||
return seal.sys.Revert(ec)
|
return seal.sys.Revert((*system.Criteria)(&ec))
|
||||||
} else {
|
} else {
|
||||||
if l := len(states); l == 0 {
|
if l := len(states); l == 0 {
|
||||||
fmsg.Verbose("no other launchers active, will clean up globals")
|
fmsg.Verbose("no other launchers active, will clean up globals")
|
||||||
ec.Set(system.User)
|
ec |= system.User
|
||||||
} else {
|
} else {
|
||||||
fmsg.Verbosef("found %d active launchers, cleaning up without globals", l)
|
fmsg.Verbosef("found %d active launchers, cleaning up without globals", l)
|
||||||
}
|
}
|
||||||
@ -72,31 +70,20 @@ func (seal *outcome) Run(rs *fst.RunState) error {
|
|||||||
// accumulate enablements of remaining launchers
|
// accumulate enablements of remaining launchers
|
||||||
for i, s := range states {
|
for i, s := range states {
|
||||||
if s.Config != nil {
|
if s.Config != nil {
|
||||||
*rt |= s.Config.Confinement.Enablements
|
rt |= s.Config.Confinement.Enablements
|
||||||
} else {
|
} else {
|
||||||
log.Printf("state entry %d does not contain config", i)
|
log.Printf("state entry %d does not contain config", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// invert accumulated enablements for cleanup
|
ec |= rt ^ (system.EWayland | system.EX11 | system.EDBus | system.EPulse)
|
||||||
for i := system.Enablement(0); i < system.Enablement(system.ELen); i++ {
|
|
||||||
if !rt.Has(i) {
|
|
||||||
ec.Set(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if fmsg.Load() {
|
if fmsg.Load() {
|
||||||
labels := make([]string, 0, system.ELen+1)
|
if ec > 0 {
|
||||||
for i := system.Enablement(0); i < system.Enablement(system.ELen+2); i++ {
|
fmsg.Verbose("reverting operations type", system.TypeString(ec))
|
||||||
if ec.Has(i) {
|
|
||||||
labels = append(labels, system.TypeString(i))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(labels) > 0 {
|
|
||||||
fmsg.Verbose("reverting operations type", strings.Join(labels, ", "))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return seal.sys.Revert(ec)
|
return seal.sys.Revert((*system.Criteria)(&ec))
|
||||||
}()
|
}()
|
||||||
})
|
})
|
||||||
storeErr.save([]error{revertErr, store.Close()})
|
storeErr.save([]error{revertErr, store.Close()})
|
||||||
|
@ -224,7 +224,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
|
|||||||
conf.Cover = append(conf.Cover, nscd)
|
conf.Cover = append(conf.Cover, nscd)
|
||||||
}
|
}
|
||||||
// bind GPU stuff
|
// bind GPU stuff
|
||||||
if config.Confinement.Enablements.Has(system.EX11) || config.Confinement.Enablements.Has(system.EWayland) {
|
if config.Confinement.Enablements&(system.EX11|system.EWayland) != 0 {
|
||||||
conf.Filesystem = append(conf.Filesystem, &fst.FilesystemConfig{Src: "/dev/dri", Device: true})
|
conf.Filesystem = append(conf.Filesystem, &fst.FilesystemConfig{Src: "/dev/dri", Device: true})
|
||||||
}
|
}
|
||||||
// opportunistically bind kvm
|
// opportunistically bind kvm
|
||||||
@ -338,7 +338,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
|
|||||||
seal.env[term] = t
|
seal.env[term] = t
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Confinement.Enablements.Has(system.EWayland) {
|
if config.Confinement.Enablements&system.EWayland != 0 {
|
||||||
// outer wayland socket (usually `/run/user/%d/wayland-%d`)
|
// outer wayland socket (usually `/run/user/%d/wayland-%d`)
|
||||||
var socketPath string
|
var socketPath string
|
||||||
if name, ok := sys.LookupEnv(wl.WaylandDisplay); !ok {
|
if name, ok := sys.LookupEnv(wl.WaylandDisplay); !ok {
|
||||||
@ -371,7 +371,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Confinement.Enablements.Has(system.EX11) {
|
if config.Confinement.Enablements&system.EX11 != 0 {
|
||||||
if d, ok := sys.LookupEnv(display); !ok {
|
if d, ok := sys.LookupEnv(display); !ok {
|
||||||
return fmsg.WrapError(ErrXDisplay,
|
return fmsg.WrapError(ErrXDisplay,
|
||||||
"DISPLAY is not set")
|
"DISPLAY is not set")
|
||||||
@ -386,7 +386,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
|
|||||||
PulseAudio server and authentication
|
PulseAudio server and authentication
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if config.Confinement.Enablements.Has(system.EPulse) {
|
if config.Confinement.Enablements&system.EPulse != 0 {
|
||||||
// PulseAudio runtime directory (usually `/run/user/%d/pulse`)
|
// PulseAudio runtime directory (usually `/run/user/%d/pulse`)
|
||||||
pulseRuntimeDir := path.Join(sc.RuntimePath, "pulse")
|
pulseRuntimeDir := path.Join(sc.RuntimePath, "pulse")
|
||||||
// PulseAudio socket (usually `/run/user/%d/pulse/native`)
|
// PulseAudio socket (usually `/run/user/%d/pulse/native`)
|
||||||
@ -439,7 +439,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
|
|||||||
D-Bus proxy
|
D-Bus proxy
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if config.Confinement.Enablements.Has(system.EDBus) {
|
if config.Confinement.Enablements&system.EDBus != 0 {
|
||||||
// ensure dbus session bus defaults
|
// ensure dbus session bus defaults
|
||||||
if config.Confinement.SessionBus == nil {
|
if config.Confinement.SessionBus == nil {
|
||||||
config.Confinement.SessionBus = dbus.NewConfig(config.ID, true, true)
|
config.Confinement.SessionBus = dbus.NewConfig(config.ID, true, true)
|
||||||
|
39
main.go
39
main.go
@ -96,12 +96,13 @@ func buildCommand(out io.Writer) command.Command {
|
|||||||
mpris bool
|
mpris bool
|
||||||
dbusVerbose bool
|
dbusVerbose bool
|
||||||
|
|
||||||
fid string
|
fid string
|
||||||
aid int
|
aid int
|
||||||
groups command.RepeatableFlag
|
groups command.RepeatableFlag
|
||||||
homeDir string
|
homeDir string
|
||||||
userName string
|
userName string
|
||||||
enablements [system.ELen]bool
|
|
||||||
|
wayland, x11, dBus, pulse bool
|
||||||
)
|
)
|
||||||
|
|
||||||
c.NewCommand("run", "Configure and start a permissive default sandbox", func(args []string) error {
|
c.NewCommand("run", "Configure and start a permissive default sandbox", func(args []string) error {
|
||||||
@ -158,15 +159,21 @@ func buildCommand(out io.Writer) command.Command {
|
|||||||
config.Confinement.Outer = homeDir
|
config.Confinement.Outer = homeDir
|
||||||
config.Confinement.Username = userName
|
config.Confinement.Username = userName
|
||||||
|
|
||||||
// enablements from flags
|
if wayland {
|
||||||
for i := system.Enablement(0); i < system.Enablement(system.ELen); i++ {
|
config.Confinement.Enablements |= system.EWayland
|
||||||
if enablements[i] {
|
}
|
||||||
config.Confinement.Enablements.Set(i)
|
if x11 {
|
||||||
}
|
config.Confinement.Enablements |= system.EX11
|
||||||
|
}
|
||||||
|
if dBus {
|
||||||
|
config.Confinement.Enablements |= system.EDBus
|
||||||
|
}
|
||||||
|
if pulse {
|
||||||
|
config.Confinement.Enablements |= system.EPulse
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse D-Bus config file from flags if applicable
|
// parse D-Bus config file from flags if applicable
|
||||||
if enablements[system.EDBus] {
|
if dBus {
|
||||||
if dbusConfigSession == "builtin" {
|
if dbusConfigSession == "builtin" {
|
||||||
config.Confinement.SessionBus = dbus.NewConfig(fid, true, mpris)
|
config.Confinement.SessionBus = dbus.NewConfig(fid, true, mpris)
|
||||||
} else {
|
} else {
|
||||||
@ -215,13 +222,13 @@ func buildCommand(out io.Writer) command.Command {
|
|||||||
"Application home directory").
|
"Application home directory").
|
||||||
Flag(&userName, "u", command.StringFlag("chronos"),
|
Flag(&userName, "u", command.StringFlag("chronos"),
|
||||||
"Passwd name within sandbox").
|
"Passwd name within sandbox").
|
||||||
Flag(&enablements[system.EWayland], "wayland", command.BoolFlag(false),
|
Flag(&wayland, "wayland", command.BoolFlag(false),
|
||||||
"Allow Wayland connections").
|
"Allow Wayland connections").
|
||||||
Flag(&enablements[system.EX11], "X", command.BoolFlag(false),
|
Flag(&x11, "X", command.BoolFlag(false),
|
||||||
"Share X11 socket and allow connection").
|
"Share X11 socket and allow connection").
|
||||||
Flag(&enablements[system.EDBus], "dbus", command.BoolFlag(false),
|
Flag(&dBus, "dbus", command.BoolFlag(false),
|
||||||
"Proxy D-Bus connection").
|
"Proxy D-Bus connection").
|
||||||
Flag(&enablements[system.EPulse], "pulse", command.BoolFlag(false),
|
Flag(&pulse, "pulse", command.BoolFlag(false),
|
||||||
"Share PulseAudio socket and cookie")
|
"Share PulseAudio socket and cookie")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ func Test_printShowInstance(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{"config", nil, fst.Template(), false, false, `App
|
{"config", nil, fst.Template(), false, false, `App
|
||||||
ID: 9 (org.chromium.Chromium)
|
ID: 9 (org.chromium.Chromium)
|
||||||
Enablements: Wayland, D-Bus, PulseAudio
|
Enablements: wayland, dbus, pulseaudio
|
||||||
Groups: ["video"]
|
Groups: ["video"]
|
||||||
Directory: /var/lib/persist/home/org.chromium.Chromium
|
Directory: /var/lib/persist/home/org.chromium.Chromium
|
||||||
Hostname: "localhost"
|
Hostname: "localhost"
|
||||||
@ -74,14 +74,14 @@ System bus
|
|||||||
|
|
||||||
App
|
App
|
||||||
ID: 0
|
ID: 0
|
||||||
Enablements: (No enablements)
|
Enablements: (no enablements)
|
||||||
Directory:
|
Directory:
|
||||||
Command:
|
Command:
|
||||||
|
|
||||||
`},
|
`},
|
||||||
{"config flag none", nil, &fst.Config{Confinement: fst.ConfinementConfig{Sandbox: new(fst.SandboxConfig)}}, false, false, `App
|
{"config flag none", nil, &fst.Config{Confinement: fst.ConfinementConfig{Sandbox: new(fst.SandboxConfig)}}, false, false, `App
|
||||||
ID: 0
|
ID: 0
|
||||||
Enablements: (No enablements)
|
Enablements: (no enablements)
|
||||||
Directory:
|
Directory:
|
||||||
Flags: none
|
Flags: none
|
||||||
Etc: /etc
|
Etc: /etc
|
||||||
@ -90,7 +90,7 @@ App
|
|||||||
`},
|
`},
|
||||||
{"config nil entries", nil, &fst.Config{Confinement: fst.ConfinementConfig{Sandbox: &fst.SandboxConfig{Filesystem: make([]*fst.FilesystemConfig, 1)}, ExtraPerms: make([]*fst.ExtraPermConfig, 1)}}, false, false, `App
|
{"config nil entries", nil, &fst.Config{Confinement: fst.ConfinementConfig{Sandbox: &fst.SandboxConfig{Filesystem: make([]*fst.FilesystemConfig, 1)}, ExtraPerms: make([]*fst.ExtraPermConfig, 1)}}, false, false, `App
|
||||||
ID: 0
|
ID: 0
|
||||||
Enablements: (No enablements)
|
Enablements: (no enablements)
|
||||||
Directory:
|
Directory:
|
||||||
Flags: none
|
Flags: none
|
||||||
Etc: /etc
|
Etc: /etc
|
||||||
@ -105,7 +105,7 @@ Extra ACL
|
|||||||
|
|
||||||
App
|
App
|
||||||
ID: 0
|
ID: 0
|
||||||
Enablements: (No enablements)
|
Enablements: (no enablements)
|
||||||
Directory:
|
Directory:
|
||||||
Command:
|
Command:
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ Session bus
|
|||||||
|
|
||||||
App
|
App
|
||||||
ID: 9 (org.chromium.Chromium)
|
ID: 9 (org.chromium.Chromium)
|
||||||
Enablements: Wayland, D-Bus, PulseAudio
|
Enablements: wayland, dbus, pulseaudio
|
||||||
Groups: ["video"]
|
Groups: ["video"]
|
||||||
Directory: /var/lib/persist/home/org.chromium.Chromium
|
Directory: /var/lib/persist/home/org.chromium.Chromium
|
||||||
Hostname: "localhost"
|
Hostname: "localhost"
|
||||||
@ -162,7 +162,7 @@ State
|
|||||||
|
|
||||||
App
|
App
|
||||||
ID: 0
|
ID: 0
|
||||||
Enablements: (No enablements)
|
Enablements: (no enablements)
|
||||||
Directory:
|
Directory:
|
||||||
Command:
|
Command:
|
||||||
|
|
||||||
@ -469,8 +469,8 @@ func Test_printPs(t *testing.T) {
|
|||||||
|
|
||||||
`},
|
`},
|
||||||
|
|
||||||
{"valid", state.Entries{testID: testState}, false, false, ` Instance PID App Uptime Enablements Command
|
{"valid", state.Entries{testID: testState}, false, false, ` Instance PID App Uptime Enablements Command
|
||||||
8e2c76b0 3735928559 9 1h2m32s Wayland, D-Bus, PulseAudio ["chromium" "--ignore-gpu-blocklist" "--disable-smooth-scrolling" "--enable-features=UseOzonePlatform" "--ozone-platform=wayland"]
|
8e2c76b0 3735928559 9 1h2m32s wayland, dbus, pulseaudio ["chromium" "--ignore-gpu-blocklist" "--disable-smooth-scrolling" "--enable-features=UseOzonePlatform" "--ozone-platform=wayland"]
|
||||||
|
|
||||||
`},
|
`},
|
||||||
{"valid short", state.Entries{testID: testState}, true, false, `8e2c76b0
|
{"valid short", state.Entries{testID: testState}, true, false, `8e2c76b0
|
||||||
|
@ -46,20 +46,20 @@ func TestUpdatePermType(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestACL_String(t *testing.T) {
|
func TestACLString(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
want string
|
want string
|
||||||
et Enablement
|
et Enablement
|
||||||
perms []acl.Perm
|
perms []acl.Perm
|
||||||
}{
|
}{
|
||||||
{`--- type: Process path: "/nonexistent"`, Process, []acl.Perm{}},
|
{`--- type: process path: "/nonexistent"`, Process, []acl.Perm{}},
|
||||||
{`r-- type: User path: "/nonexistent"`, User, []acl.Perm{acl.Read}},
|
{`r-- type: user path: "/nonexistent"`, User, []acl.Perm{acl.Read}},
|
||||||
{`-w- type: Wayland path: "/nonexistent"`, EWayland, []acl.Perm{acl.Write}},
|
{`-w- type: wayland path: "/nonexistent"`, EWayland, []acl.Perm{acl.Write}},
|
||||||
{`--x type: X11 path: "/nonexistent"`, EX11, []acl.Perm{acl.Execute}},
|
{`--x type: x11 path: "/nonexistent"`, EX11, []acl.Perm{acl.Execute}},
|
||||||
{`rw- type: D-Bus path: "/nonexistent"`, EDBus, []acl.Perm{acl.Read, acl.Write}},
|
{`rw- type: dbus path: "/nonexistent"`, EDBus, []acl.Perm{acl.Read, acl.Write}},
|
||||||
{`r-x type: PulseAudio path: "/nonexistent"`, EPulse, []acl.Perm{acl.Read, acl.Execute}},
|
{`r-x type: pulseaudio path: "/nonexistent"`, EPulse, []acl.Perm{acl.Read, acl.Execute}},
|
||||||
{`rwx type: User path: "/nonexistent"`, User, []acl.Perm{acl.Read, acl.Write, acl.Execute}},
|
{`rwx type: user path: "/nonexistent"`, User, []acl.Perm{acl.Read, acl.Write, acl.Execute}},
|
||||||
{`rwx type: Process path: "/nonexistent"`, Process, []acl.Perm{acl.Read, acl.Write, acl.Write, acl.Execute}},
|
{`rwx type: process path: "/nonexistent"`, Process, []acl.Perm{acl.Read, acl.Write, acl.Write, acl.Execute}},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
@ -1,68 +1,47 @@
|
|||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// Enablement represents optional system resources.
|
||||||
// Enablement represents an optional system resource
|
type Enablement byte
|
||||||
Enablement uint8
|
|
||||||
// Enablements represents optional system resources to share
|
|
||||||
Enablements uint64
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
EWayland Enablement = iota
|
EWayland Enablement = 1 << iota
|
||||||
EX11
|
EX11
|
||||||
EDBus
|
EDBus
|
||||||
EPulse
|
EPulse
|
||||||
|
|
||||||
|
EM
|
||||||
)
|
)
|
||||||
|
|
||||||
var enablementString = [...]string{
|
|
||||||
EWayland: "Wayland",
|
|
||||||
EX11: "X11",
|
|
||||||
EDBus: "D-Bus",
|
|
||||||
EPulse: "PulseAudio",
|
|
||||||
}
|
|
||||||
|
|
||||||
const ELen = len(enablementString)
|
|
||||||
|
|
||||||
func (e Enablement) String() string {
|
func (e Enablement) String() string {
|
||||||
if int(e) >= ELen {
|
switch e {
|
||||||
return "<invalid enablement>"
|
case 0:
|
||||||
}
|
return "(no enablements)"
|
||||||
return enablementString[e]
|
case EWayland:
|
||||||
}
|
return "wayland"
|
||||||
|
case EX11:
|
||||||
|
return "x11"
|
||||||
|
case EDBus:
|
||||||
|
return "dbus"
|
||||||
|
case EPulse:
|
||||||
|
return "pulseaudio"
|
||||||
|
default:
|
||||||
|
buf := new(strings.Builder)
|
||||||
|
buf.Grow(32)
|
||||||
|
|
||||||
func (e Enablement) Mask() Enablements {
|
for i := Enablement(1); i < EM; i <<= 1 {
|
||||||
return 1 << e
|
if e&i != 0 {
|
||||||
}
|
buf.WriteString(", " + i.String())
|
||||||
|
}
|
||||||
// Has returns whether a feature is enabled
|
|
||||||
func (es *Enablements) Has(e Enablement) bool {
|
|
||||||
return *es&e.Mask() != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set enables a feature
|
|
||||||
func (es *Enablements) Set(e Enablement) {
|
|
||||||
if es.Has(e) {
|
|
||||||
panic("enablement " + e.String() + " set twice")
|
|
||||||
}
|
|
||||||
|
|
||||||
*es |= e.Mask()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (es *Enablements) String() string {
|
|
||||||
buf := new(strings.Builder)
|
|
||||||
for i := Enablement(0); i < Enablement(ELen); i++ {
|
|
||||||
if es.Has(i) {
|
|
||||||
buf.WriteString(", " + i.String())
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if buf.Len() == 0 {
|
if buf.Len() == 0 {
|
||||||
buf.WriteString("(No enablements)")
|
return fmt.Sprintf("e%x", byte(e))
|
||||||
|
}
|
||||||
|
return strings.TrimPrefix(buf.String(), ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.TrimPrefix(buf.String(), ", ")
|
|
||||||
}
|
}
|
||||||
|
43
system/enablement_test.go
Normal file
43
system/enablement_test.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package system_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.gensokyo.uk/security/fortify/system"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEnablementString(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
flags system.Enablement
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{0, "(no enablements)"},
|
||||||
|
{system.EWayland, "wayland"},
|
||||||
|
{system.EX11, "x11"},
|
||||||
|
{system.EDBus, "dbus"},
|
||||||
|
{system.EPulse, "pulseaudio"},
|
||||||
|
{system.EWayland | system.EX11, "wayland, x11"},
|
||||||
|
{system.EWayland | system.EDBus, "wayland, dbus"},
|
||||||
|
{system.EWayland | system.EPulse, "wayland, pulseaudio"},
|
||||||
|
{system.EX11 | system.EDBus, "x11, dbus"},
|
||||||
|
{system.EX11 | system.EPulse, "x11, pulseaudio"},
|
||||||
|
{system.EDBus | system.EPulse, "dbus, pulseaudio"},
|
||||||
|
{system.EWayland | system.EX11 | system.EDBus, "wayland, x11, dbus"},
|
||||||
|
{system.EWayland | system.EX11 | system.EPulse, "wayland, x11, pulseaudio"},
|
||||||
|
{system.EWayland | system.EDBus | system.EPulse, "wayland, dbus, pulseaudio"},
|
||||||
|
{system.EX11 | system.EDBus | system.EPulse, "x11, dbus, pulseaudio"},
|
||||||
|
{system.EWayland | system.EX11 | system.EDBus | system.EPulse, "wayland, x11, dbus, pulseaudio"},
|
||||||
|
|
||||||
|
{1 << 5, "e20"},
|
||||||
|
{1 << 6, "e40"},
|
||||||
|
{1 << 7, "e80"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.want, func(t *testing.T) {
|
||||||
|
if got := tc.flags.String(); got != tc.want {
|
||||||
|
t.Errorf("String: %q, want %q", got, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -78,7 +78,7 @@ func (m *Mkdir) Path() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mkdir) String() string {
|
func (m *Mkdir) String() string {
|
||||||
t := "Ensure"
|
t := "ensure"
|
||||||
if m.ephemeral {
|
if m.ephemeral {
|
||||||
t = TypeString(m.Type())
|
t = TypeString(m.Type())
|
||||||
}
|
}
|
||||||
|
@ -41,20 +41,20 @@ func TestEphemeral(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMkdir_String(t *testing.T) {
|
func TestMkdirString(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
want string
|
want string
|
||||||
ephemeral bool
|
ephemeral bool
|
||||||
et Enablement
|
et Enablement
|
||||||
}{
|
}{
|
||||||
{"Ensure", false, User},
|
{"ensure", false, User},
|
||||||
{"Ensure", false, Process},
|
{"ensure", false, Process},
|
||||||
{"Ensure", false, EWayland},
|
{"ensure", false, EWayland},
|
||||||
|
|
||||||
{"Wayland", true, EWayland},
|
{"wayland", true, EWayland},
|
||||||
{"X11", true, EX11},
|
{"x11", true, EX11},
|
||||||
{"D-Bus", true, EDBus},
|
{"dbus", true, EDBus},
|
||||||
{"PulseAudio", true, EPulse},
|
{"pulseaudio", true, EPulse},
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.want, func(t *testing.T) {
|
t.Run(tc.want, func(t *testing.T) {
|
||||||
|
34
system/op.go
34
system/op.go
@ -5,28 +5,29 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// User type is reverted at final launcher exit.
|
// User type is reverted at final launcher exit.
|
||||||
User = Enablement(ELen)
|
User = EM << iota
|
||||||
// Process type is unconditionally reverted on exit.
|
// Process type is unconditionally reverted on exit.
|
||||||
Process = Enablement(ELen + 1)
|
Process
|
||||||
|
|
||||||
|
CM
|
||||||
)
|
)
|
||||||
|
|
||||||
// Criteria specifies types of Op to revert.
|
// Criteria specifies types of Op to revert.
|
||||||
type Criteria struct {
|
type Criteria Enablement
|
||||||
*Enablements
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *Criteria) hasType(o Op) bool {
|
func (ec *Criteria) hasType(o Op) bool {
|
||||||
// nil criteria: revert everything except User
|
// nil criteria: revert everything except User
|
||||||
if ec.Enablements == nil {
|
if ec == nil {
|
||||||
return o.Type() != User
|
return o.Type() != User
|
||||||
}
|
}
|
||||||
|
|
||||||
return ec.Has(o.Type())
|
return Enablement(*ec)&o.Type() != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Op is a reversible system operation.
|
// Op is a reversible system operation.
|
||||||
@ -48,11 +49,22 @@ type Op interface {
|
|||||||
func TypeString(e Enablement) string {
|
func TypeString(e Enablement) string {
|
||||||
switch e {
|
switch e {
|
||||||
case User:
|
case User:
|
||||||
return "User"
|
return "user"
|
||||||
case Process:
|
case Process:
|
||||||
return "Process"
|
return "process"
|
||||||
default:
|
default:
|
||||||
return e.String()
|
buf := new(strings.Builder)
|
||||||
|
buf.Grow(48)
|
||||||
|
if v := e &^ User &^ Process; v != 0 {
|
||||||
|
buf.WriteString(v.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := User; i < CM; i <<= 1 {
|
||||||
|
if e&i != 0 {
|
||||||
|
buf.WriteString(", " + TypeString(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.TrimPrefix(buf.String(), ", ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +122,7 @@ func (sys *I) Commit(ctx context.Context) error {
|
|||||||
if sp != nil {
|
if sp != nil {
|
||||||
// rollback partial commit
|
// rollback partial commit
|
||||||
msg.Verbosef("commit faulted after %d ops, rolling back partial commit", len(sp.ops))
|
msg.Verbosef("commit faulted after %d ops, rolling back partial commit", len(sp.ops))
|
||||||
if err := sp.Revert(&Criteria{nil}); err != nil {
|
if err := sp.Revert(nil); err != nil {
|
||||||
log.Println("errors returned reverting partial commit:", err)
|
log.Println("errors returned reverting partial commit:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,10 +47,10 @@ func (ptc tcOp) test(t *testing.T, gotOps []Op, wantOps []Op, fn string) {
|
|||||||
ec *Criteria
|
ec *Criteria
|
||||||
want bool
|
want bool
|
||||||
}{
|
}{
|
||||||
{"nil", newCriteria(), ptc.et != User},
|
{"nil", nil, ptc.et != User},
|
||||||
{"self", newCriteria(ptc.et), true},
|
{"self", newCriteria(ptc.et), true},
|
||||||
{"all", newCriteria(EWayland, EX11, EDBus, EPulse, User, Process), true},
|
{"all", newCriteria(EWayland | EX11 | EDBus | EPulse | User | Process), true},
|
||||||
{"enablements", newCriteria(EWayland, EX11, EDBus, EPulse), ptc.et != User && ptc.et != Process},
|
{"enablements", newCriteria(EWayland | EX11 | EDBus | EPulse), ptc.et != User && ptc.et != Process},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
@ -65,15 +65,4 @@ func (ptc tcOp) test(t *testing.T, gotOps []Op, wantOps []Op, fn string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCriteria(labels ...Enablement) *Criteria {
|
func newCriteria(e Enablement) *Criteria { return (*Criteria)(&e) }
|
||||||
ec := new(Criteria)
|
|
||||||
if len(labels) == 0 {
|
|
||||||
return ec
|
|
||||||
}
|
|
||||||
|
|
||||||
ec.Enablements = new(Enablements)
|
|
||||||
for _, e := range labels {
|
|
||||||
ec.Set(e)
|
|
||||||
}
|
|
||||||
return ec
|
|
||||||
}
|
|
||||||
|
@ -37,15 +37,17 @@ func TestTypeString(t *testing.T) {
|
|||||||
{system.EX11, system.EX11.String()},
|
{system.EX11, system.EX11.String()},
|
||||||
{system.EDBus, system.EDBus.String()},
|
{system.EDBus, system.EDBus.String()},
|
||||||
{system.EPulse, system.EPulse.String()},
|
{system.EPulse, system.EPulse.String()},
|
||||||
{system.User, "User"},
|
{system.User, "user"},
|
||||||
{system.Process, "Process"},
|
{system.Process, "process"},
|
||||||
|
{system.User | system.Process, "user, process"},
|
||||||
|
{system.EWayland | system.User | system.Process, "wayland, user, process"},
|
||||||
|
{system.EX11 | system.Process, "x11, process"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run("label type string "+tc.want, func(t *testing.T) {
|
t.Run("label type string "+tc.want, func(t *testing.T) {
|
||||||
if got := system.TypeString(tc.e); got != tc.want {
|
if got := system.TypeString(tc.e); got != tc.want {
|
||||||
t.Errorf("TypeString(%d) = %v, want %v",
|
t.Errorf("TypeString: %q, want %q",
|
||||||
tc.e,
|
|
||||||
got, tc.want)
|
got, tc.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user