ruby_smb 2.0.0 → 2.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +3 -3
  3. data.tar.gz.sig +5 -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 +117 -53
  21. data/lib/ruby_smb/client/authentication.rb +7 -12
  22. data/lib/ruby_smb/client/echo.rb +2 -4
  23. data/lib/ruby_smb/client/negotiation.rb +31 -12
  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 +3 -2
  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 +9 -24
  77. data/lib/ruby_smb/smb1/pipe.rb +8 -6
  78. data/lib/ruby_smb/smb1/tree.rb +22 -9
  79. data/lib/ruby_smb/smb2/file.rb +46 -46
  80. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +1 -1
  81. data/lib/ruby_smb/smb2/pipe.rb +9 -6
  82. data/lib/ruby_smb/smb2/tree.rb +30 -20
  83. data/lib/ruby_smb/version.rb +1 -1
  84. data/spec/lib/ruby_smb/client_spec.rb +248 -109
  85. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +1396 -77
  86. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request_spec.rb +69 -0
  87. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response_spec.rb +53 -0
  88. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request_spec.rb +69 -0
  89. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response_spec.rb +37 -0
  90. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request_spec.rb +45 -0
  91. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response_spec.rb +37 -0
  92. data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +161 -0
  93. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +49 -12
  94. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +191 -0
  95. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +38 -0
  96. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request_spec.rb +30 -0
  97. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +38 -0
  98. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +39 -0
  99. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +38 -0
  100. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +78 -0
  101. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +38 -0
  102. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +59 -0
  103. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +38 -0
  104. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +38 -0
  105. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +152 -0
  106. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_request_spec.rb +30 -0
  107. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +38 -0
  108. data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +72 -0
  109. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +46 -0
  110. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +30 -0
  111. data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +512 -0
  112. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +110 -0
  113. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +44 -0
  114. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +0 -4
  115. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +2 -2
  116. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +2 -2
  117. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +9 -4
  118. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +0 -4
  119. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +17 -17
  120. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +11 -23
  121. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +57 -0
  122. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +22 -0
  123. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +227 -41
  124. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +12 -12
  125. data/spec/lib/ruby_smb/error_spec.rb +34 -5
  126. data/spec/lib/ruby_smb/field/stringz16_spec.rb +12 -0
  127. data/spec/lib/ruby_smb/generic_packet_spec.rb +7 -0
  128. data/spec/lib/ruby_smb/nbss/session_header_spec.rb +4 -11
  129. data/spec/lib/ruby_smb/smb1/file_spec.rb +1 -3
  130. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +30 -5
  131. data/spec/lib/ruby_smb/smb1/tree_spec.rb +22 -0
  132. data/spec/lib/ruby_smb/smb2/file_spec.rb +73 -21
  133. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +9 -5
  134. data/spec/lib/ruby_smb/smb2/tree_spec.rb +64 -7
  135. metadata +91 -2
  136. metadata.gz.sig +0 -0
@@ -52,10 +52,10 @@ module RubySMB
52
52
  # @return [RubySMB::SMB2::Tree]
53
53
  attr_accessor :tree
54
54
 
55
- # Whether or not encryption is required (SMB 3.x)
56
- # @!attribute [rw] encryption_required
55
+ # Whether or not the share associated with this tree connect needs to be encrypted (SMB 3.x)
56
+ # @!attribute [rw] tree_connect_encrypt_data
57
57
  # @return [Boolean]
58
- attr_accessor :encryption_required
58
+ attr_accessor :tree_connect_encrypt_data
59
59
 
60
60
  def initialize(tree:, response:, name:, encrypt: false)
61
61
  raise ArgumentError, 'No Tree Provided' if tree.nil?
@@ -71,7 +71,7 @@ module RubySMB
71
71
  @last_write = response.last_write.to_datetime
72
72
  @size = response.end_of_file
73
73
  @size_on_disk = response.allocation_size
74
- @encryption_required = encrypt
74
+ @tree_connect_encrypt_data = encrypt
75
75
  end
76
76
 
77
77
  # Appends the supplied data to the end of the file.
@@ -89,14 +89,13 @@ module RubySMB
89
89
  # @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
90
90
  def close
91
91
  close_request = set_header_fields(RubySMB::SMB2::Packet::CloseRequest.new)
92
- raw_response = tree.client.send_recv(close_request, encrypt: @encryption_required)
92
+ raw_response = tree.client.send_recv(close_request, encrypt: @tree_connect_encrypt_data)
93
93
  response = RubySMB::SMB2::Packet::CloseResponse.read(raw_response)
94
94
  unless response.valid?
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
123
-
124
- read_request = read_packet(read_length: atomic_read_size, offset: offset)
125
- raw_response = tree.client.send_recv(read_request, encrypt: @encryption_required)
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
124
+
125
+ read_request = read_packet(read_length: atomic_read_size, offset: offset, credit_charge: credit_charge)
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)
148
- raw_response = tree.client.send_recv(read_request, encrypt: @encryption_required)
147
+ read_request = read_packet(read_length: atomic_read_size, offset: offset, credit_charge: credit_charge)
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,24 +168,25 @@ 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
 
180
181
  def send_recv_read(read_length: 0, offset: 0)
181
182
  read_request = read_packet(read_length: read_length, offset: offset)
182
- raw_response = tree.client.send_recv(read_request, encrypt: @encryption_required)
183
+ raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
183
184
  response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
184
185
  unless response.valid?
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
@@ -200,14 +200,13 @@ module RubySMB
200
200
  # @return [WindowsError::ErrorCode] the NTStatus Response code
201
201
  # @raise [RubySMB::Error::InvalidPacket] if the response is not a SetInfoResponse packet
202
202
  def delete
203
- raw_response = tree.client.send_recv(delete_packet, encrypt: @encryption_required)
203
+ raw_response = tree.client.send_recv(delete_packet, encrypt: @tree_connect_encrypt_data)
204
204
  response = RubySMB::SMB2::Packet::SetInfoResponse.read(raw_response)
205
205
  unless response.valid?
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
- raw_response = tree.client.send_recv(write_request, encrypt: @encryption_required)
253
+ write_request = write_packet(data: buffer.slice!(0, atomic_write_size), offset: offset, credit_charge: credit_charge)
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,24 +273,25 @@ 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
 
284
286
  def send_recv_write(data:'', offset: 0)
285
287
  pkt = write_packet(data: data, offset: offset)
286
- raw_response = tree.client.send_recv(pkt, encrypt: @encryption_required)
288
+ raw_response = tree.client.send_recv(pkt, encrypt: @tree_connect_encrypt_data)
287
289
  response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
288
290
  unless response.valid?
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
@@ -305,14 +306,13 @@ module RubySMB
305
306
  # @return [WindowsError::ErrorCode] the NTStatus Response code
306
307
  # @raise [RubySMB::Error::InvalidPacket] if the response is not a SetInfoResponse packet
307
308
  def rename(new_file_name)
308
- raw_response = tree.client.send_recv(rename_packet(new_file_name), encrypt: @encryption_required)
309
+ raw_response = tree.client.send_recv(rename_packet(new_file_name), encrypt: @tree_connect_encrypt_data)
309
310
  response = RubySMB::SMB2::Packet::SetInfoResponse.read(raw_response)
310
311
  unless response.valid?
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
@@ -15,7 +15,7 @@ module RubySMB
15
15
  uint16 :dialect_revision, label: 'Dialect Revision'
16
16
  uint16 :negotiate_context_count, label: 'Negotiate Context Count', initial_value: -> { negotiate_context_list.size }, onlyif: -> { has_negotiate_context? }
17
17
  uint16 :reserved1, label: 'Reserved', initial_value: 0, onlyif: -> { !has_negotiate_context? }
18
- string :server_guid, label: 'Server GUID', length: 16
18
+ string :server_guid, label: 'Server GUID', length: 16
19
19
  smb2_capabilities :capabilities
20
20
  uint32 :max_transact_size, label: 'Max Transaction Size'
21
21
  uint32 :max_read_size, label: 'Max Read Size'
@@ -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,
@@ -23,10 +23,10 @@ module RubySMB
23
23
  # @return [Integer]
24
24
  attr_accessor :id
25
25
 
26
- # Whether or not encryption is required (SMB 3.x)
27
- # @!attribute [rw] encryption_required
26
+ # Whether or not the share associated with this tree connect needs to be encrypted (SMB 3.x)
27
+ # @!attribute [rw] tree_connect_encrypt_data
28
28
  # @return [Boolean]
29
- attr_accessor :encryption_required
29
+ attr_accessor :tree_connect_encrypt_data
30
30
 
31
31
  def initialize(client:, share:, response:, encrypt: false)
32
32
  @client = client
@@ -34,7 +34,7 @@ module RubySMB
34
34
  @id = response.smb2_header.tree_id
35
35
  @permissions = response.maximal_access
36
36
  @share_type = response.share_type
37
- @encryption_required = encrypt
37
+ @tree_connect_encrypt_data = encrypt
38
38
  end
39
39
 
40
40
  # Disconnects this Tree from the current session
@@ -44,19 +44,26 @@ module RubySMB
44
44
  def disconnect!
45
45
  request = RubySMB::SMB2::Packet::TreeDisconnectRequest.new
46
46
  request = set_header_fields(request)
47
- raw_response = client.send_recv(request, encrypt: @encryption_required)
47
+ raw_response = client.send_recv(request, encrypt: @tree_connect_encrypt_data)
48
48
  response = RubySMB::SMB2::Packet::TreeDisconnectResponse.read(raw_response)
49
49
  unless response.valid?
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
 
@@ -102,14 +109,13 @@ module RubySMB
102
109
  create_request.create_disposition = disposition
103
110
  create_request.name = filename
104
111
 
105
- raw_response = client.send_recv(create_request, encrypt: @encryption_required)
112
+ raw_response = client.send_recv(create_request, encrypt: @tree_connect_encrypt_data)
106
113
  response = RubySMB::SMB2::Packet::CreateResponse.read(raw_response)
107
114
  unless response.valid?
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
@@ -118,7 +124,7 @@ module RubySMB
118
124
 
119
125
  case @share_type
120
126
  when RubySMB::SMB2::Packet::TreeConnectResponse::SMB2_SHARE_TYPE_DISK
121
- RubySMB::SMB2::File.new(name: filename, tree: self, response: response, encrypt: @encryption_required)
127
+ RubySMB::SMB2::File.new(name: filename, tree: self, response: response, encrypt: @tree_connect_encrypt_data)
122
128
  when RubySMB::SMB2::Packet::TreeConnectResponse::SMB2_SHARE_TYPE_PIPE
123
129
  RubySMB::SMB2::Pipe.new(name: filename, tree: self, response: response)
124
130
  # when RubySMB::SMB2::TreeConnectResponse::SMB2_SHARE_TYPE_PRINT
@@ -147,21 +153,27 @@ 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
- response = client.send_recv(directory_request, encrypt: @encryption_required)
170
+ response = client.send_recv(directory_request, encrypt: @tree_connect_encrypt_data)
158
171
  directory_response = RubySMB::SMB2::Packet::QueryDirectoryResponse.read(response)
159
172
  unless directory_response.valid?
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
 
@@ -199,14 +211,13 @@ module RubySMB
199
211
 
200
212
  create_request = open_directory_packet(directory: directory, disposition: disposition,
201
213
  impersonation: impersonation, read: read, write: write, delete: delete)
202
- raw_response = client.send_recv(create_request, encrypt: @encryption_required)
214
+ raw_response = client.send_recv(create_request, encrypt: @tree_connect_encrypt_data)
203
215
  response = RubySMB::SMB2::Packet::CreateResponse.read(raw_response)
204
216
  unless response.valid?
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.0'.freeze
2
+ VERSION = '2.0.5'.freeze
3
3
  end
@@ -46,7 +46,7 @@ RSpec.describe RubySMB::Client do
46
46
  it { is_expected.to respond_to :encryption_algorithm }
47
47
  it { is_expected.to respond_to :client_encryption_key }
48
48
  it { is_expected.to respond_to :server_encryption_key }
49
- it { is_expected.to respond_to :encryption_required }
49
+ it { is_expected.to respond_to :session_encrypt_data }
50
50
  it { is_expected.to respond_to :server_encryption_algorithms }
51
51
  it { is_expected.to respond_to :server_compression_algorithms }
52
52
  it { is_expected.to respond_to :negotiated_smb_version }
@@ -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
@@ -106,9 +108,9 @@ RSpec.describe RubySMB::Client do
106
108
  expect(client.password).to eq password
107
109
  end
108
110
 
109
- it 'sets the encryption_required attribute' do
111
+ it 'sets the session_encrypt_data attribute' do
110
112
  client = described_class.new(dispatcher, username: username, password: password, always_encrypt: true)
111
- expect(client.encryption_required).to eq true
113
+ expect(client.session_encrypt_data).to eq true
112
114
  end
113
115
 
114
116
  it 'creates an NTLM client' do
@@ -125,7 +127,7 @@ RSpec.describe RubySMB::Client do
125
127
  expect(opt[:workstation]).to eq(local_workstation)
126
128
  expect(opt[:domain]).to eq(domain)
127
129
  flags = Net::NTLM::Client::DEFAULT_FLAGS |
128
- Net::NTLM::FLAGS[:TARGET_INFO] | 0x02000000
130
+ Net::NTLM::FLAGS[:TARGET_INFO] | 0x02000000 ^ Net::NTLM::FLAGS[:OEM]
129
131
  expect(opt[:flags]).to eq(flags)
130
132
  end
131
133
 
@@ -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,16 +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')
192
- end
193
-
194
- it 'checks the packet version' do
195
- expect(smb1_request).to receive(:packet_smb_version).and_call_original
196
- client.send_recv(smb1_request)
207
+ allow(RubySMB::SMB2::SMB2Header).to receive(:read).and_return(smb2_header)
197
208
  end
198
209
 
199
210
  context 'when signing' do
@@ -204,9 +215,10 @@ RSpec.describe RubySMB::Client do
204
215
 
205
216
  context 'with an SMB2 packet' do
206
217
  it 'does not sign a SessionSetupRequest packet' do
218
+ allow(smb2_client).to receive(:is_status_pending?).and_return(false)
207
219
  expect(smb2_client).to_not receive(:smb2_sign)
208
220
  expect(smb2_client).to_not receive(:smb3_sign)
209
- client.send_recv(RubySMB::SMB2::Packet::SessionSetupRequest.new)
221
+ smb2_client.send_recv(RubySMB::SMB2::Packet::SessionSetupRequest.new)
210
222
  end
211
223
 
212
224
  it 'calls #smb2_sign if it is an SMB2 client' do
@@ -234,6 +246,29 @@ RSpec.describe RubySMB::Client do
234
246
  expect(smb1_client).to_not receive(:is_status_pending?)
235
247
  smb1_client.send_recv(smb1_request)
236
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
237
272
  end
238
273
 
239
274
  context 'with SMB2' do
@@ -256,10 +291,8 @@ RSpec.describe RubySMB::Client do
256
291
  context 'with a SessionSetupRequest' do
257
292
  it 'does not encrypt/decrypt' do
258
293
  request = RubySMB::SMB2::Packet::SessionSetupRequest.new
259
- expect(smb3_client).to_not receive(:send_encrypt).with(request)
260
- expect(smb3_client).to_not receive(:recv_encrypt)
261
- expect(dispatcher).to receive(:send_packet).with(request)
262
- 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)
263
296
  smb3_client.send_recv(request)
264
297
  end
265
298
  end
@@ -267,17 +300,15 @@ RSpec.describe RubySMB::Client do
267
300
  context 'with a NegotiateRequest' do
268
301
  it 'does not encrypt/decrypt' do
269
302
  request = RubySMB::SMB2::Packet::NegotiateRequest.new
270
- expect(smb3_client).to_not receive(:send_encrypt).with(request)
271
- expect(smb3_client).to_not receive(:recv_encrypt)
272
- expect(dispatcher).to receive(:send_packet).with(request)
273
- 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)
274
305
  smb3_client.send_recv(request)
275
306
  end
276
307
  end
277
308
 
278
309
  it 'encrypts and decrypts' do
279
- expect(smb3_client).to receive(:send_encrypt).with(smb2_request)
280
- 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)
281
312
  smb3_client.send_recv(smb2_request)
282
313
  end
283
314
 
@@ -285,12 +316,44 @@ RSpec.describe RubySMB::Client do
285
316
  it 'waits 1 second and reads/decrypts again' do
286
317
  allow(smb3_client).to receive(:is_status_pending?).and_return(true, false)
287
318
  expect(smb3_client).to receive(:sleep).with(1)
288
- expect(smb3_client).to receive(:send_encrypt).with(smb2_request)
289
- 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
290
321
  smb3_client.send_recv(smb2_request)
291
322
  end
292
323
  end
293
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
294
357
  end
295
358
 
296
359
  describe '#is_status_pending?' do
@@ -302,17 +365,17 @@ RSpec.describe RubySMB::Client do
302
365
  }
303
366
 
304
367
  it 'returns true when the response has a STATUS_PENDING status code and the async_command flag set' do
305
- expect(client.is_status_pending?(response.to_binary_s)).to be true
368
+ expect(client.is_status_pending?(response.smb2_header)).to be true
306
369
  end
307
370
 
308
371
  it 'returns false when the response has a STATUS_PENDING status code and the async_command flag not set' do
309
372
  response.smb2_header.flags.async_command = 0
310
- expect(client.is_status_pending?(response.to_binary_s)).to be false
373
+ expect(client.is_status_pending?(response.smb2_header)).to be false
311
374
  end
312
375
 
313
376
  it 'returns false when the response has no STATUS_PENDING status code but the async_command flag set' do
314
377
  response.smb2_header.nt_status= WindowsError::NTStatus::STATUS_SUCCESS.value
315
- expect(client.is_status_pending?(response.to_binary_s)).to be false
378
+ expect(client.is_status_pending?(response.smb2_header)).to be false
316
379
  end
317
380
  end
318
381
 
@@ -322,6 +385,11 @@ RSpec.describe RubySMB::Client do
322
385
  expect(client.can_be_encrypted?(packet)).to be true
323
386
  end
324
387
 
388
+ it 'returns false if it is an SMB1 packet' do
389
+ packet = RubySMB::SMB1::Packet::LogoffRequest.new
390
+ expect(client.can_be_encrypted?(packet)).to be false
391
+ end
392
+
325
393
  [RubySMB::SMB2::Packet::SessionSetupRequest, RubySMB::SMB2::Packet::NegotiateRequest].each do |klass|
326
394
  it "returns false if the packet is a #{klass}" do
327
395
  packet = klass.new
@@ -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,33 +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 'parses the response as a Transform response packet' do
389
- expect(RubySMB::SMB2::Packet::TransformHeader).to receive(:read).with(packet.to_binary_s)
390
- client.recv_encrypt
468
+ it 'raises an CommunicationError exception if an error occurs while receiving the response' do
469
+ allow(dispatcher).to receive(:recv_packet).and_raise(RubySMB::Error::CommunicationError)
470
+ expect { client.recv_packet }.to raise_error(RubySMB::Error::CommunicationError)
391
471
  end
392
472
 
393
- it 'raises an InvalidPacket exception if an error occurs while parsing the response' do
394
- allow(RubySMB::SMB2::Packet::TransformHeader).to receive(:read).and_raise(IOError)
395
- expect { client.recv_encrypt}.to raise_error(RubySMB::Error::InvalidPacket, 'Not a SMB2 TransformHeader packet')
396
- 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
397
482
 
398
- it 'decrypts the Transform response packet' do
399
- transform = double('Transform header packet')
400
- allow(RubySMB::SMB2::Packet::TransformHeader).to receive(:read).and_return(transform)
401
- client.recv_encrypt
402
- expect(client).to have_received(:smb3_decrypt).with(transform)
403
- 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
404
487
 
405
- it 'raises an EncryptionError exception if an error occurs while decrypting' do
406
- allow(client).to receive(:smb3_decrypt).and_raise(RubySMB::Error::RubySMBError )
407
- expect { client.recv_encrypt}.to raise_error(
408
- RubySMB::Error::EncryptionError,
409
- "Error while decrypting RubySMB::SMB2::Packet::TransformHeader packet (SMB 0x0300}): RubySMB::Error::RubySMBError"
410
- )
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
492
+
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
411
507
  end
412
508
  end
413
509
 
@@ -658,7 +754,7 @@ RSpec.describe RubySMB::Client do
658
754
  expect(session_packet.session_header.session_packet_type).to eq RubySMB::Nbss::SESSION_REQUEST
659
755
  expect(session_packet.called_name).to eq called_name
660
756
  expect(session_packet.calling_name).to eq calling_name
661
- expect(session_packet.session_header.packet_length).to eq(
757
+ expect(session_packet.session_header.stream_protocol_length).to eq(
662
758
  session_packet.called_name.to_binary_s.size + session_packet.calling_name.to_binary_s.size
663
759
  )
664
760
  end
@@ -1027,6 +1123,19 @@ RSpec.describe RubySMB::Client do
1027
1123
  it 'returns the string \'SMB2\'' do
1028
1124
  expect(client.parse_negotiate_response(smb2_response)).to eq ('SMB2')
1029
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
1030
1139
  end
1031
1140
 
1032
1141
  context 'when SMB3 was negotiated' do
@@ -1050,6 +1159,53 @@ RSpec.describe RubySMB::Client do
1050
1159
  it 'returns the string \'SMB2\'' do
1051
1160
  expect(client.parse_negotiate_response(smb3_response)).to eq ('SMB3')
1052
1161
  end
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
+
1169
+ context 'when the server supports encryption' do
1170
+ before :example do
1171
+ smb3_response.capabilities.encryption = 1
1172
+ end
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
+
1179
+ it 'keeps session encryption enabled if it was already' do
1180
+ client.session_encrypt_data = true
1181
+ client.parse_negotiate_response(smb3_response)
1182
+ expect(client.session_encrypt_data).to be true
1183
+ end
1184
+
1185
+ it 'keeps session encryption disabled if it was already' do
1186
+ client.session_encrypt_data = false
1187
+ client.parse_negotiate_response(smb3_response)
1188
+ expect(client.session_encrypt_data).to be false
1189
+ end
1190
+ end
1191
+
1192
+ context 'when the server does not support encryption' do
1193
+ before :example do
1194
+ smb3_response.capabilities.encryption = 0
1195
+ end
1196
+
1197
+ it 'disables session encryption if it was already enabled' do
1198
+ client.session_encrypt_data = true
1199
+ client.parse_negotiate_response(smb3_response)
1200
+ expect(client.session_encrypt_data).to be false
1201
+ end
1202
+
1203
+ it 'keeps session encryption disabled if it was already' do
1204
+ client.session_encrypt_data = false
1205
+ client.parse_negotiate_response(smb3_response)
1206
+ expect(client.session_encrypt_data).to be false
1207
+ end
1208
+ end
1053
1209
  end
1054
1210
 
1055
1211
  context 'when the response contains the SMB2 wildcard revision number dialect' do
@@ -1121,26 +1277,15 @@ RSpec.describe RubySMB::Client do
1121
1277
  end
1122
1278
  end
1123
1279
 
1124
- ['0x0300', '0x0302'].each do |dialect|
1125
- context "with #{dialect} dialect" do
1126
- before :example do
1127
- client.dialect = dialect
1128
- end
1129
-
1130
- it 'sets the expected encryption algorithm' do
1131
- client.negotiate
1132
- expect(client.encryption_algorithm).to eq(RubySMB::SMB2::EncryptionCapabilities::ENCRYPTION_ALGORITHM_MAP[RubySMB::SMB2::EncryptionCapabilities::AES_128_CCM])
1133
- end
1134
- end
1135
- end
1136
-
1137
1280
  context "with 0x0311 dialect" do
1138
- it 'calls #parse_smb3_encryption_data' do
1281
+ it 'calls #parse_negotiate_response and updates the preauth hash' do
1139
1282
  client.dialect = '0x0311'
1140
1283
  request_packet = client.smb2_3_negotiate_request
1141
1284
  allow(client).to receive(:negotiate_request).and_return(request_packet)
1142
1285
  allow(client).to receive(:negotiate_response).and_return(smb3_response)
1143
- 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)
1144
1289
  client.negotiate
1145
1290
  end
1146
1291
  end
@@ -1213,7 +1358,7 @@ RSpec.describe RubySMB::Client do
1213
1358
  end
1214
1359
  end
1215
1360
 
1216
- describe '#parse_smb3_encryption_data' do
1361
+ describe '#parse_smb3_capabilities' do
1217
1362
  let(:request_packet) { client.smb2_3_negotiate_request }
1218
1363
  let(:smb3_response) { RubySMB::SMB2::Packet::NegotiateResponse.new(dialect_revision: 0x311) }
1219
1364
  let(:nc_encryption) do
@@ -1240,7 +1385,7 @@ RSpec.describe RubySMB::Client do
1240
1385
  context 'when selecting the integrity hash algorithm' do
1241
1386
  context 'with one algorithm' do
1242
1387
  it 'selects the expected algorithm' do
1243
- smb3_client.parse_smb3_encryption_data(request_packet, smb3_response)
1388
+ smb3_client.parse_smb3_capabilities(smb3_response)
1244
1389
  expect(smb3_client.preauth_integrity_hash_algorithm).to eq('SHA512')
1245
1390
  end
1246
1391
  end
@@ -1251,7 +1396,7 @@ RSpec.describe RubySMB::Client do
1251
1396
  RubySMB::SMB2::NegotiateContext::SMB2_PREAUTH_INTEGRITY_CAPABILITIES
1252
1397
  )
1253
1398
  nc.data.hash_algorithms << 3
1254
- smb3_client.parse_smb3_encryption_data(request_packet, smb3_response)
1399
+ smb3_client.parse_smb3_capabilities(smb3_response)
1255
1400
  expect(smb3_client.preauth_integrity_hash_algorithm).to eq('SHA512')
1256
1401
  end
1257
1402
  end
@@ -1260,7 +1405,7 @@ RSpec.describe RubySMB::Client do
1260
1405
  it 'raises the expected exception' do
1261
1406
  smb3_response = RubySMB::SMB2::Packet::NegotiateResponse.new(dialect_revision: 0x311)
1262
1407
  smb3_response.add_negotiate_context(nc_encryption)
1263
- 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(
1264
1409
  RubySMB::Error::EncryptionError,
1265
1410
  'Unable to retrieve the Preauth Integrity Hash Algorithm from the Negotiate response'
1266
1411
  )
@@ -1276,7 +1421,7 @@ RSpec.describe RubySMB::Client do
1276
1421
  )
1277
1422
  nc.data.hash_algorithms << 5
1278
1423
  smb3_response.add_negotiate_context(nc)
1279
- 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(
1280
1425
  RubySMB::Error::EncryptionError,
1281
1426
  'Unable to retrieve the Preauth Integrity Hash Algorithm from the Negotiate response'
1282
1427
  )
@@ -1287,7 +1432,7 @@ RSpec.describe RubySMB::Client do
1287
1432
  context 'when selecting the encryption algorithm' do
1288
1433
  context 'with one algorithm' do
1289
1434
  it 'selects the expected algorithm' do
1290
- smb3_client.parse_smb3_encryption_data(request_packet, smb3_response)
1435
+ smb3_client.parse_smb3_capabilities(smb3_response)
1291
1436
  expect(smb3_client.encryption_algorithm).to eq('AES-128-CCM')
1292
1437
  end
1293
1438
  end
@@ -1298,7 +1443,7 @@ RSpec.describe RubySMB::Client do
1298
1443
  RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES
1299
1444
  )
1300
1445
  nc.data.ciphers << RubySMB::SMB2::EncryptionCapabilities::AES_128_GCM
1301
- smb3_client.parse_smb3_encryption_data(request_packet, smb3_response)
1446
+ smb3_client.parse_smb3_capabilities(smb3_response)
1302
1447
  expect(smb3_client.encryption_algorithm).to eq('AES-128-GCM')
1303
1448
  end
1304
1449
 
@@ -1307,7 +1452,7 @@ RSpec.describe RubySMB::Client do
1307
1452
  RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES
1308
1453
  )
1309
1454
  nc.data.ciphers << 3
1310
- smb3_client.parse_smb3_encryption_data(request_packet, smb3_response)
1455
+ smb3_client.parse_smb3_capabilities(smb3_response)
1311
1456
  expect(smb3_client.encryption_algorithm).to eq('AES-128-CCM')
1312
1457
  end
1313
1458
 
@@ -1316,7 +1461,7 @@ RSpec.describe RubySMB::Client do
1316
1461
  RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES
1317
1462
  )
1318
1463
  nc.data.ciphers << RubySMB::SMB2::EncryptionCapabilities::AES_128_GCM
1319
- smb3_client.parse_smb3_encryption_data(request_packet, smb3_response)
1464
+ smb3_client.parse_smb3_capabilities(smb3_response)
1320
1465
  expect(smb3_client.server_encryption_algorithms).to eq([1, 2])
1321
1466
  end
1322
1467
  end
@@ -1325,7 +1470,7 @@ RSpec.describe RubySMB::Client do
1325
1470
  it 'raises the expected exception' do
1326
1471
  smb3_response = RubySMB::SMB2::Packet::NegotiateResponse.new(dialect_revision: 0x311)
1327
1472
  smb3_response.add_negotiate_context(nc_integrity)
1328
- 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(
1329
1474
  RubySMB::Error::EncryptionError,
1330
1475
  'Unable to retrieve the encryption cipher list supported by the server from the Negotiate response'
1331
1476
  )
@@ -1341,7 +1486,7 @@ RSpec.describe RubySMB::Client do
1341
1486
  )
1342
1487
  nc.data.ciphers << 14
1343
1488
  smb3_response.add_negotiate_context(nc)
1344
- 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(
1345
1490
  RubySMB::Error::EncryptionError,
1346
1491
  'Unable to retrieve the encryption cipher list supported by the server from the Negotiate response'
1347
1492
  )
@@ -1359,16 +1504,10 @@ RSpec.describe RubySMB::Client do
1359
1504
  nc.data.compression_algorithms << RubySMB::SMB2::CompressionCapabilities::LZ77_Huffman
1360
1505
  nc.data.compression_algorithms << RubySMB::SMB2::CompressionCapabilities::Pattern_V1
1361
1506
  smb3_response.add_negotiate_context(nc)
1362
- smb3_client.parse_smb3_encryption_data(request_packet, smb3_response)
1507
+ smb3_client.parse_smb3_capabilities(smb3_response)
1363
1508
  expect(smb3_client.server_compression_algorithms).to eq([1, 2, 3, 4])
1364
1509
  end
1365
1510
  end
1366
-
1367
- it 'updates the preauth hash' do
1368
- expect(smb3_client).to receive(:update_preauth_hash).with(request_packet)
1369
- expect(smb3_client).to receive(:update_preauth_hash).with(smb3_response)
1370
- smb3_client.parse_smb3_encryption_data(request_packet, smb3_response)
1371
- end
1372
1511
  end
1373
1512
  end
1374
1513
  end
@@ -1710,22 +1849,22 @@ RSpec.describe RubySMB::Client do
1710
1849
  smb2_client.smb2_authenticate
1711
1850
  end
1712
1851
 
1713
- context 'when setting the encryption_required parameter' do
1852
+ context 'when setting the session_encrypt_data parameter' do
1714
1853
  before :example do
1715
1854
  smb2_client.smb3 = true
1716
- smb2_client.encryption_required = false
1855
+ smb2_client.session_encrypt_data = false
1717
1856
  end
1718
1857
 
1719
- it 'sets the encryption_required parameter to true if the server requires encryption' do
1858
+ it 'sets the session_encrypt_data parameter to true if the server requires encryption' do
1720
1859
  final_response_packet.session_flags.encrypt_data = 1
1721
1860
  smb2_client.smb2_authenticate
1722
- expect(smb2_client.encryption_required).to be true
1861
+ expect(smb2_client.session_encrypt_data).to be true
1723
1862
  end
1724
1863
 
1725
- it 'does not set the encryption_required parameter if the server does not require encryption' do
1864
+ it 'does not set the session_encrypt_data parameter if the server does not require encryption' do
1726
1865
  final_response_packet.session_flags.encrypt_data = 0
1727
1866
  smb2_client.smb2_authenticate
1728
- expect(smb2_client.encryption_required).to be false
1867
+ expect(smb2_client.session_encrypt_data).to be false
1729
1868
  end
1730
1869
  end
1731
1870
  end
@@ -2220,7 +2359,7 @@ RSpec.describe RubySMB::Client do
2220
2359
  let(:named_pipe){ double("Named Pipe") }
2221
2360
 
2222
2361
  before :example do
2223
- allow(tree).to receive(:open_file).and_return(named_pipe)
2362
+ allow(tree).to receive(:open_pipe).and_return(named_pipe)
2224
2363
  allow(named_pipe).to receive(:net_share_enum_all)
2225
2364
  end
2226
2365
 
@@ -2235,8 +2374,8 @@ RSpec.describe RubySMB::Client do
2235
2374
  smb1_client.net_share_enum_all(sock.peeraddr)
2236
2375
  end
2237
2376
 
2238
- it 'it calls the Tree #open_file method to open "srvsvc" named pipe' do
2239
- 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)
2240
2379
  smb1_client.net_share_enum_all(sock.peeraddr)
2241
2380
  end
2242
2381
 
@@ -2258,8 +2397,8 @@ RSpec.describe RubySMB::Client do
2258
2397
  smb2_client.net_share_enum_all(sock.peeraddr)
2259
2398
  end
2260
2399
 
2261
- it 'it calls the Tree #open_file method to open "srvsvc" named pipe' do
2262
- 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)
2263
2402
  smb2_client.net_share_enum_all(sock.peeraddr)
2264
2403
  end
2265
2404
 
@@ -2340,7 +2479,7 @@ RSpec.describe RubySMB::Client do
2340
2479
  before :example do
2341
2480
  allow(ipc_tree).to receive_messages(
2342
2481
  :share => share,
2343
- :open_file => named_pipe
2482
+ :open_pipe => named_pipe
2344
2483
  )
2345
2484
  allow(client).to receive(:tree_connect).and_return(ipc_tree)
2346
2485
  end
@@ -2362,9 +2501,9 @@ RSpec.describe RubySMB::Client do
2362
2501
  expect(client).to have_received(:tree_connect).with(share)
2363
2502
  end
2364
2503
 
2365
- it 'open \'winreg\' file on the IPC$ Tree' do
2504
+ it 'open \'winreg\' pipe on the IPC$ Tree' do
2366
2505
  client.connect_to_winreg(host)
2367
- 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)
2368
2507
  end
2369
2508
 
2370
2509
  it 'returns the expected opened named pipe' do