/* SPDX-License-Identifier: MIT * * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. */ package service import ( "errors" "runtime" "unsafe" "golang.org/x/sys/windows" ) const ( WTS_CONSOLE_CONNECT = 0x1 WTS_CONSOLE_DISCONNECT = 0x2 WTS_REMOTE_CONNECT = 0x3 WTS_REMOTE_DISCONNECT = 0x4 WTS_SESSION_LOGON = 0x5 WTS_SESSION_LOGOFF = 0x6 WTS_SESSION_LOCK = 0x7 WTS_SESSION_UNLOCK = 0x8 WTS_SESSION_REMOTE_CONTROL = 0x9 WTS_SESSION_CREATE = 0xa WTS_SESSION_TERMINATE = 0xb ) const ( WTSActive = 0 WTSConnected = 1 WTSConnectQuery = 2 WTSShadow = 3 WTSDisconnected = 4 WTSIdle = 5 WTSListen = 6 WTSReset = 7 WTSDown = 8 WTSInit = 9 ) type WTS_SESSION_NOTIFICATION struct { Size uint32 SessionID uint32 } type WTS_SESSION_INFO struct { SessionID uint32 WindowStationName *uint16 State uint32 } //sys wtsQueryUserToken(session uint32, token *windows.Token) (err error) = wtsapi32.WTSQueryUserToken //sys wtsEnumerateSessions(handle windows.Handle, reserved uint32, version uint32, sessions **WTS_SESSION_INFO, count *uint32) (err error) = wtsapi32.WTSEnumerateSessionsW //sys wtsFreeMemory(ptr uintptr) = wtsapi32.WTSFreeMemory func tokenIsElevated(token windows.Token) bool { var isElevated uint32 var outLen uint32 err := windows.GetTokenInformation(token, windows.TokenElevation, (*byte)(unsafe.Pointer(&isElevated)), uint32(unsafe.Sizeof(isElevated)), &outLen) if err != nil { return false } return outLen == uint32(unsafe.Sizeof(isElevated)) && isElevated != 0 } func getElevatedToken(token windows.Token) (windows.Token, error) { if tokenIsElevated(token) { return token, nil } var linkedToken windows.Token var outLen uint32 err := windows.GetTokenInformation(token, windows.TokenLinkedToken, (*byte)(unsafe.Pointer(&linkedToken)), uint32(unsafe.Sizeof(linkedToken)), &outLen) if err != nil { return windows.Token(0), err } if tokenIsElevated(linkedToken) { return linkedToken, nil } linkedToken.Close() return windows.Token(0), errors.New("the linked token is not elevated") } func TokenIsMemberOfBuiltInAdministrator(token windows.Token) bool { adminSid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid) if err != nil { return false } gs, err := token.GetTokenGroups() if err != nil { return false } groups := (*[(1 << 28) - 1]windows.SIDAndAttributes)(unsafe.Pointer(&gs.Groups[0]))[:gs.GroupCount] isAdmin := false for _, g := range groups { if (g.Attributes&windows.SE_GROUP_USE_FOR_DENY_ONLY != 0 || g.Attributes&windows.SE_GROUP_ENABLED != 0) && windows.EqualSid(g.Sid, adminSid) { isAdmin = true break } } runtime.KeepAlive(gs) return isAdmin } func dropAllPrivileges() error { processHandle, err := windows.GetCurrentProcess() if err != nil { return err } var processToken windows.Token err = windows.OpenProcessToken(processHandle, windows.TOKEN_READ|windows.TOKEN_WRITE, &processToken) if err != nil { return err } defer processToken.Close() var bufferSizeRequired uint32 windows.GetTokenInformation(processToken, windows.TokenPrivileges, nil, 0, &bufferSizeRequired) if bufferSizeRequired == 0 || bufferSizeRequired < uint32(unsafe.Sizeof(windows.Tokenprivileges{}.PrivilegeCount)) { return errors.New("GetTokenInformation failed to provide a buffer size") } buffer := make([]byte, bufferSizeRequired) var bytesWritten uint32 err = windows.GetTokenInformation(processToken, windows.TokenPrivileges, &buffer[0], uint32(len(buffer)), &bytesWritten) if err != nil { return err } if bytesWritten != bufferSizeRequired { return errors.New("GetTokenInformation returned incomplete data") } tokenPrivileges := (*windows.Tokenprivileges)(unsafe.Pointer(&buffer[0])) for i := uint32(0); i < tokenPrivileges.PrivilegeCount; i++ { (*windows.LUIDAndAttributes)(unsafe.Pointer(uintptr(unsafe.Pointer(&tokenPrivileges.Privileges[0])) + unsafe.Sizeof(tokenPrivileges.Privileges[0])*uintptr(i))).Attributes = windows.SE_PRIVILEGE_REMOVED } err = windows.AdjustTokenPrivileges(processToken, false, tokenPrivileges, 0, nil, nil) runtime.KeepAlive(buffer) return err }