golang.zx2c4.com/wireguard/windows@v0.5.4-0.20230123132234-dcc0eb72a04b/elevate/shellexecute.go (about) 1 /* SPDX-License-Identifier: MIT 2 * 3 * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved. 4 */ 5 6 package elevate 7 8 import ( 9 "path/filepath" 10 "runtime" 11 "syscall" 12 "unsafe" 13 14 "golang.org/x/sys/windows" 15 "golang.org/x/sys/windows/registry" 16 17 "golang.zx2c4.com/wireguard/windows/version" 18 ) 19 20 const ( 21 releaseOffset = 2 22 shellExecuteOffset = 9 23 24 cSEE_MASK_DEFAULT = 0 25 ) 26 27 func ShellExecute(program, arguments, directory string, show int32) (err error) { 28 var ( 29 program16 *uint16 30 arguments16 *uint16 31 directory16 *uint16 32 ) 33 34 if len(program) > 0 { 35 program16, _ = windows.UTF16PtrFromString(program) 36 } 37 if len(arguments) > 0 { 38 arguments16, _ = windows.UTF16PtrFromString(arguments) 39 } 40 if len(directory) > 0 { 41 directory16, _ = windows.UTF16PtrFromString(directory) 42 } 43 44 defer func() { 45 if err != nil && program16 != nil { 46 err = windows.ShellExecute(0, windows.StringToUTF16Ptr("runas"), program16, arguments16, directory16, show) 47 } 48 }() 49 50 if !version.IsRunningEVSigned() { 51 err = windows.ERROR_INSUFFICIENT_LOGON_INFO 52 return 53 } 54 55 var processToken windows.Token 56 err = windows.OpenProcessToken(windows.CurrentProcess(), windows.TOKEN_QUERY|windows.TOKEN_DUPLICATE, &processToken) 57 if err != nil { 58 return 59 } 60 defer processToken.Close() 61 if processToken.IsElevated() { 62 err = windows.ERROR_SUCCESS 63 return 64 } 65 if !TokenIsElevatedOrElevatable(processToken) { 66 err = windows.ERROR_ACCESS_DENIED 67 return 68 } 69 key, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", registry.QUERY_VALUE) 70 if err != nil { 71 return 72 } 73 promptBehavior, _, err := key.GetIntegerValue("ConsentPromptBehaviorAdmin") 74 key.Close() 75 if err != nil { 76 return 77 } 78 if uint32(promptBehavior) == 0 { 79 err = windows.ERROR_SUCCESS 80 return 81 } 82 if uint32(promptBehavior) != 5 { 83 err = windows.ERROR_ACCESS_DENIED 84 return 85 } 86 87 key, err = registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\UAC\\COMAutoApprovalList", registry.QUERY_VALUE) 88 if err == nil { 89 var autoApproved uint64 90 autoApproved, _, err = key.GetIntegerValue("{3E5FC7F9-9A51-4367-9063-A120244FBEC7}") 91 key.Close() 92 if err != nil { 93 return 94 } 95 if uint32(autoApproved) == 0 { 96 err = windows.ERROR_ACCESS_DENIED 97 return 98 } 99 } 100 dataTableEntry, err := findCurrentDataTableEntry() 101 if err != nil { 102 return 103 } 104 windowsDirectory, err := windows.GetSystemWindowsDirectory() 105 if err != nil { 106 return 107 } 108 originalPath := dataTableEntry.FullDllName.Buffer 109 explorerPath := windows.StringToUTF16Ptr(filepath.Join(windowsDirectory, "explorer.exe")) 110 windows.RtlInitUnicodeString(&dataTableEntry.FullDllName, explorerPath) 111 defer func() { 112 windows.RtlInitUnicodeString(&dataTableEntry.FullDllName, originalPath) 113 runtime.KeepAlive(explorerPath) 114 }() 115 116 if err = windows.CoInitializeEx(0, windows.COINIT_APARTMENTTHREADED); err == nil { 117 defer windows.CoUninitialize() 118 } 119 120 var interfacePointer **[0xffff]uintptr 121 if err = windows.CoGetObject( 122 windows.StringToUTF16Ptr("Elevation:Administrator!new:{3E5FC7F9-9A51-4367-9063-A120244FBEC7}"), 123 &windows.BIND_OPTS3{ 124 CbStruct: uint32(unsafe.Sizeof(windows.BIND_OPTS3{})), 125 ClassContext: windows.CLSCTX_LOCAL_SERVER, 126 }, 127 &windows.GUID{0x6EDD6D74, 0xC007, 0x4E75, [8]byte{0xB7, 0x6A, 0xE5, 0x74, 0x09, 0x95, 0xE2, 0x4C}}, 128 (**uintptr)(unsafe.Pointer(&interfacePointer)), 129 ); err != nil { 130 return 131 } 132 133 defer syscall.SyscallN((*interfacePointer)[releaseOffset], uintptr(unsafe.Pointer(interfacePointer))) 134 135 if program16 == nil { 136 return 137 } 138 139 if ret, _, _ := syscall.SyscallN((*interfacePointer)[shellExecuteOffset], 140 uintptr(unsafe.Pointer(interfacePointer)), 141 uintptr(unsafe.Pointer(program16)), 142 uintptr(unsafe.Pointer(arguments16)), 143 uintptr(unsafe.Pointer(directory16)), 144 cSEE_MASK_DEFAULT, 145 uintptr(show), 146 ); ret != uintptr(windows.ERROR_SUCCESS) { 147 err = syscall.Errno(ret) 148 return 149 } 150 151 err = nil 152 return 153 }