ruby_smb 2.0.1 → 2.0.6
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +2 -1
- data/examples/anonymous_auth.rb +3 -3
- data/examples/append_file.rb +10 -8
- data/examples/authenticate.rb +9 -5
- data/examples/delete_file.rb +8 -6
- data/examples/enum_registry_key.rb +5 -4
- data/examples/enum_registry_values.rb +5 -4
- data/examples/list_directory.rb +8 -6
- data/examples/negotiate_with_netbios_service.rb +9 -5
- data/examples/net_share_enum_all.rb +6 -4
- data/examples/pipes.rb +11 -12
- data/examples/query_service_status.rb +64 -0
- data/examples/read_file.rb +8 -6
- data/examples/read_registry_key_value.rb +6 -5
- data/examples/rename_file.rb +9 -7
- data/examples/tree_connect.rb +7 -5
- data/examples/write_file.rb +9 -7
- data/lib/ruby_smb/client.rb +81 -48
- data/lib/ruby_smb/client/authentication.rb +5 -10
- data/lib/ruby_smb/client/echo.rb +2 -4
- data/lib/ruby_smb/client/negotiation.rb +21 -14
- data/lib/ruby_smb/client/tree_connect.rb +2 -4
- data/lib/ruby_smb/client/utils.rb +16 -10
- data/lib/ruby_smb/client/winreg.rb +1 -1
- data/lib/ruby_smb/dcerpc.rb +4 -0
- data/lib/ruby_smb/dcerpc/error.rb +3 -0
- data/lib/ruby_smb/dcerpc/ndr.rb +306 -44
- data/lib/ruby_smb/dcerpc/netlogon.rb +101 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request.rb +28 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request.rb +27 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request.rb +25 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response.rb +24 -0
- data/lib/ruby_smb/dcerpc/request.rb +19 -0
- data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +34 -0
- data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +9 -6
- data/lib/ruby_smb/dcerpc/svcctl.rb +479 -0
- data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +48 -0
- data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +35 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +31 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +44 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_status_request.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +27 -0
- data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +27 -0
- data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +25 -0
- data/lib/ruby_smb/dcerpc/winreg.rb +98 -17
- data/lib/ruby_smb/dcerpc/winreg/create_key_request.rb +73 -0
- data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +36 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +4 -4
- data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +7 -6
- data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +10 -10
- data/lib/ruby_smb/dcerpc/winreg/save_key_request.rb +37 -0
- data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +23 -0
- data/lib/ruby_smb/dispatcher/base.rb +1 -1
- data/lib/ruby_smb/dispatcher/socket.rb +1 -1
- data/lib/ruby_smb/error.rb +21 -5
- data/lib/ruby_smb/field/stringz16.rb +17 -1
- data/lib/ruby_smb/generic_packet.rb +11 -1
- data/lib/ruby_smb/nbss/session_header.rb +4 -4
- data/lib/ruby_smb/smb1/file.rb +10 -25
- data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +0 -1
- data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +0 -1
- data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +1 -2
- data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +1 -13
- data/lib/ruby_smb/smb1/pipe.rb +8 -6
- data/lib/ruby_smb/smb1/tree.rb +13 -9
- data/lib/ruby_smb/smb2/file.rb +33 -33
- data/lib/ruby_smb/smb2/pipe.rb +9 -6
- data/lib/ruby_smb/smb2/tree.rb +21 -11
- data/lib/ruby_smb/version.rb +1 -1
- data/spec/lib/ruby_smb/client_spec.rb +195 -101
- data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +1396 -77
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request_spec.rb +69 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response_spec.rb +53 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request_spec.rb +69 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response_spec.rb +37 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request_spec.rb +45 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response_spec.rb +37 -0
- data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +161 -0
- data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +49 -12
- data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +191 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +39 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +78 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +59 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +152 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_request_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +72 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +46 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +512 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +110 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +44 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +0 -4
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +9 -4
- data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +0 -4
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +17 -17
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +11 -23
- data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +57 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +22 -0
- data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +227 -41
- data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +10 -10
- data/spec/lib/ruby_smb/error_spec.rb +34 -5
- data/spec/lib/ruby_smb/field/stringz16_spec.rb +12 -0
- data/spec/lib/ruby_smb/generic_packet_spec.rb +7 -0
- data/spec/lib/ruby_smb/nbss/session_header_spec.rb +4 -11
- data/spec/lib/ruby_smb/smb1/file_spec.rb +2 -4
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +0 -1
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +0 -1
- data/spec/lib/ruby_smb/smb1/packet/trans2/open2_response_spec.rb +0 -5
- data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_response_spec.rb +0 -6
- data/spec/lib/ruby_smb/smb1/pipe_spec.rb +30 -5
- data/spec/lib/ruby_smb/smb1/tree_spec.rb +22 -0
- data/spec/lib/ruby_smb/smb2/file_spec.rb +61 -9
- data/spec/lib/ruby_smb/smb2/pipe_spec.rb +9 -5
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +58 -1
- metadata +91 -2
- metadata.gz.sig +0 -0
data/lib/ruby_smb/smb1/tree.rb
CHANGED
@@ -49,13 +49,20 @@ module RubySMB
|
|
49
49
|
raise RubySMB::Error::InvalidPacket.new(
|
50
50
|
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
51
51
|
expected_cmd: RubySMB::SMB1::Packet::TreeDisconnectResponse::COMMAND,
|
52
|
-
|
53
|
-
received_cmd: response.smb_header.command
|
52
|
+
packet: response
|
54
53
|
)
|
55
54
|
end
|
56
55
|
response.status_code
|
57
56
|
end
|
58
57
|
|
58
|
+
def open_pipe(opts)
|
59
|
+
# Make sure we don't modify the caller's hash options
|
60
|
+
opts = opts.dup
|
61
|
+
opts[:filename] = opts[:filename].dup
|
62
|
+
opts[:filename].prepend('\\') unless opts[:filename].start_with?('\\')
|
63
|
+
open_file(opts)
|
64
|
+
end
|
65
|
+
|
59
66
|
# Open a file on the remote share.
|
60
67
|
#
|
61
68
|
# @example
|
@@ -135,15 +142,14 @@ module RubySMB
|
|
135
142
|
raise RubySMB::Error::InvalidPacket.new(
|
136
143
|
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
137
144
|
expected_cmd: RubySMB::SMB1::Packet::NtCreateAndxResponse::COMMAND,
|
138
|
-
|
139
|
-
received_cmd: response.smb_header.command
|
145
|
+
packet: response
|
140
146
|
)
|
141
147
|
end
|
142
148
|
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
143
149
|
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
144
150
|
end
|
145
151
|
|
146
|
-
case response.parameter_block.resource_type
|
152
|
+
case response.parameter_block.resource_type
|
147
153
|
when RubySMB::SMB1::ResourceType::BYTE_MODE_PIPE, RubySMB::SMB1::ResourceType::MESSAGE_MODE_PIPE
|
148
154
|
RubySMB::SMB1::Pipe.new(name: filename, tree: self, response: response)
|
149
155
|
when RubySMB::SMB1::ResourceType::DISK
|
@@ -195,8 +201,7 @@ module RubySMB
|
|
195
201
|
raise RubySMB::Error::InvalidPacket.new(
|
196
202
|
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
197
203
|
expected_cmd: RubySMB::SMB1::Packet::Trans2::FindFirst2Response::COMMAND,
|
198
|
-
|
199
|
-
received_cmd: response.smb_header.command
|
204
|
+
packet: response
|
200
205
|
)
|
201
206
|
end
|
202
207
|
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
@@ -230,8 +235,7 @@ module RubySMB
|
|
230
235
|
raise RubySMB::Error::InvalidPacket.new(
|
231
236
|
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
232
237
|
expected_cmd: RubySMB::SMB1::Packet::Trans2::FindNext2Response::COMMAND,
|
233
|
-
|
234
|
-
received_cmd: response.smb_header.command
|
238
|
+
packet: response
|
235
239
|
)
|
236
240
|
end
|
237
241
|
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
data/lib/ruby_smb/smb2/file.rb
CHANGED
@@ -95,8 +95,7 @@ module RubySMB
|
|
95
95
|
raise RubySMB::Error::InvalidPacket.new(
|
96
96
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
97
97
|
expected_cmd: RubySMB::SMB2::Packet::CloseResponse::COMMAND,
|
98
|
-
|
99
|
-
received_cmd: response.smb2_header.command
|
98
|
+
packet: response
|
100
99
|
)
|
101
100
|
end
|
102
101
|
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
@@ -115,21 +114,22 @@ module RubySMB
|
|
115
114
|
# @raise [RubySMB::Error::InvalidPacket] if the response is not a ReadResponse packet
|
116
115
|
# @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
|
117
116
|
def read(bytes: size, offset: 0)
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
117
|
+
max_read = tree.client.server_max_read_size
|
118
|
+
max_read = 65536 unless tree.client.server_supports_multi_credit
|
119
|
+
atomic_read_size = [bytes, max_read].min
|
120
|
+
credit_charge = 0
|
121
|
+
if tree.client.server_supports_multi_credit
|
122
|
+
credit_charge = (atomic_read_size - 1) / 65536 + 1
|
123
|
+
end
|
123
124
|
|
124
|
-
read_request = read_packet(read_length: atomic_read_size, offset: offset)
|
125
|
+
read_request = read_packet(read_length: atomic_read_size, offset: offset, credit_charge: credit_charge)
|
125
126
|
raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
|
126
127
|
response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
|
127
128
|
unless response.valid?
|
128
129
|
raise RubySMB::Error::InvalidPacket.new(
|
129
130
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
130
131
|
expected_cmd: RubySMB::SMB2::Packet::ReadResponse::COMMAND,
|
131
|
-
|
132
|
-
received_cmd: response.smb2_header.command
|
132
|
+
packet: response
|
133
133
|
)
|
134
134
|
end
|
135
135
|
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
@@ -142,17 +142,16 @@ module RubySMB
|
|
142
142
|
|
143
143
|
while remaining_bytes > 0
|
144
144
|
offset += atomic_read_size
|
145
|
-
atomic_read_size = remaining_bytes if remaining_bytes <
|
145
|
+
atomic_read_size = remaining_bytes if remaining_bytes < max_read
|
146
146
|
|
147
|
-
read_request = read_packet(read_length: atomic_read_size, offset: offset)
|
147
|
+
read_request = read_packet(read_length: atomic_read_size, offset: offset, credit_charge: credit_charge)
|
148
148
|
raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
|
149
149
|
response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
|
150
150
|
unless response.valid?
|
151
151
|
raise RubySMB::Error::InvalidPacket.new(
|
152
152
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
153
153
|
expected_cmd: RubySMB::SMB2::Packet::ReadResponse::COMMAND,
|
154
|
-
|
155
|
-
received_cmd: response.smb2_header.command
|
154
|
+
packet: response
|
156
155
|
)
|
157
156
|
end
|
158
157
|
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
@@ -169,11 +168,13 @@ module RubySMB
|
|
169
168
|
#
|
170
169
|
# @param bytes [Integer] the number of bytes to read
|
171
170
|
# @param offset [Integer] the byte offset in the file to start reading from
|
171
|
+
# @param credit_charge [Integer] the number of credits that this request consumes
|
172
172
|
# @return [RubySMB::SMB2::Packet::ReadRequest] the data read from the file
|
173
|
-
def read_packet(read_length: 0, offset: 0)
|
173
|
+
def read_packet(read_length: 0, offset: 0, credit_charge: 1)
|
174
174
|
read_request = set_header_fields(RubySMB::SMB2::Packet::ReadRequest.new)
|
175
175
|
read_request.read_length = read_length
|
176
176
|
read_request.offset = offset
|
177
|
+
read_request.smb2_header.credit_charge = credit_charge
|
177
178
|
read_request
|
178
179
|
end
|
179
180
|
|
@@ -185,8 +186,7 @@ module RubySMB
|
|
185
186
|
raise RubySMB::Error::InvalidPacket.new(
|
186
187
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
187
188
|
expected_cmd: RubySMB::SMB2::Packet::ReadResponse::COMMAND,
|
188
|
-
|
189
|
-
received_cmd: response.smb2_header.command
|
189
|
+
packet: response
|
190
190
|
)
|
191
191
|
end
|
192
192
|
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
@@ -206,8 +206,7 @@ module RubySMB
|
|
206
206
|
raise RubySMB::Error::InvalidPacket.new(
|
207
207
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
208
208
|
expected_cmd: RubySMB::SMB2::Packet::SetInfoResponse::COMMAND,
|
209
|
-
|
210
|
-
received_cmd: response.smb2_header.command
|
209
|
+
packet: response
|
211
210
|
)
|
212
211
|
end
|
213
212
|
response.smb2_header.nt_status.to_nt_status
|
@@ -240,29 +239,30 @@ module RubySMB
|
|
240
239
|
# @return [WindowsError::ErrorCode] the NTStatus code returned from the operation
|
241
240
|
# @raise [RubySMB::Error::InvalidPacket] if the response is not a WriteResponse packet
|
242
241
|
def write(data:'', offset: 0)
|
242
|
+
max_write = tree.client.server_max_write_size
|
243
|
+
max_write = 65536 unless tree.client.server_supports_multi_credit
|
243
244
|
buffer = data.dup
|
244
245
|
bytes = data.length
|
245
|
-
atomic_write_size =
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
246
|
+
atomic_write_size = [bytes, max_write].min
|
247
|
+
credit_charge = 0
|
248
|
+
if tree.client.server_supports_multi_credit
|
249
|
+
credit_charge = (atomic_write_size - 1) / 65536 + 1
|
250
|
+
end
|
250
251
|
|
251
252
|
while buffer.length > 0 do
|
252
|
-
write_request = write_packet(data: buffer.slice!(0,atomic_write_size), offset: offset)
|
253
|
+
write_request = write_packet(data: buffer.slice!(0, atomic_write_size), offset: offset, credit_charge: credit_charge)
|
253
254
|
raw_response = tree.client.send_recv(write_request, encrypt: @tree_connect_encrypt_data)
|
254
255
|
response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
|
255
256
|
unless response.valid?
|
256
257
|
raise RubySMB::Error::InvalidPacket.new(
|
257
258
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
258
259
|
expected_cmd: RubySMB::SMB2::Packet::WriteResponse::COMMAND,
|
259
|
-
|
260
|
-
received_cmd: response.smb2_header.command
|
260
|
+
packet: response
|
261
261
|
)
|
262
262
|
end
|
263
263
|
status = response.smb2_header.nt_status.to_nt_status
|
264
264
|
|
265
|
-
offset+= atomic_write_size
|
265
|
+
offset += atomic_write_size
|
266
266
|
return status unless status == WindowsError::NTStatus::STATUS_SUCCESS
|
267
267
|
end
|
268
268
|
|
@@ -273,11 +273,13 @@ module RubySMB
|
|
273
273
|
#
|
274
274
|
# @param data [String] the data to write to the file
|
275
275
|
# @param offset [Integer] the offset in the file to start writing from
|
276
|
+
# @param credit_charge [Integer] the number of credits that this request consumes
|
276
277
|
# @return []RubySMB::SMB2::Packet::WriteRequest] the request packet
|
277
|
-
def write_packet(data:'', offset: 0)
|
278
|
+
def write_packet(data:'', offset: 0, credit_charge: 1)
|
278
279
|
write_request = set_header_fields(RubySMB::SMB2::Packet::WriteRequest.new)
|
279
280
|
write_request.write_offset = offset
|
280
281
|
write_request.buffer = data
|
282
|
+
write_request.smb2_header.credit_charge = credit_charge
|
281
283
|
write_request
|
282
284
|
end
|
283
285
|
|
@@ -289,8 +291,7 @@ module RubySMB
|
|
289
291
|
raise RubySMB::Error::InvalidPacket.new(
|
290
292
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
291
293
|
expected_cmd: RubySMB::SMB2::Packet::WriteResponse::COMMAND,
|
292
|
-
|
293
|
-
received_cmd: response.smb2_header.command
|
294
|
+
packet: response
|
294
295
|
)
|
295
296
|
end
|
296
297
|
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
@@ -311,8 +312,7 @@ module RubySMB
|
|
311
312
|
raise RubySMB::Error::InvalidPacket.new(
|
312
313
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
313
314
|
expected_cmd: RubySMB::SMB2::Packet::SetInfoResponse::COMMAND,
|
314
|
-
|
315
|
-
received_cmd: response.smb2_header.command
|
315
|
+
packet: response
|
316
316
|
)
|
317
317
|
end
|
318
318
|
response.smb2_header.nt_status.to_nt_status
|
data/lib/ruby_smb/smb2/pipe.rb
CHANGED
@@ -13,9 +13,13 @@ module RubySMB
|
|
13
13
|
def initialize(tree:, response:, name:)
|
14
14
|
raise ArgumentError, 'No Name Provided' if name.nil?
|
15
15
|
case name
|
16
|
-
when '
|
16
|
+
when 'netlogon', '\\netlogon'
|
17
|
+
extend RubySMB::Dcerpc::Netlogon
|
18
|
+
when 'srvsvc', '\\srvsvc'
|
17
19
|
extend RubySMB::Dcerpc::Srvsvc
|
18
|
-
when '
|
20
|
+
when 'svcctl', '\\svcctl'
|
21
|
+
extend RubySMB::Dcerpc::Svcctl
|
22
|
+
when 'winreg', '\\winreg'
|
19
23
|
extend RubySMB::Dcerpc::Winreg
|
20
24
|
end
|
21
25
|
super(tree: tree, response: response, name: name)
|
@@ -40,8 +44,7 @@ module RubySMB
|
|
40
44
|
raise RubySMB::Error::InvalidPacket.new(
|
41
45
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
42
46
|
expected_cmd: RubySMB::SMB2::Packet::IoctlResponse::COMMAND,
|
43
|
-
|
44
|
-
received_cmd: response.smb2_header.command
|
47
|
+
packet: response
|
45
48
|
)
|
46
49
|
end
|
47
50
|
|
@@ -89,6 +92,7 @@ module RubySMB
|
|
89
92
|
request = set_header_fields(RubySMB::SMB2::Packet::IoctlRequest.new(options))
|
90
93
|
request.ctl_code = 0x0011C017
|
91
94
|
request.flags.is_fsctl = 0x00000001
|
95
|
+
# TODO: handle fragmentation when the request size > MAX_XMIT_FRAG
|
92
96
|
request.buffer = action.to_binary_s
|
93
97
|
|
94
98
|
ioctl_raw_response = @tree.client.send_recv(request)
|
@@ -97,8 +101,7 @@ module RubySMB
|
|
97
101
|
raise RubySMB::Error::InvalidPacket.new(
|
98
102
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
99
103
|
expected_cmd: RubySMB::SMB2::Packet::IoctlRequest::COMMAND,
|
100
|
-
|
101
|
-
received_cmd: ioctl_response.smb2_header.command
|
104
|
+
packet: ioctl_response
|
102
105
|
)
|
103
106
|
end
|
104
107
|
unless [WindowsError::NTStatus::STATUS_SUCCESS,
|
data/lib/ruby_smb/smb2/tree.rb
CHANGED
@@ -50,13 +50,20 @@ module RubySMB
|
|
50
50
|
raise RubySMB::Error::InvalidPacket.new(
|
51
51
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
52
52
|
expected_cmd: RubySMB::SMB2::Packet::TreeDisconnectResponse::COMMAND,
|
53
|
-
|
54
|
-
received_cmd: response.smb2_header.command
|
53
|
+
packet: response
|
55
54
|
)
|
56
55
|
end
|
57
56
|
response.status_code
|
58
57
|
end
|
59
58
|
|
59
|
+
def open_pipe(opts)
|
60
|
+
# Make sure we don't modify the caller's hash options
|
61
|
+
opts = opts.dup
|
62
|
+
opts[:filename] = opts[:filename].dup
|
63
|
+
opts[:filename] = opts[:filename][1..-1] if opts[:filename].start_with? '\\'
|
64
|
+
open_file(opts)
|
65
|
+
end
|
66
|
+
|
60
67
|
def open_file(filename:, attributes: nil, options: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
|
61
68
|
impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE, read: true, write: false, delete: false)
|
62
69
|
|
@@ -108,8 +115,7 @@ module RubySMB
|
|
108
115
|
raise RubySMB::Error::InvalidPacket.new(
|
109
116
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
110
117
|
expected_cmd: RubySMB::SMB2::Packet::CreateResponse::COMMAND,
|
111
|
-
|
112
|
-
received_cmd: response.smb2_header.command
|
118
|
+
packet: response
|
113
119
|
)
|
114
120
|
end
|
115
121
|
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
@@ -147,12 +153,19 @@ module RubySMB
|
|
147
153
|
directory_request.file_information_class = type::CLASS_LEVEL
|
148
154
|
directory_request.file_id = file_id
|
149
155
|
directory_request.name = pattern
|
150
|
-
|
156
|
+
|
157
|
+
max_read = client.server_max_read_size
|
158
|
+
max_read = 65536 unless client.server_supports_multi_credit
|
159
|
+
credit_charge = 0
|
160
|
+
if client.server_supports_multi_credit
|
161
|
+
credit_charge = (max_read - 1) / 65536 + 1
|
162
|
+
end
|
163
|
+
directory_request.output_length = max_read
|
164
|
+
directory_request.smb2_header.credit_charge = credit_charge
|
151
165
|
|
152
166
|
directory_request = set_header_fields(directory_request)
|
153
167
|
|
154
168
|
files = []
|
155
|
-
|
156
169
|
loop do
|
157
170
|
response = client.send_recv(directory_request, encrypt: @tree_connect_encrypt_data)
|
158
171
|
directory_response = RubySMB::SMB2::Packet::QueryDirectoryResponse.read(response)
|
@@ -160,8 +173,7 @@ module RubySMB
|
|
160
173
|
raise RubySMB::Error::InvalidPacket.new(
|
161
174
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
162
175
|
expected_cmd: RubySMB::SMB2::Packet::QueryDirectoryResponse::COMMAND,
|
163
|
-
|
164
|
-
received_cmd: directory_response.smb2_header.command
|
176
|
+
packet: directory_response
|
165
177
|
)
|
166
178
|
end
|
167
179
|
|
@@ -205,8 +217,7 @@ module RubySMB
|
|
205
217
|
raise RubySMB::Error::InvalidPacket.new(
|
206
218
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
207
219
|
expected_cmd: RubySMB::SMB2::Packet::CreateResponse::COMMAND,
|
208
|
-
|
209
|
-
received_cmd: response.smb2_header.command
|
220
|
+
packet: response
|
210
221
|
)
|
211
222
|
end
|
212
223
|
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
@@ -256,7 +267,6 @@ module RubySMB
|
|
256
267
|
# @return [RubySMB::SMB2::Packet] the modified packet.
|
257
268
|
def set_header_fields(request)
|
258
269
|
request.smb2_header.tree_id = id
|
259
|
-
request.smb2_header.credit_charge = 1
|
260
270
|
request.smb2_header.credits = 256
|
261
271
|
request
|
262
272
|
end
|
data/lib/ruby_smb/version.rb
CHANGED
@@ -65,6 +65,8 @@ RSpec.describe RubySMB::Client do
|
|
65
65
|
it { is_expected.to respond_to :verify_signature }
|
66
66
|
it { is_expected.to respond_to :auth_user }
|
67
67
|
it { is_expected.to respond_to :last_file_id }
|
68
|
+
it { is_expected.to respond_to :pid }
|
69
|
+
it { is_expected.to respond_to :server_supports_multi_credit }
|
68
70
|
|
69
71
|
describe '#initialize' do
|
70
72
|
it 'should raise an ArgumentError without a valid dispatcher' do
|
@@ -141,6 +143,18 @@ RSpec.describe RubySMB::Client do
|
|
141
143
|
it 'sets the max_buffer_size to MAX_BUFFER_SIZE' do
|
142
144
|
expect(client.max_buffer_size).to eq RubySMB::Client::MAX_BUFFER_SIZE
|
143
145
|
end
|
146
|
+
|
147
|
+
it 'sets the server_supports_multi_credit to false' do
|
148
|
+
expect(client.server_supports_multi_credit).to be false
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'sets the pid to a random value' do
|
152
|
+
100.times do
|
153
|
+
previous_pid = client.pid
|
154
|
+
client = described_class.new(dispatcher, username: username, password: password)
|
155
|
+
expect(client.pid).to_not eq(previous_pid)
|
156
|
+
end
|
157
|
+
end
|
144
158
|
end
|
145
159
|
|
146
160
|
describe '#echo' do
|
@@ -184,11 +198,13 @@ RSpec.describe RubySMB::Client do
|
|
184
198
|
describe '#send_recv' do
|
185
199
|
let(:smb1_request) { RubySMB::SMB1::Packet::TreeConnectRequest.new }
|
186
200
|
let(:smb2_request) { RubySMB::SMB2::Packet::TreeConnectRequest.new }
|
201
|
+
let(:smb2_header) { RubySMB::SMB2::SMB2Header.new }
|
187
202
|
|
188
203
|
before(:each) do
|
189
204
|
allow(client).to receive(:is_status_pending?).and_return(false)
|
190
205
|
allow(dispatcher).to receive(:send_packet).and_return(nil)
|
191
206
|
allow(dispatcher).to receive(:recv_packet).and_return('A')
|
207
|
+
allow(RubySMB::SMB2::SMB2Header).to receive(:read).and_return(smb2_header)
|
192
208
|
end
|
193
209
|
|
194
210
|
context 'when signing' do
|
@@ -199,9 +215,10 @@ RSpec.describe RubySMB::Client do
|
|
199
215
|
|
200
216
|
context 'with an SMB2 packet' do
|
201
217
|
it 'does not sign a SessionSetupRequest packet' do
|
218
|
+
allow(smb2_client).to receive(:is_status_pending?).and_return(false)
|
202
219
|
expect(smb2_client).to_not receive(:smb2_sign)
|
203
220
|
expect(smb2_client).to_not receive(:smb3_sign)
|
204
|
-
|
221
|
+
smb2_client.send_recv(RubySMB::SMB2::Packet::SessionSetupRequest.new)
|
205
222
|
end
|
206
223
|
|
207
224
|
it 'calls #smb2_sign if it is an SMB2 client' do
|
@@ -229,6 +246,29 @@ RSpec.describe RubySMB::Client do
|
|
229
246
|
expect(smb1_client).to_not receive(:is_status_pending?)
|
230
247
|
smb1_client.send_recv(smb1_request)
|
231
248
|
end
|
249
|
+
|
250
|
+
it 'set the #uid SMB header when #user_id is defined' do
|
251
|
+
smb1_client.user_id = 333
|
252
|
+
smb1_client.send_recv(smb1_request)
|
253
|
+
expect(smb1_request.smb_header.uid).to eq(333)
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'does not set the #uid SMB header when #user_id is not defined' do
|
257
|
+
smb1_client.send_recv(smb1_request)
|
258
|
+
expect(smb1_request.smb_header.uid).to eq(0)
|
259
|
+
end
|
260
|
+
|
261
|
+
it 'set the #pid SMB header when #pid is defined' do
|
262
|
+
smb1_client.pid = 333
|
263
|
+
smb1_client.send_recv(smb1_request)
|
264
|
+
expect(smb1_request.smb_header.pid_low).to eq(333)
|
265
|
+
end
|
266
|
+
|
267
|
+
it 'does not set the #pid SMB header when #pid is not defined' do
|
268
|
+
smb1_client.pid = nil
|
269
|
+
smb1_client.send_recv(smb1_request)
|
270
|
+
expect(smb1_request.smb_header.pid_low).to eq(0)
|
271
|
+
end
|
232
272
|
end
|
233
273
|
|
234
274
|
context 'with SMB2' do
|
@@ -251,10 +291,8 @@ RSpec.describe RubySMB::Client do
|
|
251
291
|
context 'with a SessionSetupRequest' do
|
252
292
|
it 'does not encrypt/decrypt' do
|
253
293
|
request = RubySMB::SMB2::Packet::SessionSetupRequest.new
|
254
|
-
expect(smb3_client).
|
255
|
-
expect(smb3_client).
|
256
|
-
expect(dispatcher).to receive(:send_packet).with(request)
|
257
|
-
expect(dispatcher).to receive(:recv_packet)
|
294
|
+
expect(smb3_client).to receive(:send_packet).with(request, encrypt: false)
|
295
|
+
expect(smb3_client).to receive(:recv_packet).with(encrypt: false)
|
258
296
|
smb3_client.send_recv(request)
|
259
297
|
end
|
260
298
|
end
|
@@ -262,17 +300,15 @@ RSpec.describe RubySMB::Client do
|
|
262
300
|
context 'with a NegotiateRequest' do
|
263
301
|
it 'does not encrypt/decrypt' do
|
264
302
|
request = RubySMB::SMB2::Packet::NegotiateRequest.new
|
265
|
-
expect(smb3_client).
|
266
|
-
expect(smb3_client).
|
267
|
-
expect(dispatcher).to receive(:send_packet).with(request)
|
268
|
-
expect(dispatcher).to receive(:recv_packet)
|
303
|
+
expect(smb3_client).to receive(:send_packet).with(request, encrypt: false)
|
304
|
+
expect(smb3_client).to receive(:recv_packet).with(encrypt: false)
|
269
305
|
smb3_client.send_recv(request)
|
270
306
|
end
|
271
307
|
end
|
272
308
|
|
273
309
|
it 'encrypts and decrypts' do
|
274
|
-
expect(smb3_client).to receive(:
|
275
|
-
expect(smb3_client).to receive(:
|
310
|
+
expect(smb3_client).to receive(:send_packet).with(smb2_request, encrypt: true)
|
311
|
+
expect(smb3_client).to receive(:recv_packet).with(encrypt: true)
|
276
312
|
smb3_client.send_recv(smb2_request)
|
277
313
|
end
|
278
314
|
|
@@ -280,12 +316,44 @@ RSpec.describe RubySMB::Client do
|
|
280
316
|
it 'waits 1 second and reads/decrypts again' do
|
281
317
|
allow(smb3_client).to receive(:is_status_pending?).and_return(true, false)
|
282
318
|
expect(smb3_client).to receive(:sleep).with(1)
|
283
|
-
expect(smb3_client).to receive(:
|
284
|
-
expect(smb3_client).to receive(:
|
319
|
+
expect(smb3_client).to receive(:send_packet).with(smb2_request, encrypt: true)
|
320
|
+
expect(smb3_client).to receive(:recv_packet).with(encrypt: true).twice
|
285
321
|
smb3_client.send_recv(smb2_request)
|
286
322
|
end
|
287
323
|
end
|
288
324
|
end
|
325
|
+
|
326
|
+
it 'increments the sequence counter if signing is required and the session key exist' do
|
327
|
+
allow(smb2_client).to receive(:is_status_pending?).and_return(false)
|
328
|
+
smb2_client.signing_required = true
|
329
|
+
smb2_client.session_key = 'key'
|
330
|
+
smb2_client.sequence_counter = 0
|
331
|
+
smb2_client.send_recv(smb2_request)
|
332
|
+
expect(smb2_client.sequence_counter).to eq(1)
|
333
|
+
end
|
334
|
+
|
335
|
+
it 'updates #smb2_message_id with SMB2 header #credit_charge if the server supports multi credits' do
|
336
|
+
allow(smb2_client).to receive(:is_status_pending?).and_return(false)
|
337
|
+
smb2_client.smb2_message_id = 0
|
338
|
+
smb2_client.server_supports_multi_credit = true
|
339
|
+
smb2_header.credit_charge = 5
|
340
|
+
smb2_client.send_recv(smb2_request)
|
341
|
+
expect(smb2_client.smb2_message_id).to eq(5)
|
342
|
+
end
|
343
|
+
|
344
|
+
it 'does not update #msb2_message_id with SMB2 header #credit_charge if the server does not support multi credits' do
|
345
|
+
allow(smb2_client).to receive(:is_status_pending?).and_return(false)
|
346
|
+
smb2_client.smb2_message_id = 0
|
347
|
+
smb2_client.server_supports_multi_credit = false
|
348
|
+
smb2_header.credit_charge = 5
|
349
|
+
smb2_client.send_recv(smb2_request)
|
350
|
+
expect(smb2_client.smb2_message_id).to eq(1)
|
351
|
+
end
|
352
|
+
|
353
|
+
it 'ignores errors thrown when parsing the SMB2 header' do
|
354
|
+
allow(RubySMB::SMB2::SMB2Header).to receive(:read).and_raise(IOError)
|
355
|
+
expect { smb2_client.send_recv(smb2_request) }.to_not raise_error
|
356
|
+
end
|
289
357
|
end
|
290
358
|
|
291
359
|
describe '#is_status_pending?' do
|
@@ -297,17 +365,17 @@ RSpec.describe RubySMB::Client do
|
|
297
365
|
}
|
298
366
|
|
299
367
|
it 'returns true when the response has a STATUS_PENDING status code and the async_command flag set' do
|
300
|
-
expect(client.is_status_pending?(response.
|
368
|
+
expect(client.is_status_pending?(response.smb2_header)).to be true
|
301
369
|
end
|
302
370
|
|
303
371
|
it 'returns false when the response has a STATUS_PENDING status code and the async_command flag not set' do
|
304
372
|
response.smb2_header.flags.async_command = 0
|
305
|
-
expect(client.is_status_pending?(response.
|
373
|
+
expect(client.is_status_pending?(response.smb2_header)).to be false
|
306
374
|
end
|
307
375
|
|
308
376
|
it 'returns false when the response has no STATUS_PENDING status code but the async_command flag set' do
|
309
377
|
response.smb2_header.nt_status= WindowsError::NTStatus::STATUS_SUCCESS.value
|
310
|
-
expect(client.is_status_pending?(response.
|
378
|
+
expect(client.is_status_pending?(response.smb2_header)).to be false
|
311
379
|
end
|
312
380
|
end
|
313
381
|
|
@@ -344,35 +412,47 @@ RSpec.describe RubySMB::Client do
|
|
344
412
|
end
|
345
413
|
end
|
346
414
|
|
347
|
-
describe '#
|
415
|
+
describe '#send_packet' do
|
348
416
|
let(:packet) { RubySMB::SMB2::Packet::SessionSetupRequest.new }
|
349
417
|
before :example do
|
350
418
|
allow(dispatcher).to receive(:send_packet)
|
351
419
|
client.dialect = '0x0300'
|
352
420
|
end
|
353
421
|
|
354
|
-
it '
|
355
|
-
expect(client).
|
356
|
-
client.
|
422
|
+
it 'does not encrypt the packet' do
|
423
|
+
expect(client).to_not receive(:smb3_encrypt)
|
424
|
+
client.send_packet(packet)
|
357
425
|
end
|
358
426
|
|
359
|
-
it '
|
360
|
-
|
361
|
-
expect
|
362
|
-
RubySMB::Error::EncryptionError,
|
363
|
-
"Error while encrypting #{packet.class.name} packet (SMB 0x0300): Error"
|
364
|
-
)
|
427
|
+
it 'sends the packet through the dispatcher' do
|
428
|
+
client.send_packet(packet)
|
429
|
+
expect(dispatcher).to have_received(:send_packet).with(packet)
|
365
430
|
end
|
366
431
|
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
432
|
+
context 'with encryption' do
|
433
|
+
it 'creates a Transform request' do
|
434
|
+
expect(client).to receive(:smb3_encrypt).with(packet.to_binary_s)
|
435
|
+
client.send_packet(packet, encrypt: true)
|
436
|
+
end
|
437
|
+
|
438
|
+
it 'raises an EncryptionError exception if an error occurs while encrypting' do
|
439
|
+
allow(client).to receive(:smb3_encrypt).and_raise(RubySMB::Error::RubySMBError.new('Error'))
|
440
|
+
expect { client.send_packet(packet, encrypt: true) }.to raise_error(
|
441
|
+
RubySMB::Error::EncryptionError,
|
442
|
+
"Error while encrypting #{packet.class.name} packet (SMB 0x0300): Error"
|
443
|
+
)
|
444
|
+
end
|
445
|
+
|
446
|
+
it 'sends the encrypted packet' do
|
447
|
+
encrypted_packet = double('Encrypted packet')
|
448
|
+
allow(client).to receive(:smb3_encrypt).and_return(encrypted_packet)
|
449
|
+
client.send_packet(packet, encrypt: true)
|
450
|
+
expect(dispatcher).to have_received(:send_packet).with(encrypted_packet)
|
451
|
+
end
|
372
452
|
end
|
373
453
|
end
|
374
454
|
|
375
|
-
describe '#
|
455
|
+
describe '#recv_packet' do
|
376
456
|
let(:packet) { RubySMB::SMB2::Packet::SessionSetupRequest.new }
|
377
457
|
before :example do
|
378
458
|
allow(dispatcher).to receive(:recv_packet).and_return(packet.to_binary_s)
|
@@ -381,42 +461,49 @@ RSpec.describe RubySMB::Client do
|
|
381
461
|
end
|
382
462
|
|
383
463
|
it 'reads the response packet' do
|
384
|
-
client.
|
464
|
+
client.recv_packet
|
385
465
|
expect(dispatcher).to have_received(:recv_packet)
|
386
466
|
end
|
387
467
|
|
388
|
-
it 'raises an
|
468
|
+
it 'raises an CommunicationError exception if an error occurs while receiving the response' do
|
389
469
|
allow(dispatcher).to receive(:recv_packet).and_raise(RubySMB::Error::CommunicationError)
|
390
|
-
expect { client.
|
391
|
-
RubySMB::Error::EncryptionError,
|
392
|
-
'Communication error with the remote host: RubySMB::Error::CommunicationError. '\
|
393
|
-
'The server supports encryption but was not able to handle the encrypted request.'
|
394
|
-
)
|
470
|
+
expect { client.recv_packet }.to raise_error(RubySMB::Error::CommunicationError)
|
395
471
|
end
|
396
472
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
473
|
+
context 'with encryption' do
|
474
|
+
it 'raises an EncryptionError exception if an error occurs while receiving the response' do
|
475
|
+
allow(dispatcher).to receive(:recv_packet).and_raise(RubySMB::Error::CommunicationError)
|
476
|
+
expect { client.recv_packet(encrypt: true) }.to raise_error(
|
477
|
+
RubySMB::Error::EncryptionError,
|
478
|
+
'Communication error with the remote host: RubySMB::Error::CommunicationError. '\
|
479
|
+
'The server supports encryption but was not able to handle the encrypted request.'
|
480
|
+
)
|
481
|
+
end
|
401
482
|
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
483
|
+
it 'parses the response as a Transform response packet' do
|
484
|
+
expect(RubySMB::SMB2::Packet::TransformHeader).to receive(:read).with(packet.to_binary_s)
|
485
|
+
client.recv_packet(encrypt: true)
|
486
|
+
end
|
406
487
|
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
expect(client).to have_received(:smb3_decrypt).with(transform)
|
412
|
-
end
|
488
|
+
it 'raises an InvalidPacket exception if an error occurs while parsing the response' do
|
489
|
+
allow(RubySMB::SMB2::Packet::TransformHeader).to receive(:read).and_raise(IOError)
|
490
|
+
expect { client.recv_packet(encrypt: true) }.to raise_error(RubySMB::Error::InvalidPacket, 'Not a SMB2 TransformHeader packet')
|
491
|
+
end
|
413
492
|
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
493
|
+
it 'decrypts the Transform response packet' do
|
494
|
+
transform = double('Transform header packet')
|
495
|
+
allow(RubySMB::SMB2::Packet::TransformHeader).to receive(:read).and_return(transform)
|
496
|
+
client.recv_packet(encrypt: true)
|
497
|
+
expect(client).to have_received(:smb3_decrypt).with(transform)
|
498
|
+
end
|
499
|
+
|
500
|
+
it 'raises an EncryptionError exception if an error occurs while decrypting' do
|
501
|
+
allow(client).to receive(:smb3_decrypt).and_raise(RubySMB::Error::RubySMBError)
|
502
|
+
expect { client.recv_packet(encrypt: true) }.to raise_error(
|
503
|
+
RubySMB::Error::EncryptionError,
|
504
|
+
'Error while decrypting RubySMB::SMB2::Packet::TransformHeader packet (SMB 0x0300}): RubySMB::Error::RubySMBError'
|
505
|
+
)
|
506
|
+
end
|
420
507
|
end
|
421
508
|
end
|
422
509
|
|
@@ -667,7 +754,7 @@ RSpec.describe RubySMB::Client do
|
|
667
754
|
expect(session_packet.session_header.session_packet_type).to eq RubySMB::Nbss::SESSION_REQUEST
|
668
755
|
expect(session_packet.called_name).to eq called_name
|
669
756
|
expect(session_packet.calling_name).to eq calling_name
|
670
|
-
expect(session_packet.session_header.
|
757
|
+
expect(session_packet.session_header.stream_protocol_length).to eq(
|
671
758
|
session_packet.called_name.to_binary_s.size + session_packet.calling_name.to_binary_s.size
|
672
759
|
)
|
673
760
|
end
|
@@ -1036,6 +1123,19 @@ RSpec.describe RubySMB::Client do
|
|
1036
1123
|
it 'returns the string \'SMB2\'' do
|
1037
1124
|
expect(client.parse_negotiate_response(smb2_response)).to eq ('SMB2')
|
1038
1125
|
end
|
1126
|
+
|
1127
|
+
it 'sets #server_supports_multi_credit to true when the response has #large_mtu capability set' do
|
1128
|
+
smb2_response.capabilities.large_mtu = 1
|
1129
|
+
client.parse_negotiate_response(smb2_response)
|
1130
|
+
expect(client.server_supports_multi_credit).to be true
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
it 'sets #server_supports_multi_credit to false when the dialect is 0x0202' do
|
1134
|
+
smb2_response.dialect_revision = 0x0202
|
1135
|
+
smb2_response.capabilities.large_mtu = 1 # just to make sure it won't affect the result
|
1136
|
+
client.parse_negotiate_response(smb2_response)
|
1137
|
+
expect(client.server_supports_multi_credit).to be false
|
1138
|
+
end
|
1039
1139
|
end
|
1040
1140
|
|
1041
1141
|
context 'when SMB3 was negotiated' do
|
@@ -1060,11 +1160,22 @@ RSpec.describe RubySMB::Client do
|
|
1060
1160
|
expect(client.parse_negotiate_response(smb3_response)).to eq ('SMB3')
|
1061
1161
|
end
|
1062
1162
|
|
1163
|
+
it 'sets #server_supports_multi_credit to true when the response has #large_mtu capability set' do
|
1164
|
+
smb3_response.capabilities.large_mtu = 1
|
1165
|
+
client.parse_negotiate_response(smb3_response)
|
1166
|
+
expect(client.server_supports_multi_credit).to be true
|
1167
|
+
end
|
1168
|
+
|
1063
1169
|
context 'when the server supports encryption' do
|
1064
1170
|
before :example do
|
1065
1171
|
smb3_response.capabilities.encryption = 1
|
1066
1172
|
end
|
1067
1173
|
|
1174
|
+
it 'sets the expected encryption algorithm' do
|
1175
|
+
client.parse_negotiate_response(smb3_response)
|
1176
|
+
expect(client.encryption_algorithm).to eq(RubySMB::SMB2::EncryptionCapabilities::ENCRYPTION_ALGORITHM_MAP[RubySMB::SMB2::EncryptionCapabilities::AES_128_CCM])
|
1177
|
+
end
|
1178
|
+
|
1068
1179
|
it 'keeps session encryption enabled if it was already' do
|
1069
1180
|
client.session_encrypt_data = true
|
1070
1181
|
client.parse_negotiate_response(smb3_response)
|
@@ -1166,26 +1277,15 @@ RSpec.describe RubySMB::Client do
|
|
1166
1277
|
end
|
1167
1278
|
end
|
1168
1279
|
|
1169
|
-
['0x0300', '0x0302'].each do |dialect|
|
1170
|
-
context "with #{dialect} dialect" do
|
1171
|
-
before :example do
|
1172
|
-
client.dialect = dialect
|
1173
|
-
end
|
1174
|
-
|
1175
|
-
it 'sets the expected encryption algorithm' do
|
1176
|
-
client.negotiate
|
1177
|
-
expect(client.encryption_algorithm).to eq(RubySMB::SMB2::EncryptionCapabilities::ENCRYPTION_ALGORITHM_MAP[RubySMB::SMB2::EncryptionCapabilities::AES_128_CCM])
|
1178
|
-
end
|
1179
|
-
end
|
1180
|
-
end
|
1181
|
-
|
1182
1280
|
context "with 0x0311 dialect" do
|
1183
|
-
it 'calls #
|
1281
|
+
it 'calls #parse_negotiate_response and updates the preauth hash' do
|
1184
1282
|
client.dialect = '0x0311'
|
1185
1283
|
request_packet = client.smb2_3_negotiate_request
|
1186
1284
|
allow(client).to receive(:negotiate_request).and_return(request_packet)
|
1187
1285
|
allow(client).to receive(:negotiate_response).and_return(smb3_response)
|
1188
|
-
expect(client).to receive(:
|
1286
|
+
expect(client).to receive(:parse_negotiate_response).with(smb3_response)
|
1287
|
+
expect(client).to receive(:update_preauth_hash).with(request_packet)
|
1288
|
+
expect(client).to receive(:update_preauth_hash).with(smb3_response)
|
1189
1289
|
client.negotiate
|
1190
1290
|
end
|
1191
1291
|
end
|
@@ -1258,7 +1358,7 @@ RSpec.describe RubySMB::Client do
|
|
1258
1358
|
end
|
1259
1359
|
end
|
1260
1360
|
|
1261
|
-
describe '#
|
1361
|
+
describe '#parse_smb3_capabilities' do
|
1262
1362
|
let(:request_packet) { client.smb2_3_negotiate_request }
|
1263
1363
|
let(:smb3_response) { RubySMB::SMB2::Packet::NegotiateResponse.new(dialect_revision: 0x311) }
|
1264
1364
|
let(:nc_encryption) do
|
@@ -1285,7 +1385,7 @@ RSpec.describe RubySMB::Client do
|
|
1285
1385
|
context 'when selecting the integrity hash algorithm' do
|
1286
1386
|
context 'with one algorithm' do
|
1287
1387
|
it 'selects the expected algorithm' do
|
1288
|
-
smb3_client.
|
1388
|
+
smb3_client.parse_smb3_capabilities(smb3_response)
|
1289
1389
|
expect(smb3_client.preauth_integrity_hash_algorithm).to eq('SHA512')
|
1290
1390
|
end
|
1291
1391
|
end
|
@@ -1296,7 +1396,7 @@ RSpec.describe RubySMB::Client do
|
|
1296
1396
|
RubySMB::SMB2::NegotiateContext::SMB2_PREAUTH_INTEGRITY_CAPABILITIES
|
1297
1397
|
)
|
1298
1398
|
nc.data.hash_algorithms << 3
|
1299
|
-
smb3_client.
|
1399
|
+
smb3_client.parse_smb3_capabilities(smb3_response)
|
1300
1400
|
expect(smb3_client.preauth_integrity_hash_algorithm).to eq('SHA512')
|
1301
1401
|
end
|
1302
1402
|
end
|
@@ -1305,7 +1405,7 @@ RSpec.describe RubySMB::Client do
|
|
1305
1405
|
it 'raises the expected exception' do
|
1306
1406
|
smb3_response = RubySMB::SMB2::Packet::NegotiateResponse.new(dialect_revision: 0x311)
|
1307
1407
|
smb3_response.add_negotiate_context(nc_encryption)
|
1308
|
-
expect { smb3_client.
|
1408
|
+
expect { smb3_client.parse_smb3_capabilities(smb3_response) }.to raise_error(
|
1309
1409
|
RubySMB::Error::EncryptionError,
|
1310
1410
|
'Unable to retrieve the Preauth Integrity Hash Algorithm from the Negotiate response'
|
1311
1411
|
)
|
@@ -1321,7 +1421,7 @@ RSpec.describe RubySMB::Client do
|
|
1321
1421
|
)
|
1322
1422
|
nc.data.hash_algorithms << 5
|
1323
1423
|
smb3_response.add_negotiate_context(nc)
|
1324
|
-
expect { smb3_client.
|
1424
|
+
expect { smb3_client.parse_smb3_capabilities(smb3_response) }.to raise_error(
|
1325
1425
|
RubySMB::Error::EncryptionError,
|
1326
1426
|
'Unable to retrieve the Preauth Integrity Hash Algorithm from the Negotiate response'
|
1327
1427
|
)
|
@@ -1332,7 +1432,7 @@ RSpec.describe RubySMB::Client do
|
|
1332
1432
|
context 'when selecting the encryption algorithm' do
|
1333
1433
|
context 'with one algorithm' do
|
1334
1434
|
it 'selects the expected algorithm' do
|
1335
|
-
smb3_client.
|
1435
|
+
smb3_client.parse_smb3_capabilities(smb3_response)
|
1336
1436
|
expect(smb3_client.encryption_algorithm).to eq('AES-128-CCM')
|
1337
1437
|
end
|
1338
1438
|
end
|
@@ -1343,7 +1443,7 @@ RSpec.describe RubySMB::Client do
|
|
1343
1443
|
RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES
|
1344
1444
|
)
|
1345
1445
|
nc.data.ciphers << RubySMB::SMB2::EncryptionCapabilities::AES_128_GCM
|
1346
|
-
smb3_client.
|
1446
|
+
smb3_client.parse_smb3_capabilities(smb3_response)
|
1347
1447
|
expect(smb3_client.encryption_algorithm).to eq('AES-128-GCM')
|
1348
1448
|
end
|
1349
1449
|
|
@@ -1352,7 +1452,7 @@ RSpec.describe RubySMB::Client do
|
|
1352
1452
|
RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES
|
1353
1453
|
)
|
1354
1454
|
nc.data.ciphers << 3
|
1355
|
-
smb3_client.
|
1455
|
+
smb3_client.parse_smb3_capabilities(smb3_response)
|
1356
1456
|
expect(smb3_client.encryption_algorithm).to eq('AES-128-CCM')
|
1357
1457
|
end
|
1358
1458
|
|
@@ -1361,7 +1461,7 @@ RSpec.describe RubySMB::Client do
|
|
1361
1461
|
RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES
|
1362
1462
|
)
|
1363
1463
|
nc.data.ciphers << RubySMB::SMB2::EncryptionCapabilities::AES_128_GCM
|
1364
|
-
smb3_client.
|
1464
|
+
smb3_client.parse_smb3_capabilities(smb3_response)
|
1365
1465
|
expect(smb3_client.server_encryption_algorithms).to eq([1, 2])
|
1366
1466
|
end
|
1367
1467
|
end
|
@@ -1370,7 +1470,7 @@ RSpec.describe RubySMB::Client do
|
|
1370
1470
|
it 'raises the expected exception' do
|
1371
1471
|
smb3_response = RubySMB::SMB2::Packet::NegotiateResponse.new(dialect_revision: 0x311)
|
1372
1472
|
smb3_response.add_negotiate_context(nc_integrity)
|
1373
|
-
expect { smb3_client.
|
1473
|
+
expect { smb3_client.parse_smb3_capabilities(smb3_response) }.to raise_error(
|
1374
1474
|
RubySMB::Error::EncryptionError,
|
1375
1475
|
'Unable to retrieve the encryption cipher list supported by the server from the Negotiate response'
|
1376
1476
|
)
|
@@ -1386,7 +1486,7 @@ RSpec.describe RubySMB::Client do
|
|
1386
1486
|
)
|
1387
1487
|
nc.data.ciphers << 14
|
1388
1488
|
smb3_response.add_negotiate_context(nc)
|
1389
|
-
expect { smb3_client.
|
1489
|
+
expect { smb3_client.parse_smb3_capabilities(smb3_response) }.to raise_error(
|
1390
1490
|
RubySMB::Error::EncryptionError,
|
1391
1491
|
'Unable to retrieve the encryption cipher list supported by the server from the Negotiate response'
|
1392
1492
|
)
|
@@ -1404,16 +1504,10 @@ RSpec.describe RubySMB::Client do
|
|
1404
1504
|
nc.data.compression_algorithms << RubySMB::SMB2::CompressionCapabilities::LZ77_Huffman
|
1405
1505
|
nc.data.compression_algorithms << RubySMB::SMB2::CompressionCapabilities::Pattern_V1
|
1406
1506
|
smb3_response.add_negotiate_context(nc)
|
1407
|
-
smb3_client.
|
1507
|
+
smb3_client.parse_smb3_capabilities(smb3_response)
|
1408
1508
|
expect(smb3_client.server_compression_algorithms).to eq([1, 2, 3, 4])
|
1409
1509
|
end
|
1410
1510
|
end
|
1411
|
-
|
1412
|
-
it 'updates the preauth hash' do
|
1413
|
-
expect(smb3_client).to receive(:update_preauth_hash).with(request_packet)
|
1414
|
-
expect(smb3_client).to receive(:update_preauth_hash).with(smb3_response)
|
1415
|
-
smb3_client.parse_smb3_encryption_data(request_packet, smb3_response)
|
1416
|
-
end
|
1417
1511
|
end
|
1418
1512
|
end
|
1419
1513
|
end
|
@@ -2265,7 +2359,7 @@ RSpec.describe RubySMB::Client do
|
|
2265
2359
|
let(:named_pipe){ double("Named Pipe") }
|
2266
2360
|
|
2267
2361
|
before :example do
|
2268
|
-
allow(tree).to receive(:
|
2362
|
+
allow(tree).to receive(:open_pipe).and_return(named_pipe)
|
2269
2363
|
allow(named_pipe).to receive(:net_share_enum_all)
|
2270
2364
|
end
|
2271
2365
|
|
@@ -2280,8 +2374,8 @@ RSpec.describe RubySMB::Client do
|
|
2280
2374
|
smb1_client.net_share_enum_all(sock.peeraddr)
|
2281
2375
|
end
|
2282
2376
|
|
2283
|
-
it 'it calls the Tree #
|
2284
|
-
expect(tree).to receive(:
|
2377
|
+
it 'it calls the Tree #open_pipe method to open "srvsvc" named pipe' do
|
2378
|
+
expect(tree).to receive(:open_pipe).with(filename: "srvsvc", write: true, read: true).and_return(named_pipe)
|
2285
2379
|
smb1_client.net_share_enum_all(sock.peeraddr)
|
2286
2380
|
end
|
2287
2381
|
|
@@ -2303,8 +2397,8 @@ RSpec.describe RubySMB::Client do
|
|
2303
2397
|
smb2_client.net_share_enum_all(sock.peeraddr)
|
2304
2398
|
end
|
2305
2399
|
|
2306
|
-
it 'it calls the Tree #
|
2307
|
-
expect(tree).to receive(:
|
2400
|
+
it 'it calls the Tree #open_pipe method to open "srvsvc" named pipe' do
|
2401
|
+
expect(tree).to receive(:open_pipe).with(filename: "srvsvc", write: true, read: true).and_return(named_pipe)
|
2308
2402
|
smb2_client.net_share_enum_all(sock.peeraddr)
|
2309
2403
|
end
|
2310
2404
|
|
@@ -2385,7 +2479,7 @@ RSpec.describe RubySMB::Client do
|
|
2385
2479
|
before :example do
|
2386
2480
|
allow(ipc_tree).to receive_messages(
|
2387
2481
|
:share => share,
|
2388
|
-
:
|
2482
|
+
:open_pipe => named_pipe
|
2389
2483
|
)
|
2390
2484
|
allow(client).to receive(:tree_connect).and_return(ipc_tree)
|
2391
2485
|
end
|
@@ -2407,9 +2501,9 @@ RSpec.describe RubySMB::Client do
|
|
2407
2501
|
expect(client).to have_received(:tree_connect).with(share)
|
2408
2502
|
end
|
2409
2503
|
|
2410
|
-
it 'open \'winreg\'
|
2504
|
+
it 'open \'winreg\' pipe on the IPC$ Tree' do
|
2411
2505
|
client.connect_to_winreg(host)
|
2412
|
-
expect(ipc_tree).to have_received(:
|
2506
|
+
expect(ipc_tree).to have_received(:open_pipe).with(filename: "winreg", write: true, read: true)
|
2413
2507
|
end
|
2414
2508
|
|
2415
2509
|
it 'returns the expected opened named pipe' do
|