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 <co...@suse.com>
+
+- 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 (<d...@chef.io>)
 # Author:: John Keiser (<jkei...@chef.io>)
 # Author:: Ho-Sheng Hsiao (<h...@chef.io>)
-# 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: i...@chef.io
@@ -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


Reply via email to