summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn McCrae <mccrae@progress.com>2023-02-05 08:20:57 +0600
committerJohn <john.mccrae@progress.com>2023-02-28 18:27:19 -0800
commit9f8c667ae2b0ccdd9fa0a77a7d215aaf2df98c5f (patch)
tree2b923e5db2ba206ead8717eb6be03795b6fa7691
parent625558f029d1503d03e7cc42ab9c69d210fa7fa3 (diff)
downloadchef-9f8c667ae2b0ccdd9fa0a77a7d215aaf2df98c5f.tar.gz
Correcting cert retrieval issues for multiple user scenarios
Signed-off-by: John McCrae <mccrae@progress.com>
-rw-r--r--.expeditor/scripts/prep_and_run_tests.ps12
-rw-r--r--chef-config/lib/chef-config/config.rb10
-rw-r--r--lib/chef/client.rb29
-rw-r--r--lib/chef/http/authenticator.rb151
-rw-r--r--spec/integration/client/client_spec.rb38
-rw-r--r--spec/unit/client_spec.rb28
-rw-r--r--spec/unit/http/authenticator_spec.rb75
7 files changed, 263 insertions, 70 deletions
diff --git a/.expeditor/scripts/prep_and_run_tests.ps1 b/.expeditor/scripts/prep_and_run_tests.ps1
index 76e475cd8a..bbf798cbd2 100644
--- a/.expeditor/scripts/prep_and_run_tests.ps1
+++ b/.expeditor/scripts/prep_and_run_tests.ps1
@@ -11,7 +11,7 @@ if ($TestType -eq 'Functional') {
}
Write-Output "--- Running Chef bundle install"
-bundle install --jobs=3 --retry=3
+bundle install --jobs=3 --retry=3
switch ($TestType) {
"Unit" {[string[]]$RakeTest = 'spec:unit','component_specs'; break}
diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb
index 2932fdc82f..6b6d9de427 100644
--- a/chef-config/lib/chef-config/config.rb
+++ b/chef-config/lib/chef-config/config.rb
@@ -778,6 +778,16 @@ module ChefConfig
# Then the public key from the new cert is pushed to Chef Server for authentication
default :migrate_key_to_keystore, false
+ # When we move certs into the certstore, we need to manage multi-user scenarios.
+ # This flag is used in conjunction with 'migrate_key_to_keystore'. If 2 users, Bob and Terri, are using
+ # this node, we need a means to separate the private keys of each user. Alternately, if an Admin
+ # configures the node and then relies on a system account to run chef afterward, we need a second
+ # method for that. Setting this in the client.rb file with a "user" flag will cause chef to create
+ # and manage separate private keys. We look for this flag to be set to "user" to manage distinct users keys with,
+ # However, if that key is set to anything else, we assume that keys are to be stored in the LocalMachine store.
+ # Leaving this key omitted has the same effect as setting it to anything other than "user"
+ default :auth_key_registry_type, nil
+
# When registering the client, should we allow the client key location to
# be a symlink? eg: /etc/chef/client.pem -> /etc/chef/prod-client.pem
# If the path of the key goes through a directory like /tmp this should
diff --git a/lib/chef/client.rb b/lib/chef/client.rb
index e5fcd56eb0..743b5ec473 100644
--- a/lib/chef/client.rb
+++ b/lib/chef/client.rb
@@ -66,6 +66,15 @@ class Chef
class Client
CRYPT_EXPORTABLE = 0x00000001
+ # adding these
+ # certstore 65536 == 0x00010000 == CurrentUser
+ # certstore 131072 == 0x00020000 == LocalMachine
+ # Reference: https://github.com/chef/win32-certstore/blob/main/lib/win32/certstore/mixin/crypto.rb#L90
+ CERT_SYSTEM_STORE_LOCAL_MACHINE = 0x00020000
+ CERT_SYSTEM_STORE_CURRENT_USER = 0x00010000
+ CERT_SYSTEM_STORE_SERVICES = 0x00050000
+ CERT_SYSTEM_STORE_USERS = 0x00060000
+
attr_reader :local_context
extend Chef::Mixin::Deprecation
@@ -674,9 +683,15 @@ class Chef
# In the brave new world of No Certs On Disk, we want to put the pem file into Keychain or the Certstore
# But is it already there?
+ # We're solving the multi-user scenario where both a system/admin user can run on the box but also someone without
+ # admin rights can also run correctly locally.
def check_certstore_for_key(cert_name)
require "win32-certstore"
- win32certstore = ::Win32::Certstore.open("MY")
+ if Chef::Config[:auth_key_registry_type] == "user"
+ win32certstore = ::Win32::Certstore.open("MY", store_location: CERT_SYSTEM_STORE_CURRENT_USER)
+ else
+ win32certstore = ::Win32::Certstore.open("MY")
+ end
win32certstore.search("#{cert_name}")
end
@@ -783,8 +798,6 @@ class Chef
require "time" unless defined?(Time)
autoload :URI, "uri"
- # KeyMigration.instance.key_migrated = true
-
node = Chef::Config[:node_name]
d = Time.now
if d.month == 10 || d.month == 11 || d.month == 12
@@ -818,9 +831,13 @@ class Chef
require "win32-certstore"
tempfile = Tempfile.new("#{Chef::Config[:node_name]}.pfx")
File.open(tempfile, "wb") { |f| f.print new_pfx.to_der }
-
- store = ::Win32::Certstore.open("MY")
- store.add_pfx(tempfile, password, CRYPT_EXPORTABLE)
+ # Need to determine where to store the key
+ if Chef::Config[:auth_key_registry_type] == "user"
+ win32certstore = ::Win32::Certstore.open("MY", store_location: CERT_SYSTEM_STORE_CURRENT_USER)
+ else
+ win32certstore = ::Win32::Certstore.open("MY")
+ end
+ win32certstore.add_pfx(tempfile, password, CRYPT_EXPORTABLE)
tempfile.unlink
end
diff --git a/lib/chef/http/authenticator.rb b/lib/chef/http/authenticator.rb
index c795297397..10d85db4e6 100644
--- a/lib/chef/http/authenticator.rb
+++ b/lib/chef/http/authenticator.rb
@@ -102,12 +102,12 @@ class Chef
self.class.get_cert_password
end
- def encrypt_pfx_pass
- self.class.encrypt_pfx_pass
+ def encrypt_pfx_pass_with_vector
+ self.class.encrypt_pfx_pass_with_vector
end
- def decrypt_pfx_pass
- self.class.decrypt_pfx_pass
+ def decrypt_pfx_pass_with_vector
+ self.class.decrypt_pfx_pass_with_vector
end
# Detects if a private key exists in a certificate repository like Keychain (macOS) or Certificate Store (Windows)
@@ -123,9 +123,18 @@ class Chef
end
end
+ def self.get_cert_user
+ Chef::Config[:auth_key_registry_type] == "user" ? store = "CurrentUser" : store = "LocalMachine"
+ end
+
+ def self.get_registry_user
+ Chef::Config[:auth_key_registry_type] == "user" ? store = "HKEY_CURRENT_USER" : store = "HKEY_LOCAL_MACHINE"
+ end
+
def self.check_certstore_for_key(client_name)
+ store = get_cert_user
powershell_code = <<~CODE
- $cert = Get-ChildItem -path cert:\\LocalMachine\\My -Recurse -Force | Where-Object { $_.Subject -Match "chef-#{client_name}" } -ErrorAction Stop
+ $cert = Get-ChildItem -path cert:\\#{store}\\My -Recurse -Force | Where-Object { $_.Subject -Match "chef-#{client_name}" } -ErrorAction Stop
if (($cert.HasPrivateKey -eq $true) -and ($cert.PrivateKey.Key.ExportPolicy -ne "NonExportable")) {
return $true
}
@@ -164,49 +173,86 @@ class Chef
end
def self.get_cert_password
+ store = get_registry_user
@win32registry = Chef::Win32::Registry.new
- path = "HKEY_LOCAL_MACHINE\\Software\\Progress\\Authentication"
+ path = "#{store}\\Software\\Progress\\Authentication"
# does the registry key even exist?
- present = @win32registry.get_values(path)
- if present.nil? || present.empty?
+ # password_blob should be an array of hashes
+ password_blob = @win32registry.get_values(path)
+ if password_blob.nil? || password_blob.empty?
raise Chef::Exceptions::Win32RegKeyMissing
end
- present.each do |secret|
- if secret[:name] == "PfxPass"
- password = decrypt_pfx_pass(secret[:data])
- return password
+ # Did someone have just the password stored in the registry?
+ raw_data = password_blob.map { |x| x[:data] }
+ vector = raw_data[2]
+ if !!vector
+ decrypted_password = decrypt_pfx_pass_with_vector(password_blob)
+ else
+ decrypted_password = decrypt_pfx_pass_with_password(password_blob)
+ if !!decrypted_password
+ migrate_pass_to_use_vector(decrypted_password)
+ else
+ Chef::Log.error("Failed to retrieve certificate password")
end
end
-
- raise Chef::Exceptions::Win32RegKeyMissing
-
+ decrypted_password
rescue Chef::Exceptions::Win32RegKeyMissing
# if we don't have a password, log that and generate one
- Chef::Log.warn "Authentication Hive and values not present in registry, creating them now"
- new_path = "HKEY_LOCAL_MACHINE\\Software\\Progress\\Authentication"
+ store = get_registry_user
+ new_path = "#{store}\\Software\\Progress\\Authentication"
unless @win32registry.key_exists?(new_path)
@win32registry.create_key(new_path, true)
end
- require "securerandom" unless defined?(SecureRandom)
- size = 14
- password = SecureRandom.alphanumeric(size)
- encrypted_pass = encrypt_pfx_pass(password)
- values = { name: "PfxPass", type: :string, data: encrypted_pass }
- @win32registry.set_value(new_path, values)
- password
+ create_and_store_new_password
end
- def self.encrypt_pfx_pass(password)
+ def self.encrypt_pfx_pass_with_vector(password)
powershell_code = <<~CODE
- $encrypted_string = ConvertTo-SecureString "#{password}" -AsPlainText -Force
- $secure_string = ConvertFrom-SecureString $encrypted_string
- return $secure_string
+ $AES = [System.Security.Cryptography.Aes]::Create()
+ $key_temp = [System.Convert]::ToBase64String($AES.Key)
+ $iv_temp = [System.Convert]::ToBase64String($AES.IV)
+ $encryptor = $AES.CreateEncryptor()
+ [System.Byte[]]$Bytes = [System.Text.Encoding]::Unicode.GetBytes("#{password}")
+ $EncryptedBytes = $encryptor.TransformFinalBlock($Bytes,0,$Bytes.Length)
+ $EncryptedBase64String = [System.Convert]::ToBase64String($EncryptedBytes)
+ # create array of encrypted pass, key, iv
+ $password_blob = @($EncryptedBase64String, $key_temp, $iv_temp)
+ return $password_blob
CODE
powershell_exec!(powershell_code).result
end
- def self.decrypt_pfx_pass(password)
+ def self.decrypt_pfx_pass_with_vector(password_blob)
+ raw_data = password_blob.map { |x| x[:data] }
+ password = raw_data[0]
+ key = raw_data[1]
+ vector = raw_data[2]
+
+ powershell_code = <<~CODE
+ $KeyBytes = [System.Convert]::FromBase64String("#{key}")
+ $IVBytes = [System.Convert]::FromBase64String("#{vector}")
+ $aes = [System.Security.Cryptography.Aes]::Create()
+ $aes.Key = $KeyBytes
+ $aes.IV = $IVBytes
+ $EncryptedBytes = [System.Convert]::FromBase64String("#{password}")
+ $Decryptor = $aes.CreateDecryptor()
+ $DecryptedBytes = $Decryptor.TransformFinalBlock($EncryptedBytes,0,$EncryptedBytes.Length)
+ $DecryptedString = [System.Text.Encoding]::Unicode.GetString($DecryptedBytes)
+ return $DecryptedString
+ CODE
+ results = powershell_exec!(powershell_code).result
+ end
+
+ def self.decrypt_pfx_pass_with_password(password_blob)
+ password = ""
+ password_blob.each do |secret|
+ if secret[:name] == "PfxPass"
+ password = secret[:data]
+ else
+ Chef::Log.error("Failed to retrieve a password for the private key")
+ end
+ end
powershell_code = <<~CODE
$secure_string = "#{password}" | ConvertTo-SecureString
$string = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR((($secure_string))))
@@ -215,14 +261,49 @@ class Chef
powershell_exec!(powershell_code).result
end
- def self.retrieve_certificate_key(client_name)
- require "openssl" unless defined?(OpenSSL)
+ def self.migrate_pass_to_use_vector(password)
+ store = get_cert_user
+ corrected_store = (store == "CurrentUser" ? "HKCU" : "HKLM")
+ powershell_code = <<~CODE
+ Remove-ItemProperty -Path "#{corrected_store}:\\Software\\Progress\\Authentication" -Name "PfXPass"
+ CODE
+ powershell_exec!(powershell_code)
+ create_and_store_new_password(password)
+ end
+ # This method name is a bit of a misnomer. We call it to legit create a new password using the vector format.
+ # But we also call it with a password that needs to be migrated to use the vector format too.
+ def self.create_and_store_new_password(password = nil)
+ @win32registry = Chef::Win32::Registry.new
+ store = get_registry_user
+ path = "#{store}\\Software\\Progress\\Authentication"
+ if password.nil?
+ require "securerandom" unless defined?(SecureRandom)
+ size = 14
+ password = SecureRandom.alphanumeric(size)
+ end
+ encrypted_blob = encrypt_pfx_pass_with_vector(password)
+ encrypted_password = encrypted_blob[0]
+ key = encrypted_blob[1]
+ vector = encrypted_blob[2]
+ values = [
+ { name: "PfxPass", type: :string, data: encrypted_password },
+ { name: "PfxKey", type: :string, data: key },
+ { name: "PfxIV", type: :string, data: vector },
+ ]
+ values.each do |i|
+ @win32registry.set_value(path, i)
+ end
+ password
+ end
+
+ def self.retrieve_certificate_key(client_name)
if ChefUtils.windows?
+ require "openssl" unless defined?(OpenSSL)
password = get_cert_password
return false unless password
- if check_certstore_for_key(client_name)
+ if !!check_certstore_for_key(client_name)
ps_blob = powershell_exec!(get_the_key_ps(client_name, password)).result
file_path = ps_blob["PSPath"].split("::")[1]
pkcs = OpenSSL::PKCS12.new(File.binread(file_path), password)
@@ -252,10 +333,11 @@ class Chef
end
def self.get_the_key_ps(client_name, password)
+ store = get_cert_user
powershell_code = <<~CODE
Try {
$my_pwd = ConvertTo-SecureString -String "#{password}" -Force -AsPlainText;
- $cert = Get-ChildItem -path cert:\\LocalMachine\\My -Recurse | Where-Object { $_.Subject -match "chef-#{client_name}$" } -ErrorAction Stop;
+ $cert = Get-ChildItem -path cert:\\#{store}\\My -Recurse | Where-Object { $_.Subject -match "chef-#{client_name}$" } -ErrorAction Stop;
$tempfile = [System.IO.Path]::GetTempPath() + "export_pfx.pfx";
Export-PfxCertificate -Cert $cert -Password $my_pwd -FilePath $tempfile;
}
@@ -266,8 +348,9 @@ class Chef
end
def self.delete_old_key_ps(client_name)
+ store = get_cert_user
powershell_code = <<~CODE
- Get-ChildItem -path cert:\\LocalMachine\\My -Recurse | Where-Object { $_.Subject -match "chef-#{client_name}$" } | Remove-Item -ErrorAction Stop;
+ Get-ChildItem -path cert:\\#{store}\\My -Recurse | Where-Object { $_.Subject -match "chef-#{client_name}$" } | Remove-Item -ErrorAction Stop;
CODE
end
diff --git a/spec/integration/client/client_spec.rb b/spec/integration/client/client_spec.rb
index 1bd84fa940..38d414701a 100644
--- a/spec/integration/client/client_spec.rb
+++ b/spec/integration/client/client_spec.rb
@@ -35,14 +35,14 @@ describe "chef-client" do
@server = @api = nil
end
- def install_certificate_in_store(client_name)
+ def install_certificate_in_store(client_name, store_location)
if ChefUtils.windows?
powershell_exec! <<~EOH
if (-not (($PSVersionTable.PSVersion.Major -ge 5) -and ($PSVersionTable.PSVersion.Build -ge 22000)) ) {
- New-SelfSignedCertificate -CertStoreLocation Cert:\\LocalMachine\\My -DnsName "#{client_name}"
+ New-SelfSignedCertificate -CertStoreLocation Cert:\\#{store_location}\\My -DnsName "#{client_name}"
}
else {
- New-SelfSignedCertificate -CertStoreLocation Cert:\\LocalMachine\\My -Subject "#{client_name}" -FriendlyName "#{client_name}" -KeyExportPolicy Exportable
+ New-SelfSignedCertificate -CertStoreLocation Cert:\\#{store_location}\\My -Subject "#{client_name}" -FriendlyName "#{client_name}" -KeyExportPolicy Exportable
}
EOH
end
@@ -50,14 +50,6 @@ describe "chef-client" do
def create_registry_key
::Chef::HTTP::Authenticator.get_cert_password
- # @win32registry = Chef::Win32::Registry.new
- # path = "HKEY_LOCAL_MACHINE\\Software\\Progress\\Authentication"
- # unless @win32registry.key_exists?(path)
- # @win32registry.create_key(path, true)
- # end
- # password = SOME_CHARS.sample(1 + rand(SOME_CHARS.count)).join[0...14]
- # values = { name: "PfxPass", type: :string, data: password }
- # @win32registry.set_value(path, values)
end
def remove_certificate_from_store
@@ -111,6 +103,9 @@ describe "chef-client" do
tempfile.close
@path = tempfile.path
Chef::Config.validation_key = @path
+ if ChefUtils.windows?
+ create_registry_key
+ end
file "config/client.rb", <<~EOM
local_mode true
@@ -201,17 +196,27 @@ describe "chef-client" do
if ChefUtils.windows?
context "and the private key is in the Windows CertStore" do
- before do
- install_certificate_in_store(client_name)
+
+ it "should verify that the cert is loaded in the \\LocalMachine\\My store" do
+ Chef::Config[:auth_key_registry_type] = "machine"
+ install_certificate_in_store(client_name, "LocalMachine")
create_registry_key
+ expect(Chef::HTTP::Authenticator.check_certstore_for_key(hostname)).to eq(true)
end
- after do
+ it "should verify that the export password for the pfx is loaded in the Registry" do
+ expect(verify_export_password_exists.result).to eq(true)
+ end
+
+ it "should verify that a private key is returned to me" do
+ expect(Chef::HTTP::Authenticator.retrieve_certificate_key(client_name)).not_to be nil
remove_certificate_from_store
- remove_registry_key
end
- it "should verify that the cert is loaded in the LocalMachine\\My" do
+ it "should verify that the cert is loaded in the \\CurrentUser\\My store" do
+ Chef::Config[:auth_key_registry_type] = "user"
+ install_certificate_in_store(client_name, "CurrentUser")
+ create_registry_key
expect(Chef::HTTP::Authenticator.check_certstore_for_key(hostname)).to eq(true)
end
@@ -221,6 +226,7 @@ describe "chef-client" do
it "should verify that a private key is returned to me" do
expect(Chef::HTTP::Authenticator.retrieve_certificate_key(client_name)).not_to be nil
+ remove_certificate_from_store
end
end
end
diff --git a/spec/unit/client_spec.rb b/spec/unit/client_spec.rb
index 7f02eacb39..ae11fd9793 100644
--- a/spec/unit/client_spec.rb
+++ b/spec/unit/client_spec.rb
@@ -310,25 +310,49 @@ describe Chef::Client, :windows_only do
end
context "when the client intially boots the first time" do
- it "verfies that a certificate was correctly created and exists in the Cert Store" do
+ it "verfies that a certificate was correctly created and exists in the LocalMachine Cert Store" do
+ Chef::Config[:node_name] = "test"
new_pfx = my_client.generate_pfx_package(cert_name, end_date)
my_client.import_pfx_to_store(new_pfx)
expect(my_client.check_certstore_for_key(cert_name)).not_to be false
+ delete_certificate(cert_name)
end
it "correctly returns a new Publc Key" do
new_pfx = my_client.generate_pfx_package(cert_name, end_date)
cert_object = new_pfx.certificate.public_key.to_pem
expect(cert_object.to_s).to match(/PUBLIC KEY/)
+ delete_certificate(cert_name)
+ end
+
+ end
+
+ context "when the client intially boots the first time and auth_key_registry_type is set to 'user' " do
+ it "verfies that a certificate was correctly created and exists in the CurrentUser Cert Store" do
+ Chef::Config[:node_name] = "test"
+ Chef::Config[:auth_key_registry_type] = "user"
+ new_pfx = my_client.generate_pfx_package(cert_name, end_date)
+ my_client.import_pfx_to_store(new_pfx)
+ expect(my_client.check_certstore_for_key(cert_name)).not_to be false
+ delete_certificate(cert_name)
+ end
+
+ it "correctly returns a new Publc Key" do
+ Chef::Config[:auth_key_registry_type] = "user"
+ new_pfx = my_client.generate_pfx_package(cert_name, end_date)
+ cert_object = new_pfx.certificate.public_key.to_pem
+ expect(cert_object.to_s).to match(/PUBLIC KEY/)
+ delete_certificate(cert_name)
end
end
def delete_certificate(cert_name)
+ Chef::Config[:auth_key_registry_type] == "user" ? store = "CurrentUser" : store = "LocalMachine"
require "chef/mixin/powershell_exec"
extend Chef::Mixin::PowershellExec
powershell_code = <<~CODE
- Get-ChildItem -path cert:\\LocalMachine\\My -Recurse -Force | Where-Object { $_.Subject -Match "#{cert_name}" } | Remove-item
+ Get-ChildItem -path cert:\\#{store}\\My -Recurse -Force | Where-Object { $_.Subject -Match "#{cert_name}" } | Remove-item
CODE
powershell_exec!(powershell_code)
end
diff --git a/spec/unit/http/authenticator_spec.rb b/spec/unit/http/authenticator_spec.rb
index bbd952b436..a088a1f4f9 100644
--- a/spec/unit/http/authenticator_spec.rb
+++ b/spec/unit/http/authenticator_spec.rb
@@ -18,6 +18,9 @@
require "spec_helper"
require "chef/http/authenticator"
+require "chef/mixin/powershell_exec"
+
+require_relative "../../../lib/chef/win32/registry"
describe Chef::HTTP::Authenticator, :windows_only do
let(:class_instance) { Chef::HTTP::Authenticator.new(client_name: "test") }
@@ -28,7 +31,7 @@ describe Chef::HTTP::Authenticator, :windows_only do
let(:node_name) { "test" }
let(:passwrd) { "some_insecure_password" }
- before do
+ before(:each) do
Chef::Config[:node_name] = node_name
cert_name = "chef-#{node_name}"
d = Time.now
@@ -36,6 +39,7 @@ describe Chef::HTTP::Authenticator, :windows_only do
end_date = end_date.utc.iso8601
my_client = Chef::Client.new
+ class_instance.get_cert_password
pfx = my_client.generate_pfx_package(cert_name, end_date)
my_client.import_pfx_to_store(pfx)
end
@@ -47,10 +51,21 @@ describe Chef::HTTP::Authenticator, :windows_only do
delete_certificate(cert_name)
end
- context "when retrieving a certificate from the certificate store" do
+ context "when retrieving a certificate from the certificate store it" do
+ it "properly creates the password hive in the registry when it doesn't exist" do
+ delete_registry_hive
+ class_instance.get_cert_password
+ win32registry = Chef::Win32::Registry.new
+ expected_path = "HKEY_LOCAL_MACHINE\\Software\\Progress\\Authentication"
+ path_created = win32registry.key_exists?(expected_path)
+ expect(path_created).to be(true)
+ end
+
it "retrieves a certificate password from the registry when the hive does not already exist" do
delete_registry_hive
+ password = class_instance.get_cert_password
expect { class_instance.get_cert_password }.not_to raise_error
+ expect(password).not_to be(nil)
end
it "should return a password of at least 14 characters in length" do
@@ -58,7 +73,27 @@ describe Chef::HTTP::Authenticator, :windows_only do
expect(password.length).to eql(14)
end
- it "correctly retrieves a valid certificate in pem format from the certstore" do
+ it "will retrieve a password from a partial registry hive and upgrades it while using the old decryptor" do
+ delete_registry_hive
+ load_partial_registry_hive
+ password = class_instance.get_cert_password
+ expect(password).to eql(passwrd)
+ end
+
+ it "verifies that the new password is now using a vector" do
+ win32registry = Chef::Win32::Registry.new
+ path = "HKEY_LOCAL_MACHINE\\Software\\Progress\\Authentication"
+ password_blob = win32registry.get_values(path)
+ if password_blob.nil? || password_blob.empty?
+ raise Chef::Exceptions::Win32RegKeyMissing
+ end
+
+ raw_data = password_blob.map { |x| x[:data] }
+ vector = raw_data[2]
+ expect(vector).not_to be(nil)
+ end
+
+ it "correctly retrieves a valid certificate in pem format from the LocalMachine certstore" do
require "openssl"
certificate = class_instance.retrieve_certificate_key(node_name)
cert_object = OpenSSL::PKey::RSA.new(certificate)
@@ -66,21 +101,39 @@ describe Chef::HTTP::Authenticator, :windows_only do
end
end
- def delete_certificate(cert_name)
+ def load_partial_registry_hive
+ extend Chef::Mixin::PowershellExec
+ password = "some_insecure_password"
powershell_code = <<~CODE
- Get-ChildItem -path cert:\\LocalMachine\\My -Recurse -Force | Where-Object { $_.Subject -Match "#{cert_name}" } | Remove-item
+ $encrypted_string = ConvertTo-SecureString "#{password}" -AsPlainText -Force
+ $secure_string = ConvertFrom-SecureString $encrypted_string
+ return $secure_string
CODE
- powershell_exec!(powershell_code)
+ encrypted_pass = powershell_exec!(powershell_code).result
+ Chef::Config[:auth_key_registry_type] == "user" ? store = "HKEY_CURRENT_USER" : store = "HKEY_LOCAL_MACHINE"
+ hive_path = "#{store}\\Software\\Progress\\Authentication"
+ win32registry = Chef::Win32::Registry.new
+ unless win32registry.key_exists?(hive_path)
+ win32registry.create_key(hive_path, true)
+ end
+ values = { name: "PfxPass", type: :string, data: encrypted_pass }
+ win32registry.set_value(hive_path, values)
end
def delete_registry_hive
- @win32registry = Chef::Win32::Registry.new
- path = "HKEY_LOCAL_MACHINE\\Software\\Progress\\Authentication"
- present = @win32registry.get_values(path)
- unless present.nil? || present.empty?
- @win32registry.delete_key(path, true)
+ win32registry = Chef::Win32::Registry.new
+ hive_path = "HKEY_LOCAL_MACHINE\\Software\\Progress"
+ if win32registry.key_exists?(hive_path)
+ win32registry.delete_key(hive_path, true)
end
end
+
+ def delete_certificate(cert_name)
+ powershell_code = <<~CODE
+ Get-ChildItem -path cert:\\LocalMachine\\My -Recurse -Force | Where-Object { $_.Subject -Match "#{cert_name}" } | Remove-item
+ CODE
+ powershell_exec!(powershell_code)
+ end
end
describe Chef::HTTP::Authenticator do