aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/service
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2019-02-25 18:47:03 +0100
committerJason A. Donenfeld <Jason@zx2c4.com>2019-02-28 08:05:02 +0100
commitab3263502a3eb2ba008e7aab61709cb11c8aac1e (patch)
tree2184c0d2782f81ace9b2e78e4bbc554a534fc393 /service
parentconf: introduce configuration management (diff)
downloadwireguard-windows-ab3263502a3eb2ba008e7aab61709cb11c8aac1e.tar.xz
wireguard-windows-ab3263502a3eb2ba008e7aab61709cb11c8aac1e.zip
service: introduce base of services
Diffstat (limited to 'service')
-rw-r--r--service/errors.go19
-rw-r--r--service/install.go196
-rw-r--r--service/mksyscall.go8
-rw-r--r--service/service_manager.go294
-rw-r--r--service/service_tunnel.go174
-rw-r--r--service/zsyscall_windows.go116
6 files changed, 807 insertions, 0 deletions
diff --git a/service/errors.go b/service/errors.go
new file mode 100644
index 00000000..b6566d00
--- /dev/null
+++ b/service/errors.go
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package service
+
+const (
+ ERROR_LOG_CONTAINER_OPEN_FAILED uint32 = 0x000019F1
+ ERROR_INVALID_PARAMETER uint32 = 0x00000057
+ ERROR_OPEN_FAILED uint32 = 0x0000006E
+ ERROR_ADAP_HDW_ERR uint32 = 0x00000039
+ ERROR_PIPE_LISTENING uint32 = 0x00000218
+ ERROR_BAD_LOGON_SESSION_STATE uint32 = 0x00000555
+ ERROR_BAD_PATHNAME uint32 = 0x000000A1
+ ERROR_FILE_NOT_FOUND uint32 = 0x00000002
+ ERROR_SERVER_SID_MISMATCH uint32 = 0x00000274
+ ERROR_NETWORK_BUSY uint32 = 0x00000036
+)
diff --git a/service/install.go b/service/install.go
new file mode 100644
index 00000000..32131f94
--- /dev/null
+++ b/service/install.go
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package service
+
+import (
+ "errors"
+ "golang.org/x/sys/windows"
+ "golang.org/x/sys/windows/svc"
+ svcdbg "golang.org/x/sys/windows/svc/debug"
+ "golang.org/x/sys/windows/svc/mgr"
+ "golang.zx2c4.com/wireguard/windows/conf"
+ "os"
+ "time"
+)
+
+var cachedServiceManager *mgr.Mgr
+
+func serviceManager() (*mgr.Mgr, error) {
+ if cachedServiceManager != nil {
+ return cachedServiceManager, nil
+ }
+ m, err := mgr.Connect()
+ if err != nil {
+ return nil, err
+ }
+ cachedServiceManager = m
+ return cachedServiceManager, nil
+}
+
+func InstallManager() error {
+ m, err := serviceManager()
+ if err != nil {
+ return err
+ }
+ path, err := os.Executable()
+ if err != nil {
+ return nil
+ }
+
+ //TODO: Do we want to bail if executable isn't being run from the right location?
+
+ serviceName := "WireGuard Manager"
+ service, err := m.OpenService(serviceName)
+ if err == nil {
+ status, err := service.Query()
+ if err != nil {
+ service.Close()
+ return err
+ }
+ if status.State != svc.Stopped {
+ service.Close()
+ return errors.New("Manager already installed and running")
+ }
+ err = service.Delete()
+ service.Close()
+ if err != nil {
+ return err
+ }
+ for {
+ service, err = m.OpenService(serviceName)
+ if err != nil {
+ break
+ }
+ service.Close()
+ time.Sleep(time.Second)
+ }
+ }
+
+ config := mgr.Config{
+ ServiceType: windows.SERVICE_WIN32_OWN_PROCESS,
+ StartType: mgr.StartAutomatic,
+ ErrorControl: mgr.ErrorNormal,
+ DisplayName: serviceName,
+ }
+
+ service, err = m.CreateService(serviceName, path, config, "/managerservice")
+ if err != nil {
+ return err
+ }
+ service.Start()
+ return service.Close()
+}
+
+func UninstallManager() error {
+ m, err := serviceManager()
+ if err != nil {
+ return err
+ }
+ serviceName := "WireGuard Manager"
+ service, err := m.OpenService(serviceName)
+ if err != nil {
+ return err
+ }
+ service.Control(svc.Stop)
+ err = service.Delete()
+ err2 := service.Close()
+ if err != nil {
+ return err
+ }
+ return err2
+}
+
+func RunManager() error {
+ return svc.Run("WireGuard Manager", &managerService{})
+}
+
+func InstallTunnel(configPath string) error {
+ m, err := serviceManager()
+ if err != nil {
+ return err
+ }
+ path, err := os.Executable()
+ if err != nil {
+ return nil
+ }
+
+ name, err := conf.NameFromPath(configPath)
+ if err != nil {
+ return err
+ }
+
+ serviceName := "WireGuard Tunnel: " + name
+ service, err := m.OpenService(serviceName)
+ if err == nil {
+ status, err := service.Query()
+ if err != nil {
+ service.Close()
+ return err
+ }
+ if status.State != svc.Stopped {
+ service.Close()
+ return errors.New("Tunnel already installed and running")
+ }
+ err = service.Delete()
+ service.Close()
+ if err != nil {
+ return err
+ }
+ for {
+ service, err = m.OpenService(serviceName)
+ if err != nil {
+ break
+ }
+ service.Close()
+ time.Sleep(time.Second)
+ }
+ }
+
+ config := mgr.Config{
+ ServiceType: windows.SERVICE_WIN32_OWN_PROCESS,
+ StartType: mgr.StartAutomatic,
+ ErrorControl: mgr.ErrorNormal,
+ DisplayName: serviceName,
+ }
+
+ service, err = m.CreateService(serviceName, path, config, "/tunnelservice", configPath)
+ if err != nil {
+ return err
+ }
+ service.Start()
+ return service.Close()
+}
+
+func UninstallTunnel(name string) error {
+ m, err := serviceManager()
+ if err != nil {
+ return err
+ }
+ serviceName := "WireGuard Tunnel: " + name
+ service, err := m.OpenService(serviceName)
+ if err != nil {
+ return err
+ }
+ service.Control(svc.Stop)
+ err = service.Delete()
+ err2 := service.Close()
+ if err != nil {
+ return err
+ }
+ return err2
+}
+
+func RunTunnel(confPath string, debug bool) error {
+ name, err := conf.NameFromPath(confPath)
+ if err != nil {
+ return err
+ }
+ if debug {
+ return svcdbg.Run("WireGuard Tunnel: "+name, &tunnelService{confPath, true})
+ } else {
+ return svc.Run("WireGuard Tunnel: "+name, &tunnelService{confPath, false})
+ }
+}
diff --git a/service/mksyscall.go b/service/mksyscall.go
new file mode 100644
index 00000000..f80b9d1a
--- /dev/null
+++ b/service/mksyscall.go
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package service
+
+//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go service_manager.go ipc_server.go ipc_event.go
diff --git a/service/service_manager.go b/service/service_manager.go
new file mode 100644
index 00000000..9f529bd1
--- /dev/null
+++ b/service/service_manager.go
@@ -0,0 +1,294 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package service
+
+import (
+ "golang.org/x/sys/windows"
+ "golang.org/x/sys/windows/svc"
+ "golang.org/x/sys/windows/svc/eventlog"
+ "log"
+ "os"
+ "strconv"
+ "sync"
+ "syscall"
+ "unsafe"
+)
+
+const (
+ wtsSessionLogon uint32 = 5
+ wtsSessionLogoff uint32 = 6
+)
+
+type wtsState int
+
+const (
+ wtsActive wtsState = iota
+ wtsConnected
+ wtsConnectQuery
+ wtsShadow
+ wtsDisconnected
+ wtsIdle
+ wtsListen
+ wtsReset
+ wtsDown
+ wtsInit
+)
+
+type wtsSessionNotification struct {
+ size uint32
+ sessionID uint32
+}
+
+type wtsSessionInfo struct {
+ sessionID uint32
+ windowStationName *uint16
+ state wtsState
+}
+
+type wellKnownSidType uint32
+
+const (
+ winBuiltinAdministratorsSid wellKnownSidType = 26
+)
+
+//sys wtfQueryUserToken(session uint32, token *windows.Token) (err error) = wtsapi32.WTSQueryUserToken
+//sys wtsEnumerateSessions(handle windows.Handle, reserved uint32, version uint32, sessions **wtsSessionInfo, count *uint32) (err error) = wtsapi32.WTSEnumerateSessionsW
+//sys wtsFreeMemory(ptr uintptr) = wtsapi32.WTSFreeMemory
+//sys createWellKnownSid(sidType wellKnownSidType, domainSid *windows.SID, sid *windows.SID, sizeSid *uint32) (err error) = advapi32.CreateWellKnownSid
+
+//TODO: Upstream this to x/sys/windows
+func localWellKnownSid(sidType wellKnownSidType) (*windows.SID, error) {
+ n := uint32(50)
+ for {
+ b := make([]byte, n)
+ sid := (*windows.SID)(unsafe.Pointer(&b[0]))
+ err := createWellKnownSid(sidType, nil, sid, &n)
+ if err == nil {
+ return sid, nil
+ }
+ if err != windows.ERROR_INSUFFICIENT_BUFFER {
+ return nil, err
+ }
+ if n <= uint32(len(b)) {
+ return nil, err
+ }
+ }
+}
+
+type managerService struct{}
+
+type elogger struct {
+ *eventlog.Log
+}
+
+func (elog elogger) Write(p []byte) (n int, err error) {
+ msg := string(p)
+ n = len(msg)
+ err = elog.Warning(1, msg)
+ return
+}
+
+func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
+ changes <- svc.Status{State: svc.StartPending}
+
+ //TODO: remember to clean this up in the msi uninstaller
+ eventlog.InstallAsEventCreate("WireGuard", eventlog.Info|eventlog.Warning|eventlog.Error)
+ elog, err := eventlog.Open("WireGuard")
+ if err != nil {
+ changes <- svc.Status{State: svc.StopPending}
+ exitCode = ERROR_LOG_CONTAINER_OPEN_FAILED
+ return
+ }
+ log.SetOutput(elogger{elog})
+
+ path, err := os.Executable()
+ if err != nil {
+ elog.Error(1, "Unable to determine own executable path: "+err.Error())
+ changes <- svc.Status{State: svc.StopPending}
+ exitCode = ERROR_BAD_PATHNAME
+ return
+ }
+
+ adminSid, err := localWellKnownSid(winBuiltinAdministratorsSid)
+ if err != nil {
+ elog.Error(1, "Unable to find Administrators SID: "+err.Error())
+ changes <- svc.Status{State: svc.StopPending}
+ exitCode = ERROR_SERVER_SID_MISMATCH
+ return
+ }
+
+ devNull, err := os.OpenFile(os.DevNull, os.O_RDWR, 0)
+ if err != nil {
+ elog.Error(1, "Unable to open NUL file: "+err.Error())
+ changes <- svc.Status{State: svc.StopPending}
+ exitCode = ERROR_FILE_NOT_FOUND
+ return
+ }
+
+ procs := make(map[uint32]*os.Process)
+ procsLock := sync.Mutex{}
+ var startProcess func(session uint32)
+
+ startProcess = func(session uint32) {
+ for {
+ var userToken windows.Token
+ err := wtfQueryUserToken(session, &userToken)
+ if err != nil {
+ return
+ }
+
+ //TODO: SECURITY CRITICIAL!
+ //TODO: Isn't it better to use an impersonation token and userToken.IsMember instead?
+ gs, err := userToken.GetTokenGroups()
+ if err != nil {
+ elog.Error(1, "Unable to lookup user groups from token: "+err.Error())
+ return
+ }
+ p := unsafe.Pointer(&gs.Groups[0])
+ //TODO: x/sys/windows/svc/security.go uses 2 << 20, but shouldn't this be 1 << 20? Send upstream
+ groups := (*[1 << 20]windows.SIDAndAttributes)(p)[:gs.GroupCount]
+ isAdmin := false
+ for _, g := range groups {
+ if windows.EqualSid(g.Sid, adminSid) {
+ isAdmin = true
+ break
+ }
+ }
+ if !isAdmin {
+ return
+ }
+
+ user, err := userToken.GetTokenUser()
+ if err != nil {
+ elog.Error(1, "Unable to lookup user from token: "+err.Error())
+ return
+ }
+ username, domain, accType, err := user.User.Sid.LookupAccount("")
+ if err != nil {
+ elog.Error(1, "Unable to lookup username from sid: "+err.Error())
+ return
+ }
+ if accType != windows.SidTypeUser {
+ return
+ }
+
+ ourReader, theirReader, theirReaderStr, ourWriter, theirWriter, theirWriterStr, err := inheritableSocketpairEmulation()
+ if err != nil {
+ elog.Error(1, "Unable to create two inheritable pipes: "+err.Error())
+ return
+ }
+ err = IPCServerListen(ourReader, ourWriter)
+ if err != nil {
+ elog.Error(1, "Unable to listen on IPC pipes: "+err.Error())
+ return
+ }
+
+ elog.Info(1, "Starting UI process for user: "+username+", domain: "+domain)
+ attr := &os.ProcAttr{
+ Sys: &syscall.SysProcAttr{
+ Token: syscall.Token(userToken),
+ },
+ Files: []*os.File{devNull, devNull, devNull},
+ }
+ proc, err := os.StartProcess(path, []string{path, "/ui", theirReaderStr, theirWriterStr}, attr)
+ theirReader.Close()
+ theirWriter.Close()
+ if err != nil {
+ elog.Error(1, "Unable to start manager UI process: "+err.Error())
+ return
+ }
+
+ procsLock.Lock()
+ procs[session] = proc
+ procsLock.Unlock()
+ proc.Wait()
+ procsLock.Lock()
+ delete(procs, session)
+ procsLock.Unlock()
+ ourReader.Close()
+ ourWriter.Close()
+ }
+ }
+
+ var sessionsPointer *wtsSessionInfo
+ var count uint32
+ err = wtsEnumerateSessions(0, 0, 1, &sessionsPointer, &count)
+ if err != nil {
+ elog.Error(1, "Unable to enumerate current sessions: "+err.Error())
+ changes <- svc.Status{State: svc.StopPending}
+ exitCode = ERROR_BAD_LOGON_SESSION_STATE
+ return
+ }
+ sessions := *(*[]wtsSessionInfo)(unsafe.Pointer(&struct {
+ addr *wtsSessionInfo
+ len int
+ cap int
+ }{sessionsPointer, int(count), int(count)}))
+ for _, session := range sessions {
+ if session.state == wtsActive {
+ go startProcess(session.sessionID)
+ }
+ }
+ wtsFreeMemory(uintptr(unsafe.Pointer(sessionsPointer)))
+
+ changes <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptSessionChange}
+
+ uninstall := false
+loop:
+ for {
+ select {
+ case <-quitManagersChan:
+ uninstall = true
+ break loop
+ case c := <-r:
+ switch c.Cmd {
+ case svc.Stop:
+ break loop
+ case svc.Interrogate:
+ changes <- c.CurrentStatus
+ case svc.SessionChange:
+ //TODO: All the logic here depends on https://go-review.googlesource.com/c/sys/+/158698 being merged
+ if c.EventType != wtsSessionLogon && c.EventType != wtsSessionLogoff {
+ continue
+ }
+ sessionNotification := (*wtsSessionNotification)(unsafe.Pointer(c.EventData))
+ if uintptr(sessionNotification.size) != unsafe.Sizeof(*sessionNotification) {
+ elog.Error(1, "Unexpected size of WTSSESSION_NOTIFICATION: "+strconv.Itoa(int(sessionNotification.size)))
+ continue
+ }
+ if c.EventType == wtsSessionLogoff {
+ procsLock.Lock()
+ if proc, ok := procs[sessionNotification.sessionID]; ok {
+ proc.Kill()
+ }
+ procsLock.Unlock()
+ } else if c.EventType == wtsSessionLogon {
+ procsLock.Lock()
+ if _, ok := procs[sessionNotification.sessionID]; !ok {
+ go startProcess(sessionNotification.sessionID)
+ }
+ procsLock.Unlock()
+ }
+ default:
+ elog.Info(1, "Unexpected service control request "+strconv.Itoa(int(c.Cmd)))
+ }
+ }
+ }
+
+ changes <- svc.Status{State: svc.StopPending}
+ procsLock.Lock()
+ for _, proc := range procs {
+ proc.Kill()
+ }
+ procsLock.Unlock()
+ if uninstall {
+ err = UninstallManager()
+ if err != nil {
+ elog.Error(1, "Unable to uninstaller manager when quitting: "+err.Error())
+ }
+ }
+ return
+}
diff --git a/service/service_tunnel.go b/service/service_tunnel.go
new file mode 100644
index 00000000..b1f1df60
--- /dev/null
+++ b/service/service_tunnel.go
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package service
+
+import (
+ "bufio"
+ "log"
+ "net"
+ "strings"
+
+ "golang.org/x/sys/windows/svc"
+ "golang.org/x/sys/windows/svc/debug"
+ "golang.org/x/sys/windows/svc/eventlog"
+
+ "golang.zx2c4.com/winipcfg"
+ "golang.zx2c4.com/wireguard/windows/conf"
+ "golang.zx2c4.com/wireguard/windows/service/tun"
+)
+
+type confElogger struct {
+ elog debug.Log
+ conf *conf.Config
+ level int
+}
+
+func (elog confElogger) Write(p []byte) (n int, err error) {
+ msg := elog.conf.Name + ": " + string(p)
+ n = len(msg)
+ switch elog.level {
+ case 1:
+ err = elog.elog.Info(1, msg)
+ case 2:
+ err = elog.elog.Warning(1, msg)
+ case 3:
+ err = elog.elog.Error(1, msg)
+ }
+ return
+}
+
+type tunnelService struct {
+ path string
+ debug bool
+}
+
+func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
+ changes <- svc.Status{State: svc.StartPending}
+
+ var elog debug.Log
+ var err error
+ if service.debug {
+ elog = debug.New("WireGuard")
+ } else {
+ //TODO: remember to clean this up in the msi uninstaller
+ eventlog.InstallAsEventCreate("WireGuard", eventlog.Info|eventlog.Warning|eventlog.Error)
+ elog, err = eventlog.Open("WireGuard")
+ if err != nil {
+ changes <- svc.Status{State: svc.StopPending}
+ exitCode = ERROR_LOG_CONTAINER_OPEN_FAILED
+ return
+ }
+ }
+
+ conf, err := conf.LoadFromPath(service.path)
+ if err != nil {
+ elog.Error(1, "Unable to load configuration file from path "+service.path+": "+err.Error())
+ changes <- svc.Status{State: svc.StopPending}
+ exitCode = ERROR_OPEN_FAILED
+ return
+ }
+
+ logger := &Logger{
+ Debug: log.New(&confElogger{elog: elog, conf: conf, level: 1}, "", 0),
+ Info: log.New(&confElogger{elog: elog, conf: conf, level: 2}, "", 0),
+ Error: log.New(&confElogger{elog: elog, conf: conf, level: 3}, "", 0),
+ }
+
+ logger.Info.Println("Starting wireguard-go version", WireGuardGoVersion)
+ logger.Debug.Println("Debug log enabled")
+
+ tun, err := tun.CreateTUN(conf.Name)
+ if err == nil {
+ realInterfaceName, err2 := tun.Name()
+ if err2 == nil {
+ conf.Name = realInterfaceName
+ }
+ } else {
+ logger.Error.Println("Failed to create TUN device:", err)
+ changes <- svc.Status{State: svc.StopPending}
+ exitCode = ERROR_ADAP_HDW_ERR
+ return
+ }
+
+ device := NewDevice(tun, logger)
+ device.Up()
+ logger.Info.Println("Device started")
+
+ uapi, err := UAPIListen(conf.Name)
+ if err != nil {
+ logger.Error.Println("Failed to listen on uapi socket:", err)
+ changes <- svc.Status{State: svc.StopPending}
+ exitCode = ERROR_PIPE_LISTENING
+ device.Close()
+ return
+ }
+ errs := make(chan error)
+
+ go func() {
+ for {
+ conn, err := uapi.Accept()
+ if err != nil {
+ errs <- err
+ return
+ }
+ go ipcHandle(device, conn)
+ }
+ }()
+ logger.Info.Println("UAPI listener started")
+ uapiConf, err := conf.ToUAPI()
+ if err != nil {
+ logger.Error.Println("Failed to convert to UAPI serialization:", err)
+ changes <- svc.Status{State: svc.StopPending}
+ exitCode = ERROR_INVALID_PARAMETER
+ device.Close()
+ return
+ }
+ ipcSetOperation(device, bufio.NewReader(strings.NewReader(uapiConf)))
+
+ //TODO: configure addresses, routes, and DNS with winipcfg
+ iface, err := winipcfg.InterfaceFromFriendlyName(conf.Name)
+ if err == nil {
+ a := make([]*net.IPNet, len(conf.Interface.Addresses))
+ for i, addr := range conf.Interface.Addresses {
+ a[i] = &net.IPNet{addr.IP, net.CIDRMask(int(addr.Cidr), len(addr.IP))}
+ }
+ err = iface.SetAddresses(a)
+ }
+ if err != nil {
+ logger.Error.Println("Unable to setup interface addresses:", err)
+ changes <- svc.Status{State: svc.StopPending}
+ exitCode = ERROR_NETWORK_BUSY
+ device.Close()
+ return
+ }
+
+ changes <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop}
+
+loop:
+ for {
+ select {
+ case c := <-r:
+ switch c.Cmd {
+ case svc.Stop:
+ break loop
+ case svc.Interrogate:
+ changes <- c.CurrentStatus
+ default:
+ logger.Error.Printf("Unexpected service control request #%d", c)
+ }
+ case <-errs:
+ break loop
+ case <-device.Wait():
+ break loop
+ }
+ }
+
+ changes <- svc.Status{State: svc.StopPending}
+ logger.Info.Println("Shutting down")
+ uapi.Close()
+ device.Close()
+ return
+}
diff --git a/service/zsyscall_windows.go b/service/zsyscall_windows.go
new file mode 100644
index 00000000..79d4ccf5
--- /dev/null
+++ b/service/zsyscall_windows.go
@@ -0,0 +1,116 @@
+// Code generated by 'go generate'; DO NOT EDIT.
+
+package service
+
+import (
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+var _ unsafe.Pointer
+
+// Do the interface allocations only once for common
+// Errno values.
+const (
+ errnoERROR_IO_PENDING = 997
+)
+
+var (
+ errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
+)
+
+// errnoErr returns common boxed Errno values, to prevent
+// allocations at runtime.
+func errnoErr(e syscall.Errno) error {
+ switch e {
+ case 0:
+ return nil
+ case errnoERROR_IO_PENDING:
+ return errERROR_IO_PENDING
+ }
+ // TODO: add more here, after collecting data on the common
+ // error values see on Windows. (perhaps when running
+ // all.bat?)
+ return e
+}
+
+var (
+ modwtsapi32 = windows.NewLazySystemDLL("wtsapi32.dll")
+ modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
+ moduser32 = windows.NewLazySystemDLL("user32.dll")
+
+ procWTSQueryUserToken = modwtsapi32.NewProc("WTSQueryUserToken")
+ procWTSEnumerateSessionsW = modwtsapi32.NewProc("WTSEnumerateSessionsW")
+ procWTSFreeMemory = modwtsapi32.NewProc("WTSFreeMemory")
+ procCreateWellKnownSid = modadvapi32.NewProc("CreateWellKnownSid")
+ procPostMessageW = moduser32.NewProc("PostMessageW")
+ procRegisterWindowMessageW = moduser32.NewProc("RegisterWindowMessageW")
+)
+
+func wtfQueryUserToken(session uint32, token *windows.Token) (err error) {
+ r1, _, e1 := syscall.Syscall(procWTSQueryUserToken.Addr(), 2, uintptr(session), uintptr(unsafe.Pointer(token)), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func wtsEnumerateSessions(handle windows.Handle, reserved uint32, version uint32, sessions **wtsSessionInfo, count *uint32) (err error) {
+ r1, _, e1 := syscall.Syscall6(procWTSEnumerateSessionsW.Addr(), 5, uintptr(handle), uintptr(reserved), uintptr(version), uintptr(unsafe.Pointer(sessions)), uintptr(unsafe.Pointer(count)), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func wtsFreeMemory(ptr uintptr) {
+ syscall.Syscall(procWTSFreeMemory.Addr(), 1, uintptr(ptr), 0, 0)
+ return
+}
+
+func createWellKnownSid(sidType wellKnownSidType, domainSid *windows.SID, sid *windows.SID, sizeSid *uint32) (err error) {
+ r1, _, e1 := syscall.Syscall6(procCreateWellKnownSid.Addr(), 4, uintptr(sidType), uintptr(unsafe.Pointer(domainSid)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sizeSid)), 0, 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func postMessage(hwnd windows.Handle, msg uint, wparam uintptr, lparam uintptr) (err error) {
+ r1, _, e1 := syscall.Syscall6(procPostMessageW.Addr(), 4, uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func registerWindowMessage(name *uint16) (message uint, err error) {
+ r0, _, e1 := syscall.Syscall(procRegisterWindowMessageW.Addr(), 1, uintptr(unsafe.Pointer(name)), 0, 0)
+ message = uint(r0)
+ if message == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}