puppet 5.5.14 → 5.5.16

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +18 -17
  4. data/ext/solaris/smf/puppet.xml +2 -0
  5. data/lib/hiera/scope.rb +7 -0
  6. data/lib/puppet.rb +1 -1
  7. data/lib/puppet/application/device.rb +22 -10
  8. data/lib/puppet/configurer.rb +23 -38
  9. data/lib/puppet/network/http/connection.rb +2 -0
  10. data/lib/puppet/pops/types/types.rb +5 -3
  11. data/lib/puppet/provider.rb +1 -2
  12. data/lib/puppet/provider/cron/crontab.rb +1 -1
  13. data/lib/puppet/provider/package.rb +2 -0
  14. data/lib/puppet/provider/package/dpkg.rb +15 -2
  15. data/lib/puppet/provider/package/gem.rb +65 -29
  16. data/lib/puppet/provider/package/pip.rb +136 -111
  17. data/lib/puppet/provider/package/pip3.rb +1 -1
  18. data/lib/puppet/provider/package/puppet_gem.rb +1 -1
  19. data/lib/puppet/provider/package/rpm.rb +27 -16
  20. data/lib/puppet/provider/package/yum.rb +1 -1
  21. data/lib/puppet/provider/package_targetable.rb +68 -0
  22. data/lib/puppet/provider/service/upstart.rb +8 -8
  23. data/lib/puppet/provider/user/useradd.rb +16 -13
  24. data/lib/puppet/settings/server_list_setting.rb +9 -0
  25. data/lib/puppet/ssl/validator/default_validator.rb +30 -0
  26. data/lib/puppet/type/package.rb +46 -9
  27. data/lib/puppet/util/pidlock.rb +15 -1
  28. data/lib/puppet/util/windows/process.rb +70 -0
  29. data/lib/puppet/util/windows/registry.rb +7 -1
  30. data/lib/puppet/util/windows/user.rb +14 -4
  31. data/lib/puppet/version.rb +1 -1
  32. data/locales/puppet.pot +81 -78
  33. data/man/man5/puppet.conf.5 +2 -2
  34. data/man/man8/puppet-agent.8 +1 -1
  35. data/man/man8/puppet-apply.8 +1 -1
  36. data/man/man8/puppet-ca.8 +1 -1
  37. data/man/man8/puppet-catalog.8 +1 -1
  38. data/man/man8/puppet-cert.8 +1 -1
  39. data/man/man8/puppet-certificate.8 +1 -1
  40. data/man/man8/puppet-certificate_request.8 +1 -1
  41. data/man/man8/puppet-certificate_revocation_list.8 +1 -1
  42. data/man/man8/puppet-config.8 +1 -1
  43. data/man/man8/puppet-describe.8 +1 -1
  44. data/man/man8/puppet-device.8 +1 -1
  45. data/man/man8/puppet-doc.8 +1 -1
  46. data/man/man8/puppet-epp.8 +1 -1
  47. data/man/man8/puppet-facts.8 +1 -1
  48. data/man/man8/puppet-filebucket.8 +1 -1
  49. data/man/man8/puppet-generate.8 +1 -1
  50. data/man/man8/puppet-help.8 +1 -1
  51. data/man/man8/puppet-key.8 +1 -1
  52. data/man/man8/puppet-lookup.8 +1 -1
  53. data/man/man8/puppet-man.8 +1 -1
  54. data/man/man8/puppet-master.8 +1 -1
  55. data/man/man8/puppet-module.8 +1 -1
  56. data/man/man8/puppet-node.8 +1 -1
  57. data/man/man8/puppet-parser.8 +1 -1
  58. data/man/man8/puppet-plugin.8 +1 -1
  59. data/man/man8/puppet-report.8 +1 -1
  60. data/man/man8/puppet-resource.8 +1 -1
  61. data/man/man8/puppet-script.8 +1 -1
  62. data/man/man8/puppet-status.8 +1 -1
  63. data/man/man8/puppet.8 +2 -2
  64. data/spec/integration/network/http_pool_spec.rb +120 -0
  65. data/spec/integration/type/package_spec.rb +1 -1
  66. data/spec/integration/util/windows/registry_spec.rb +52 -0
  67. data/spec/integration/util/windows/user_spec.rb +19 -0
  68. data/spec/lib/puppet_spec/https.rb +166 -0
  69. data/spec/unit/configurer_spec.rb +49 -13
  70. data/spec/unit/functions/new_spec.rb +15 -0
  71. data/spec/unit/hiera/scope_spec.rb +7 -0
  72. data/spec/unit/network/http/connection_spec.rb +0 -130
  73. data/spec/unit/provider/package/dpkg_spec.rb +18 -1
  74. data/spec/unit/provider/package/gem_spec.rb +101 -48
  75. data/spec/unit/provider/package/pip3_spec.rb +17 -0
  76. data/spec/unit/provider/package/pip_spec.rb +59 -68
  77. data/spec/unit/provider/package/puppet_gem_spec.rb +22 -6
  78. data/spec/unit/provider/package/rpm_spec.rb +116 -27
  79. data/spec/unit/provider/service/upstart_spec.rb +3 -19
  80. data/spec/unit/settings/server_list_setting_spec.rb +21 -0
  81. data/spec/unit/ssl/validator_spec.rb +2 -0
  82. data/spec/unit/util/pidlock_spec.rb +46 -0
  83. metadata +9 -2
@@ -27,14 +27,14 @@ Puppet::Type.type(:service).provide :upstart, :parent => :debian do
27
27
  # We only want to use upstart as our provider if the upstart daemon is running.
28
28
  # This can be checked by running `initctl version --quiet` on a machine that has
29
29
  # upstart installed.
30
- confine :true => lambda {
31
- begin
32
- initctl('version', '--quiet')
33
- true
34
- rescue
35
- false
36
- end
37
- }
30
+ confine :true => lambda { has_initctl? }
31
+
32
+ def self.has_initctl?
33
+ initctl('version', '--quiet')
34
+ true
35
+ rescue
36
+ false
37
+ end
38
38
 
39
39
  # upstart developer haven't implemented initctl enable/disable yet:
40
40
  # http://www.linuxplanet.com/linuxplanet/tutorials/7033/2/
@@ -105,11 +105,11 @@ Puppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameServ
105
105
  # because by default duplicates are allowed. This check is
106
106
  # to ensure consistent behaviour of the useradd provider when
107
107
  # using both useradd and luseradd
108
- if not @resource.allowdupe? and @resource.forcelocal?
109
- if @resource.should(:uid) and finduser('uid', @resource.should(:uid).to_s)
108
+ if (!@resource.allowdupe?) && @resource.forcelocal?
109
+ if @resource.should(:uid) && finduser('uid', @resource.should(:uid).to_s)
110
110
  raise(Puppet::Error, "UID #{@resource.should(:uid).to_s} already exists, use allowdupe to force user creation")
111
111
  end
112
- elsif @resource.allowdupe? and not @resource.forcelocal?
112
+ elsif @resource.allowdupe? && (!@resource.forcelocal?)
113
113
  return ["-o"]
114
114
  end
115
115
  []
@@ -126,16 +126,16 @@ Puppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameServ
126
126
 
127
127
  def check_manage_home
128
128
  cmd = []
129
- if @resource.managehome? and not @resource.forcelocal?
129
+ if @resource.managehome? && (!@resource.forcelocal?)
130
130
  cmd << "-m"
131
- elsif not @resource.managehome? and Facter.value(:osfamily) == 'RedHat'
131
+ elsif (!@resource.managehome?) && Facter.value(:osfamily) == 'RedHat'
132
132
  cmd << "-M"
133
133
  end
134
134
  cmd
135
135
  end
136
136
 
137
137
  def check_system_users
138
- if self.class.system_users? and resource.system?
138
+ if self.class.system_users? && resource.system?
139
139
  ["-r"]
140
140
  else
141
141
  []
@@ -149,11 +149,11 @@ Puppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameServ
149
149
  Puppet::Type.type(:user).validproperties.sort.each do |property|
150
150
  next if property == :ensure
151
151
  next if property_manages_password_age?(property)
152
- next if property == :groups and @resource.forcelocal?
153
- next if property == :expiry and @resource.forcelocal?
152
+ next if (property == :groups) && @resource.forcelocal?
153
+ next if (property == :expiry) && @resource.forcelocal?
154
154
  # the value needs to be quoted, mostly because -c might
155
155
  # have spaces in it
156
- if value = @resource.should(property) and value != ""
156
+ if (value = @resource.should(property)) && (value != "")
157
157
  cmd << flag(property) << munge(property, value)
158
158
  end
159
159
  end
@@ -167,7 +167,7 @@ Puppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameServ
167
167
  else
168
168
  cmd = [command(:add)]
169
169
  end
170
- if not @resource.should(:gid) and Puppet::Util.gid(@resource[:name])
170
+ if (!@resource.should(:gid)) && Puppet::Util.gid(@resource[:name])
171
171
  cmd += ["-g", @resource[:name]]
172
172
  end
173
173
  cmd += add_properties
@@ -203,7 +203,10 @@ Puppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameServ
203
203
  else
204
204
  cmd = [command(:delete)]
205
205
  end
206
- cmd += @resource.managehome? ? ['-r'] : []
206
+ # Solaris `userdel -r` will fail if the homedir does not exist.
207
+ if @resource.managehome? && (('Solaris' != Facter.value(:operatingsystem)) || Dir.exist?(Dir.home(@resource[:name])))
208
+ cmd << '-r'
209
+ end
207
210
  cmd << @resource[:name]
208
211
  end
209
212
 
@@ -239,10 +242,10 @@ Puppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameServ
239
242
  check_valid_shell
240
243
  end
241
244
  super
242
- if @resource.forcelocal? and self.groups?
245
+ if @resource.forcelocal? && self.groups?
243
246
  set(:groups, @resource[:groups])
244
247
  end
245
- if @resource.forcelocal? and @resource[:expiry]
248
+ if @resource.forcelocal? && @resource[:expiry]
246
249
  set(:expiry, @resource[:expiry])
247
250
  end
248
251
  end
@@ -4,6 +4,15 @@ class Puppet::Settings::ServerListSetting < Puppet::Settings::ArraySetting
4
4
  :server_list
5
5
  end
6
6
 
7
+ def print(value)
8
+ if value.is_a?(Array)
9
+ #turn into a string
10
+ value.map {|item| item.join(":") }.join(",")
11
+ else
12
+ value
13
+ end
14
+ end
15
+
7
16
  def munge(value)
8
17
  servers = super
9
18
  servers.map! { |server|
@@ -10,6 +10,7 @@ class Puppet::SSL::Validator::DefaultValidator #< class Puppet::SSL::Validator
10
10
  attr_reader :peer_certs
11
11
  attr_reader :verify_errors
12
12
  attr_reader :ssl_configuration
13
+ attr_reader :last_error
13
14
 
14
15
  FIVE_MINUTES_AS_SECONDS = 5 * 60
15
16
 
@@ -40,6 +41,8 @@ class Puppet::SSL::Validator::DefaultValidator #< class Puppet::SSL::Validator
40
41
  def reset!
41
42
  @peer_certs = []
42
43
  @verify_errors = []
44
+ @hostname = nil
45
+ @last_error = nil
43
46
  end
44
47
 
45
48
  # Performs verification of the SSL connection and collection of the
@@ -85,6 +88,31 @@ class Puppet::SSL::Validator::DefaultValidator #< class Puppet::SSL::Validator
85
88
  error_string = store_context.error_string || "OpenSSL error #{error}"
86
89
 
87
90
  case error
91
+ when OpenSSL::X509::V_OK
92
+ if @hostname
93
+ # chain is from leaf to root, opposite of the order that `call` is invoked
94
+ chain_cert = store_context.chain.first
95
+ peer_cert = @peer_certs.last
96
+
97
+ # ruby 2.4 doesn't compare certs based on value, so force to DER byte array
98
+ if peer_cert && chain_cert && peer_cert.content.to_der == chain_cert.to_der && !OpenSSL::SSL.verify_certificate_identity(peer_cert.content, @hostname)
99
+ valid_certnames = [peer_cert.name, *peer_cert.subject_alt_names].uniq
100
+ if valid_certnames.size > 1
101
+ expected_certnames = _("expected one of %{certnames}") % { certnames: valid_certnames.join(', ') }
102
+ else
103
+ expected_certnames = _("expected %{certname}") % { certname: valid_certnames.first }
104
+ end
105
+
106
+ msg = _("Server hostname '%{host}' did not match server certificate; %{expected_certnames}") % { host: @hostname, expected_certnames: expected_certnames }
107
+ @last_error = Puppet::Error.new(msg)
108
+ return false
109
+ else
110
+ @verify_errors << "#{error_string} for #{current_cert.subject}"
111
+ end
112
+ else
113
+ @verify_errors << "#{error_string} for #{current_cert.subject}"
114
+ end
115
+
88
116
  when OpenSSL::X509::V_ERR_CRL_NOT_YET_VALID
89
117
  # current_crl can be nil
90
118
  # https://github.com/ruby/ruby/blob/ruby_1_9_3/ext/openssl/ossl_x509store.c#L501-L510
@@ -118,6 +146,8 @@ class Puppet::SSL::Validator::DefaultValidator #< class Puppet::SSL::Validator
118
146
  # @api private
119
147
  #
120
148
  def setup_connection(connection)
149
+ @hostname = connection.address
150
+
121
151
  if ssl_certificates_are_present?
122
152
  connection.cert_store = @ssl_host.ssl_store
123
153
  connection.ca_file = @ssl_configuration.ca_auth_file
@@ -20,8 +20,8 @@ module Puppet
20
20
  requires in order to function, and you must meet those requirements
21
21
  to use a given provider.
22
22
 
23
- You can declare multiple package resources with the same `name`, as long
24
- as they specify different providers and have unique titles.
23
+ You can declare multiple package resources with the same `name` as long
24
+ as they have unique titles, and specify different providers and commands.
25
25
 
26
26
  Note that you must use the _title_ to make a reference to a package
27
27
  resource; `Package[<NAME>]` is not a synonym for `Package[<TITLE>]` like
@@ -66,6 +66,8 @@ module Puppet
66
66
  :methods => [:package_settings_insync?, :package_settings, :package_settings=]
67
67
  feature :virtual_packages, "The provider accepts virtual package names for install and uninstall."
68
68
 
69
+ feature :targetable, "The provider accepts a targeted package management command."
70
+
69
71
  ensurable do
70
72
  desc <<-EOT
71
73
  What state the package should be in. On packaging systems that can
@@ -271,20 +273,55 @@ module Puppet
271
273
  providify
272
274
  paramclass(:provider).isnamevar
273
275
 
274
- # We have more than one namevar, so we need title_patterns. However, we
275
- # cheat and set the patterns to map to name only and completely ignore
276
- # provider. So far, the logic that determines uniqueness appears to just
277
- # "Do The Right Thing™" when the provider is explicitly set by the user.
276
+ # Specify a targeted package management command.
277
+ newparam(:command, :required_features => :targetable) do
278
+ desc <<-EOT
279
+ The targeted command to use when managing a package:
280
+
281
+ package { 'mysql':
282
+ provider => gem,
283
+ }
284
+
285
+ package { 'mysql-opt':
286
+ name => 'mysql',
287
+ provider => gem,
288
+ command => '/opt/ruby/bin/gem',
289
+ }
290
+
291
+ Each provider defines a package management command; and uses the first
292
+ instance of the command found in the PATH.
293
+
294
+ Providers supporting the targetable feature allow you to specify the
295
+ absolute path of the package management command; useful when multiple
296
+ instances of the command are installed, or the command is not in the PATH.
297
+ EOT
298
+
299
+ isnamevar
300
+ defaultto :default
301
+ end
302
+
303
+ # We have more than one namevar, so we need title_patterns.
304
+ # However, we cheat and set the patterns to map to name only
305
+ # and completely ignore provider (and command, for targetable providers).
306
+ # So far, the logic that determines uniqueness appears to just
307
+ # "Do The Right Thing™" when provider (and command) are explicitly set.
278
308
  #
279
309
  # The following resources will be seen as unique by puppet:
280
310
  #
281
311
  # # Uniqueness Key: ['mysql', nil]
282
- # package{'mysql': }
312
+ # package {'mysql': }
313
+ #
314
+ # # Uniqueness Key: ['mysql', 'gem', nil]
315
+ # package {'gem-mysql':
316
+ # name => 'mysql,
317
+ # provider => gem,
318
+ # }
283
319
  #
284
- # # Uniqueness Key: ['mysql', 'gem']
285
- # package{'gem-mysql':
320
+ # # Uniqueness Key: ['mysql', 'gem', '/opt/ruby/bin/gem']
321
+ # package {'gem-mysql-opt':
286
322
  # name => 'mysql,
287
323
  # provider => gem
324
+ # command => '/opt/ruby/bin/gem',
288
325
  # }
289
326
  #
290
327
  # This does not handle the case where providers like 'yum' and 'rpm' should
@@ -54,7 +54,21 @@ class Puppet::Util::Pidlock
54
54
  begin
55
55
  Process.kill(0, lock_pid)
56
56
  rescue *errors
57
- @lockfile.unlock
57
+ return @lockfile.unlock
58
+ end
59
+
60
+ # Ensure the process associated with this pid is our process. If
61
+ # not, we can unlock the lockfile. For now this is only done on
62
+ # POSIX and Windows platforms (PUP-9247).
63
+ if Puppet.features.posix?
64
+ procname = Puppet::Util::Execution.execute(["ps", "-p", lock_pid, "-o", "comm="]).strip
65
+ args = Puppet::Util::Execution.execute(["ps", "-p", lock_pid, "-o", "args="]).strip
66
+ @lockfile.unlock unless procname =~ /ruby/ && args =~ /puppet/ || procname =~ /puppet(-.*)?$/
67
+ elsif Puppet.features.microsoft_windows?
68
+ # On Windows, we're checking if the filesystem path name of the running
69
+ # process is our vendored ruby:
70
+ exe_path = Puppet::Util::Windows::Process::get_process_image_name_by_pid(lock_pid)
71
+ @lockfile.unlock unless exe_path =~ /\\bin\\ruby.exe$/
58
72
  end
59
73
  end
60
74
  private :clear_if_stale
@@ -10,6 +10,10 @@ module Puppet::Util::Windows::Process
10
10
  WAIT_INTERVAL = 200
11
11
  # https://docs.microsoft.com/en-us/windows/desktop/ProcThread/process-creation-flags
12
12
  CREATE_NO_WINDOW = 0x08000000
13
+ #https://docs.microsoft.com/en-us/windows/desktop/ProcThread/process-security-and-access-rights
14
+ PROCESS_QUERY_INFORMATION = 0x0400
15
+ # https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
16
+ MAX_PATH_LENGTH = 32767
13
17
 
14
18
  def execute(command, arguments, stdin, stdout, stderr)
15
19
  create_args = {
@@ -62,6 +66,26 @@ module Puppet::Util::Windows::Process
62
66
  end
63
67
  module_function :get_current_process
64
68
 
69
+ def open_process(desired_access, inherit_handle, process_id, &block)
70
+ phandle = nil
71
+ inherit = inherit_handle ? FFI::WIN32_TRUE : FFI::WIN32_FALSE
72
+ begin
73
+ phandle = OpenProcess(desired_access, inherit, process_id)
74
+ if phandle == FFI::Pointer::NULL_HANDLE
75
+ raise Puppet::Util::Windows::Error.new(
76
+ "OpenProcess(#{desired_access.to_s(8)}, #{inherit}, #{process_id})")
77
+ end
78
+
79
+ yield phandle
80
+ ensure
81
+ FFI::WIN32.CloseHandle(phandle) if phandle
82
+ end
83
+
84
+ # phandle has had CloseHandle called against it, so nothing to return
85
+ nil
86
+ end
87
+ module_function :open_process
88
+
65
89
  def open_process_token(handle, desired_access, &block)
66
90
  token_handle = nil
67
91
  begin
@@ -97,6 +121,32 @@ module Puppet::Util::Windows::Process
97
121
  end
98
122
  module_function :with_process_token
99
123
 
124
+ def get_process_image_name_by_pid(pid)
125
+ image_name = ""
126
+
127
+ open_process(PROCESS_QUERY_INFORMATION, false, pid) do |phandle|
128
+
129
+ FFI::MemoryPointer.new(:dword, 1) do |exe_name_length_ptr|
130
+ # UTF is 2 bytes/char:
131
+ max_chars = MAX_PATH_LENGTH + 1
132
+ exe_name_length_ptr.write_dword(max_chars)
133
+ FFI::MemoryPointer.new(:wchar, max_chars) do |exe_name_ptr|
134
+ use_win32_path_format = 0
135
+ result = QueryFullProcessImageNameW(phandle, use_win32_path_format, exe_name_ptr, exe_name_length_ptr)
136
+ if result == FFI::WIN32_FALSE
137
+ raise Puppet::Util::Windows::Error.new(
138
+ "QueryFullProcessImageNameW(phandle, #{use_win32_path_format}, " +
139
+ "exe_name_ptr, #{max_chars}")
140
+ end
141
+ image_name = exe_name_ptr.read_wide_string(exe_name_length_ptr.read_dword)
142
+ end
143
+ end
144
+ end
145
+
146
+ image_name
147
+ end
148
+ module_function :get_process_image_name_by_pid
149
+
100
150
  def lookup_privilege_value(name, system_name = '', &block)
101
151
  FFI::MemoryPointer.new(LUID.size) do |luid_ptr|
102
152
  result = LookupPrivilegeValueW(
@@ -366,6 +416,16 @@ module Puppet::Util::Windows::Process
366
416
  attach_function_private :SetEnvironmentVariableW,
367
417
  [:lpcwstr, :lpcwstr], :win32_bool
368
418
 
419
+ # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320(v=vs.85).aspx
420
+ # HANDLE WINAPI OpenProcess(
421
+ # _In_ DWORD DesiredAccess,
422
+ # _In_ BOOL InheritHandle,
423
+ # _In_ DWORD ProcessId
424
+ # );
425
+ ffi_lib :kernel32
426
+ attach_function_private :OpenProcess,
427
+ [:dword, :win32_bool, :dword], :handle
428
+
369
429
  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379295(v=vs.85).aspx
370
430
  # BOOL WINAPI OpenProcessToken(
371
431
  # _In_ HANDLE ProcessHandle,
@@ -376,6 +436,16 @@ module Puppet::Util::Windows::Process
376
436
  attach_function_private :OpenProcessToken,
377
437
  [:handle, :dword, :phandle], :win32_bool
378
438
 
439
+ # https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-queryfullprocessimagenamew
440
+ # BOOL WINAPI QueryFullProcessImageName(
441
+ # _In_ HANDLE hProcess,
442
+ # _In_ DWORD dwFlags,
443
+ # _Out_ LPWSTR lpExeName,
444
+ # _In_ PDWORD lpdwSize,
445
+ # );
446
+ ffi_lib :kernel32
447
+ attach_function_private :QueryFullProcessImageNameW,
448
+ [:handle, :dword, :lpwstr, :pdword], :win32_bool
379
449
 
380
450
  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379261(v=vs.85).aspx
381
451
  # typedef struct _LUID {
@@ -232,7 +232,7 @@ module Puppet::Util::Windows
232
232
  begin
233
233
  case type
234
234
  when Win32::Registry::REG_SZ, Win32::Registry::REG_EXPAND_SZ
235
- result = [ type, data_ptr.read_wide_string(string_length) ]
235
+ result = [ type, sanitize(data_ptr.read_wide_string(string_length)) ]
236
236
  when Win32::Registry::REG_MULTI_SZ
237
237
  result = [ type, data_ptr.read_wide_string(string_length).split(/\0/) ]
238
238
  when Win32::Registry::REG_BINARY
@@ -312,6 +312,12 @@ module Puppet::Util::Windows
312
312
  result
313
313
  end
314
314
 
315
+ def sanitize(value)
316
+ # Replace null bytes with a space
317
+ value.gsub!("\x00", ' ')
318
+ value
319
+ end
320
+
315
321
  ffi_convention :stdcall
316
322
 
317
323
  # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724862(v=vs.85).aspx
@@ -75,15 +75,18 @@ module Puppet::Util::Windows::User
75
75
  module_function :password_is?
76
76
 
77
77
  def logon_user(name, password, &block)
78
- fLOGON32_LOGON_NETWORK = 3
79
78
  fLOGON32_PROVIDER_DEFAULT = 0
79
+ fLOGON32_LOGON_INTERACTIVE = 2
80
+ fLOGON32_LOGON_NETWORK = 3
80
81
 
81
82
  token = nil
82
83
  begin
83
84
  FFI::MemoryPointer.new(:handle, 1) do |token_pointer|
84
- if LogonUserW(wide_string(name), wide_string('.'), password.nil? ? FFI::Pointer::NULL : wide_string(password),
85
- fLOGON32_LOGON_NETWORK, fLOGON32_PROVIDER_DEFAULT, token_pointer) == FFI::WIN32_FALSE
86
- raise Puppet::Util::Windows::Error.new(_("Failed to logon user %{name}") % { name: name.inspect })
85
+ #try logon using network else try logon using interactive mode
86
+ if logon_user_by_logon_type(name, password, fLOGON32_LOGON_NETWORK, fLOGON32_PROVIDER_DEFAULT, token_pointer) == FFI::WIN32_FALSE
87
+ if logon_user_by_logon_type(name, password, fLOGON32_LOGON_INTERACTIVE, fLOGON32_PROVIDER_DEFAULT, token_pointer) == FFI::WIN32_FALSE
88
+ raise Puppet::Util::Windows::Error.new(_("Failed to logon user %{name}") % {name: name.inspect})
89
+ end
87
90
  end
88
91
 
89
92
  yield token = token_pointer.read_handle
@@ -95,8 +98,15 @@ module Puppet::Util::Windows::User
95
98
  # token has been closed by this point
96
99
  true
97
100
  end
101
+
98
102
  module_function :logon_user
99
103
 
104
+ def self.logon_user_by_logon_type(name, password, logon_type, logon_provider, token)
105
+ LogonUserW(wide_string(name), wide_string('.'), password.nil? ? FFI::Pointer::NULL : wide_string(password), logon_type, logon_provider, token)
106
+ end
107
+
108
+ private_class_method :logon_user_by_logon_type
109
+
100
110
  def load_profile(user, password)
101
111
  logon_user(user, password) do |token|
102
112
  FFI::MemoryPointer.from_string_to_wide_string(user) do |lpUserName|