ruby_smb 1.0.0 → 1.1.0

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 (171) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.travis.yml +4 -1
  4. data/README.md +35 -47
  5. data/examples/enum_registry_key.rb +28 -0
  6. data/examples/enum_registry_values.rb +30 -0
  7. data/examples/pipes.rb +2 -1
  8. data/examples/read_registry_key_value.rb +32 -0
  9. data/lib/ruby_smb/client/authentication.rb +43 -25
  10. data/lib/ruby_smb/client/echo.rb +20 -2
  11. data/lib/ruby_smb/client/negotiation.rb +27 -12
  12. data/lib/ruby_smb/client/tree_connect.rb +20 -14
  13. data/lib/ruby_smb/client/utils.rb +2 -2
  14. data/lib/ruby_smb/client/winreg.rb +46 -0
  15. data/lib/ruby_smb/client.rb +28 -4
  16. data/lib/ruby_smb/dcerpc/bind.rb +2 -2
  17. data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
  18. data/lib/ruby_smb/dcerpc/error.rb +3 -0
  19. data/lib/ruby_smb/dcerpc/ndr.rb +95 -16
  20. data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
  21. data/lib/ruby_smb/dcerpc/request.rb +28 -9
  22. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +35 -0
  23. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
  24. data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
  25. data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
  26. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
  27. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
  28. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
  29. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
  30. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
  31. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
  32. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
  33. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
  34. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
  35. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
  36. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
  37. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +39 -0
  38. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
  39. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
  40. data/lib/ruby_smb/dcerpc/winreg.rb +340 -0
  41. data/lib/ruby_smb/dcerpc.rb +38 -0
  42. data/lib/ruby_smb/dispatcher/socket.rb +3 -1
  43. data/lib/ruby_smb/error.rb +40 -1
  44. data/lib/ruby_smb/generic_packet.rb +33 -4
  45. data/lib/ruby_smb/smb1/file.rb +67 -11
  46. data/lib/ruby_smb/smb1/packet/close_request.rb +2 -5
  47. data/lib/ruby_smb/smb1/packet/close_response.rb +2 -1
  48. data/lib/ruby_smb/smb1/packet/echo_request.rb +2 -4
  49. data/lib/ruby_smb/smb1/packet/echo_response.rb +2 -1
  50. data/lib/ruby_smb/smb1/packet/empty_packet.rb +10 -1
  51. data/lib/ruby_smb/smb1/packet/logoff_request.rb +2 -4
  52. data/lib/ruby_smb/smb1/packet/logoff_response.rb +2 -1
  53. data/lib/ruby_smb/smb1/packet/negotiate_request.rb +2 -5
  54. data/lib/ruby_smb/smb1/packet/negotiate_response.rb +3 -7
  55. data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +4 -4
  56. data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +2 -4
  57. data/lib/ruby_smb/smb1/packet/nt_create_andx_response.rb +2 -1
  58. data/lib/ruby_smb/smb1/packet/nt_trans/create_request.rb +2 -1
  59. data/lib/ruby_smb/smb1/packet/nt_trans/create_response.rb +2 -1
  60. data/lib/ruby_smb/smb1/packet/nt_trans/request.rb +2 -4
  61. data/lib/ruby_smb/smb1/packet/nt_trans/response.rb +2 -1
  62. data/lib/ruby_smb/smb1/packet/read_andx_request.rb +2 -5
  63. data/lib/ruby_smb/smb1/packet/read_andx_response.rb +2 -1
  64. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +2 -1
  65. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -1
  66. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +2 -5
  67. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -1
  68. data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_request.rb +0 -1
  69. data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_response.rb +3 -2
  70. data/lib/ruby_smb/smb1/packet/trans/request.rb +2 -5
  71. data/lib/ruby_smb/smb1/packet/trans/response.rb +2 -1
  72. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_request.rb +1 -1
  73. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_response.rb +1 -1
  74. data/lib/ruby_smb/smb1/packet/trans2/find_first2_request.rb +2 -1
  75. data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +8 -2
  76. data/lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb +2 -1
  77. data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +8 -2
  78. data/lib/ruby_smb/smb1/packet/trans2/open2_request.rb +2 -1
  79. data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +2 -1
  80. data/lib/ruby_smb/smb1/packet/trans2/request.rb +2 -4
  81. data/lib/ruby_smb/smb1/packet/trans2/request_secondary.rb +2 -4
  82. data/lib/ruby_smb/smb1/packet/trans2/response.rb +2 -1
  83. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb +2 -1
  84. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +2 -1
  85. data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +2 -4
  86. data/lib/ruby_smb/smb1/packet/tree_connect_response.rb +13 -3
  87. data/lib/ruby_smb/smb1/packet/tree_disconnect_request.rb +2 -4
  88. data/lib/ruby_smb/smb1/packet/tree_disconnect_response.rb +2 -1
  89. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +2 -5
  90. data/lib/ruby_smb/smb1/packet/write_andx_response.rb +2 -1
  91. data/lib/ruby_smb/smb1/pipe.rb +86 -5
  92. data/lib/ruby_smb/smb1/tree.rb +40 -2
  93. data/lib/ruby_smb/smb2/file.rb +97 -1
  94. data/lib/ruby_smb/smb2/packet/close_request.rb +2 -4
  95. data/lib/ruby_smb/smb2/packet/close_response.rb +2 -1
  96. data/lib/ruby_smb/smb2/packet/create_request.rb +2 -4
  97. data/lib/ruby_smb/smb2/packet/create_response.rb +2 -1
  98. data/lib/ruby_smb/smb2/packet/echo_request.rb +2 -4
  99. data/lib/ruby_smb/smb2/packet/echo_response.rb +2 -1
  100. data/lib/ruby_smb/smb2/packet/error_packet.rb +15 -3
  101. data/lib/ruby_smb/smb2/packet/ioctl_request.rb +2 -5
  102. data/lib/ruby_smb/smb2/packet/ioctl_response.rb +2 -1
  103. data/lib/ruby_smb/smb2/packet/logoff_request.rb +2 -4
  104. data/lib/ruby_smb/smb2/packet/logoff_response.rb +2 -1
  105. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +2 -5
  106. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +2 -1
  107. data/lib/ruby_smb/smb2/packet/query_directory_request.rb +2 -4
  108. data/lib/ruby_smb/smb2/packet/query_directory_response.rb +8 -2
  109. data/lib/ruby_smb/smb2/packet/read_request.rb +2 -4
  110. data/lib/ruby_smb/smb2/packet/read_response.rb +2 -1
  111. data/lib/ruby_smb/smb2/packet/session_setup_request.rb +2 -5
  112. data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -1
  113. data/lib/ruby_smb/smb2/packet/set_info_request.rb +2 -4
  114. data/lib/ruby_smb/smb2/packet/set_info_response.rb +2 -1
  115. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +2 -5
  116. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -2
  117. data/lib/ruby_smb/smb2/packet/tree_disconnect_request.rb +2 -4
  118. data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +2 -1
  119. data/lib/ruby_smb/smb2/packet/write_request.rb +2 -4
  120. data/lib/ruby_smb/smb2/packet/write_response.rb +2 -1
  121. data/lib/ruby_smb/smb2/pipe.rb +98 -11
  122. data/lib/ruby_smb/smb2/tree.rb +44 -6
  123. data/lib/ruby_smb/version.rb +1 -1
  124. data/lib/ruby_smb.rb +0 -1
  125. data/ruby_smb.gemspec +3 -3
  126. data/spec/lib/ruby_smb/client_spec.rb +271 -11
  127. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
  128. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
  129. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +410 -0
  130. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
  131. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +98 -0
  132. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
  133. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
  134. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
  135. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
  136. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +108 -0
  137. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
  138. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
  139. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
  140. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
  141. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
  142. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +90 -0
  143. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
  144. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +39 -0
  145. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
  146. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
  147. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +150 -0
  148. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
  149. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +710 -0
  150. data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
  151. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +3 -1
  152. data/spec/lib/ruby_smb/generic_packet_spec.rb +52 -4
  153. data/spec/lib/ruby_smb/smb1/file_spec.rb +191 -2
  154. data/spec/lib/ruby_smb/smb1/packet/empty_packet_spec.rb +68 -0
  155. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +11 -2
  156. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +11 -2
  157. data/spec/lib/ruby_smb/smb1/packet/tree_connect_response_spec.rb +40 -0
  158. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +272 -149
  159. data/spec/lib/ruby_smb/smb1/tree_spec.rb +44 -7
  160. data/spec/lib/ruby_smb/smb2/file_spec.rb +295 -2
  161. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +78 -0
  162. data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +8 -0
  163. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +8 -0
  164. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +322 -145
  165. data/spec/lib/ruby_smb/smb2/tree_spec.rb +214 -0
  166. data.tar.gz.sig +0 -0
  167. metadata +70 -11
  168. metadata.gz.sig +0 -0
  169. data/lib/ruby_smb/smb1/dcerpc.rb +0 -67
  170. data/lib/ruby_smb/smb2/dcerpc.rb +0 -70
  171. data/spec/lib/ruby_smb/smb1/packet/error_packet_spec.rb +0 -37
@@ -0,0 +1,710 @@
1
+ RSpec.describe RubySMB::Dcerpc::Winreg do
2
+ let(:winreg) do
3
+ RubySMB::SMB1::Pipe.new(
4
+ tree: double('Tree'),
5
+ response: RubySMB::SMB1::Packet::NtCreateAndxResponse.new,
6
+ name: 'winreg'
7
+ )
8
+ end
9
+
10
+ describe '#open_root_key' do
11
+ let(:root_key_request_packet) { double('Root Key Request Packet') }
12
+ let(:response) { double('Response') }
13
+ let(:root_key_response_packet) { double('Root Key Response Packet') }
14
+ let(:ph_key) { double('PHKEY') }
15
+ before :example do
16
+ allow(described_class::OpenRootKeyRequest).to receive(:new).and_return(root_key_request_packet)
17
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
18
+ allow(described_class::OpenRootKeyResponse).to receive(:read).and_return(root_key_response_packet)
19
+ allow(root_key_response_packet).to receive_messages(
20
+ :error_status => WindowsError::Win32::ERROR_SUCCESS,
21
+ :ph_key => ph_key
22
+ )
23
+ end
24
+
25
+ context 'when the root key is unknown' do
26
+ it 'raises an ArgumentError exception' do
27
+ expect { winreg.open_root_key('UNKNOWN') }.to raise_error(ArgumentError)
28
+ end
29
+ end
30
+
31
+ it 'create the expected OpenRootKeyRequest packet' do
32
+ winreg.open_root_key('HKLM')
33
+ expect(described_class::OpenRootKeyRequest).to have_received(:new).with(opnum: described_class::OPEN_HKLM)
34
+ end
35
+
36
+ it 'sends the expected dcerpc request' do
37
+ winreg.open_root_key('HKLM')
38
+ expect(winreg).to have_received(:dcerpc_request).with(root_key_request_packet)
39
+ end
40
+
41
+ it 'creates a OpenRootKeyResponse structure from the expected dcerpc response' do
42
+ winreg.open_root_key('HKLM')
43
+ expect(described_class::OpenRootKeyResponse).to have_received(:read).with(response)
44
+ end
45
+
46
+ context 'when an IOError occurs while parsing the response' do
47
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
48
+ allow(described_class::OpenRootKeyResponse).to receive(:read).and_raise(IOError)
49
+ expect { winreg.open_root_key('HKLM') }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
50
+ end
51
+ end
52
+
53
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
54
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
55
+ allow(root_key_response_packet).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
56
+ expect { winreg.open_root_key('HKLM') }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
57
+ end
58
+ end
59
+
60
+ it 'returns the expected handler' do
61
+ expect(winreg.open_root_key('HKLM')).to eq(ph_key)
62
+ end
63
+ end
64
+
65
+ describe '#open_key' do
66
+ let(:handle) { double('Handle') }
67
+ let(:sub_key) { double('Sub-key') }
68
+ let(:openkey_request_packet) { double('OpenKey Request Packet') }
69
+ let(:regsam) { double('Regsam') }
70
+ let(:response) { double('Response') }
71
+ let(:open_key_response) { double('OpenKey Response') }
72
+ let(:phk_result) { double('Phk Result') }
73
+ before :example do
74
+ allow(described_class::OpenKeyRequest).to receive(:new).and_return(openkey_request_packet)
75
+ allow(openkey_request_packet).to receive(:sam_desired).and_return(regsam)
76
+ allow(regsam).to receive_messages(
77
+ :read_control= => nil,
78
+ :key_query_value= => nil,
79
+ :key_enumerate_sub_keys= => nil,
80
+ :key_notify= => nil,
81
+ )
82
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
83
+ allow(described_class::OpenKeyResponse).to receive(:read).and_return(open_key_response)
84
+ allow(open_key_response).to receive_messages(
85
+ :error_status => WindowsError::Win32::ERROR_SUCCESS,
86
+ :phk_result => phk_result
87
+ )
88
+ end
89
+
90
+ it 'create the expected OpenKeyRequest packet' do
91
+ winreg.open_key(handle, sub_key)
92
+ expect(described_class::OpenKeyRequest).to have_received(:new).with(hkey: handle, lp_sub_key: sub_key)
93
+ end
94
+
95
+ it 'sets the expected user rights on the request packet' do
96
+ winreg.open_key(handle, sub_key)
97
+ expect(regsam).to have_received(:read_control=).with(1)
98
+ expect(regsam).to have_received(:key_query_value=).with(1)
99
+ expect(regsam).to have_received(:key_enumerate_sub_keys=).with(1)
100
+ expect(regsam).to have_received(:key_notify=).with(1)
101
+ end
102
+
103
+ it 'sends the expected dcerpc request' do
104
+ winreg.open_key(handle, sub_key)
105
+ expect(winreg).to have_received(:dcerpc_request).with(openkey_request_packet)
106
+ end
107
+
108
+ it 'creates a OpenKeyResponse structure from the expected dcerpc response' do
109
+ winreg.open_key(handle, sub_key)
110
+ expect(described_class::OpenKeyResponse).to have_received(:read).with(response)
111
+ end
112
+
113
+ context 'when an IOError occurs while parsing the response' do
114
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
115
+ allow(described_class::OpenKeyResponse).to receive(:read).and_raise(IOError)
116
+ expect { winreg.open_key(handle, sub_key) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
117
+ end
118
+ end
119
+
120
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
121
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
122
+ allow(open_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
123
+ expect { winreg.open_key(handle, sub_key) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
124
+ end
125
+ end
126
+
127
+ it 'returns the expected handler' do
128
+ expect(winreg.open_key(handle, sub_key)).to eq(phk_result)
129
+ end
130
+ end
131
+
132
+ describe '#query_value' do
133
+ let(:handle) { double('Handle') }
134
+ let(:value_name) { double('Value Name') }
135
+ let(:query_value_request_packet1) { double('Query Value Request Packet #1') }
136
+ let(:query_value_request_packet2) { double('Query Value Request Packet #2') }
137
+ let(:lp_data1) { double('LpData #1') }
138
+ let(:lp_data2) { double('LpData #2') }
139
+ let(:response1) { double('Response #1') }
140
+ let(:response2) { double('Response #2') }
141
+ let(:query_value_response1) { double('Query Value Response #1') }
142
+ let(:query_value_response2) { double('Query Value Response #2') }
143
+ let(:data) { double('Data') }
144
+ let(:lpcb_data) { double('LpcbData') }
145
+ let(:lpcb_data_referent) { double('Lpcb Data Referent') }
146
+ before :example do
147
+ first_request = true
148
+ allow(described_class::QueryValueRequest).to receive(:new) do
149
+ if first_request
150
+ first_request = false
151
+ query_value_request_packet1
152
+ else
153
+ query_value_request_packet2
154
+ end
155
+ end
156
+ allow(query_value_request_packet1).to receive(:lp_data).and_return(lp_data1)
157
+ allow(query_value_request_packet2).to receive_messages(
158
+ :lp_data => lp_data2,
159
+ :lpcb_data= => nil
160
+ )
161
+ allow(lp_data1).to receive(:referent_identifier=)
162
+ allow(lp_data2).to receive(:max_count=)
163
+ allow(winreg).to receive(:dcerpc_request).with(query_value_request_packet1).and_return(response1)
164
+ allow(winreg).to receive(:dcerpc_request).with(query_value_request_packet2).and_return(response2)
165
+ allow(described_class::QueryValueResponse).to receive(:read).with(response1).and_return(query_value_response1)
166
+ allow(described_class::QueryValueResponse).to receive(:read).with(response2).and_return(query_value_response2)
167
+ allow(query_value_response1).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
168
+ allow(query_value_response2).to receive_messages(
169
+ :error_status => WindowsError::Win32::ERROR_SUCCESS,
170
+ :data => data
171
+ )
172
+ allow(query_value_response1).to receive(:lpcb_data).and_return(lpcb_data)
173
+ allow(lpcb_data).to receive(:referent).and_return(lpcb_data_referent)
174
+ end
175
+
176
+ it 'create the expected QueryValueRequest packets' do
177
+ winreg.query_value(handle, value_name)
178
+ expect(described_class::QueryValueRequest).to have_received(:new).with(hkey: handle, lp_value_name: value_name).twice
179
+ end
180
+
181
+ it 'sets the expected user rights on the first request packet' do
182
+ winreg.query_value(handle, value_name)
183
+ expect(lp_data1).to have_received(:referent_identifier=).with(0)
184
+ end
185
+
186
+ it 'sets the expected user rights on the second request packet' do
187
+ winreg.query_value(handle, value_name)
188
+ expect(lp_data2).to have_received(:max_count=).with(lpcb_data_referent)
189
+ expect(query_value_request_packet2).to have_received(:lpcb_data=).with(lpcb_data)
190
+ end
191
+
192
+ it 'sends the expected dcerpc requests' do
193
+ winreg.query_value(handle, value_name)
194
+ expect(winreg).to have_received(:dcerpc_request).with(query_value_request_packet1).once.ordered
195
+ expect(winreg).to have_received(:dcerpc_request).with(query_value_request_packet2).once.ordered
196
+ end
197
+
198
+ context 'when receiving the first response' do
199
+ it 'creates a QueryValueResponse structure from the expected dcerpc response' do
200
+ winreg.query_value(handle, value_name)
201
+ expect(described_class::QueryValueResponse).to have_received(:read).with(response1)
202
+ end
203
+
204
+ context 'when an IOError occurs while parsing the response' do
205
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
206
+ allow(described_class::QueryValueResponse).to receive(:read).with(response1).and_raise(IOError)
207
+ expect { winreg.query_value(handle, value_name) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
208
+ end
209
+ end
210
+
211
+ context 'when the first response error status is not WindowsError::Win32::ERROR_SUCCESS' do
212
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
213
+ allow(query_value_response1).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
214
+ expect { winreg.query_value(handle, value_name) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
215
+ end
216
+ end
217
+ end
218
+
219
+ context 'when receiving the second response' do
220
+ it 'creates a QueryValueResponse structure from the expected dcerpc response' do
221
+ winreg.query_value(handle, value_name)
222
+ expect(described_class::QueryValueResponse).to have_received(:read).with(response2)
223
+ end
224
+
225
+ context 'when an IOError occurs while parsing the response' do
226
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
227
+ allow(described_class::QueryValueResponse).to receive(:read).with(response2).and_raise(IOError)
228
+ expect { winreg.query_value(handle, value_name) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
229
+ end
230
+ end
231
+
232
+ context 'when the first response error status is not WindowsError::Win32::ERROR_SUCCESS' do
233
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
234
+ allow(query_value_response2).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
235
+ expect { winreg.query_value(handle, value_name) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
236
+ end
237
+ end
238
+ end
239
+
240
+ it 'returns the expected response data' do
241
+ expect(winreg.query_value(handle, value_name)).to eq(data)
242
+ end
243
+ end
244
+
245
+ describe '#close_key' do
246
+ let(:handle) { double('Handle') }
247
+ let(:close_key_request_packet) { double('CloseKey Request Packet') }
248
+ let(:response) { double('Response') }
249
+ let(:close_key_response) { double('CloseKey Response') }
250
+ before :example do
251
+ allow(described_class::CloseKeyRequest).to receive(:new).and_return(close_key_request_packet)
252
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
253
+ allow(described_class::CloseKeyResponse).to receive(:read).and_return(close_key_response)
254
+ allow(close_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
255
+ end
256
+
257
+ it 'create the expected CloseKeyRequest packet' do
258
+ winreg.close_key(handle)
259
+ expect(described_class::CloseKeyRequest).to have_received(:new).with(hkey: handle)
260
+ end
261
+
262
+ it 'sends the expected dcerpc request' do
263
+ winreg.close_key(handle)
264
+ expect(winreg).to have_received(:dcerpc_request).with(close_key_request_packet)
265
+ end
266
+
267
+ it 'creates a CloseKeyResponse structure from the expected dcerpc response' do
268
+ winreg.close_key(handle)
269
+ expect(described_class::CloseKeyResponse).to have_received(:read).with(response)
270
+ end
271
+
272
+ context 'when an IOError occurs while parsing the response' do
273
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
274
+ allow(described_class::CloseKeyResponse).to receive(:read).and_raise(IOError)
275
+ expect { winreg.close_key(handle) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
276
+ end
277
+ end
278
+
279
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
280
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
281
+ allow(close_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
282
+ expect { winreg.close_key(handle) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
283
+ end
284
+ end
285
+
286
+ it 'returns the expected error status' do
287
+ expect(winreg.close_key(handle)).to eq(WindowsError::Win32::ERROR_SUCCESS)
288
+ end
289
+ end
290
+
291
+ describe '#query_info_key' do
292
+ let(:handle) { double('Handle') }
293
+ let(:query_info_key_request_packet) { double('CloseKey Request Packet') }
294
+ let(:response) { double('Response') }
295
+ let(:query_info_key_response) { double('CloseKey Response') }
296
+ before :example do
297
+ allow(described_class::QueryInfoKeyRequest).to receive(:new).and_return(query_info_key_request_packet)
298
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
299
+ allow(described_class::QueryInfoKeyResponse).to receive(:read).and_return(query_info_key_response)
300
+ allow(query_info_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
301
+ end
302
+
303
+ it 'create the expected QueryInfoKeyRequest packet' do
304
+ winreg.query_info_key(handle)
305
+ expect(described_class::QueryInfoKeyRequest).to have_received(:new).with(hkey: handle)
306
+ end
307
+
308
+ it 'sends the expected dcerpc request' do
309
+ winreg.query_info_key(handle)
310
+ expect(winreg).to have_received(:dcerpc_request).with(query_info_key_request_packet)
311
+ end
312
+
313
+ it 'creates a QueryInfoKeyResponse structure from the expected dcerpc response' do
314
+ winreg.query_info_key(handle)
315
+ expect(described_class::QueryInfoKeyResponse).to have_received(:read).with(response)
316
+ end
317
+
318
+ context 'when an IOError occurs while parsing the response' do
319
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
320
+ allow(described_class::QueryInfoKeyResponse).to receive(:read).and_raise(IOError)
321
+ expect { winreg.query_info_key(handle) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
322
+ end
323
+ end
324
+
325
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
326
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
327
+ allow(query_info_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
328
+ expect { winreg.query_info_key(handle) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
329
+ end
330
+ end
331
+
332
+ it 'returns the expected response' do
333
+ expect(winreg.query_info_key(handle)).to eq(query_info_key_response)
334
+ end
335
+ end
336
+
337
+ describe '#enum_key' do
338
+ let(:handle) { double('Handle') }
339
+ let(:index) { double('Index') }
340
+ let(:enum_key_request_packet) { double('enum_key Request Packet') }
341
+ let(:lp_class) { double('Lp Class') }
342
+ let(:lp_name) { double('Lp Name') }
343
+ let(:buffer) { double('Buffer') }
344
+ let(:lp_class_buffer_referent) { double('Lp Class buffer referent') }
345
+ let(:lp_name_buffer_referent) { double('Lp Name buffer referent') }
346
+ let(:response) { double('Response') }
347
+ let(:enum_key_response) { double('enum_key Response') }
348
+ let(:result_str) { double('Result String') }
349
+ before :example do
350
+ allow(described_class::EnumKeyRequest).to receive(:new).and_return(enum_key_request_packet)
351
+ allow(enum_key_request_packet).to receive_messages(
352
+ :lpft_last_write_time= => nil,
353
+ :lp_class => lp_class,
354
+ :lp_name => lp_name
355
+ )
356
+ allow(lp_class).to receive(:referent).and_return(lp_class_buffer_referent)
357
+ allow(lp_name).to receive(:buffer).and_return(buffer)
358
+ allow(buffer).to receive(:referent).and_return(lp_name_buffer_referent)
359
+ allow(lp_class_buffer_referent).to receive(:buffer=)
360
+ allow(lp_name_buffer_referent).to receive(:max_count=)
361
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
362
+ allow(described_class::EnumKeyResponse).to receive(:read).and_return(enum_key_response)
363
+ allow(enum_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
364
+ allow(enum_key_response).to receive_message_chain(:lp_name, :to_s => result_str)
365
+ end
366
+
367
+ it 'create the expected EnumKeyRequest packet' do
368
+ winreg.enum_key(handle, index)
369
+ expect(described_class::EnumKeyRequest).to have_received(:new).with(hkey: handle, dw_index: index)
370
+ end
371
+
372
+ it 'sets the expected user rights on the request packet' do
373
+ winreg.enum_key(handle, index)
374
+ expect(enum_key_request_packet).to have_received(:lpft_last_write_time=).with(0)
375
+ expect(lp_class_buffer_referent).to have_received(:buffer=).with(0)
376
+ expect(lp_name_buffer_referent).to have_received(:max_count=).with(256)
377
+ end
378
+
379
+ it 'sends the expected dcerpc request' do
380
+ winreg.enum_key(handle, index)
381
+ expect(winreg).to have_received(:dcerpc_request).with(enum_key_request_packet)
382
+ end
383
+
384
+ it 'creates a EnumKeyResponse structure from the expected dcerpc response' do
385
+ winreg.enum_key(handle, index)
386
+ expect(described_class::EnumKeyResponse).to have_received(:read).with(response)
387
+ end
388
+
389
+ context 'when an IOError occurs while parsing the response' do
390
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
391
+ allow(described_class::EnumKeyResponse).to receive(:read).and_raise(IOError)
392
+ expect { winreg.enum_key(handle, index) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
393
+ end
394
+ end
395
+
396
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
397
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
398
+ allow(enum_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
399
+ expect { winreg.enum_key(handle, index) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
400
+ end
401
+ end
402
+
403
+ it 'returns the expected key name' do
404
+ expect(winreg.enum_key(handle, index)).to eq(result_str)
405
+ end
406
+ end
407
+
408
+ describe '#enum_value' do
409
+ let(:handle) { double('Handle') }
410
+ let(:index) { double('Index') }
411
+ let(:enum_value_request_packet) { double('EnumValue Request Packet') }
412
+ let(:lp_value_name) { double('Lp Value Name') }
413
+ let(:lp_data) { double('Lp Data') }
414
+ let(:buffer) { double('Buffer') }
415
+ let(:referent) { double('Referent') }
416
+ let(:response) { double('Response') }
417
+ let(:enum_value_response) { double('EnumValue Response') }
418
+ let(:result_str) { double('Result String') }
419
+ before :example do
420
+ allow(described_class::EnumValueRequest).to receive(:new).and_return(enum_value_request_packet)
421
+ allow(enum_value_request_packet).to receive_messages(
422
+ :lp_value_name => lp_value_name,
423
+ :lp_data => lp_data
424
+ )
425
+ allow(lp_value_name).to receive(:buffer).and_return(buffer)
426
+ allow(buffer).to receive(:referent).and_return(referent)
427
+ allow(referent).to receive(:max_count=)
428
+ allow(lp_data).to receive(:referent_identifier=)
429
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
430
+ allow(described_class::EnumValueResponse).to receive(:read).and_return(enum_value_response)
431
+ allow(enum_value_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
432
+ allow(enum_value_response).to receive_message_chain(:lp_value_name, :to_s => result_str)
433
+ end
434
+
435
+ it 'create the expected EnumValueRequest packet' do
436
+ winreg.enum_value(handle, index)
437
+ expect(described_class::EnumValueRequest).to have_received(:new).with(hkey: handle, dw_index: index)
438
+ end
439
+
440
+ it 'sets the expected user rights on the request packet' do
441
+ winreg.enum_value(handle, index)
442
+ expect(referent).to have_received(:max_count=).with(256)
443
+ expect(lp_data).to have_received(:referent_identifier=).with(0)
444
+ end
445
+
446
+ it 'sends the expected dcerpc request' do
447
+ winreg.enum_value(handle, index)
448
+ expect(winreg).to have_received(:dcerpc_request).with(enum_value_request_packet)
449
+ end
450
+
451
+ it 'creates a EnumValueResponse structure from the expected dcerpc response' do
452
+ winreg.enum_value(handle, index)
453
+ expect(described_class::EnumValueResponse).to have_received(:read).with(response)
454
+ end
455
+
456
+ context 'when an IOError occurs while parsing the response' do
457
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
458
+ allow(described_class::EnumValueResponse).to receive(:read).and_raise(IOError)
459
+ expect { winreg.enum_value(handle, index) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
460
+ end
461
+ end
462
+
463
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
464
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
465
+ allow(enum_value_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
466
+ expect { winreg.enum_value(handle, index) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
467
+ end
468
+ end
469
+
470
+ it 'returns the expected key name' do
471
+ expect(winreg.enum_value(handle, index)).to eq(result_str)
472
+ end
473
+ end
474
+
475
+ describe '#has_registry_key?' do
476
+ let(:root_key) { 'HKLM' }
477
+ let(:sub_key) { 'my\\sub\\key\\path' }
478
+ let(:key) { "#{root_key}\\#{sub_key}" }
479
+ let(:root_key_handle) { double('Root Key Handle') }
480
+ let(:subkey_handle) { double('Subkey Handle') }
481
+ before :example do
482
+ allow(winreg).to receive_messages(
483
+ :bind => nil,
484
+ :open_root_key => root_key_handle,
485
+ :open_key => subkey_handle,
486
+ :close_key => nil
487
+ )
488
+ end
489
+
490
+ it 'binds a DCERPC connection to the expected remote endpoint' do
491
+ winreg.has_registry_key?(key)
492
+ expect(winreg).to have_received(:bind).with(endpoint: RubySMB::Dcerpc::Winreg)
493
+ end
494
+
495
+ it 'opens the expected root key' do
496
+ winreg.has_registry_key?(key)
497
+ expect(winreg).to have_received(:open_root_key).with(root_key)
498
+ end
499
+
500
+ it 'opens the expected registry key' do
501
+ winreg.has_registry_key?(key)
502
+ expect(winreg).to have_received(:open_key).with(root_key_handle, sub_key)
503
+ end
504
+
505
+ context 'when a WinregError occurs while opening the root key' do
506
+ it 'returns false' do
507
+ allow(winreg).to receive(:open_root_key).and_raise(RubySMB::Dcerpc::Error::WinregError)
508
+ expect(winreg.has_registry_key?(key)).to be false
509
+ end
510
+ end
511
+
512
+ context 'when a WinregError occurs while opening the registry key' do
513
+ it 'returns false' do
514
+ allow(winreg).to receive(:open_key).and_raise(RubySMB::Dcerpc::Error::WinregError)
515
+ expect(winreg.has_registry_key?(key)).to be false
516
+ end
517
+ end
518
+
519
+ it 'closes the key' do
520
+ winreg.has_registry_key?(key)
521
+ expect(winreg).to have_received(:close_key).with(subkey_handle)
522
+ end
523
+
524
+ it 'returns true when no error occurs' do
525
+ expect(winreg.has_registry_key?(key)).to be true
526
+ end
527
+ end
528
+
529
+ describe '#read_registry_key_value' do
530
+ let(:root_key) { 'HKLM' }
531
+ let(:sub_key) { 'my\\sub\\key\\path' }
532
+ let(:key) { "#{root_key}\\#{sub_key}" }
533
+ let(:value_name) { 'registry_value_name' }
534
+ let(:root_key_handle) { double('Root Key Handle') }
535
+ let(:subkey_handle) { double('Subkey Handle') }
536
+ let(:value) { double('Value') }
537
+ before :example do
538
+ allow(winreg).to receive_messages(
539
+ :bind => nil,
540
+ :open_root_key => root_key_handle,
541
+ :open_key => subkey_handle,
542
+ :query_value => value,
543
+ :close_key => nil
544
+ )
545
+ end
546
+
547
+ it 'binds a DCERPC connection to the expected remote endpoint' do
548
+ winreg.read_registry_key_value(key, value_name)
549
+ expect(winreg).to have_received(:bind).with(endpoint: RubySMB::Dcerpc::Winreg)
550
+ end
551
+
552
+ it 'opens the expected root key' do
553
+ winreg.read_registry_key_value(key, value_name)
554
+ expect(winreg).to have_received(:open_root_key).with(root_key)
555
+ end
556
+
557
+ it 'opens the expected registry key' do
558
+ winreg.read_registry_key_value(key, value_name)
559
+ expect(winreg).to have_received(:open_key).with(root_key_handle, sub_key)
560
+ end
561
+
562
+ it 'queries the expected registry key value' do
563
+ winreg.read_registry_key_value(key, value_name)
564
+ expect(winreg).to have_received(:query_value).with(subkey_handle, value_name)
565
+ end
566
+
567
+ it 'closes the key' do
568
+ winreg.read_registry_key_value(key, value_name)
569
+ expect(winreg).to have_received(:close_key).with(subkey_handle)
570
+ end
571
+
572
+ it 'returns expect registry key value' do
573
+ expect(winreg.read_registry_key_value(key, value_name)).to eq(value)
574
+ end
575
+ end
576
+
577
+ describe '#enum_registry_key' do
578
+ let(:root_key) { 'HKLM' }
579
+ let(:sub_key) { 'my\\sub\\key\\path' }
580
+ let(:key) { "#{root_key}\\#{sub_key}" }
581
+ let(:value_name) { 'registry_value_name' }
582
+ let(:root_key_handle) { double('Root Key Handle') }
583
+ let(:subkey_handle) { double('Subkey Handle') }
584
+ let(:query_info_key_response) { double('Query Info Key Response') }
585
+ let(:subkey_nb) { 2 }
586
+ before :example do
587
+ allow(winreg).to receive_messages(
588
+ :bind => nil,
589
+ :open_root_key => root_key_handle,
590
+ :open_key => subkey_handle,
591
+ :query_info_key => query_info_key_response,
592
+ :enum_key => nil,
593
+ :close_key => nil
594
+ )
595
+ allow(query_info_key_response).to receive(:lpc_sub_keys).and_return(subkey_nb)
596
+ end
597
+
598
+ it 'binds a DCERPC connection to the expected remote endpoint' do
599
+ winreg.enum_registry_key(key)
600
+ expect(winreg).to have_received(:bind).with(endpoint: RubySMB::Dcerpc::Winreg)
601
+ end
602
+
603
+ it 'opens the expected root key' do
604
+ winreg.enum_registry_key(key)
605
+ expect(winreg).to have_received(:open_root_key).with(root_key)
606
+ end
607
+
608
+ context 'when the registry key only contains the root key' do
609
+ it 'queries information for the root key' do
610
+ winreg.enum_registry_key(root_key)
611
+ expect(winreg).to have_received(:query_info_key).with(root_key_handle)
612
+ end
613
+ end
614
+
615
+ it 'opens the expected registry key' do
616
+ winreg.enum_registry_key(key)
617
+ expect(winreg).to have_received(:open_key).with(root_key_handle, sub_key)
618
+ end
619
+
620
+ it 'queries information for the expected registry key' do
621
+ winreg.enum_registry_key(key)
622
+ expect(winreg).to have_received(:query_info_key).with(subkey_handle)
623
+ end
624
+
625
+ it 'calls #enum_key the expected number of times' do
626
+ winreg.enum_registry_key(key)
627
+ expect(winreg).to have_received(:enum_key).with(subkey_handle, instance_of(Fixnum)).twice
628
+ end
629
+
630
+ it 'closes the key' do
631
+ winreg.enum_registry_key(key)
632
+ expect(winreg).to have_received(:close_key).with(subkey_handle)
633
+ end
634
+
635
+ it 'returns the expected array of enumerated keys' do
636
+ key1 = 'key1'
637
+ key2 = 'key2'
638
+ allow(winreg).to receive(:enum_key).with(subkey_handle, 0).and_return(key1)
639
+ allow(winreg).to receive(:enum_key).with(subkey_handle, 1).and_return(key2)
640
+ expect(winreg.enum_registry_key(key)).to eq([key1, key2])
641
+ end
642
+ end
643
+
644
+ describe '#enum_registry_values' do
645
+ let(:root_key) { 'HKLM' }
646
+ let(:sub_key) { 'my\\sub\\key\\path' }
647
+ let(:key) { "#{root_key}\\#{sub_key}" }
648
+ let(:value_name) { 'registry_value_name' }
649
+ let(:root_key_handle) { double('Root Key Handle') }
650
+ let(:subkey_handle) { double('Subkey Handle') }
651
+ let(:query_info_key_response) { double('Query Info Key Response') }
652
+ let(:subkey_nb) { 2 }
653
+ before :example do
654
+ allow(winreg).to receive_messages(
655
+ :bind => nil,
656
+ :open_root_key => root_key_handle,
657
+ :open_key => subkey_handle,
658
+ :query_info_key => query_info_key_response,
659
+ :enum_value => nil,
660
+ :close_key => nil
661
+ )
662
+ allow(query_info_key_response).to receive(:lpc_values).and_return(subkey_nb)
663
+ end
664
+
665
+ it 'binds a DCERPC connection to the expected remote endpoint' do
666
+ winreg.enum_registry_values(key)
667
+ expect(winreg).to have_received(:bind).with(endpoint: RubySMB::Dcerpc::Winreg)
668
+ end
669
+
670
+ it 'opens the expected root key' do
671
+ winreg.enum_registry_values(key)
672
+ expect(winreg).to have_received(:open_root_key).with(root_key)
673
+ end
674
+
675
+ context 'when the registry key only contains the root key' do
676
+ it 'queries information for the root key' do
677
+ winreg.enum_registry_values(root_key)
678
+ expect(winreg).to have_received(:query_info_key).with(root_key_handle)
679
+ end
680
+ end
681
+
682
+ it 'opens the expected registry key' do
683
+ winreg.enum_registry_values(key)
684
+ expect(winreg).to have_received(:open_key).with(root_key_handle, sub_key)
685
+ end
686
+
687
+ it 'queries information for the expected registry key' do
688
+ winreg.enum_registry_values(key)
689
+ expect(winreg).to have_received(:query_info_key).with(subkey_handle)
690
+ end
691
+
692
+ it 'calls #enum_key the expected number of times' do
693
+ winreg.enum_registry_values(key)
694
+ expect(winreg).to have_received(:enum_value).with(subkey_handle, instance_of(Fixnum)).twice
695
+ end
696
+
697
+ it 'closes the key' do
698
+ winreg.enum_registry_values(key)
699
+ expect(winreg).to have_received(:close_key).with(subkey_handle)
700
+ end
701
+
702
+ it 'returns the expected array of enumerated keys' do
703
+ value1 = 'value1'
704
+ value2 = 'value2'
705
+ allow(winreg).to receive(:enum_value).with(subkey_handle, 0).and_return(value1)
706
+ allow(winreg).to receive(:enum_value).with(subkey_handle, 1).and_return(value2)
707
+ expect(winreg.enum_registry_values(key)).to eq([value1, value2])
708
+ end
709
+ end
710
+ end