app: generate and replace passwd and group files

This ensures libc functions get correct user information.

Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
This commit is contained in:
Ophestra Umiker 2024-10-13 02:43:00 +09:00
parent 65a5f8fb08
commit e4536b87ad
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
5 changed files with 81 additions and 11 deletions

View File

@ -9,6 +9,7 @@ import (
"strconv" "strconv"
"git.ophivana.moe/cat/fortify/dbus" "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"
"git.ophivana.moe/cat/fortify/internal/state" "git.ophivana.moe/cat/fortify/internal/state"
"git.ophivana.moe/cat/fortify/internal/verbose" "git.ophivana.moe/cat/fortify/internal/verbose"
@ -159,6 +160,11 @@ func (a *app) Seal(config *Config) error {
} }
conf.Filesystem = append(conf.Filesystem, b...) 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 // bind GPU stuff
if config.Confinement.Enablements.Has(state.EnableX) || config.Confinement.Enablements.Has(state.EnableWayland) { if config.Confinement.Enablements.Has(state.EnableX) || config.Confinement.Enablements.Has(state.EnableWayland) {
conf.Filesystem = append(conf.Filesystem, &FilesystemConfig{Src: "/dev/dri", Device: true}) conf.Filesystem = append(conf.Filesystem, &FilesystemConfig{Src: "/dev/dri", Device: true})

View File

@ -1,7 +1,6 @@
package app package app
import ( import (
"os"
"path" "path"
"git.ophivana.moe/cat/fortify/acl" "git.ophivana.moe/cat/fortify/acl"
@ -13,17 +12,10 @@ const (
xdgRuntimeDir = "XDG_RUNTIME_DIR" xdgRuntimeDir = "XDG_RUNTIME_DIR"
xdgSessionClass = "XDG_SESSION_CLASS" xdgSessionClass = "XDG_SESSION_CLASS"
xdgSessionType = "XDG_SESSION_TYPE" xdgSessionType = "XDG_SESSION_TYPE"
shell = "SHELL"
) )
// shareRuntime queues actions for sharing/ensuring the runtime and share directories // shareRuntime queues actions for sharing/ensuring the runtime and share directories
func (seal *appSeal) shareRuntime() { 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`) // mount tmpfs on inner runtime (e.g. `/run/user/%d`)
seal.sys.bwrap.Tmpfs = append(seal.sys.bwrap.Tmpfs, seal.sys.bwrap.Tmpfs = append(seal.sys.bwrap.Tmpfs,
bwrap.PermConfig[bwrap.TmpfsConfig]{ bwrap.PermConfig[bwrap.TmpfsConfig]{

View File

@ -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)
}

View File

@ -75,6 +75,8 @@ type appSealTx struct {
xhost []string xhost []string
// paths of directories to ensure // paths of directories to ensure
mkdir []appEnsureEntry mkdir []appEnsureEntry
// dst, data pairs of temporarily available files
files [][2]string
// dst, src pairs of temporarily shared files // dst, src pairs of temporarily shared files
tmpfiles [][2]string tmpfiles [][2]string
// dst, src pairs of temporarily hard linked files // dst, src pairs of temporarily hard linked files
@ -177,6 +179,13 @@ func (tx *appSealTx) changeHosts(username string) {
tx.xhost = append(tx.xhost, username) 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 // copyFile appends a tmpfiles action
func (tx *appSealTx) copyFile(dst, src string) { func (tx *appSealTx) copyFile(dst, src string) {
tx.tmpfiles = append(tx.tmpfiles, [2]string{dst, src}) tx.tmpfiles = append(tx.tmpfiles, [2]string{dst, src})
@ -198,7 +207,7 @@ type (
) )
// commit applies recorded actions // commit applies recorded actions
// order: xhost, mkdir, tmpfiles, hardlinks, dbus, acl // order: xhost, mkdir, files, tmpfiles, hardlinks, dbus, acl
func (tx *appSealTx) commit() error { func (tx *appSealTx) commit() error {
if tx.complete { if tx.complete {
panic("seal transaction committed twice") 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 // publish tmpfiles
for _, tmpfile := range tx.tmpfiles { for _, tmpfile := range tx.tmpfiles {
verbose.Println("publishing tmpfile", tmpfile[0], "from", tmpfile[1]) verbose.Println("publishing tmpfile", tmpfile[0], "from", tmpfile[1])
@ -307,7 +328,7 @@ func (tx *appSealTx) commit() error {
} }
// revert rolls back recorded actions // 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 // errors are printed but not treated as fatal
func (tx *appSealTx) revert(tags *state.Enablements) error { func (tx *appSealTx) revert(tags *state.Enablements) error {
if tx.closed { 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)) 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 // remove (empty) ephemeral directories
for i := len(tx.mkdir); i > 0; i-- { for i := len(tx.mkdir); i > 0; i-- {
dir := tx.mkdir[i-1] dir := tx.mkdir[i-1]
@ -389,6 +417,7 @@ func (seal *appSeal) shareAll(bus [2]*dbus.Config) error {
seal.shared = true seal.shared = true
seal.shareRuntime() seal.shareRuntime()
seal.shareSystem()
targetRuntime := seal.shareRuntimeChild() targetRuntime := seal.shareRuntimeChild()
verbose.Printf("child runtime data dir '%s' configured\n", targetRuntime) verbose.Printf("child runtime data dir '%s' configured\n", targetRuntime)
if err := seal.shareDisplay(); err != nil { if err := seal.shareDisplay(); err != nil {

View File

@ -13,7 +13,7 @@ type Payload struct {
Exec [2]string Exec [2]string
// bwrap config, nil for permissive // bwrap config, nil for permissive
Bwrap *bwrap.Config Bwrap *bwrap.Config
// whether to pas wayland fd // whether to pass wayland fd
WL bool WL bool
// verbosity pass through // verbosity pass through