diff --git a/internal/env/env.go b/internal/env/env.go index 45fd5fcb..0c413733 100644 --- a/internal/env/env.go +++ b/internal/env/env.go @@ -1,21 +1,29 @@ -// Package env provides the [Paths] struct for efficiently building paths from the environment. +// Package env provides the [Paths] struct for efficiently building paths from +// the environment. package env import ( + "errors" + "io/fs" "log" "os" "strconv" "hakurei.app/check" + "hakurei.app/fhs" "hakurei.app/hst" ) +const VarRunNscd = fhs.Var + "run/nscd" + // Paths holds paths copied from the environment and is used to create [hst.Paths]. type Paths struct { // TempDir is returned by [os.TempDir]. TempDir *check.Absolute // RuntimePath is copied from $XDG_RUNTIME_DIR. RuntimePath *check.Absolute + // Whether [VarRunNscd] is a directory. + HasNscd bool } // Copy expands [Paths] into [hst.Paths]. @@ -37,14 +45,17 @@ func (env *Paths) Copy(v *hst.Paths, userid int) { } // CopyPaths returns a populated [Paths]. -func CopyPaths() *Paths { return CopyPathsFunc(log.Fatalf, os.TempDir, os.Getenv) } +func CopyPaths() *Paths { + return CopyPathsFunc(log.Fatalf, os.TempDir, os.Getenv, os.Stat) +} -// CopyPathsFunc returns a populated [Paths], -// using the provided [log.Fatalf], [os.TempDir], [os.Getenv] functions. +// CopyPathsFunc returns a populated [Paths], using the provided [log.Fatalf], +// [os.TempDir], [os.Getenv] functions. func CopyPathsFunc( fatalf func(format string, v ...any), tempdir func() string, getenv func(key string) string, + stat func(name string) (fs.FileInfo, error), ) *Paths { const xdgRuntimeDir = "XDG_RUNTIME_DIR" @@ -61,5 +72,14 @@ func CopyPathsFunc( env.RuntimePath = a } + if fi, err := stat(VarRunNscd); err != nil { + if !errors.Is(err, fs.ErrNotExist) { + fatalf("%v", err) + panic("unreachable") + } + } else { + env.HasNscd = fi.IsDir() + } + return &env } diff --git a/internal/env/env_test.go b/internal/env/env_test.go index 0e5db4a9..cce97da0 100644 --- a/internal/env/env_test.go +++ b/internal/env/env_test.go @@ -2,6 +2,7 @@ package env_test import ( "fmt" + "io/fs" "reflect" "testing" @@ -104,7 +105,9 @@ func TestCopyPaths(t *testing.T) { t.Fatalf("fatalf: %q, want %q", got, tc.fatal) } panic(stub.PanicExit) - }, func() string { return tc.tmp }, func(key string) string { return tc.env[key] }) + }, func() string { return tc.tmp }, func(key string) string { return tc.env[key] }, func(name string) (fs.FileInfo, error) { + return nil, fs.ErrNotExist + }) if tc.fatal != "" { t.Fatalf("copyPaths: expected fatal %q", tc.fatal) diff --git a/internal/outcome/dispatcher_test.go b/internal/outcome/dispatcher_test.go index 9ae2ba52..00771e56 100644 --- a/internal/outcome/dispatcher_test.go +++ b/internal/outcome/dispatcher_test.go @@ -23,6 +23,7 @@ import ( "hakurei.app/container/seccomp" "hakurei.app/container/std" "hakurei.app/hst" + "hakurei.app/internal/env" "hakurei.app/internal/stub" "hakurei.app/internal/system" "hakurei.app/message" @@ -174,6 +175,7 @@ func checkOpBehaviour(t *testing.T, testCases []opBehaviourTestCase) { call("cmdOutput", stub.ExpectArgs{container.Nonexistent, os.Stderr, []string{}, "/"}, []byte("0"), nil), call("tempdir", stub.ExpectArgs{}, container.Nonexistent+"/tmp", nil), call("lookupEnv", stub.ExpectArgs{"XDG_RUNTIME_DIR"}, wantRuntimePath, nil), + call("stat", stub.ExpectArgs{env.VarRunNscd}, stubFileInfoIsDir(true), nil), call("getuid", stub.ExpectArgs{}, 1000, nil), call("getgid", stub.ExpectArgs{}, 100, nil), diff --git a/internal/outcome/outcome.go b/internal/outcome/outcome.go index 4bdc2ede..0fe0016e 100644 --- a/internal/outcome/outcome.go +++ b/internal/outcome/outcome.go @@ -110,7 +110,7 @@ func newOutcomeState(k syscallDispatcher, msg message.Msg, id *hst.ID, config *h Paths: env.CopyPathsFunc(k.fatalf, k.tempdir, func(key string) string { v, _ := k.lookupEnv(key) return v - }), + }, k.stat), Container: config.Container, } diff --git a/internal/outcome/run_test.go b/internal/outcome/run_test.go index 2a412d8b..a1d83ec9 100644 --- a/internal/outcome/run_test.go +++ b/internal/outcome/run_test.go @@ -710,7 +710,7 @@ func (k *stubNixOS) lookupEnv(key string) (string, bool) { func (k *stubNixOS) stat(name string) (fs.FileInfo, error) { switch name { case "/var/run/nscd": - return nil, nil + return stubFileInfoIsDir(true), nil case "/run/user/1971/pulse": return nil, nil case "/run/user/1971/pulse/native": diff --git a/internal/outcome/spcontainer.go b/internal/outcome/spcontainer.go index 97d98dd1..bfa5dbdc 100644 --- a/internal/outcome/spcontainer.go +++ b/internal/outcome/spcontainer.go @@ -18,13 +18,12 @@ import ( "hakurei.app/hst" "hakurei.app/internal/acl" "hakurei.app/internal/dbus" + "hakurei.app/internal/env" "hakurei.app/internal/system" "hakurei.app/internal/validate" "hakurei.app/message" ) -const varRunNscd = fhs.Var + "run/nscd" - func init() { gob.Register(new(spParamsOp)) } // spParamsOp initialises unordered fields of [container.Params] and the @@ -136,17 +135,23 @@ type spFilesystemOp struct { } func (s *spFilesystemOp) toSystem(state *outcomeStateSys) error { - /* retrieve paths and hide them if they're made available in the sandbox; - - this feature tries to improve user experience of permissive defaults, and - to warn about issues in custom configuration; it is NOT a security feature - and should not be treated as such, ALWAYS be careful with what you bind */ + // retrieve paths and hide them if they're made available in the sandbox + // + // this feature tries to improve user experience of permissive defaults, and + // to warn about issues in custom configuration; it is NOT a security feature + // and should not be treated as such, ALWAYS be careful with what you bind hidePaths := []string{ state.sc.RuntimePath.String(), state.sc.SharePath.String(), + } - // this causes emulated passwd database to be bypassed on some /etc/ setups - varRunNscd, + if state.Paths == nil || state.HasNscd { + hidePaths = append(hidePaths, + // this causes emulated passwd database to be bypassed on some /etc/ + // setups, made optional to avoid needlessly creating it on + // non-glibc systems when invoking permissive defaults + env.VarRunNscd, + ) } // dbus.Address does not go through syscallDispatcher