Hello community,
here is the log from the commit of package rubygem-mixlib-shellout for
openSUSE:Factory checked in at 2019-08-06 15:10:00
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rubygem-mixlib-shellout (Old)
and /work/SRC/openSUSE:Factory/.rubygem-mixlib-shellout.new.4126 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-mixlib-shellout"
Tue Aug 6 15:10:00 2019 rev:20 rq:717309 version:3.0.4
Changes:
--------
---
/work/SRC/openSUSE:Factory/rubygem-mixlib-shellout/rubygem-mixlib-shellout.changes
2019-03-04 09:20:22.108601054 +0100
+++
/work/SRC/openSUSE:Factory/.rubygem-mixlib-shellout.new.4126/rubygem-mixlib-shellout.changes
2019-08-06 15:10:02.551785121 +0200
@@ -1,0 +2,6 @@
+Fri Jul 19 09:22:43 UTC 2019 - Stephan Kulow <[email protected]>
+
+- updated to version 3.0.4
+ no changelog found
+
+-------------------------------------------------------------------
Old:
----
mixlib-shellout-2.4.4.gem
New:
----
mixlib-shellout-3.0.4.gem
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ rubygem-mixlib-shellout.spec ++++++
--- /var/tmp/diff_new_pack.pBvyhy/_old 2019-08-06 15:10:03.211784747 +0200
+++ /var/tmp/diff_new_pack.pBvyhy/_new 2019-08-06 15:10:03.215784745 +0200
@@ -1,7 +1,7 @@
#
# spec file for package rubygem-mixlib-shellout
#
-# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -24,7 +24,7 @@
#
Name: rubygem-mixlib-shellout
-Version: 2.4.4
+Version: 3.0.4
Release: 0
%define mod_name mixlib-shellout
%define mod_full_name %{mod_name}-%{version}
++++++ mixlib-shellout-2.4.4.gem -> mixlib-shellout-3.0.4.gem ++++++
Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/mixlib/shellout/version.rb
new/lib/mixlib/shellout/version.rb
--- old/lib/mixlib/shellout/version.rb 2018-12-12 05:20:53.000000000 +0100
+++ new/lib/mixlib/shellout/version.rb 2019-06-07 01:26:21.000000000 +0200
@@ -1,5 +1,5 @@
module Mixlib
class ShellOut
- VERSION = "2.4.4".freeze
+ VERSION = "3.0.4".freeze
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/mixlib/shellout/windows/core_ext.rb
new/lib/mixlib/shellout/windows/core_ext.rb
--- old/lib/mixlib/shellout/windows/core_ext.rb 2018-12-12 05:20:53.000000000
+0100
+++ new/lib/mixlib/shellout/windows/core_ext.rb 2019-06-07 01:26:21.000000000
+0200
@@ -36,10 +36,48 @@
ERROR_PRIVILEGE_NOT_HELD = 1314
ERROR_LOGON_TYPE_NOT_GRANTED = 0x569
+
+ # Only documented in Userenv.h ???
+ # - ZERO (type Local) is assumed, no docs found
+ WIN32_PROFILETYPE_LOCAL = 0x00
+ WIN32_PROFILETYPE_PT_TEMPORARY = 0x01
+ WIN32_PROFILETYPE_PT_ROAMING = 0x02
+ WIN32_PROFILETYPE_PT_MANDATORY = 0x04
+ WIN32_PROFILETYPE_PT_ROAMING_PREEXISTING = 0x08
+
+end
+
+# Structs required for data handling
+module Process::Structs
+
+ class PROFILEINFO < FFI::Struct
+ layout(
+ :dwSize, :dword,
+ :dwFlags, :dword,
+ :lpUserName, :pointer,
+ :lpProfilePath, :pointer,
+ :lpDefaultPath, :pointer,
+ :lpServerName, :pointer,
+ :lpPolicyPath, :pointer,
+ :hProfile, :handle
+ )
+ end
+
end
# Define the functions needed to check with Service windows station
module Process::Functions
+ ffi_lib :userenv
+
+ attach_pfunc :GetProfileType,
+ [:pointer], :bool
+
+ attach_pfunc :LoadUserProfileW,
+ [:handle, :pointer], :bool
+
+ attach_pfunc :UnloadUserProfile,
+ [:handle, :handle], :bool
+
ffi_lib :advapi32
attach_pfunc :LogonUserW,
@@ -64,9 +102,12 @@
# as of 2015-10-15 from commit cc066e5df25048f9806a610f54bf5f7f253e86f7
module Process
+ class UnsupportedFeature < StandardError; end
+
# Explicitly reopen singleton class so that class/constant declarations from
# extensions are visible in Modules.nesting.
class << self
+
def create(args)
unless args.kind_of?(Hash)
raise TypeError, "hash keyword arguments expected"
@@ -85,9 +126,9 @@
# Set default values
hash = {
- "app_name" => nil,
+ "app_name" => nil,
"creation_flags" => 0,
- "close_handles" => true,
+ "close_handles" => true,
}
# Validate the keys, and convert symbols and case to lowercase strings.
@@ -238,6 +279,7 @@
inherit = hash["inherit"] ? 1 : 0
if hash["with_logon"]
+
logon, passwd, domain = format_creds_from_hash(hash)
hash["creation_flags"] |= CREATE_UNICODE_ENVIRONMENT
@@ -255,51 +297,42 @@
# can simulate running a command 'elevated' by running it under a
separate
# logon as a batch process.
if hash["elevated"] || winsta_name =~ /^Service-0x0-.*$/i
- logon_type = if hash["elevated"]
- LOGON32_LOGON_BATCH
- else
- LOGON32_LOGON_INTERACTIVE
- end
- token = logon_user(logon, domain, passwd, logon_type)
+ 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
+
+ if logon_has_roaming_profile?
+ msg = %w{
+ Mixlib does not currently support executing commands as users
+ configured with Roaming Profiles. [%s]
+ }.join(" ") % logon.encode("UTF-8").unpack("A*")
+ raise UnsupportedFeature.new(msg)
+ end
+
+ load_user_profile(token, profile.pointer)
+
+ create_process_as_user(token, app, cmd, process_security,
+ thread_security, inherit, hash["creation_flags"], env,
+ cwd, startinfo, procinfo)
- create_process_as_user(token, app, cmd, process_security,
thread_security, inherit, hash["creation_flags"], env, cwd, startinfo, procinfo)
else
- bool = CreateProcessWithLogonW(
- logon, # User
- domain, # Domain
- passwd, # Password
- LOGON_WITH_PROFILE, # Logon flags
- app, # App name
- cmd, # Command line
- hash["creation_flags"], # Creation flags
- env, # Environment
- cwd, # Working directory
- startinfo, # Startup Info
- procinfo # Process Info
- )
- unless bool
- raise SystemCallError.new("CreateProcessWithLogonW", FFI.errno)
- end
+ create_process_with_logon(logon, domain, passwd, LOGON_WITH_PROFILE,
+ app, cmd, hash["creation_flags"], env, cwd, startinfo, procinfo)
+
end
+
else
- bool = CreateProcessW(
- app, # App name
- cmd, # Command line
- process_security, # Process attributes
- thread_security, # Thread attributes
- inherit, # Inherit handles?
- hash["creation_flags"], # Creation flags
- env, # Environment
- cwd, # Working directory
- startinfo, # Startup Info
- procinfo # Process Info
- )
- unless bool
- raise SystemCallError.new("CreateProcessW", FFI.errno)
- end
+ create_process(app, cmd, process_security, thread_security, inherit,
+ hash["creation_flags"], env, cwd, startinfo, procinfo)
+
end
# Automatically close the process and thread handles in the
@@ -314,12 +347,120 @@
procinfo[:hThread] = 0
end
- ProcessInfo.new(
+ process = ProcessInfo.new(
procinfo[:hProcess],
procinfo[:hThread],
procinfo[:dwProcessId],
procinfo[:dwThreadId]
)
+
+ [ process, profile, token ]
+ end
+
+ # See Process::Constants::WIN32_PROFILETYPE
+ def logon_has_roaming_profile?
+ get_profile_type >= 2
+ end
+
+ def get_profile_type
+ ptr = FFI::MemoryPointer.new(:uint)
+ unless GetProfileType(ptr)
+ raise SystemCallError.new("GetProfileType", FFI.errno)
+ end
+ ptr.read_uint
+ end
+
+ def load_user_profile(token, profile_ptr)
+ unless LoadUserProfileW(token, profile_ptr)
+ raise SystemCallError.new("LoadUserProfileW", FFI.errno)
+ end
+ true
+ end
+
+ def unload_user_profile(token, profile)
+ if profile[:hProfile] == 0
+ warn "\n\nWARNING: Profile not loaded\n"
+ else
+ unless UnloadUserProfile(token, profile[:hProfile])
+ raise SystemCallError.new("UnloadUserProfile", FFI.errno)
+ end
+ end
+ true
+ end
+
+ def create_process_as_user(token, app, cmd, process_security,
+ thread_security, inherit, creation_flags, env, cwd, startinfo, procinfo)
+
+ bool = CreateProcessAsUserW(
+ token, # User token handle
+ app, # App name
+ cmd, # Command line
+ process_security, # Process attributes
+ thread_security, # Thread attributes
+ inherit, # Inherit handles
+ creation_flags, # Creation Flags
+ env, # Environment
+ cwd, # Working directory
+ startinfo, # Startup Info
+ procinfo # Process Info
+ )
+
+ unless bool
+ msg = case FFI.errno
+ when ERROR_PRIVILEGE_NOT_HELD
+ [
+ %{CreateProcessAsUserW (User '%s' must hold the 'Replace a
process},
+ %{level token' and 'Adjust Memory Quotas for a process'
permissions.},
+ %{Logoff the user after adding this right to make it
effective.)},
+ ].join(" ") % ::ENV["USERNAME"]
+ else
+ "CreateProcessAsUserW failed."
+ end
+ raise SystemCallError.new(msg, FFI.errno)
+ end
+ end
+
+ def create_process_with_logon(logon, domain, passwd, logon_flags, app, cmd,
+ creation_flags, env, cwd, startinfo, procinfo)
+
+ bool = CreateProcessWithLogonW(
+ logon, # User
+ domain, # Domain
+ passwd, # Password
+ logon_flags, # Logon flags
+ app, # App name
+ cmd, # Command line
+ creation_flags, # Creation flags
+ env, # Environment
+ cwd, # Working directory
+ startinfo, # Startup Info
+ procinfo # Process Info
+ )
+
+ unless bool
+ raise SystemCallError.new("CreateProcessWithLogonW", FFI.errno)
+ end
+ end
+
+ def create_process(app, cmd, process_security, thread_security, inherit,
+ creation_flags, env, cwd, startinfo, procinfo)
+
+ bool = CreateProcessW(
+ app, # App name
+ cmd, # Command line
+ process_security, # Process attributes
+ thread_security, # Thread attributes
+ inherit, # Inherit handles?
+ creation_flags, # Creation flags
+ env, # Environment
+ cwd, # Working directory
+ startinfo, # Startup Info
+ procinfo # Process Info
+ )
+
+ unless bool
+ raise SystemCallError.new("CreateProcessW", FFI.errno)
+ end
end
def logon_user(user, domain, passwd, type, provider =
LOGON32_PROVIDER_DEFAULT)
@@ -346,32 +487,6 @@
token.read_ulong
end
- def create_process_as_user(token, app, cmd, process_security,
thread_security, inherit, creation_flags, env, cwd, startinfo, procinfo)
- bool = CreateProcessAsUserW(
- token, # User token handle
- app, # App name
- cmd, # Command line
- process_security, # Process attributes
- thread_security, # Thread attributes
- inherit, # Inherit handles
- creation_flags, # Creation Flags
- env, # Environment
- cwd, # Working directory
- startinfo, # Startup Info
- procinfo # Process Info
- )
-
- unless bool
- if FFI.errno == ERROR_PRIVILEGE_NOT_HELD
- raise SystemCallError.new("CreateProcessAsUserW (User
'#{::ENV['USERNAME']}' must hold the 'Replace a process level token' and
'Adjust Memory Quotas for a process' permissions. Logoff the user after adding
this right to make it effective.)", FFI.errno)
- else
- raise SystemCallError.new("CreateProcessAsUserW failed.", FFI.errno)
- end
- end
- ensure
- CloseHandle(token)
- end
-
def get_windows_station_name
winsta_name = FFI::MemoryPointer.new(:char, 256)
return_size = FFI::MemoryPointer.new(:ulong)
@@ -406,5 +521,6 @@
[ logon, passwd, domain ]
end
+
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/mixlib/shellout/windows.rb
new/lib/mixlib/shellout/windows.rb
--- old/lib/mixlib/shellout/windows.rb 2018-12-12 05:20:53.000000000 +0100
+++ new/lib/mixlib/shellout/windows.rb 2019-06-07 01:26:21.000000000 +0200
@@ -2,7 +2,7 @@
# Author:: Daniel DeLeo (<[email protected]>)
# Author:: John Keiser (<[email protected]>)
# Author:: Ho-Sheng Hsiao (<[email protected]>)
-# Copyright:: Copyright (c) 2011-2016 Chef Software, Inc.
+# Copyright:: Copyright (c) 2011-2019, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -66,7 +66,7 @@
#
# Set cwd, environment, appname, etc.
#
- app_name, command_line = command_to_run(command)
+ app_name, command_line = command_to_run(combine_args(*command))
create_process_args = {
app_name: app_name,
command_line: command_line,
@@ -88,7 +88,7 @@
#
# Start the process
#
- process = Process.create(create_process_args)
+ process, profile, token = Process.create(create_process_args)
logger.debug(format_process(process, app_name, command_line,
timeout)) if logger
begin
# Start pushing data into input
@@ -143,6 +143,8 @@
ensure
CloseHandle(process.thread_handle) if process.thread_handle
CloseHandle(process.process_handle) if process.process_handle
+ Process.unload_user_profile(token, profile) if profile
+ CloseHandle(token) if token
end
ensure
@@ -196,6 +198,46 @@
true
end
+ # Use to support array passing semantics on windows
+ #
+ # 1. strings with whitespace or quotes in them need quotes around them.
+ # 2. interior quotes need to get backslash escaped (parser needs to
know when it really ends).
+ # 3. random backlsashes in paths themselves remain untouched.
+ # 4. if the argument must be quoted by #1 and terminates in a sequence
of backslashes then all the backlashes must themselves
+ # be backslash excaped (double the backslashes).
+ # 5. if an interior quote that must be escaped by #2 has a sequence of
backslashes before it then all the backslashes must
+ # themselves be backslash excaped along with the backslash ecape of
the interior quote (double plus one backslashes).
+ #
+ # And to restate. We are constructing a string which will be parsed by
the windows parser into arguments, and we want those
+ # arguments to match the *args array we are passed here. So call the
windows parser operation A then we need to apply A^-1 to
+ # our args to construct the string so that applying A gives windows back
our *args.
+ #
+ # And when the windows parser sees a series of backslashes followed by a
double quote, it has to determine if that double quote
+ # is terminating or not, and how many backslashes to insert in the args.
So what it does is divide it by two (rounding down) to
+ # get the number of backslashes to insert. Then if it is even the
double quotes terminate the argument. If it is even the
+ # double quotes are interior double quotes (the extra backslash quotes
the double quote).
+ #
+ # We construct the inverse operation so interior double quotes preceeded
by N backslashes get 2N+1 backslashes in front of the quote,
+ # while trailing N backslashes get 2N backslashes in front of the quote
that terminates the argument.
+ #
+ # see:
https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
+ #
+ # @api private
+ # @param args [Array<String>] array of command arguments
+ # @return String
+ def combine_args(*args)
+ return args[0] if args.length == 1
+ args.map do |arg|
+ if arg =~ /[ \t\n\v"]/
+ arg = arg.gsub(/(\\*)"/, '\1\1\"') # interior quotes with N
preceeding backslashes need 2N+1 backslashes
+ arg = arg.sub(/(\\+)$/, '\1\1') # trailing N backslashes need to
become 2N backslashes
+ "\"#{arg}\""
+ else
+ arg
+ end
+ end.join(" ")
+ end
+
def command_to_run(command)
return run_under_cmd(command) if should_run_under_cmd?(command)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/metadata new/metadata
--- old/metadata 2018-12-12 05:20:53.000000000 +0100
+++ new/metadata 2019-06-07 01:26:21.000000000 +0200
@@ -1,14 +1,14 @@
--- !ruby/object:Gem::Specification
name: mixlib-shellout
version: !ruby/object:Gem::Version
- version: 2.4.4
+ version: 3.0.4
platform: ruby
authors:
- Chef Software Inc.
autorequire:
bindir: bin
cert_chain: []
-date: 2018-12-12 00:00:00.000000000 Z
+date: 2019-06-06 00:00:00.000000000 Z
dependencies: []
description: Run external commands on Unix or Windows
email: [email protected]
@@ -41,8 +41,7 @@
- !ruby/object:Gem::Version
version: '0'
requirements: []
-rubyforge_project:
-rubygems_version: 2.7.6
+rubygems_version: 3.0.3
signing_key:
specification_version: 4
summary: Run external commands on Unix or Windows