summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorTim Smith <tsmith@chef.io>2020-11-12 08:42:58 -0800
committerGitHub <noreply@github.com>2020-11-12 08:42:58 -0800
commit560e3eaf8ec102ecb3176ab5cb522abde29321b4 (patch)
tree841823601e0a7f3e793ab3684752cab1498bd68d /lib
parent5725bca78e8d5187592952bbd92ffcd58d5b8d7e (diff)
parente2e9b4b827c13c35d078c341f32223ae89db34a2 (diff)
downloadmixlib-shellout-560e3eaf8ec102ecb3176ab5cb522abde29321b4.tar.gz
Merge pull request #224 from MsysTechnologiesllc/Kapil/MSYS-1131_Retrieves_the_environment_variables_for_the_specified_user
Signed-off-by: Tim Smith <tsmith@chef.io>
Diffstat (limited to 'lib')
-rw-r--r--lib/mixlib/shellout/windows/core_ext.rb100
1 files changed, 99 insertions, 1 deletions
diff --git a/lib/mixlib/shellout/windows/core_ext.rb b/lib/mixlib/shellout/windows/core_ext.rb
index 1c2830b..fdc3d9b 100644
--- a/lib/mixlib/shellout/windows/core_ext.rb
+++ b/lib/mixlib/shellout/windows/core_ext.rb
@@ -18,6 +18,7 @@
#
require "win32/process"
+require "ffi/win32/extensions"
# Add new constants for Logon
module Process::Constants
@@ -44,6 +45,8 @@ module Process::Constants
WIN32_PROFILETYPE_PT_MANDATORY = 0x04
WIN32_PROFILETYPE_PT_ROAMING_PREEXISTING = 0x08
+ # The environment block list ends with two nulls (\0\0).
+ ENVIRONMENT_BLOCK_ENDS = "\0\0".freeze
end
# Structs required for data handling
@@ -77,6 +80,12 @@ module Process::Functions
attach_pfunc :UnloadUserProfile,
%i{handle handle}, :bool
+ attach_pfunc :CreateEnvironmentBlock,
+ %i{pointer ulong bool}, :bool
+
+ attach_pfunc :DestroyEnvironmentBlock,
+ %i{pointer}, :bool
+
ffi_lib :advapi32
attach_pfunc :LogonUserW,
@@ -169,9 +178,25 @@ module Process
env = nil
+ # Retrieve the environment variables for the specified user.
+ if hash["with_logon"]
+ logon, passwd, domain = format_creds_from_hash(hash)
+ logon_type = hash["elevated"] ? LOGON32_LOGON_BATCH : LOGON32_LOGON_INTERACTIVE
+ token = logon_user(logon, domain, passwd, logon_type)
+ logon_ptr = FFI::MemoryPointer.from_string(logon)
+ profile = PROFILEINFO.new.tap do |dat|
+ dat[:dwSize] = dat.size
+ dat[:dwFlags] = 1
+ dat[:lpUserName] = logon_ptr
+ end
+
+ load_user_profile(token, profile.pointer)
+ env_list = retrieve_environment_variables(token)
+ end
+
# The env string should be passed as a string of ';' separated paths.
if hash["environment"]
- env = hash["environment"]
+ env = env_list.nil? ? hash["environment"] : merge_env_variables(env_list, hash["environment"])
unless env.respond_to?(:join)
env = hash["environment"].split(File::PATH_SEPARATOR)
@@ -393,6 +418,33 @@ module Process
true
end
+ # Retrieves the environment variables for the specified user.
+ #
+ # @param env_pointer [Pointer] The environment block is an array of null-terminated Unicode strings.
+ # @param token [Integer] User token handle.
+ # @return [Boolean] true if successfully retrieves the environment variables for the specified user.
+ #
+ def create_environment_block(env_pointer, token)
+ unless CreateEnvironmentBlock(env_pointer, token, false)
+ raise SystemCallError.new("CreateEnvironmentBlock", FFI.errno)
+ end
+
+ true
+ end
+
+ # Frees environment variables created by the CreateEnvironmentBlock function.
+ #
+ # @param env_pointer [Pointer] The environment block is an array of null-terminated Unicode strings.
+ # @return [Boolean] true if successfully frees environment variables created by the CreateEnvironmentBlock function.
+ #
+ def destroy_environment_block(env_pointer)
+ unless DestroyEnvironmentBlock(env_pointer)
+ raise SystemCallError.new("DestroyEnvironmentBlock", FFI.errno)
+ end
+
+ true
+ end
+
def create_process_as_user(token, app, cmd, process_security,
thread_security, inherit, creation_flags, env, cwd, startinfo, procinfo)
@@ -527,5 +579,51 @@ module Process
[ logon, passwd, domain ]
end
+ # Retrieves the environment variables for the specified user.
+ #
+ # @param token [Integer] User token handle.
+ # @return env_list [Array<String>] Environment variables of specified user.
+ #
+ def retrieve_environment_variables(token)
+ env_list = []
+ env_pointer = FFI::MemoryPointer.new(:pointer)
+ create_environment_block(env_pointer, token)
+ str_ptr = env_pointer.read_pointer
+ offset = 0
+ loop do
+ new_str_pointer = str_ptr + offset
+ break if new_str_pointer.read_string(2) == ENVIRONMENT_BLOCK_ENDS
+
+ environment = new_str_pointer.read_wstring
+ env_list << environment
+ offset = offset + environment.length * 2 + 2
+ end
+
+ # To free the buffer when we have finished with the environment block
+ destroy_environment_block(str_ptr)
+ env_list
+ end
+
+ # Merge environment variables of specified user and current environment variables.
+ #
+ # @param fetched_env [Array<String>] environment variables of specified user.
+ # @param current_env [Array<String>] current environment variables.
+ # @return [Array<String>] Merged environment variables.
+ #
+ def merge_env_variables(fetched_env, current_env)
+ env_hash_1 = environment_list_to_hash(fetched_env)
+ env_hash_2 = environment_list_to_hash(current_env)
+ merged_env = env_hash_2.merge(env_hash_1)
+ merged_env.map { |k, v| "#{k}=#{v}" }
+ end
+
+ # Convert an array to a hash.
+ #
+ # @param env_var [Array<String>] Environment variables.
+ # @return [Hash] Converted an array to hash.
+ #
+ def environment_list_to_hash(env_var)
+ Hash[ env_var.map { |pair| pair.split("=", 2) } ]
+ end
end
end