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  }