golang.zx2c4.com/wireguard/windows@v0.5.4-0.20230123132234-dcc0eb72a04b/conf/path_windows.go (about)

     1  /* SPDX-License-Identifier: MIT
     2   *
     3   * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
     4   */
     5  
     6  package conf
     7  
     8  import (
     9  	"errors"
    10  	"os"
    11  	"path/filepath"
    12  	"strings"
    13  	"unsafe"
    14  
    15  	"golang.org/x/sys/windows"
    16  )
    17  
    18  var (
    19  	cachedConfigFileDir string
    20  	cachedRootDir       string
    21  )
    22  
    23  func tunnelConfigurationsDirectory() (string, error) {
    24  	if cachedConfigFileDir != "" {
    25  		return cachedConfigFileDir, nil
    26  	}
    27  	root, err := RootDirectory(true)
    28  	if err != nil {
    29  		return "", err
    30  	}
    31  	c := filepath.Join(root, "Configurations")
    32  	err = os.Mkdir(c, os.ModeDir|0o700)
    33  	if err != nil && !os.IsExist(err) {
    34  		return "", err
    35  	}
    36  	cachedConfigFileDir = c
    37  	return cachedConfigFileDir, nil
    38  }
    39  
    40  // PresetRootDirectory causes RootDirectory() to not try any automatic deduction, and instead
    41  // uses what's passed to it. This isn't used by wireguard-windows, but is useful for external
    42  // consumers of our libraries who might want to do strange things.
    43  func PresetRootDirectory(root string) {
    44  	cachedRootDir = root
    45  }
    46  
    47  func RootDirectory(create bool) (string, error) {
    48  	if cachedRootDir != "" {
    49  		return cachedRootDir, nil
    50  	}
    51  	root, err := windows.KnownFolderPath(windows.FOLDERID_ProgramFiles, windows.KF_FLAG_DEFAULT)
    52  	if err != nil {
    53  		return "", err
    54  	}
    55  	root = filepath.Join(root, "WireGuard")
    56  	if !create {
    57  		return filepath.Join(root, "Data"), nil
    58  	}
    59  	root16, err := windows.UTF16PtrFromString(root)
    60  	if err != nil {
    61  		return "", err
    62  	}
    63  
    64  	// The root directory inherits its ACL from Program Files; we don't want to mess with that
    65  	err = windows.CreateDirectory(root16, nil)
    66  	if err != nil && err != windows.ERROR_ALREADY_EXISTS {
    67  		return "", err
    68  	}
    69  
    70  	dataDirectorySd, err := windows.SecurityDescriptorFromString("O:SYG:SYD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)")
    71  	if err != nil {
    72  		return "", err
    73  	}
    74  	dataDirectorySa := &windows.SecurityAttributes{
    75  		Length:             uint32(unsafe.Sizeof(windows.SecurityAttributes{})),
    76  		SecurityDescriptor: dataDirectorySd,
    77  	}
    78  
    79  	data := filepath.Join(root, "Data")
    80  	data16, err := windows.UTF16PtrFromString(data)
    81  	if err != nil {
    82  		return "", err
    83  	}
    84  	var dataHandle windows.Handle
    85  	for {
    86  		err = windows.CreateDirectory(data16, dataDirectorySa)
    87  		if err != nil && err != windows.ERROR_ALREADY_EXISTS {
    88  			return "", err
    89  		}
    90  		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|windows.FILE_ATTRIBUTE_DIRECTORY, 0)
    91  		if err != nil && err != windows.ERROR_FILE_NOT_FOUND {
    92  			return "", err
    93  		}
    94  		if err == nil {
    95  			break
    96  		}
    97  	}
    98  	defer windows.CloseHandle(dataHandle)
    99  	var fileInfo windows.ByHandleFileInformation
   100  	err = windows.GetFileInformationByHandle(dataHandle, &fileInfo)
   101  	if err != nil {
   102  		return "", err
   103  	}
   104  	if fileInfo.FileAttributes&windows.FILE_ATTRIBUTE_DIRECTORY == 0 {
   105  		return "", errors.New("Data directory is actually a file")
   106  	}
   107  	if fileInfo.FileAttributes&windows.FILE_ATTRIBUTE_REPARSE_POINT != 0 {
   108  		return "", errors.New("Data directory is reparse point")
   109  	}
   110  	buf := make([]uint16, windows.MAX_PATH+4)
   111  	for {
   112  		bufLen, err := windows.GetFinalPathNameByHandle(dataHandle, &buf[0], uint32(len(buf)), 0)
   113  		if err != nil {
   114  			return "", err
   115  		}
   116  		if bufLen < uint32(len(buf)) {
   117  			break
   118  		}
   119  		buf = make([]uint16, bufLen)
   120  	}
   121  	if !strings.EqualFold(`\\?\`+data, windows.UTF16ToString(buf[:])) {
   122  		return "", errors.New("Data directory jumped to unexpected location")
   123  	}
   124  	err = windows.SetKernelObjectSecurity(dataHandle, windows.DACL_SECURITY_INFORMATION|windows.GROUP_SECURITY_INFORMATION|windows.OWNER_SECURITY_INFORMATION|windows.PROTECTED_DACL_SECURITY_INFORMATION, dataDirectorySd)
   125  	if err != nil {
   126  		return "", err
   127  	}
   128  	cachedRootDir = data
   129  	return cachedRootDir, nil
   130  }
   131  
   132  func LogFile(createRoot bool) (string, error) {
   133  	root, err := RootDirectory(createRoot)
   134  	if err != nil {
   135  		return "", err
   136  	}
   137  	return filepath.Join(root, "log.bin"), nil
   138  }