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 }