From 8a68a2a8bd043ae0c75d2c21c0b75259942039e4 Mon Sep 17 00:00:00 2001 From: Adam Edwards Date: Sat, 12 Dec 2015 23:08:24 -0800 Subject: Windows alternate user support for execute resources --- lib/chef/provider/execute.rb | 19 +++++++++++++---- lib/chef/provider/powershell_script.rb | 8 +++++++ lib/chef/provider/script.rb | 38 ++++++++++++++++++++++++++++++---- 3 files changed, 57 insertions(+), 8 deletions(-) (limited to 'lib/chef/provider') diff --git a/lib/chef/provider/execute.rb b/lib/chef/provider/execute.rb index 45f0ad5488..c8e3d2732b 100644 --- a/lib/chef/provider/execute.rb +++ b/lib/chef/provider/execute.rb @@ -16,18 +16,25 @@ # limitations under the License. # -require "chef/log" -require "chef/provider" -require "forwardable" +require 'chef/log' +require 'chef/provider' +require 'forwardable' +require 'chef/mixin/user_identity' class Chef class Provider class Execute < Chef::Provider extend Forwardable + include Chef::Mixin::UserIdentity + provides :execute - def_delegators :@new_resource, :command, :returns, :environment, :user, :group, :cwd, :umask, :creates + def_delegators :@new_resource, :command, :returns, :environment, :user, :domain, :password, :group, :cwd, :umask, :creates + + def initialize(new_resource, run_context) + super + end def load_current_resource current_resource = Chef::Resource::Execute.new(new_resource.name) @@ -52,6 +59,7 @@ class Chef end def action_run + validate_identity(new_resource.user, new_resource.password, new_resource.domain) if creates && sentinel_file.exist? Chef::Log.debug("#{new_resource} sentinel file #{sentinel_file} exists - nothing to do") return false @@ -92,6 +100,8 @@ class Chef opts[:returns] = returns if returns opts[:environment] = environment if environment opts[:user] = user if user + opts[:domain] = domain if domain + opts[:password] = password if password opts[:group] = group if group opts[:cwd] = cwd if cwd opts[:umask] = umask if umask @@ -120,6 +130,7 @@ class Chef ( cwd && creates_relative? ) ? ::File.join(cwd, creates) : creates )) end + end end end diff --git a/lib/chef/provider/powershell_script.rb b/lib/chef/provider/powershell_script.rb index ab85ec35ac..0cace362df 100644 --- a/lib/chef/provider/powershell_script.rb +++ b/lib/chef/provider/powershell_script.rb @@ -149,6 +149,14 @@ EOH <<-EOH # Chef Client wrapper for powershell_script resources +# In rare cases, such as when PowerShell is executed +# as an alternate user, the new-variable cmdlet is not +# available, so import it just in case +if ( get-module -ListAvailable Microsoft.PowerShell.Utility ) +{ + Import-Module Microsoft.PowerShell.Utility +} + # LASTEXITCODE can be uninitialized -- make it explictly 0 # to avoid incorrect detection of failure (non-zero) codes $global:LASTEXITCODE = 0 diff --git a/lib/chef/provider/script.rb b/lib/chef/provider/script.rb index 6ca4e9f6f3..9aee6fe442 100644 --- a/lib/chef/provider/script.rb +++ b/lib/chef/provider/script.rb @@ -16,9 +16,10 @@ # limitations under the License. # -require "tempfile" -require "chef/provider/execute" -require "forwardable" +require 'tempfile' +require 'chef/provider/execute' +require 'chef/win32/security' if Chef::Platform.windows? +require 'forwardable' class Chef class Provider @@ -69,7 +70,36 @@ class Chef # FileUtils itself implements a no-op if +user+ or +group+ are nil # You can prove this by running FileUtils.chown(nil,nil,'/tmp/file') # as an unprivileged user. - FileUtils.chown(new_resource.user, new_resource.group, script_file.path) + if ! Chef::Platform.windows? + FileUtils.chown(new_resource.user, new_resource.group, script_file.path) + else + grant_alternate_user_read_access + end + end + + def grant_alternate_user_read_access + return if new_resource.user.nil? + + # Duplicate the script file's existing DACL + # so we can add an ACE later + securable_object = Chef::ReservedNames::Win32::Security::SecurableObject.new(script_file.path) + aces = securable_object.security_descriptor.dacl.reduce([]) { | result, current | result.push(current) } + + username = new_resource.user + + if new_resource.domain + username = new_resource.domain + '\\' + new_resource.user + end + + # Create an ACE that allows the alternate user read access to the script + # file so it can be read and executed. + user_sid = Chef::ReservedNames::Win32::Security::SID.from_account(username) + read_ace = Chef::ReservedNames::Win32::Security::ACE.access_allowed(user_sid, Chef::ReservedNames::Win32::API::Security::GENERIC_READ | Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE, 0) + aces.push(read_ace) + acl = Chef::ReservedNames::Win32::Security::ACL.create(aces) + + # This actually applies the modified DACL to the file + securable_object.dacl = acl end def script_file -- cgit v1.2.1