fix(auth): harden Windows auth store privacy

This commit is contained in:
2026-08-16 18:44:38 +02:00
parent f6e4dbcae2
commit 25ee8b87d1
8 changed files with 556 additions and 31 deletions
+15 -20
View File
@@ -6,7 +6,6 @@ import (
"io"
"os"
"path/filepath"
"runtime"
"github.com/aritmolab/thothii/tools/tht/internal/safeio"
"github.com/gofrs/flock"
@@ -112,26 +111,15 @@ func decodeStrictYAML(contents []byte, destination any) error {
}
func requirePrivateDirectory(directory string) error {
if err := safeio.ValidateCanonicalPath(directory); err != nil {
return err
}
info, err := os.Lstat(directory)
if err != nil || !info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
return safeio.ErrUnsafeFile
}
resolved, err := filepath.EvalSymlinks(directory)
if err != nil || resolved != directory {
return safeio.ErrUnsafeFile
}
if runtime.GOOS != "windows" && info.Mode().Perm() != 0o700 {
return safeio.ErrUnsafeFile
}
return nil
return safeio.ValidatePrivateDirectory(directory)
}
func readPrivateFile(path string) ([]byte, error) {
if err := safeio.ValidatePrivateRegular(path); err != nil {
return nil, err
}
before, err := os.Lstat(path)
if err != nil || !before.Mode().IsRegular() || before.Mode()&os.ModeSymlink != 0 || (runtime.GOOS != "windows" && before.Mode().Perm() != 0o600) {
if err != nil {
return nil, safeio.ErrUnsafeFile
}
contents, err := safeio.ReadCanonicalRegular(path, maxYAMLBytes)
@@ -139,7 +127,10 @@ func readPrivateFile(path string) ([]byte, error) {
return nil, err
}
after, err := os.Lstat(path)
if err != nil || !after.Mode().IsRegular() || after.Mode()&os.ModeSymlink != 0 || (runtime.GOOS != "windows" && after.Mode().Perm() != 0o600) || !os.SameFile(before, after) {
if err != nil || !os.SameFile(before, after) {
return nil, safeio.ErrUnsafeFile
}
if err := safeio.ValidatePrivateRegular(path); err != nil {
return nil, safeio.ErrUnsafeFile
}
return contents, nil
@@ -149,14 +140,18 @@ func acquireLock(directory string) (*flock.Flock, error) {
path := filepath.Join(directory, lockFileName)
if _, err := os.Lstat(path); errors.Is(err, os.ErrNotExist) {
if err := safeio.WriteCanonicalNewFile(path, nil, 0o600); err != nil {
info, statErr := os.Lstat(path)
if statErr != nil || !info.Mode().IsRegular() || info.Mode()&os.ModeSymlink != 0 || (runtime.GOOS != "windows" && info.Mode().Perm() != 0o600) {
// A competing mutation may have created the lock after our Lstat. Accept only
// that exact safe/private lock; every other creation failure remains unsafe.
if validateErr := safeio.ValidatePrivateRegular(path); validateErr != nil {
return nil, safeio.ErrUnsafeFile
}
}
} else if err != nil {
return nil, safeio.ErrUnsafeFile
}
if err := safeio.ValidatePrivateRegular(path); err != nil {
return nil, err
}
lock := flock.New(path, flock.SetPermissions(0o600))
if err := lock.Lock(); err != nil {
return nil, errInvalidAuthenticationConfig