go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/registrykey.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package resources 5 6 import ( 7 "errors" 8 "runtime" 9 "strings" 10 11 "go.mondoo.com/cnquery/llx" 12 "go.mondoo.com/cnquery/providers-sdk/v1/plugin" 13 "go.mondoo.com/cnquery/providers/os/resources/powershell" 14 "go.mondoo.com/cnquery/providers/os/resources/windows" 15 "go.mondoo.com/ranger-rpc/codes" 16 "go.mondoo.com/ranger-rpc/status" 17 ) 18 19 func (k *mqlRegistrykey) id() (string, error) { 20 return k.Path.Data, nil 21 } 22 23 func (k *mqlRegistrykey) exists() (bool, error) { 24 // if we are running locally on windows, we can use native api 25 if runtime.GOOS == "windows" { 26 items, err := windows.GetNativeRegistryKeyItems(k.Path.Data) 27 if err == nil && len(items) > 0 { 28 return true, nil 29 } 30 std, ok := status.FromError(err) 31 if ok && std.Code() == codes.NotFound { 32 return false, nil 33 } 34 if err != nil { 35 return false, err 36 } 37 } 38 39 script := powershell.Encode(windows.GetRegistryKeyItemScript(k.Path.Data)) 40 o, err := CreateResource(k.MqlRuntime, "command", map[string]*llx.RawData{ 41 "command": llx.StringData(script), 42 }) 43 if err != nil { 44 return false, err 45 } 46 cmd := o.(*mqlCommand) 47 48 exit := cmd.GetExitcode() 49 if exit.Error != nil { 50 return false, exit.Error 51 } 52 if exit.Data != 0 { 53 stderr := cmd.GetStderr() 54 // this would be an expected error and would ensure that we do not throw an error on windows systems 55 // TODO: revisit how this is handled for non-english systems 56 if err == nil && (strings.Contains(stderr.Data, "not exist") || strings.Contains(stderr.Data, "ObjectNotFound")) { 57 return false, nil 58 } 59 60 return false, errors.New("could not retrieve registry key") 61 } 62 return true, nil 63 } 64 65 // GetEntries returns a list of registry key property resources 66 func (k *mqlRegistrykey) getEntries() ([]windows.RegistryKeyItem, error) { 67 // if we are running locally on windows, we can use native api 68 if runtime.GOOS == "windows" { 69 return windows.GetNativeRegistryKeyItems(k.Path.Data) 70 } 71 72 // parse the output of the powershell script 73 script := powershell.Encode(windows.GetRegistryKeyItemScript(k.Path.Data)) 74 o, err := CreateResource(k.MqlRuntime, "command", map[string]*llx.RawData{ 75 "command": llx.StringData(script), 76 }) 77 if err != nil { 78 return nil, err 79 } 80 cmd := o.(*mqlCommand) 81 exit := cmd.GetExitcode() 82 if exit.Error != nil { 83 return nil, exit.Error 84 } 85 if exit.Data != 0 { 86 return nil, errors.New("could not retrieve registry key") 87 } 88 89 stdout := cmd.GetStdout() 90 if stdout.Error != nil { 91 return nil, stdout.Error 92 } 93 94 return windows.ParsePowershellRegistryKeyItems(strings.NewReader(stdout.Data)) 95 } 96 97 // Deprecated: properties returns the properties of a registry key 98 // This function is deprecated and will be removed in a future release 99 func (k *mqlRegistrykey) properties() (map[string]interface{}, error) { 100 res := map[string]interface{}{} 101 102 entries, err := k.getEntries() 103 if err != nil { 104 return nil, err 105 } 106 107 for i := range entries { 108 rkey := entries[i] 109 res[rkey.Key] = rkey.String() 110 } 111 112 return res, nil 113 } 114 115 // items returns a list of registry key property resources 116 func (k *mqlRegistrykey) items() ([]interface{}, error) { 117 entries, err := k.getEntries() 118 if err != nil { 119 return nil, err 120 } 121 122 // create MQL mount entry resources for each mount 123 items := make([]interface{}, len(entries)) 124 for i, entry := range entries { 125 o, err := CreateResource(k.MqlRuntime, "registrykey.property", map[string]*llx.RawData{ 126 "path": llx.StringData(k.Path.Data), 127 "name": llx.StringData(entry.Key), 128 "value": llx.StringData(entry.String()), 129 "type": llx.StringData(entry.Kind()), 130 "data": llx.DictData(entry.GetRawValue()), 131 "exists": llx.BoolData(true), 132 }) 133 if err != nil { 134 return nil, err 135 } 136 137 items[i] = o.(*mqlRegistrykeyProperty) 138 } 139 140 return items, nil 141 } 142 143 func (k *mqlRegistrykey) children() ([]interface{}, error) { 144 res := []interface{}{} 145 146 var children []windows.RegistryKeyChild 147 if runtime.GOOS == "windows" { 148 var err error 149 children, err = windows.GetNativeRegistryKeyChildren(k.Path.Data) 150 if err != nil { 151 return nil, err 152 } 153 } else { 154 // parse powershell script 155 script := powershell.Encode(windows.GetRegistryKeyChildItemsScript(k.Path.Data)) 156 o, err := CreateResource(k.MqlRuntime, "command", map[string]*llx.RawData{ 157 "command": llx.StringData(script), 158 }) 159 if err != nil { 160 return res, err 161 } 162 cmd := o.(*mqlCommand) 163 exitcode := cmd.GetExitcode() 164 if exitcode.Error != nil { 165 return nil, exitcode.Error 166 } 167 if exitcode.Data != 0 { 168 return nil, errors.New("could not retrieve registry key") 169 } 170 171 stdout := cmd.GetStdout() 172 if stdout.Error != nil { 173 return res, stdout.Error 174 } 175 children, err = windows.ParsePowershellRegistryKeyChildren(strings.NewReader(stdout.Data)) 176 if err != nil { 177 return nil, err 178 } 179 } 180 181 for i := range children { 182 child := children[i] 183 res = append(res, child.Path) 184 } 185 186 return res, nil 187 } 188 189 func (p *mqlRegistrykeyProperty) id() (string, error) { 190 return p.Path.Data + " - " + p.Name.Data, nil 191 } 192 193 func initRegistrykeyProperty(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) { 194 path := args["path"] 195 if path == nil { 196 return args, nil, nil 197 } 198 199 name := args["name"] 200 if name == nil { 201 return args, nil, nil 202 } 203 204 // if the data is set, we do not need to fetch the data first 205 dataRaw := args["data"] 206 if dataRaw != nil { 207 return args, nil, nil 208 } 209 210 // create resource here, but do not use it yet 211 obj, err := CreateResource(runtime, "registrykey", map[string]*llx.RawData{ 212 "path": path, 213 }) 214 if err != nil { 215 return nil, nil, err 216 } 217 key := obj.(*mqlRegistrykey) 218 219 exists := key.GetExists() 220 if err != nil { 221 return nil, nil, err 222 } 223 224 // set default values 225 args["exists"] = llx.BoolFalse 226 // NOTE: we do not set a value here so that MQL throws an error when a user try to gather the data for a 227 // non-existing key 228 229 // path exists 230 if exists.Data { 231 items := key.GetItems() 232 if items.Error != nil { 233 return nil, nil, items.Error 234 } 235 236 for i := range items.Data { 237 property := items.Data[i].(*mqlRegistrykeyProperty) 238 iname := property.GetName() 239 if iname.Error != nil { 240 return nil, nil, iname.Error 241 } 242 243 // property exists, return it 244 if strings.EqualFold(iname.Data, name.Value.(string)) { 245 return nil, property, nil 246 } 247 } 248 } 249 return args, nil, nil 250 } 251 252 func (p *mqlRegistrykeyProperty) exists() (bool, error) { 253 // NOTE: will not be called since it will always be set in init 254 return false, errors.New("could not determine if the property exists") 255 } 256 257 func (p *mqlRegistrykeyProperty) compute_type() (string, error) { 258 // NOTE: if we reach here the value has not been set in init, therefore we return an error 259 return "", errors.New("requested property does not exist") 260 } 261 262 func (p *mqlRegistrykeyProperty) data() (interface{}, error) { 263 // NOTE: if we reach here the value has not been set in init, therefore we return an error 264 return "", errors.New("requested property does not exist") 265 } 266 267 func (p *mqlRegistrykeyProperty) value() (string, error) { 268 // NOTE: if we reach here the value has not been set in init, therefore we return an error 269 return "", errors.New("requested property does not exist") 270 }