ruby_smb 1.0.5 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.travis.yml +3 -2
  5. data/Gemfile +6 -2
  6. data/README.md +35 -47
  7. data/examples/anonymous_auth.rb +3 -3
  8. data/examples/append_file.rb +10 -8
  9. data/examples/authenticate.rb +9 -5
  10. data/examples/delete_file.rb +8 -6
  11. data/examples/enum_registry_key.rb +29 -0
  12. data/examples/enum_registry_values.rb +31 -0
  13. data/examples/list_directory.rb +8 -6
  14. data/examples/negotiate.rb +51 -8
  15. data/examples/negotiate_with_netbios_service.rb +9 -5
  16. data/examples/net_share_enum_all.rb +6 -4
  17. data/examples/pipes.rb +13 -13
  18. data/examples/query_service_status.rb +64 -0
  19. data/examples/read_file.rb +8 -6
  20. data/examples/read_file_encryption.rb +56 -0
  21. data/examples/read_registry_key_value.rb +33 -0
  22. data/examples/rename_file.rb +9 -7
  23. data/examples/tree_connect.rb +7 -5
  24. data/examples/write_file.rb +9 -7
  25. data/lib/ruby_smb.rb +4 -1
  26. data/lib/ruby_smb/client.rb +239 -21
  27. data/lib/ruby_smb/client/authentication.rb +27 -8
  28. data/lib/ruby_smb/client/encryption.rb +62 -0
  29. data/lib/ruby_smb/client/negotiation.rb +154 -12
  30. data/lib/ruby_smb/client/signing.rb +19 -0
  31. data/lib/ruby_smb/client/tree_connect.rb +4 -4
  32. data/lib/ruby_smb/client/utils.rb +8 -7
  33. data/lib/ruby_smb/client/winreg.rb +46 -0
  34. data/lib/ruby_smb/crypto.rb +30 -0
  35. data/lib/ruby_smb/dcerpc.rb +40 -0
  36. data/lib/ruby_smb/dcerpc/bind.rb +2 -2
  37. data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
  38. data/lib/ruby_smb/dcerpc/error.rb +6 -0
  39. data/lib/ruby_smb/dcerpc/ndr.rb +260 -16
  40. data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
  41. data/lib/ruby_smb/dcerpc/request.rb +41 -9
  42. data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +34 -0
  43. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +38 -0
  44. data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
  45. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
  46. data/lib/ruby_smb/dcerpc/svcctl.rb +479 -0
  47. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +48 -0
  48. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +26 -0
  49. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request.rb +25 -0
  50. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +26 -0
  51. data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +26 -0
  52. data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +26 -0
  53. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +35 -0
  54. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +23 -0
  55. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +31 -0
  56. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +23 -0
  57. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +25 -0
  58. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +44 -0
  59. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_request.rb +23 -0
  60. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +27 -0
  61. data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +25 -0
  62. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +27 -0
  63. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +25 -0
  64. data/lib/ruby_smb/dcerpc/winreg.rb +421 -0
  65. data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
  66. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
  67. data/lib/ruby_smb/dcerpc/winreg/create_key_request.rb +73 -0
  68. data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +36 -0
  69. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
  70. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
  71. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
  72. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
  73. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
  74. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
  75. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
  76. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
  77. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
  78. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
  79. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +40 -0
  80. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
  81. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
  82. data/lib/ruby_smb/dcerpc/winreg/save_key_request.rb +37 -0
  83. data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +23 -0
  84. data/lib/ruby_smb/dispatcher/base.rb +1 -1
  85. data/lib/ruby_smb/dispatcher/socket.rb +5 -4
  86. data/lib/ruby_smb/error.rb +28 -1
  87. data/lib/ruby_smb/field/stringz16.rb +17 -1
  88. data/lib/ruby_smb/nbss/session_header.rb +4 -4
  89. data/lib/ruby_smb/smb1/commands.rb +1 -1
  90. data/lib/ruby_smb/smb1/file.rb +8 -14
  91. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +1 -1
  92. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -2
  93. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
  94. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -2
  95. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +1 -1
  96. data/lib/ruby_smb/smb1/pipe.rb +81 -3
  97. data/lib/ruby_smb/smb1/tree.rb +12 -3
  98. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
  99. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
  100. data/lib/ruby_smb/smb2/file.rb +51 -61
  101. data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
  102. data/lib/ruby_smb/smb2/packet.rb +2 -0
  103. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
  104. data/lib/ruby_smb/smb2/packet/error_packet.rb +2 -4
  105. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -14
  106. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +50 -4
  107. data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
  108. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +92 -6
  109. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -26
  110. data/lib/ruby_smb/smb2/pipe.rb +80 -3
  111. data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
  112. data/lib/ruby_smb/smb2/tree.rb +32 -20
  113. data/lib/ruby_smb/version.rb +1 -1
  114. data/ruby_smb.gemspec +5 -3
  115. data/spec/lib/ruby_smb/client_spec.rb +1583 -102
  116. data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
  117. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
  118. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
  119. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +1729 -0
  120. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
  121. data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +161 -0
  122. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +135 -0
  123. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
  124. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
  125. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +191 -0
  126. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +38 -0
  127. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request_spec.rb +30 -0
  128. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +38 -0
  129. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +39 -0
  130. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +38 -0
  131. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +78 -0
  132. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +38 -0
  133. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +59 -0
  134. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +38 -0
  135. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +38 -0
  136. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +152 -0
  137. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_request_spec.rb +30 -0
  138. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +38 -0
  139. data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +72 -0
  140. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +46 -0
  141. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +30 -0
  142. data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +512 -0
  143. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
  144. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
  145. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +110 -0
  146. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +44 -0
  147. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +104 -0
  148. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
  149. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
  150. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
  151. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
  152. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
  153. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +95 -0
  154. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
  155. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +35 -0
  156. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
  157. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
  158. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +138 -0
  159. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
  160. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +57 -0
  161. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +22 -0
  162. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +884 -0
  163. data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
  164. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +12 -12
  165. data/spec/lib/ruby_smb/error_spec.rb +59 -0
  166. data/spec/lib/ruby_smb/field/stringz16_spec.rb +12 -0
  167. data/spec/lib/ruby_smb/nbss/session_header_spec.rb +4 -11
  168. data/spec/lib/ruby_smb/smb1/file_spec.rb +9 -1
  169. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
  170. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
  171. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
  172. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
  173. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +216 -147
  174. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
  175. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
  176. data/spec/lib/ruby_smb/smb2/file_spec.rb +146 -68
  177. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
  178. data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
  179. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +3 -24
  180. data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
  181. data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
  182. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
  183. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
  184. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -30
  185. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +226 -148
  186. data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
  187. data/spec/lib/ruby_smb/smb2/tree_spec.rb +88 -9
  188. metadata +257 -81
  189. metadata.gz.sig +0 -0
  190. data/lib/ruby_smb/smb1/dcerpc.rb +0 -72
  191. data/lib/ruby_smb/smb2/dcerpc.rb +0 -75
@@ -0,0 +1,88 @@
1
+ RSpec.describe RubySMB::Dcerpc::Winreg::RpcHkey do
2
+ it 'is NdrContextHandle subclass' do
3
+ expect(described_class).to be < RubySMB::Dcerpc::Ndr::NdrContextHandle
4
+ end
5
+ end
6
+
7
+ RSpec.describe RubySMB::Dcerpc::Winreg::QueryValueRequest do
8
+ subject(:packet) { described_class.new }
9
+
10
+ it { is_expected.to respond_to :hkey }
11
+ it { is_expected.to respond_to :lp_value_name }
12
+ it { is_expected.to respond_to :pad1 }
13
+ it { is_expected.to respond_to :lp_type }
14
+ it { is_expected.to respond_to :lp_data }
15
+ it { is_expected.to respond_to :pad2 }
16
+ it { is_expected.to respond_to :lpcb_data }
17
+ it { is_expected.to respond_to :lpcb_len }
18
+ it { is_expected.to respond_to :opnum }
19
+
20
+ it 'is little endian' do
21
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
22
+ end
23
+
24
+ describe '#hkey' do
25
+ it 'is a RpcHkey structure' do
26
+ expect(packet.hkey).to be_a RubySMB::Dcerpc::Winreg::RpcHkey
27
+ end
28
+ end
29
+
30
+ describe '#lp_value_name' do
31
+ it 'is a RrpUnicodeString structure' do
32
+ expect(packet.lp_value_name).to be_a RubySMB::Dcerpc::RrpUnicodeString
33
+ end
34
+ end
35
+
36
+ describe '#pad1' do
37
+ it 'is a string' do
38
+ expect(packet.pad1).to be_a BinData::String
39
+ end
40
+
41
+ it 'should keep #lp_type 4-byte aligned' do
42
+ packet.lp_value_name = 'test'
43
+ expect(packet.lp_type.abs_offset % 4).to eq 0
44
+ end
45
+ end
46
+
47
+ describe '#lp_type' do
48
+ it 'is a NdrLpDword structure' do
49
+ expect(packet.lp_type).to be_a RubySMB::Dcerpc::Ndr::NdrLpDword
50
+ end
51
+ end
52
+
53
+ describe '#lp_data' do
54
+ it 'is a NdrLpByteArray structure' do
55
+ expect(packet.lp_data).to be_a RubySMB::Dcerpc::Ndr::NdrLpByteArray
56
+ end
57
+ end
58
+
59
+ describe '#lpcb_data' do
60
+ it 'is a NdrLpDword structure' do
61
+ expect(packet.lpcb_data).to be_a RubySMB::Dcerpc::Ndr::NdrLpDword
62
+ end
63
+ end
64
+
65
+ describe '#pad2' do
66
+ it 'is a string' do
67
+ expect(packet.pad2).to be_a BinData::String
68
+ end
69
+
70
+ it 'should keep #lpcb_data 4-byte aligned' do
71
+ packet.lp_data = [1, 2]
72
+ expect(packet.lpcb_data.abs_offset % 4).to eq 0
73
+ end
74
+ end
75
+
76
+ describe '#lpcb_len' do
77
+ it 'is a NdrLpDword structure' do
78
+ expect(packet.lpcb_len).to be_a RubySMB::Dcerpc::Ndr::NdrLpDword
79
+ end
80
+ end
81
+
82
+ describe '#initialize_instance' do
83
+ it 'sets #opnum to REG_QUERY_VALUE constant' do
84
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Winreg::REG_QUERY_VALUE)
85
+ end
86
+ end
87
+ end
88
+
@@ -0,0 +1,138 @@
1
+ RSpec.describe RubySMB::Dcerpc::Winreg::QueryValueResponse do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :lp_type }
5
+ it { is_expected.to respond_to :lp_data }
6
+ it { is_expected.to respond_to :pad }
7
+ it { is_expected.to respond_to :lpcb_data }
8
+ it { is_expected.to respond_to :lpcb_len }
9
+ it { is_expected.to respond_to :error_status }
10
+ it { is_expected.to respond_to :opnum }
11
+
12
+ it 'is little endian' do
13
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
14
+ end
15
+
16
+ describe '#lp_type' do
17
+ it 'is a NdrLpDword structure' do
18
+ expect(packet.lp_type).to be_a RubySMB::Dcerpc::Ndr::NdrLpDword
19
+ end
20
+ end
21
+
22
+ describe '#lp_data' do
23
+ it 'is a NdrLpByteArray structure' do
24
+ expect(packet.lp_data).to be_a RubySMB::Dcerpc::Ndr::NdrLpByteArray
25
+ end
26
+ end
27
+
28
+ describe '#pad' do
29
+ it 'is a string' do
30
+ expect(packet.pad).to be_a BinData::String
31
+ end
32
+
33
+ it 'should keep #lpcb_data 4-byte aligned' do
34
+ packet.lp_data = 'spec_test'.bytes
35
+ expect(packet.lpcb_data.abs_offset % 4).to eq 0
36
+ end
37
+ end
38
+
39
+ describe '#lpcb_data' do
40
+ it 'is a NdrLpDword structure' do
41
+ expect(packet.lpcb_data).to be_a RubySMB::Dcerpc::Ndr::NdrLpDword
42
+ end
43
+ end
44
+
45
+ describe '#lpcb_len' do
46
+ it 'is a NdrLpDword structure' do
47
+ expect(packet.lpcb_len).to be_a RubySMB::Dcerpc::Ndr::NdrLpDword
48
+ end
49
+ end
50
+
51
+ describe '#error_status' do
52
+ it 'is a 32-bit unsigned integer' do
53
+ expect(packet.error_status).to be_a BinData::Uint32le
54
+ end
55
+ end
56
+
57
+ describe '#initialize_instance' do
58
+ it 'sets #opnum to REG_QUERY_VALUE constant' do
59
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Winreg::REG_QUERY_VALUE)
60
+ end
61
+ end
62
+
63
+ describe '#data' do
64
+ context 'when #lp_type is 1 (unicode null-terminated string)' do
65
+ it 'returns the expected value' do
66
+ str = 'spec test string'.encode('utf-16le')
67
+ packet.lp_type = 1
68
+ packet.lp_data = str.bytes
69
+ expect(packet.data).to eq(str)
70
+ end
71
+ end
72
+
73
+ context 'when #lp_type is 2 (unicode null-terminated string with unexpanded references to environment variables)' do
74
+ it 'returns the expected value' do
75
+ str = '/%PATH%/foo'.encode('utf-16le')
76
+ packet.lp_type = 2
77
+ packet.lp_data = str.bytes
78
+ expect(packet.data).to eq(str)
79
+ end
80
+ end
81
+
82
+ context 'when #lp_type is 3 (binary data)' do
83
+ it 'returns the expected value' do
84
+ bytes = [0xFF, 0xEE, 0xDD, 0xCC].pack('C*')
85
+ packet.lp_type = 3
86
+ packet.lp_data = bytes.bytes
87
+ expect(packet.data).to eq(bytes)
88
+ end
89
+ end
90
+
91
+ context 'when #lp_type is 4 (a 32-bit number in little-endian format)' do
92
+ it 'returns the expected value' do
93
+ number = 12345
94
+ packet.lp_type = 4
95
+ packet.lp_data = [number].pack('V').bytes
96
+ expect(packet.data).to eq(number)
97
+ end
98
+ end
99
+
100
+ context 'when #lp_type is 5 (a 32-bit number in big-endian format)' do
101
+ it 'returns the expected value' do
102
+ number = 12345
103
+ packet.lp_type = 5
104
+ packet.lp_data = [number].pack('N').bytes
105
+ expect(packet.data).to eq(number)
106
+ end
107
+ end
108
+
109
+ context 'when #lp_type is 7 (a sequence of unicode null-terminated strings, terminated by an empty string)' do
110
+ it 'returns the expected value' do
111
+ str_array = ['String1', 'String2', 'String3', 'LastString'].map {|v| v.encode('utf-16le')}
112
+ null_byte = "\0".encode('utf-16le')
113
+ str = (str_array + [null_byte]).join(null_byte)
114
+ packet.lp_type = 7
115
+ packet.lp_data = str.bytes
116
+ expect(packet.data).to eq(str_array)
117
+ end
118
+ end
119
+
120
+ context 'when #lp_type is 11 (a 64-bit number in little-endian format)' do
121
+ it 'returns the expected value' do
122
+ number = 0x1234567812345678
123
+ packet.lp_type = 11
124
+ packet.lp_data = [number].pack('Q<').bytes
125
+ expect(packet.data).to eq(number)
126
+ end
127
+ end
128
+
129
+ context 'when #lp_type is an unknown value' do
130
+ it 'returns an empty string' do
131
+ str = 'test'
132
+ packet.lp_type = 6
133
+ packet.lp_data = str.bytes
134
+ expect(packet.data).to eq('')
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,32 @@
1
+ RSpec.describe RubySMB::Dcerpc::Winreg::Regsam do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :reserved }
5
+ it { is_expected.to respond_to :key_create_link }
6
+ it { is_expected.to respond_to :key_notify }
7
+ it { is_expected.to respond_to :key_enumerate_sub_keys }
8
+ it { is_expected.to respond_to :key_create_sub_key }
9
+ it { is_expected.to respond_to :key_set_value }
10
+ it { is_expected.to respond_to :key_query_value }
11
+ it { is_expected.to respond_to :reserved2 }
12
+ it { is_expected.to respond_to :key_wow64_32key }
13
+ it { is_expected.to respond_to :key_wow64_64key }
14
+ it { is_expected.to respond_to :reserved3 }
15
+ it { is_expected.to respond_to :synchronize }
16
+ it { is_expected.to respond_to :write_owner }
17
+ it { is_expected.to respond_to :write_dac }
18
+ it { is_expected.to respond_to :read_control }
19
+ it { is_expected.to respond_to :delete_access }
20
+ it { is_expected.to respond_to :generic_read }
21
+ it { is_expected.to respond_to :generic_write }
22
+ it { is_expected.to respond_to :generic_execute }
23
+ it { is_expected.to respond_to :generic_all }
24
+ it { is_expected.to respond_to :reserved4 }
25
+ it { is_expected.to respond_to :maximum }
26
+ it { is_expected.to respond_to :system_security }
27
+
28
+
29
+ it 'is little endian' do
30
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
31
+ end
32
+ end
@@ -0,0 +1,57 @@
1
+ RSpec.describe RubySMB::Dcerpc::Winreg::RpcHkey do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it 'is NdrContextHandle subclass' do
5
+ expect(described_class).to be < RubySMB::Dcerpc::Ndr::NdrContextHandle
6
+ end
7
+ end
8
+
9
+ RSpec.describe RubySMB::Dcerpc::Winreg::SaveKeyRequest do
10
+ subject(:packet) { described_class.new }
11
+
12
+
13
+ it 'is little endian' do
14
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
15
+ end
16
+
17
+ it { is_expected.to respond_to :hkey }
18
+ it { is_expected.to respond_to :lp_file }
19
+ it { is_expected.to respond_to :pad }
20
+ it { is_expected.to respond_to :lp_security_attributes }
21
+
22
+ describe '#hkey' do
23
+ it 'is a RpcHkey structure' do
24
+ expect(packet.hkey).to be_a RubySMB::Dcerpc::Winreg::RpcHkey
25
+ end
26
+ end
27
+
28
+ describe '#lp_file' do
29
+ it 'is a RrpUnicodeString structure' do
30
+ expect(packet.lp_file).to be_a RubySMB::Dcerpc::RrpUnicodeString
31
+ end
32
+ end
33
+
34
+ describe '#pad' do
35
+ it 'is a string' do
36
+ expect(packet.pad).to be_a BinData::String
37
+ end
38
+
39
+ it 'should keep #lp_security_attributes 4-byte aligned' do
40
+ packet.lp_file = "test"
41
+ expect(packet.lp_security_attributes.abs_offset % 4).to eq 0
42
+ end
43
+ end
44
+
45
+ describe '#lp_security_attributes' do
46
+ it 'is a PrpcSecurityAttributes structure' do
47
+ expect(packet.lp_security_attributes).to be_a RubySMB::Dcerpc::PrpcSecurityAttributes
48
+ end
49
+ end
50
+
51
+ describe '#initialize_instance' do
52
+ it 'sets #opnum to REG_SAVE_KEY constant' do
53
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Winreg::REG_SAVE_KEY)
54
+ end
55
+ end
56
+ end
57
+
@@ -0,0 +1,22 @@
1
+ RSpec.describe RubySMB::Dcerpc::Winreg::SaveKeyResponse do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :error_status }
5
+
6
+ it 'is little endian' do
7
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
8
+ end
9
+
10
+ describe '#error_status' do
11
+ it 'is a 32-bit unsigned integer' do
12
+ expect(packet.error_status).to be_a BinData::Uint32le
13
+ end
14
+ end
15
+
16
+ describe '#initialize_instance' do
17
+ it 'sets #opnum to REG_CREATE_KEY constant' do
18
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Winreg::REG_CREATE_KEY)
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,884 @@
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_packet) { double('Query Value Request Packet #1') }
136
+ let(:lp_data1) { double('LpData #1') }
137
+ let(:lp_data2) { double('LpData #2') }
138
+ let(:response1) { double('Response #1') }
139
+ let(:response2) { double('Response #2') }
140
+ let(:query_value_response1) { double('Query Value Response #1') }
141
+ let(:query_value_response2) { double('Query Value Response #2') }
142
+ let(:data) { double('Data') }
143
+ let(:lpcb_data) { double('LpcbData') }
144
+ let(:lpcb_data_referent) { double('LpcbData Referent') }
145
+ let(:lp_data2_referent) { double('LpData Referent') }
146
+ before :example do
147
+ allow(described_class::QueryValueRequest).to receive(:new).and_return(query_value_request_packet)
148
+ allow(query_value_request_packet).to receive_messages(
149
+ :lp_type= => nil,
150
+ :lpcb_data= => nil,
151
+ :lpcb_len= => nil,
152
+ :lp_data= => nil,
153
+ :lp_data => lp_data2,
154
+ )
155
+ allow(lp_data2).to receive(:referent).and_return(lp_data2_referent)
156
+ allow(lp_data2_referent).to receive(:max_count=)
157
+ first_request = true
158
+ allow(winreg).to receive(:dcerpc_request) do |arg|
159
+ if first_request
160
+ first_request = false
161
+ response1
162
+ else
163
+ response2
164
+ end
165
+ end
166
+ allow(described_class::QueryValueResponse).to receive(:read).with(response1).and_return(query_value_response1)
167
+ allow(described_class::QueryValueResponse).to receive(:read).with(response2).and_return(query_value_response2)
168
+ allow(query_value_response1).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
169
+ allow(query_value_response2).to receive_messages(
170
+ :error_status => WindowsError::Win32::ERROR_SUCCESS,
171
+ :data => data
172
+ )
173
+ allow(query_value_response1).to receive(:lpcb_data).and_return(lpcb_data)
174
+ allow(lpcb_data).to receive(:referent).and_return(lpcb_data_referent)
175
+ end
176
+
177
+ it 'create the expected QueryValueRequest packets' do
178
+ winreg.query_value(handle, value_name)
179
+ expect(described_class::QueryValueRequest).to have_received(:new).with(hkey: handle, lp_value_name: value_name)
180
+ end
181
+
182
+ it 'sets the expected fields on the request packet' do
183
+ winreg.query_value(handle, value_name)
184
+ expect(query_value_request_packet).to have_received(:lp_type=).with(0)
185
+ expect(query_value_request_packet).to have_received(:lpcb_data=).with(0)
186
+ expect(query_value_request_packet).to have_received(:lpcb_len=).with(0)
187
+ expect(query_value_request_packet).to have_received(:lpcb_data=).with(lpcb_data)
188
+ expect(query_value_request_packet).to have_received(:lp_data=).with([])
189
+ expect(lp_data2_referent).to have_received(:max_count=).with(lpcb_data_referent)
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_packet).twice
195
+ end
196
+
197
+ context 'when receiving the first response' do
198
+ it 'creates a QueryValueResponse structure from the expected dcerpc response' do
199
+ winreg.query_value(handle, value_name)
200
+ expect(described_class::QueryValueResponse).to have_received(:read).with(response1)
201
+ end
202
+
203
+ context 'when an IOError occurs while parsing the response' do
204
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
205
+ allow(described_class::QueryValueResponse).to receive(:read).with(response1).and_raise(IOError)
206
+ expect { winreg.query_value(handle, value_name) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
207
+ end
208
+ end
209
+
210
+ context 'when the first response error status is not WindowsError::Win32::ERROR_SUCCESS' do
211
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
212
+ allow(query_value_response1).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
213
+ expect { winreg.query_value(handle, value_name) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
214
+ end
215
+ end
216
+ end
217
+
218
+ context 'when receiving the second response' do
219
+ it 'creates a QueryValueResponse structure from the expected dcerpc response' do
220
+ winreg.query_value(handle, value_name)
221
+ expect(described_class::QueryValueResponse).to have_received(:read).with(response2)
222
+ end
223
+
224
+ context 'when an IOError occurs while parsing the response' do
225
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
226
+ allow(described_class::QueryValueResponse).to receive(:read).with(response2).and_raise(IOError)
227
+ expect { winreg.query_value(handle, value_name) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
228
+ end
229
+ end
230
+
231
+ context 'when the first response error status is not WindowsError::Win32::ERROR_SUCCESS' do
232
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
233
+ allow(query_value_response2).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
234
+ expect { winreg.query_value(handle, value_name) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
235
+ end
236
+ end
237
+ end
238
+
239
+ it 'returns the expected response data' do
240
+ expect(winreg.query_value(handle, value_name)).to eq(data)
241
+ end
242
+ end
243
+
244
+ describe '#close_key' do
245
+ let(:handle) { double('Handle') }
246
+ let(:close_key_request_packet) { double('CloseKey Request Packet') }
247
+ let(:response) { double('Response') }
248
+ let(:close_key_response) { double('CloseKey Response') }
249
+ before :example do
250
+ allow(described_class::CloseKeyRequest).to receive(:new).and_return(close_key_request_packet)
251
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
252
+ allow(described_class::CloseKeyResponse).to receive(:read).and_return(close_key_response)
253
+ allow(close_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
254
+ end
255
+
256
+ it 'create the expected CloseKeyRequest packet' do
257
+ winreg.close_key(handle)
258
+ expect(described_class::CloseKeyRequest).to have_received(:new).with(hkey: handle)
259
+ end
260
+
261
+ it 'sends the expected dcerpc request' do
262
+ winreg.close_key(handle)
263
+ expect(winreg).to have_received(:dcerpc_request).with(close_key_request_packet)
264
+ end
265
+
266
+ it 'creates a CloseKeyResponse structure from the expected dcerpc response' do
267
+ winreg.close_key(handle)
268
+ expect(described_class::CloseKeyResponse).to have_received(:read).with(response)
269
+ end
270
+
271
+ context 'when an IOError occurs while parsing the response' do
272
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
273
+ allow(described_class::CloseKeyResponse).to receive(:read).and_raise(IOError)
274
+ expect { winreg.close_key(handle) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
275
+ end
276
+ end
277
+
278
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
279
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
280
+ allow(close_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
281
+ expect { winreg.close_key(handle) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
282
+ end
283
+ end
284
+
285
+ it 'returns the expected error status' do
286
+ expect(winreg.close_key(handle)).to eq(WindowsError::Win32::ERROR_SUCCESS)
287
+ end
288
+ end
289
+
290
+ describe '#query_info_key' do
291
+ let(:handle) { double('Handle') }
292
+ let(:query_info_key_request_packet) { double('CloseKey Request Packet') }
293
+ let(:response) { double('Response') }
294
+ let(:query_info_key_response) { double('CloseKey Response') }
295
+ let(:lp_class) { double('LpClass') }
296
+ let(:lp_class_referent) { double('LpClass referent') }
297
+ let(:lp_class_buf_ref) { double('LpClass buffer referent') }
298
+ before :example do
299
+ allow(described_class::QueryInfoKeyRequest).to receive(:new).and_return(query_info_key_request_packet)
300
+ allow(query_info_key_request_packet).to receive_messages(
301
+ :lp_class= => nil,
302
+ :lp_class => lp_class,
303
+ )
304
+ allow(lp_class).to receive(:referent).and_return(lp_class_referent)
305
+ allow(lp_class_referent).to receive(:actual_count=)
306
+ allow(lp_class).to receive(:maximum_length=)
307
+ allow(lp_class).to receive_message_chain(:buffer, :referent => lp_class_buf_ref)
308
+ allow(lp_class_buf_ref).to receive(:max_count=)
309
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
310
+ allow(described_class::QueryInfoKeyResponse).to receive(:read).and_return(query_info_key_response)
311
+ allow(query_info_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
312
+ end
313
+
314
+ it 'create the expected QueryInfoKeyRequest packet' do
315
+ winreg.query_info_key(handle)
316
+ expect(described_class::QueryInfoKeyRequest).to have_received(:new).with(hkey: handle)
317
+ end
318
+
319
+ it 'sends the expected dcerpc request' do
320
+ winreg.query_info_key(handle)
321
+ expect(winreg).to have_received(:dcerpc_request).with(query_info_key_request_packet)
322
+ end
323
+
324
+ it 'sets the expected fields on the request packet' do
325
+ winreg.query_info_key(handle)
326
+ expect(query_info_key_request_packet).to have_received(:lp_class=).with('')
327
+ expect(lp_class_referent).to have_received(:actual_count=).with(0)
328
+ expect(lp_class).to have_received(:maximum_length=).with(1024)
329
+ expect(lp_class_buf_ref).to have_received(:max_count=).with(1024 / 2)
330
+ end
331
+
332
+ it 'creates a QueryInfoKeyResponse structure from the expected dcerpc response' do
333
+ winreg.query_info_key(handle)
334
+ expect(described_class::QueryInfoKeyResponse).to have_received(:read).with(response)
335
+ end
336
+
337
+ context 'when an IOError occurs while parsing the response' do
338
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
339
+ allow(described_class::QueryInfoKeyResponse).to receive(:read).and_raise(IOError)
340
+ expect { winreg.query_info_key(handle) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
341
+ end
342
+ end
343
+
344
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
345
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
346
+ allow(query_info_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
347
+ expect { winreg.query_info_key(handle) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
348
+ end
349
+ end
350
+
351
+ it 'returns the expected response' do
352
+ expect(winreg.query_info_key(handle)).to eq(query_info_key_response)
353
+ end
354
+ end
355
+
356
+ describe '#enum_key' do
357
+ let(:handle) { double('Handle') }
358
+ let(:index) { double('Index') }
359
+ let(:enum_key_request_packet) { double('enum_key Request Packet') }
360
+ let(:lp_name) { double('Lp Name') }
361
+ let(:buffer) { double('Buffer') }
362
+ let(:lp_name_buffer_referent) { double('Lp Name buffer referent') }
363
+ let(:response) { double('Response') }
364
+ let(:enum_key_response) { double('enum_key Response') }
365
+ let(:result_str) { double('Result String') }
366
+ let(:lp_class) { double('Lp Class') }
367
+ let(:lp_class_buffer_referent) { double('Lp Class buffer referent') }
368
+ before :example do
369
+ allow(described_class::EnumKeyRequest).to receive(:new).and_return(enum_key_request_packet)
370
+ allow(enum_key_request_packet).to receive_messages(
371
+ :lpft_last_write_time= => nil,
372
+ :lp_class= => nil,
373
+ :lp_name => lp_name,
374
+ :lp_class => lp_class
375
+ )
376
+ allow(lp_class).to receive(:referent).and_return(lp_class_buffer_referent)
377
+ allow(lp_class_buffer_referent).to receive(:buffer=)
378
+ allow(lp_name).to receive(:buffer).and_return(buffer)
379
+ allow(lp_name).to receive(:buffer=)
380
+ allow(buffer).to receive(:referent).and_return(lp_name_buffer_referent)
381
+ allow(lp_name_buffer_referent).to receive(:max_count=)
382
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
383
+ allow(described_class::EnumKeyResponse).to receive(:read).and_return(enum_key_response)
384
+ allow(enum_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
385
+ allow(enum_key_response).to receive_message_chain(:lp_name, :to_s => result_str)
386
+ end
387
+
388
+ it 'create the expected EnumKeyRequest packet' do
389
+ winreg.enum_key(handle, index)
390
+ expect(described_class::EnumKeyRequest).to have_received(:new).with(hkey: handle, dw_index: index)
391
+ end
392
+
393
+ it 'sets the expected parameters on the request packet' do
394
+ winreg.enum_key(handle, index)
395
+ expect(enum_key_request_packet).to have_received(:lpft_last_write_time=).with(0)
396
+ expect(enum_key_request_packet).to have_received(:lp_class=).with('')
397
+ expect(lp_class_buffer_referent).to have_received(:buffer=).with(:null)
398
+ expect(lp_name).to have_received(:buffer=).with('')
399
+ expect(lp_name_buffer_referent).to have_received(:max_count=).with(256)
400
+ end
401
+
402
+ it 'sends the expected dcerpc request' do
403
+ winreg.enum_key(handle, index)
404
+ expect(winreg).to have_received(:dcerpc_request).with(enum_key_request_packet)
405
+ end
406
+
407
+ it 'creates a EnumKeyResponse structure from the expected dcerpc response' do
408
+ winreg.enum_key(handle, index)
409
+ expect(described_class::EnumKeyResponse).to have_received(:read).with(response)
410
+ end
411
+
412
+ context 'when an IOError occurs while parsing the response' do
413
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
414
+ allow(described_class::EnumKeyResponse).to receive(:read).and_raise(IOError)
415
+ expect { winreg.enum_key(handle, index) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
416
+ end
417
+ end
418
+
419
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
420
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
421
+ allow(enum_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
422
+ expect { winreg.enum_key(handle, index) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
423
+ end
424
+ end
425
+
426
+ it 'returns the expected key name' do
427
+ expect(winreg.enum_key(handle, index)).to eq(result_str)
428
+ end
429
+ end
430
+
431
+ describe '#enum_value' do
432
+ let(:handle) { double('Handle') }
433
+ let(:index) { double('Index') }
434
+ let(:enum_value_request_packet) { double('EnumValue Request Packet') }
435
+ let(:lp_value_name) { double('Lp Value Name') }
436
+ let(:buffer) { double('Buffer') }
437
+ let(:referent) { double('Referent') }
438
+ let(:response) { double('Response') }
439
+ let(:enum_value_response) { double('EnumValue Response') }
440
+ let(:result_str) { double('Result String') }
441
+ before :example do
442
+ allow(described_class::EnumValueRequest).to receive(:new).and_return(enum_value_request_packet)
443
+ allow(enum_value_request_packet).to receive(:lp_value_name).and_return(lp_value_name)
444
+ allow(lp_value_name).to receive(:buffer).and_return(buffer)
445
+ allow(lp_value_name).to receive(:buffer=)
446
+ allow(buffer).to receive(:referent).and_return(referent)
447
+ allow(referent).to receive(:max_count=)
448
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
449
+ allow(described_class::EnumValueResponse).to receive(:read).and_return(enum_value_response)
450
+ allow(enum_value_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
451
+ allow(enum_value_response).to receive_message_chain(:lp_value_name, :to_s => result_str)
452
+ end
453
+
454
+ it 'create the expected EnumValueRequest packet' do
455
+ winreg.enum_value(handle, index)
456
+ expect(described_class::EnumValueRequest).to have_received(:new).with(hkey: handle, dw_index: index)
457
+ end
458
+
459
+ it 'sets the expected buffer on the request packet' do
460
+ winreg.enum_value(handle, index)
461
+ expect(referent).to have_received(:max_count=).with(256)
462
+ expect(lp_value_name).to have_received(:buffer=).with('')
463
+ end
464
+
465
+ it 'sends the expected dcerpc request' do
466
+ winreg.enum_value(handle, index)
467
+ expect(winreg).to have_received(:dcerpc_request).with(enum_value_request_packet)
468
+ end
469
+
470
+ it 'creates a EnumValueResponse structure from the expected dcerpc response' do
471
+ winreg.enum_value(handle, index)
472
+ expect(described_class::EnumValueResponse).to have_received(:read).with(response)
473
+ end
474
+
475
+ context 'when an IOError occurs while parsing the response' do
476
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
477
+ allow(described_class::EnumValueResponse).to receive(:read).and_raise(IOError)
478
+ expect { winreg.enum_value(handle, index) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
479
+ end
480
+ end
481
+
482
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
483
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
484
+ allow(enum_value_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
485
+ expect { winreg.enum_value(handle, index) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
486
+ end
487
+ end
488
+
489
+ it 'returns the expected key name' do
490
+ expect(winreg.enum_value(handle, index)).to eq(result_str)
491
+ end
492
+ end
493
+
494
+ describe '#has_registry_key?' do
495
+ let(:root_key) { 'HKLM' }
496
+ let(:sub_key) { 'my\\sub\\key\\path' }
497
+ let(:key) { "#{root_key}\\#{sub_key}" }
498
+ let(:root_key_handle) { double('Root Key Handle') }
499
+ let(:subkey_handle) { double('Subkey Handle') }
500
+ before :example do
501
+ allow(winreg).to receive_messages(
502
+ :bind => nil,
503
+ :open_root_key => root_key_handle,
504
+ :open_key => subkey_handle,
505
+ :close_key => nil
506
+ )
507
+ end
508
+
509
+ it 'binds a DCERPC connection to the expected remote endpoint' do
510
+ winreg.has_registry_key?(key)
511
+ expect(winreg).to have_received(:bind).with(endpoint: RubySMB::Dcerpc::Winreg)
512
+ end
513
+
514
+ it 'does not bind a DCERPC connection if #bind argument is false' do
515
+ winreg.has_registry_key?(key, bind: false)
516
+ expect(winreg).to_not have_received(:bind)
517
+ end
518
+
519
+ it 'opens the expected root key' do
520
+ winreg.has_registry_key?(key)
521
+ expect(winreg).to have_received(:open_root_key).with(root_key)
522
+ end
523
+
524
+ it 'opens the expected registry key' do
525
+ winreg.has_registry_key?(key)
526
+ expect(winreg).to have_received(:open_key).with(root_key_handle, sub_key)
527
+ end
528
+
529
+ context 'when a WinregError occurs while opening the root key' do
530
+ it 'returns false' do
531
+ allow(winreg).to receive(:open_root_key).and_raise(RubySMB::Dcerpc::Error::WinregError)
532
+ expect(winreg.has_registry_key?(key)).to be false
533
+ end
534
+ end
535
+
536
+ context 'when a WinregError occurs while opening the registry key' do
537
+ it 'returns false' do
538
+ allow(winreg).to receive(:open_key).and_raise(RubySMB::Dcerpc::Error::WinregError)
539
+ expect(winreg.has_registry_key?(key)).to be false
540
+ end
541
+ end
542
+
543
+ it 'closes the key' do
544
+ winreg.has_registry_key?(key)
545
+ expect(winreg).to have_received(:close_key).with(subkey_handle)
546
+ expect(winreg).to have_received(:close_key).with(root_key_handle)
547
+ end
548
+
549
+ it 'returns true when no error occurs' do
550
+ expect(winreg.has_registry_key?(key)).to be true
551
+ end
552
+ end
553
+
554
+ describe '#read_registry_key_value' do
555
+ let(:root_key) { 'HKLM' }
556
+ let(:sub_key) { 'my\\sub\\key\\path' }
557
+ let(:key) { "#{root_key}\\#{sub_key}" }
558
+ let(:value_name) { 'registry_value_name' }
559
+ let(:root_key_handle) { double('Root Key Handle') }
560
+ let(:subkey_handle) { double('Subkey Handle') }
561
+ let(:value) { double('Value') }
562
+ before :example do
563
+ allow(winreg).to receive_messages(
564
+ :bind => nil,
565
+ :open_root_key => root_key_handle,
566
+ :open_key => subkey_handle,
567
+ :query_value => value,
568
+ :close_key => nil
569
+ )
570
+ end
571
+
572
+ it 'binds a DCERPC connection to the expected remote endpoint' do
573
+ winreg.read_registry_key_value(key, value_name)
574
+ expect(winreg).to have_received(:bind).with(endpoint: RubySMB::Dcerpc::Winreg)
575
+ end
576
+
577
+ it 'does not bind a DCERPC connection if #bind argument is false' do
578
+ winreg.read_registry_key_value(key, value_name, bind: false)
579
+ expect(winreg).to_not have_received(:bind)
580
+ end
581
+
582
+ it 'opens the expected root key' do
583
+ winreg.read_registry_key_value(key, value_name)
584
+ expect(winreg).to have_received(:open_root_key).with(root_key)
585
+ end
586
+
587
+ it 'opens the expected registry key' do
588
+ winreg.read_registry_key_value(key, value_name)
589
+ expect(winreg).to have_received(:open_key).with(root_key_handle, sub_key)
590
+ end
591
+
592
+ it 'queries the expected registry key value' do
593
+ winreg.read_registry_key_value(key, value_name)
594
+ expect(winreg).to have_received(:query_value).with(subkey_handle, value_name)
595
+ end
596
+
597
+ it 'closes the key' do
598
+ winreg.read_registry_key_value(key, value_name)
599
+ expect(winreg).to have_received(:close_key).with(subkey_handle)
600
+ expect(winreg).to have_received(:close_key).with(root_key_handle)
601
+ end
602
+
603
+ it 'returns expect registry key value' do
604
+ expect(winreg.read_registry_key_value(key, value_name)).to eq(value)
605
+ end
606
+ end
607
+
608
+ describe '#enum_registry_key' do
609
+ let(:root_key) { 'HKLM' }
610
+ let(:sub_key) { 'my\\sub\\key\\path' }
611
+ let(:key) { "#{root_key}\\#{sub_key}" }
612
+ let(:value_name) { 'registry_value_name' }
613
+ let(:root_key_handle) { double('Root Key Handle') }
614
+ let(:subkey_handle) { double('Subkey Handle') }
615
+ let(:query_info_key_response) { double('Query Info Key Response') }
616
+ let(:subkey_nb) { 2 }
617
+ before :example do
618
+ allow(winreg).to receive_messages(
619
+ :bind => nil,
620
+ :open_root_key => root_key_handle,
621
+ :open_key => subkey_handle,
622
+ :query_info_key => query_info_key_response,
623
+ :enum_key => nil,
624
+ :close_key => nil
625
+ )
626
+ allow(query_info_key_response).to receive(:lpc_sub_keys).and_return(subkey_nb)
627
+ end
628
+
629
+ it 'binds a DCERPC connection to the expected remote endpoint' do
630
+ winreg.enum_registry_key(key)
631
+ expect(winreg).to have_received(:bind).with(endpoint: RubySMB::Dcerpc::Winreg)
632
+ end
633
+
634
+ it 'does not bind a DCERPC connection if #bind argument is false' do
635
+ winreg.enum_registry_key(key, bind: false)
636
+ expect(winreg).to_not have_received(:bind)
637
+ end
638
+
639
+ it 'opens the expected root key' do
640
+ winreg.enum_registry_key(key)
641
+ expect(winreg).to have_received(:open_root_key).with(root_key)
642
+ end
643
+
644
+ context 'when the registry key only contains the root key' do
645
+ it 'queries information for the root key' do
646
+ winreg.enum_registry_key(root_key)
647
+ expect(winreg).to have_received(:query_info_key).with(root_key_handle)
648
+ end
649
+ end
650
+
651
+ it 'opens the expected registry key' do
652
+ winreg.enum_registry_key(key)
653
+ expect(winreg).to have_received(:open_key).with(root_key_handle, sub_key)
654
+ end
655
+
656
+ it 'queries information for the expected registry key' do
657
+ winreg.enum_registry_key(key)
658
+ expect(winreg).to have_received(:query_info_key).with(subkey_handle)
659
+ end
660
+
661
+ it 'calls #enum_key the expected number of times' do
662
+ winreg.enum_registry_key(key)
663
+ expect(winreg).to have_received(:enum_key).with(subkey_handle, instance_of(Fixnum)).twice
664
+ end
665
+
666
+ it 'closes the key' do
667
+ winreg.enum_registry_key(key)
668
+ expect(winreg).to have_received(:close_key).with(subkey_handle)
669
+ expect(winreg).to have_received(:close_key).with(root_key_handle)
670
+ end
671
+
672
+ it 'returns the expected array of enumerated keys' do
673
+ key1 = 'key1'
674
+ key2 = 'key2'
675
+ allow(winreg).to receive(:enum_key).with(subkey_handle, 0).and_return(key1)
676
+ allow(winreg).to receive(:enum_key).with(subkey_handle, 1).and_return(key2)
677
+ expect(winreg.enum_registry_key(key)).to eq([key1, key2])
678
+ end
679
+ end
680
+
681
+ describe '#enum_registry_values' do
682
+ let(:root_key) { 'HKLM' }
683
+ let(:sub_key) { 'my\\sub\\key\\path' }
684
+ let(:key) { "#{root_key}\\#{sub_key}" }
685
+ let(:value_name) { 'registry_value_name' }
686
+ let(:root_key_handle) { double('Root Key Handle') }
687
+ let(:subkey_handle) { double('Subkey Handle') }
688
+ let(:query_info_key_response) { double('Query Info Key Response') }
689
+ let(:subkey_nb) { 2 }
690
+ before :example do
691
+ allow(winreg).to receive_messages(
692
+ :bind => nil,
693
+ :open_root_key => root_key_handle,
694
+ :open_key => subkey_handle,
695
+ :query_info_key => query_info_key_response,
696
+ :enum_value => nil,
697
+ :close_key => nil
698
+ )
699
+ allow(query_info_key_response).to receive(:lpc_values).and_return(subkey_nb)
700
+ end
701
+
702
+ it 'binds a DCERPC connection to the expected remote endpoint' do
703
+ winreg.enum_registry_values(key)
704
+ expect(winreg).to have_received(:bind).with(endpoint: RubySMB::Dcerpc::Winreg)
705
+ end
706
+
707
+ it 'does not bind a DCERPC connection if #bind argument is false' do
708
+ winreg.enum_registry_values(key, bind: false)
709
+ expect(winreg).to_not have_received(:bind)
710
+ end
711
+
712
+ it 'opens the expected root key' do
713
+ winreg.enum_registry_values(key)
714
+ expect(winreg).to have_received(:open_root_key).with(root_key)
715
+ end
716
+
717
+ context 'when the registry key only contains the root key' do
718
+ it 'queries information for the root key' do
719
+ winreg.enum_registry_values(root_key)
720
+ expect(winreg).to have_received(:query_info_key).with(root_key_handle)
721
+ end
722
+ end
723
+
724
+ it 'opens the expected registry key' do
725
+ winreg.enum_registry_values(key)
726
+ expect(winreg).to have_received(:open_key).with(root_key_handle, sub_key)
727
+ end
728
+
729
+ it 'queries information for the expected registry key' do
730
+ winreg.enum_registry_values(key)
731
+ expect(winreg).to have_received(:query_info_key).with(subkey_handle)
732
+ end
733
+
734
+ it 'calls #enum_key the expected number of times' do
735
+ winreg.enum_registry_values(key)
736
+ expect(winreg).to have_received(:enum_value).with(subkey_handle, instance_of(Fixnum)).twice
737
+ end
738
+
739
+ it 'closes the key' do
740
+ winreg.enum_registry_values(key)
741
+ expect(winreg).to have_received(:close_key).with(subkey_handle)
742
+ expect(winreg).to have_received(:close_key).with(root_key_handle)
743
+ end
744
+
745
+ it 'returns the expected array of enumerated keys' do
746
+ value1 = 'value1'
747
+ value2 = 'value2'
748
+ allow(winreg).to receive(:enum_value).with(subkey_handle, 0).and_return(value1)
749
+ allow(winreg).to receive(:enum_value).with(subkey_handle, 1).and_return(value2)
750
+ expect(winreg.enum_registry_values(key)).to eq([value1, value2])
751
+ end
752
+ end
753
+
754
+ describe '#create_key' do
755
+ let(:handle) { double('Handle') }
756
+ let(:sub_key) { double('Sub key') }
757
+ let(:create_key_request) { double('CreateKey Request') }
758
+ let(:response) { double('Response') }
759
+ let(:create_key_response) { double('CreateKey Response') }
760
+ let(:hkey) { double('hkey') }
761
+ before :example do
762
+ allow(described_class::CreateKeyRequest).to receive(:new).and_return(create_key_request)
763
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
764
+ allow(described_class::CreateKeyResponse).to receive(:read).and_return(create_key_response)
765
+ allow(create_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
766
+ allow(create_key_response).to receive(:hkey).and_return(hkey)
767
+ end
768
+
769
+ it 'create the expected CreateKeyRequest packet with the default options' do
770
+ opts = {
771
+ hkey: handle,
772
+ lp_sub_key: sub_key,
773
+ lp_class: :null,
774
+ dw_options: RubySMB::Dcerpc::Winreg::CreateKeyRequest::REG_KEY_TYPE_VOLATILE,
775
+ sam_desired: RubySMB::Dcerpc::Winreg::Regsam.new(maximum: 1),
776
+ lp_security_attributes: RubySMB::Dcerpc::RpcSecurityAttributes.new,
777
+ lpdw_disposition: RubySMB::Dcerpc::Winreg::CreateKeyRequest::REG_CREATED_NEW_KEY
778
+ }
779
+ winreg.create_key(handle, sub_key)
780
+ expect(described_class::CreateKeyRequest).to have_received(:new).with(opts)
781
+ end
782
+
783
+ it 'create the expected CreateKeyRequest packet with custom options' do
784
+ opts = {
785
+ hkey: handle,
786
+ lp_sub_key: sub_key,
787
+ lp_class: 'MyClass',
788
+ dw_options: RubySMB::Dcerpc::Winreg::CreateKeyRequest::REG_KEY_TYPE_SYMLINK,
789
+ sam_desired: RubySMB::Dcerpc::Winreg::Regsam.new(key_set_value: 1),
790
+ lp_security_attributes: RubySMB::Dcerpc::RpcSecurityAttributes.new(n_length: 3),
791
+ lpdw_disposition: RubySMB::Dcerpc::Winreg::CreateKeyRequest::REG_OPENED_EXISTING_KEY
792
+ }
793
+ winreg.create_key(handle, sub_key, opts)
794
+ expect(described_class::CreateKeyRequest).to have_received(:new).with(opts)
795
+ end
796
+
797
+ it 'sends the expected dcerpc request' do
798
+ winreg.create_key(handle, sub_key)
799
+ expect(winreg).to have_received(:dcerpc_request).with(create_key_request)
800
+ end
801
+
802
+ it 'creates a CreateKeyResponse structure from the expected dcerpc response' do
803
+ winreg.create_key(handle, sub_key)
804
+ expect(described_class::CreateKeyResponse).to have_received(:read).with(response)
805
+ end
806
+
807
+ context 'when an IOError occurs while parsing the response' do
808
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
809
+ allow(described_class::CreateKeyResponse).to receive(:read).and_raise(IOError)
810
+ expect { winreg.create_key(handle, sub_key) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
811
+ end
812
+ end
813
+
814
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
815
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
816
+ allow(create_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
817
+ expect { winreg.create_key(handle, sub_key) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
818
+ end
819
+ end
820
+
821
+ it 'returns the expected key name' do
822
+ expect(winreg.create_key(handle, sub_key)).to eq(hkey)
823
+ end
824
+ end
825
+
826
+ describe '#save_key' do
827
+ let(:handle) { double('Handle') }
828
+ let(:filename) { double('Filename') }
829
+ let(:save_key_request) { double('CreateKey Request') }
830
+ let(:response) { double('Response') }
831
+ let(:save_key_response) { double('CreateKey Response') }
832
+ let(:hkey) { double('hkey') }
833
+ before :example do
834
+ allow(described_class::SaveKeyRequest).to receive(:new).and_return(save_key_request)
835
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
836
+ allow(described_class::SaveKeyResponse).to receive(:read).and_return(save_key_response)
837
+ allow(save_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
838
+ end
839
+
840
+ it 'create the expected SaveKeyRequest packet with the default options' do
841
+ opts = {
842
+ hkey: handle,
843
+ lp_file: filename,
844
+ lp_security_attributes: :null,
845
+ }
846
+ winreg.save_key(handle, filename)
847
+ expect(described_class::SaveKeyRequest).to have_received(:new).with(opts)
848
+ end
849
+
850
+ it 'create the expected SaveKeyRequest packet with custom options' do
851
+ opts = {
852
+ hkey: handle,
853
+ lp_file: filename,
854
+ lp_security_attributes: RubySMB::Dcerpc::RpcSecurityAttributes.new,
855
+ }
856
+ winreg.save_key(handle, filename, opts)
857
+ expect(described_class::SaveKeyRequest).to have_received(:new).with(opts)
858
+ end
859
+
860
+ it 'sends the expected dcerpc request' do
861
+ winreg.save_key(handle, filename)
862
+ expect(winreg).to have_received(:dcerpc_request).with(save_key_request)
863
+ end
864
+
865
+ it 'creates a SaveKeyResponse structure from the expected dcerpc response' do
866
+ winreg.save_key(handle, filename)
867
+ expect(described_class::SaveKeyResponse).to have_received(:read).with(response)
868
+ end
869
+
870
+ context 'when an IOError occurs while parsing the response' do
871
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
872
+ allow(described_class::SaveKeyResponse).to receive(:read).and_raise(IOError)
873
+ expect { winreg.save_key(handle, filename) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
874
+ end
875
+ end
876
+
877
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
878
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
879
+ allow(save_key_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
880
+ expect { winreg.save_key(handle, filename) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
881
+ end
882
+ end
883
+ end
884
+ end