feat(auth): add safe Argon2id local user registry
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
//go:build windows
|
||||
|
||||
package safeio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const windowsReplaceMoveFlags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH
|
||||
|
||||
func replaceCanonicalRegular(path string, contents []byte) error {
|
||||
directory := filepath.Dir(path)
|
||||
if err := requireCanonicalDirectory(directory); err != nil || !safeExistingRegular(path) {
|
||||
return ErrUnsafeFile
|
||||
}
|
||||
temporary, err := writePrivateTemporary(directory, contents)
|
||||
if err != nil {
|
||||
return ErrUnsafeFile
|
||||
}
|
||||
defer func() { _ = os.Remove(temporary) }()
|
||||
if !safeExistingRegular(path) {
|
||||
return ErrUnsafeFile
|
||||
}
|
||||
from, err := windows.UTF16PtrFromString(temporary)
|
||||
if err != nil {
|
||||
return ErrUnsafeFile
|
||||
}
|
||||
to, err := windows.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return ErrUnsafeFile
|
||||
}
|
||||
if err := windows.MoveFileEx(from, to, windowsReplaceMoveFlags); err != nil {
|
||||
return ErrUnsafeFile
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func safeExistingRegular(path string) bool {
|
||||
handle, err := openWindowsComponent(path, false)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return windows.CloseHandle(handle) == nil
|
||||
}
|
||||
|
||||
func writePrivateTemporary(directory string, contents []byte) (string, error) {
|
||||
for attempt := 0; attempt < 16; attempt++ {
|
||||
name, err := randomTemporaryName()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
path := filepath.Join(directory, name)
|
||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o600)
|
||||
if errors.Is(err, os.ErrExist) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := file.Chmod(0o600); err == nil {
|
||||
var written int
|
||||
written, err = file.Write(contents)
|
||||
if err == nil && written != len(contents) {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
err = file.Sync()
|
||||
}
|
||||
closeErr := file.Close()
|
||||
if err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
if err != nil {
|
||||
_ = os.Remove(path)
|
||||
return "", err
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
return "", ErrUnsafeFile
|
||||
}
|
||||
Reference in New Issue
Block a user