go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/windows/registrykey_windows.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 //go:build windows 5 // +build windows 6 7 package windows 8 9 import ( 10 "errors" 11 "strconv" 12 "strings" 13 14 "github.com/rs/zerolog/log" 15 "go.mondoo.com/ranger-rpc/codes" 16 "go.mondoo.com/ranger-rpc/status" 17 "golang.org/x/sys/windows/registry" 18 ) 19 20 // parseRegistryKeyPath parses a registry key path into the hive and the path 21 // https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-hives 22 func parseRegistryKeyPath(path string) (registry.Key, string, error) { 23 if strings.HasPrefix(path, "HKEY_LOCAL_MACHINE") { 24 return registry.LOCAL_MACHINE, strings.TrimPrefix(path, "HKEY_LOCAL_MACHINE\\"), nil 25 } 26 if strings.HasPrefix(path, "HKLM") { 27 return registry.LOCAL_MACHINE, strings.TrimPrefix(path, "HKLM\\"), nil 28 } 29 30 if strings.HasPrefix(path, "HKEY_CURRENT_USER") { 31 return registry.CURRENT_USER, strings.TrimPrefix(path, "HKEY_CURRENT_USER\\"), nil 32 } 33 34 if strings.HasPrefix(path, "HKCU") { 35 return registry.CURRENT_USER, strings.TrimPrefix(path, "HKCU\\"), nil 36 } 37 38 if strings.HasPrefix(path, "HKEY_USERS") { 39 return registry.USERS, strings.TrimPrefix(path, "HKEY_USERS\\"), nil 40 } 41 42 return registry.LOCAL_MACHINE, "", errors.New("invalid registry key hive: " + path) 43 } 44 45 func GetNativeRegistryKeyItems(path string) ([]RegistryKeyItem, error) { 46 log.Debug().Str("path", path).Msg("search registry key values using native registry api") 47 key, path, err := parseRegistryKeyPath(path) 48 if err != nil { 49 return nil, err 50 } 51 regKey, err := registry.OpenKey(key, path, registry.ENUMERATE_SUB_KEYS|registry.QUERY_VALUE) 52 if err != nil && registry.ErrNotExist == err { 53 return nil, status.Error(codes.NotFound, "registry key not found: "+path) 54 } else if err != nil { 55 return nil, err 56 } 57 defer regKey.Close() 58 59 res := []RegistryKeyItem{} 60 values, err := regKey.ReadValueNames(0) 61 if err != nil { 62 return nil, err 63 } 64 for _, value := range values { 65 stringValue, valtype, err := regKey.GetStringValue(value) 66 if err != registry.ErrUnexpectedType && err != nil { 67 return nil, err 68 } 69 70 regValue := RegistryKeyValue{ 71 Kind: int(valtype), 72 String: stringValue, 73 } 74 75 switch valtype { 76 case registry.SZ, registry.EXPAND_SZ: 77 // covered by GetStringValue, nothing to do 78 case registry.BINARY: 79 binaryValue, _, err := regKey.GetBinaryValue(value) 80 if err != nil { 81 return nil, err 82 } 83 regValue.Binary = binaryValue 84 case registry.DWORD: 85 fallthrough 86 case registry.QWORD: 87 intVal, _, err := regKey.GetIntegerValue(value) 88 if err != nil { 89 return nil, err 90 } 91 regValue.Number = int64(intVal) 92 regValue.String = strconv.FormatInt(int64(intVal), 10) 93 case registry.MULTI_SZ: 94 entries, _, err := regKey.GetStringsValue(value) 95 if err != nil { 96 return nil, err 97 } 98 regValue.MultiString = entries 99 if len(entries) > 0 { 100 // NOTE: this is to be consistent with the output before we moved to multi-datatype support for registry keys 101 regValue.String = strings.Join(entries, " ") 102 } 103 case registry.DWORD_BIG_ENDIAN, registry.LINK, registry.RESOURCE_LIST, registry.FULL_RESOURCE_DESCRIPTOR, registry.RESOURCE_REQUIREMENTS_LIST: 104 // not supported by golang.org/x/sys/windows/registry 105 } 106 res = append(res, RegistryKeyItem{ 107 Key: value, 108 Value: regValue, 109 }) 110 } 111 return res, nil 112 } 113 114 func GetNativeRegistryKeyChildren(path string) ([]RegistryKeyChild, error) { 115 log.Debug().Str("path", path).Msg("search registry key children using native registry api") 116 key, path, err := parseRegistryKeyPath(path) 117 if err != nil { 118 return nil, err 119 } 120 121 regKey, err := registry.OpenKey(key, path, registry.ENUMERATE_SUB_KEYS|registry.QUERY_VALUE) 122 if err != nil && registry.ErrNotExist == err { 123 return nil, status.Error(codes.NotFound, "registry key not found: "+path) 124 } else if err != nil { 125 return nil, err 126 } 127 defer regKey.Close() 128 129 // reads all child keys 130 entries, err := regKey.ReadSubKeyNames(0) 131 if err != nil { 132 return nil, err 133 } 134 135 res := make([]RegistryKeyChild, len(entries)) 136 137 for i, entry := range entries { 138 res[i] = RegistryKeyChild{ 139 Path: path, 140 Name: entry, 141 } 142 } 143 144 return res, nil 145 }