Compare commits

..
3 Commits
6 changed files with 122 additions and 53 deletions
Binary file not shown.
+9
View File
@@ -47,8 +47,17 @@ func main() {
Repo: envOrDefault("GITEA_REPO", defaultRepo), Repo: envOrDefault("GITEA_REPO", defaultRepo),
} }
if err := platformEnsureElevated(); err != nil {
printError(err)
os.Exit(1)
}
args := os.Args[1:] args := os.Args[1:]
if len(args) > 0 && args[0] == "--elevated" {
args = args[1:]
}
if len(args) == 0 { if len(args) == 0 {
runTUI(config) runTUI(config)
return return
+4
View File
@@ -13,6 +13,10 @@ func getInstallDir() (string, error) {
return "/opt/logofclient", nil return "/opt/logofclient", nil
} }
func platformEnsureElevated() error {
return nil
}
func platformRunPrivileged(config Config, action string) error { func platformRunPrivileged(config Config, action string) error {
// Bereits als root gestartet. // Bereits als root gestartet.
if os.Geteuid() == 0 { if os.Geteuid() == 0 {
+102 -46
View File
@@ -6,7 +6,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"syscall" "syscall"
"unsafe" "unsafe"
@@ -15,10 +14,22 @@ import (
var ( var (
shell32 = syscall.NewLazyDLL("shell32.dll") shell32 = syscall.NewLazyDLL("shell32.dll")
shellExecuteExW = shell32.NewProc("ShellExecuteExW") 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 ( const (
seeMaskNocloseprocess = 0x00000040 tokenQuery = 0x0008
tokenElevationClass = 20
errorCancelled = 1223
swShow = 1
) )
type shellExecuteInfo struct { type shellExecuteInfo struct {
@@ -39,43 +50,41 @@ type shellExecuteInfo struct {
hProcess uintptr hProcess uintptr
} }
func getInstallDir() (string, error) { type tokenElevationData struct {
programFiles := os.Getenv("ProgramFiles") TokenIsElevated uint32
}
if programFiles == "" { func platformEnsureElevated() error {
return "", errors.New("ProgramFiles ist nicht gesetzt") // Die erhöhte Instanz wurde bereits gestartet.
// Sie darf sich nicht noch einmal selbst per runas starten.
if hasElevatedFlag() {
return nil
} }
return filepath.Join(programFiles, "logofclient"), nil if isWindowsElevated() {
} return nil
func platformRunPrivileged(config Config, action string) error {
if isWindowsAdmin() {
return runAction(config, action)
} }
return runElevated(action) return restartElevated()
} }
func isWindowsAdmin() bool { func hasElevatedFlag() bool {
// Ein einfacher und robuster Test: for _, arg := range os.Args[1:] {
// Versuch, in das Windows-Systemverzeichnis zu schreiben. if arg == "--elevated" {
// return true
// Da wir dort natürlich nichts verändern wollen, verwenden wir }
// stattdessen "net session". Der Befehl funktioniert nur mit }
// Administratorrechten.
cmd := exec.Command("net", "session") return false
cmd.Stdout = nil
cmd.Stderr = nil
return cmd.Run() == nil
} }
func runElevated(action string) error { func restartElevated() error {
exe, err := os.Executable() exe, err := os.Executable()
if err != nil { 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") verb, err := syscall.UTF16PtrFromString("runas")
@@ -88,43 +97,90 @@ func runElevated(action string) error {
return err return err
} }
parameters, err := syscall.UTF16PtrFromString(action) // Die erhöhte Instanz bekommt nur das interne Flag.
parameters, err := syscall.UTF16PtrFromString("--elevated")
if err != nil { if err != nil {
return err return err
} }
info := shellExecuteInfo{ info := shellExecuteInfo{
cbSize: uint32(unsafe.Sizeof(shellExecuteInfo{})), cbSize: uint32(unsafe.Sizeof(shellExecuteInfo{})),
fMask: seeMaskNocloseprocess,
lpVerb: verb, lpVerb: verb,
lpFile: file, lpFile: file,
lpParameters: parameters, 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 { if ret == 0 {
// ERROR_CANCELLED = 1223: if errno, ok := callErr.(syscall.Errno); ok &&
// Benutzer hat den UAC-Dialog abgebrochen. errno == errorCancelled {
if errno, ok := callErr.(syscall.Errno); ok && errno == 1223 {
fmt.Println("Administratorrechte wurden nicht erteilt.") fmt.Println("Administratorrechte wurden nicht erteilt.")
return nil return errors.New("Administratorrechte erforderlich")
} }
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. // WICHTIG:
if info.hProcess != 0 { // Die ursprüngliche Instanz beendet sich sofort.
handle := syscall.Handle(info.hProcess) // Nur die erhöhte Instanz führt danach main() weiter aus.
defer syscall.CloseHandle(handle) os.Exit(0)
_, err := syscall.WaitForSingleObject(handle, syscall.INFINITE)
if err != nil {
return fmt.Errorf("auf erhöhte Instanz konnte nicht gewartet werden: %w", err)
}
}
return nil 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
}
+2 -2
View File
@@ -11,7 +11,7 @@ import (
func platformInstallIntegration(installDir string) error { func platformInstallIntegration(installDir string) error {
binaryPath := filepath.Join( binaryPath := filepath.Join(
installDir, installDir,
"Logof Client", "Logofclient",
) )
linkPath := "/usr/local/bin/logofclient" linkPath := "/usr/local/bin/logofclient"
@@ -56,7 +56,7 @@ func platformInstallIntegration(installDir string) error {
desktopEntry := fmt.Sprintf(`[Desktop Entry] desktopEntry := fmt.Sprintf(`[Desktop Entry]
Name=Logof Client Name=Logof Client
Comment=Logof Client Comment=Logofclient
Exec=%s Exec=%s
Terminal=false Terminal=false
Type=Application Type=Application
+3 -3
View File
@@ -26,7 +26,7 @@ func platformInstallIntegration(installDir string) error {
shortcutPath := filepath.Join( shortcutPath := filepath.Join(
startMenuDir, startMenuDir,
"Logof Client.lnk", "Logofclient.lnk",
) )
if err := os.MkdirAll(startMenuDir, 0755); err != nil { if err := os.MkdirAll(startMenuDir, 0755); err != nil {
@@ -58,7 +58,7 @@ func platformRemoveIntegration() error {
shortcutPath := filepath.Join( shortcutPath := filepath.Join(
startMenuDir, startMenuDir,
"Logof Client.lnk", "Logofclient.lnk",
) )
if err := os.Remove(shortcutPath); err != nil && !os.IsNotExist(err) { if err := os.Remove(shortcutPath); err != nil && !os.IsNotExist(err) {
@@ -117,7 +117,7 @@ func platformCreateShortcut(
} }
// Beschreibung. // 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) return fmt.Errorf("Description konnte nicht gesetzt werden: %w", err)
} }