github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/windows/regutil/regutil_windows.go (about)

     1  // Copyright 2017 Google Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  //go:build windows
    16  
    17  // Package regutil contains utility functions for Windows registry handling.
    18  package regutil
    19  
    20  import (
    21  	"errors"
    22  	"fmt"
    23  	"path/filepath"
    24  	"strings"
    25  
    26  	"golang.org/x/sys/windows/registry"
    27  )
    28  
    29  const hkeyPrefix = `HKEY_LOCAL_MACHINE\`
    30  
    31  // ErrKeyNotExist is returned if an attempt is made to access a key that does not exist.
    32  var ErrKeyNotExist = errors.New("regutil: registry key does not exist")
    33  
    34  // ErrValueNotExist is returned if an attempt is made to read a value that a key does not have.
    35  var ErrValueNotExist = errors.New("regutil: registry value does not exist")
    36  
    37  // VerifyPath performs basic verification on registry paths.
    38  func VerifyPath(configurationPath string) error {
    39  	if !strings.HasPrefix(configurationPath, hkeyPrefix) {
    40  		return fmt.Errorf("invalid path [%s], only registry paths starting with %s are supported", configurationPath, hkeyPrefix)
    41  	}
    42  
    43  	return nil
    44  }
    45  
    46  func stripHKey(keypath string) (string, error) {
    47  	if err := VerifyPath(keypath); err != nil {
    48  		return "", err
    49  	}
    50  
    51  	return filepath.Clean(keypath[len(hkeyPrefix):]), nil
    52  }
    53  
    54  // Thin wrapper over registry.OpenKey() with better error-reporting.
    55  func openKey(keypath string, access uint32) (registry.Key, error) {
    56  	k, err := registry.OpenKey(registry.LOCAL_MACHINE, keypath, access)
    57  	if err != nil {
    58  		if err == registry.ErrNotExist {
    59  			return k, ErrKeyNotExist
    60  		}
    61  		return k, fmt.Errorf("unable to open registry keypath [%s]: %v", keypath, err)
    62  	}
    63  	return k, nil
    64  }
    65  
    66  // CreateKeyIfNotExist checks if a registry key exists, and if it does not, creates it (along with all its ancestors).
    67  func CreateKeyIfNotExist(keypath string) error {
    68  	p, err := stripHKey(keypath)
    69  	if err != nil {
    70  		return err
    71  	}
    72  
    73  	key, _, err := registry.CreateKey(registry.LOCAL_MACHINE, p, registry.QUERY_VALUE)
    74  	if err != nil {
    75  		return err
    76  	}
    77  	defer key.Close()
    78  	return nil
    79  }
    80  
    81  // WriteBinaryValue writes a REG_BINARY value to the given registry path.
    82  func WriteBinaryValue(keypath, valuename string, content []byte) error {
    83  	p, err := stripHKey(keypath)
    84  	if err != nil {
    85  		return err
    86  	}
    87  
    88  	k, err := openKey(p, registry.SET_VALUE)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	defer k.Close()
    93  
    94  	err = k.SetBinaryValue(valuename, content)
    95  	if err != nil {
    96  		return fmt.Errorf("unable to write binary (REG_BINARY) registry value [%s -> %s]: %v", keypath, valuename, err)
    97  	}
    98  
    99  	return nil
   100  }
   101  
   102  // ReadBinaryValue reads a REG_BINARY value from the given registry path.
   103  func ReadBinaryValue(keypath, valuename string) ([]byte, error) {
   104  	p, err := stripHKey(keypath)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	k, err := openKey(p, registry.QUERY_VALUE)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	defer k.Close()
   114  
   115  	s, _, err := k.GetBinaryValue(valuename)
   116  	if err != nil {
   117  		if err == registry.ErrNotExist {
   118  			return nil, ErrValueNotExist
   119  		}
   120  		return nil, fmt.Errorf("unable to read binary (REG_BINARY) registry value [%s -> %s]: %v", p, valuename, err)
   121  	}
   122  
   123  	return s, nil
   124  }
   125  
   126  // WriteStringValue writes a REG_SZ value to the given registry path.
   127  func WriteStringValue(keypath, valuename string, content string) error {
   128  	p, err := stripHKey(keypath)
   129  	if err != nil {
   130  		return err
   131  	}
   132  
   133  	k, err := openKey(p, registry.SET_VALUE)
   134  	if err != nil {
   135  		return err
   136  	}
   137  	defer k.Close()
   138  
   139  	err = k.SetStringValue(valuename, content)
   140  	if err != nil {
   141  		return fmt.Errorf("unable to write string (REG_SZ) registry value [%s -> %s]: %v", keypath, valuename, err)
   142  	}
   143  
   144  	return nil
   145  }
   146  
   147  // ReadStringValue reads a REG_SZ value from the given registry path.
   148  func ReadStringValue(keypath, valuename string) (string, error) {
   149  	p, err := stripHKey(keypath)
   150  	if err != nil {
   151  		return "", err
   152  	}
   153  
   154  	k, err := openKey(p, registry.QUERY_VALUE)
   155  	if err != nil {
   156  		return "", err
   157  	}
   158  	defer k.Close()
   159  
   160  	s, _, err := k.GetStringValue(valuename)
   161  	if err != nil {
   162  		if err == registry.ErrNotExist {
   163  			return "", ErrValueNotExist
   164  		}
   165  		return "", fmt.Errorf("unable to read string (REG_SZ) registry value [%s -> %s]: %v", p, valuename, err)
   166  	}
   167  
   168  	return s, nil
   169  }
   170  
   171  // ReadMultiStringValue reads a REG_MULTI_SZ value from the given registry path.
   172  func ReadMultiStringValue(keypath, valuename string) ([]string, error) {
   173  	p, err := stripHKey(keypath)
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  
   178  	k, err := openKey(p, registry.QUERY_VALUE)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	defer k.Close()
   183  
   184  	vs, _, err := k.GetStringsValue(valuename)
   185  	if err != nil {
   186  		if err == registry.ErrNotExist {
   187  			return nil, ErrValueNotExist
   188  		}
   189  		return nil, fmt.Errorf("unable to read multi-string (REG_MULTI_SZ) registry value [%s -> %s]: %v", p, valuename, err)
   190  	}
   191  
   192  	return vs, nil
   193  }
   194  
   195  // Ls returns all the value names of the given key.
   196  func Ls(keypath string) ([]string, error) {
   197  	p, err := stripHKey(keypath)
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  
   202  	k, err := openKey(p, registry.QUERY_VALUE)
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  	defer k.Close()
   207  
   208  	vs, err := k.ReadValueNames(0)
   209  	if err != nil {
   210  		return nil, fmt.Errorf("unable to list values in key path [%s]: %v", p, err)
   211  	}
   212  
   213  	return vs, nil
   214  }