From bde9dd424457b8b8393576cee7d4d0e8fcfbe41a Mon Sep 17 00:00:00 2001 From: NAshwini Date: Tue, 21 Nov 2017 13:29:04 +0530 Subject: [PATCH] Add code to delete certificate from cert_store. Signed-off-by: NAshwini --- lib/win32/certstore.rb | 6 ++++ lib/win32/certstore/mixin/crypto.rb | 4 +++ lib/win32/certstore/store_base.rb | 22 ++++++++++++++ spec/win32/unit/certstore_spec.rb | 45 ++++++++++++++++++++++++++++- 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/lib/win32/certstore.rb b/lib/win32/certstore.rb index 3e0a903..f1dcc96 100644 --- a/lib/win32/certstore.rb +++ b/lib/win32/certstore.rb @@ -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 + return delete_cert + end + private attr_reader :certstore_handler diff --git a/lib/win32/certstore/mixin/crypto.rb b/lib/win32/certstore/mixin/crypto.rb index f8e4c4a..02cc978 100644 --- a/lib/win32/certstore/mixin/crypto.rb +++ b/lib/win32/certstore/mixin/crypto.rb @@ -105,6 +105,10 @@ 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 end end end diff --git a/lib/win32/certstore/store_base.rb b/lib/win32/certstore/store_base.rb index 7007353..b144395 100644 --- a/lib/win32/certstore/store_base.rb +++ b/lib/win32/certstore/store_base.rb @@ -58,6 +58,26 @@ def cert_add(store_handler, cert_file_path) end end + def cert_delete(store_handler, certificate_name) + cert_name = FFI::MemoryPointer.new(2, 128) + begin + while (pCertContext = CertEnumCertificatesInStore(store_handler, pCertContext) and not pCertContext.null? ) do + if (CertGetNameStringW(pCertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, nil,cert_name, 1024) && + cert_name.read_wstring.downcase == certificate_name.downcase) + if CertDeleteCertificateFromStore(CertDuplicateCertificateContext(pCertContext)) + return "Deleted certificate #{certificate_name} successfully" + else + lookup_error + end + 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) @@ -73,6 +93,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 diff --git a/spec/win32/unit/certstore_spec.rb b/spec/win32/unit/certstore_spec.rb index b0c8977..296e12d 100644 --- a/spec/win32/unit/certstore_spec.rb +++ b/spec/win32/unit/certstore_spec.rb @@ -69,10 +69,46 @@ end end + context "When deleting valid certificate" do + let (:store_name) { "ca" } + let (:certificate_name) { 'GeoTrust Global CA' } + before(:each) do + 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") + 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(certbase).to receive(:CertDeleteCertificateFromStore).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(certbase).to receive(:CertDeleteCertificateFromStore).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(certbase).to receive(:CertAddEncodedCertificateToStore).and_return(false) allow(FFI::LastError).to receive(:error).and_return(1223) @@ -107,6 +143,13 @@ 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(: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