1// Copyright 2017 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
7package windows_test
8
9import (
10	"fmt"
11	"internal/syscall/windows"
12	"os"
13	"os/exec"
14	"syscall"
15	"testing"
16	"unsafe"
17)
18
19func TestRunAtLowIntegrity(t *testing.T) {
20	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
21		wil, err := getProcessIntegrityLevel()
22		if err != nil {
23			fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
24			os.Exit(9)
25			return
26		}
27		fmt.Printf("%s", wil)
28		os.Exit(0)
29		return
30	}
31
32	cmd := exec.Command(os.Args[0], "-test.run=TestRunAtLowIntegrity", "--")
33	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
34
35	token, err := getIntegrityLevelToken(sidWilLow)
36	if err != nil {
37		t.Fatal(err)
38	}
39	defer token.Close()
40
41	cmd.SysProcAttr = &syscall.SysProcAttr{
42		Token: token,
43	}
44
45	out, err := cmd.CombinedOutput()
46	if err != nil {
47		t.Fatal(err)
48	}
49
50	if string(out) != sidWilLow {
51		t.Fatalf("Child process did not run as low integrity level: %s", string(out))
52	}
53}
54
55const (
56	sidWilLow = `S-1-16-4096`
57)
58
59func getProcessIntegrityLevel() (string, error) {
60	procToken, err := syscall.OpenCurrentProcessToken()
61	if err != nil {
62		return "", err
63	}
64	defer procToken.Close()
65
66	p, err := tokenGetInfo(procToken, syscall.TokenIntegrityLevel, 64)
67	if err != nil {
68		return "", err
69	}
70
71	tml := (*windows.TOKEN_MANDATORY_LABEL)(p)
72
73	sid := (*syscall.SID)(unsafe.Pointer(tml.Label.Sid))
74
75	return sid.String()
76}
77
78func tokenGetInfo(t syscall.Token, class uint32, initSize int) (unsafe.Pointer, error) {
79	n := uint32(initSize)
80	for {
81		b := make([]byte, n)
82		e := syscall.GetTokenInformation(t, class, &b[0], uint32(len(b)), &n)
83		if e == nil {
84			return unsafe.Pointer(&b[0]), nil
85		}
86		if e != syscall.ERROR_INSUFFICIENT_BUFFER {
87			return nil, e
88		}
89		if n <= uint32(len(b)) {
90			return nil, e
91		}
92	}
93}
94
95func getIntegrityLevelToken(wns string) (syscall.Token, error) {
96	var procToken, token syscall.Token
97
98	proc, err := syscall.GetCurrentProcess()
99	if err != nil {
100		return 0, err
101	}
102	defer syscall.CloseHandle(proc)
103
104	err = syscall.OpenProcessToken(proc,
105		syscall.TOKEN_DUPLICATE|
106			syscall.TOKEN_ADJUST_DEFAULT|
107			syscall.TOKEN_QUERY|
108			syscall.TOKEN_ASSIGN_PRIMARY,
109		&procToken)
110	if err != nil {
111		return 0, err
112	}
113	defer procToken.Close()
114
115	sid, err := syscall.StringToSid(wns)
116	if err != nil {
117		return 0, err
118	}
119
120	tml := &windows.TOKEN_MANDATORY_LABEL{}
121	tml.Label.Attributes = windows.SE_GROUP_INTEGRITY
122	tml.Label.Sid = sid
123
124	err = windows.DuplicateTokenEx(procToken, 0, nil, windows.SecurityImpersonation,
125		windows.TokenPrimary, &token)
126	if err != nil {
127		return 0, err
128	}
129
130	err = windows.SetTokenInformation(token,
131		syscall.TokenIntegrityLevel,
132		uintptr(unsafe.Pointer(tml)),
133		tml.Size())
134	if err != nil {
135		token.Close()
136		return 0, err
137	}
138	return token, nil
139}
140