aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/app/app.go4
-rw-r--r--internal/app/export_test.go2
-rw-r--r--internal/app/seal.go96
-rw-r--r--internal/app/share.go88
-rw-r--r--internal/app/start.go22
-rw-r--r--internal/app/system.go48
6 files changed, 122 insertions, 138 deletions
diff --git a/internal/app/app.go b/internal/app/app.go
index 9dd082b9..eb463924 100644
--- a/internal/app/app.go
+++ b/internal/app/app.go
@@ -47,10 +47,10 @@ func (a *app) String() string {
}
if a.appSeal != nil {
- if a.appSeal.sys.user.uid == nil {
+ if a.appSeal.user.uid == nil {
return fmt.Sprintf("(sealed app %s with invalid uid)", a.id)
}
- return fmt.Sprintf("(sealed app %s as uid %s)", a.id, a.appSeal.sys.user.uid)
+ return fmt.Sprintf("(sealed app %s as uid %s)", a.id, a.appSeal.user.uid)
}
return fmt.Sprintf("(unsealed app %s)", a.id)
diff --git a/internal/app/export_test.go b/internal/app/export_test.go
index 54b7f345..af52912b 100644
--- a/internal/app/export_test.go
+++ b/internal/app/export_test.go
@@ -16,5 +16,5 @@ func NewWithID(id fst.ID, os sys.State) fst.App {
func AppSystemBwrap(a fst.App) (*system.I, *bwrap.Config) {
v := a.(*app)
- return v.appSeal.sys.I, v.appSeal.sys.bwrap
+ return v.appSeal.sys, v.appSeal.container
}
diff --git a/internal/app/seal.go b/internal/app/seal.go
index 9fce8f2c..251ee2d6 100644
--- a/internal/app/seal.go
+++ b/internal/app/seal.go
@@ -7,6 +7,7 @@ import (
"fmt"
"io"
"io/fs"
+ "os"
"path"
"regexp"
@@ -42,8 +43,13 @@ type appSeal struct {
appID string
// final argv, passed to init
command []string
- // state instance initialised during seal and used on process lifecycle events
+
+ // state instance initialised during seal; used during process lifecycle events
store state.Store
+ // whether [system.I] was committed; used during process lifecycle events
+ needRevert bool
+ // whether state was inserted into [state.Store]; used during process lifecycle events
+ stateInStore bool
// process-specific share directory path ([os.TempDir])
share string
@@ -57,13 +63,24 @@ type appSeal struct {
// when this gets set no attempt is made to attach security-context-v1
// and the bare socket is mounted to the sandbox
directWayland bool
+ // mount tmpfs over these paths, runs right before extraPerms
+ override []string
// extra [acl.Update] ops, appended at the end of [system.I]
extraPerms []*sealedExtraPerm
+ // post fsu state
+ user appUser
+ // inner XDG_RUNTIME_DIR, default formatting via user
+ innerRuntimeDir string
+ // mapped uid and gid in user namespace
+ mapuid *stringPair[int]
+
+ sys *system.I
+ container *bwrap.Config
+ bwrapSync *os.File
+
// prevents sharing from happening twice
shared bool
- // seal system-level component
- sys *appSealSys
system.Enablements
fst.Paths
@@ -71,6 +88,24 @@ type appSeal struct {
// protected by upstream mutex
}
+// appUser stores post-fsu credentials and metadata
+type appUser struct {
+ // application id
+ aid *stringPair[int]
+ // target uid resolved by fid:aid
+ uid *stringPair[int]
+
+ // supplementary group ids
+ supp []string
+
+ // home directory host path
+ data string
+ // app user home directory
+ home string
+ // passwd database username
+ username string
+}
+
type sealedExtraPerm struct {
name string
perms acl.Perms
@@ -110,9 +145,6 @@ func (a *app) Seal(config *fst.Config) error {
seal.appID = config.ID
seal.command = config.Command
- // create seal system component
- seal.sys = new(appSealSys)
-
{
// mapped uid defaults to 65534 to work around file ownership checks due to a bwrap limitation
mapuid := 65534
@@ -121,8 +153,8 @@ func (a *app) Seal(config *fst.Config) error {
// separate workaround is introduced to map priv-side caller uid in namespace
mapuid = a.sys.Geteuid()
}
- seal.sys.mapuid = newInt(mapuid)
- seal.sys.runtime = path.Join("/run/user", seal.sys.mapuid.String())
+ seal.mapuid = newInt(mapuid)
+ seal.innerRuntimeDir = path.Join("/run/user", seal.mapuid.String())
}
// validate uid and set user info
@@ -130,42 +162,42 @@ func (a *app) Seal(config *fst.Config) error {
return fmsg.WrapError(ErrUser,
fmt.Sprintf("aid %d out of range", config.Confinement.AppID))
}
- seal.sys.user = appUser{
+ seal.user = appUser{
aid: newInt(config.Confinement.AppID),
data: config.Confinement.Outer,
home: config.Confinement.Inner,
username: config.Confinement.Username,
}
- if seal.sys.user.username == "" {
- seal.sys.user.username = "chronos"
- } else if !posixUsername.MatchString(seal.sys.user.username) ||
- len(seal.sys.user.username) >= internal.Sysconf_SC_LOGIN_NAME_MAX() {
+ if seal.user.username == "" {
+ seal.user.username = "chronos"
+ } else if !posixUsername.MatchString(seal.user.username) ||
+ len(seal.user.username) >= internal.Sysconf_SC_LOGIN_NAME_MAX() {
return fmsg.WrapError(ErrName,
- fmt.Sprintf("invalid user name %q", seal.sys.user.username))
+ fmt.Sprintf("invalid user name %q", seal.user.username))
}
- if seal.sys.user.data == "" || !path.IsAbs(seal.sys.user.data) {
+ if seal.user.data == "" || !path.IsAbs(seal.user.data) {
return fmsg.WrapError(ErrHome,
- fmt.Sprintf("invalid home directory %q", seal.sys.user.data))
+ fmt.Sprintf("invalid home directory %q", seal.user.data))
}
- if seal.sys.user.home == "" {
- seal.sys.user.home = seal.sys.user.data
+ if seal.user.home == "" {
+ seal.user.home = seal.user.data
}
// invoke fsu for full uid
- if u, err := a.sys.Uid(seal.sys.user.aid.unwrap()); err != nil {
+ if u, err := a.sys.Uid(seal.user.aid.unwrap()); err != nil {
return err
} else {
- seal.sys.user.uid = newInt(u)
+ seal.user.uid = newInt(u)
}
// resolve supplementary group ids from names
- seal.sys.user.supp = make([]string, len(config.Confinement.Groups))
+ seal.user.supp = make([]string, len(config.Confinement.Groups))
for i, name := range config.Confinement.Groups {
if g, err := a.sys.LookupGroup(name); err != nil {
return fmsg.WrapError(err,
fmt.Sprintf("unknown group %q", name))
} else {
- seal.sys.user.supp[i] = g.Gid
+ seal.user.supp[i] = g.Gid
}
}
@@ -242,11 +274,11 @@ func (a *app) Seal(config *fst.Config) error {
if b, err := config.Confinement.Sandbox.Bwrap(a.sys); err != nil {
return err
} else {
- seal.sys.bwrap = b
+ seal.container = b
}
- seal.sys.override = config.Confinement.Sandbox.Override
- if seal.sys.bwrap.SetEnv == nil {
- seal.sys.bwrap.SetEnv = make(map[string]string)
+ seal.override = config.Confinement.Sandbox.Override
+ if seal.container.SetEnv == nil {
+ seal.container.SetEnv = make(map[string]string)
}
// open process state store
@@ -255,11 +287,11 @@ func (a *app) Seal(config *fst.Config) error {
seal.store = state.NewMulti(seal.RunDirPath)
// initialise system interface with os uid
- seal.sys.I = system.New(seal.sys.user.uid.unwrap())
- seal.sys.I.IsVerbose = fmsg.Load
- seal.sys.I.Verbose = fmsg.Verbose
- seal.sys.I.Verbosef = fmsg.Verbosef
- seal.sys.I.WrapErr = fmsg.WrapError
+ seal.sys = system.New(seal.user.uid.unwrap())
+ seal.sys.IsVerbose = fmsg.Load
+ seal.sys.Verbose = fmsg.Verbose
+ seal.sys.Verbosef = fmsg.Verbosef
+ seal.sys.WrapErr = fmsg.WrapError
// pass through enablements
seal.Enablements = config.Confinement.Enablements
@@ -271,7 +303,7 @@ func (a *app) Seal(config *fst.Config) error {
// verbose log seal information
fmsg.Verbosef("created application seal for uid %s (%s) groups: %v, command: %s",
- seal.sys.user.uid, seal.sys.user.username, config.Confinement.Groups, config.Command)
+ seal.user.uid, seal.user.username, config.Confinement.Groups, config.Command)
// seal app and release lock
a.appSeal = seal
diff --git a/internal/app/share.go b/internal/app/share.go
index f6271b22..2094b48b 100644
--- a/internal/app/share.go
+++ b/internal/app/share.go
@@ -68,23 +68,23 @@ func (seal *appSeal) setupShares(bus [2]*dbus.Config, os sys.State) error {
seal.sys.UpdatePermType(system.User, targetTmpdirParent, acl.Execute)
// ensure child tmpdir (e.g. `/tmp/fortify.%d/tmpdir/%d`)
- targetTmpdir := path.Join(targetTmpdirParent, seal.sys.user.aid.String())
+ targetTmpdir := path.Join(targetTmpdirParent, seal.user.aid.String())
seal.sys.Ensure(targetTmpdir, 01700)
seal.sys.UpdatePermType(system.User, targetTmpdir, acl.Read, acl.Write, acl.Execute)
- seal.sys.bwrap.Bind(targetTmpdir, "/tmp", false, true)
+ seal.container.Bind(targetTmpdir, "/tmp", false, true)
/*
XDG runtime directory
*/
// mount tmpfs on inner runtime (e.g. `/run/user/%d`)
- seal.sys.bwrap.Tmpfs("/run/user", 1*1024*1024)
- seal.sys.bwrap.Tmpfs(seal.sys.runtime, 8*1024*1024)
+ seal.container.Tmpfs("/run/user", 1*1024*1024)
+ seal.container.Tmpfs(seal.innerRuntimeDir, 8*1024*1024)
// point to inner runtime path `/run/user/%d`
- seal.sys.bwrap.SetEnv[xdgRuntimeDir] = seal.sys.runtime
- seal.sys.bwrap.SetEnv[xdgSessionClass] = "user"
- seal.sys.bwrap.SetEnv[xdgSessionType] = "tty"
+ seal.container.SetEnv[xdgRuntimeDir] = seal.innerRuntimeDir
+ seal.container.SetEnv[xdgSessionClass] = "user"
+ seal.container.SetEnv[xdgSessionType] = "tty"
// ensure RunDir (e.g. `/run/user/%d/fortify`)
seal.sys.Ensure(seal.RunDirPath, 0700)
@@ -106,29 +106,29 @@ func (seal *appSeal) setupShares(bus [2]*dbus.Config, os sys.State) error {
// look up shell
sh := "/bin/sh"
if s, ok := os.LookupEnv(shell); ok {
- seal.sys.bwrap.SetEnv[shell] = s
+ seal.container.SetEnv[shell] = s
sh = s
}
// bind home directory
homeDir := "/var/empty"
- if seal.sys.user.home != "" {
- homeDir = seal.sys.user.home
+ if seal.user.home != "" {
+ homeDir = seal.user.home
}
username := "chronos"
- if seal.sys.user.username != "" {
- username = seal.sys.user.username
+ if seal.user.username != "" {
+ username = seal.user.username
}
- seal.sys.bwrap.Bind(seal.sys.user.data, homeDir, false, true)
- seal.sys.bwrap.Chdir = homeDir
- seal.sys.bwrap.SetEnv["HOME"] = homeDir
- seal.sys.bwrap.SetEnv["USER"] = username
+ seal.container.Bind(seal.user.data, homeDir, false, true)
+ seal.container.Chdir = homeDir
+ seal.container.SetEnv["HOME"] = homeDir
+ seal.container.SetEnv["USER"] = username
// generate /etc/passwd and /etc/group
- seal.sys.bwrap.CopyBind("/etc/passwd",
- []byte(username+":x:"+seal.sys.mapuid.String()+":"+seal.sys.mapuid.String()+":Fortify:"+homeDir+":"+sh+"\n"))
- seal.sys.bwrap.CopyBind("/etc/group",
- []byte("fortify:x:"+seal.sys.mapuid.String()+":\n"))
+ seal.container.CopyBind("/etc/passwd",
+ []byte(username+":x:"+seal.mapuid.String()+":"+seal.mapuid.String()+":Fortify:"+homeDir+":"+sh+"\n"))
+ seal.container.CopyBind("/etc/group",
+ []byte("fortify:x:"+seal.mapuid.String()+":\n"))
/*
Display servers
@@ -136,7 +136,7 @@ func (seal *appSeal) setupShares(bus [2]*dbus.Config, os sys.State) error {
// pass $TERM to launcher
if t, ok := os.LookupEnv(term); ok {
- seal.sys.bwrap.SetEnv[term] = t
+ seal.container.SetEnv[term] = t
}
// set up wayland
@@ -151,8 +151,8 @@ func (seal *appSeal) setupShares(bus [2]*dbus.Config, os sys.State) error {
socketPath = name
}
- innerPath := path.Join(seal.sys.runtime, wl.FallbackName)
- seal.sys.bwrap.SetEnv[wl.WaylandDisplay] = wl.FallbackName
+ innerPath := path.Join(seal.innerRuntimeDir, wl.FallbackName)
+ seal.container.SetEnv[wl.WaylandDisplay] = wl.FallbackName
if !seal.directWayland { // set up security-context-v1
socketDir := path.Join(seal.SharePath, "wayland")
@@ -163,11 +163,11 @@ func (seal *appSeal) setupShares(bus [2]*dbus.Config, os sys.State) error {
// use instance ID in case app id is not set
appID = "uk.gensokyo.fortify." + seal.id
}
- seal.sys.Wayland(&seal.sys.sp, outerPath, socketPath, appID, seal.id)
- seal.sys.bwrap.Bind(outerPath, innerPath)
+ seal.sys.Wayland(&seal.bwrapSync, outerPath, socketPath, appID, seal.id)
+ seal.container.Bind(outerPath, innerPath)
} else { // bind mount wayland socket (insecure)
fmsg.Verbose("direct wayland access, PROCEED WITH CAUTION")
- seal.sys.bwrap.Bind(socketPath, innerPath)
+ seal.container.Bind(socketPath, innerPath)
// ensure Wayland socket ACL (e.g. `/run/user/%d/wayland-%d`)
seal.sys.UpdatePermType(system.EWayland, socketPath, acl.Read, acl.Write, acl.Execute)
@@ -181,9 +181,9 @@ func (seal *appSeal) setupShares(bus [2]*dbus.Config, os sys.State) error {
return fmsg.WrapError(ErrXDisplay,
"DISPLAY is not set")
} else {
- seal.sys.ChangeHosts("#" + seal.sys.user.uid.String())
- seal.sys.bwrap.SetEnv[display] = d
- seal.sys.bwrap.Bind("/tmp/.X11-unix", "/tmp/.X11-unix")
+ seal.sys.ChangeHosts("#" + seal.user.uid.String())
+ seal.container.SetEnv[display] = d
+ seal.container.Bind("/tmp/.X11-unix", "/tmp/.X11-unix")
}
}
@@ -221,10 +221,10 @@ func (seal *appSeal) setupShares(bus [2]*dbus.Config, os sys.State) error {
// hard link pulse socket into target-executable share
psi := path.Join(seal.shareLocal, "pulse")
- p := path.Join(seal.sys.runtime, "pulse", "native")
+ p := path.Join(seal.innerRuntimeDir, "pulse", "native")
seal.sys.Link(ps, psi)
- seal.sys.bwrap.Bind(psi, p)
- seal.sys.bwrap.SetEnv[pulseServer] = "unix:" + p
+ seal.container.Bind(psi, p)
+ seal.container.SetEnv[pulseServer] = "unix:" + p
// publish current user's pulse cookie for target user
if src, err := discoverPulseCookie(os); err != nil {
@@ -232,9 +232,9 @@ func (seal *appSeal) setupShares(bus [2]*dbus.Config, os sys.State) error {
fmsg.Verbose(strings.TrimSpace(err.(*fmsg.BaseError).Message()))
} else {
innerDst := fst.Tmp + "/pulse-cookie"
- seal.sys.bwrap.SetEnv[pulseCookie] = innerDst
+ seal.container.SetEnv[pulseCookie] = innerDst
payload := new([]byte)
- seal.sys.bwrap.CopyBindRef(innerDst, &payload)
+ seal.container.CopyBindRef(innerDst, &payload)
seal.sys.CopyFile(payload, src, 256, 256)
}
}
@@ -260,14 +260,14 @@ func (seal *appSeal) setupShares(bus [2]*dbus.Config, os sys.State) error {
}
// share proxy sockets
- sessionInner := path.Join(seal.sys.runtime, "bus")
- seal.sys.bwrap.SetEnv[dbusSessionBusAddress] = "unix:path=" + sessionInner
- seal.sys.bwrap.Bind(sessionPath, sessionInner)
+ sessionInner := path.Join(seal.innerRuntimeDir, "bus")
+ seal.container.SetEnv[dbusSessionBusAddress] = "unix:path=" + sessionInner
+ seal.container.Bind(sessionPath, sessionInner)
seal.sys.UpdatePerm(sessionPath, acl.Read, acl.Write)
if bus[1] != nil {
systemInner := "/run/dbus/system_bus_socket"
- seal.sys.bwrap.SetEnv[dbusSystemBusAddress] = "unix:path=" + systemInner
- seal.sys.bwrap.Bind(systemPath, systemInner)
+ seal.container.SetEnv[dbusSystemBusAddress] = "unix:path=" + systemInner
+ seal.container.Bind(systemPath, systemInner)
seal.sys.UpdatePerm(systemPath, acl.Read, acl.Write)
}
}
@@ -276,14 +276,14 @@ func (seal *appSeal) setupShares(bus [2]*dbus.Config, os sys.State) error {
Miscellaneous
*/
- // queue overriding tmpfs at the end of seal.sys.bwrap.Filesystem
- for _, dest := range seal.sys.override {
- seal.sys.bwrap.Tmpfs(dest, 8*1024)
+ // queue overriding tmpfs at the end of seal.container.Filesystem
+ for _, dest := range seal.override {
+ seal.container.Tmpfs(dest, 8*1024)
}
// mount fortify in sandbox for init
- seal.sys.bwrap.Bind(os.MustExecutable(), path.Join(fst.Tmp, "sbin/fortify"))
- seal.sys.bwrap.Symlink("fortify", path.Join(fst.Tmp, "sbin/init"))
+ seal.container.Bind(os.MustExecutable(), path.Join(fst.Tmp, "sbin/fortify"))
+ seal.container.Symlink("fortify", path.Join(fst.Tmp, "sbin/init"))
// append extra perms
for _, p := range seal.extraPerms {
diff --git a/internal/app/start.go b/internal/app/start.go
index f5fe9a33..4cbe6168 100644
--- a/internal/app/start.go
+++ b/internal/app/start.go
@@ -51,15 +51,15 @@ func (a *app) Run(ctx context.Context, rs *fst.RunState) error {
if err := a.appSeal.sys.Commit(ctx); err != nil {
return err
}
- a.appSeal.sys.needRevert = true
+ a.appSeal.needRevert = true
// start shim via manager
a.shim = new(shim.Shim)
waitErr := make(chan error, 1)
if startTime, err := a.shim.Start(
- a.appSeal.sys.user.aid.String(),
- a.appSeal.sys.user.supp,
- a.appSeal.sys.sp,
+ a.appSeal.user.aid.String(),
+ a.appSeal.user.supp,
+ a.appSeal.bwrapSync,
); err != nil {
return err
} else {
@@ -80,8 +80,8 @@ func (a *app) Run(ctx context.Context, rs *fst.RunState) error {
if err = a.shim.Serve(shimSetupCtx, &shim.Payload{
Argv: a.appSeal.command,
Exec: shimExec,
- Bwrap: a.appSeal.sys.bwrap,
- Home: a.appSeal.sys.user.data,
+ Bwrap: a.appSeal.container,
+ Home: a.appSeal.user.data,
Verbose: fmsg.Load(),
}); err != nil {
@@ -97,10 +97,10 @@ func (a *app) Run(ctx context.Context, rs *fst.RunState) error {
// register process state
var err0 = new(StateStoreError)
- err0.Inner, err0.DoErr = a.appSeal.store.Do(a.appSeal.sys.user.aid.unwrap(), func(c state.Cursor) {
+ err0.Inner, err0.DoErr = a.appSeal.store.Do(a.appSeal.user.aid.unwrap(), func(c state.Cursor) {
err0.InnerErr = c.Save(&sd, a.appSeal.ct)
})
- a.appSeal.sys.saveState = true
+ a.appSeal.stateInStore = true
if err = err0.equiv("cannot save process state:"); err != nil {
return err
}
@@ -147,10 +147,10 @@ func (a *app) Run(ctx context.Context, rs *fst.RunState) error {
// update store and revert app setup transaction
e := new(StateStoreError)
- e.Inner, e.DoErr = a.appSeal.store.Do(a.appSeal.sys.user.aid.unwrap(), func(b state.Cursor) {
+ e.Inner, e.DoErr = a.appSeal.store.Do(a.appSeal.user.aid.unwrap(), func(b state.Cursor) {
e.InnerErr = func() error {
// destroy defunct state entry
- if cmd := a.shim.Unwrap(); cmd != nil && a.appSeal.sys.saveState {
+ if cmd := a.shim.Unwrap(); cmd != nil && a.appSeal.stateInStore {
if err := b.Destroy(a.id.unwrap()); err != nil {
return err
}
@@ -198,7 +198,7 @@ func (a *app) Run(ctx context.Context, rs *fst.RunState) error {
}
}
- if a.appSeal.sys.needRevert {
+ if a.appSeal.needRevert {
if err := a.appSeal.sys.Revert(ec); err != nil {
return err.(RevertCompoundError)
}
diff --git a/internal/app/system.go b/internal/app/system.go
deleted file mode 100644
index 3a92b0fd..00000000
--- a/internal/app/system.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package app
-
-import (
- "os"
-
- "git.gensokyo.uk/security/fortify/helper/bwrap"
- "git.gensokyo.uk/security/fortify/system"
-)
-
-// appSealSys encapsulates app seal behaviour with OS interactions
-type appSealSys struct {
- bwrap *bwrap.Config
- // bwrap sync fd
- sp *os.File
- // paths to override by mounting tmpfs over them
- override []string
-
- // default formatted XDG_RUNTIME_DIR of User
- runtime string
- // target user sealed from config
- user appUser
-
- // mapped uid and gid in user namespace
- mapuid *stringPair[int]
-
- needRevert bool
- saveState bool
- *system.I
-
- // protected by upstream mutex
-}
-
-type appUser struct {
- // application id
- aid *stringPair[int]
- // target uid resolved by fid:aid
- uid *stringPair[int]
-
- // supplementary group ids
- supp []string
-
- // home directory host path
- data string
- // app user home directory
- home string
- // passwd database username
- username string
-}