github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/client/config/windows_registry_persistence_handler.go (about)

     1  // Copyright 2018 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 config
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"path/filepath"
    23  
    24  	log "github.com/golang/glog"
    25  	"google.golang.org/protobuf/encoding/prototext"
    26  	"google.golang.org/protobuf/proto"
    27  
    28  	clpb "github.com/google/fleetspeak/fleetspeak/src/client/proto/fleetspeak_client"
    29  	fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak"
    30  	"github.com/google/fleetspeak/fleetspeak/src/windows/regutil"
    31  )
    32  
    33  const (
    34  	communicatorValuename = "communicator"
    35  	writebackValuename    = "writeback"
    36  	signedServicesKeyname = "services"
    37  	textServicesKeyname   = "textservices"
    38  )
    39  
    40  // WindowsRegistryPersistenceHandler defines the Windows registry configuration storage strategy.
    41  type WindowsRegistryPersistenceHandler struct {
    42  	configurationPath string
    43  	readonly          bool
    44  }
    45  
    46  // NewWindowsRegistryPersistenceHandler initializes registry keys used to store
    47  // state and configuration info for Fleetspeak, and instantiates a WindowsRegistryPersistenceHandler.
    48  //
    49  // configurationPath is the registry key location to look for additional
    50  // configuration values. Possible values include:
    51  //
    52  // \communicator           - Path to a text-format clpb.CommunicatorConfig, used to tweak communicator behavior.
    53  // \writeback              - REG_BINARY value that is used to maintain state across restarts.
    54  // \services\<service>     - Path to a binary format SignedClientServiceConfig. One registry value for each configured service.
    55  // \textservices\<service> - Path to a text-format fspb.ClientServiceConfig. One registry value for each configured service.
    56  //
    57  // All of these values are optional, though Fleetspeak will not be particularly
    58  // useful without at least one configured service.
    59  //
    60  // If readonly is true, the client will not attempt to write to
    61  // <ConfigurationPath>\writeback, in order to preserve client identity.
    62  //
    63  // readonly is intended for testing and specialized applications - should be
    64  // hardcoded false in normal deployments.
    65  func NewWindowsRegistryPersistenceHandler(configurationPath string, readonly bool) (*WindowsRegistryPersistenceHandler, error) {
    66  	if err := regutil.VerifyPath(configurationPath); err != nil {
    67  		return nil, fmt.Errorf("invalid configuration path: %v", err)
    68  	}
    69  
    70  	signedServicesKey := filepath.Join(configurationPath, signedServicesKeyname)
    71  	textServicesKey := filepath.Join(configurationPath, textServicesKeyname)
    72  	if err := regutil.CreateKeyIfNotExist(signedServicesKey); err != nil {
    73  		return nil, err
    74  	}
    75  	if err := regutil.CreateKeyIfNotExist(textServicesKey); err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	return &WindowsRegistryPersistenceHandler{
    80  		configurationPath: configurationPath,
    81  		readonly:          readonly,
    82  	}, nil
    83  }
    84  
    85  // ReadState implements PersistenceHandler.
    86  func (h *WindowsRegistryPersistenceHandler) ReadState() (*clpb.ClientState, error) {
    87  	b, err := regutil.ReadBinaryValue(h.configurationPath, writebackValuename)
    88  	if err != nil {
    89  		if err == regutil.ErrValueNotExist {
    90  			// Clean state, writeback regvalue doesn't exist yet.
    91  			return nil, nil
    92  		}
    93  		return nil, fmt.Errorf("error while reading state from registry: %v", err)
    94  	}
    95  
    96  	ret := &clpb.ClientState{}
    97  	if err := proto.Unmarshal(b, ret); err != nil {
    98  		return nil, fmt.Errorf("unable to parse writeback registry value: %v", err)
    99  	}
   100  
   101  	return ret, nil
   102  }
   103  
   104  // WriteState implements PersistenceHandler.
   105  func (h *WindowsRegistryPersistenceHandler) WriteState(s *clpb.ClientState) error {
   106  	if h.readonly {
   107  		return nil
   108  	}
   109  
   110  	b, err := proto.Marshal(s)
   111  	if err != nil {
   112  		log.Fatalf("Unable to serialize writeback: %v", err)
   113  	}
   114  
   115  	if err := regutil.WriteBinaryValue(h.configurationPath, writebackValuename, b); err != nil {
   116  		return fmt.Errorf("unable to write new configuration: %v", err)
   117  	}
   118  
   119  	return nil
   120  }
   121  
   122  // ReadCommunicatorConfig implements PersistenceHandler.
   123  func (h *WindowsRegistryPersistenceHandler) ReadCommunicatorConfig() (*clpb.CommunicatorConfig, error) {
   124  	fpath, err := regutil.ReadStringValue(h.configurationPath, communicatorValuename)
   125  	if err != nil {
   126  		if err == regutil.ErrValueNotExist {
   127  			// No communicator config specified.
   128  			return nil, nil
   129  		}
   130  		return nil, fmt.Errorf("can't read communicator file path [%s -> %s]: %v", h.configurationPath, communicatorValuename, err)
   131  	}
   132  
   133  	fbytes, err := os.ReadFile(fpath)
   134  	if err != nil {
   135  		return nil, fmt.Errorf("can't read communicator config file [%s]: %v", fpath, err)
   136  	}
   137  
   138  	ret := &clpb.CommunicatorConfig{}
   139  	if err := prototext.Unmarshal(fbytes, ret); err != nil {
   140  		return nil, fmt.Errorf("can't parse communicator config file [%s]: %v", fpath, err)
   141  	}
   142  
   143  	return ret, nil
   144  }
   145  
   146  // ReadSignedServices implements PersistenceHandler.
   147  func (h *WindowsRegistryPersistenceHandler) ReadSignedServices() ([]*fspb.SignedClientServiceConfig, error) {
   148  	keyPath := filepath.Join(h.configurationPath, signedServicesKeyname)
   149  
   150  	regValues, err := regutil.Ls(keyPath)
   151  	if err != nil {
   152  		return nil, fmt.Errorf("unable to list values in signed services key path [%s]: %v", keyPath, err)
   153  	}
   154  
   155  	services := make([]*fspb.SignedClientServiceConfig, 0)
   156  
   157  	for _, regValue := range regValues {
   158  		fpath, err := regutil.ReadStringValue(keyPath, regValue)
   159  		if err != nil {
   160  			log.Errorf("Unable to read signed service registry value [%s -> %s], ignoring: %v", keyPath, regValue, err)
   161  			continue
   162  		}
   163  
   164  		fbytes, err := os.ReadFile(fpath)
   165  		if err != nil {
   166  			log.Errorf("Unable to read signed service file [%s], ignoring: %v", fpath, err)
   167  			continue
   168  		}
   169  
   170  		service := &fspb.SignedClientServiceConfig{}
   171  		if err := proto.Unmarshal(fbytes, service); err != nil {
   172  			log.Errorf("Unable to parse signed service registry file [%s], ignoring: %v", fpath, err)
   173  			continue
   174  		}
   175  
   176  		services = append(services, service)
   177  	}
   178  
   179  	return services, nil
   180  }
   181  
   182  // ReadServices implements PersistenceHandler.
   183  func (h *WindowsRegistryPersistenceHandler) ReadServices() ([]*fspb.ClientServiceConfig, error) {
   184  	keyPath := filepath.Join(h.configurationPath, textServicesKeyname)
   185  
   186  	regValues, err := regutil.Ls(keyPath)
   187  	if err != nil {
   188  		return nil, fmt.Errorf("unable to list values in services key path [%s]: %v", keyPath, err)
   189  	}
   190  
   191  	services := make([]*fspb.ClientServiceConfig, 0)
   192  
   193  	for _, regValue := range regValues {
   194  		fpath, err := regutil.ReadStringValue(keyPath, regValue)
   195  		if err != nil {
   196  			log.Errorf("Unable to read service registry value [%s -> %s], ignoring: %v", keyPath, regValue, err)
   197  			continue
   198  		}
   199  
   200  		fbytes, err := os.ReadFile(fpath)
   201  		if err != nil {
   202  			log.Errorf("Unable to read service file [%s], ignoring: %v", fpath, err)
   203  			continue
   204  		}
   205  
   206  		service := &fspb.ClientServiceConfig{}
   207  		if err := prototext.Unmarshal(fbytes, service); err != nil {
   208  			log.Errorf("Unable to parse service file [%s], ignoring: %v", fpath, err)
   209  			continue
   210  		}
   211  
   212  		services = append(services, service)
   213  	}
   214  
   215  	return services, nil
   216  }