chef 18.0.185-x64-mingw-ucrt → 18.1.29-x64-mingw-ucrt

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -3
  3. data/chef.gemspec +3 -2
  4. data/lib/chef/application/base.rb +18 -2
  5. data/lib/chef/chef_fs/file_system.rb +21 -7
  6. data/lib/chef/client.rb +23 -6
  7. data/lib/chef/http/authenticator.rb +117 -34
  8. data/lib/chef/mixin/proxified_socket.rb +1 -1
  9. data/lib/chef/property.rb +8 -3
  10. data/lib/chef/provider/launchd.rb +1 -0
  11. data/lib/chef/provider/package/yum/yum_helper.py +5 -17
  12. data/lib/chef/provider/yum_repository.rb +13 -1
  13. data/lib/chef/resource/apt_repository.rb +20 -2
  14. data/lib/chef/resource/bash.rb +13 -0
  15. data/lib/chef/resource/dsc_script.rb +1 -1
  16. data/lib/chef/resource/launchd.rb +4 -1
  17. data/lib/chef/resource/macos_userdefaults.rb +3 -3
  18. data/lib/chef/resource/rhsm_register.rb +2 -1
  19. data/lib/chef/resource/selinux_fcontext.rb +1 -1
  20. data/lib/chef/resource/selinux_permissive.rb +1 -1
  21. data/lib/chef/resource/selinux_port.rb +1 -1
  22. data/lib/chef/resource/selinux_state.rb +1 -1
  23. data/lib/chef/resource/service.rb +1 -1
  24. data/lib/chef/resource/user.rb +2 -2
  25. data/lib/chef/resource/windows_user_privilege.rb +14 -10
  26. data/lib/chef/resource/yum_repository.rb +4 -0
  27. data/lib/chef/version.rb +1 -1
  28. data/spec/functional/assets/yumrepo-empty/repodata/01a3b489a465bcac22a43492163df43451dc6ce47d27f66de289756b91635523-filelists.sqlite.bz2 +0 -0
  29. data/spec/functional/assets/yumrepo-empty/repodata/401dc19bda88c82c403423fb835844d64345f7e95f5b9835888189c03834cc93-filelists.xml.gz +0 -0
  30. data/spec/functional/assets/yumrepo-empty/repodata/5dc1e6e73c84803f059bb3065e684e56adfc289a7e398946574d79dac6643945-primary.sqlite.bz2 +0 -0
  31. data/spec/functional/assets/yumrepo-empty/repodata/6bf9672d0862e8ef8b8ff05a2fd0208a922b1f5978e6589d87944c88259cb670-other.xml.gz +0 -0
  32. data/spec/functional/assets/yumrepo-empty/repodata/7c36572015e075add2b38b900837bcdbb8a504130ddff49b2351a7fc0affa3d4-other.sqlite.bz2 +0 -0
  33. data/spec/functional/assets/yumrepo-empty/repodata/dabe2ce5481d23de1f4f52bdcfee0f9af98316c9e0de2ce8123adeefa0dd08b9-primary.xml.gz +0 -0
  34. data/spec/functional/assets/yumrepo-empty/repodata/repomd.xml +55 -0
  35. data/spec/functional/resource/yum_package_spec.rb +16 -0
  36. data/spec/integration/client/client_spec.rb +22 -16
  37. data/spec/integration/client/fips_spec.rb +20 -0
  38. data/spec/spec_helper.rb +4 -0
  39. data/spec/support/platform_helpers.rb +4 -0
  40. data/spec/unit/chef_fs/file_system_spec.rb +2 -0
  41. data/spec/unit/client_spec.rb +26 -2
  42. data/spec/unit/compliance/runner_spec.rb +8 -0
  43. data/spec/unit/http/authenticator_spec.rb +64 -11
  44. data/spec/unit/property/validation_spec.rb +30 -0
  45. data/spec/unit/provider/apt_repository_spec.rb +26 -5
  46. data/spec/unit/resource/yum_repository_spec.rb +4 -0
  47. metadata +21 -14
  48. data/distro/powershell/chef/chef.psm1 +0 -459
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c1122c2620c6af08992ae09d67f314ac5b96734570a645e2f9cd8884f2dd377
4
- data.tar.gz: 20bc9d8e1ee79d6eb62643889f4eb03bacdc9da1bac21d3f80df2da1687a887e
3
+ metadata.gz: 937611f440a583a253b01525212c4d6214da8f25aee63891ab928b9a3d53de67
4
+ data.tar.gz: 7d69aeb9e4df6f13d76b464c08178f9935bc9be01eed136539d5d123315f0fea
5
5
  SHA512:
6
- metadata.gz: 3afda04ca448d98ff93d09766628ce5482a8cefda00b1f08c86b46f5049e8d332e89dae62ac32226bf141ab27aac4491214dd855f65c96dcfb9b7493e93c96f4
7
- data.tar.gz: 1081a2c6449d32ff1f922aeacdb25e1cfecd71d1b47491f626e8db533d9c29a64a06f8833f30ba82f4c606397061dd1392e994241e836b2e84c38a3f15edb59c
6
+ metadata.gz: 2bc789e6367a7ba5277f905d460f7b5390854f62c98f0ba07270e6659e7e70bb92b2d4fc64c8d87e524a972da75657f64022d3f01305558e00f90348f8d6811a
7
+ data.tar.gz: dea177b817a903ef86d7b8bacef0aca894ca778c26615339a041186276da976eb23abede8c47c1a3b9ee459a392961ff75fbc8532475186a1d21d77a1a528fde
data/Gemfile CHANGED
@@ -37,9 +37,6 @@ group(:omnibus_package, :pry) do
37
37
  gem "pry-stack_explorer"
38
38
  end
39
39
 
40
- # proxifier gem is busted on ruby 3.1 and seems abandoned so use git fork of gem
41
- gem "proxifier", git: "https://github.com/chef/ruby-proxifier", branch: "lcg/ruby-3"
42
-
43
40
  # Everything except AIX and Windows
44
41
  group(:ruby_shadow) do
45
42
  # if ruby-shadow does a release that supports ruby-3.0 this can be removed
data/chef.gemspec CHANGED
@@ -49,7 +49,8 @@ Gem::Specification.new do |s|
49
49
  s.add_dependency "net-ftp" # remote_file resource
50
50
  s.add_dependency "erubis", "~> 2.7" # template resource / cookbook syntax check
51
51
  s.add_dependency "diff-lcs", ">= 1.2.4", "!= 1.4.0", "< 1.6.0" # 1.4 breaks output. Used in lib/chef/util/diff
52
- s.add_dependency "ffi-libarchive", "~> 1.0", ">= 1.0.3" # archive_file resource
52
+ # s.add_dependency "ffi-libarchive", "~> 1.0", ">= 1.0.3" # archive_file resource
53
+ s.add_dependency "ffi-libarchive", "~> 1.1", ">= 1.1.3"
53
54
  s.add_dependency "chef-zero", ">= 14.0.11"
54
55
  s.add_dependency "chef-vault" # chef-vault resources and helpers
55
56
 
@@ -61,7 +62,7 @@ Gem::Specification.new do |s|
61
62
  s.add_dependency "unf_ext", ">= 0.0.8.2" # This is ruby31 compatible ucrt gem version
62
63
  s.add_dependency "corefoundation", "~> 0.3.4" # macos_userdefaults resource
63
64
 
64
- s.add_dependency "proxifier", "~> 1.0"
65
+ s.add_dependency "proxifier2", "~> 1.1"
65
66
 
66
67
  s.add_dependency "aws-sdk-s3", "~> 1.91" # s3 recipe-url support
67
68
  s.add_dependency "aws-sdk-secretsmanager", "~> 1.46"
@@ -386,8 +386,10 @@ class Chef::Application::Base < Chef::Application
386
386
  elsif uri.scheme == "s3"
387
387
  require "aws-sdk-s3" unless defined?(Aws::S3)
388
388
 
389
- s3 = Aws::S3::Client.new
390
- object = s3.get_object(bucket: uri.hostname, key: uri.path[1..-1])
389
+ bucket_name = uri.hostname
390
+ s3 = Aws::S3::Client.new(region: s3_bucket_location(bucket_name))
391
+
392
+ object = s3.get_object(bucket: bucket_name, key: uri.path[1..-1])
391
393
  File.open(path, "wb") do |f|
392
394
  f.write(object.body.read)
393
395
  end
@@ -403,6 +405,20 @@ class Chef::Application::Base < Chef::Application
403
405
  end
404
406
  end
405
407
 
408
+ def s3_bucket_location(bucket_name)
409
+ s3 = Aws::S3::Client.new(region: aws_api_region)
410
+
411
+ resp = s3.get_bucket_location(bucket: bucket_name)
412
+ resp.location_constraint
413
+ rescue Aws::S3::Errors::AccessDenied => _e
414
+ Chef::Log.warn("Missing s3:GetBucketLocation privilege, trying currently configured region #{aws_api_region}")
415
+ aws_api_region
416
+ end
417
+
418
+ def aws_api_region
419
+ ENV["AWS_REGION"] || Aws.shared_config.region || Aws::EC2Metadata.new.get("/latest/meta-data/placement/region")
420
+ end
421
+
406
422
  def interval_run_chef_client
407
423
  if Chef::Config[:daemonize]
408
424
  Chef::Daemon.daemonize(ChefUtils::Dist::Infra::PRODUCT)
@@ -140,17 +140,18 @@ class Chef
140
140
  def self.copy_to(pattern, src_root, dest_root, recurse_depth, options, ui = nil, format_path = nil)
141
141
  found_result = false
142
142
  error = false
143
+ result = {}
143
144
  list_pairs(pattern, src_root, dest_root).parallel_each do |src, dest|
144
145
  found_result = true
145
146
  new_dest_parent = get_or_create_parent(dest, options, ui, format_path)
146
- child_error = copy_entries(src, dest, new_dest_parent, recurse_depth, options, ui, format_path)
147
+ child_error, result = copy_entries(src, dest, new_dest_parent, recurse_depth, options, ui, format_path)
147
148
  error ||= child_error
148
149
  end
149
150
  if !found_result && pattern.exact_path
150
151
  ui.error "#{pattern}: No such file or directory on remote or local" if ui
151
152
  error = true
152
153
  end
153
- error
154
+ [error, result]
154
155
  end
155
156
 
156
157
  # Yield entries for children that are in either +a_root+ or +b_root+, with
@@ -275,6 +276,7 @@ class Chef
275
276
  # case we shouldn't waste time trying PUT if we know the file doesn't
276
277
  # exist.
277
278
  # Will need to decide how that works with checksums, though.
279
+ result = { "total" => 0, "success_count" => 0, "failed" => [] }
278
280
  error = false
279
281
  begin
280
282
  dest_path = format_path.call(dest_entry) if ui
@@ -290,6 +292,8 @@ class Chef
290
292
  dest_entry.delete(true)
291
293
  ui.output "Deleted extra entry #{dest_path} (purge is on)" if ui
292
294
  rescue Chef::ChefFS::FileSystem::NotFoundError
295
+ failure = { "src_path" => src_path, "reason" => "Entry #{dest_path} does not exist" }
296
+ result["failed"].append(failure)
293
297
  ui.output "Entry #{dest_path} does not exist. Nothing to do. (purge is on)" if ui
294
298
  end
295
299
  end
@@ -323,7 +327,7 @@ class Chef
323
327
  if recurse_depth != 0
324
328
  src_entry.children.parallel_each do |src_child|
325
329
  new_dest_child = new_dest_dir.child(src_child.name)
326
- child_error = copy_entries(src_child, new_dest_child, new_dest_dir, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
330
+ child_error, result = copy_entries(src_child, new_dest_child, new_dest_dir, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
327
331
  error ||= child_error
328
332
  end
329
333
  end
@@ -339,14 +343,15 @@ class Chef
339
343
 
340
344
  else
341
345
  # Both exist.
342
-
343
346
  # If the entry can do a copy directly, do that.
344
347
  if dest_entry.respond_to?(:copy_from)
345
348
  if options[:force] || compare(src_entry, dest_entry)[0] == false
346
349
  if options[:dry_run]
347
350
  ui.output "Would update #{dest_path}" if ui
348
351
  else
352
+ result["total"] += 1
349
353
  dest_entry.copy_from(src_entry, options)
354
+ result["success_count"] += 1
350
355
  ui.output "Updated #{dest_path}" if ui
351
356
  end
352
357
  end
@@ -359,7 +364,7 @@ class Chef
359
364
  # If both are directories, recurse into their children
360
365
  if recurse_depth != 0
361
366
  child_pairs(src_entry, dest_entry).parallel_each do |src_child, dest_child|
362
- child_error = copy_entries(src_child, dest_child, dest_entry, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
367
+ child_error, result = copy_entries(src_child, dest_child, dest_entry, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
363
368
  error ||= child_error
364
369
  end
365
370
  end
@@ -373,7 +378,6 @@ class Chef
373
378
  ui.error("File #{src_path} is a regular file while file #{dest_path} is a directory\n") if ui
374
379
  return
375
380
  else
376
-
377
381
  # Both are files! Copy them unless we're sure they are the same.'
378
382
  if options[:diff] == false
379
383
  should_copy = false
@@ -389,7 +393,9 @@ class Chef
389
393
  ui.output "Would update #{dest_path}" if ui
390
394
  else
391
395
  src_value = src_entry.read if src_value.nil?
396
+ result["total"] += 1
392
397
  dest_entry.write(src_value)
398
+ result["success_count"] += 1
393
399
  ui.output "Updated #{dest_path}" if ui
394
400
  end
395
401
  end
@@ -397,17 +403,25 @@ class Chef
397
403
  end
398
404
  end
399
405
  rescue RubyFileError => e
406
+ failure = { "src_path" => src_path, "reason" => e.reason }
407
+ result["failed"].append(failure)
400
408
  ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui
401
409
  rescue DefaultEnvironmentCannotBeModifiedError => e
410
+ failure = { "src_path" => src_path, "reason" => e.reason }
411
+ result["failed"].append(failure)
402
412
  ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui
403
413
  rescue OperationFailedError => e
414
+ failure = { "src_path" => src_path, "reason" => e.reason }
415
+ result["failed"].append(failure)
404
416
  ui.error "#{format_path.call(e.entry)} failed to #{e.operation}: #{e.message}" if ui
405
417
  error = true
406
418
  rescue OperationNotAllowedError => e
419
+ failure = { "src_path" => src_path, "reason" => e.reason }
420
+ result["failed"].append(failure)
407
421
  ui.error "#{format_path.call(e.entry)} #{e.reason}." if ui
408
422
  error = true
409
423
  end
410
- error
424
+ [error, result]
411
425
  end
412
426
 
413
427
  def get_or_create_parent(entry, options, ui, format_path)
data/lib/chef/client.rb CHANGED
@@ -66,6 +66,15 @@ class Chef
66
66
  class Client
67
67
  CRYPT_EXPORTABLE = 0x00000001
68
68
 
69
+ # adding these
70
+ # certstore 65536 == 0x00010000 == CurrentUser
71
+ # certstore 131072 == 0x00020000 == LocalMachine
72
+ # Reference: https://github.com/chef/win32-certstore/blob/main/lib/win32/certstore/mixin/crypto.rb#L90
73
+ CERT_SYSTEM_STORE_LOCAL_MACHINE = 0x00020000
74
+ CERT_SYSTEM_STORE_CURRENT_USER = 0x00010000
75
+ CERT_SYSTEM_STORE_SERVICES = 0x00050000
76
+ CERT_SYSTEM_STORE_USERS = 0x00060000
77
+
69
78
  attr_reader :local_context
70
79
 
71
80
  extend Chef::Mixin::Deprecation
@@ -674,9 +683,15 @@ class Chef
674
683
 
675
684
  # In the brave new world of No Certs On Disk, we want to put the pem file into Keychain or the Certstore
676
685
  # But is it already there?
686
+ # We're solving the multi-user scenario where both a system/admin user can run on the box but also someone without
687
+ # admin rights can also run correctly locally.
677
688
  def check_certstore_for_key(cert_name)
678
689
  require "win32-certstore"
679
- win32certstore = ::Win32::Certstore.open("MY")
690
+ if Chef::Config[:auth_key_registry_type] == "user"
691
+ win32certstore = ::Win32::Certstore.open("MY", store_location: CERT_SYSTEM_STORE_CURRENT_USER)
692
+ else
693
+ win32certstore = ::Win32::Certstore.open("MY")
694
+ end
680
695
  win32certstore.search("#{cert_name}")
681
696
  end
682
697
 
@@ -783,8 +798,6 @@ class Chef
783
798
  require "time" unless defined?(Time)
784
799
  autoload :URI, "uri"
785
800
 
786
- # KeyMigration.instance.key_migrated = true
787
-
788
801
  node = Chef::Config[:node_name]
789
802
  d = Time.now
790
803
  if d.month == 10 || d.month == 11 || d.month == 12
@@ -818,9 +831,13 @@ class Chef
818
831
  require "win32-certstore"
819
832
  tempfile = Tempfile.new("#{Chef::Config[:node_name]}.pfx")
820
833
  File.open(tempfile, "wb") { |f| f.print new_pfx.to_der }
821
-
822
- store = ::Win32::Certstore.open("MY")
823
- store.add_pfx(tempfile, password, CRYPT_EXPORTABLE)
834
+ # Need to determine where to store the key
835
+ if Chef::Config[:auth_key_registry_type] == "user"
836
+ win32certstore = ::Win32::Certstore.open("MY", store_location: CERT_SYSTEM_STORE_CURRENT_USER)
837
+ else
838
+ win32certstore = ::Win32::Certstore.open("MY")
839
+ end
840
+ win32certstore.add_pfx(tempfile, password, CRYPT_EXPORTABLE)
824
841
  tempfile.unlink
825
842
  end
826
843
 
@@ -102,12 +102,12 @@ class Chef
102
102
  self.class.get_cert_password
103
103
  end
104
104
 
105
- def encrypt_pfx_pass
106
- self.class.encrypt_pfx_pass
105
+ def encrypt_pfx_pass_with_vector
106
+ self.class.encrypt_pfx_pass_with_vector
107
107
  end
108
108
 
109
- def decrypt_pfx_pass
110
- self.class.decrypt_pfx_pass
109
+ def decrypt_pfx_pass_with_vector
110
+ self.class.decrypt_pfx_pass_with_vector
111
111
  end
112
112
 
113
113
  # Detects if a private key exists in a certificate repository like Keychain (macOS) or Certificate Store (Windows)
@@ -123,9 +123,18 @@ class Chef
123
123
  end
124
124
  end
125
125
 
126
+ def self.get_cert_user
127
+ Chef::Config[:auth_key_registry_type] == "user" ? store = "CurrentUser" : store = "LocalMachine"
128
+ end
129
+
130
+ def self.get_registry_user
131
+ Chef::Config[:auth_key_registry_type] == "user" ? store = "HKEY_CURRENT_USER" : store = "HKEY_LOCAL_MACHINE"
132
+ end
133
+
126
134
  def self.check_certstore_for_key(client_name)
135
+ store = get_cert_user
127
136
  powershell_code = <<~CODE
128
- $cert = Get-ChildItem -path cert:\\LocalMachine\\My -Recurse -Force | Where-Object { $_.Subject -Match "chef-#{client_name}" } -ErrorAction Stop
137
+ $cert = Get-ChildItem -path cert:\\#{store}\\My -Recurse -Force | Where-Object { $_.Subject -Match "chef-#{client_name}" } -ErrorAction Stop
129
138
  if (($cert.HasPrivateKey -eq $true) -and ($cert.PrivateKey.Key.ExportPolicy -ne "NonExportable")) {
130
139
  return $true
131
140
  }
@@ -164,49 +173,86 @@ class Chef
164
173
  end
165
174
 
166
175
  def self.get_cert_password
176
+ store = get_registry_user
167
177
  @win32registry = Chef::Win32::Registry.new
168
- path = "HKEY_LOCAL_MACHINE\\Software\\Progress\\Authentication"
178
+ path = "#{store}\\Software\\Progress\\Authentication"
169
179
  # does the registry key even exist?
170
- present = @win32registry.get_values(path)
171
- if present.nil? || present.empty?
180
+ # password_blob should be an array of hashes
181
+ password_blob = @win32registry.get_values(path)
182
+ if password_blob.nil? || password_blob.empty?
172
183
  raise Chef::Exceptions::Win32RegKeyMissing
173
184
  end
174
185
 
175
- present.each do |secret|
176
- if secret[:name] == "PfxPass"
177
- password = decrypt_pfx_pass(secret[:data])
178
- return password
186
+ # Did someone have just the password stored in the registry?
187
+ raw_data = password_blob.map { |x| x[:data] }
188
+ vector = raw_data[2]
189
+ if !!vector
190
+ decrypted_password = decrypt_pfx_pass_with_vector(password_blob)
191
+ else
192
+ decrypted_password = decrypt_pfx_pass_with_password(password_blob)
193
+ if !!decrypted_password
194
+ migrate_pass_to_use_vector(decrypted_password)
195
+ else
196
+ Chef::Log.error("Failed to retrieve certificate password")
179
197
  end
180
198
  end
181
-
182
- raise Chef::Exceptions::Win32RegKeyMissing
183
-
199
+ decrypted_password
184
200
  rescue Chef::Exceptions::Win32RegKeyMissing
185
201
  # if we don't have a password, log that and generate one
186
- Chef::Log.warn "Authentication Hive and values not present in registry, creating them now"
187
- new_path = "HKEY_LOCAL_MACHINE\\Software\\Progress\\Authentication"
202
+ store = get_registry_user
203
+ new_path = "#{store}\\Software\\Progress\\Authentication"
188
204
  unless @win32registry.key_exists?(new_path)
189
205
  @win32registry.create_key(new_path, true)
190
206
  end
191
- require "securerandom" unless defined?(SecureRandom)
192
- size = 14
193
- password = SecureRandom.alphanumeric(size)
194
- encrypted_pass = encrypt_pfx_pass(password)
195
- values = { name: "PfxPass", type: :string, data: encrypted_pass }
196
- @win32registry.set_value(new_path, values)
197
- password
207
+ create_and_store_new_password
198
208
  end
199
209
 
200
- def self.encrypt_pfx_pass(password)
210
+ def self.encrypt_pfx_pass_with_vector(password)
201
211
  powershell_code = <<~CODE
202
- $encrypted_string = ConvertTo-SecureString "#{password}" -AsPlainText -Force
203
- $secure_string = ConvertFrom-SecureString $encrypted_string
204
- return $secure_string
212
+ $AES = [System.Security.Cryptography.Aes]::Create()
213
+ $key_temp = [System.Convert]::ToBase64String($AES.Key)
214
+ $iv_temp = [System.Convert]::ToBase64String($AES.IV)
215
+ $encryptor = $AES.CreateEncryptor()
216
+ [System.Byte[]]$Bytes = [System.Text.Encoding]::Unicode.GetBytes("#{password}")
217
+ $EncryptedBytes = $encryptor.TransformFinalBlock($Bytes,0,$Bytes.Length)
218
+ $EncryptedBase64String = [System.Convert]::ToBase64String($EncryptedBytes)
219
+ # create array of encrypted pass, key, iv
220
+ $password_blob = @($EncryptedBase64String, $key_temp, $iv_temp)
221
+ return $password_blob
205
222
  CODE
206
223
  powershell_exec!(powershell_code).result
207
224
  end
208
225
 
209
- def self.decrypt_pfx_pass(password)
226
+ def self.decrypt_pfx_pass_with_vector(password_blob)
227
+ raw_data = password_blob.map { |x| x[:data] }
228
+ password = raw_data[0]
229
+ key = raw_data[1]
230
+ vector = raw_data[2]
231
+
232
+ powershell_code = <<~CODE
233
+ $KeyBytes = [System.Convert]::FromBase64String("#{key}")
234
+ $IVBytes = [System.Convert]::FromBase64String("#{vector}")
235
+ $aes = [System.Security.Cryptography.Aes]::Create()
236
+ $aes.Key = $KeyBytes
237
+ $aes.IV = $IVBytes
238
+ $EncryptedBytes = [System.Convert]::FromBase64String("#{password}")
239
+ $Decryptor = $aes.CreateDecryptor()
240
+ $DecryptedBytes = $Decryptor.TransformFinalBlock($EncryptedBytes,0,$EncryptedBytes.Length)
241
+ $DecryptedString = [System.Text.Encoding]::Unicode.GetString($DecryptedBytes)
242
+ return $DecryptedString
243
+ CODE
244
+ results = powershell_exec!(powershell_code).result
245
+ end
246
+
247
+ def self.decrypt_pfx_pass_with_password(password_blob)
248
+ password = ""
249
+ password_blob.each do |secret|
250
+ if secret[:name] == "PfxPass"
251
+ password = secret[:data]
252
+ else
253
+ Chef::Log.error("Failed to retrieve a password for the private key")
254
+ end
255
+ end
210
256
  powershell_code = <<~CODE
211
257
  $secure_string = "#{password}" | ConvertTo-SecureString
212
258
  $string = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR((($secure_string))))
@@ -215,14 +261,49 @@ class Chef
215
261
  powershell_exec!(powershell_code).result
216
262
  end
217
263
 
218
- def self.retrieve_certificate_key(client_name)
219
- require "openssl" unless defined?(OpenSSL)
264
+ def self.migrate_pass_to_use_vector(password)
265
+ store = get_cert_user
266
+ corrected_store = (store == "CurrentUser" ? "HKCU" : "HKLM")
267
+ powershell_code = <<~CODE
268
+ Remove-ItemProperty -Path "#{corrected_store}:\\Software\\Progress\\Authentication" -Name "PfXPass"
269
+ CODE
270
+ powershell_exec!(powershell_code)
271
+ create_and_store_new_password(password)
272
+ end
220
273
 
274
+ # This method name is a bit of a misnomer. We call it to legit create a new password using the vector format.
275
+ # But we also call it with a password that needs to be migrated to use the vector format too.
276
+ def self.create_and_store_new_password(password = nil)
277
+ @win32registry = Chef::Win32::Registry.new
278
+ store = get_registry_user
279
+ path = "#{store}\\Software\\Progress\\Authentication"
280
+ if password.nil?
281
+ require "securerandom" unless defined?(SecureRandom)
282
+ size = 14
283
+ password = SecureRandom.alphanumeric(size)
284
+ end
285
+ encrypted_blob = encrypt_pfx_pass_with_vector(password)
286
+ encrypted_password = encrypted_blob[0]
287
+ key = encrypted_blob[1]
288
+ vector = encrypted_blob[2]
289
+ values = [
290
+ { name: "PfxPass", type: :string, data: encrypted_password },
291
+ { name: "PfxKey", type: :string, data: key },
292
+ { name: "PfxIV", type: :string, data: vector },
293
+ ]
294
+ values.each do |i|
295
+ @win32registry.set_value(path, i)
296
+ end
297
+ password
298
+ end
299
+
300
+ def self.retrieve_certificate_key(client_name)
221
301
  if ChefUtils.windows?
302
+ require "openssl" unless defined?(OpenSSL)
222
303
  password = get_cert_password
223
304
  return false unless password
224
305
 
225
- if check_certstore_for_key(client_name)
306
+ if !!check_certstore_for_key(client_name)
226
307
  ps_blob = powershell_exec!(get_the_key_ps(client_name, password)).result
227
308
  file_path = ps_blob["PSPath"].split("::")[1]
228
309
  pkcs = OpenSSL::PKCS12.new(File.binread(file_path), password)
@@ -252,10 +333,11 @@ class Chef
252
333
  end
253
334
 
254
335
  def self.get_the_key_ps(client_name, password)
336
+ store = get_cert_user
255
337
  powershell_code = <<~CODE
256
338
  Try {
257
339
  $my_pwd = ConvertTo-SecureString -String "#{password}" -Force -AsPlainText;
258
- $cert = Get-ChildItem -path cert:\\LocalMachine\\My -Recurse | Where-Object { $_.Subject -match "chef-#{client_name}$" } -ErrorAction Stop;
340
+ $cert = Get-ChildItem -path cert:\\#{store}\\My -Recurse | Where-Object { $_.Subject -match "chef-#{client_name}$" } -ErrorAction Stop;
259
341
  $tempfile = [System.IO.Path]::GetTempPath() + "export_pfx.pfx";
260
342
  Export-PfxCertificate -Cert $cert -Password $my_pwd -FilePath $tempfile;
261
343
  }
@@ -266,8 +348,9 @@ class Chef
266
348
  end
267
349
 
268
350
  def self.delete_old_key_ps(client_name)
351
+ store = get_cert_user
269
352
  powershell_code = <<~CODE
270
- Get-ChildItem -path cert:\\LocalMachine\\My -Recurse | Where-Object { $_.Subject -match "chef-#{client_name}$" } | Remove-Item -ErrorAction Stop;
353
+ Get-ChildItem -path cert:\\#{store}\\My -Recurse | Where-Object { $_.Subject -match "chef-#{client_name}$" } | Remove-Item -ErrorAction Stop;
271
354
  CODE
272
355
  end
273
356
 
@@ -15,7 +15,7 @@
15
15
  # limitations under the License.
16
16
  #
17
17
 
18
- require "proxifier"
18
+ require "proxifier2"
19
19
  require "chef-config/mixin/fuzzy_hostname_matcher"
20
20
 
21
21
  class Chef
data/lib/chef/property.rb CHANGED
@@ -307,7 +307,7 @@ class Chef
307
307
  #
308
308
  def required?(action = nil)
309
309
  if !action.nil? && options[:required].is_a?(Array)
310
- options[:required].include?(action)
310
+ (options[:required] & Array(action)).any?
311
311
  else
312
312
  !!options[:required]
313
313
  end
@@ -426,7 +426,7 @@ class Chef
426
426
  end
427
427
  end
428
428
 
429
- if value.nil? && required?
429
+ if value.nil? && required?(resource_action(resource))
430
430
  raise Chef::Exceptions::ValidationFailed, "#{name} is a required property"
431
431
  else
432
432
  value
@@ -455,7 +455,7 @@ class Chef
455
455
  Chef.deprecated(:property, options[:deprecated])
456
456
  end
457
457
 
458
- if value.nil? && required?
458
+ if value.nil? && required?(resource_action(resource))
459
459
  raise Chef::Exceptions::ValidationFailed, "#{name} is a required property"
460
460
  else
461
461
  value
@@ -768,5 +768,10 @@ class Chef
768
768
  end
769
769
  visitor.call(value)
770
770
  end
771
+
772
+ # action from resource, if available
773
+ def resource_action(resource)
774
+ resource.action if resource.respond_to?(:action)
775
+ end
771
776
  end
772
777
  end
@@ -153,6 +153,7 @@ class Chef
153
153
  "program" => "Program",
154
154
  "program_arguments" => "ProgramArguments",
155
155
  "abandon_process_group" => "AbandonProcessGroup",
156
+ "associated_bundle_identifiers" => "AssociatedBundleIdentifiers",
156
157
  "debug" => "Debug",
157
158
  "disabled" => "Disabled",
158
159
  "enable_globbing" => "EnableGlobbing",
@@ -53,16 +53,14 @@ def install_only_packages(base, name):
53
53
  outpipe.flush()
54
54
 
55
55
  def query(base, command):
56
- enabled_repos = base.repos.listEnabled()
57
-
58
56
  # Handle any repocontrols passed in with our options
59
57
 
60
58
  if 'repos' in command:
61
59
  for repo in command['repos']:
62
60
  if 'enable' in repo:
63
61
  base.repos.enableRepo(repo['enable'])
64
- if 'disable' in repo:
65
- base.repos.disableRepo(repo['disable'])
62
+ if 'disable' in repo:
63
+ base.repos.disableRepo(repo['disable'])
66
64
 
67
65
  args = { 'name': command['provides'] }
68
66
  do_nevra = False
@@ -94,8 +92,8 @@ def query(base, command):
94
92
  # then the result was searchNevra'd. please be extremely careful if attempting to fix that
95
93
  # since searchNevra does not support prco tuples.
96
94
  if bool(re.search('\\s+', command['provides'])):
97
- # handles flags (<, >, =, etc) and versions, but no wildcareds
98
- # raises error for any invalid input like: 'FOO BAR BAZ'
95
+ # handles flags (<, >, =, etc) and versions, but no wildcards
96
+ # raises error for any invalid input like: 'FOO BAR BAZ'
99
97
  pkgs = obj.getProvides(*string_to_prco_tuple(command['provides']))
100
98
  elif do_nevra:
101
99
  # now if we're given version or arch properties explicitly, then we do a SearchNevra.
@@ -123,16 +121,6 @@ def query(base, command):
123
121
  outpipe.write("%(n)s %(e)s:%(v)s-%(r)s %(a)s\n" % { 'n': pkg.name, 'e': pkg.epoch, 'v': pkg.version, 'r': pkg.release, 'a': pkg.arch })
124
122
  outpipe.flush()
125
123
 
126
- # Reset any repos we were passed in enablerepo/disablerepo to the original state in enabled_repos
127
- if 'repos' in command:
128
- for repo in command['repos']:
129
- if 'enable' in repo:
130
- if base.repos.getRepo(repo['enable']) not in enabled_repos:
131
- base.repos.disableRepo(repo['enable'])
132
- if 'disable' in repo:
133
- if base.repos.getRepo(repo['disable']) in enabled_repos:
134
- base.repos.enableRepo(repo['disable'])
135
-
136
124
  # the design of this helper is that it should try to be 'brittle' and fail hard and exit in order
137
125
  # to keep process tables clean. additional error handling should probably be added to the retry loop
138
126
  # on the ruby side.
@@ -178,7 +166,7 @@ try:
178
166
 
179
167
  try:
180
168
  command = json.loads(line)
181
- except ValueError, e:
169
+ except ValueError as e:
182
170
  raise RuntimeError("bad json parse")
183
171
 
184
172
  if base is None:
@@ -44,7 +44,12 @@ class Chef
44
44
  mode new_resource.mode
45
45
  if new_resource.make_cache
46
46
  notifies :run, "execute[yum clean metadata #{new_resource.repositoryid}]", :immediately if new_resource.clean_metadata || new_resource.clean_headers
47
- notifies :run, "execute[yum-makecache-#{new_resource.repositoryid}]", :immediately
47
+ # makecache fast only works on non-dnf systems.
48
+ if !which "dnf" && new_resource.makecache_fast
49
+ notifies :run, "execute[yum-makecache-fast-#{new_resource.repositoryid}]", :immediately
50
+ else
51
+ notifies :run, "execute[yum-makecache-#{new_resource.repositoryid}]", :immediately
52
+ end
48
53
  notifies :flush_cache, "package[package-cache-reload-#{new_resource.repositoryid}]", :immediately
49
54
  end
50
55
  end
@@ -63,6 +68,13 @@ class Chef
63
68
  only_if { new_resource.enabled }
64
69
  end
65
70
 
71
+ # download only the minimum required metadata
72
+ execute "yum-makecache-fast-#{new_resource.repositoryid}" do
73
+ command "yum -q -y makecache fast --disablerepo=* --enablerepo=#{new_resource.repositoryid}"
74
+ action :nothing
75
+ only_if { new_resource.enabled }
76
+ end
77
+
66
78
  package "package-cache-reload-#{new_resource.repositoryid}" do
67
79
  action :nothing
68
80
  end
@@ -187,6 +187,24 @@ class Chef
187
187
  end.compact
188
188
  end
189
189
 
190
+ # run the specified command and extract the public key ids
191
+ # accepts the command so it can be used to extract both the current keys
192
+ # and the new keys
193
+ # @param [Array<String>] cmd the command to run
194
+ #
195
+ # @return [Array] an array of key ids
196
+ def extract_public_keys_from_cmd(*cmd)
197
+ so = shell_out(*cmd)
198
+ # Sample output
199
+ # pub:-:4096:1:D94AA3F0EFE21092:1336774248:::-:::scSC::::::23::0:
200
+ so.stdout.split(/\n/).map do |t|
201
+ if t.match(/^pub:/)
202
+ f = t.split(":")
203
+ f.slice(0, 6).join(":")
204
+ end
205
+ end.compact
206
+ end
207
+
190
208
  # validate the key against the apt keystore to see if that version is expired
191
209
  # @param [String] key
192
210
  #
@@ -222,8 +240,8 @@ class Chef
222
240
  def no_new_keys?(file)
223
241
  # Now we are using the option --with-colons that works across old os versions
224
242
  # as well as the latest (16.10). This for both `apt-key` and `gpg` commands
225
- installed_keys = extract_fingerprints_from_cmd(*LIST_APT_KEY_FINGERPRINTS)
226
- proposed_keys = extract_fingerprints_from_cmd("gpg", "--with-fingerprint", "--with-colons", file)
243
+ installed_keys = extract_public_keys_from_cmd(*LIST_APT_KEY_FINGERPRINTS)
244
+ proposed_keys = extract_public_keys_from_cmd("gpg", "--with-fingerprint", "--with-colons", file)
227
245
  (installed_keys & proposed_keys).sort == proposed_keys.sort
228
246
  end
229
247