win32-certstore 0.5.3 → 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: b391e4d81e324162538a2a3644aea74f0da1f8679409733a58dcf4c590734a6c
4
- data.tar.gz: 5ed7821ec5bffe58cb09608cfc008b287874b5ddafe8556662fa27f42a25c1b1
3
+ metadata.gz: 5a55969ab67094eb7c246c946a77807c596afb12d76fd4eef07d48076ffa43b7
4
+ data.tar.gz: 711f553c79a83a4edb8ed2b572ca4ddac1478658b910d57fdb097d2323b4985f
5
5
  SHA512:
6
- metadata.gz: fc5c77cd659695ac3a58480ab875a6c5daa2b93cd83e45f1fcdfcd1bd89e0cf9653d38d63ae9e5260ecff90a7277f6ee600b91137665d53e672fbb77e1543cfb
7
- data.tar.gz: 5b6a8025b85ae8ce026a9a7b603a35137000525ba182bbbcc8fc9667fb78dd6cf179ad1cfbb1cbdcdf582dd4749f82795a17df01181720d7f77a36a4477dc639
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
@@ -21,18 +21,21 @@ module Win32
21
21
  class Certstore
22
22
  module Mixin
23
23
  module Helper
24
- # PSCommand to search certificate from thumbprint and convert in pem
25
- def cert_ps_cmd(thumbprint, store_name, store_location: CERT_SYSTEM_STORE_LOCAL_MACHINE)
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.
26
26
  <<-EOH
27
+ $cert = Get-ChildItem Cert:\\#{store_location}\\#{store_name} -Recurse | Where-Object { $_.Thumbprint -eq "#{thumbprint}" }
28
+
29
+ $certdata = [System.Convert]::ToBase64String($cert.RawData, 'InsertLineBreaks')
27
30
  $content = $null
28
- $cert = Get-ChildItem Cert:\\'#{store_location}'\\'#{store_name}' -Recurse | Where { $_.Thumbprint -eq '#{thumbprint}' }
29
- if($cert -ne $null)
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
@@ -36,14 +36,12 @@ module Win32
36
36
  ustring += "\000\000" if ustring.length == 0 || ustring[-1].chr != "\000"
37
37
 
38
38
  # encode it all as UTF-16LE AKA Windows Wide Character AKA Windows Unicode
39
- ustring = begin
40
- if ustring.respond_to?(:encode)
41
- ustring.encode("UTF-16LE")
42
- else
43
- require "iconv"
44
- Iconv.conv("UTF-16LE", "UTF-8", ustring)
45
- end
46
- end
39
+ ustring = if ustring.respond_to?(:encode)
40
+ ustring.encode("UTF-16LE")
41
+ else
42
+ require "iconv"
43
+ Iconv.conv("UTF-16LE", "UTF-8", ustring)
44
+ end
47
45
  ustring
48
46
  end
49
47
 
@@ -53,14 +51,12 @@ module Win32
53
51
  wstring = wstring.force_encoding("UTF-16LE") if wstring.respond_to?(:force_encoding)
54
52
 
55
53
  # encode it all as UTF-8
56
- wstring = begin
57
- if wstring.respond_to?(:encode)
58
- wstring.encode("UTF-8")
59
- else
60
- require "iconv"
61
- Iconv.conv("UTF-8", "UTF-16LE", wstring)
62
- end
63
- end
54
+ wstring = if wstring.respond_to?(:encode)
55
+ wstring.encode("UTF-8")
56
+ else
57
+ require "iconv"
58
+ Iconv.conv("UTF-8", "UTF-16LE", wstring)
59
+ end
64
60
  # remove trailing CRLF and NULL characters
65
61
  wstring.strip!
66
62
  wstring
@@ -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
@@ -92,9 +97,8 @@ module Win32
92
97
  thumbprint = update_thumbprint(certificate_thumbprint)
93
98
  cert_pem = get_cert_pem(thumbprint)
94
99
  cert_pem = format_pem(cert_pem)
95
- unless cert_pem.empty?
96
- build_openssl_obj(cert_pem)
97
- end
100
+ verify_certificate(cert_pem)
101
+ build_openssl_obj(cert_pem)
98
102
  end
99
103
 
100
104
  # Listing certificate of open certstore and return list in json
@@ -121,6 +125,7 @@ module Win32
121
125
  def cert_delete(store_handler, certificate_thumbprint)
122
126
  validate_thumbprint(certificate_thumbprint)
123
127
  thumbprint = update_thumbprint(certificate_thumbprint)
128
+
124
129
  cert_delete_flag = false
125
130
  begin
126
131
  cert_args = cert_find_args(store_handler, thumbprint)
@@ -141,6 +146,7 @@ module Win32
141
146
  def cert_validate(certificate_thumbprint)
142
147
  validate_thumbprint(certificate_thumbprint)
143
148
  thumbprint = update_thumbprint(certificate_thumbprint)
149
+
144
150
  cert_pem = get_cert_pem(thumbprint)
145
151
  cert_pem = format_pem(cert_pem)
146
152
  verify_certificate(cert_pem)
@@ -167,6 +173,26 @@ module Win32
167
173
  certificate_list
168
174
  end
169
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
+
170
196
  # To close and destroy pointer of open certificate store handler
171
197
  def close_cert_store(certstore_handler = @certstore_handler)
172
198
  closed = CertCloseStore(certstore_handler, CERT_CLOSE_STORE_FORCE_FLAG)
@@ -190,6 +216,11 @@ module Win32
190
216
  [certstore_handler, cert_context, CERT_STORE_ADD_REPLACE_EXISTING, nil]
191
217
  end
192
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
+
193
224
  # Match certificate CN exist in cert_rdn
194
225
  def is_cn_match?(cert_rdn, certificate_name)
195
226
  cert_rdn.read_wstring.match(/(^|\W)#{certificate_name}($|\W)/i)
@@ -212,14 +243,9 @@ module Win32
212
243
  [pcert_context, search_type, CERT_NAME_ISSUER_FLAG, nil, cert_name, 1024]
213
244
  end
214
245
 
215
- # Remove extra space and : from thumbprint
216
- def update_thumbprint(certificate_thumbprint)
217
- certificate_thumbprint.gsub(/[^A-Za-z0-9]/, "")
218
- end
219
-
220
246
  # Verify OpenSSL::X509::Certificate object
221
247
  def verify_certificate(cert_pem)
222
- return "Certificate not found" if cert_pem.empty?
248
+ raise ArgumentError, "Certificate not found" if cert_pem.empty?
223
249
 
224
250
  valid_duration?(build_openssl_obj(cert_pem))
225
251
  end
@@ -229,16 +255,17 @@ module Win32
229
255
  FFI::MemoryPointer.from_string(cert_obj.to_der)
230
256
  end
231
257
 
232
- # Get certificate pem
233
- def get_cert_pem(thumbprint)
234
- 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
235
261
  "LocalMachine"
236
262
  else
237
263
  "CurrentUser"
238
264
  end
239
- get_data = powershell_exec!(cert_ps_cmd(thumbprint, store_name, store_location: converted_store))
240
- get_data.stdout
241
- # get_data.result
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}"
242
269
  end
243
270
 
244
271
  # Format pem
@@ -1,6 +1,6 @@
1
1
  module Win32
2
2
  class Certstore
3
- VERSION = "0.5.3".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
 
@@ -78,6 +78,17 @@ module Win32
78
78
  cert_get(certificate_thumbprint)
79
79
  end
80
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
90
+ end
91
+
81
92
  # Returns all the certificates in a store
82
93
  # @param [nil]
83
94
  # @return [Array] array of certificates list
@@ -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
120
  def valid?(certificate_thumbprint)
106
- cert_validate(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.5.3
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-02-01 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