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,81 @@
1
+ RSpec.describe RubySMB::Dcerpc do
2
+ let(:tree) { double('Tree') }
3
+ let(:pipe) do
4
+ RubySMB::SMB1::Pipe.new(
5
+ tree: tree,
6
+ response: RubySMB::SMB1::Packet::NtCreateAndxResponse.new,
7
+ name: 'winreg'
8
+ )
9
+ end
10
+
11
+ describe '#bind' do
12
+ let(:options) { { endpoint: RubySMB::Dcerpc::Winreg } }
13
+ let(:bind_packet) { RubySMB::Dcerpc::Bind.new(options) }
14
+ let(:bind_ack_packet) { RubySMB::Dcerpc::BindAck.new }
15
+ let(:client) { double('Client') }
16
+
17
+ before :example do
18
+ allow(RubySMB::Dcerpc::Bind).to receive(:new).and_return(bind_packet)
19
+ allow(pipe).to receive(:write)
20
+ allow(pipe).to receive(:read)
21
+ bind_ack_packet.p_result_list.n_results = 1
22
+ bind_ack_packet.p_result_list.p_results[0].result = RubySMB::Dcerpc::BindAck::ACCEPTANCE
23
+ allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_return(bind_ack_packet)
24
+ allow(tree).to receive(:client).and_return(client)
25
+ allow(client).to receive(:max_buffer_size=)
26
+ end
27
+
28
+ it 'creates a Bind packet' do
29
+ pipe.bind(options)
30
+ expect(RubySMB::Dcerpc::Bind).to have_received(:new).with(options)
31
+ end
32
+
33
+ it 'writes to the named pipe' do
34
+ pipe.bind(options)
35
+ expect(pipe).to have_received(:write).with(data: bind_packet.to_binary_s)
36
+ end
37
+
38
+ it 'reads the socket' do
39
+ pipe.bind(options)
40
+ expect(pipe).to have_received(:read)
41
+ end
42
+
43
+ it 'creates a BindAck packet from the response' do
44
+ raw_response = RubySMB::Dcerpc::BindAck.new.to_binary_s
45
+ allow(pipe).to receive(:read).and_return(raw_response)
46
+ pipe.bind(options)
47
+ expect(RubySMB::Dcerpc::BindAck).to have_received(:read).with(raw_response)
48
+ end
49
+
50
+ it 'raises the expected exception when an invalid packet is received' do
51
+ allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_raise(IOError)
52
+ expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
53
+ end
54
+
55
+ it 'raises the expected exception when it is not a BindAck packet' do
56
+ response = RubySMB::Dcerpc::Bind.new
57
+ allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_return(response)
58
+ expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
59
+ end
60
+
61
+ it 'raises an exception when no result is returned' do
62
+ bind_ack_packet.p_result_list.n_results = 0
63
+ expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
64
+ end
65
+
66
+ it 'raises an exception when result is not ACCEPTANCE' do
67
+ bind_ack_packet.p_result_list.p_results[0].result = RubySMB::Dcerpc::BindAck::USER_REJECTION
68
+ expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
69
+ end
70
+
71
+ it 'sets the Tree #client.max_buffer_size property to the DCERPC response #max_xmit_frag property value' do
72
+ bind_ack_packet.max_xmit_frag = 64
73
+ pipe.bind(options)
74
+ expect(client).to have_received(:max_buffer_size=).with(64)
75
+ end
76
+
77
+ it 'returns the expected BindAck packet' do
78
+ expect(pipe.bind(options)).to eq(bind_ack_packet)
79
+ end
80
+ end
81
+ end
@@ -85,7 +85,7 @@ RSpec.describe RubySMB::Dispatcher::Socket do
85
85
  end
86
86
 
87
87
  it 'reads the nbss header using Nbss::SessionHeader structure' do
88
- expect(RubySMB::Nbss::SessionHeader).to receive(:read).with(response_socket).and_return(session_header)
88
+ expect(RubySMB::Nbss::SessionHeader).to receive(:read).with(nbss).and_return(session_header)
89
89
  smb_socket.recv_packet
90
90
  end
91
91
 
@@ -105,6 +105,7 @@ RSpec.describe RubySMB::Dispatcher::Socket do
105
105
  packet_length = 10
106
106
  session_header.packet_length = packet_length
107
107
  allow(RubySMB::Nbss::SessionHeader).to receive(:read).and_return(session_header)
108
+ expect(response_socket).to receive(:read).with(4).and_return(session_header)
108
109
  expect(response_socket).to receive(:read).with(packet_length).and_return('A' * packet_length)
109
110
  smb_socket.recv_packet
110
111
  end
@@ -116,6 +117,7 @@ RSpec.describe RubySMB::Dispatcher::Socket do
116
117
  session_header.packet_length = packet_length
117
118
  allow(RubySMB::Nbss::SessionHeader).to receive(:read).and_return(session_header)
118
119
 
120
+ expect(response_socket).to receive(:read).with(4).and_return(session_header)
119
121
  loop do
120
122
  expect(response_socket).to receive(:read).with(packet_length).and_return('A' * returned_length).once
121
123
  packet_length -= returned_length
@@ -63,8 +63,13 @@ RSpec.describe RubySMB::GenericPacket do
63
63
  expect(RubySMB::SMB1::Packet::NegotiateResponse.read(smb1_error_packet.to_binary_s)).to be_a RubySMB::SMB1::Packet::EmptyPacket
64
64
  end
65
65
 
66
- it 'reraises the exception if it is not a valid error packet either' do
67
- expect{RubySMB::SMB1::Packet::NegotiateResponse.read('a')}.to raise_error(EOFError)
66
+ it 'raises an InvaliPacket exception if it is not a valid error packet either' do
67
+ expect{RubySMB::SMB1::Packet::NegotiateResponse.read('a')}.to raise_error(RubySMB::Error::InvalidPacket)
68
+ end
69
+
70
+ it 'sets the EmptyPacket#original_command attribute to the original COMMAND' do
71
+ packet = RubySMB::SMB1::Packet::NegotiateResponse.read(smb1_error_packet.to_binary_s)
72
+ expect(packet.original_command).to eq RubySMB::SMB1::Packet::NegotiateResponse::COMMAND
68
73
  end
69
74
  end
70
75
 
@@ -75,8 +80,51 @@ RSpec.describe RubySMB::GenericPacket do
75
80
  expect(RubySMB::SMB2::Packet::NegotiateResponse.read(smb2_error_packet.to_binary_s)).to be_a RubySMB::SMB2::Packet::ErrorPacket
76
81
  end
77
82
 
78
- it 'reraises the exception if it is not a valid error packet either' do
79
- expect{RubySMB::SMB2::Packet::NegotiateResponse.read('a')}.to raise_error(EOFError)
83
+ it 'raises an InvaliPacket exception if it is not a valid error packet either' do
84
+ expect{RubySMB::SMB2::Packet::NegotiateResponse.read('a')}.to raise_error(RubySMB::Error::InvalidPacket)
85
+ end
86
+
87
+ it 'sets the ErrorPacket#original_command attribute to the original COMMAND' do
88
+ packet = RubySMB::SMB2::Packet::NegotiateResponse.read(smb2_error_packet.to_binary_s)
89
+ expect(packet.original_command).to eq RubySMB::SMB2::Packet::NegotiateResponse::COMMAND
90
+ end
91
+ end
92
+ end
93
+
94
+ describe '#valid?' do
95
+ context 'when reading an SMB1 packet' do
96
+ let(:packet) { RubySMB::SMB1::Packet::NegotiateResponse.new }
97
+
98
+ it 'returns true if the packet protocol ID and header command are valid' do
99
+ expect(packet).to be_valid
100
+ end
101
+
102
+ it 'returns false if the packet protocol ID is wrong' do
103
+ packet.smb_header.protocol = RubySMB::SMB2::SMB2_PROTOCOL_ID
104
+ expect(packet).to_not be_valid
105
+ end
106
+
107
+ it 'returns false if the packet header command is wrong' do
108
+ packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_TREE_CONNECT
109
+ expect(packet).to_not be_valid
110
+ end
111
+ end
112
+
113
+ context 'when reading an SMB2 packet' do
114
+ let(:packet) { RubySMB::SMB2::Packet::NegotiateResponse.new }
115
+
116
+ it 'returns true if the packet protocol ID and header command are valid' do
117
+ expect(packet).to be_valid
118
+ end
119
+
120
+ it 'returns false if the packet protocol ID is wrong' do
121
+ packet.smb2_header.protocol = RubySMB::SMB1::SMB_PROTOCOL_ID
122
+ expect(packet).to_not be_valid
123
+ end
124
+
125
+ it 'returns false if the packet header command is wrong' do
126
+ packet.smb2_header.command = RubySMB::SMB2::Commands::TREE_CONNECT
127
+ expect(packet).to_not be_valid
80
128
  end
81
129
  end
82
130
  end
@@ -110,10 +110,18 @@ RSpec.describe RubySMB::SMB1::File do
110
110
  file.read_packet
111
111
  end
112
112
 
113
- it 'sets the read_length of the packet' do
113
+ it 'sets the #max_count_of_bytes_to_return of the packet' do
114
114
  expect(file.read_packet(read_length: 55).parameter_block.max_count_of_bytes_to_return).to eq 55
115
115
  end
116
116
 
117
+ it 'sets the #min_count_of_bytes_to_return of the packet' do
118
+ expect(file.read_packet(read_length: 55).parameter_block.min_count_of_bytes_to_return).to eq 55
119
+ end
120
+
121
+ it 'sets the #remaining of the packet' do
122
+ expect(file.read_packet(read_length: 55).parameter_block.remaining).to eq 55
123
+ end
124
+
117
125
  it 'sets the offset of the packet' do
118
126
  expect(file.read_packet(offset: 55).parameter_block.offset).to eq 55
119
127
  end
@@ -163,7 +171,7 @@ RSpec.describe RubySMB::SMB1::File do
163
171
  end
164
172
 
165
173
  context 'when sending the request packet and gets a response back' do
166
- context 'when the response is not a ReadAndxResponse packet' do
174
+ context 'when the response is not valid' do
167
175
  it 'raise an InvalidPacket exception' do
168
176
  read_andx_response.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_ECHO
169
177
  expect { file.read }.to raise_error(RubySMB::Error::InvalidPacket)
@@ -181,6 +189,7 @@ RSpec.describe RubySMB::SMB1::File do
181
189
  let(:empty_packet) do
182
190
  empty_packet = RubySMB::SMB1::Packet::EmptyPacket.new
183
191
  empty_packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_READ_ANDX
192
+ empty_packet.original_command = RubySMB::SMB1::Commands::SMB_COM_READ_ANDX
184
193
  empty_packet
185
194
  end
186
195
 
@@ -345,6 +354,11 @@ RSpec.describe RubySMB::SMB1::File do
345
354
  set_info_res.smb_header.nt_status = status.value
346
355
  expect(file.delete).to eq status
347
356
  end
357
+
358
+ it 'raises an InvalidPacket exception if the response is not valid' do
359
+ allow(set_info_res).to receive(:valid?).and_return(false)
360
+ expect { file.delete }.to raise_error(RubySMB::Error::InvalidPacket)
361
+ end
348
362
  end
349
363
 
350
364
  describe '#delete_packet' do
@@ -415,6 +429,11 @@ RSpec.describe RubySMB::SMB1::File do
415
429
  set_info_res.smb_header.nt_status = status.value
416
430
  expect(file.rename(filename)).to eq status
417
431
  end
432
+
433
+ it 'raises an InvalidPacket exception if the response is not valid' do
434
+ allow(set_info_res).to receive(:valid?).and_return(false)
435
+ expect { file.rename(filename) }.to raise_error(RubySMB::Error::InvalidPacket)
436
+ end
418
437
  end
419
438
 
420
439
  describe '#rename_packet' do
@@ -465,5 +484,175 @@ RSpec.describe RubySMB::SMB1::File do
465
484
  expect(parameter_block.max_data_count).to eq 16_384
466
485
  end
467
486
  end
487
+
488
+ describe '#close' do
489
+ let(:request) { double('CloseRequest') }
490
+ let(:response) { double('CloseResponse') }
491
+ let(:raw_response) { double('Raw response') }
492
+
493
+ before :example do
494
+ allow(RubySMB::SMB1::Packet::CloseRequest).to receive(:new).and_return(request)
495
+ allow(file).to receive(:set_header_fields).and_return(request)
496
+ allow(client).to receive(:send_recv).and_return(raw_response)
497
+ allow(RubySMB::SMB1::Packet::CloseResponse).to receive(:read).and_return(response)
498
+ allow(response).to receive(:valid?).and_return(true)
499
+ allow(response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_SUCCESS)
500
+ end
501
+
502
+ it 'creates a new SMB1 CloseRequest packet' do
503
+ expect(RubySMB::SMB1::Packet::CloseRequest).to receive(:new)
504
+ file.close
505
+ end
506
+
507
+ it 'calls Tree #set_header_fields to set SetFileInformationRequest headers' do
508
+ expect(file).to receive(:set_header_fields).with(request)
509
+ file.close
510
+ end
511
+
512
+ it 'calls Client #send_recv with the expected request' do
513
+ expect(client).to receive(:send_recv).with(request)
514
+ file.close
515
+ end
516
+
517
+ it 'parses the response as a SMB1 CloseResponse packet' do
518
+ expect(RubySMB::SMB1::Packet::CloseResponse).to receive(:read).with(raw_response)
519
+ file.close
520
+ end
521
+
522
+ it 'raises an InvalidPacket exception if the response is not valid' do
523
+ allow(response).to receive(:valid?).and_return(false)
524
+ smb_header = double('SMB Header')
525
+ allow(response).to receive(:smb_header).and_return(smb_header)
526
+ allow(smb_header).to receive_messages(:protocol => nil, :command => nil)
527
+ expect { file.close }.to raise_error(RubySMB::Error::InvalidPacket)
528
+ end
529
+
530
+ it 'raises an UnexpectedStatusCode exception if the response status code is not STATUS_SUCCESS' do
531
+ allow(response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_OBJECT_NAME_NOT_FOUND)
532
+ expect { file.close }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
533
+ end
534
+
535
+ it 'returns the response status code' do
536
+ expect(file.close).to eq WindowsError::NTStatus::STATUS_SUCCESS
537
+ end
538
+ end
539
+
540
+ describe '#send_recv_read' do
541
+ let(:read_data) { 'read data' }
542
+ let(:raw_response) { double('fake raw response data') }
543
+ let(:read_andx_response) {
544
+ res = RubySMB::SMB1::Packet::ReadAndxResponse.new
545
+ res.data_block.data = read_data
546
+ res
547
+ }
548
+
549
+ before :example do
550
+ allow(client).to receive(:send_recv).and_return(raw_response)
551
+ allow(RubySMB::SMB1::Packet::ReadAndxResponse).to receive(:read).with(raw_response).and_return(read_andx_response)
552
+ end
553
+
554
+ context 'when the number of bytes to read is not provided' do
555
+ it 'reads 0 bytes by default' do
556
+ expect(file).to receive(:read_packet).with(read_length: 0, offset: 0).once.and_call_original
557
+ file.send_recv_read
558
+ end
559
+ end
560
+
561
+ it 'only reads the number of bytes provided as argument' do
562
+ bytes = 5
563
+ expect(file).to receive(:read_packet).with(read_length: bytes, offset: 0).once.and_call_original
564
+ file.send_recv_read(read_length: bytes)
565
+ end
566
+
567
+ it 'reads from the offset provided as argument' do
568
+ offset = 3
569
+ expect(file).to receive(:read_packet).with(read_length: 0, offset: offset).once.and_call_original
570
+ file.send_recv_read(offset: offset)
571
+ end
572
+
573
+ it 'calls Client #send_recv with the expected request' do
574
+ request = double('Request')
575
+ allow(file).to receive(:read_packet).and_return(request)
576
+ expect(client).to receive(:send_recv).with(request)
577
+ file.send_recv_read
578
+ end
579
+
580
+ it 'parses the response as a SMB1 ReadAndxResponse packet' do
581
+ expect(RubySMB::SMB1::Packet::ReadAndxResponse).to receive(:read).with(raw_response)
582
+ file.send_recv_read
583
+ end
584
+
585
+ it 'raises an InvalidPacket exception if the response is not valid' do
586
+ allow(read_andx_response).to receive(:valid?).and_return(false)
587
+ expect { file.send_recv_read }.to raise_error(RubySMB::Error::InvalidPacket)
588
+ end
589
+
590
+ it 'raises an UnexpectedStatusCode exception if the response status code is not STATUS_SUCCESS' do
591
+ allow(read_andx_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_OBJECT_NAME_NOT_FOUND)
592
+ expect { file.send_recv_read }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
593
+ end
594
+
595
+ it 'returns the expected string' do
596
+ expect(file.send_recv_read).to eq(read_data)
597
+ end
598
+ end
599
+
600
+ describe '#send_recv_write' do
601
+ let(:write_data) { 'write data' }
602
+ let(:request) { double('Request') }
603
+ let(:raw_response) { double('fake raw response data') }
604
+ let(:write_andx_response) {
605
+ res = RubySMB::SMB1::Packet::WriteAndxResponse.new
606
+ res.parameter_block.count_low = write_data.size
607
+ res
608
+ }
609
+
610
+ before :example do
611
+ allow(file).to receive(:write_packet).and_return(request)
612
+ allow(request).to receive(:set_64_bit_offset)
613
+ allow(client).to receive(:send_recv).and_return(raw_response)
614
+ allow(RubySMB::SMB1::Packet::WriteAndxResponse).to receive(:read).with(raw_response).and_return(write_andx_response)
615
+ end
616
+
617
+ it 'reads 0 bytes from offset 0 by default' do
618
+ expect(file).to receive(:write_packet).with(data: '', offset: 0).once.and_call_original
619
+ file.send_recv_write
620
+ end
621
+
622
+ it 'writes the data provided as argument' do
623
+ expect(file).to receive(:write_packet).with(data: write_data, offset: 0).once.and_call_original
624
+ file.send_recv_write(data: write_data)
625
+ end
626
+
627
+ it 'reads from the offset provided as argument' do
628
+ offset = 3
629
+ expect(file).to receive(:write_packet).with(data: '', offset: offset).once.and_call_original
630
+ file.send_recv_write(offset: offset)
631
+ end
632
+
633
+ it 'sets the 64 bit offset to true' do
634
+ expect(request).to receive(:set_64_bit_offset).with(true)
635
+ file.send_recv_write
636
+ end
637
+
638
+ it 'calls Client #send_recv with the expected request' do
639
+ expect(client).to receive(:send_recv).with(request)
640
+ file.send_recv_write
641
+ end
642
+
643
+ it 'parses the response as a SMB1 WriteAndxResponse packet' do
644
+ expect(RubySMB::SMB1::Packet::WriteAndxResponse).to receive(:read).with(raw_response)
645
+ file.send_recv_write
646
+ end
647
+
648
+ it 'raises an InvalidPacket exception if the response is not valid' do
649
+ allow(write_andx_response).to receive(:valid?).and_return(false)
650
+ expect { file.send_recv_write }.to raise_error(RubySMB::Error::InvalidPacket)
651
+ end
652
+
653
+ it 'returns the expected response #count_low value' do
654
+ expect(file.send_recv_write).to eq(write_data.size)
655
+ end
656
+ end
468
657
  end
469
658
 
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RubySMB::SMB1::Packet::EmptyPacket do
4
+ subject(:packet) { described_class.new }
5
+
6
+ describe '#smb_header' do
7
+ subject(:header) { packet.smb_header }
8
+
9
+ it 'is a standard SMB Header' do
10
+ expect(header).to be_a RubySMB::SMB1::SMBHeader
11
+ end
12
+ end
13
+
14
+ describe '#parameter_block' do
15
+ subject(:parameter_block) { packet.parameter_block }
16
+
17
+ it 'is a standard ParameterBlock' do
18
+ expect(parameter_block).to be_a RubySMB::SMB1::ParameterBlock
19
+ end
20
+
21
+ it 'should be empty' do
22
+ expect(parameter_block.to_binary_s).to eq "\x00"
23
+ end
24
+ end
25
+
26
+ describe '#data_block' do
27
+ subject(:data_block) { packet.data_block }
28
+
29
+ it 'is a standard DataBlock' do
30
+ expect(data_block).to be_a RubySMB::SMB1::DataBlock
31
+ end
32
+
33
+ it 'should be empty' do
34
+ expect(data_block.to_binary_s).to eq "\x00\x00"
35
+ end
36
+ end
37
+
38
+ describe '#valid?' do
39
+ before :example do
40
+ packet.original_command = RubySMB::SMB1::Commands::SMB_COM_TREE_CONNECT
41
+ packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_TREE_CONNECT
42
+ end
43
+
44
+ it 'returns true if the packet protocol ID and header command are valid' do
45
+ expect(packet).to be_valid
46
+ end
47
+
48
+ it 'returns false if the packet protocol ID is wrong' do
49
+ packet.smb_header.protocol = RubySMB::SMB2::SMB2_PROTOCOL_ID
50
+ expect(packet).to_not be_valid
51
+ end
52
+
53
+ it 'returns false if the packet header command is wrong' do
54
+ packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_NEGOTIATE
55
+ expect(packet).to_not be_valid
56
+ end
57
+
58
+ it 'returns false if the packet parameter block size is not 0' do
59
+ packet.parameter_block.word_count = 10
60
+ expect(packet).to_not be_valid
61
+ end
62
+
63
+ it 'returns false if the packet data block size is not 0' do
64
+ packet.data_block.byte_count = 10
65
+ expect(packet).to_not be_valid
66
+ end
67
+ end
68
+ end
@@ -80,6 +80,8 @@ RSpec.describe RubySMB::SMB1::Packet::Trans2::FindFirst2Response do
80
80
 
81
81
  let(:names_blob) { names_array.collect(&:to_binary_s).join('') }
82
82
 
83
+ let(:find_info) { FindFileFullDirectoryInfo.new }
84
+
83
85
  it 'returns an array of parsed FindFileFullDirectoryInfo structs' do
84
86
  packet.data_block.trans2_data.buffer = names_blob
85
87
  expect(packet.results(FindFileFullDirectoryInfo, unicode: false)).to eq names_array
@@ -87,7 +89,6 @@ RSpec.describe RubySMB::SMB1::Packet::Trans2::FindFirst2Response do
87
89
 
88
90
  it 'sets the FindFileFullDirectoryInfo unicode attribute when unicode argument is true' do
89
91
  packet.data_block.trans2_data.buffer = names1.to_binary_s
90
- find_info = FindFileFullDirectoryInfo.new
91
92
  allow(FindFileFullDirectoryInfo).to receive(:new).and_return find_info
92
93
  expect(find_info).to receive(:unicode=).with(true).once
93
94
  packet.results(FindFileFullDirectoryInfo, unicode: true)
@@ -95,10 +96,18 @@ RSpec.describe RubySMB::SMB1::Packet::Trans2::FindFirst2Response do
95
96
 
96
97
  it 'does not set the FindFileFullDirectoryInfo unicode attribute when unicode argument is false' do
97
98
  packet.data_block.trans2_data.buffer = names1.to_binary_s
98
- find_info = FindFileFullDirectoryInfo.new
99
99
  allow(FindFileFullDirectoryInfo).to receive(:new).and_return find_info
100
100
  expect(find_info).to receive(:unicode=).with(false).once
101
101
  packet.results(FindFileFullDirectoryInfo, unicode: false)
102
102
  end
103
+
104
+ context 'when the File Information is not a valid' do
105
+ it 'raises an InvalidPacket exception' do
106
+ packet.data_block.trans2_data.buffer = names1.to_binary_s
107
+ allow(FindFileFullDirectoryInfo).to receive(:new).and_return(find_info)
108
+ allow(find_info).to receive(:read).and_raise(IOError)
109
+ expect { packet.results(FindFileFullDirectoryInfo, unicode: false) }.to raise_error(RubySMB::Error::InvalidPacket)
110
+ end
111
+ end
103
112
  end
104
113
  end
@@ -78,6 +78,8 @@ RSpec.describe RubySMB::SMB1::Packet::Trans2::FindNext2Response do
78
78
 
79
79
  let(:names_blob) { names_array.collect(&:to_binary_s).join('') }
80
80
 
81
+ let(:find_info) { FindFileFullDirectoryInfo.new }
82
+
81
83
  it 'returns an array of parsed FindFileFullDirectoryInfo structs' do
82
84
  packet.data_block.trans2_data.buffer = names_blob
83
85
  expect(packet.results(FindFileFullDirectoryInfo, unicode: false)).to eq names_array
@@ -85,7 +87,6 @@ RSpec.describe RubySMB::SMB1::Packet::Trans2::FindNext2Response do
85
87
 
86
88
  it 'sets the FindFileFullDirectoryInfo unicode attribute when unicode argument is true' do
87
89
  packet.data_block.trans2_data.buffer = names1.to_binary_s
88
- find_info = FindFileFullDirectoryInfo.new
89
90
  allow(FindFileFullDirectoryInfo).to receive(:new).and_return find_info
90
91
  expect(find_info).to receive(:unicode=).with(true).once
91
92
  packet.results(FindFileFullDirectoryInfo, unicode: true)
@@ -93,10 +94,18 @@ RSpec.describe RubySMB::SMB1::Packet::Trans2::FindNext2Response do
93
94
 
94
95
  it 'does not set the FindFileFullDirectoryInfo unicode attribute when unicode argument is false' do
95
96
  packet.data_block.trans2_data.buffer = names1.to_binary_s
96
- find_info = FindFileFullDirectoryInfo.new
97
97
  allow(FindFileFullDirectoryInfo).to receive(:new).and_return find_info
98
98
  expect(find_info).to receive(:unicode=).with(false).once
99
99
  packet.results(FindFileFullDirectoryInfo, unicode: false)
100
100
  end
101
+
102
+ context 'when the File Information is not a valid' do
103
+ it 'raises an InvalidPacket exception' do
104
+ packet.data_block.trans2_data.buffer = names1.to_binary_s
105
+ allow(FindFileFullDirectoryInfo).to receive(:new).and_return(find_info)
106
+ allow(find_info).to receive(:read).and_raise(IOError)
107
+ expect { packet.results(FindFileFullDirectoryInfo, unicode: false) }.to raise_error(RubySMB::Error::InvalidPacket)
108
+ end
109
+ end
101
110
  end
102
111
  end
@@ -104,4 +104,44 @@ RSpec.describe RubySMB::SMB1::Packet::TreeConnectResponse do
104
104
  expect(file_response.guest_access_rights).to be_a RubySMB::SMB1::BitField::FileAccessMask
105
105
  end
106
106
  end
107
+
108
+ describe '#access_rights' do
109
+ it 'is a DirectoryAccessMask if the Tree is a directory' do
110
+ allow(packet).to receive(:is_directory?).and_return(true)
111
+ expect(packet.access_rights).to be_a RubySMB::SMB1::BitField::DirectoryAccessMask
112
+ end
113
+
114
+ it 'is a FileAccessMask if the Tree is not a directory' do
115
+ allow(packet).to receive(:is_directory?).and_return(false)
116
+ expect(packet.access_rights).to be_a RubySMB::SMB1::BitField::FileAccessMask
117
+ end
118
+
119
+ context 'when it is not a valid FileAccessMask' do
120
+ it 'raises an InvalidBitField exception' do
121
+ allow(packet).to receive(:is_directory?).and_return(false)
122
+ allow(RubySMB::SMB1::BitField::FileAccessMask).to receive(:read).and_raise(IOError)
123
+ expect { packet.access_rights }.to raise_error(RubySMB::Error::InvalidBitField)
124
+ end
125
+ end
126
+ end
127
+
128
+ describe '#guest_access_rights' do
129
+ it 'is a DirectoryAccessMask if the Tree is a directory' do
130
+ allow(packet).to receive(:is_directory?).and_return(true)
131
+ expect(packet.guest_access_rights).to be_a RubySMB::SMB1::BitField::DirectoryAccessMask
132
+ end
133
+
134
+ it 'is a FileAccessMask if the Tree is not a directory' do
135
+ allow(packet).to receive(:is_directory?).and_return(false)
136
+ expect(packet.guest_access_rights).to be_a RubySMB::SMB1::BitField::FileAccessMask
137
+ end
138
+
139
+ context 'when it is not a valid FileAccessMask' do
140
+ it 'raises an InvalidBitField exception' do
141
+ allow(packet).to receive(:is_directory?).and_return(false)
142
+ allow(RubySMB::SMB1::BitField::FileAccessMask).to receive(:read).and_raise(IOError)
143
+ expect { packet.guest_access_rights }.to raise_error(RubySMB::Error::InvalidBitField)
144
+ end
145
+ end
146
+ end
107
147
  end