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.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -1
  4. data/examples/anonymous_auth.rb +3 -3
  5. data/examples/append_file.rb +10 -8
  6. data/examples/authenticate.rb +9 -5
  7. data/examples/delete_file.rb +8 -6
  8. data/examples/enum_registry_key.rb +5 -4
  9. data/examples/enum_registry_values.rb +5 -4
  10. data/examples/list_directory.rb +8 -6
  11. data/examples/negotiate_with_netbios_service.rb +9 -5
  12. data/examples/net_share_enum_all.rb +6 -4
  13. data/examples/pipes.rb +11 -12
  14. data/examples/query_service_status.rb +64 -0
  15. data/examples/read_file.rb +8 -6
  16. data/examples/read_registry_key_value.rb +6 -5
  17. data/examples/rename_file.rb +9 -7
  18. data/examples/tree_connect.rb +7 -5
  19. data/examples/write_file.rb +9 -7
  20. data/lib/ruby_smb/client.rb +81 -48
  21. data/lib/ruby_smb/client/authentication.rb +5 -10
  22. data/lib/ruby_smb/client/echo.rb +2 -4
  23. data/lib/ruby_smb/client/negotiation.rb +21 -14
  24. data/lib/ruby_smb/client/tree_connect.rb +2 -4
  25. data/lib/ruby_smb/client/utils.rb +16 -10
  26. data/lib/ruby_smb/client/winreg.rb +1 -1
  27. data/lib/ruby_smb/dcerpc.rb +4 -0
  28. data/lib/ruby_smb/dcerpc/error.rb +3 -0
  29. data/lib/ruby_smb/dcerpc/ndr.rb +306 -44
  30. data/lib/ruby_smb/dcerpc/netlogon.rb +101 -0
  31. data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request.rb +28 -0
  32. data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response.rb +26 -0
  33. data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request.rb +27 -0
  34. data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response.rb +23 -0
  35. data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request.rb +25 -0
  36. data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response.rb +24 -0
  37. data/lib/ruby_smb/dcerpc/request.rb +19 -0
  38. data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +34 -0
  39. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +9 -6
  40. data/lib/ruby_smb/dcerpc/svcctl.rb +479 -0
  41. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +48 -0
  42. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +26 -0
  43. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request.rb +25 -0
  44. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +26 -0
  45. data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +26 -0
  46. data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +26 -0
  47. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +35 -0
  48. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +23 -0
  49. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +31 -0
  50. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +23 -0
  51. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +25 -0
  52. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +44 -0
  53. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_request.rb +23 -0
  54. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +27 -0
  55. data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +25 -0
  56. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +27 -0
  57. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +25 -0
  58. data/lib/ruby_smb/dcerpc/winreg.rb +98 -17
  59. data/lib/ruby_smb/dcerpc/winreg/create_key_request.rb +73 -0
  60. data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +36 -0
  61. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +1 -1
  62. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +1 -1
  63. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +1 -1
  64. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +4 -4
  65. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +1 -1
  66. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +7 -6
  67. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +10 -10
  68. data/lib/ruby_smb/dcerpc/winreg/save_key_request.rb +37 -0
  69. data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +23 -0
  70. data/lib/ruby_smb/dispatcher/base.rb +1 -1
  71. data/lib/ruby_smb/dispatcher/socket.rb +1 -1
  72. data/lib/ruby_smb/error.rb +21 -5
  73. data/lib/ruby_smb/field/stringz16.rb +17 -1
  74. data/lib/ruby_smb/generic_packet.rb +11 -1
  75. data/lib/ruby_smb/nbss/session_header.rb +4 -4
  76. data/lib/ruby_smb/smb1/file.rb +10 -25
  77. data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +0 -1
  78. data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +0 -1
  79. data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +1 -2
  80. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +1 -13
  81. data/lib/ruby_smb/smb1/pipe.rb +8 -6
  82. data/lib/ruby_smb/smb1/tree.rb +13 -9
  83. data/lib/ruby_smb/smb2/file.rb +33 -33
  84. data/lib/ruby_smb/smb2/pipe.rb +9 -6
  85. data/lib/ruby_smb/smb2/tree.rb +21 -11
  86. data/lib/ruby_smb/version.rb +1 -1
  87. data/spec/lib/ruby_smb/client_spec.rb +195 -101
  88. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +1396 -77
  89. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request_spec.rb +69 -0
  90. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response_spec.rb +53 -0
  91. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request_spec.rb +69 -0
  92. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response_spec.rb +37 -0
  93. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request_spec.rb +45 -0
  94. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response_spec.rb +37 -0
  95. data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +161 -0
  96. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +49 -12
  97. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +191 -0
  98. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +38 -0
  99. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request_spec.rb +30 -0
  100. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +38 -0
  101. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +39 -0
  102. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +38 -0
  103. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +78 -0
  104. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +38 -0
  105. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +59 -0
  106. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +38 -0
  107. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +38 -0
  108. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +152 -0
  109. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_request_spec.rb +30 -0
  110. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +38 -0
  111. data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +72 -0
  112. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +46 -0
  113. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +30 -0
  114. data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +512 -0
  115. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +110 -0
  116. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +44 -0
  117. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +0 -4
  118. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +2 -2
  119. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +2 -2
  120. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +9 -4
  121. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +0 -4
  122. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +17 -17
  123. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +11 -23
  124. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +57 -0
  125. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +22 -0
  126. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +227 -41
  127. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +10 -10
  128. data/spec/lib/ruby_smb/error_spec.rb +34 -5
  129. data/spec/lib/ruby_smb/field/stringz16_spec.rb +12 -0
  130. data/spec/lib/ruby_smb/generic_packet_spec.rb +7 -0
  131. data/spec/lib/ruby_smb/nbss/session_header_spec.rb +4 -11
  132. data/spec/lib/ruby_smb/smb1/file_spec.rb +2 -4
  133. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +0 -1
  134. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +0 -1
  135. data/spec/lib/ruby_smb/smb1/packet/trans2/open2_response_spec.rb +0 -5
  136. data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_response_spec.rb +0 -6
  137. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +30 -5
  138. data/spec/lib/ruby_smb/smb1/tree_spec.rb +22 -0
  139. data/spec/lib/ruby_smb/smb2/file_spec.rb +61 -9
  140. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +9 -5
  141. data/spec/lib/ruby_smb/smb2/tree_spec.rb +58 -1
  142. metadata +91 -2
  143. metadata.gz.sig +0 -0
@@ -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
- received_proto: response.smb_header.protocol,
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
- received_proto: response.smb_header.protocol,
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
- received_proto: response.smb_header.protocol,
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
- received_proto: response.smb_header.protocol,
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
@@ -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
- received_proto: response.smb2_header.protocol,
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
- atomic_read_size = if bytes > tree.client.server_max_read_size
119
- tree.client.server_max_read_size
120
- else
121
- bytes
122
- end
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
- received_proto: response.smb2_header.protocol,
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 < tree.client.server_max_read_size
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
- received_proto: response.smb2_header.protocol,
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
- received_proto: response.smb2_header.protocol,
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
- received_proto: response.smb2_header.protocol,
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 = if bytes > tree.client.server_max_write_size
246
- tree.client.server_max_write_size
247
- else
248
- bytes
249
- end
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
- received_proto: response.smb2_header.protocol,
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
- received_proto: response.smb2_header.protocol,
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
- received_proto: response.smb2_header.protocol,
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
@@ -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 'srvsvc'
16
+ when 'netlogon', '\\netlogon'
17
+ extend RubySMB::Dcerpc::Netlogon
18
+ when 'srvsvc', '\\srvsvc'
17
19
  extend RubySMB::Dcerpc::Srvsvc
18
- when 'winreg'
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
- received_proto: response.smb2_header.protocol,
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
- received_proto: ioctl_response.smb2_header.protocol,
101
- received_cmd: ioctl_response.smb2_header.command
104
+ packet: ioctl_response
102
105
  )
103
106
  end
104
107
  unless [WindowsError::NTStatus::STATUS_SUCCESS,
@@ -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
- received_proto: response.smb2_header.protocol,
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
- received_proto: response.smb2_header.protocol,
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
- directory_request.output_length = 65_535
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
- received_proto: directory_response.smb2_header.protocol,
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
- received_proto: response.smb2_header.protocol,
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
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '2.0.1'.freeze
2
+ VERSION = '2.0.6'.freeze
3
3
  end
@@ -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
- client.send_recv(RubySMB::SMB2::Packet::SessionSetupRequest.new)
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).to_not receive(:send_encrypt).with(request)
255
- expect(smb3_client).to_not receive(:recv_encrypt)
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).to_not receive(:send_encrypt).with(request)
266
- expect(smb3_client).to_not receive(:recv_encrypt)
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(:send_encrypt).with(smb2_request)
275
- expect(smb3_client).to receive(:recv_encrypt)
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(:send_encrypt).with(smb2_request)
284
- expect(smb3_client).to receive(:recv_encrypt).twice
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.to_binary_s)).to be true
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.to_binary_s)).to be false
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.to_binary_s)).to be false
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 '#send_encrypt' do
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 'creates a Transform request' do
355
- expect(client).to receive(:smb3_encrypt).with(packet.to_binary_s)
356
- client.send_encrypt(packet)
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 'raises an EncryptionError exception if an error occurs while encrypting' do
360
- allow(client).to receive(:smb3_encrypt).and_raise(RubySMB::Error::RubySMBError.new('Error'))
361
- expect { client.send_encrypt(packet) }.to raise_error(
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
- it 'sends the encrypted packet' do
368
- encrypted_packet = double('Encrypted packet')
369
- allow(client).to receive(:smb3_encrypt).and_return(encrypted_packet)
370
- client.send_encrypt(packet)
371
- expect(dispatcher).to have_received(:send_packet).with(encrypted_packet)
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 '#recv_encrypt' do
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.recv_encrypt
464
+ client.recv_packet
385
465
  expect(dispatcher).to have_received(:recv_packet)
386
466
  end
387
467
 
388
- it 'raises an EncryptionError exception if an error occurs while receiving the response' do
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.recv_encrypt }.to raise_error(
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
- it 'parses the response as a Transform response packet' do
398
- expect(RubySMB::SMB2::Packet::TransformHeader).to receive(:read).with(packet.to_binary_s)
399
- client.recv_encrypt
400
- end
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
- it 'raises an InvalidPacket exception if an error occurs while parsing the response' do
403
- allow(RubySMB::SMB2::Packet::TransformHeader).to receive(:read).and_raise(IOError)
404
- expect { client.recv_encrypt }.to raise_error(RubySMB::Error::InvalidPacket, 'Not a SMB2 TransformHeader packet')
405
- end
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
- it 'decrypts the Transform response packet' do
408
- transform = double('Transform header packet')
409
- allow(RubySMB::SMB2::Packet::TransformHeader).to receive(:read).and_return(transform)
410
- client.recv_encrypt
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
- it 'raises an EncryptionError exception if an error occurs while decrypting' do
415
- allow(client).to receive(:smb3_decrypt).and_raise(RubySMB::Error::RubySMBError)
416
- expect { client.recv_encrypt }.to raise_error(
417
- RubySMB::Error::EncryptionError,
418
- 'Error while decrypting RubySMB::SMB2::Packet::TransformHeader packet (SMB 0x0300}): RubySMB::Error::RubySMBError'
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.packet_length).to eq(
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 #parse_smb3_encryption_data' do
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(:parse_smb3_encryption_data).with(request_packet, smb3_response)
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 '#parse_smb3_encryption_data' do
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.parse_smb3_encryption_data(request_packet, smb3_response)
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.parse_smb3_encryption_data(request_packet, smb3_response)
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.parse_smb3_encryption_data(request_packet, smb3_response) }.to raise_error(
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.parse_smb3_encryption_data(request_packet, smb3_response) }.to raise_error(
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.parse_smb3_encryption_data(request_packet, smb3_response)
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.parse_smb3_encryption_data(request_packet, smb3_response)
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.parse_smb3_encryption_data(request_packet, smb3_response)
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.parse_smb3_encryption_data(request_packet, smb3_response)
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.parse_smb3_encryption_data(request_packet, smb3_response) }.to raise_error(
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.parse_smb3_encryption_data(request_packet, smb3_response) }.to raise_error(
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.parse_smb3_encryption_data(request_packet, smb3_response)
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(:open_file).and_return(named_pipe)
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 #open_file method to open "srvsvc" named pipe' do
2284
- expect(tree).to receive(:open_file).with(filename: "srvsvc", write: true, read: true).and_return(named_pipe)
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 #open_file method to open "srvsvc" named pipe' do
2307
- expect(tree).to receive(:open_file).with(filename: "srvsvc", write: true, read: true).and_return(named_pipe)
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
- :open_file => named_pipe
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\' file on the IPC$ Tree' do
2504
+ it 'open \'winreg\' pipe on the IPC$ Tree' do
2411
2505
  client.connect_to_winreg(host)
2412
- expect(ipc_tree).to have_received(:open_file).with(filename: "winreg", write: true, read: true)
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