File: security.go

package info (click to toggle)
kubernetes 1.20.5%2Breally1.20.2-1.1
  • links: PTS
  • area: main
  • in suites: bookworm
  • size: 268,428 kB
  • sloc: sh: 29,129; asm: 11,883; makefile: 1,770; ansic: 995; sed: 440; python: 405
file content (157 lines) | stat: -rw-r--r-- 4,518 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build windows

package svc

import (
	"errors"
	"syscall"
	"unsafe"

	"golang.org/x/sys/windows"
)

func allocSid(subAuth0 uint32) (*windows.SID, error) {
	var sid *windows.SID
	err := windows.AllocateAndInitializeSid(&windows.SECURITY_NT_AUTHORITY,
		1, subAuth0, 0, 0, 0, 0, 0, 0, 0, &sid)
	if err != nil {
		return nil, err
	}
	return sid, nil
}

// IsAnInteractiveSession determines if calling process is running interactively.
// It queries the process token for membership in the Interactive group.
// http://stackoverflow.com/questions/2668851/how-do-i-detect-that-my-application-is-running-as-service-or-in-an-interactive-s
//
// Deprecated: Use IsWindowsService instead.
func IsAnInteractiveSession() (bool, error) {
	interSid, err := allocSid(windows.SECURITY_INTERACTIVE_RID)
	if err != nil {
		return false, err
	}
	defer windows.FreeSid(interSid)

	serviceSid, err := allocSid(windows.SECURITY_SERVICE_RID)
	if err != nil {
		return false, err
	}
	defer windows.FreeSid(serviceSid)

	t, err := windows.OpenCurrentProcessToken()
	if err != nil {
		return false, err
	}
	defer t.Close()

	gs, err := t.GetTokenGroups()
	if err != nil {
		return false, err
	}

	for _, g := range gs.AllGroups() {
		if windows.EqualSid(g.Sid, interSid) {
			return true, nil
		}
		if windows.EqualSid(g.Sid, serviceSid) {
			return false, nil
		}
	}
	return false, nil
}

var (
	ntdll                      = windows.NewLazySystemDLL("ntdll.dll")
	_NtQueryInformationProcess = ntdll.NewProc("NtQueryInformationProcess")

	kernel32                    = windows.NewLazySystemDLL("kernel32.dll")
	_QueryFullProcessImageNameA = kernel32.NewProc("QueryFullProcessImageNameA")
)

// IsWindowsService reports whether the process is currently executing
// as a Windows service.
func IsWindowsService() (bool, error) {
	// This code was copied from runtime.isWindowsService function.

	// The below technique looks a bit hairy, but it's actually
	// exactly what the .NET framework does for the similarly named function:
	// https://github.com/dotnet/extensions/blob/f4066026ca06984b07e90e61a6390ac38152ba93/src/Hosting/WindowsServices/src/WindowsServiceHelpers.cs#L26-L31
	// Specifically, it looks up whether the parent process has session ID zero
	// and is called "services".
	const _CURRENT_PROCESS = ^uintptr(0)
	// pbi is a PROCESS_BASIC_INFORMATION struct, where we just care about
	// the 6th pointer inside of it, which contains the pid of the process
	// parent:
	// https://github.com/wine-mirror/wine/blob/42cb7d2ad1caba08de235e6319b9967296b5d554/include/winternl.h#L1294
	var pbi [6]uintptr
	var pbiLen uint32
	r0, _, _ := syscall.Syscall6(_NtQueryInformationProcess.Addr(), 5, _CURRENT_PROCESS, 0, uintptr(unsafe.Pointer(&pbi[0])), uintptr(unsafe.Sizeof(pbi)), uintptr(unsafe.Pointer(&pbiLen)), 0)
	if r0 != 0 {
		return false, errors.New("NtQueryInformationProcess failed: error=" + itoa(int(r0)))
	}
	var psid uint32
	err := windows.ProcessIdToSessionId(uint32(pbi[5]), &psid)
	if err != nil {
		return false, err
	}
	if psid != 0 {
		// parent session id should be 0 for service process
		return false, nil
	}

	pproc, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pbi[5]))
	if err != nil {
		return false, err
	}
	defer windows.CloseHandle(pproc)

	// exeName gets the path to the executable image of the parent process
	var exeName [261]byte
	exeNameLen := uint32(len(exeName) - 1)
	r0, _, e0 := syscall.Syscall6(_QueryFullProcessImageNameA.Addr(), 4, uintptr(pproc), 0, uintptr(unsafe.Pointer(&exeName[0])), uintptr(unsafe.Pointer(&exeNameLen)), 0, 0)
	if r0 == 0 {
		if e0 != 0 {
			return false, e0
		} else {
			return false, syscall.EINVAL
		}
	}
	const (
		servicesLower = "services.exe"
		servicesUpper = "SERVICES.EXE"
	)
	i := int(exeNameLen) - 1
	j := len(servicesLower) - 1
	if i < j {
		return false, nil
	}
	for {
		if j == -1 {
			return i == -1 || exeName[i] == '\\', nil
		}
		if exeName[i] != servicesLower[j] && exeName[i] != servicesUpper[j] {
			return false, nil
		}
		i--
		j--
	}
}

func itoa(val int) string { // do it here rather than with fmt to avoid dependency
	if val < 0 {
		return "-" + itoa(-val)
	}
	var buf [32]byte // big enough for int64
	i := len(buf) - 1
	for val >= 10 {
		buf[i] = byte(val%10 + '0')
		i--
		val /= 10
	}
	buf[i] = byte(val + '0')
	return string(buf[i:])
}