diff options
author | kali kaneko (leap communications) <kali@leap.se> | 2020-02-17 17:13:25 +0100 |
---|---|---|
committer | kali kaneko (leap communications) <kali@leap.se> | 2020-04-30 22:10:15 +0200 |
commit | 75b26ec7f4f001a13db2c8dd1fa02d7481fd2b72 (patch) | |
tree | 555134c02f5cfcf22f3060fac59bf51fe8a64f1a /pkg/helper/windows.go | |
parent | af04003d9e37ef4a08c29e967962df40d2541660 (diff) |
[feat] initial implementation of windows service
Diffstat (limited to 'pkg/helper/windows.go')
-rw-r--r-- | pkg/helper/windows.go | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/pkg/helper/windows.go b/pkg/helper/windows.go index 7e47884..fc80853 100644 --- a/pkg/helper/windows.go +++ b/pkg/helper/windows.go @@ -17,14 +17,19 @@ package helper import ( + "fmt" "log" "os" "os/exec" + "strings" "0xacab.org/leap/bitmask-vpn/pkg/config" + "golang.org/x/sys/windows" + "golang.org/x/sys/windows/svc" ) const ( + svcName = config.BinaryName + `-helper` appPath = `C:\Program Files\` + config.ApplicationName + `\` LogFolder = appPath openvpnPath = appPath + `openvpn.exe` @@ -36,10 +41,66 @@ var ( "--script-security", "1", "--block-outside-dns", } + httpBindAddr string ) +func parseCliArgs() { + isIntSess, err := svc.IsAnInteractiveSession() + if err != nil { + log.Fatalf("Failed to determine if we are running in an interactive session: %v", err) + } + if !isIntSess { + runService(svcName, false) + return + } + admin := isAdmin() + fmt.Printf("Running as admin: %v\n", admin) + if !admin { + log.Fatal("Needs to be run as administrator") + } + if len(os.Args) < 2 { + usage("ERROR: no command specified") + } + cmd := strings.ToLower(os.Args[1]) + switch cmd { + case "debug": + runService(svcName, true) + return + case "install": + // TODO get binary name + err = installService(svcName, "bitmask-helper service") + case "remove": + err = removeService(svcName) + case "start": + err = startService(svcName) + case "stop": + err = controlService(svcName, svc.Stop, svc.Stopped) + default: + usage(fmt.Sprintf("ERROR: Invalid command %s", cmd)) + } + if err != nil { + log.Fatalf("Failed to %s %s: %v", cmd, svcName, err) + } + return +} + +func usage(errmsg string) { + fmt.Fprintf(os.Stderr, + "%s\n\n"+ + "usage: %s <command>\n"+ + " where <command> is one of\n"+ + " install, remove, debug, start, stop\n", + errmsg, os.Args[0]) + os.Exit(2) +} + func daemonize() {} +// http server is called from within Execute in windows +func doHandleCommands(bindAddr string) { + httpBindAddr = bindAddr +} + func getOpenvpnPath() string { if _, err := os.Stat(openvpnPath); !os.IsNotExist(err) { return openvpnPath @@ -67,3 +128,41 @@ func firewallIsUp() bool { log.Println("IsUp firewall: do nothing, not implemented") return false } + +func isAdmin() bool { + var sid *windows.SID + + // Although this looks scary, it is directly copied from the + // official windows documentation. The Go API for this is a + // direct wrap around the official C++ API. + // See https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-checktokenmembership + err := windows.AllocateAndInitializeSid( + &windows.SECURITY_NT_AUTHORITY, + 2, + windows.SECURITY_BUILTIN_DOMAIN_RID, + windows.DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, + &sid) + if err != nil { + log.Fatalf("SID Error: %s", err) + return false + } + + // This appears to cast a null pointer so I'm not sure why this + // works, but this guy says it does and it Works for Me™: + // https://github.com/golang/go/issues/28804#issuecomment-438838144 + token := windows.Token(0) + + member, err := token.IsMember(sid) + //fmt.Println("Admin?", member) + if err != nil { + log.Fatalf("Token Membership Error: %s", err) + return false + } + return member + + // Also note that an admin is _not_ necessarily considered + // elevated. + // For elevation see https://github.com/mozey/run-as-admin + //fmt.Println("Elevated?", token.IsElevated()) +} |