diff options
author | Paul Morton <pmorton@biaprotect.com> | 2011-08-05 16:17:29 -0700 |
---|---|---|
committer | Seth Chisamore <schisamo@opscode.com> | 2011-08-10 14:21:29 -0400 |
commit | bf677aef4a723b83752e87d1e60653182477c7e9 (patch) | |
tree | e757e4fc3d0765181926e46334deb3f177138f2f /libraries | |
parent | f3de9d593b0471023a0dbf8b4857d3aaa7b07e42 (diff) | |
download | mixlib-shellout-bf677aef4a723b83752e87d1e60653182477c7e9.tar.gz |
Refactor the registry code into a module
Add windows privileged from dougm
Added convience methods in the base Registry::
Diffstat (limited to 'libraries')
-rw-r--r-- | libraries/registry_helper.rb | 298 | ||||
-rw-r--r-- | libraries/windows_privileged.rb | 95 |
2 files changed, 393 insertions, 0 deletions
diff --git a/libraries/registry_helper.rb b/libraries/registry_helper.rb new file mode 100644 index 0000000..8192a15 --- /dev/null +++ b/libraries/registry_helper.rb @@ -0,0 +1,298 @@ +#
+# Author:: Doug MacEachern (<dougm@vmware.com>)
+# Author:: Seth Chisamore (<schisamo@opscode.com>)
+# Author:: Paul Morotn (<pmorton@biaprotect.com>)
+# Cookbook Name:: windows
+# Provider:: registry
+#
+# Copyright:: 2010, VMware, Inc.
+# Copyright:: 2011, Opscode, Inc.
+# Copyright:: 2011, Business Intelligence Associates, 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.
+#
+
+module Windows
+ module RegistryHelper
+ require 'win32/registry'
+ require 'ruby-wmi'
+ @@native_registry_constant = ENV['PROCESSOR_ARCHITEW6432'] == 'AMD64' ? 0x0100 : 0x0200
+
+ def get_hive_name(path)
+ Chef::Log.debug("Resolving registry shortcuts to full names")
+
+ reg_path = path.split("\\")
+ hive_name = reg_path.shift
+
+ hkey = {
+ "HKLM" => "HKEY_LOCAL_MACHINE",
+ "HKCU" => "HKEY_CURRENT_USER",
+ "HKU" => "HKEY_USERS"
+ }[hive_name] || hive_name
+
+ Chef::Log.debug("Hive resolved to #{hkey}")
+ return hkey
+ end
+
+ def get_hive(path)
+
+ Chef::Log.debug("Getting hive for #{path}")
+ reg_path = path.split("\\")
+ hive_name = reg_path.shift
+
+ hkey = get_hive_name(path)
+
+ hive = {
+ "HKEY_LOCAL_MACHINE" => Win32::Registry::HKEY_LOCAL_MACHINE,
+ "HKEY_USERS" => Win32::Registry::HKEY_USERS,
+ "HKEY_CURRENT_USER" => Win32::Registry::HKEY_CURRENT_USER
+ }[hkey]
+
+ unless hive
+ Chef::Application.fatal!("Unsupported registry hive '#{hive_name}'")
+ end
+
+
+ Chef::Log.debug("Registry hive resolved to #{hkey}")
+ return hive
+ end
+
+ def unload_hive(path)
+ hive = get_hive(path)
+ if hive == Win32::Registry::HKEY_USERS
+ reg_path = path.split("\\")
+ priv = Chef::WindowsPrivileged.new
+ begin
+ priv.reg_unload_key(reg_path[1])
+ rescue
+ end
+ end
+ end
+
+ def set_value(mode,path,values)
+ hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path)
+ key_name = reg_path.join("\\")
+
+ Chef::Log.debug("Registry Mode (#{mode})")
+
+ Chef::Log.debug("Registry Constant #@@native_registry_constant")
+
+ hive.send(mode, key_name, Win32::Registry::KEY_ALL_ACCESS | @@native_registry_constant) do |reg|
+ values.each do |k,val|
+ key = "#{k}" #wtf. avoid "can't modify frozen string" in win32/registry.rb
+ cur_val = nil
+ begin
+ cur_val = reg[key]
+ rescue
+ #subkey does not exist (ok)
+ end
+ if cur_val != val
+ Chef::Log.debug("setting #{key}=#{val}")
+ reg[key] = val
+
+ if(hive_loaded)
+ Chef::Log.debug("Hive was loaded, we really should unload it")
+ unload_hive(path)
+ end
+
+ return true
+ end
+ end
+ end
+ return false
+ end
+
+ def get_value(path,value)
+ hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path)
+ key = reg_path.join("\\")
+
+ hive.open(key, Win32::Registry::KEY_ALL_ACCESS | @@native_registry_constant) do | reg |
+ begin
+ return reg[value]
+ rescue
+ return nil
+ ensure
+ if(hive_loaded)
+ Chef::Log.debug("Hive was loaded, we really should unload it")
+ unload_hive(path)
+ end
+ end
+ end
+
+
+ end
+
+ def value_exists?(path,value)
+ if key_exists?(path,true)
+
+ hive, reg_path, hive_name, root_key , hive_loaded = get_reg_path_info(path)
+ key = reg_path.join("\\")
+
+ Chef::Log.debug("Attempting to open #{key}");
+ Chef::Log.debug("Native Constant #@@native_registry_constant")
+ Chef::Log.debug("Hive #{hive}")
+
+ hive.open(key, Win32::Registry::KEY_READ | @@native_registry_constant) do | reg |
+ begin
+ rtn_value = reg[value]
+ return true
+ rescue
+ return false
+ ensure
+ if(hive_loaded)
+ Chef::Log.debug("Hive was loaded, we really should unload it")
+ unload_hive(path)
+ end
+ end
+ end
+
+ end
+ return false
+ end
+
+ # TODO: Does not load user registry...
+ def key_exists?(path, load_hive = false)
+ if load_hive
+ hive, reg_path, hive_name, root_key , hive_loaded = get_reg_path_info(path)
+ key = reg_path.join("\\")
+ else
+ hive = get_hive(path)
+ reg_path = path.split("\\")
+ hive_name = reg_path.shift
+ root_key = reg_path[0]
+ key = reg_path.join("\\")
+ hive_loaded = false
+ end
+
+
+ begin
+ hive.open(key, Win32::Registry::Constants::KEY_READ | @@native_registry_constant )
+ return true
+ rescue
+ return false
+ ensure
+ if(hive_loaded)
+ Chef::Log.debug("Hive was loaded, we really should unload it")
+ unload_hive(path)
+ end
+ end
+ end
+
+ def get_user_hive_location(sid)
+ reg_key = "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\#{sid}"
+ Chef::Log.debug("Looking for profile at #{reg_key}")
+ if key_exists?(reg_key)
+ return get_value(reg_key,'ProfileImagePath')
+ else
+ return nil
+ end
+
+ end
+
+ def resolve_user_to_sid(username)
+ begin
+ sid = WMI::Win32_UserAccount.find(:first, :conditions => {:name => username}).sid
+ Chef::Log.debug("Resolved user SID to #{sid}")
+ return sid
+ rescue
+ return nil
+ end
+ end
+
+ def hive_loaded?(path)
+ hive = get_hive(path)
+ reg_path = path.split("\\")
+ hive_name = reg_path.shift
+ user_hive = path[0]
+
+ if is_user_hive?(hive)
+ return key_exists?("#{hive_name}\\#{user_hive}")
+ else
+ return true
+ end
+ end
+
+ def is_user_hive?(hive)
+ if hive == Win32::Registry::HKEY_USERS
+ return true
+ else
+ return true
+ end
+ end
+
+ def get_reg_path_info(path)
+ hive = get_hive(path)
+ reg_path = path.split("\\")
+ hive_name = reg_path.shift
+ root_key = reg_path[0]
+ hive_loaded = false
+
+ if is_user_hive?(hive) && !key_exists?("#{hive_name}\\#{root_key}")
+ reg_path, hive_loaded = load_user_hive(hive,reg_path,root_key)
+ root_key = reg_path[0]
+ Chef::Log.debug("Resolved user (#{path}) to (#{reg_path.join('/')})")
+ end
+
+ return hive, reg_path, hive_name, root_key, hive_loaded
+ end
+
+ def load_user_hive(hive,reg_path,user_hive)
+ Chef::Log.debug("Reg Path #{reg_path}")
+ # See if the hive is loaded. Logged in users will have a key that is named their SID
+ # if the user has specified the a path by SID and the user is logged in, this function
+ # should not be executed.
+ if is_user_hive?(hive) && !key_exists?("HKU\\#{user_hive}")
+ Chef::Log.debug("The user is not logged in and has not been specified by SID")
+ sid = resolve_user_to_sid(user_hive)
+ Chef::Log.debug("User SID resolved to (#{sid})")
+ # Now that the user has been resolved to a SID, check and see if the hive exists.
+ # If this exists by SID, the user is logged in and we should use that key.
+ # TODO: Replace the username with the sid and send it back because the username
+ # does not exist as the key location.
+ load_reg = false
+ if key_exists?("HKU\\#{sid}")
+ reg_path[0] = sid #use the active profile (user is logged on)
+ Chef::Log.debug("HKEY_USERS Mapped: #{user_hive} -> #{sid}")
+ else
+ Chef::Log.debug("User is not logged in")
+ load_reg = true
+ end
+
+ # The user is not logged in, so we should load the registry from disk
+ if load_reg
+ profile_path = get_user_hive_location(sid)
+ if profile_path != nil
+ ntuser_dat = "#{profile_path}\\NTUSER.DAT"
+ if ::File.exists?(ntuser_dat)
+ priv = Chef::WindowsPrivileged.new
+ if priv.reg_load_key(sid,ntuser_dat)
+ Chef::Log.debug("RegLoadKey(#{sid}, #{user_hive}, #{ntuser_dat})")
+ reg_path[0] = sid
+
+ else
+ Chef::Log.debug("Failed RegLoadKey(#{sid}, #{user_hive}, #{ntuser_dat})")
+ end
+ end
+ end
+ end
+ end
+
+ return reg_path, load_reg
+
+ end
+ end
+end
+
+class Registry
+ extend Windows::RegistryHelper
+end
\ No newline at end of file diff --git a/libraries/windows_privileged.rb b/libraries/windows_privileged.rb new file mode 100644 index 0000000..fc26a08 --- /dev/null +++ b/libraries/windows_privileged.rb @@ -0,0 +1,95 @@ +# +# Cookbook Name:: windows +# Library:: windows_privileged +# Author:: Doug MacEachern <dougm@vmware.com> +# Author:: Paul Morton (<pmorton@biaprotect.com>) +# +# Copyright 2010, VMware, Inc. +# Copyright:: 2011, Business Intelligence Associates, 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. +# + +#helpers for Windows API calls that require privilege adjustments +class Chef + class WindowsPrivileged + + #XXX can we tell Chef not to load this library unless on Windows? + if RUBY_PLATFORM =~ /mswin|mingw32|windows/ + require 'windows/error' + require 'windows/registry' + require 'windows/process' + require 'windows/security' + + include Windows::Error + include Windows::Registry + include Windows::Process + include Windows::Security + end + + #File -> Load Hive... in regedit.exe + def reg_load_key(name, file) + run(SE_BACKUP_NAME, SE_RESTORE_NAME) do + rc = RegLoadKey(HKEY_USERS, "#{name}", file) + if rc == ERROR_SUCCESS + return true + elsif rc == ERROR_SHARING_VIOLATION + return false + else + raise get_last_error(rc) + end + end + end + + #File -> Unload Hive... in regedit.exe + def reg_unload_key(name) + run(SE_BACKUP_NAME, SE_RESTORE_NAME) do + rc = RegUnLoadKey(HKEY_USERS, "#{name}") + if rc != ERROR_SUCCESS + raise get_last_error(rc) + end + end + end + + def run(*privileges) + token = [0].pack('L') + + unless OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, token) + raise get_last_error + end + token = token.unpack('L')[0] + + privileges.each do |name| + unless adjust_privilege(token, name, SE_PRIVILEGE_ENABLED) + raise get_last_error + end + end + + begin + yield + ensure #disable privs + privileges.each do |name| + adjust_privilege(token, name, 0) + end + end + end + + def adjust_privilege(token, priv, attr=0) + luid = [0,0].pack('Ll') + if LookupPrivilegeValue(nil, priv, luid) + new_state = [1, luid.unpack('Ll'), attr].flatten.pack('LLlL') + AdjustTokenPrivileges(token, 0, new_state, new_state.size, 0, 0) + end + end + end +end
\ No newline at end of file |