golang.org/x/sys@v0.9.0/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  //go:build windows
     6  // +build windows
     7  
     8  package svc
     9  
    10  import (
    11  	"strings"
    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  // IsWindowsService reports whether the process is currently executing
    68  // as a Windows service.
    69  func IsWindowsService() (bool, error) {
    70  	// The below technique looks a bit hairy, but it's actually
    71  	// exactly what the .NET framework does for the similarly named function:
    72  	// https://github.com/dotnet/extensions/blob/f4066026ca06984b07e90e61a6390ac38152ba93/src/Hosting/WindowsServices/src/WindowsServiceHelpers.cs#L26-L31
    73  	// Specifically, it looks up whether the parent process has session ID zero
    74  	// and is called "services".
    75  
    76  	var currentProcess windows.PROCESS_BASIC_INFORMATION
    77  	infoSize := uint32(unsafe.Sizeof(currentProcess))
    78  	err := windows.NtQueryInformationProcess(windows.CurrentProcess(), windows.ProcessBasicInformation, unsafe.Pointer(&currentProcess), infoSize, &infoSize)
    79  	if err != nil {
    80  		return false, err
    81  	}
    82  	var parentProcess *windows.SYSTEM_PROCESS_INFORMATION
    83  	for infoSize = uint32((unsafe.Sizeof(*parentProcess) + unsafe.Sizeof(uintptr(0))) * 1024); ; {
    84  		parentProcess = (*windows.SYSTEM_PROCESS_INFORMATION)(unsafe.Pointer(&make([]byte, infoSize)[0]))
    85  		err = windows.NtQuerySystemInformation(windows.SystemProcessInformation, unsafe.Pointer(parentProcess), infoSize, &infoSize)
    86  		if err == nil {
    87  			break
    88  		} else if err != windows.STATUS_INFO_LENGTH_MISMATCH {
    89  			return false, err
    90  		}
    91  	}
    92  	for ; ; parentProcess = (*windows.SYSTEM_PROCESS_INFORMATION)(unsafe.Pointer(uintptr(unsafe.Pointer(parentProcess)) + uintptr(parentProcess.NextEntryOffset))) {
    93  		if parentProcess.UniqueProcessID == currentProcess.InheritedFromUniqueProcessId {
    94  			return parentProcess.SessionID == 0 && strings.EqualFold("services.exe", parentProcess.ImageName.String()), nil
    95  		}
    96  		if parentProcess.NextEntryOffset == 0 {
    97  			break
    98  		}
    99  	}
   100  	return false, nil
   101  }