puppet 6.16.0-universal-darwin → 6.17.0-universal-darwin

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 (138) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -2
  3. data/Gemfile.lock +10 -10
  4. data/README.md +2 -2
  5. data/lib/puppet/agent.rb +2 -2
  6. data/lib/puppet/application/agent.rb +14 -3
  7. data/lib/puppet/configurer.rb +20 -12
  8. data/lib/puppet/confine.rb +1 -1
  9. data/lib/puppet/defaults.rb +25 -8
  10. data/lib/puppet/file_serving/http_metadata.rb +13 -1
  11. data/lib/puppet/file_serving/metadata.rb +4 -1
  12. data/lib/puppet/file_serving/terminus_selector.rb +7 -8
  13. data/lib/puppet/file_system/file_impl.rb +1 -1
  14. data/lib/puppet/file_system/uniquefile.rb +8 -16
  15. data/lib/puppet/forge.rb +1 -1
  16. data/lib/puppet/forge/cache.rb +1 -1
  17. data/lib/puppet/forge/repository.rb +3 -7
  18. data/lib/puppet/http/client.rb +5 -0
  19. data/lib/puppet/http/redirector.rb +9 -7
  20. data/lib/puppet/http/response.rb +19 -0
  21. data/lib/puppet/indirector.rb +1 -1
  22. data/lib/puppet/indirector/file_content/rest.rb +1 -1
  23. data/lib/puppet/indirector/file_metadata/http.rb +24 -5
  24. data/lib/puppet/indirector/file_metadata/rest.rb +2 -2
  25. data/lib/puppet/indirector/request.rb +1 -1
  26. data/lib/puppet/network/http/api/indirected_routes.rb +1 -1
  27. data/lib/puppet/network/http/api/master/v3/environment.rb +3 -0
  28. data/lib/puppet/network/http/connection_adapter.rb +6 -4
  29. data/lib/puppet/parser/ast/leaf.rb +5 -5
  30. data/lib/puppet/parser/ast/pops_bridge.rb +0 -4
  31. data/lib/puppet/parser/compiler.rb +1 -1
  32. data/lib/puppet/parser/compiler/catalog_validator/env_relationship_validator.rb +2 -0
  33. data/lib/puppet/parser/compiler/catalog_validator/site_validator.rb +2 -0
  34. data/lib/puppet/parser/environment_compiler.rb +4 -1
  35. data/lib/puppet/parser/resource.rb +3 -2
  36. data/lib/puppet/parser/resource/param.rb +6 -0
  37. data/lib/puppet/pops/evaluator/evaluator_impl.rb +5 -5
  38. data/lib/puppet/pops/issues.rb +5 -0
  39. data/lib/puppet/pops/resource/resource_type_impl.rb +2 -0
  40. data/lib/puppet/pops/validation/checker4_0.rb +10 -0
  41. data/lib/puppet/pops/validation/validator_factory_4_0.rb +1 -0
  42. data/lib/puppet/provider/package/aptitude.rb +1 -1
  43. data/lib/puppet/provider/package/yum.rb +1 -1
  44. data/lib/puppet/provider/service/windows.rb +23 -7
  45. data/lib/puppet/provider/user/useradd.rb +11 -4
  46. data/lib/puppet/reports/http.rb +2 -0
  47. data/lib/puppet/resource.rb +2 -1
  48. data/lib/puppet/resource/type.rb +8 -0
  49. data/lib/puppet/ssl/ssl_context.rb +2 -2
  50. data/lib/puppet/ssl/ssl_provider.rb +20 -1
  51. data/lib/puppet/test/test_helper.rb +8 -10
  52. data/lib/puppet/trusted_external.rb +29 -1
  53. data/lib/puppet/type.rb +12 -5
  54. data/lib/puppet/type/file.rb +38 -13
  55. data/lib/puppet/type/file/checksum.rb +4 -4
  56. data/lib/puppet/type/file/source.rb +4 -4
  57. data/lib/puppet/type/service.rb +49 -0
  58. data/lib/puppet/util.rb +39 -15
  59. data/lib/puppet/util/checksums.rb +19 -4
  60. data/lib/puppet/util/fileparsing.rb +2 -2
  61. data/lib/puppet/util/provider_features.rb +1 -1
  62. data/lib/puppet/util/reference.rb +1 -1
  63. data/lib/puppet/util/windows/api_types.rb +45 -32
  64. data/lib/puppet/util/windows/eventlog.rb +1 -6
  65. data/lib/puppet/util/windows/principal.rb +8 -6
  66. data/lib/puppet/util/windows/registry.rb +11 -11
  67. data/lib/puppet/util/windows/service.rb +43 -26
  68. data/lib/puppet/util/windows/user.rb +23 -8
  69. data/lib/puppet/version.rb +1 -1
  70. data/locales/puppet.pot +249 -221
  71. data/man/man5/puppet.conf.5 +19 -8
  72. data/man/man8/puppet-agent.8 +2 -2
  73. data/man/man8/puppet-apply.8 +1 -1
  74. data/man/man8/puppet-catalog.8 +1 -1
  75. data/man/man8/puppet-config.8 +1 -1
  76. data/man/man8/puppet-describe.8 +1 -1
  77. data/man/man8/puppet-device.8 +1 -1
  78. data/man/man8/puppet-doc.8 +1 -1
  79. data/man/man8/puppet-epp.8 +1 -1
  80. data/man/man8/puppet-facts.8 +1 -1
  81. data/man/man8/puppet-filebucket.8 +1 -1
  82. data/man/man8/puppet-generate.8 +1 -1
  83. data/man/man8/puppet-help.8 +1 -1
  84. data/man/man8/puppet-key.8 +1 -1
  85. data/man/man8/puppet-lookup.8 +1 -1
  86. data/man/man8/puppet-man.8 +1 -1
  87. data/man/man8/puppet-module.8 +1 -1
  88. data/man/man8/puppet-node.8 +1 -1
  89. data/man/man8/puppet-parser.8 +1 -1
  90. data/man/man8/puppet-plugin.8 +1 -1
  91. data/man/man8/puppet-report.8 +1 -1
  92. data/man/man8/puppet-resource.8 +1 -1
  93. data/man/man8/puppet-script.8 +1 -1
  94. data/man/man8/puppet-ssl.8 +1 -1
  95. data/man/man8/puppet-status.8 +1 -1
  96. data/man/man8/puppet.8 +2 -2
  97. data/spec/integration/application/agent_spec.rb +89 -0
  98. data/spec/integration/defaults_spec.rb +1 -2
  99. data/spec/integration/network/http_pool_spec.rb +26 -9
  100. data/spec/integration/parser/compiler_spec.rb +11 -0
  101. data/spec/integration/type/file_spec.rb +1 -1
  102. data/spec/integration/util/windows/registry_spec.rb +7 -7
  103. data/spec/integration/util/windows/user_spec.rb +40 -5
  104. data/spec/unit/configurer/fact_handler_spec.rb +4 -4
  105. data/spec/unit/context/trusted_information_spec.rb +10 -4
  106. data/spec/unit/file_serving/http_metadata_spec.rb +37 -14
  107. data/spec/unit/file_serving/terminus_selector_spec.rb +45 -26
  108. data/spec/unit/http/client_spec.rb +64 -8
  109. data/spec/unit/http/response_spec.rb +6 -0
  110. data/spec/unit/indirector/file_metadata/http_spec.rb +27 -0
  111. data/spec/unit/indirector/request_spec.rb +1 -1
  112. data/spec/unit/interface_spec.rb +3 -3
  113. data/spec/unit/network/http/api/indirected_routes_spec.rb +2 -1
  114. data/spec/unit/network/http/connection_spec.rb +42 -32
  115. data/spec/unit/parser/ast/block_expression_spec.rb +1 -1
  116. data/spec/unit/parser/environment_compiler_spec.rb +7 -0
  117. data/spec/unit/parser/scope_spec.rb +1 -1
  118. data/spec/unit/pops/evaluator/evaluating_parser_spec.rb +15 -1
  119. data/spec/unit/pops/loaders/loaders_spec.rb +1 -1
  120. data/spec/unit/pops/types/type_calculator_spec.rb +1 -11
  121. data/spec/unit/provider/service/windows_spec.rb +22 -14
  122. data/spec/unit/provider/user/openbsd_spec.rb +1 -0
  123. data/spec/unit/provider/user/useradd_spec.rb +22 -16
  124. data/spec/unit/resource_spec.rb +3 -3
  125. data/spec/unit/ssl/ssl_provider_spec.rb +69 -43
  126. data/spec/unit/test/test_helper_spec.rb +17 -0
  127. data/spec/unit/transaction/report_spec.rb +1 -1
  128. data/spec/unit/type/file/source_spec.rb +3 -3
  129. data/spec/unit/type/file_spec.rb +122 -96
  130. data/spec/unit/type/service_spec.rb +176 -0
  131. data/spec/unit/type_spec.rb +50 -0
  132. data/spec/unit/util/checksums_spec.rb +16 -0
  133. data/spec/unit/util/windows/api_types_spec.rb +104 -40
  134. data/spec/unit/util/windows/service_spec.rb +4 -4
  135. data/spec/unit/util_spec.rb +3 -3
  136. data/spec/unit/x509/cert_provider_spec.rb +1 -1
  137. metadata +5 -5
  138. data/spec/integration/test/test_helper_spec.rb +0 -31
@@ -26,21 +26,20 @@ module Util
26
26
 
27
27
  extend Puppet::Util::SymbolicFileMode
28
28
 
29
- def default_env
30
- Puppet::Util::Platform.windows? ?
31
- :windows :
32
- :posix
33
- end
34
- module_function :default_env
29
+ DEFAULT_ENV = if Puppet::Util::Platform.windows?
30
+ :windows
31
+ else
32
+ :posix
33
+ end.freeze
35
34
 
36
35
  # @param name [String] The name of the environment variable to retrieve
37
36
  # @param mode [Symbol] Which operating system mode to use e.g. :posix or :windows. Use nil to autodetect
38
37
  # @return [String] Value of the specified environment variable. nil if it does not exist
39
38
  # @api private
40
- def get_env(name, mode = default_env)
39
+ def get_env(name, mode = DEFAULT_ENV)
41
40
  if mode == :windows
42
- Puppet::Util::Windows::Process.get_environment_strings.each do |key, value |
43
- if name.casecmp(key) == 0 then
41
+ Puppet::Util::Windows::Process.get_environment_strings.find do |key, value|
42
+ if name.casecmp(key) == 0
44
43
  return value
45
44
  end
46
45
  end
@@ -54,7 +53,7 @@ module Util
54
53
  # @param mode [Symbol] Which operating system mode to use e.g. :posix or :windows. Use nil to autodetect
55
54
  # @return [Hash] A hashtable of all environment variables
56
55
  # @api private
57
- def get_environment(mode = default_env)
56
+ def get_environment(mode = DEFAULT_ENV)
58
57
  case mode
59
58
  when :posix
60
59
  ENV.to_hash
@@ -69,7 +68,7 @@ module Util
69
68
  # Removes all environment variables
70
69
  # @param mode [Symbol] Which operating system mode to use e.g. :posix or :windows. Use nil to autodetect
71
70
  # @api private
72
- def clear_environment(mode = default_env)
71
+ def clear_environment(mode = DEFAULT_ENV)
73
72
  case mode
74
73
  when :posix
75
74
  ENV.clear
@@ -87,7 +86,7 @@ module Util
87
86
  # @param value [String] The value to set the variable to. nil deletes the environment variable
88
87
  # @param mode [Symbol] Which operating system mode to use e.g. :posix or :windows. Use nil to autodetect
89
88
  # @api private
90
- def set_env(name, value = nil, mode = default_env)
89
+ def set_env(name, value = nil, mode = DEFAULT_ENV)
91
90
  case mode
92
91
  when :posix
93
92
  ENV[name] = value
@@ -102,7 +101,7 @@ module Util
102
101
  # @param name [Hash] Environment variables to merge into the existing environment. nil values will remove the variable
103
102
  # @param mode [Symbol] Which operating system mode to use e.g. :posix or :windows. Use nil to autodetect
104
103
  # @api private
105
- def merge_environment(env_hash, mode = default_env)
104
+ def merge_environment(env_hash, mode = DEFAULT_ENV)
106
105
  case mode
107
106
  when :posix
108
107
  env_hash.each { |name, val| ENV[name.to_s] = val }
@@ -296,6 +295,11 @@ module Util
296
295
  AbsolutePathWindows = %r!^(?:(?:[A-Z]:#{slash})|(?:#{slash}#{slash}#{label}#{slash}#{label})|(?:#{slash}#{slash}\?#{slash}#{label}))!io
297
296
  AbsolutePathPosix = %r!^/!
298
297
  def absolute_path?(path, platform=nil)
298
+ unless path.is_a?(String)
299
+ Puppet.warning("Cannot check if #{path} is an absolute path because it is a '#{path.class}' and not a String'")
300
+ return false
301
+ end
302
+
299
303
  # Ruby only sets File::ALT_SEPARATOR on Windows and the Ruby standard
300
304
  # library uses that to test what platform it's on. Normally in Puppet we
301
305
  # would use Puppet.features.microsoft_windows?, but this method needs to
@@ -356,7 +360,7 @@ module Util
356
360
 
357
361
  # CGI.unescape doesn't handle space rules properly in uri paths
358
362
  # URI.unescape does, but returns strings in their original encoding
359
- path = URI.unescape(uri.path.encode(Encoding::UTF_8))
363
+ path = uri_unescape(uri.path.encode(Encoding::UTF_8))
360
364
 
361
365
  if Puppet::Util::Platform.windows? && uri.scheme == 'file'
362
366
  if uri.host && !uri.host.empty?
@@ -460,7 +464,18 @@ module Util
460
464
  # + should be left unencoded
461
465
  # URI::parse and URI::Generic.build don't like paths encoded with CGI.escape
462
466
  # URI.escape does not change / to %2F and : to %3A like CGI.escape
463
- encoded += URI.escape(parts[:path]) unless parts[:path].nil?
467
+ #
468
+ # URI.escape is obsolete in Ruby 2.7. Ignore this error until we're able to
469
+ # switch to a different escape mechanism. If this is JRuby, we can't mask
470
+ # the error message, because this isn't thread safe. JRuby shouldn't be
471
+ # using Ruby 2.7 or raising the warning anyway.
472
+ orig_verbose = $VERBOSE
473
+ $VERBOSE = nil unless Puppet::Util::Platform.jruby?
474
+ begin
475
+ encoded += URI.escape(parts[:path]) unless parts[:path].nil?
476
+ ensure
477
+ $VERBOSE = orig_verbose unless Puppet::Util::Platform.jruby?
478
+ end
464
479
 
465
480
  # each query parameter
466
481
  if !parts[:query].nil?
@@ -479,6 +494,15 @@ module Util
479
494
  end
480
495
  module_function :uri_encode
481
496
 
497
+ def uri_unescape(path)
498
+ orig_verbose = $VERBOSE
499
+ $VERBOSE = nil unless Puppet::Util::Platform.jruby?
500
+ return URI.unescape(path)
501
+ ensure
502
+ $VERBOSE = orig_verbose unless Puppet::Util::Platform.jruby?
503
+ end
504
+ module_function :uri_unescape
505
+
482
506
  def safe_posix_fork(stdin=$stdin, stdout=$stdout, stderr=$stderr, &block)
483
507
  child_pid = Kernel.fork do
484
508
  STDIN.reopen(stdin)
@@ -7,11 +7,26 @@ require 'time'
7
7
  module Puppet::Util::Checksums
8
8
  module_function
9
9
 
10
+ KNOWN_CHECKSUMS = [
11
+ :sha256, :sha256lite,
12
+ :md5, :md5lite,
13
+ :sha1, :sha1lite,
14
+ :sha512,
15
+ :sha384,
16
+ :sha224,
17
+ :mtime, :ctime, :none
18
+ ].freeze
19
+
10
20
  # It's not a good idea to use some of these in some contexts: for example, I
11
21
  # wouldn't try bucketing a file using the :none checksum type.
12
22
  def known_checksum_types
13
- [:sha256, :sha256lite, :md5, :md5lite, :sha1, :sha1lite, :sha512, :sha384, :sha224,
14
- :mtime, :ctime, :none]
23
+ KNOWN_CHECKSUMS
24
+ end
25
+
26
+ def valid_checksum?(type, value)
27
+ !!send("#{type}?", value)
28
+ rescue NoMethodError
29
+ false
15
30
  end
16
31
 
17
32
  class FakeChecksum
@@ -223,7 +238,7 @@ module Puppet::Util::Checksums
223
238
 
224
239
  # Return the :mtime timestamp of a file.
225
240
  def mtime_file(filename)
226
- Puppet::FileSystem.stat(filename).send(:mtime)
241
+ Puppet::FileSystem.stat(filename).mtime
227
242
  end
228
243
 
229
244
  # by definition this doesn't exist
@@ -293,7 +308,7 @@ module Puppet::Util::Checksums
293
308
 
294
309
  # Return the :ctime of a file.
295
310
  def ctime_file(filename)
296
- Puppet::FileSystem.stat(filename).send(:ctime)
311
+ Puppet::FileSystem.stat(filename).ctime
297
312
  end
298
313
 
299
314
  def ctime_stream(&block)
@@ -300,7 +300,7 @@ module Puppet::Util::FileParsing
300
300
  def record_line(name, options, &block)
301
301
  raise ArgumentError, _("Must include a list of fields") unless options.include?(:fields)
302
302
 
303
- record = FileRecord.new(:record, options, &block)
303
+ record = FileRecord.new(:record, **options, &block)
304
304
  record.name = name.intern
305
305
 
306
306
  new_line_type(record)
@@ -315,7 +315,7 @@ module Puppet::Util::FileParsing
315
315
  def text_line(name, options, &block)
316
316
  raise ArgumentError, _("You must provide a :match regex for text lines") unless options.include?(:match)
317
317
 
318
- record = FileRecord.new(:text, options, &block)
318
+ record = FileRecord.new(:text, **options, &block)
319
319
  record.name = name.intern
320
320
 
321
321
  new_line_type(record)
@@ -62,7 +62,7 @@ module Puppet::Util::ProviderFeatures
62
62
  @features ||= {}
63
63
  raise Puppet::DevError, _("Feature %{name} is already defined") % { name: name } if @features.include?(name)
64
64
  begin
65
- obj = ProviderFeature.new(name, docs, hash)
65
+ obj = ProviderFeature.new(name, docs, **hash)
66
66
  @features[obj.name] = obj
67
67
  rescue ArgumentError => detail
68
68
  error = ArgumentError.new(
@@ -15,7 +15,7 @@ class Puppet::Util::Reference
15
15
  end
16
16
 
17
17
  def self.newreference(name, options = {}, &block)
18
- ref = self.new(name, options, &block)
18
+ ref = self.new(name, **options, &block)
19
19
  instance_hash(:reference)[name.intern] = ref
20
20
 
21
21
  ref
@@ -19,15 +19,11 @@ module Puppet::Util::Windows::APITypes
19
19
 
20
20
  class ::FFI::Pointer
21
21
  NULL_HANDLE = 0
22
+ WCHAR_NULL = "\0\0".encode('UTF-16LE').freeze
22
23
 
23
24
  def self.from_string_to_wide_string(str, &block)
24
25
  str = Puppet::Util::Windows::String.wide_string(str)
25
- FFI::MemoryPointer.new(:byte, str.bytesize) do |ptr|
26
- # uchar here is synonymous with byte
27
- ptr.put_array_of_uchar(0, str.bytes.to_a)
28
-
29
- yield ptr
30
- end
26
+ FFI::MemoryPointer.from_wide_string(str, &block)
31
27
 
32
28
  # ptr has already had free called, so nothing to return
33
29
  nil
@@ -53,11 +49,17 @@ module Puppet::Util::Windows::APITypes
53
49
  alias_method :read_word, :read_uint16
54
50
  alias_method :read_array_of_wchar, :read_array_of_uint16
55
51
 
56
- def read_wide_string(char_length, dst_encoding = Encoding::UTF_8, encode_options = {})
52
+ def read_wide_string(char_length, dst_encoding = Encoding::UTF_8, strip = false, encode_options = {})
57
53
  # char_length is number of wide chars (typically excluding NULLs), *not* bytes
58
54
  str = get_bytes(0, char_length * 2).force_encoding('UTF-16LE')
55
+
56
+ if strip
57
+ i = str.index(WCHAR_NULL)
58
+ str = str[0, i] if i
59
+ end
60
+
59
61
  str.encode(dst_encoding, str.encoding, encode_options)
60
- rescue Exception => e
62
+ rescue EncodingError => e
61
63
  Puppet.debug "Unable to convert value #{str.nil? ? 'nil' : str.dump} to encoding #{dst_encoding} due to #{e.inspect}"
62
64
  raise
63
65
  end
@@ -68,32 +70,31 @@ module Puppet::Util::Windows::APITypes
68
70
  # null_terminator = :double_null, then the terminating sequence is four bytes of zero. This is UNIT32 = 0
69
71
  # @param encode_options [Hash] Accepts the same option hash that may be passed to String#encode in Ruby
70
72
  def read_arbitrary_wide_string_up_to(max_char_length = 512, null_terminator = :single_null, encode_options = {})
71
- if null_terminator != :single_null && null_terminator != :double_null
72
- raise _("Unable to read wide strings with %{null_terminator} terminal nulls") % { null_terminator: null_terminator }
73
- end
74
-
75
- terminator_width = null_terminator == :single_null ? 1 : 2
76
- reader_method = null_terminator == :single_null ? :get_uint16 : :get_uint32
77
-
78
- # Look for a null terminating characters; if found, read up to that null (exclusive)
79
- (0...max_char_length - terminator_width).each do |i|
80
- return read_wide_string(i, Encoding::UTF_8, encode_options) if send(reader_method, (i * 2)) == 0
81
- end
82
-
83
- # String is longer than the max; read just to the max
84
- read_wide_string(max_char_length, Encoding::UTF_8, encode_options)
73
+ idx = case null_terminator
74
+ when :single_null
75
+ # find index of wide null between 0 and max (exclusive)
76
+ (0...max_char_length).find do |i|
77
+ get_uint16(i * 2) == 0
78
+ end
79
+ when :double_null
80
+ # find index of double-wide null between 0 and max - 1 (exclusive)
81
+ (0...max_char_length - 1).find do |i|
82
+ get_uint32(i * 2) == 0
83
+ end
84
+ else
85
+ raise _("Unable to read wide strings with %{null_terminator} terminal nulls") % { null_terminator: null_terminator }
86
+ end
87
+
88
+ read_wide_string(idx || max_char_length, Encoding::UTF_8, false, encode_options)
85
89
  end
86
90
 
87
91
  def read_win32_local_pointer(&block)
88
- ptr = nil
92
+ ptr = read_pointer
89
93
  begin
90
- ptr = read_pointer
91
94
  yield ptr
92
95
  ensure
93
- if ptr && ! ptr.null?
94
- if FFI::WIN32::LocalFree(ptr.address) != FFI::Pointer::NULL_HANDLE
95
- Puppet.debug "LocalFree memory leak"
96
- end
96
+ if !ptr.null? && FFI::WIN32::LocalFree(ptr.address) != FFI::Pointer::NULL_HANDLE
97
+ Puppet.debug "LocalFree memory leak"
97
98
  end
98
99
  end
99
100
 
@@ -102,23 +103,35 @@ module Puppet::Util::Windows::APITypes
102
103
  end
103
104
 
104
105
  def read_com_memory_pointer(&block)
105
- ptr = nil
106
+ ptr = read_pointer
106
107
  begin
107
- ptr = read_pointer
108
108
  yield ptr
109
109
  ensure
110
- FFI::WIN32::CoTaskMemFree(ptr) if ptr && ! ptr.null?
110
+ FFI::WIN32::CoTaskMemFree(ptr) unless ptr.null?
111
111
  end
112
112
 
113
113
  # ptr has already had CoTaskMemFree called, so nothing to return
114
114
  nil
115
115
  end
116
116
 
117
-
118
117
  alias_method :write_dword, :write_uint32
119
118
  alias_method :write_word, :write_uint16
120
119
  end
121
120
 
121
+ class FFI::MemoryPointer
122
+ # Return a MemoryPointer that points to wide string. This is analogous to the
123
+ # FFI::MemoryPointer.from_string method.
124
+ def self.from_wide_string(wstr)
125
+ ptr = FFI::MemoryPointer.new(:uchar, wstr.bytesize + 2)
126
+ ptr.put_array_of_uchar(0, wstr.bytes.to_a)
127
+ ptr.put_uint16(wstr.bytesize, 0)
128
+
129
+ yield ptr if block_given?
130
+
131
+ ptr
132
+ end
133
+ end
134
+
122
135
  # FFI Types
123
136
  # https://github.com/ffi/ffi/wiki/Types
124
137
 
@@ -140,12 +140,7 @@ class Puppet::Util::Windows::EventLog
140
140
  # @api private
141
141
  def from_string_to_wide_string(str, &block)
142
142
  str = wide_string(str)
143
- FFI::MemoryPointer.new(:uchar, str.bytesize) do |ptr|
144
- # uchar here is synonymous with byte
145
- ptr.put_array_of_uchar(0, str.bytes.to_a)
146
-
147
- yield ptr
148
- end
143
+ FFI::MemoryPointer.from_wide_string(str) { |ptr| yield ptr }
149
144
 
150
145
  # ptr has already had free called, so nothing to return
151
146
  nil
@@ -41,6 +41,7 @@ module Puppet::Util::Windows::SID
41
41
  # = 8 + max sub identifiers (15) * 4
42
42
  MAXIMUM_SID_BYTE_LENGTH = 68
43
43
 
44
+ ERROR_INVALID_PARAMETER = 87
44
45
  ERROR_INSUFFICIENT_BUFFER = 122
45
46
 
46
47
  def self.lookup_account_name(system_name = nil, account_name)
@@ -48,9 +49,7 @@ module Puppet::Util::Windows::SID
48
49
  begin
49
50
  if system_name
50
51
  system_name_wide = Puppet::Util::Windows::String.wide_string(system_name)
51
- # uchar here is synonymous with byte
52
- system_name_ptr = FFI::MemoryPointer.new(:byte, system_name_wide.bytesize)
53
- system_name_ptr.put_array_of_uchar(0, system_name_wide.bytes.to_a)
52
+ system_name_ptr = FFI::MemoryPointer.from_wide_string(system_name_wide)
54
53
  end
55
54
 
56
55
  FFI::MemoryPointer.from_string_to_wide_string(account_name) do |account_name_ptr|
@@ -101,9 +100,7 @@ module Puppet::Util::Windows::SID
101
100
  begin
102
101
  if system_name
103
102
  system_name_wide = Puppet::Util::Windows::String.wide_string(system_name)
104
- # uchar here is synonymous with byte
105
- system_name_ptr = FFI::MemoryPointer.new(:byte, system_name_wide.bytesize)
106
- system_name_ptr.put_array_of_uchar(0, system_name_wide.bytes.to_a)
103
+ system_name_ptr = FFI::MemoryPointer.from_wide_string(system_name_wide)
107
104
  end
108
105
 
109
106
  FFI::MemoryPointer.new(:byte, sid_bytes.length) do |sid_ptr|
@@ -112,6 +109,11 @@ module Puppet::Util::Windows::SID
112
109
  FFI::MemoryPointer.new(:uint32, 1) do |name_use_enum_ptr|
113
110
 
114
111
  sid_ptr.write_array_of_uchar(sid_bytes)
112
+
113
+ if Puppet::Util::Windows::SID.IsValidSid(sid_ptr) == FFI::WIN32_FALSE
114
+ raise Puppet::Util::Windows::Error.new(_('Byte array for lookup_account_sid is invalid: %{sid_bytes}') % { sid_bytes: sid_bytes }, ERROR_INVALID_PARAMETER)
115
+ end
116
+
115
117
  success = LookupAccountSidW(system_name_ptr, sid_ptr, FFI::Pointer::NULL, name_length_ptr,
116
118
  FFI::Pointer::NULL, domain_length_ptr, name_use_enum_ptr)
117
119
  last_error = FFI.errno
@@ -110,13 +110,16 @@ module Puppet::Util::Windows
110
110
 
111
111
  private
112
112
 
113
- def reg_enum_key(key, index, max_key_length = Win32::Registry::Constants::MAX_KEY_LENGTH)
113
+ # max number of wide characters including NULL terminator
114
+ MAX_KEY_CHAR_LENGTH = 255 + 1
115
+
116
+ def reg_enum_key(key, index, max_key_char_length = MAX_KEY_CHAR_LENGTH)
114
117
  subkey, filetime = nil, nil
115
118
 
116
119
  FFI::MemoryPointer.new(:dword) do |subkey_length_ptr|
117
120
  FFI::MemoryPointer.new(FFI::WIN32::FILETIME.size) do |filetime_ptr|
118
- FFI::MemoryPointer.new(:wchar, max_key_length) do |subkey_ptr|
119
- subkey_length_ptr.write_dword(max_key_length)
121
+ FFI::MemoryPointer.new(:wchar, max_key_char_length) do |subkey_ptr|
122
+ subkey_length_ptr.write_dword(max_key_char_length)
120
123
 
121
124
  # RegEnumKeyEx cannot be called twice to properly size the buffer
122
125
  result = RegEnumKeyExW(key.hkey, index,
@@ -141,7 +144,10 @@ module Puppet::Util::Windows
141
144
  [subkey, filetime]
142
145
  end
143
146
 
144
- def reg_enum_value(key, index, max_value_length = Win32::Registry::Constants::MAX_VALUE_LENGTH)
147
+ # max number of wide characters including NULL terminator
148
+ MAX_VALUE_CHAR_LENGTH = 16383 + 1
149
+
150
+ def reg_enum_value(key, index, max_value_length = MAX_VALUE_CHAR_LENGTH)
145
151
  subkey, type, data = nil, nil, nil
146
152
 
147
153
  FFI::MemoryPointer.new(:dword) do |subkey_length_ptr|
@@ -234,7 +240,7 @@ module Puppet::Util::Windows
234
240
  begin
235
241
  case type
236
242
  when Win32::Registry::REG_SZ, Win32::Registry::REG_EXPAND_SZ
237
- result = [ type, sanitize(data_ptr.read_wide_string(string_length)) ]
243
+ result = [ type, data_ptr.read_wide_string(string_length, Encoding::UTF_8, true) ]
238
244
  when Win32::Registry::REG_MULTI_SZ
239
245
  result = [ type, data_ptr.read_wide_string(string_length).split(/\0/) ]
240
246
  when Win32::Registry::REG_BINARY
@@ -314,12 +320,6 @@ module Puppet::Util::Windows
314
320
  result
315
321
  end
316
322
 
317
- def sanitize(value)
318
- # Replace null bytes with a space
319
- value.tr!("\x00", ' ')
320
- value
321
- end
322
-
323
323
  ffi_convention :stdcall
324
324
 
325
325
  # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724862(v=vs.85).aspx
@@ -440,43 +440,60 @@ module Puppet::Util::Windows
440
440
  end
441
441
  module_function :service_start_type
442
442
 
443
- # Change the startup mode of a windows service
443
+ # Query the configuration of a service using QueryServiceConfigW
444
+ # to find its current logon account
444
445
  #
445
- # @param [String] service_name the name of the service to modify
446
- # @param [Integer] startup_type a code corresponding to a start type for
447
- # windows service, see the "Service start type codes" section in the
448
- # Puppet::Util::Windows::Service file for the list of available codes
449
- # @param [Bool] delayed whether the service should be started with a delay
450
- def set_startup_mode(service_name, startup_type, delayed=false)
451
- startup_code = SERVICE_START_TYPES.key(startup_type)
452
- if startup_code.nil?
453
- raise Puppet::Error.new(_("Unknown start type %{start_type}") % {startup_type: startup_type.to_s})
446
+ # @return [String] logon_account account currently set for the service's logon
447
+ # in the format "DOMAIN\Account" or ".\Account" if it's a local account
448
+ def logon_account(service_name)
449
+ open_service(service_name, SC_MANAGER_CONNECT, SERVICE_QUERY_CONFIG) do |service|
450
+ query_config(service) do |config|
451
+ return config[:lpServiceStartName].read_arbitrary_wide_string_up_to(Puppet::Util::Windows::ADSI::User::MAX_USERNAME_LENGTH)
452
+ end
454
453
  end
454
+ end
455
+ module_function :logon_account
456
+
457
+ # Set the startup configuration of a windows service
458
+ #
459
+ # @param [String] service_name the name of the service to modify
460
+ # @param [Hash] options the configuration to be applied. Expected option keys:
461
+ # - [Integer] startup_type a code corresponding to a start type for
462
+ # windows service, see the "Service start type codes" section in the
463
+ # Puppet::Util::Windows::Service file for the list of available codes
464
+ # - [String] logon_account the account to be used by the service for logon
465
+ # - [String] logon_password the provided logon_account's password to be used by the service for logon
466
+ # - [Bool] delayed whether the service should be started with a delay
467
+ def set_startup_configuration(service_name, options: {})
468
+ options[:startup_type] = SERVICE_START_TYPES.key(options[:startup_type]) || SERVICE_NO_CHANGE
469
+ options[:logon_account] = wide_string(options[:logon_account]) || FFI::Pointer::NULL
470
+ options[:logon_password] = wide_string(options[:logon_password]) || FFI::Pointer::NULL
471
+
455
472
  open_service(service_name, SC_MANAGER_CONNECT, SERVICE_CHANGE_CONFIG) do |service|
456
- # Currently the only thing puppet's API can really manage
457
- # in this list is dwStartType (the third param). Thus no
458
- # generic function was written to make use of all the params
459
- # since the API as-is couldn't use them anyway
460
473
  success = ChangeServiceConfigW(
461
474
  service,
462
- SERVICE_NO_CHANGE, # dwServiceType
463
- startup_code, # dwStartType
464
- SERVICE_NO_CHANGE, # dwErrorControl
465
- FFI::Pointer::NULL, # lpBinaryPathName
466
- FFI::Pointer::NULL, # lpLoadOrderGroup
467
- FFI::Pointer::NULL, # lpdwTagId
468
- FFI::Pointer::NULL, # lpDependencies
469
- FFI::Pointer::NULL, # lpServiceStartName
470
- FFI::Pointer::NULL, # lpPassword
471
- FFI::Pointer::NULL # lpDisplayName
475
+ SERVICE_NO_CHANGE, # dwServiceType
476
+ options[:startup_type], # dwStartType
477
+ SERVICE_NO_CHANGE, # dwErrorControl
478
+ FFI::Pointer::NULL, # lpBinaryPathName
479
+ FFI::Pointer::NULL, # lpLoadOrderGroup
480
+ FFI::Pointer::NULL, # lpdwTagId
481
+ FFI::Pointer::NULL, # lpDependencies
482
+ options[:logon_account], # lpServiceStartName
483
+ options[:logon_password], # lpPassword
484
+ FFI::Pointer::NULL # lpDisplayName
472
485
  )
473
486
  if success == FFI::WIN32_FALSE
474
487
  raise Puppet::Util::Windows::Error.new(_("Failed to update service configuration"))
475
488
  end
476
489
  end
477
- set_startup_mode_delayed(service_name, delayed)
490
+
491
+ if options[:startup_type]
492
+ options[:delayed] ||= false
493
+ set_startup_mode_delayed(service_name, options[:delayed])
494
+ end
478
495
  end
479
- module_function :set_startup_mode
496
+ module_function :set_startup_configuration
480
497
 
481
498
  # enumerate over all services in all states and return them as a hash
482
499
  #