chef 18.0.185 → 18.1.29

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) 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/data/apt/chef-integration-test-1.0/debian/rules +0 -0
  29. data/spec/data/apt/chef-integration-test-1.1/debian/rules +0 -0
  30. data/spec/data/apt/chef-integration-test2-1.0/debian/rules +0 -0
  31. data/spec/functional/assets/chefinittest +0 -0
  32. data/spec/functional/assets/testchefsubsys +0 -0
  33. data/spec/functional/assets/yumrepo-empty/repodata/01a3b489a465bcac22a43492163df43451dc6ce47d27f66de289756b91635523-filelists.sqlite.bz2 +0 -0
  34. data/spec/functional/assets/yumrepo-empty/repodata/401dc19bda88c82c403423fb835844d64345f7e95f5b9835888189c03834cc93-filelists.xml.gz +0 -0
  35. data/spec/functional/assets/yumrepo-empty/repodata/5dc1e6e73c84803f059bb3065e684e56adfc289a7e398946574d79dac6643945-primary.sqlite.bz2 +0 -0
  36. data/spec/functional/assets/yumrepo-empty/repodata/6bf9672d0862e8ef8b8ff05a2fd0208a922b1f5978e6589d87944c88259cb670-other.xml.gz +0 -0
  37. data/spec/functional/assets/yumrepo-empty/repodata/7c36572015e075add2b38b900837bcdbb8a504130ddff49b2351a7fc0affa3d4-other.sqlite.bz2 +0 -0
  38. data/spec/functional/assets/yumrepo-empty/repodata/dabe2ce5481d23de1f4f52bdcfee0f9af98316c9e0de2ce8123adeefa0dd08b9-primary.xml.gz +0 -0
  39. data/spec/functional/assets/yumrepo-empty/repodata/repomd.xml +55 -0
  40. data/spec/functional/provider/remote_file/cache_control_data_spec.rb +0 -0
  41. data/spec/functional/resource/aix_service_spec.rb +0 -0
  42. data/spec/functional/resource/aixinit_service_spec.rb +0 -0
  43. data/spec/functional/resource/yum_package_spec.rb +16 -0
  44. data/spec/integration/client/client_spec.rb +22 -16
  45. data/spec/integration/client/fips_spec.rb +20 -0
  46. data/spec/spec_helper.rb +4 -0
  47. data/spec/support/platform_helpers.rb +4 -0
  48. data/spec/unit/chef_fs/file_system_spec.rb +2 -0
  49. data/spec/unit/client_spec.rb +26 -2
  50. data/spec/unit/compliance/runner_spec.rb +8 -0
  51. data/spec/unit/http/authenticator_spec.rb +64 -11
  52. data/spec/unit/property/validation_spec.rb +30 -0
  53. data/spec/unit/provider/apt_repository_spec.rb +26 -5
  54. data/spec/unit/resource/yum_repository_spec.rb +4 -0
  55. metadata +23 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5e26d247c5f5a3d647c5fab3090148d07e5714d73f5b51e79ac836ef4abfb8cb
4
- data.tar.gz: f31e1d3a651490f209276bf1583310940eaa67391d6879c50704504bdfdfe928
3
+ metadata.gz: 01fc036d9cbe5a20b0ca17c3d09db60986f92c4759ce2b20212e0b34d9a57535
4
+ data.tar.gz: 16f9392fddd572bf0d5e58c35d2018a423ee20858038af6bc0d49aa5cdd55626
5
5
  SHA512:
6
- metadata.gz: 860f0b162f5ebbd6d8e40383aeb682df596dc0ac09207d5bb5be807ff5de0359e50f22f603a94e385b95b1c03cf87286c056dfc160a523ced7e6902960ae5b7c
7
- data.tar.gz: 8e3c3dc548d6cabd651adb17a150a91dec76c09d1d93a2b3e9561d0ff3abf69ad9301b4368901a1dc2015241cec589da5861a3642e70ce05f0356bd045e10c86
6
+ metadata.gz: e7ca0d1982af93db302d882654d42544dee4344ee9c9025c8ea857e2bf6fcb9f6dfdebd7b8eb78dd559e88d4597a6153d3acd04397eb708cc419ca0f782ed380
7
+ data.tar.gz: 673c088d0f223b34daa9cf14a7598d80d0b5c7276e6478d54a93e64c12550235a05c10d39cba1d9d522a5b42fa654c113d943e618c1e4eea9cf45bae9ec81ae2
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