Files
logofclient-installer/platform_windows.go
T

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
}