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  }