windows: add various functions for shutting down and logging out

There are a few functions to control the behavior of shutdown and
logout, both for what the current process does during shutdown, and also
whether or not the current process is running in an interactive session.
The below code is a port of the MSDN example code to Go using one of the
added new functions:

https://docs.microsoft.com/en-us/windows/win32/shutdown/how-to-shut-down-the-system

func shutdownLikeMSDNDoes() error {
  seShutdownName, err := windows.UTF16PtrFromString("SeShutdownPrivilege")
  if err != nil {
    return err
  }

  var shutdownPriv windows.Tokenprivileges
  err = windows.LookupPrivilegeValue(nil, seShutdownName, &shutdownPriv.Privileges[0].Luid)
  if err != nil {
    return err
  }
  shutdownPriv.Privileges[0].Attributes = windows.SE_PRIVILEGE_ENABLED
  shutdownPriv.PrivilegeCount = 1

  process, err := windows.GetCurrentProcess()
  if err != nil {
    return err
  }
  var token windows.Token
  err = windows.OpenProcessToken(process, windows.TOKEN_ADJUST_PRIVILEGES | windows.TOKEN_QUERY, &token)
  if err != nil {
    return err
  }
  defer token.Close()

  err = windows.AdjustTokenPrivileges(token, false, &shutdownPriv, 0, nil, nil)
  if err != nil {
    return err
  }

  err = windows.ExitWindowsEx(windows.EWX_SHUTDOWN | windows.EWX_FORCE,
    windows.SHTDN_REASON_MAJOR_OPERATINGSYSTEM | windows.SHTDN_REASON_MINOR_UPGRADE | windows.SHTDN_REASON_FLAG_PLANNED)
  if err != nil {
    return err
  }

  return nil
}

Note, though, that this function doesn't set the token privs back to how
they were before, which isn't good. A more robust method than the MSDN
one above would be to duplicate&impersonate.

Fixes: golang/go#34271
Change-Id: Ibe55ddd35b709d9ab793cb9af47c39901c5e5c69
Reviewed-on: https://go-review.googlesource.com/c/sys/+/195497
Run-TryBot: Jason A. Donenfeld <Jason@zx2c4.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Bruce Downs <bruceadowns@gmail.com>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
This commit is contained in:
Jason A. Donenfeld
2019-09-15 11:50:40 -06:00
parent 1a3b71a79e
commit 8a69140bde
3 changed files with 133 additions and 0 deletions

View File

@@ -296,6 +296,10 @@ func NewCallbackCDecl(fn interface{}) uintptr {
//sys SetVolumeLabel(rootPathName *uint16, volumeName *uint16) (err error) = SetVolumeLabelW
//sys SetVolumeMountPoint(volumeMountPoint *uint16, volumeName *uint16) (err error) = SetVolumeMountPointW
//sys MessageBox(hwnd Handle, text *uint16, caption *uint16, boxtype uint32) (ret int32, err error) [failretval==0] = user32.MessageBoxW
//sys ExitWindowsEx(flags uint32, reason uint32) (err error) = user32.ExitWindowsEx
//sys InitiateSystemShutdownEx(machineName *uint16, message *uint16, timeout uint32, forceAppsClosed bool, rebootAfterShutdown bool, reason uint32) (err error) = advapi32.InitiateSystemShutdownExW
//sys SetProcessShutdownParameters(level uint32, flags uint32) (err error) = kernel32.SetProcessShutdownParameters
//sys GetProcessShutdownParameters(level *uint32, flags *uint32) (err error) = kernel32.GetProcessShutdownParameters
//sys clsidFromString(lpsz *uint16, pclsid *GUID) (ret error) = ole32.CLSIDFromString
//sys stringFromGUID2(rguid *GUID, lpsz *uint16, cchMax int32) (chars int32) = ole32.StringFromGUID2
//sys coCreateGuid(pguid *GUID) (ret error) = ole32.CoCreateGuid

View File

@@ -1688,3 +1688,68 @@ type OsVersionInfoEx struct {
ProductType byte
_ byte
}
const (
EWX_LOGOFF = 0x00000000
EWX_SHUTDOWN = 0x00000001
EWX_REBOOT = 0x00000002
EWX_FORCE = 0x00000004
EWX_POWEROFF = 0x00000008
EWX_FORCEIFHUNG = 0x00000010
EWX_QUICKRESOLVE = 0x00000020
EWX_RESTARTAPPS = 0x00000040
EWX_HYBRID_SHUTDOWN = 0x00400000
EWX_BOOTOPTIONS = 0x01000000
SHTDN_REASON_FLAG_COMMENT_REQUIRED = 0x01000000
SHTDN_REASON_FLAG_DIRTY_PROBLEM_ID_REQUIRED = 0x02000000
SHTDN_REASON_FLAG_CLEAN_UI = 0x04000000
SHTDN_REASON_FLAG_DIRTY_UI = 0x08000000
SHTDN_REASON_FLAG_USER_DEFINED = 0x40000000
SHTDN_REASON_FLAG_PLANNED = 0x80000000
SHTDN_REASON_MAJOR_OTHER = 0x00000000
SHTDN_REASON_MAJOR_NONE = 0x00000000
SHTDN_REASON_MAJOR_HARDWARE = 0x00010000
SHTDN_REASON_MAJOR_OPERATINGSYSTEM = 0x00020000
SHTDN_REASON_MAJOR_SOFTWARE = 0x00030000
SHTDN_REASON_MAJOR_APPLICATION = 0x00040000
SHTDN_REASON_MAJOR_SYSTEM = 0x00050000
SHTDN_REASON_MAJOR_POWER = 0x00060000
SHTDN_REASON_MAJOR_LEGACY_API = 0x00070000
SHTDN_REASON_MINOR_OTHER = 0x00000000
SHTDN_REASON_MINOR_NONE = 0x000000ff
SHTDN_REASON_MINOR_MAINTENANCE = 0x00000001
SHTDN_REASON_MINOR_INSTALLATION = 0x00000002
SHTDN_REASON_MINOR_UPGRADE = 0x00000003
SHTDN_REASON_MINOR_RECONFIG = 0x00000004
SHTDN_REASON_MINOR_HUNG = 0x00000005
SHTDN_REASON_MINOR_UNSTABLE = 0x00000006
SHTDN_REASON_MINOR_DISK = 0x00000007
SHTDN_REASON_MINOR_PROCESSOR = 0x00000008
SHTDN_REASON_MINOR_NETWORKCARD = 0x00000009
SHTDN_REASON_MINOR_POWER_SUPPLY = 0x0000000a
SHTDN_REASON_MINOR_CORDUNPLUGGED = 0x0000000b
SHTDN_REASON_MINOR_ENVIRONMENT = 0x0000000c
SHTDN_REASON_MINOR_HARDWARE_DRIVER = 0x0000000d
SHTDN_REASON_MINOR_OTHERDRIVER = 0x0000000e
SHTDN_REASON_MINOR_BLUESCREEN = 0x0000000F
SHTDN_REASON_MINOR_SERVICEPACK = 0x00000010
SHTDN_REASON_MINOR_HOTFIX = 0x00000011
SHTDN_REASON_MINOR_SECURITYFIX = 0x00000012
SHTDN_REASON_MINOR_SECURITY = 0x00000013
SHTDN_REASON_MINOR_NETWORK_CONNECTIVITY = 0x00000014
SHTDN_REASON_MINOR_WMI = 0x00000015
SHTDN_REASON_MINOR_SERVICEPACK_UNINSTALL = 0x00000016
SHTDN_REASON_MINOR_HOTFIX_UNINSTALL = 0x00000017
SHTDN_REASON_MINOR_SECURITYFIX_UNINSTALL = 0x00000018
SHTDN_REASON_MINOR_MMC = 0x00000019
SHTDN_REASON_MINOR_SYSTEMRESTORE = 0x0000001a
SHTDN_REASON_MINOR_TERMSRV = 0x00000020
SHTDN_REASON_MINOR_DC_PROMOTION = 0x00000021
SHTDN_REASON_MINOR_DC_DEMOTION = 0x00000022
SHTDN_REASON_UNKNOWN = SHTDN_REASON_MINOR_NONE
SHTDN_REASON_LEGACY_API = SHTDN_REASON_MAJOR_LEGACY_API | SHTDN_REASON_FLAG_PLANNED
SHTDN_REASON_VALID_BIT_MASK = 0xc0ffffff
SHUTDOWN_NORETRY = 0x1
)

View File

@@ -234,6 +234,10 @@ var (
procSetVolumeLabelW = modkernel32.NewProc("SetVolumeLabelW")
procSetVolumeMountPointW = modkernel32.NewProc("SetVolumeMountPointW")
procMessageBoxW = moduser32.NewProc("MessageBoxW")
procExitWindowsEx = moduser32.NewProc("ExitWindowsEx")
procInitiateSystemShutdownExW = modadvapi32.NewProc("InitiateSystemShutdownExW")
procSetProcessShutdownParameters = modkernel32.NewProc("SetProcessShutdownParameters")
procGetProcessShutdownParameters = modkernel32.NewProc("GetProcessShutdownParameters")
procCLSIDFromString = modole32.NewProc("CLSIDFromString")
procStringFromGUID2 = modole32.NewProc("StringFromGUID2")
procCoCreateGuid = modole32.NewProc("CoCreateGuid")
@@ -2584,6 +2588,66 @@ func MessageBox(hwnd Handle, text *uint16, caption *uint16, boxtype uint32) (ret
return
}
func ExitWindowsEx(flags uint32, reason uint32) (err error) {
r1, _, e1 := syscall.Syscall(procExitWindowsEx.Addr(), 2, uintptr(flags), uintptr(reason), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func InitiateSystemShutdownEx(machineName *uint16, message *uint16, timeout uint32, forceAppsClosed bool, rebootAfterShutdown bool, reason uint32) (err error) {
var _p0 uint32
if forceAppsClosed {
_p0 = 1
} else {
_p0 = 0
}
var _p1 uint32
if rebootAfterShutdown {
_p1 = 1
} else {
_p1 = 0
}
r1, _, e1 := syscall.Syscall6(procInitiateSystemShutdownExW.Addr(), 6, uintptr(unsafe.Pointer(machineName)), uintptr(unsafe.Pointer(message)), uintptr(timeout), uintptr(_p0), uintptr(_p1), uintptr(reason))
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func SetProcessShutdownParameters(level uint32, flags uint32) (err error) {
r1, _, e1 := syscall.Syscall(procSetProcessShutdownParameters.Addr(), 2, uintptr(level), uintptr(flags), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func GetProcessShutdownParameters(level *uint32, flags *uint32) (err error) {
r1, _, e1 := syscall.Syscall(procGetProcessShutdownParameters.Addr(), 2, uintptr(unsafe.Pointer(level)), uintptr(unsafe.Pointer(flags)), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func clsidFromString(lpsz *uint16, pclsid *GUID) (ret error) {
r0, _, _ := syscall.Syscall(procCLSIDFromString.Addr(), 2, uintptr(unsafe.Pointer(lpsz)), uintptr(unsafe.Pointer(pclsid)), 0)
if r0 != 0 {