ruby_smb 2.0.3 → 2.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +1 -2
  4. data/.github/workflows/verify.yml +56 -0
  5. data/README.md +0 -1
  6. data/examples/delete_file.rb +0 -0
  7. data/examples/net_share_enum_all.rb +0 -0
  8. data/examples/pipes.rb +0 -0
  9. data/examples/rename_file.rb +0 -0
  10. data/lib/ruby_smb.rb +3 -2
  11. data/lib/ruby_smb/client.rb +12 -8
  12. data/lib/ruby_smb/client/authentication.rb +5 -10
  13. data/lib/ruby_smb/client/echo.rb +2 -4
  14. data/lib/ruby_smb/client/negotiation.rb +4 -6
  15. data/lib/ruby_smb/client/tree_connect.rb +2 -4
  16. data/lib/ruby_smb/client/utils.rb +16 -10
  17. data/lib/ruby_smb/client/winreg.rb +1 -1
  18. data/lib/ruby_smb/compression.rb +7 -0
  19. data/lib/ruby_smb/compression/lznt1.rb +164 -0
  20. data/lib/ruby_smb/dcerpc.rb +3 -1
  21. data/lib/ruby_smb/dcerpc/ndr.rb +97 -0
  22. data/lib/ruby_smb/dcerpc/netlogon.rb +101 -0
  23. data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request.rb +37 -0
  24. data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response.rb +26 -0
  25. data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request.rb +37 -0
  26. data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response.rb +23 -0
  27. data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request.rb +32 -0
  28. data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response.rb +24 -0
  29. data/lib/ruby_smb/dcerpc/request.rb +6 -0
  30. data/lib/ruby_smb/dcerpc/winreg.rb +2 -2
  31. data/lib/ruby_smb/dispatcher/socket.rb +1 -1
  32. data/lib/ruby_smb/error.rb +21 -5
  33. data/lib/ruby_smb/generic_packet.rb +11 -1
  34. data/lib/ruby_smb/smb1/file.rb +8 -15
  35. data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +0 -1
  36. data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +0 -1
  37. data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +1 -2
  38. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +1 -13
  39. data/lib/ruby_smb/smb1/pipe.rb +8 -8
  40. data/lib/ruby_smb/smb1/tree.rb +13 -9
  41. data/lib/ruby_smb/smb2/file.rb +8 -16
  42. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +4 -0
  43. data/lib/ruby_smb/smb2/pipe.rb +8 -8
  44. data/lib/ruby_smb/smb2/tree.rb +12 -8
  45. data/lib/ruby_smb/version.rb +1 -1
  46. data/spec/lib/ruby_smb/client_spec.rb +17 -12
  47. data/spec/lib/ruby_smb/compression/lznt1_spec.rb +32 -0
  48. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request_spec.rb +69 -0
  49. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response_spec.rb +53 -0
  50. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request_spec.rb +69 -0
  51. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response_spec.rb +37 -0
  52. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request_spec.rb +45 -0
  53. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response_spec.rb +37 -0
  54. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +12 -0
  55. data/spec/lib/ruby_smb/error_spec.rb +34 -5
  56. data/spec/lib/ruby_smb/generic_packet_spec.rb +7 -0
  57. data/spec/lib/ruby_smb/smb1/file_spec.rb +2 -4
  58. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +0 -1
  59. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +0 -1
  60. data/spec/lib/ruby_smb/smb1/packet/trans2/open2_response_spec.rb +0 -5
  61. data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_response_spec.rb +0 -6
  62. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +23 -5
  63. data/spec/lib/ruby_smb/smb1/tree_spec.rb +22 -0
  64. data/spec/lib/ruby_smb/smb2/file_spec.rb +1 -3
  65. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +2 -5
  66. data/spec/lib/ruby_smb/smb2/tree_spec.rb +23 -0
  67. data/spec/spec_helper.rb +1 -1
  68. metadata +42 -19
  69. metadata.gz.sig +0 -0
  70. data/.travis.yml +0 -6
@@ -13,14 +13,16 @@ module RubySMB
13
13
  require 'ruby_smb/dcerpc/rpc_security_attributes'
14
14
  require 'ruby_smb/dcerpc/pdu_header'
15
15
  require 'ruby_smb/dcerpc/srvsvc'
16
- require 'ruby_smb/dcerpc/winreg'
17
16
  require 'ruby_smb/dcerpc/svcctl'
17
+ require 'ruby_smb/dcerpc/winreg'
18
+ require 'ruby_smb/dcerpc/netlogon'
18
19
  require 'ruby_smb/dcerpc/request'
19
20
  require 'ruby_smb/dcerpc/response'
20
21
  require 'ruby_smb/dcerpc/bind'
21
22
  require 'ruby_smb/dcerpc/bind_ack'
22
23
 
23
24
 
25
+
24
26
  # Bind to the remote server interface endpoint.
25
27
  #
26
28
  # @param options [Hash] the options to pass to the Bind request packet. At least, :endpoint must but provided with an existing Dcerpc class
@@ -7,6 +7,9 @@ module RubySMB
7
7
  VER_MAJOR = 2
8
8
  VER_MINOR = 0
9
9
 
10
+ # An NDR Enum type as defined in
11
+ # [Transfer Syntax NDR - Enumerated Types](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_02_05_01)
12
+ class NdrEnum < BinData::Int16le; end
10
13
 
11
14
  # An NDR Conformant and Varying String representation as defined in
12
15
  # [Transfer Syntax NDR - Conformant and Varying Strings](http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_04_02)
@@ -92,6 +95,100 @@ module RubySMB
92
95
  end
93
96
  end
94
97
 
98
+ # An NDR Uni-dimensional Fixed Array of bytes representation as defined in:
99
+ # [Transfer Syntax NDR - NDR Constructed Types](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_03_01)
100
+ class NdrFixedByteArray < BinData::BasePrimitive
101
+ optional_parameters :read_length, :length, :pad_byte, :pad_front
102
+ default_parameters pad_byte: 0
103
+ mutually_exclusive_parameters :length, :value
104
+
105
+ def initialize_shared_instance
106
+ if (has_parameter?(:value) || has_parameter?(:asserted_value)) && !has_parameter?(:read_length)
107
+ extend WarnNoReadLengthPlugin
108
+ end
109
+ super
110
+ end
111
+
112
+ def assign(val)
113
+ super(fixed_byte_array(val))
114
+ end
115
+
116
+ def snapshot
117
+ clamp_to_length(super)
118
+ end
119
+
120
+ class << self
121
+ def arg_processor
122
+ NdrFixedByteArrayArgProcessor.new
123
+ end
124
+ end
125
+
126
+ private
127
+
128
+ def clamp_to_length(val)
129
+ val = fixed_byte_array(val)
130
+ len = eval_parameter(:length) || val.length
131
+ if val.length > len
132
+ val = val.first(len)
133
+ elsif val.length < len
134
+ pad = eval_parameter(:pad_byte)
135
+ if get_parameter(:pad_front)
136
+ val = val.insert(0, *Array.new(len - val.length, pad))
137
+ else
138
+ val = val.fill(pad, val.length...len)
139
+ end
140
+ end
141
+
142
+ val
143
+ end
144
+
145
+ def fixed_byte_array(val)
146
+ val = val.bytes if val.is_a? String
147
+ val.to_ary
148
+ end
149
+
150
+ def read_and_return_value(io)
151
+ len = eval_parameter(:read_length) || eval_parameter(:length) || 0
152
+ io.readbytes(len)
153
+ end
154
+
155
+ def sensible_default
156
+ [ ]
157
+ end
158
+
159
+ def value_to_binary_string(val)
160
+ clamp_to_length(val).pack('C*')
161
+ end
162
+
163
+ class NdrFixedByteArrayArgProcessor < BinData::BaseArgProcessor
164
+ def sanitize_parameters!(obj_class, obj_params)
165
+ obj_params.must_be_integer(:length, :pad_byte)
166
+ obj_params.sanitize(:pad_byte) { |byte| sanitized_pad_byte(byte) }
167
+ end
168
+
169
+ private
170
+
171
+ def sanitized_pad_byte(byte)
172
+ if byte.is_a?(String)
173
+ raise ArgumentError, ':pad_byte must not contain more than 1 byte' if byte.bytesize > 1
174
+
175
+ byte = byte.ord
176
+ end
177
+ raise ArgumentError, ':pad_byte must be within the range of 0 - 255' unless ((byte >= 0) && (byte <= 255))
178
+
179
+ byte
180
+ end
181
+ end
182
+
183
+ # Warns when reading if :value && no :read_length
184
+ module WarnNoReadLengthPlugin
185
+ def read_and_return_value(io)
186
+ warn "#{debug_name} does not have a :read_length parameter - returning empty array"
187
+ ""
188
+ end
189
+ end
190
+ end
191
+
95
192
  # An NDR Context Handle representation as defined in
96
193
  # [IDL Data Type Declarations - Basic Type Declarations](http://pubs.opengroup.org/onlinepubs/9629399/apdxn.htm#tagcjh_34_01)
97
194
  class NdrContextHandle < BinData::Primitive
@@ -0,0 +1,101 @@
1
+ module RubySMB
2
+ module Dcerpc
3
+ module Netlogon
4
+
5
+ # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/592edbc8-f6f1-40c0-9ab3-fe6725ac6d7e
6
+ UUID = '12345678-1234-abcd-ef00-01234567cffb'
7
+ VER_MAJOR = 1
8
+ VER_MINOR = 0
9
+
10
+ # Operation numbers
11
+ NETR_SERVER_REQ_CHALLENGE = 4
12
+ NETR_SERVER_AUTHENTICATE3 = 26
13
+ NETR_SERVER_PASSWORD_SET2 = 30
14
+
15
+ # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/3b224201-b531-43e2-8c79-b61f6dea8640
16
+ class LogonsrvHandle < Ndr::NdrLpStr; end
17
+
18
+ # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/d55e2632-7163-4f6c-b662-4b870e8cc1cd
19
+ class NetlogonCredential < Ndr::NdrFixedByteArray
20
+ default_parameters length: 8
21
+ end
22
+
23
+ # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/76c93227-942a-4687-ab9d-9d972ffabdab
24
+ class NetlogonAuthenticator < BinData::Record
25
+ endian :little
26
+
27
+ netlogon_credential :credential
28
+ uint32 :timestamp
29
+ end
30
+
31
+ # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/4d1235e3-2c96-4e9f-a147-3cb338a0d09f
32
+ class NetlogonSecureChannelType < Ndr::NdrEnum
33
+ # enum example from dmendel/bindata#38 https://github.com/dmendel/bindata/issues/38#issuecomment-46397163
34
+ ALL = {
35
+ 0 => :NullSecureChannel,
36
+ 1 => :MsvApSecureChannel,
37
+ 2 => :WorkstationSecureChannel,
38
+ 3 => :TrustedDnsDomainSecureChannel,
39
+ 4 => :TrustedDomainSecureChannel,
40
+ 5 => :UasServerSecureChannel,
41
+ 6 => :ServerSecureChannel,
42
+ 7 => :CdcServerSecureChannel
43
+ }
44
+ ALL.each_pair { |val,sym| const_set(sym.to_s.gsub(/([a-z])([A-Z])/, '\1_\2').upcase, val) }
45
+ default_parameter assert: -> { ALL.keys.include? value }
46
+
47
+ def as_enum
48
+ ALL[value]
49
+ end
50
+
51
+ def assign(val)
52
+ if val.is_a? Symbol
53
+ val = ALL.key(val)
54
+ raise ArgumentError, 'invalid value name' if val.nil?
55
+ end
56
+
57
+ super
58
+ end
59
+ end
60
+
61
+ require 'ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request'
62
+ require 'ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response'
63
+ require 'ruby_smb/dcerpc/netlogon/netr_server_password_set2_request'
64
+ require 'ruby_smb/dcerpc/netlogon/netr_server_password_set2_response'
65
+ require 'ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request'
66
+ require 'ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response'
67
+
68
+ # Calculate the netlogon session key from the provided shared secret and
69
+ # challenges. The shared secret is an NTLM hash.
70
+ #
71
+ # @param shared_secret [String] the share secret between the client and the server
72
+ # @param client_challenge [String] the client challenge portion of the negotiation
73
+ # @param server_challenge [String] the server challenge portion of the negotiation
74
+ # @return [String] the session key for encryption
75
+ def self.calculate_session_key(shared_secret, client_challenge, server_challenge)
76
+ client_challenge = client_challenge.to_binary_s if client_challenge.is_a? NetlogonCredential
77
+ server_challenge = server_challenge.to_binary_s if server_challenge.is_a? NetlogonCredential
78
+
79
+ hmac = OpenSSL::HMAC.new(shared_secret, OpenSSL::Digest::SHA256.new)
80
+ hmac << client_challenge
81
+ hmac << server_challenge
82
+ hmac.digest.first(16)
83
+ end
84
+
85
+ # Encrypt the input data using the specified session key. This is used for
86
+ # certain Netlogon service operations including the authentication
87
+ # process. Per the specification, this uses AES-128-CFB8 with an all zero
88
+ # initialization vector.
89
+ #
90
+ # @param session_key [String] the session key to use for encryption (must be 16 bytes long)
91
+ # @param input_data [String] the data to encrypt
92
+ # @return [String] the encrypted data
93
+ def self.encrypt_credential(session_key, input_data)
94
+ cipher = OpenSSL::Cipher.new('AES-128-CFB8').encrypt
95
+ cipher.iv = "\x00" * 16
96
+ cipher.key = session_key
97
+ cipher.update(input_data) + cipher.final
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,37 @@
1
+ require 'ruby_smb/dcerpc/ndr'
2
+
3
+ module RubySMB
4
+ module Dcerpc
5
+ module Netlogon
6
+
7
+ # [3.5.4.4.2 NetrServerAuthenticate3 (Opnum 26)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/3a9ed16f-8014-45ae-80af-c0ecb06e2db9)
8
+ class NetrServerAuthenticate3Request < BinData::Record
9
+ attr_reader :opnum
10
+
11
+ endian :little
12
+
13
+ logonsrv_handle :primary_name
14
+ string :pad1, length: -> { pad_length(self.primary_name) }
15
+ ndr_string :account_name
16
+ netlogon_secure_channel_type :secure_channel_type
17
+ string :pad2, length: -> { pad_length(self.secure_channel_type) }
18
+ ndr_string :computer_name
19
+ netlogon_credential :client_credential
20
+ string :pad3, length: -> { pad_length(self.client_credential) }
21
+ uint32 :flags
22
+
23
+ def initialize_instance
24
+ super
25
+ @opnum = NETR_SERVER_AUTHENTICATE3
26
+ end
27
+
28
+ # Determines the correct length for the padding, so that the next
29
+ # field is 4-byte aligned.
30
+ def pad_length(prev_element)
31
+ offset = (prev_element.abs_offset + prev_element.to_binary_s.length) % 4
32
+ (4 - offset) % 4
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ require 'ruby_smb/dcerpc/ndr'
2
+
3
+ module RubySMB
4
+ module Dcerpc
5
+ module Netlogon
6
+
7
+ # [3.5.4.4.2 NetrServerAuthenticate3 (Opnum 26)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/3a9ed16f-8014-45ae-80af-c0ecb06e2db9)
8
+ class NetrServerAuthenticate3Response < BinData::Record
9
+ attr_reader :opnum
10
+
11
+ endian :little
12
+
13
+ netlogon_credential :server_credential
14
+ uint32 :negotiate_flags
15
+ uint32 :account_rid
16
+ uint32 :error_status
17
+
18
+ def initialize_instance
19
+ super
20
+ @opnum = NETR_SERVER_AUTHENTICATE3
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,37 @@
1
+ require 'ruby_smb/dcerpc/ndr'
2
+
3
+ module RubySMB
4
+ module Dcerpc
5
+ module Netlogon
6
+
7
+ # [3.5.4.4.5 NetrServerPasswordSet2 (Opnum 30)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/14b020a8-0bcf-4af5-ab72-cc92bc6b1d81)
8
+ class NetrServerPasswordSet2Request < BinData::Record
9
+ attr_reader :opnum
10
+
11
+ endian :little
12
+
13
+ logonsrv_handle :primary_name
14
+ string :pad1, length: -> { pad_length(self.primary_name) }
15
+ ndr_string :account_name
16
+ netlogon_secure_channel_type :secure_channel_type
17
+ string :pad2, length: -> { pad_length(self.secure_channel_type) }
18
+ ndr_string :computer_name
19
+ string :pad3, length: -> { pad_length(self.computer_name) }
20
+ netlogon_authenticator :authenticator
21
+ ndr_fixed_byte_array :clear_new_password, length: 516 # this is an encrypted NL_TRUST_PASSWORD
22
+
23
+ def initialize_instance
24
+ super
25
+ @opnum = Netlogon::NETR_SERVER_PASSWORD_SET2
26
+ end
27
+
28
+ # Determines the correct length for the padding, so that the next
29
+ # field is 4-byte aligned.
30
+ def pad_length(prev_element)
31
+ offset = (prev_element.abs_offset + prev_element.to_binary_s.length) % 4
32
+ (4 - offset) % 4
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,23 @@
1
+ require 'ruby_smb/dcerpc/ndr'
2
+
3
+ module RubySMB
4
+ module Dcerpc
5
+ module Netlogon
6
+
7
+ # [3.5.4.4.5 NetrServerPasswordSet2 (Opnum 30)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/14b020a8-0bcf-4af5-ab72-cc92bc6b1d81)
8
+ class NetrServerPasswordSet2Response < BinData::Record
9
+ attr_reader :opnum
10
+
11
+ endian :little
12
+
13
+ netlogon_authenticator :return_authenticator
14
+ uint32 :error_status
15
+
16
+ def initialize_instance
17
+ super
18
+ @opnum = Netlogon::NETR_SERVER_PASSWORD_SET2
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,32 @@
1
+ require 'ruby_smb/dcerpc/ndr'
2
+
3
+ module RubySMB
4
+ module Dcerpc
5
+ module Netlogon
6
+
7
+ # [3.5.4.4.1 NetrServerReqChallenge (Opnum 4)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/5ad9db9f-7441-4ce5-8c7b-7b771e243d32)
8
+ class NetrServerReqChallengeRequest < BinData::Record
9
+ attr_reader :opnum
10
+
11
+ endian :little
12
+
13
+ logonsrv_handle :primary_name
14
+ string :pad1, length: -> { pad_length(self.primary_name) }
15
+ ndr_string :computer_name
16
+ netlogon_credential :client_challenge
17
+
18
+ def initialize_instance
19
+ super
20
+ @opnum = NETR_SERVER_REQ_CHALLENGE
21
+ end
22
+
23
+ # Determines the correct length for the padding, so that the next
24
+ # field is 4-byte aligned.
25
+ def pad_length(prev_element)
26
+ offset = (prev_element.abs_offset + prev_element.to_binary_s.length) % 4
27
+ (4 - offset) % 4
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,24 @@
1
+ require 'ruby_smb/dcerpc/ndr'
2
+
3
+ module RubySMB
4
+ module Dcerpc
5
+ module Netlogon
6
+
7
+ # [3.5.4.4.1 NetrServerReqChallenge (Opnum 4)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/5ad9db9f-7441-4ce5-8c7b-7b771e243d32)
8
+ class NetrServerReqChallengeResponse < BinData::Record
9
+ attr_reader :opnum
10
+
11
+ endian :little
12
+
13
+ netlogon_credential :server_challenge
14
+ uint32 :error_status
15
+
16
+ def initialize_instance
17
+ super
18
+ @opnum = NETR_SERVER_REQ_CHALLENGE
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
@@ -31,6 +31,12 @@ module RubySMB
31
31
  save_key_request RubySMB::Dcerpc::Winreg::REG_SAVE_KEY
32
32
  string :default
33
33
  end
34
+ choice 'Netlogon', selection: -> { opnum } do
35
+ netr_server_authenticate3_request RubySMB::Dcerpc::Netlogon::NETR_SERVER_AUTHENTICATE3
36
+ netr_server_password_set2_request RubySMB::Dcerpc::Netlogon::NETR_SERVER_PASSWORD_SET2
37
+ netr_server_req_challenge_request RubySMB::Dcerpc::Netlogon::NETR_SERVER_REQ_CHALLENGE
38
+ string :default
39
+ end
34
40
  choice 'Srvsvc', selection: -> { opnum } do
35
41
  net_share_enum_all RubySMB::Dcerpc::Srvsvc::NET_SHARE_ENUM_ALL, host: -> { host rescue '' }
36
42
  string :default
@@ -387,7 +387,7 @@ module RubySMB
387
387
  enum_result
388
388
  ensure
389
389
  close_key(subkey_handle) if subkey_handle
390
- close_key(root_key_handle) if root_key_handle
390
+ close_key(root_key_handle) if root_key_handle && root_key_handle != subkey_handle
391
391
  end
392
392
 
393
393
  # Enumerate the values for the specified registry key.
@@ -413,7 +413,7 @@ module RubySMB
413
413
  enum_result
414
414
  ensure
415
415
  close_key(subkey_handle) if subkey_handle
416
- close_key(root_key_handle) if root_key_handle
416
+ close_key(root_key_handle) if root_key_handle && root_key_handle != subkey_handle
417
417
  end
418
418
 
419
419
  end