ruby_smb 2.0.0 → 2.0.5

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 (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