github.com/tristanisham/sys@v0.0.0-20240326010300-a16cbabb7555/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  
     7  package svc
     8  
     9  import (
    10  	"strings"
    11  	"unsafe"
    12  
    13  	"golang.org/x/sys/windows"
    14  )
    15  
    16  func allocSid(subAuth0 uint32) (*windows.SID, error) {
    17  	var sid *windows.SID
    18  	err := windows.AllocateAndInitializeSid(&windows.SECURITY_NT_AUTHORITY,
    19  		1, subAuth0, 0, 0, 0, 0, 0, 0, 0, &sid)
    20  	if err != nil {
    21  		return nil, err
    22  	}
    23  	return sid, nil
    24  }
    25  
    26  // IsAnInteractiveSession determines if calling process is running interactively.
    27  // It queries the process token for membership in the Interactive group.
    28  // http://stackoverflow.com/questions/2668851/how-do-i-detect-that-my-application-is-running-as-service-or-in-an-interactive-s
    29  //
    30  // Deprecated: Use IsWindowsService instead.
    31  func IsAnInteractiveSession() (bool, error) {
    32  	interSid, err := allocSid(windows.SECURITY_INTERACTIVE_RID)
    33  	if err != nil {
    34  		return false, err
    35  	}
    36  	defer windows.FreeSid(interSid)
    37  
    38  	serviceSid, err := allocSid(windows.SECURITY_SERVICE_RID)
    39  	if err != nil {
    40  		return false, err
    41  	}
    42  	defer windows.FreeSid(serviceSid)
    43  
    44  	t, err := windows.OpenCurrentProcessToken()
    45  	if err != nil {
    46  		return false, err
    47  	}
    48  	defer t.Close()
    49  
    50  	gs, err := t.GetTokenGroups()
    51  	if err != nil {
    52  		return false, err
    53  	}
    54  
    55  	for _, g := range gs.AllGroups() {
    56  		if windows.EqualSid(g.Sid, interSid) {
    57  			return true, nil
    58  		}
    59  		if windows.EqualSid(g.Sid, serviceSid) {
    60  			return false, nil
    61  		}
    62  	}
    63  	return false, nil
    64  }
    65  
    66  // IsWindowsService reports whether the process is currently executing
    67  // as a Windows service.
    68  func IsWindowsService() (bool, error) {
    69  	// The below technique looks a bit hairy, but it's actually
    70  	// exactly what the .NET framework does for the similarly named function:
    71  	// https://github.com/dotnet/extensions/blob/f4066026ca06984b07e90e61a6390ac38152ba93/src/Hosting/WindowsServices/src/WindowsServiceHelpers.cs#L26-L31
    72  	// Specifically, it looks up whether the parent process has session ID zero
    73  	// and is called "services".
    74  
    75  	var currentProcess windows.PROCESS_BASIC_INFORMATION
    76  	infoSize := uint32(unsafe.Sizeof(currentProcess))
    77  	err := windows.NtQueryInformationProcess(windows.CurrentProcess(), windows.ProcessBasicInformation, unsafe.Pointer(&currentProcess), infoSize, &infoSize)
    78  	if err != nil {
    79  		return false, err
    80  	}
    81  	var parentProcess *windows.SYSTEM_PROCESS_INFORMATION
    82  	for infoSize = uint32((unsafe.Sizeof(*parentProcess) + unsafe.Sizeof(uintptr(0))) * 1024); ; {
    83  		parentProcess = (*windows.SYSTEM_PROCESS_INFORMATION)(unsafe.Pointer(&make([]byte, infoSize)[0]))
    84  		err = windows.NtQuerySystemInformation(windows.SystemProcessInformation, unsafe.Pointer(parentProcess), infoSize, &infoSize)
    85  		if err == nil {
    86  			break
    87  		} else if err != windows.STATUS_INFO_LENGTH_MISMATCH {
    88  			return false, err
    89  		}
    90  	}
    91  	for ; ; parentProcess = (*windows.SYSTEM_PROCESS_INFORMATION)(unsafe.Pointer(uintptr(unsafe.Pointer(parentProcess)) + uintptr(parentProcess.NextEntryOffset))) {
    92  		if parentProcess.UniqueProcessID == currentProcess.InheritedFromUniqueProcessId {
    93  			return parentProcess.SessionID == 0 && strings.EqualFold("services.exe", parentProcess.ImageName.String()), nil
    94  		}
    95  		if parentProcess.NextEntryOffset == 0 {
    96  			break
    97  		}
    98  	}
    99  	return false, nil
   100  }