win32-certstore 0.6.2 → 0.6.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e832ae077e8be7cd039393f84b74062e58dd331b1317cdcc2a1bb1f13109f176
4
- data.tar.gz: 453bd4ad7e2d6a92d3935b0a4df4f241636b657256a39f1ac35bc81d2d34030b
3
+ metadata.gz: 5a55969ab67094eb7c246c946a77807c596afb12d76fd4eef07d48076ffa43b7
4
+ data.tar.gz: 711f553c79a83a4edb8ed2b572ca4ddac1478658b910d57fdb097d2323b4985f
5
5
  SHA512:
6
- metadata.gz: a3254affc58f8eb862a585b78cc5c5451c61db3398ef1de59ffcb6580755f5e24338c2a1caf16e89e46db4e8ee7c9f431a1c4372768cb7ff08bd76265d1b53da
7
- data.tar.gz: bab7b27e4c0c6d780556a6410836283f6de31c64d559fb1b331b679c3d671b68673b6313f53560916e0a5c89f83fff658fd3335f965cfa3024b5066860393b3b
6
+ metadata.gz: 79af95ff03edc99c69c77cb2b333258929afda17bd3c7068cfc08f3fac65619d4723e2b0042f6f1b77fd6b34f0c2136d0136931275dc1514f2e5e78252a05d79
7
+ data.tar.gz: c8d84f2111d7ff052bacf7c3384552a519a17c9f49feec1e11e924115708c6838b2d034a3c9ecefafddf0ca08b3a0d1a0a4ef10b9863811abb684d92bb52c743
@@ -37,13 +37,13 @@ module Win32
37
37
  # Validate certificate Object
38
38
  def validate_certificate_obj(cert_obj)
39
39
  unless cert_obj.class == OpenSSL::X509::Certificate
40
- raise ArgumentError, "Invalid Certificate object."
40
+ raise ArgumentError, "Invalid Certificate object. This is not a properly formatted x509 object"
41
41
  end
42
42
  end
43
43
 
44
44
  # Validate thumbprint
45
45
  def validate_thumbprint(cert_thumbprint)
46
- if cert_thumbprint.nil? || cert_thumbprint.strip.empty?
46
+ if cert_thumbprint.nil? || cert_thumbprint.empty? || cert_thumbprint.strip.empty?
47
47
  raise ArgumentError, "Invalid certificate thumbprint."
48
48
  end
49
49
  end
@@ -82,7 +82,7 @@ module Win32
82
82
  # ROOT -> Root certificates.
83
83
  # SPC -> Software Publisher Certificate.
84
84
  def valid_store_name
85
- %w{MY CA ROOT AUTHROOT DISALLOWED SPC TRUST TRUSTEDPEOPLE TRUSTEDPUBLISHER CLIENTAUTHISSUER TRUSTEDDEVICES SMARTCARDROOT WEBHOSTING REMOTE\ DESKTOP}
85
+ ["MY", "CA", "ROOT", "AUTHROOT", "DISALLOWED", "SPC", "TRUST", "TRUSTEDPEOPLE", "TRUSTEDPUBLISHER", "CLIENTAUTHISSUER", "TRUSTEDDEVICES", "SMARTCARDROOT", "WEBHOSTING", "REMOTE DESKTOP"]
86
86
  end
87
87
  end
88
88
  end
@@ -22,17 +22,20 @@ module Win32
22
22
  module Mixin
23
23
  module Helper
24
24
  def cert_ps_cmd(thumbprint, store_location: "LocalMachine", store_name: "My")
25
+ # the PowerShell block below uses a "Here-String" - it is explicitly formatted against the left margin.
25
26
  <<-EOH
26
- $cert = Get-ChildItem Cert:\\#{store_location}\\#{store_name} -Recurse | Where { $_.Thumbprint -eq "#{thumbprint}" }
27
+ $cert = Get-ChildItem Cert:\\#{store_location}\\#{store_name} -Recurse | Where-Object { $_.Thumbprint -eq "#{thumbprint}" }
27
28
 
29
+ $certdata = [System.Convert]::ToBase64String($cert.RawData, 'InsertLineBreaks')
28
30
  $content = $null
29
31
  if($null -ne $cert)
30
32
  {
31
- $content = @(
32
- '-----BEGIN CERTIFICATE-----'
33
- [System.Convert]::ToBase64String($cert.RawData, 'InsertLineBreaks')
34
- '-----END CERTIFICATE-----'
35
- )
33
+ $content =
34
+ @"
35
+ -----BEGIN CERTIFICATE-----
36
+ $($certdata)
37
+ -----END CERTIFICATE-----
38
+ "@
36
39
  }
37
40
  $content
38
41
  EOH
@@ -17,20 +17,25 @@
17
17
 
18
18
  require_relative "mixin/crypto"
19
19
  require_relative "mixin/string"
20
- require_relative "mixin/shell_exec"
21
20
  require_relative "mixin/unicode"
22
21
  require "openssl" unless defined?(OpenSSL)
23
22
  require "json" unless defined?(JSON)
24
23
 
24
+ begin
25
+ require "chef-powershell"
26
+ rescue LoadError
27
+ puts "Not loading powershell_exec during testing"
28
+ end
29
+
25
30
  module Win32
26
31
  class Certstore
27
32
  module StoreBase
28
33
  include Win32::Certstore::Mixin::Crypto
29
34
  include Win32::Certstore::Mixin::Assertions
30
35
  include Win32::Certstore::Mixin::String
31
- include Win32::Certstore::Mixin::ShellExec
32
36
  include Win32::Certstore::Mixin::Unicode
33
37
  include Win32::Certstore::Mixin::Helper
38
+ include ChefPowerShell::ChefPowerShellModule::PowerShellExec
34
39
 
35
40
  # Adding new certification in open certificate and return boolean
36
41
  # store_handler => Open certificate store handler
@@ -87,18 +92,13 @@ module Win32
87
92
 
88
93
  # Get certificate from open certificate store and return certificate object
89
94
  # certificate_thumbprint => thumbprint is a hash. which could be sha1 or md5.
90
- def cert_get(certificate_thumbprint, store_name:, store_location:)
95
+ def cert_get(certificate_thumbprint)
91
96
  validate_thumbprint(certificate_thumbprint)
92
97
  thumbprint = update_thumbprint(certificate_thumbprint)
93
- cert_pem = get_cert_pem(thumbprint, store_name: store_name, store_location: store_location)
98
+ cert_pem = get_cert_pem(thumbprint)
94
99
  cert_pem = format_pem(cert_pem)
95
- if cert_pem.empty?
96
- raise ArgumentError, "Unable to retrieve the certificate"
97
- end
98
-
99
- unless cert_pem.empty?
100
- build_openssl_obj(cert_pem)
101
- end
100
+ verify_certificate(cert_pem)
101
+ build_openssl_obj(cert_pem)
102
102
  end
103
103
 
104
104
  # Listing certificate of open certstore and return list in json
@@ -125,6 +125,7 @@ module Win32
125
125
  def cert_delete(store_handler, certificate_thumbprint)
126
126
  validate_thumbprint(certificate_thumbprint)
127
127
  thumbprint = update_thumbprint(certificate_thumbprint)
128
+
128
129
  cert_delete_flag = false
129
130
  begin
130
131
  cert_args = cert_find_args(store_handler, thumbprint)
@@ -142,10 +143,11 @@ module Win32
142
143
  # Verify certificate from open certificate store and return boolean or exceptions
143
144
  # store_handler => Open certificate store handler
144
145
  # certificate_thumbprint => thumbprint is a hash. which could be sha1 or md5.
145
- def cert_validate(certificate_thumbprint, store_location:, store_name:)
146
+ def cert_validate(certificate_thumbprint)
146
147
  validate_thumbprint(certificate_thumbprint)
147
148
  thumbprint = update_thumbprint(certificate_thumbprint)
148
- cert_pem = get_cert_pem(thumbprint, store_name: store_name, store_location: store_location)
149
+
150
+ cert_pem = get_cert_pem(thumbprint)
149
151
  cert_pem = format_pem(cert_pem)
150
152
  verify_certificate(cert_pem)
151
153
  end
@@ -171,6 +173,26 @@ module Win32
171
173
  certificate_list
172
174
  end
173
175
 
176
+ # how can I find a cert if I don't have the thumbprint? This should be replaced by a call to CertFindCertificateInStore
177
+ def cert_lookup_by_token(search_token, store_name: @store_name, store_location: @store_location, timeout: -1)
178
+ raise ArgumentError, "Invalid search token" if !search_token || search_token.strip.empty?
179
+
180
+ converted_store = if store_location == CERT_SYSTEM_STORE_LOCAL_MACHINE || store_location == 131072
181
+ "LocalMachine"
182
+ else
183
+ "CurrentUser"
184
+ end
185
+ powershell_cmd = <<~EOH
186
+ $result = Get-ChildItem -Path Cert:\\#{converted_store}\\#{store_name} | Where-Object { $_.Subject -match "#{search_token.strip}" } | Select-Object Thumbprint
187
+ return $result[0].Thumbprint
188
+ EOH
189
+
190
+ powershell_exec!(powershell_cmd, :powershell, timeout: timeout).result
191
+
192
+ rescue ChefPowerShell::PowerShellExceptions::PowerShellCommandFailed
193
+ raise ArgumentError, "Certificate not found while looking for certificate : #{search_token} in store : #{store_name} at this location : #{store_location}"
194
+ end
195
+
174
196
  # To close and destroy pointer of open certificate store handler
175
197
  def close_cert_store(certstore_handler = @certstore_handler)
176
198
  closed = CertCloseStore(certstore_handler, CERT_CLOSE_STORE_FORCE_FLAG)
@@ -194,6 +216,11 @@ module Win32
194
216
  [certstore_handler, cert_context, CERT_STORE_ADD_REPLACE_EXISTING, nil]
195
217
  end
196
218
 
219
+ # Remove extra space and : from thumbprint
220
+ def update_thumbprint(certificate_thumbprint)
221
+ certificate_thumbprint.gsub(/[^A-Za-z0-9]/, "")
222
+ end
223
+
197
224
  # Match certificate CN exist in cert_rdn
198
225
  def is_cn_match?(cert_rdn, certificate_name)
199
226
  cert_rdn.read_wstring.match(/(^|\W)#{certificate_name}($|\W)/i)
@@ -216,14 +243,9 @@ module Win32
216
243
  [pcert_context, search_type, CERT_NAME_ISSUER_FLAG, nil, cert_name, 1024]
217
244
  end
218
245
 
219
- # Remove extra space and : from thumbprint
220
- def update_thumbprint(certificate_thumbprint)
221
- certificate_thumbprint.gsub(/[^A-Za-z0-9]/, "")
222
- end
223
-
224
246
  # Verify OpenSSL::X509::Certificate object
225
247
  def verify_certificate(cert_pem)
226
- return "Certificate not found" if cert_pem.empty?
248
+ raise ArgumentError, "Certificate not found" if cert_pem.empty?
227
249
 
228
250
  valid_duration?(build_openssl_obj(cert_pem))
229
251
  end
@@ -233,15 +255,17 @@ module Win32
233
255
  FFI::MemoryPointer.from_string(cert_obj.to_der)
234
256
  end
235
257
 
236
- # Get certificate pem
237
- def get_cert_pem(thumbprint, store_name:, store_location:)
238
- converted_store = if store_location == CERT_SYSTEM_STORE_LOCAL_MACHINE
258
+ # Get certificate pem.
259
+ def get_cert_pem(thumbprint, store_name: @store_name, store_location: @store_location, timeout: -1)
260
+ converted_store = if store_location == CERT_SYSTEM_STORE_LOCAL_MACHINE || store_location == 131072
239
261
  "LocalMachine"
240
262
  else
241
263
  "CurrentUser"
242
264
  end
243
- get_data = powershell_exec!(cert_ps_cmd(thumbprint, store_location: converted_store, store_name: store_name))
244
- get_data.stdout
265
+ get_data = powershell_exec!(cert_ps_cmd(thumbprint, store_location: converted_store, store_name: store_name), :powershell, timeout: timeout)
266
+ get_data.result
267
+ rescue ChefPowerShell::PowerShellExceptions::PowerShellCommandFailed
268
+ raise ArgumentError, "PowerShell threw an error retreiving the certificate. You asked for a cert with this thumbprint : #{thumbprint}, located in this store : #{store_name}, at this location : #{store_location}"
245
269
  end
246
270
 
247
271
  # Format pem
@@ -1,6 +1,6 @@
1
1
  module Win32
2
2
  class Certstore
3
- VERSION = "0.6.2".freeze
3
+ VERSION = "0.6.10".freeze
4
4
  MAJOR, MINOR, TINY = VERSION.split(".")
5
5
  end
6
6
  end
@@ -29,23 +29,23 @@ module Win32
29
29
  include Win32::Certstore::Mixin::String
30
30
  include Win32::Certstore::StoreBase
31
31
 
32
- attr_accessor :store_name
32
+ attr_accessor :store_name, :store_location
33
33
 
34
34
  # Initializes a new instance of a certificate store.
35
35
  # takes 2 parameters - the store name (My, Root, etc) and the location (CurrentUser or LocalMachine), it defaults to LocalMachine for backwards compatibility
36
- def initialize(store_name, store_location: CERT_SYSTEM_STORE_LOCAL_MACHINE)
36
+ def initialize(store_name, store_location)
37
37
  @store_name = store_name
38
38
  @store_location = store_location
39
- @certstore_handler = open(store_name, store_location: store_location)
39
+ @certstore_handler = open(store_name, store_location)
40
40
  end
41
41
 
42
42
  # To open given certificate store
43
43
  def self.open(store_name, store_location: CERT_SYSTEM_STORE_LOCAL_MACHINE)
44
44
  validate_store(store_name)
45
45
  if block_given?
46
- yield new(store_name, store_location: store_location)
46
+ yield new(store_name, store_location)
47
47
  else
48
- new(store_name, store_location: store_location)
48
+ new(store_name, store_location)
49
49
  end
50
50
  end
51
51
 
@@ -74,8 +74,19 @@ module Win32
74
74
  # Return `OpenSSL::X509` certificate object
75
75
  # @param request [thumbprint<string>] of certificate
76
76
  # @return [Object] of certificates in OpenSSL::X509 format
77
- def get(certificate_thumbprint, store_name: @store_name, store_location: @store_location)
78
- cert_get(certificate_thumbprint, store_name: store_name, store_location: store_location)
77
+ def get(certificate_thumbprint)
78
+ cert_get(certificate_thumbprint)
79
+ end
80
+
81
+ # Return `OpenSSL::X509` certificate object if present otherwise raise a "Certificate not found!" error
82
+ # @param request [thumbprint<string>] of certificate
83
+ # @return [Object] of certificates in OpenSSL::X509 format
84
+ def get!(certificate_thumbprint)
85
+ cert_pem = cert_get(certificate_thumbprint)
86
+
87
+ raise ArgumentError, "Unable to retrieve the certificate" if cert_pem.empty?
88
+
89
+ cert_pem
79
90
  end
80
91
 
81
92
  # Returns all the certificates in a store
@@ -99,11 +110,21 @@ module Win32
99
110
  cert_search(certstore_handler, search_token)
100
111
  end
101
112
 
113
+ def get_thumbprint(search_token)
114
+ cert_lookup_by_token(search_token)
115
+ end
116
+
102
117
  # Validates a certificate in a certificate store on the basis of time validity
103
118
  # @param request[thumbprint<string>] of certificate
104
119
  # @return [true, false] only true or false
105
- def valid?(certificate_thumbprint, store_location: "", store_name: "")
106
- cert_validate(certificate_thumbprint, store_location: store_location, store_name: store_name)
120
+ def valid?(certificate_thumbprint)
121
+ cert_validate(certificate_thumbprint).yield_self do |x|
122
+ if x.is_a?(TrueClass) || x.is_a?(FalseClass)
123
+ x
124
+ else
125
+ false
126
+ end
127
+ end
107
128
  end
108
129
 
109
130
  # To close and destroy pointer of open certificate store handler
@@ -122,7 +143,7 @@ module Win32
122
143
 
123
144
  # To open certstore and return open certificate store pointer
124
145
 
125
- def open(store_name, store_location: CERT_SYSTEM_STORE_LOCAL_MACHINE)
146
+ def open(store_name, store_location)
126
147
  certstore_handler = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, nil, store_location, wstring(store_name))
127
148
  unless certstore_handler
128
149
  last_error = FFI::LastError.error
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: win32-certstore
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.6.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chef Software
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-15 00:00:00.000000000 Z
11
+ date: 2022-02-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '3.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: mixlib-shellout
42
+ name: ffi
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -53,19 +53,19 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: ffi
56
+ name: chef-powershell
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: 1.0.12
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: 1.0.12
69
69
  description:
70
70
  email:
71
71
  - oss@chef.io
@@ -79,7 +79,6 @@ files:
79
79
  - lib/win32/certstore/mixin/assertions.rb
80
80
  - lib/win32/certstore/mixin/crypto.rb
81
81
  - lib/win32/certstore/mixin/helper.rb
82
- - lib/win32/certstore/mixin/shell_exec.rb
83
82
  - lib/win32/certstore/mixin/string.rb
84
83
  - lib/win32/certstore/mixin/unicode.rb
85
84
  - lib/win32/certstore/store_base.rb
@@ -1,105 +0,0 @@
1
- #
2
- # Author:: Daniel DeLeo (<dan@chef.io>)
3
- # Copyright:: Copyright (c) 2017 Chef Software, Inc.
4
- # License:: Apache License, Version 2.0
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
-
18
- require "mixlib/shellout" unless defined?(Mixlib::ShellOut)
19
-
20
- module Win32
21
- class Certstore
22
- module Mixin
23
- module ShellExec
24
- def shell_out_command(*command_args)
25
- cmd = Mixlib::ShellOut.new(*command_args)
26
- cmd.live_stream
27
- cmd.run_command
28
- if cmd.error!
29
- raise Mixlib::ShellOut::ShellCommandFailed, cmd.error!
30
- end
31
-
32
- cmd
33
- end
34
-
35
- # Run a command under powershell with the same API as shell_out. The
36
- # options hash is extended to take an "architecture" flag which
37
- # can be set to :i386 or :x86_64 to force the windows architecture.
38
- #
39
- # @param script [String] script to run
40
- # @param options [Hash] options hash
41
- # @return [Mixlib::Shellout] mixlib-shellout object
42
- def powershell_exec(*command_args)
43
- script = command_args.first
44
- options = command_args.last.is_a?(Hash) ? command_args.last : nil
45
-
46
- run_command_with_os_architecture(script, options)
47
- end
48
-
49
- # Run a command under powershell with the same API as shell_out!
50
- # (raises exceptions on errors)
51
- #
52
- # @param script [String] script to run
53
- # @param options [Hash] options hash
54
- # @return [Mixlib::Shellout] mixlib-shellout object
55
- def powershell_exec!(*command_args)
56
- cmd = powershell_exec(*command_args)
57
- cmd.error!
58
- cmd
59
- end
60
-
61
- private
62
-
63
- # Helper function to run shell_out and wrap it with the correct
64
- # flags to possibly disable WOW64 redirection (which we often need
65
- # because chef-client runs as a 32-bit app on 64-bit windows).
66
- #
67
- # @param script [String] script to run
68
- # @param options [Hash] options hash
69
- # @return [Mixlib::Shellout] mixlib-shellout object
70
- def run_command_with_os_architecture(script, options)
71
- options ||= {}
72
- options = options.dup
73
-
74
- shell_out_command(
75
- build_powershell_command(script),
76
- options
77
- )
78
- end
79
-
80
- # Helper to build a powershell command around the script to run.
81
- #
82
- # @param script [String] script to run
83
- # @return [String] powershell command to execute
84
- def build_powershell_command(script)
85
- flags = [
86
- # Hides the copyright banner at startup.
87
- "-NoLogo",
88
- # Does not present an interactive prompt to the user.
89
- "-NonInteractive",
90
- # Does not load the Windows PowerShell profile.
91
- "-NoProfile",
92
- # always set the ExecutionPolicy flag
93
- # see http://technet.microsoft.com/en-us/library/ee176961.aspx
94
- "-ExecutionPolicy Unrestricted",
95
- # Powershell will hang if STDIN is redirected
96
- # http://connect.microsoft.com/PowerShell/feedback/details/572313/powershell-exe-can-hang-if-stdin-is-redirected
97
- "-InputFormat None",
98
- ]
99
-
100
- "powershell.exe #{flags.join(" ")} -Command \"#{script.gsub('"', '\"')}\""
101
- end
102
- end
103
- end
104
- end
105
- end