186 lines
3.6 KiB
Go
186 lines
3.6 KiB
Go
//go:build windows
|
|
|
|
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
var (
|
|
shell32 = syscall.NewLazyDLL("shell32.dll")
|
|
shellExecuteExW = shell32.NewProc("ShellExecuteExW")
|
|
|
|
advapi32 = syscall.NewLazyDLL("advapi32.dll")
|
|
openProcessToken = advapi32.NewProc("OpenProcessToken")
|
|
getTokenInformation = advapi32.NewProc("GetTokenInformation")
|
|
|
|
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
|
closeHandle = kernel32.NewProc("CloseHandle")
|
|
)
|
|
|
|
const (
|
|
tokenQuery = 0x0008
|
|
tokenElevationClass = 20
|
|
|
|
errorCancelled = 1223
|
|
|
|
swShow = 1
|
|
)
|
|
|
|
type shellExecuteInfo struct {
|
|
cbSize uint32
|
|
fMask uint32
|
|
hwnd uintptr
|
|
lpVerb *uint16
|
|
lpFile *uint16
|
|
lpParameters *uint16
|
|
lpDirectory *uint16
|
|
nShow int32
|
|
hInstApp uintptr
|
|
lpIDList uintptr
|
|
lpClass *uint16
|
|
hkeyClass uintptr
|
|
dwHotKey uint32
|
|
hIcon uintptr
|
|
hProcess uintptr
|
|
}
|
|
|
|
type tokenElevationData struct {
|
|
TokenIsElevated uint32
|
|
}
|
|
|
|
func platformEnsureElevated() error {
|
|
// Die erhöhte Instanz wurde bereits gestartet.
|
|
// Sie darf sich nicht noch einmal selbst per runas starten.
|
|
if hasElevatedFlag() {
|
|
return nil
|
|
}
|
|
|
|
if isWindowsElevated() {
|
|
return nil
|
|
}
|
|
|
|
return restartElevated()
|
|
}
|
|
|
|
func hasElevatedFlag() bool {
|
|
for _, arg := range os.Args[1:] {
|
|
if arg == "--elevated" {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func restartElevated() error {
|
|
exe, err := os.Executable()
|
|
if err != nil {
|
|
return fmt.Errorf(
|
|
"Pfad des Installers konnte nicht ermittelt werden: %w",
|
|
err,
|
|
)
|
|
}
|
|
|
|
verb, err := syscall.UTF16PtrFromString("runas")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
file, err := syscall.UTF16PtrFromString(exe)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Die erhöhte Instanz bekommt nur das interne Flag.
|
|
parameters, err := syscall.UTF16PtrFromString("--elevated")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
info := shellExecuteInfo{
|
|
cbSize: uint32(unsafe.Sizeof(shellExecuteInfo{})),
|
|
lpVerb: verb,
|
|
lpFile: file,
|
|
lpParameters: parameters,
|
|
nShow: swShow,
|
|
}
|
|
|
|
ret, _, callErr := shellExecuteExW.Call(
|
|
uintptr(unsafe.Pointer(&info)),
|
|
)
|
|
|
|
if ret == 0 {
|
|
if errno, ok := callErr.(syscall.Errno); ok &&
|
|
errno == errorCancelled {
|
|
fmt.Println("Administratorrechte wurden nicht erteilt.")
|
|
return errors.New("Administratorrechte erforderlich")
|
|
}
|
|
|
|
return fmt.Errorf(
|
|
"Administratorrechte konnten nicht angefordert werden: %w",
|
|
callErr,
|
|
)
|
|
}
|
|
|
|
// WICHTIG:
|
|
// Die ursprüngliche Instanz beendet sich sofort.
|
|
// Nur die erhöhte Instanz führt danach main() weiter aus.
|
|
os.Exit(0)
|
|
|
|
return nil
|
|
}
|
|
|
|
func isWindowsElevated() bool {
|
|
var token uintptr
|
|
|
|
// GetCurrentProcess() = -1
|
|
ret, _, _ := openProcessToken.Call(
|
|
uintptr(^uint(0)),
|
|
tokenQuery,
|
|
uintptr(unsafe.Pointer(&token)),
|
|
)
|
|
|
|
if ret == 0 || token == 0 {
|
|
return false
|
|
}
|
|
|
|
defer closeHandle.Call(token)
|
|
|
|
var elevation tokenElevationData
|
|
var returned uint32
|
|
|
|
ret, _, _ = getTokenInformation.Call(
|
|
token,
|
|
tokenElevationClass,
|
|
uintptr(unsafe.Pointer(&elevation)),
|
|
uintptr(unsafe.Sizeof(elevation)),
|
|
uintptr(unsafe.Pointer(&returned)),
|
|
)
|
|
|
|
if ret == 0 {
|
|
return false
|
|
}
|
|
|
|
return elevation.TokenIsElevated != 0
|
|
}
|
|
|
|
func platformRunPrivileged(config Config, action string) error {
|
|
// Die komplette Windows-Anwendung läuft bereits erhöht.
|
|
return runAction(config, action)
|
|
}
|
|
|
|
func getInstallDir() (string, error) {
|
|
programFiles := os.Getenv("ProgramFiles")
|
|
|
|
if programFiles == "" {
|
|
return "", errors.New("ProgramFiles ist nicht gesetzt")
|
|
}
|
|
|
|
return filepath.Join(programFiles, "logofclient"), nil
|
|
} |