diff --git a/internal/app/seal.go b/internal/app/seal.go index fd42af6..9199f5b 100644 --- a/internal/app/seal.go +++ b/internal/app/seal.go @@ -9,6 +9,7 @@ import ( "strconv" "git.ophivana.moe/cat/fortify/dbus" + "git.ophivana.moe/cat/fortify/helper/bwrap" "git.ophivana.moe/cat/fortify/internal" "git.ophivana.moe/cat/fortify/internal/state" "git.ophivana.moe/cat/fortify/internal/verbose" @@ -159,6 +160,11 @@ func (a *app) Seal(config *Config) error { } conf.Filesystem = append(conf.Filesystem, b...) } + // hide nscd from sandbox if present + nscd := "/var/run/nscd" + if _, err := os.Stat(nscd); !errors.Is(err, os.ErrNotExist) { + conf.Tmpfs = append(conf.Tmpfs, bwrap.TmpfsConfig{Size: 8 * 1024, Dir: nscd}) + } // bind GPU stuff if config.Confinement.Enablements.Has(state.EnableX) || config.Confinement.Enablements.Has(state.EnableWayland) { conf.Filesystem = append(conf.Filesystem, &FilesystemConfig{Src: "/dev/dri", Device: true}) diff --git a/internal/app/share.runtime.go b/internal/app/share.runtime.go index 86add79..ac4135f 100644 --- a/internal/app/share.runtime.go +++ b/internal/app/share.runtime.go @@ -1,7 +1,6 @@ package app import ( - "os" "path" "git.ophivana.moe/cat/fortify/acl" @@ -13,17 +12,10 @@ const ( xdgRuntimeDir = "XDG_RUNTIME_DIR" xdgSessionClass = "XDG_SESSION_CLASS" xdgSessionType = "XDG_SESSION_TYPE" - - shell = "SHELL" ) // shareRuntime queues actions for sharing/ensuring the runtime and share directories func (seal *appSeal) shareRuntime() { - // look up shell - if s, ok := os.LookupEnv(shell); ok { - seal.sys.setEnv(shell, s) - } - // mount tmpfs on inner runtime (e.g. `/run/user/%d`) seal.sys.bwrap.Tmpfs = append(seal.sys.bwrap.Tmpfs, bwrap.PermConfig[bwrap.TmpfsConfig]{ diff --git a/internal/app/share.system.go b/internal/app/share.system.go new file mode 100644 index 0000000..1b17ca4 --- /dev/null +++ b/internal/app/share.system.go @@ -0,0 +1,43 @@ +package app + +import ( + "os" + "path" +) + +const ( + shell = "SHELL" +) + +// shareSystem queues various system-related actions +func (seal *appSeal) shareSystem() { + // look up shell + sh := "/bin/sh" + if s, ok := os.LookupEnv(shell); ok { + seal.sys.setEnv(shell, s) + sh = s + } + + // generate /etc/passwd + passwdPath := path.Join(seal.share, "passwd") + username := "chronos" + if seal.sys.Username != "" { + username = seal.sys.Username + seal.sys.setEnv("USER", seal.sys.Username) + } + homeDir := "/var/empty" + if seal.sys.HomeDir != "" { + homeDir = seal.sys.HomeDir + seal.sys.setEnv("HOME", seal.sys.HomeDir) + } + passwd := username + ":x:65534:65534:Fortify:" + homeDir + ":" + sh + "\n" + seal.sys.writeFile(passwdPath, []byte(passwd)) + + // write /etc/group + groupPath := path.Join(seal.share, "group") + seal.sys.writeFile(groupPath, []byte("fortify:x:65534:\n")) + + // bind /etc/passwd and /etc/group + seal.sys.bind(passwdPath, "/etc/passwd", true) + seal.sys.bind(groupPath, "/etc/group", true) +} diff --git a/internal/app/system.go b/internal/app/system.go index 7fc7312..4f3b673 100644 --- a/internal/app/system.go +++ b/internal/app/system.go @@ -75,6 +75,8 @@ type appSealTx struct { xhost []string // paths of directories to ensure mkdir []appEnsureEntry + // dst, data pairs of temporarily available files + files [][2]string // dst, src pairs of temporarily shared files tmpfiles [][2]string // dst, src pairs of temporarily hard linked files @@ -177,6 +179,13 @@ func (tx *appSealTx) changeHosts(username string) { tx.xhost = append(tx.xhost, username) } +// writeFile appends a files action +func (tx *appSealTx) writeFile(dst string, data []byte) { + tx.files = append(tx.files, [2]string{dst, string(data)}) + tx.updatePerm(dst, acl.Read) + tx.bind(dst, dst, true) +} + // copyFile appends a tmpfiles action func (tx *appSealTx) copyFile(dst, src string) { tx.tmpfiles = append(tx.tmpfiles, [2]string{dst, src}) @@ -198,7 +207,7 @@ type ( ) // commit applies recorded actions -// order: xhost, mkdir, tmpfiles, hardlinks, dbus, acl +// order: xhost, mkdir, files, tmpfiles, hardlinks, dbus, acl func (tx *appSealTx) commit() error { if tx.complete { panic("seal transaction committed twice") @@ -249,6 +258,18 @@ func (tx *appSealTx) commit() error { } } + // write files + for _, file := range tx.files { + verbose.Println("writing", len(file[1]), "bytes of data to", file[0]) + if err := os.WriteFile(file[0], []byte(file[1]), 0600); err != nil { + return (*TmpfileError)(wrapError(err, + fmt.Sprintf("cannot write file '%s': %s", file[0], err))) + } else { + // register partial commit + txp.writeFile(file[0], make([]byte, 0)) // data not necessary for revert + } + } + // publish tmpfiles for _, tmpfile := range tx.tmpfiles { verbose.Println("publishing tmpfile", tmpfile[0], "from", tmpfile[1]) @@ -307,7 +328,7 @@ func (tx *appSealTx) commit() error { } // revert rolls back recorded actions -// order: acl, dbus, hardlinks, tmpfiles, mkdir, xhost +// order: acl, dbus, hardlinks, tmpfiles, files, mkdir, xhost // errors are printed but not treated as fatal func (tx *appSealTx) revert(tags *state.Enablements) error { if tx.closed { @@ -357,6 +378,13 @@ func (tx *appSealTx) revert(tags *state.Enablements) error { joinError(err, fmt.Sprintf("cannot remove tmpfile '%s': %s", tmpfile[0], err)) } + // remove files + for _, file := range tx.files { + verbose.Println("removing file", file[0]) + err := os.Remove(file[0]) + joinError(err, fmt.Sprintf("cannot remove file '%s': %s", file[0], err)) + } + // remove (empty) ephemeral directories for i := len(tx.mkdir); i > 0; i-- { dir := tx.mkdir[i-1] @@ -389,6 +417,7 @@ func (seal *appSeal) shareAll(bus [2]*dbus.Config) error { seal.shared = true seal.shareRuntime() + seal.shareSystem() targetRuntime := seal.shareRuntimeChild() verbose.Printf("child runtime data dir '%s' configured\n", targetRuntime) if err := seal.shareDisplay(); err != nil { diff --git a/internal/shim/payload.go b/internal/shim/payload.go index 718921b..3919bb8 100644 --- a/internal/shim/payload.go +++ b/internal/shim/payload.go @@ -13,7 +13,7 @@ type Payload struct { Exec [2]string // bwrap config, nil for permissive Bwrap *bwrap.Config - // whether to pas wayland fd + // whether to pass wayland fd WL bool // verbosity pass through