fix: fall back to env path resolution when root has no passwd entry

Running as uid 0 without a resolvable root passwd entry (scratch
containers, minimal chroots) panicked at startup on every command,
because the euid-based path resolution had no fallback. Fall back to
env-derived resolution there: without a passwd database there is no
user switching, so the cross-user poisoning that branch prevents
cannot occur.

Also restore the underlying cause in the generic event-log init error
(minimal output hid it after the usefulerror change), and document
that root's per-user data lives under /root regardless of a preserved
HOME.
This commit is contained in:
Sahilb315
2026-07-14 04:49:07 +05:30
parent ad16d8c162
commit 297f516242
4 changed files with 45 additions and 10 deletions
+19 -8
View File
@@ -658,6 +658,12 @@ func rootCacheDir() (string, error) {
return filepath.Join(home, ".cache"), nil
}
// Overridable in tests to exercise the passwd-unavailable fallback.
var (
rootConfigDirResolver = rootConfigDir
rootCacheDirResolver = rootCacheDir
)
// realUserHomeDir returns the current user's home from the passwd database,
// ignoring HOME and XDG_* env vars that may be leaked from another account.
// Overridable in tests.
@@ -707,11 +713,15 @@ func configDir() (string, error) {
}
if configGeteuid() == 0 {
base, err := rootConfigDir()
if err != nil {
return "", err
if base, err := rootConfigDirResolver(); err == nil {
return filepath.Join(base, pmgDefaultHomeRelativePath), nil
} else {
// No resolvable root passwd entry (e.g. scratch containers,
// minimal chroots). Fall back to env-based resolution: without a
// passwd database there is no user switching, so the cross-user
// poisoning this branch prevents cannot occur.
log.Warnf("failed to resolve root home for config dir, using environment: %v", err)
}
return filepath.Join(base, pmgDefaultHomeRelativePath), nil
}
userConfigDir, err := os.UserConfigDir()
@@ -835,11 +845,12 @@ func cacheDir() (string, error) {
return filepath.Join(baseDir, pmgDefaultHomeRelativePath), nil
case "darwin", "linux":
if configGeteuid() == 0 {
base, err := rootCacheDir()
if err != nil {
return "", err
if base, err := rootCacheDirResolver(); err == nil {
return filepath.Join(base, pmgDefaultHomeRelativePath), nil
} else {
// Same fallback rationale as configDir.
log.Warnf("failed to resolve root home for cache dir, using environment: %v", err)
}
return filepath.Join(base, pmgDefaultHomeRelativePath), nil
}
userCacheDir, err := os.UserCacheDir()
+20
View File
@@ -78,3 +78,23 @@ func TestCacheDirAsNonRootUsesEnvHome(t *testing.T) {
require.NoError(t, err)
assert.Contains(t, dir, "/home/victim")
}
func TestRootDirsFallBackToEnvWhenPasswdUnavailable(t *testing.T) {
poisonUserEnv(t)
withEuid(t, 0)
origConfig, origCache := rootConfigDirResolver, rootCacheDirResolver
rootConfigDirResolver = func() (string, error) { return "", assert.AnError }
rootCacheDirResolver = func() (string, error) { return "", assert.AnError }
t.Cleanup(func() {
rootConfigDirResolver, rootCacheDirResolver = origConfig, origCache
})
dir, err := configDir()
require.NoError(t, err)
assert.Contains(t, dir, "/home/victim")
dir, err = cacheDir()
require.NoError(t, err)
assert.Contains(t, dir, "/home/victim")
}