Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MSYS-545] Add code to delete certificate from cert_store. #10

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/win32/certstore.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ def add(cert_file_path)
return add
end

def delete(certificate_name)
delete_cert = cert_delete(certstore_handler, certificate_name)
close
delete_cert
end

private

attr_reader :certstore_handler
Expand Down
6 changes: 6 additions & 0 deletions lib/win32/certstore/mixin/crypto.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ def initialize(str = nil)
safe_attach_function :CertAddEncodedCertificateToStore, [HCERTSTORE, :DWORD, :PWSTR, :DWORD, :INT_PTR, PCCERT_CONTEXT], :BOOL

safe_attach_function :CertSerializeCertificateStoreElement, [PCCERT_CONTEXT, :DWORD, :pointer, :DWORD], :BOOL
# Duplicates a certificate context by incrementing its reference count
safe_attach_function :CertDuplicateCertificateContext, [PCCERT_CONTEXT], PCCERT_CONTEXT
# Delete certification from certification store
safe_attach_function :CertDeleteCertificateFromStore, [PCCERT_CONTEXT], :BOOL
# To retrieve specific certificates from certificate store
safe_attach_function :CertFindCertificateInStore, [HCERTSTORE, :DWORD, :DWORD, :DWORD, :LPVOID, PCCERT_CONTEXT], PCCERT_CONTEXT
end
end
end
Expand Down
18 changes: 18 additions & 0 deletions lib/win32/certstore/store_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,22 @@ def cert_add(store_handler, cert_file_path)
end
end

def cert_delete(store_handler, certificate_name)
begin
if ( pCertContext = CertFindCertificateInStore(store_handler, X509_ASN_ENCODING, 0, CERT_NAME_FRIENDLY_DISPLAY_TYPE, certificate_name, nil) and not pCertContext.null? )
if CertDeleteCertificateFromStore(CertDuplicateCertificateContext(pCertContext))
return "Deleted certificate #{certificate_name} successfully"
else
lookup_error
end
end
return "Cannot find certificate with name as `#{certificate_name}`. Please re-verify certificate Issuer name or Friendly name"
rescue Exception => e
@error = "delete: "
lookup_error
end
end

private

def lookup_error(failed_operation = nil)
Expand All @@ -73,6 +89,8 @@ def lookup_error(failed_operation = nil)
raise Chef::Exceptions::Win32APIError, "ASN1 bad tag value met. -- Is the certificate in DER format?"
when -2146881278
raise Chef::Exceptions::Win32APIError, "ASN1 unexpected end of data."
when -2147024891
raise Chef::Exceptions::Win32APIError, "System.UnauthorizedAccessException, Access denied.."
else
raise Chef::Exceptions::Win32APIError, "Unable to #{failed_operation} certificate with error: #{last_error}."
end
Expand Down
47 changes: 46 additions & 1 deletion spec/win32/unit/certstore_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,47 @@
end
end

context "When deleting valid certificate" do
let (:store_name) { "ca" }
let (:certificate_name) { 'GeoTrust Global CA' }
before(:each) do
allow(certbase).to receive_message_chain(:CertFindCertificateInStore, :last).and_return(true)
allow_any_instance_of(certbase).to receive(:CertDeleteCertificateFromStore).and_return(true)
end
it "return message of successful deletion" do
store = certstore.open(store_name)
delete_cert = store.delete(certificate_name)
expect(delete_cert).to eq("Deleted certificate GeoTrust Global CA successfully")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CertEnumCertificatesInStore isn't stubbed so this spec may fail on a system which doesn't have a GeoTrust Global CA certificate ca store. Since appveyor isn't configured, this isn't very clear right now.
Anyway, if we decide to stop using CertEnumCertificatesInStore as per my previous comment, then the specs will change.

end
end

context "When deleting invalid certificate" do
let (:store_name) { "my" }
let (:certificate_name) { "tmp_cert.mydomain.com" }
it "return message of `Cannot find certificate`" do
allow_any_instance_of(certbase).to receive(:CertFindCertificateInStore).and_return(false)
store = certstore.open(store_name)
delete_cert = store.delete(certificate_name)
expect(delete_cert).to eq("Cannot find certificate with name as `tmp_cert.mydomain.com`. Please re-verify certificate Issuer name or Friendly name")
end
end

context "When passing empty certificate_name to delete it" do
let (:store_name) { "my" }
let (:certificate_name) { "" }
it "return message of `Cannot find certificate`" do
allow_any_instance_of(certbase).to receive(:CertFindCertificateInStore).and_return(false)
store = certstore.open(store_name)
delete_cert = store.delete(certificate_name)
expect(delete_cert).to eq("Cannot find certificate with name as ``. Please re-verify certificate Issuer name or Friendly name")
end
end

context "When adding certificate failed with FFI::LastError" do
let (:store_name) { "root" }
let (:cert_file_path) { '.\win32\unit\assets\test.cer' }

let (:certificate_name) { 'GlobalSign' }

it "returns 'The operation was canceled by the user'" do
allow(certstore_obj).to receive(:CertAddEncodedCertificateToStore).and_return(false)
allow(FFI::LastError).to receive(:error).and_return(1223)
Expand Down Expand Up @@ -120,6 +157,14 @@
store = certstore.open(store_name)
expect { store.add(cert_file_path) }.to raise_error("ASN1 unexpected end of data.")
end

it "return 'System.UnauthorizedAccessException, Access denied..'" do
allow(certbase).to receive(:CertFindCertificateInStore).and_return(true)
allow(certbase).to receive(:CertDeleteCertificateFromStore).and_return(false)
allow(FFI::LastError).to receive(:error).and_return(-2147024891)
store = certstore.open(store_name)
expect { store.delete(certificate_name) }.to raise_error(Chef::Exceptions::Win32APIError)
end
end
end
end