tailscale/util/winutil/winutil_windows_test.go
Aaron Klotz 2aa8299c37 cmd/tailscaled, util/winutil: log our registry keys during tailscaled startup
In order to improve our ability to understand the state of policies and
registry settings when troubleshooting, we enumerate all values in all subkeys.
x/sys/windows does not already offer this, so we need to call RegEnumValue
directly.

For now we're just logging this during startup, however in a future PR I plan to
also trigger this code during a bugreport. I also want to log more than just
registry.

Fixes #8141

Signed-off-by: Aaron Klotz <aaron@tailscale.com>
2023-06-01 13:39:17 -06:00

151 lines
3.9 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package winutil
import (
"errors"
"fmt"
"strings"
"testing"
"golang.org/x/exp/maps"
"golang.org/x/sys/windows/registry"
)
const (
localSystemSID = "S-1-5-18"
networkSID = "S-1-5-2"
)
func TestLookupPseudoUser(t *testing.T) {
localSystem, err := LookupPseudoUser(localSystemSID)
if err != nil {
t.Errorf("LookupPseudoUser(%q) error: %v", localSystemSID, err)
}
if localSystem.Gid != localSystemSID {
t.Errorf("incorrect Gid, got %q, want %q", localSystem.Gid, localSystemSID)
}
t.Logf("localSystem: %v", localSystem)
// networkSID is a built-in known group but not a pseudo-user.
_, err = LookupPseudoUser(networkSID)
if err == nil {
t.Errorf("LookupPseudoUser(%q) unexpectedly succeeded", networkSID)
}
}
func makeLongBinaryValue() []byte {
buf := make([]byte, maxBinaryValueLen*2)
for i, _ := range buf {
buf[i] = byte(i % 0xFF)
}
return buf
}
var testData = map[string]any{
"": "I am the default",
"StringEmpty": "",
"StringShort": "Hello",
"StringLong": strings.Repeat("7", initialValueBufLen+1),
"MultiStringEmpty": []string{},
"MultiStringSingle": []string{"Foo"},
"MultiStringSingleEmpty": []string{""},
"MultiString": []string{"Foo", "Bar", "Baz"},
"MultiStringWithEmptyBeginning": []string{"", "Foo", "Bar"},
"MultiStringWithEmptyMiddle": []string{"Foo", "", "Bar"},
"MultiStringWithEmptyEnd": []string{"Foo", "Bar", ""},
"DWord": uint32(0x12345678),
"QWord": uint64(0x123456789abcdef0),
"BinaryEmpty": []byte{},
"BinaryShort": []byte{0x01, 0x02, 0x03, 0x04},
"BinaryLong": makeLongBinaryValue(),
}
const (
keyNameTest = `SOFTWARE\Tailscale Test`
subKeyNameTest = "SubKey"
)
func setValues(t *testing.T, k registry.Key) {
for vk, v := range testData {
var err error
switch tv := v.(type) {
case string:
err = k.SetStringValue(vk, tv)
case []string:
err = k.SetStringsValue(vk, tv)
case uint32:
err = k.SetDWordValue(vk, tv)
case uint64:
err = k.SetQWordValue(vk, tv)
case []byte:
err = k.SetBinaryValue(vk, tv)
default:
t.Fatalf("Unknown type")
}
if err != nil {
t.Fatalf("Error setting %q: %v", vk, err)
}
}
}
func TestRegistrySupportInfo(t *testing.T) {
// Make sure the key doesn't exist yet
k, err := registry.OpenKey(registry.CURRENT_USER, keyNameTest, registry.READ)
switch {
case err == nil:
k.Close()
t.Fatalf("Test key already exists")
case !errors.Is(err, registry.ErrNotExist):
t.Fatal(err)
}
func() {
k, _, err := registry.CreateKey(registry.CURRENT_USER, keyNameTest, registry.WRITE)
if err != nil {
t.Fatalf("Error creating test key: %v", err)
}
defer k.Close()
setValues(t, k)
sk, _, err := registry.CreateKey(k, subKeyNameTest, registry.WRITE)
if err != nil {
t.Fatalf("Error creating test subkey: %v", err)
}
defer sk.Close()
setValues(t, sk)
}()
t.Cleanup(func() {
registry.DeleteKey(registry.CURRENT_USER, keyNameTest+"\\"+subKeyNameTest)
registry.DeleteKey(registry.CURRENT_USER, keyNameTest)
})
wantValuesData := maps.Clone(testData)
wantValuesData["BinaryLong"] = (wantValuesData["BinaryLong"].([]byte))[:maxBinaryValueLen]
wantKeyData := make(map[string]any)
maps.Copy(wantKeyData, wantValuesData)
wantSubKeyData := make(map[string]any)
maps.Copy(wantSubKeyData, wantValuesData)
wantKeyData[subKeyNameTest] = wantSubKeyData
wantData := map[string]any{
"HKCU\\" + keyNameTest: wantKeyData,
}
gotData, err := getRegistrySupportInfo(registry.CURRENT_USER, []string{keyNameTest})
if err != nil {
t.Errorf("getRegistrySupportInfo error: %v", err)
}
want, got := fmt.Sprintf("%#v", wantData), fmt.Sprintf("%#v", gotData)
if want != got {
t.Errorf("Compare error: want\n%s,\ngot %s", want, got)
}
}