# Author:: Prajakta Purohit () # Author:: Lamont Granquist () # # Copyright:: Copyright (c) Chef Software Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require_relative "../resource" require_relative "../digester" require "chef-utils/dist" unless defined?(ChefUtils::Dist) class Chef class Resource class RegistryKey < Chef::Resource unified_mode true provides(:registry_key) { true } description "Use the **registry_key** resource to create and delete registry keys in Microsoft Windows. Note: 64-bit versions of Microsoft Windows have a 32-bit compatibility layer in the registry that reflects and redirects certain keys (and their values) into specific locations (or logical views) of the registry hive.\n\n#{ChefUtils::Dist::Infra::PRODUCT} can access any reflected or redirected registry key. The machine architecture of the system on which #{ChefUtils::Dist::Infra::PRODUCT} is running is used as the default (non-redirected) location. Access to the SysWow64 location is redirected must be specified. Typically, this is only necessary to ensure compatibility with 32-bit applications that are running on a 64-bit operating system.\n\nFor more information, see: [Registry Reflection](https://docs.microsoft.com/en-us/windows/win32/winprog64/registry-reflection)." examples <<~'DOC' **Create a registry key** ```ruby registry_key 'HKEY_LOCAL_MACHINE\\path-to-key\\Policies\\System' do values [{ name: 'EnableLUA', type: :dword, data: 0 }] action :create end ``` **Create a registry key with binary data: "\x01\x02\x03"**: ```ruby registry_key 'HKEY_CURRENT_USER\ChefTest' do values [{ :name => "test", :type => :binary, :data => [0, 1, 2].map(&:chr).join }] action :create end ``` **Create 32-bit key in redirected wow6432 tree** In 64-bit versions of Microsoft Windows, HKEY_LOCAL_MACHINE\SOFTWARE\Example is a re-directed key. In the following examples, because HKEY_LOCAL_MACHINE\SOFTWARE\Example is a 32-bit key, the output will be “Found 32-bit key” if they are run on a version of Microsoft Windows that is 64-bit: ```ruby registry_key 'HKEY_LOCAL_MACHINE\SOFTWARE\Example' do architecture :i386 recursive true action :create end ``` **Set proxy settings to be the same as those used by #{ChefUtils::Dist::Infra::PRODUCT}** ```ruby proxy = URI.parse(Chef::Config[:http_proxy]) registry_key 'HKCU\Software\Microsoft\path\to\key\Internet Settings' do values [{name: 'ProxyEnable', type: :reg_dword, data: 1}, {name: 'ProxyServer', data: "#{proxy.host}:#{proxy.port}"}, {name: 'ProxyOverride', type: :reg_string, data: }, ] action :create end ``` **Set the name of a registry key to "(Default)"** ```ruby registry_key 'Set (Default) value' do key 'HKLM\Software\Test\Key\Path' values [ {name: '', type: :string, data: 'test'}, ] action :create end ``` **Delete a registry key value** ```ruby registry_key 'HKEY_LOCAL_MACHINE\SOFTWARE\path\to\key\AU' do values [{ name: 'NoAutoRebootWithLoggedOnUsers', type: :dword, data: '' }] action :delete end ``` Note: If data: is not specified, you get an error: Missing data key in RegistryKey values hash **Delete a registry key and its subkeys, recursively** ```ruby registry_key 'HKCU\SOFTWARE\Policies\path\to\key\Themes' do recursive true action :delete_key end ``` Note: Be careful when using the :delete_key action with the recursive attribute. This will delete the registry key, all of its values and all of the names, types, and data associated with them. This cannot be undone by #{ChefUtils::Dist::Infra::PRODUCT}. DOC default_action :create allowed_actions :create, :create_if_missing, :delete, :delete_key VALID_VALUE_HASH_KEYS = %i{name type data}.freeze property :key, String, name_property: true property :values, [Hash, Array], default: [], coerce: proc { |v| @unscrubbed_values = case v when Hash [ Mash.new(v).symbolize_keys ] when Array v.map { |value| Mash.new(value).symbolize_keys } else raise ArgumentError, "Bad type for RegistryKey resource, use Hash or Array" end scrub_values(@unscrubbed_values) }, callbacks: { "Missing name key in RegistryKey values hash" => lambda { |v| v.all? { |value| value.key?(:name) } }, "Bad key in RegistryKey values hash. Should be one of: #{VALID_VALUE_HASH_KEYS}" => lambda do |v| v.all? do |value| value.keys.all? { |key| VALID_VALUE_HASH_KEYS.include?(key) } end end, "Type of name should be a string" => lambda { |v| v.all? { |value| value[:name].is_a?(String) } }, "Type of type should be a symbol" => lambda { |v| v.all? { |value| value[:type] ? value[:type].is_a?(Symbol) : true } }, } property :recursive, [TrueClass, FalseClass], default: false property :architecture, Symbol, default: :machine, equal_to: %i{machine x86_64 i386} # Some registry key data types may not be safely reported as json. # Example (CHEF-5323): # # registry_key 'HKEY_CURRENT_USER\\ChefTest2014' do # values [{ # :name => "ValueWithBadData", # :type => :binary, # :data => 255.chr * 1 # }] # action :create # end # # will raise Encoding::UndefinedConversionError: "\xFF" from ASCII-8BIT to UTF-8. # # To avoid sending data that cannot be nicely converted for json, we have # the values method return "safe" data if the data type is "unsafe". Known "unsafe" # data types are :binary, :dword, :dword-big-endian, and :qword. If other # criteria generate data that cannot reliably be sent as json, add that criteria # to the needs_checksum? method. When unsafe data is detected, the values method # returns an md5 checksum of the listed data. # # :unscrubbed_values returns the values exactly as provided in the resource (i.e., # data is not checksummed, regardless of the data type/"unsafe" criteria). # # Future: # If we have conflicts with other resources reporting json incompatible state, we # may want to extend the state_attrs API with the ability to rename POST'd attrs. # # See lib/chef/resource_reporter.rb for more information. def unscrubbed_values @unscrubbed_values ||= [] end private def scrub_values(values) scrubbed = [] values.each do |value| scrubbed_value = value.dup if needs_checksum?(scrubbed_value) data_io = StringIO.new(scrubbed_value[:data].to_s) scrubbed_value[:data] = Chef::Digester.instance.generate_checksum(data_io) end scrubbed << scrubbed_value end scrubbed end # Some data types may raise errors when sent as json. Returns true if this # value's data may need to be converted to a checksum. def needs_checksum?(value) unsafe_types = %i{binary dword dword_big_endian qword} unsafe_types.include?(value[:type]) end end end end