diff --git a/tools/tht/internal/safeio/private_windows.go b/tools/tht/internal/safeio/private_windows.go index ee614da7..1c89ccad 100644 --- a/tools/tht/internal/safeio/private_windows.go +++ b/tools/tht/internal/safeio/private_windows.go @@ -279,7 +279,12 @@ func newOwnerOnlySecurityDescriptor() (*ownerOnlySecurityDescriptor, error) { owner.Close() return nil, ErrUnsafeFile } - return &ownerOnlySecurityDescriptor{ownerOnlyDACL: owner, descriptor: descriptor}, nil + selfRelative, err := descriptor.ToSelfRelative() + if err != nil || selfRelative == nil || !selfRelative.IsValid() { + owner.Close() + return nil, ErrUnsafeFile + } + return &ownerOnlySecurityDescriptor{ownerOnlyDACL: owner, descriptor: selfRelative}, nil } func (descriptor *ownerOnlySecurityDescriptor) Close() { diff --git a/tools/tht/internal/safeio/private_windows_test.go b/tools/tht/internal/safeio/private_windows_test.go index e18dbc1b..e124b72c 100644 --- a/tools/tht/internal/safeio/private_windows_test.go +++ b/tools/tht/internal/safeio/private_windows_test.go @@ -40,6 +40,17 @@ func TestPrivateWindowsDACLRejectsPermissiveDirectoryAndRegularFile(t *testing.T for name, path := range map[string]string{"directory": directory, "regular file": path} { t.Run(name, func(t *testing.T) { + t.Cleanup(func() { + var restoreErr error + if name == "directory" { + restoreErr = ProtectPrivateDirectory(path) + } else { + restoreErr = ProtectPrivateRegular(path) + } + if restoreErr != nil { + t.Errorf("restore owner-only DACL: %v", restoreErr) + } + }) if err := setPermissiveDACL(path); err != nil { t.Fatal(err) } @@ -56,6 +67,38 @@ func TestPrivateWindowsDACLRejectsPermissiveDirectoryAndRegularFile(t *testing.T } } +func TestOwnerOnlyDACLCanProtectInheritedRegularFile(t *testing.T) { + directory := filepath.Join(t.TempDir(), "auth") + if err := os.Mkdir(directory, 0o700); err != nil { + t.Fatal(err) + } + path := filepath.Join(directory, "operator.env") + if err := os.WriteFile(path, []byte("private"), 0o600); err != nil { + t.Fatal(err) + } + parents, target, err := openCanonicalWindowsParent(path) + if err != nil { + t.Fatalf("openCanonicalWindowsParent() error = %T %v", err, err) + } + defer parents.Close() + handle, err := openWindowsRelativeComponent( + parents.handles[len(parents.handles)-1], + target, + false, + windows.GENERIC_READ|windows.WRITE_DAC|windows.WRITE_OWNER, + ) + if err != nil { + t.Fatalf("openWindowsRelativeComponent() error = %T %v", err, err) + } + defer windows.CloseHandle(handle) + if err := setOwnerOnlyDACL(handle); err != nil { + t.Fatalf("setOwnerOnlyDACL() inherited file error = %T %v", err, err) + } + if err := validateOwnerOnlyDACL(handle); err != nil { + t.Fatalf("validateOwnerOnlyDACL() inherited file error = %T %v", err, err) + } +} + func TestOwnerOnlyDACLAcceptsWindowsFullControlMask(t *testing.T) { const fileSpecificAll = uint32(0x1ff) effectiveFullControl := uint32(windows.STANDARD_RIGHTS_REQUIRED|windows.SYNCHRONIZE) | fileSpecificAll