aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/conf
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2020-11-10 17:17:34 +0100
committerJason A. Donenfeld <Jason@zx2c4.com>2020-11-16 19:03:37 +0100
commita07fb45f3672167dda329f64618f93ad69851a35 (patch)
tree2b60fae6cd8d7715829e57da758056589e591e9d /conf
parentfetcher: introduce downloader utility (diff)
downloadwireguard-windows-a07fb45f3672167dda329f64618f93ad69851a35.tar.xz
wireguard-windows-a07fb45f3672167dda329f64618f93ad69851a35.zip
conf: move configuration to C:\Program Files\WireGuard\Data
It doesn't get wiped out on Windows upgrades. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'conf')
-rw-r--r--conf/migration_windows.go62
-rw-r--r--conf/path_windows.go135
2 files changed, 163 insertions, 34 deletions
diff --git a/conf/migration_windows.go b/conf/migration_windows.go
index 72b298b6..9e5e1eb9 100644
--- a/conf/migration_windows.go
+++ b/conf/migration_windows.go
@@ -6,46 +6,58 @@
package conf
import (
+ "io/ioutil"
"log"
+ "os"
"path/filepath"
- "strings"
"golang.org/x/sys/windows"
)
-func maybeMigrate(c string) {
+func maybeMigrateConfiguration(c string) {
if disableAutoMigration {
return
}
-
- vol := filepath.VolumeName(c)
- withoutVol := strings.TrimPrefix(c, vol)
- oldRoot := filepath.Join(vol, "\\windows.old")
- oldC := filepath.Join(oldRoot, withoutVol)
-
- sd, err := windows.GetNamedSecurityInfo(oldRoot, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION)
- if err == windows.ERROR_PATH_NOT_FOUND || err == windows.ERROR_FILE_NOT_FOUND {
- return
- }
+ oldRoot, err := windows.KnownFolderPath(windows.FOLDERID_LocalAppData, windows.KF_FLAG_DEFAULT)
if err != nil {
- log.Printf("Not migrating configuration from ‘%s’ due to GetNamedSecurityInfo error: %v", oldRoot, err)
return
}
- owner, defaulted, err := sd.Owner()
+ oldC := filepath.Join(oldRoot, "WireGuard", "Configurations")
+ files, err := ioutil.ReadDir(oldC)
if err != nil {
- log.Printf("Not migrating configuration from ‘%s’ due to GetSecurityDescriptorOwner error: %v", oldRoot, err)
- return
- }
- if defaulted || (!owner.IsWellKnown(windows.WinLocalSystemSid) && !owner.IsWellKnown(windows.WinBuiltinAdministratorsSid)) {
- log.Printf("Not migrating configuration from ‘%s’, as it is not explicitly owned by SYSTEM or Built-in Administrators, but rather ‘%v’", oldRoot, owner)
return
}
- err = windows.MoveFileEx(windows.StringToUTF16Ptr(oldC), windows.StringToUTF16Ptr(c), windows.MOVEFILE_COPY_ALLOWED)
- if err != nil {
- if err != windows.ERROR_FILE_NOT_FOUND && err != windows.ERROR_ALREADY_EXISTS {
- log.Printf("Not migrating configuration from ‘%s’ due to error when moving files: %v", oldRoot, err)
+ for i := range files {
+ if files[i].IsDir() {
+ continue
}
- return
+ fileName := files[i].Name()
+ newPath := filepath.Join(c, fileName)
+ newFile, err := os.OpenFile(newPath, os.O_EXCL|os.O_CREATE|os.O_WRONLY, 0600)
+ if err != nil {
+ continue
+ }
+ oldPath := filepath.Join(oldC, fileName)
+ oldConfig, err := ioutil.ReadFile(oldPath)
+ if err != nil {
+ newFile.Close()
+ os.Remove(newPath)
+ continue
+ }
+ _, err = newFile.Write(oldConfig)
+ if err != nil {
+ newFile.Close()
+ os.Remove(newPath)
+ continue
+ }
+ newFile.Close()
+ os.Remove(oldPath)
+ log.Printf("Migrated configuration from ‘%s’ to ‘%s’", oldPath, newPath)
+ }
+ if os.Remove(oldC) == nil {
+ oldLog := filepath.Join(oldRoot, "WireGuard", "log.bin")
+ oldRoot := filepath.Join(oldRoot, "WireGuard")
+ os.Remove(oldLog)
+ os.Remove(oldRoot)
}
- log.Printf("Migrated configuration from ‘%s’", oldRoot)
}
diff --git a/conf/path_windows.go b/conf/path_windows.go
index a53968c5..0d7e09b3 100644
--- a/conf/path_windows.go
+++ b/conf/path_windows.go
@@ -6,10 +6,17 @@
package conf
import (
+ "debug/pe"
+ "errors"
+ "fmt"
"os"
"path/filepath"
+ "strings"
+ "syscall"
+ "unsafe"
"golang.org/x/sys/windows"
+ "golang.org/x/sys/windows/registry"
)
var cachedConfigFileDir string
@@ -20,16 +27,16 @@ func tunnelConfigurationsDirectory() (string, error) {
if cachedConfigFileDir != "" {
return cachedConfigFileDir, nil
}
- root, err := RootDirectory()
+ root, err := RootDirectory(true)
if err != nil {
return "", err
}
c := filepath.Join(root, "Configurations")
- maybeMigrate(c)
- err = os.MkdirAll(c, os.ModeDir|0700)
- if err != nil {
+ err = os.Mkdir(c, os.ModeDir|0700)
+ if err != nil && !os.IsExist(err) {
return "", err
}
+ maybeMigrateConfiguration(c)
cachedConfigFileDir = c
return cachedConfigFileDir, nil
}
@@ -42,19 +49,129 @@ func PresetRootDirectory(root string) {
disableAutoMigration = true
}
-func RootDirectory() (string, error) {
+// TODO: replace with x/sys/windows upstreamed function
+func setKernelObjectSecurity(handle windows.Handle, securityInformation windows.SECURITY_INFORMATION, securityDescriptor *windows.SECURITY_DESCRIPTOR) (err error) {
+ r1, _, e1 := syscall.Syscall(windows.NewLazySystemDLL("advapi32.dll").NewProc("SetKernelObjectSecurity").Addr(), 3, uintptr(handle), uintptr(securityInformation), uintptr(unsafe.Pointer(securityDescriptor)))
+ if r1 == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+func getFinalPathNameByHandle(file windows.Handle, filePath *uint16, filePathSize uint32, flags uint32) (n uint32, err error) {
+ r0, _, e1 := syscall.Syscall6(windows.NewLazySystemDLL("kernel32.dll").NewProc("GetFinalPathNameByHandleW").Addr(), 4, uintptr(file), uintptr(unsafe.Pointer(filePath)), uintptr(filePathSize), uintptr(flags), 0, 0)
+ n = uint32(r0)
+ if n == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func RootDirectory(create bool) (string, error) {
if cachedRootDir != "" {
return cachedRootDir, nil
}
- root, err := windows.KnownFolderPath(windows.FOLDERID_LocalAppData, windows.KF_FLAG_CREATE)
+ var isWow bool
+ var processMachine, nativeMachine uint16
+ err := windows.IsWow64Process2(windows.CurrentProcess(), &processMachine, &nativeMachine)
+ if err == nil {
+ isWow = processMachine != pe.IMAGE_FILE_MACHINE_UNKNOWN
+ } else {
+ if !errors.Is(err, windows.ERROR_PROC_NOT_FOUND) {
+ return "", err
+ }
+ err = windows.IsWow64Process(windows.CurrentProcess(), &isWow)
+ if err != nil {
+ return "", err
+ }
+ }
+ var root string
+ if !isWow {
+ root, err = windows.KnownFolderPath(windows.FOLDERID_ProgramFiles, windows.KF_FLAG_CREATE)
+ if err != nil {
+ return "", err
+ }
+ } else {
+ key, err := registry.OpenKey(windows.HKEY_LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows\CurrentVersion`, registry.READ|registry.WOW64_64KEY)
+ if err != nil {
+ return "", err
+ }
+ var typ uint32
+ root, typ, err = key.GetStringValue("ProgramFilesDir")
+ key.Close()
+ if err != nil {
+ return "", err
+ }
+ if typ != registry.SZ {
+ return "", registry.ErrUnexpectedType
+ }
+ }
+ root = filepath.Join(root, "WireGuard")
+ if !create {
+ return filepath.Join(root, "Data"), nil
+ }
+ root16, err := windows.UTF16PtrFromString(root)
+ if err != nil {
+ return "", err
+ }
+
+ // The root directory inherits its ACL from Program Files; we don't want to mess with that
+ err = windows.CreateDirectory(root16, nil)
+ if err != nil && err != windows.ERROR_ALREADY_EXISTS {
+ return "", err
+ }
+
+ dataDirectorySd, err := windows.SecurityDescriptorFromString("O:SYG:SYD:PAI(A;OICI;FA;;;SY)(A;OICI;FR;;;BA)")
if err != nil {
return "", err
}
- c := filepath.Join(root, "WireGuard")
- err = os.MkdirAll(c, os.ModeDir|0700)
+ dataDirectorySa := &windows.SecurityAttributes{
+ Length: uint32(unsafe.Sizeof(windows.SecurityAttributes{})),
+ SecurityDescriptor: dataDirectorySd,
+ }
+
+ data := filepath.Join(root, "Data")
+ data16, err := windows.UTF16PtrFromString(data)
+ if err != nil {
+ return "", err
+ }
+ var dataHandle windows.Handle
+ for {
+ err = windows.CreateDirectory(data16, dataDirectorySa)
+ if err != nil && err != windows.ERROR_ALREADY_EXISTS {
+ return "", err
+ }
+ dataHandle, err = windows.CreateFile(data16, windows.READ_CONTROL|windows.WRITE_OWNER|windows.WRITE_DAC, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE, nil, windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS|windows.FILE_FLAG_OPEN_REPARSE_POINT, 0)
+ if err != nil && err != windows.ERROR_FILE_NOT_FOUND {
+ return "", err
+ }
+ if err == nil {
+ break
+ }
+ }
+ defer windows.CloseHandle(dataHandle)
+ var fileInfo windows.ByHandleFileInformation
+ err = windows.GetFileInformationByHandle(dataHandle, &fileInfo)
+ if err != nil {
+ return "", err
+ }
+ if fileInfo.FileAttributes&windows.FILE_ATTRIBUTE_DIRECTORY == 0 {
+ return "", errors.New("Data directory is actually a file")
+ }
+ if fileInfo.FileAttributes&windows.FILE_ATTRIBUTE_REPARSE_POINT != 0 {
+ return "", errors.New("Data directory is reparse point")
+ }
+ var buf [windows.MAX_PATH * 4]uint16
+ _, err = getFinalPathNameByHandle(dataHandle, &buf[0], uint32(len(buf)), 0)
+ if err != nil {
+ return "", err
+ }
+ if !strings.EqualFold(`\\?\`+data, windows.UTF16ToString(buf[:])) {
+ return "", fmt.Errorf("Data directory jumped to unexpected location: got %q; want %q", windows.UTF16ToString(buf[:]), `\\?\`+data)
+ }
+ err = setKernelObjectSecurity(dataHandle, windows.DACL_SECURITY_INFORMATION|windows.GROUP_SECURITY_INFORMATION|windows.OWNER_SECURITY_INFORMATION|windows.PROTECTED_DACL_SECURITY_INFORMATION, dataDirectorySd)
if err != nil {
return "", err
}
- cachedRootDir = c
+ cachedRootDir = data
return cachedRootDir, nil
}