diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/chef/mixin/user_context.rb | 9 | ||||
-rw-r--r-- | lib/chef/provider/remote_file/network_file.rb | 2 | ||||
-rw-r--r-- | lib/chef/resource/remote_file.rb | 2 | ||||
-rw-r--r-- | lib/chef/util/windows/logon_session.rb | 7 | ||||
-rw-r--r-- | lib/chef/win32/api/security.rb | 11 | ||||
-rw-r--r-- | lib/chef/win32/security.rb | 30 |
6 files changed, 54 insertions, 7 deletions
diff --git a/lib/chef/mixin/user_context.rb b/lib/chef/mixin/user_context.rb index 526d6b0f3f..f5c2da1389 100644 --- a/lib/chef/mixin/user_context.rb +++ b/lib/chef/mixin/user_context.rb @@ -22,7 +22,10 @@ class Chef module Mixin module UserContext - def with_user_context(user, password, domain = nil, &block) + # valid values for authentication => :remote, :local + # When authentication = :local, we use the credentials to create a logon session against the local system, and then try to access the files. + # When authentication = :remote, we continue with the current user but pass the provided credentials to the remote system. + def with_user_context(user, password, domain = nil, authentication = :remote, &block) unless Chef::Platform.windows? raise Exceptions::UnsupportedPlatform, "User context impersonation is supported only on the Windows platform" end @@ -31,11 +34,11 @@ class Chef raise ArgumentError, "You must supply a block to `with_user_context`" end - login_session = nil + logon_session = nil begin if user - logon_session = Chef::Util::Windows::LogonSession.new(user, password, domain) + logon_session = Chef::Util::Windows::LogonSession.new(user, password, domain, authentication) logon_session.open logon_session.set_user_context end diff --git a/lib/chef/provider/remote_file/network_file.rb b/lib/chef/provider/remote_file/network_file.rb index a08bfd2453..f9dc7b0e7b 100644 --- a/lib/chef/provider/remote_file/network_file.rb +++ b/lib/chef/provider/remote_file/network_file.rb @@ -43,7 +43,7 @@ class Chef tempfile = Chef::FileContentManagement::Tempfile.new(new_resource).tempfile Chef::Log.debug("#{new_resource} staging #{@source} to #{tempfile.path}") - with_user_context(new_resource.remote_user, new_resource.remote_password, new_resource.remote_domain) do + with_user_context(new_resource.remote_user, new_resource.remote_password, new_resource.remote_domain, new_resource.authentication) do ::File.open(@source, "rb") do |remote_file| while data = remote_file.read(TRANSFER_CHUNK_SIZE) tempfile.write(data) diff --git a/lib/chef/resource/remote_file.rb b/lib/chef/resource/remote_file.rb index 4db055a20d..d2c2622524 100644 --- a/lib/chef/resource/remote_file.rb +++ b/lib/chef/resource/remote_file.rb @@ -139,6 +139,8 @@ class Chef property :remote_password, String, sensitive: true + property :authentication, equal_to: [:remote, :local], default: :remote + def after_created validate_identity_platform(remote_user, remote_password, remote_domain) identity = qualify_user(remote_user, remote_password, remote_domain) diff --git a/lib/chef/util/windows/logon_session.rb b/lib/chef/util/windows/logon_session.rb index ef80b113b1..afe58ae4f9 100644 --- a/lib/chef/util/windows/logon_session.rb +++ b/lib/chef/util/windows/logon_session.rb @@ -25,7 +25,7 @@ class Chef class LogonSession include Chef::Mixin::WideString - def initialize(username, password, domain = nil) + def initialize(username, password, domain = nil, authentication = :remote) if username.nil? || password.nil? raise ArgumentError, "The logon session must be initialize with non-nil user name and password parameters" end @@ -33,6 +33,7 @@ class Chef @original_username = username @original_password = password @original_domain = domain + @authentication = authentication @token = FFI::Buffer.new(:pointer) @session_opened = false @impersonating = false @@ -47,7 +48,8 @@ class Chef password = wstring(original_password) domain = wstring(original_domain) - status = Chef::ReservedNames::Win32::API::Security.LogonUserW(username, domain, password, Chef::ReservedNames::Win32::API::Security::LOGON32_LOGON_NEW_CREDENTIALS, Chef::ReservedNames::Win32::API::Security::LOGON32_PROVIDER_DEFAULT, token) + logon_type = (authentication == :local) ? (Chef::ReservedNames::Win32::API::Security::LOGON32_LOGON_NETWORK) : (Chef::ReservedNames::Win32::API::Security::LOGON32_LOGON_NEW_CREDENTIALS) + status = Chef::ReservedNames::Win32::API::Security.LogonUserW(username, domain, password, logon_type, Chef::ReservedNames::Win32::API::Security::LOGON32_PROVIDER_DEFAULT, token) if !status last_error = FFI::LastError.error @@ -110,6 +112,7 @@ class Chef attr_reader :original_username attr_reader :original_password attr_reader :original_domain + attr_reader :authentication attr_reader :token attr_reader :session_opened diff --git a/lib/chef/win32/api/security.rb b/lib/chef/win32/api/security.rb index a6f79f5d7d..6620f321aa 100644 --- a/lib/chef/win32/api/security.rb +++ b/lib/chef/win32/api/security.rb @@ -303,6 +303,17 @@ class Chef :SecurityDelegation, ] + # https://msdn.microsoft.com/en-us/library/windows/desktop/bb530718%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 + ELEVATION_TYPE = enum :ELEVATION_TYPE, [ + :TokenElevationTypeDefault, 1, + :TokenElevationTypeFull, + :TokenElevationTypeLimited + ] + + class TOKEN_ELEVATION_TYPE < FFI::Struct + layout :ElevationType, :ELEVATION_TYPE + end + # SECURITY_DESCRIPTOR is an opaque structure whose contents can vary. Pass the # pointer around and free it with LocalFree. # http://msdn.microsoft.com/en-us/library/windows/desktop/aa379561(v=vs.85).aspx diff --git a/lib/chef/win32/security.rb b/lib/chef/win32/security.rb index c7d3f55a40..63b626b1d7 100644 --- a/lib/chef/win32/security.rb +++ b/lib/chef/win32/security.rb @@ -341,6 +341,22 @@ class Chef SID.new(group_result[:PrimaryGroup], group_result_storage) end + def self.get_token_information_elevation_type(token) + token_result_size = FFI::MemoryPointer.new(:ulong) + if GetTokenInformation(token.handle.handle, :TokenElevationType, nil, 0, token_result_size) + raise "Expected ERROR_INSUFFICIENT_BUFFER from GetTokenInformation, and got no error!" + elsif FFI::LastError.error != ERROR_INSUFFICIENT_BUFFER + Chef::ReservedNames::Win32::Error.raise! + end + info_ptr = FFI::MemoryPointer.new(:pointer) + token_info_pointer = TOKEN_ELEVATION_TYPE.new info_ptr + token_info_length = 4 + unless GetTokenInformation(token.handle.handle, :TokenElevationType, token_info_pointer, token_info_length, token_result_size) + Chef::ReservedNames::Win32::Error.raise! + end + token_info_pointer[:ElevationType] + end + def self.initialize_acl(acl_size) acl = FFI::MemoryPointer.new acl_size unless InitializeAcl(acl, acl_size, ACL_REVISION) @@ -632,7 +648,19 @@ class Chef true else - process_token = open_current_process_token(TOKEN_READ) + # a regular user doesn't have privileges to call Chef::ReservedNames::Win32::Security.OpenProcessToken + # hence we return false if the open_current_process_token fails with `Access is denied.` error message. + begin + process_token = open_current_process_token(TOKEN_READ) + rescue Exception => run_error + return false if run_error.message =~ /Access is denied/ + Chef::ReservedNames::Win32::Error.raise! + end + + # display token elevation details + token_elevation_type = get_token_information_elevation_type(process_token) + Chef::Log.debug("Token Elevation Type: #{token_elevation_type}") + elevation_result = FFI::Buffer.new(:ulong) elevation_result_size = FFI::MemoryPointer.new(:uint32) success = GetTokenInformation(process_token.handle.handle, :TokenElevation, elevation_result, 4, elevation_result_size) |