diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2019-08-03 09:25:35 +0200 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2019-08-05 09:50:50 +0200 |
commit | 8333c8c4550f84248e9fb2f62b2b04b58d72a580 (patch) | |
tree | 3749e8e321929423f3d05000afbe3ccee9004a06 /elevate/shellexecute.go | |
parent | ui: raise window more aggressively (diff) | |
download | wireguard-windows-8333c8c4550f84248e9fb2f62b2b04b58d72a580.tar.xz wireguard-windows-8333c8c4550f84248e9fb2f62b2b04b58d72a580.zip |
elevate: do not show UAC prompt for frictionless UX
Diffstat (limited to 'elevate/shellexecute.go')
-rw-r--r-- | elevate/shellexecute.go | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/elevate/shellexecute.go b/elevate/shellexecute.go new file mode 100644 index 00000000..c3dc84eb --- /dev/null +++ b/elevate/shellexecute.go @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package elevate + +import ( + "path/filepath" + "runtime" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" + "golang.org/x/sys/windows/registry" +) + +const ( + releaseOffset = 2 + shellExecuteOffset = 9 + + cSEE_MASK_DEFAULT = 0 +) + +func ShellExecute(program string, arguments string, directory string, show int32) (err error) { + var ( + program16 *uint16 + arguments16 *uint16 + directory16 *uint16 + ) + + if len(program) > 0 { + program16, _ = windows.UTF16PtrFromString(program) + } + if len(arguments) > 0 { + arguments16, _ = windows.UTF16PtrFromString(arguments) + } + if len(directory) > 0 { + directory16, _ = windows.UTF16PtrFromString(directory) + } + + defer func() { + if err != nil { + err = windows.ShellExecute(0, windows.StringToUTF16Ptr("runas"), program16, arguments16, directory16, show) + } + }() + + processToken, err := windows.OpenCurrentProcessToken() + if err != nil { + return + } + defer processToken.Close() + if processToken.IsElevated() { + err = windows.ERROR_SUCCESS + return + } + + key, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\UAC\\COMAutoApprovalList", registry.QUERY_VALUE) + if err == nil { + var autoApproved uint64 + autoApproved, _, err = key.GetIntegerValue("{3E5FC7F9-9A51-4367-9063-A120244FBEC7}") + key.Close() + if err != nil { + return + } + if uint32(autoApproved) == 0 { + err = windows.ERROR_ACCESS_DENIED + return + } + } + + moduleHandle, err := getModuleHandle(nil) + if err != nil { + return + } + var dataTableEntry *cLDR_DATA_TABLE_ENTRY + if ret := ldrFindEntryForAddress(moduleHandle, &dataTableEntry); ret != 0 { + err = syscall.Errno(windows.ERROR_INTERNAL_ERROR) + return + } + var windowsDirectory [windows.MAX_PATH]uint16 + if _, err = getWindowsDirectory(&windowsDirectory[0], windows.MAX_PATH); err != nil { + return + } + originalPath := dataTableEntry.FullDllName.Buffer + explorerPath := windows.StringToUTF16Ptr(filepath.Join(windows.UTF16ToString(windowsDirectory[:]), "explorer.exe")) + rtlInitUnicodeString(&dataTableEntry.FullDllName, explorerPath) + defer func() { + rtlInitUnicodeString(&dataTableEntry.FullDllName, originalPath) + runtime.KeepAlive(explorerPath) + }() + + if err = coInitializeEx(0, cCOINIT_APARTMENTTHREADED); err == nil { + defer coUninitialize() + } + + var interfacePointer **[0xffff]uintptr + if err = coGetObject( + windows.StringToUTF16Ptr("Elevation:Administrator!new:{3E5FC7F9-9A51-4367-9063-A120244FBEC7}"), + &cBIND_OPTS3{ + cbStruct: uint32(unsafe.Sizeof(cBIND_OPTS3{})), + dwClassContext: cCLSCTX_LOCAL_SERVER, + }, + &windows.GUID{0x6EDD6D74, 0xC007, 0x4E75, [8]byte{0xB7, 0x6A, 0xE5, 0x74, 0x09, 0x95, 0xE2, 0x4C}}, + &interfacePointer, + ); err != nil { + return + } + + defer syscall.Syscall((*interfacePointer)[releaseOffset], 1, uintptr(unsafe.Pointer(interfacePointer)), 0, 0) + + if ret, _, _ := syscall.Syscall6((*interfacePointer)[shellExecuteOffset], 6, + uintptr(unsafe.Pointer(interfacePointer)), + uintptr(unsafe.Pointer(program16)), + uintptr(unsafe.Pointer(arguments16)), + uintptr(unsafe.Pointer(directory16)), + cSEE_MASK_DEFAULT, + uintptr(show), + ); ret != uintptr(windows.ERROR_SUCCESS) { + err = syscall.Errno(ret) + return + } + + err = nil + return +} |