github.com/etecs-ru/go-sys-wineventlog@v0.0.0-20210227233244-4c3abb794018/windows/svc/security.go (about) 1 // Copyright 2012 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build windows 6 7 package svc 8 9 import ( 10 "errors" 11 "syscall" 12 "unsafe" 13 14 "golang.org/x/sys/windows" 15 ) 16 17 func allocSid(subAuth0 uint32) (*windows.SID, error) { 18 var sid *windows.SID 19 err := windows.AllocateAndInitializeSid(&windows.SECURITY_NT_AUTHORITY, 20 1, subAuth0, 0, 0, 0, 0, 0, 0, 0, &sid) 21 if err != nil { 22 return nil, err 23 } 24 return sid, nil 25 } 26 27 // IsAnInteractiveSession determines if calling process is running interactively. 28 // It queries the process token for membership in the Interactive group. 29 // http://stackoverflow.com/questions/2668851/how-do-i-detect-that-my-application-is-running-as-service-or-in-an-interactive-s 30 // 31 // Deprecated: Use IsWindowsService instead. 32 func IsAnInteractiveSession() (bool, error) { 33 interSid, err := allocSid(windows.SECURITY_INTERACTIVE_RID) 34 if err != nil { 35 return false, err 36 } 37 defer windows.FreeSid(interSid) 38 39 serviceSid, err := allocSid(windows.SECURITY_SERVICE_RID) 40 if err != nil { 41 return false, err 42 } 43 defer windows.FreeSid(serviceSid) 44 45 t, err := windows.OpenCurrentProcessToken() 46 if err != nil { 47 return false, err 48 } 49 defer t.Close() 50 51 gs, err := t.GetTokenGroups() 52 if err != nil { 53 return false, err 54 } 55 56 for _, g := range gs.AllGroups() { 57 if windows.EqualSid(g.Sid, interSid) { 58 return true, nil 59 } 60 if windows.EqualSid(g.Sid, serviceSid) { 61 return false, nil 62 } 63 } 64 return false, nil 65 } 66 67 var ( 68 ntdll = windows.NewLazySystemDLL("ntdll.dll") 69 _NtQueryInformationProcess = ntdll.NewProc("NtQueryInformationProcess") 70 71 kernel32 = windows.NewLazySystemDLL("kernel32.dll") 72 _QueryFullProcessImageNameA = kernel32.NewProc("QueryFullProcessImageNameA") 73 ) 74 75 // IsWindowsService reports whether the process is currently executing 76 // as a Windows service. 77 func IsWindowsService() (bool, error) { 78 // This code was copied from runtime.isWindowsService function. 79 80 // The below technique looks a bit hairy, but it's actually 81 // exactly what the .NET framework does for the similarly named function: 82 // https://github.com/dotnet/extensions/blob/f4066026ca06984b07e90e61a6390ac38152ba93/src/Hosting/WindowsServices/src/WindowsServiceHelpers.cs#L26-L31 83 // Specifically, it looks up whether the parent process has session ID zero 84 // and is called "services". 85 const _CURRENT_PROCESS = ^uintptr(0) 86 // pbi is a PROCESS_BASIC_INFORMATION struct, where we just care about 87 // the 6th pointer inside of it, which contains the pid of the process 88 // parent: 89 // https://github.com/wine-mirror/wine/blob/42cb7d2ad1caba08de235e6319b9967296b5d554/include/winternl.h#L1294 90 var pbi [6]uintptr 91 var pbiLen uint32 92 r0, _, _ := syscall.Syscall6(_NtQueryInformationProcess.Addr(), 5, _CURRENT_PROCESS, 0, uintptr(unsafe.Pointer(&pbi[0])), uintptr(unsafe.Sizeof(pbi)), uintptr(unsafe.Pointer(&pbiLen)), 0) 93 if r0 != 0 { 94 return false, errors.New("NtQueryInformationProcess failed: error=" + itoa(int(r0))) 95 } 96 var psid uint32 97 err := windows.ProcessIdToSessionId(uint32(pbi[5]), &psid) 98 if err != nil { 99 if err == windows.ERROR_INVALID_PARAMETER { 100 // This error happens when Windows cannot find process parent. 101 // Perhaps process parent exited. 102 // Assume we are not running in a service, because service 103 // parent process (services.exe) cannot exit. 104 return false, nil 105 } 106 return false, err 107 } 108 if psid != 0 { 109 // parent session id should be 0 for service process 110 return false, nil 111 } 112 113 pproc, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pbi[5])) 114 if err != nil { 115 return false, err 116 } 117 defer windows.CloseHandle(pproc) 118 119 // exeName gets the path to the executable image of the parent process 120 var exeName [261]byte 121 exeNameLen := uint32(len(exeName) - 1) 122 r0, _, e0 := syscall.Syscall6(_QueryFullProcessImageNameA.Addr(), 4, uintptr(pproc), 0, uintptr(unsafe.Pointer(&exeName[0])), uintptr(unsafe.Pointer(&exeNameLen)), 0, 0) 123 if r0 == 0 { 124 if e0 != 0 { 125 return false, e0 126 } else { 127 return false, syscall.EINVAL 128 } 129 } 130 const ( 131 servicesLower = "services.exe" 132 servicesUpper = "SERVICES.EXE" 133 ) 134 i := int(exeNameLen) - 1 135 j := len(servicesLower) - 1 136 if i < j { 137 return false, nil 138 } 139 for { 140 if j == -1 { 141 return i == -1 || exeName[i] == '\\', nil 142 } 143 if exeName[i] != servicesLower[j] && exeName[i] != servicesUpper[j] { 144 return false, nil 145 } 146 i-- 147 j-- 148 } 149 } 150 151 func itoa(val int) string { // do it here rather than with fmt to avoid dependency 152 if val < 0 { 153 return "-" + itoa(-val) 154 } 155 var buf [32]byte // big enough for int64 156 i := len(buf) - 1 157 for val >= 10 { 158 buf[i] = byte(val%10 + '0') 159 i-- 160 val /= 10 161 } 162 buf[i] = byte(val + '0') 163 return string(buf[i:]) 164 }