diff --git a/build/logofclientinstall-win.exe b/build/logofclientinstall-win.exe index a37ada9..7f9cdef 100755 Binary files a/build/logofclientinstall-win.exe and b/build/logofclientinstall-win.exe differ diff --git a/platform_windows.go b/platform_windows.go index 5a9ed77..f68048d 100644 --- a/platform_windows.go +++ b/platform_windows.go @@ -6,19 +6,32 @@ import ( "errors" "fmt" "os" - "os/exec" "path/filepath" + "strings" "syscall" "unsafe" ) var ( - shell32 = syscall.NewLazyDLL("shell32.dll") - shellExecuteExW = shell32.NewProc("ShellExecuteExW") + 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") + waitForSingleObject = kernel32.NewProc("WaitForSingleObject") ) const ( - seeMaskNocloseprocess = 0x00000040 + tokenQuery = 0x0008 + tokenElevationClass = 20 + seeMaskNoCloseProcess = 0x00000040 + swShow = 1 + infinite = 0xFFFFFFFF + errorCancelled = 1223 ) type shellExecuteInfo struct { @@ -39,6 +52,10 @@ type shellExecuteInfo struct { hProcess uintptr } +type tokenElevationData struct { + TokenIsElevated uint32 +} + func getInstallDir() (string, error) { programFiles := os.Getenv("ProgramFiles") @@ -50,32 +67,70 @@ func getInstallDir() (string, error) { } func platformRunPrivileged(config Config, action string) error { - if isWindowsAdmin() { + if isWindowsElevated() { return runAction(config, action) } + fmt.Println() + fmt.Println("Für diese Aktion werden Administratorrechte benötigt.") + fmt.Print("Jetzt Administratorrechte anfordern? [J/n]: ") + + var answer string + if _, err := fmt.Scanln(&answer); err != nil { + // Enter ohne Eingabe = Ja + answer = "" + } + + answer = strings.ToLower(strings.TrimSpace(answer)) + + if answer != "" && answer != "j" && answer != "ja" { + fmt.Println("Abgebrochen.") + return nil + } + return runElevated(action) } -func isWindowsAdmin() bool { - // Ein einfacher und robuster Test: - // Versuch, in das Windows-Systemverzeichnis zu schreiben. - // - // Da wir dort natürlich nichts verändern wollen, verwenden wir - // stattdessen "net session". Der Befehl funktioniert nur mit - // Administratorrechten. +func isWindowsElevated() bool { + var token uintptr - cmd := exec.Command("net", "session") - cmd.Stdout = nil - cmd.Stderr = nil + ret, _, _ := openProcessToken.Call( + uintptr(^uint(0)>>1), + tokenQuery, + uintptr(unsafe.Pointer(&token)), + ) - return cmd.Run() == nil + 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 runElevated(action string) error { exe, err := os.Executable() if err != nil { - return fmt.Errorf("Pfad des Installers konnte nicht ermittelt werden: %w", err) + return fmt.Errorf( + "Pfad des Installers konnte nicht ermittelt werden: %w", + err, + ) } verb, err := syscall.UTF16PtrFromString("runas") @@ -95,34 +150,44 @@ func runElevated(action string) error { info := shellExecuteInfo{ cbSize: uint32(unsafe.Sizeof(shellExecuteInfo{})), - fMask: seeMaskNocloseprocess, + fMask: seeMaskNoCloseProcess, lpVerb: verb, lpFile: file, lpParameters: parameters, - nShow: 1, // SW_SHOWNORMAL + nShow: swShow, } - ret, _, callErr := shellExecuteExW.Call(uintptr(unsafe.Pointer(&info))) + ret, _, callErr := shellExecuteExW.Call( + uintptr(unsafe.Pointer(&info)), + ) if ret == 0 { - // ERROR_CANCELLED = 1223: - // Benutzer hat den UAC-Dialog abgebrochen. - if errno, ok := callErr.(syscall.Errno); ok && errno == 1223 { + if errno, ok := callErr.(syscall.Errno); ok && + errno == errorCancelled { fmt.Println("Administratorrechte wurden nicht erteilt.") return nil } - return fmt.Errorf("Administratorrechte konnten nicht angefordert werden: %w", callErr) + return fmt.Errorf( + "Administratorrechte konnten nicht angefordert werden: %w", + callErr, + ) } - // Auf die erhöhte Instanz warten. if info.hProcess != 0 { handle := syscall.Handle(info.hProcess) defer syscall.CloseHandle(handle) - _, err := syscall.WaitForSingleObject(handle, syscall.INFINITE) - if err != nil { - return fmt.Errorf("auf erhöhte Instanz konnte nicht gewartet werden: %w", err) + ret, _, err := waitForSingleObject.Call( + uintptr(handle), + infinite, + ) + + if ret == 0xFFFFFFFF { + return fmt.Errorf( + "auf erhöhte Instanz konnte nicht gewartet werden: %w", + err, + ) } } diff --git a/shortcut_linux.go b/shortcut_linux.go index 0cb4d14..dcbf8fc 100644 --- a/shortcut_linux.go +++ b/shortcut_linux.go @@ -11,7 +11,7 @@ import ( func platformInstallIntegration(installDir string) error { binaryPath := filepath.Join( installDir, - "Logof Client", + "Logofclient", ) linkPath := "/usr/local/bin/logofclient" @@ -56,7 +56,7 @@ func platformInstallIntegration(installDir string) error { desktopEntry := fmt.Sprintf(`[Desktop Entry] Name=Logof Client -Comment=Logof Client +Comment=Logofclient Exec=%s Terminal=false Type=Application diff --git a/shortcut_windows.go b/shortcut_windows.go index cdfa7a6..989a76f 100644 --- a/shortcut_windows.go +++ b/shortcut_windows.go @@ -26,7 +26,7 @@ func platformInstallIntegration(installDir string) error { shortcutPath := filepath.Join( startMenuDir, - "Logof Client.lnk", + "Logofclient.lnk", ) if err := os.MkdirAll(startMenuDir, 0755); err != nil { @@ -58,7 +58,7 @@ func platformRemoveIntegration() error { shortcutPath := filepath.Join( startMenuDir, - "Logof Client.lnk", + "Logofclient.lnk", ) if err := os.Remove(shortcutPath); err != nil && !os.IsNotExist(err) { @@ -117,7 +117,7 @@ func platformCreateShortcut( } // Beschreibung. - if _, err := oleutil.PutProperty(shortcut, "Description", "Logof Client"); err != nil { + if _, err := oleutil.PutProperty(shortcut, "Description", "Logofclient"); err != nil { return fmt.Errorf("Description konnte nicht gesetzt werden: %w", err) }