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
@@ -9,18 +9,20 @@
9
9
  require 'bundler/setup'
10
10
  require 'ruby_smb'
11
11
 
12
- address = ARGV[0]
13
- username = ARGV[1]
14
- password = ARGV[2]
15
- share = ARGV[3]
16
- file = ARGV[4]
17
- data = ARGV[5]
12
+ address = ARGV[0]
13
+ username = ARGV[1]
14
+ password = ARGV[2]
15
+ share = ARGV[3]
16
+ file = ARGV[4]
17
+ data = ARGV[5]
18
+ smb_versions = ARGV[6]&.split(',') || ['1','2','3']
19
+
18
20
  path = "\\\\#{address}\\#{share}"
19
21
 
20
22
  sock = TCPSocket.new address, 445
21
23
  dispatcher = RubySMB::Dispatcher::Socket.new(sock)
22
24
 
23
- client = RubySMB::Client.new(dispatcher, smb1: true, smb2: true, username: username, password: password)
25
+ client = RubySMB::Client.new(dispatcher, smb1: smb_versions.include?('1'), smb2: smb_versions.include?('2'), smb3: smb_versions.include?('3'), username: username, password: password)
24
26
  protocol = client.negotiate
25
27
  status = client.authenticate
26
28
 
@@ -160,9 +160,16 @@ module RubySMB
160
160
 
161
161
  # The UID set in SMB1
162
162
  # @!attribute [rw] user_id
163
- # @return [String]
163
+ # @return [Integer]
164
164
  attr_accessor :user_id
165
165
 
166
+ # The Process ID set in SMB1
167
+ # It is randomly generated during the client initialization, but can
168
+ # be modified using the accessor.
169
+ # @!attribute [rw] pid
170
+ # @return [Integer]
171
+ attr_accessor :pid
172
+
166
173
  # The maximum size SMB message that the Client accepts (in bytes)
167
174
  # The default value is equal to {MAX_BUFFER_SIZE}.
168
175
  # @!attribute [rw] max_buffer_size
@@ -216,10 +223,10 @@ module RubySMB
216
223
  # @return [String]
217
224
  attr_accessor :server_encryption_key
218
225
 
219
- # Whether or not encryption is required (SMB 3.x)
220
- # @!attribute [rw] encryption_required
226
+ # Whether or not the whole session needs to be encrypted (SMB 3.x)
227
+ # @!attribute [rw] session_encrypt_data
221
228
  # @return [Boolean]
222
- attr_accessor :encryption_required
229
+ attr_accessor :session_encrypt_data
223
230
 
224
231
  # The encryption algorithms supported by the server (SMB 3.x).
225
232
  # @!attribute [rw] server_encryption_algorithms
@@ -233,12 +240,39 @@ module RubySMB
233
240
  # (constants defined in RubySMB::SMB2::CompressionCapabilities)
234
241
  attr_accessor :server_compression_algorithms
235
242
 
243
+ # The GUID of the server (SMB 2.x and 3.x).
244
+ # @!attribute [rw] server_guid
245
+ # @return [String]
246
+ attr_accessor :server_guid
247
+
248
+ # The server's start time if it is reported as part of the negotiation
249
+ # process (SMB 2.x and 3.x). This value is nil if the server does not report
250
+ # it (reports a value of 0).
251
+ # @!attribute [rw] server_start_time
252
+ # @return [Time] the time that the server reports that it was started at
253
+ attr_accessor :server_start_time
254
+
255
+ # The server's current time if it is reported as part of the negotiation
256
+ # process (SMB 2.x and 3.x). This value is nil if the server does not report
257
+ # it (reports a value of 0).
258
+ # @!attribute [rw] server_system_time
259
+ # @return [Time] the time that the server reports as current
260
+ attr_accessor :server_system_time
261
+
236
262
  # The SMB version that has been successfully negotiated. This value is only
237
263
  # set after the NEGOTIATE handshake has been performed.
238
264
  # @!attribute [rw] negotiated_smb_version
239
265
  # @return [Integer] the negotiated SMB version
240
266
  attr_accessor :negotiated_smb_version
241
267
 
268
+ # Whether or not the server supports multi-credit operations. It is
269
+ # reported by the LARGE_MTU capabiliy as part of the negotiation process
270
+ # (SMB 2.x and 3.x).
271
+ # @!attribute [rw] server_supports_multi_credit
272
+ # @return [Boolean] true if the server supports multi-credit operations,
273
+ # false otherwise
274
+ attr_accessor :server_supports_multi_credit
275
+
242
276
  # @param dispatcher [RubySMB::Dispatcher::Socket] the packet dispatcher to use
243
277
  # @param smb1 [Boolean] whether or not to enable SMB1 support
244
278
  # @param smb2 [Boolean] whether or not to enable SMB2 support
@@ -249,6 +283,7 @@ module RubySMB
249
283
  raise ArgumentError, 'You must enable at least one Protocol'
250
284
  end
251
285
  @dispatcher = dispatcher
286
+ @pid = rand(0xFFFF)
252
287
  @domain = domain
253
288
  @local_workstation = local_workstation
254
289
  @password = password.encode('utf-8') || ''.encode('utf-8')
@@ -266,14 +301,16 @@ module RubySMB
266
301
  @server_max_read_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
267
302
  @server_max_write_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
268
303
  @server_max_transact_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
304
+ @server_supports_multi_credit = false
269
305
 
270
306
  # SMB 3.x options
271
- @encryption_required = always_encrypt
307
+ @session_encrypt_data = always_encrypt
272
308
 
273
309
  negotiate_version_flag = 0x02000000
274
310
  flags = Net::NTLM::Client::DEFAULT_FLAGS |
275
311
  Net::NTLM::FLAGS[:TARGET_INFO] |
276
- negotiate_version_flag
312
+ negotiate_version_flag ^
313
+ Net::NTLM::FLAGS[:OEM]
277
314
 
278
315
  @ntlm_client = Net::NTLM::Client.new(
279
316
  @username,
@@ -324,7 +361,7 @@ module RubySMB
324
361
  # @param packet [RubySMB::GenericPacket] the packet to set the message id for
325
362
  # @return [RubySMB::GenericPacket] the modified packet
326
363
  def increment_smb_message_id(packet)
327
- packet.smb2_header.message_id = smb2_message_id
364
+ packet.smb2_header.message_id = self.smb2_message_id
328
365
  self.smb2_message_id += 1
329
366
  packet
330
367
  end
@@ -347,7 +384,8 @@ module RubySMB
347
384
  negotiate_version_flag = 0x02000000
348
385
  flags = Net::NTLM::Client::DEFAULT_FLAGS |
349
386
  Net::NTLM::FLAGS[:TARGET_INFO] |
350
- negotiate_version_flag
387
+ negotiate_version_flag ^
388
+ Net::NTLM::FLAGS[:OEM]
351
389
 
352
390
  @ntlm_client = Net::NTLM::Client.new(
353
391
  @username,
@@ -373,8 +411,7 @@ module RubySMB
373
411
  raise RubySMB::Error::InvalidPacket.new(
374
412
  expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
375
413
  expected_cmd: RubySMB::SMB2::Packet::LogoffResponse::COMMAND,
376
- received_proto: response.smb2_header.protocol,
377
- received_cmd: response.smb2_header.command
414
+ packet: response
378
415
  )
379
416
  end
380
417
  else
@@ -385,8 +422,7 @@ module RubySMB
385
422
  raise RubySMB::Error::InvalidPacket.new(
386
423
  expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
387
424
  expected_cmd: RubySMB::SMB1::Packet::LogoffResponse::COMMAND,
388
- received_proto: response.smb_header.protocol,
389
- received_cmd: response.smb_header.command
425
+ packet: response
390
426
  )
391
427
  end
392
428
  end
@@ -398,12 +434,16 @@ module RubySMB
398
434
  # It will also sign the packet if neccessary.
399
435
  #
400
436
  # @param packet [RubySMB::GenericPacket] the request to be sent
437
+ # @param encrypt [Boolean] true if encryption has to be enabled for this transaction
438
+ # (note that if @session_encrypt_data is set, encryption will be enabled
439
+ # regardless of this parameter value)
401
440
  # @return [String] the raw response data received
402
441
  def send_recv(packet, encrypt: false)
403
442
  version = packet.packet_smb_version
404
443
  case version
405
444
  when 'SMB1'
406
- packet.smb_header.uid = user_id if user_id
445
+ packet.smb_header.uid = self.user_id if self.user_id
446
+ packet.smb_header.pid_low = self.pid if self.pid
407
447
  packet = smb1_sign(packet)
408
448
  when 'SMB2'
409
449
  packet = increment_smb_message_id(packet)
@@ -419,84 +459,107 @@ module RubySMB
419
459
  packet = packet
420
460
  end
421
461
 
422
- if can_be_encrypted?(packet) && encryption_supported? && (@encryption_required || encrypt)
423
- send_encrypt(packet)
424
- raw_response = recv_encrypt
425
- loop do
426
- break unless is_status_pending?(raw_response)
427
- sleep 1
428
- raw_response = recv_encrypt
429
- end
430
- else
431
- dispatcher.send_packet(packet)
432
- raw_response = dispatcher.recv_packet
433
- loop do
434
- break unless is_status_pending?(raw_response)
435
- sleep 1
436
- raw_response = dispatcher.recv_packet
437
- end unless version == 'SMB1'
462
+ encrypt_data = false
463
+ if can_be_encrypted?(packet) && encryption_supported? && (@session_encrypt_data || encrypt)
464
+ encrypt_data = true
438
465
  end
466
+ send_packet(packet, encrypt: encrypt_data)
467
+ raw_response = recv_packet(encrypt: encrypt_data)
468
+ smb2_header = nil
469
+ loop do
470
+ smb2_header = RubySMB::SMB2::SMB2Header.read(raw_response)
471
+ break unless is_status_pending?(smb2_header)
472
+ sleep 1
473
+ raw_response = recv_packet(encrypt: encrypt_data)
474
+ rescue IOError
475
+ # We're expecting an SMB2 packet, but the server sent an SMB1 packet
476
+ # instead. This behavior has been observed with older versions of Samba
477
+ # when something goes wrong on the server side. So, we just ignore it
478
+ # and expect the caller to handle this wrong response packet.
479
+ break
480
+ end unless version == 'SMB1'
439
481
 
440
482
  self.sequence_counter += 1 if signing_required && !session_key.empty?
483
+ # update the SMB2 message ID according to the received Credit Charged
484
+ self.smb2_message_id += smb2_header.credit_charge - 1 if smb2_header && self.server_supports_multi_credit
441
485
  raw_response
442
486
  end
443
487
 
444
488
  # Check if the response is an asynchronous operation with STATUS_PENDING
445
489
  # status code.
446
490
  #
447
- # @param raw_response [String] the raw response packet
491
+ # @param smb2_header [String] the response packet SMB2 header
448
492
  # @return [Boolean] true if it is a status pending operation, false otherwise
449
- def is_status_pending?(raw_response)
450
- smb2_header = RubySMB::SMB2::SMB2Header.read(raw_response)
493
+ def is_status_pending?(smb2_header)
451
494
  value = smb2_header.nt_status.value
452
495
  status_code = WindowsError::NTStatus.find_by_retval(value).first
453
496
  status_code == WindowsError::NTStatus::STATUS_PENDING &&
454
497
  smb2_header.flags.async_command == 1
455
498
  end
456
499
 
457
- # Check if the request packet can be encrypted. Per the SMB spec,
500
+ # Check if the request packet can be encrypted. Per the SMB2 spec,
458
501
  # SessionSetupRequest and NegotiateRequest must not be encrypted.
459
502
  #
460
503
  # @param packet [RubySMB::GenericPacket] the request packet
461
504
  # @return [Boolean] true if the packet can be encrypted
462
505
  def can_be_encrypted?(packet)
506
+ return false if packet.packet_smb_version == 'SMB1'
463
507
  [RubySMB::SMB2::Packet::SessionSetupRequest, RubySMB::SMB2::Packet::NegotiateRequest].none? do |klass|
464
508
  packet.is_a?(klass)
465
509
  end
466
510
  end
467
511
 
468
- # Check if the current dialect support encryption.
512
+ # Check if the current dialect supports encryption.
469
513
  #
470
514
  # @return [Boolean] true if encryption is supported
471
515
  def encryption_supported?
472
516
  ['0x0300', '0x0302', '0x0311'].include?(@dialect)
473
517
  end
474
518
 
475
- # Encrypt and send a packet
476
- def send_encrypt(packet)
477
- begin
478
- transform_request = smb3_encrypt(packet.to_binary_s)
479
- rescue RubySMB::Error::RubySMBError => e
480
- raise RubySMB::Error::EncryptionError, "Error while encrypting #{packet.class.name} packet (SMB #{@dialect}): #{e}"
519
+ # Encrypt (if required) and send a packet.
520
+ #
521
+ # @param encrypt [Boolean] true if the packet should be encrypted, false
522
+ # otherwise
523
+ def send_packet(packet, encrypt: false)
524
+ if encrypt
525
+ begin
526
+ packet = smb3_encrypt(packet.to_binary_s)
527
+ rescue RubySMB::Error::RubySMBError => e
528
+ raise RubySMB::Error::EncryptionError, "Error while encrypting #{packet.class.name} packet (SMB #{@dialect}): #{e}"
529
+ end
481
530
  end
482
- dispatcher.send_packet(transform_request)
531
+ dispatcher.send_packet(packet)
483
532
  end
484
533
 
485
- # Receives the raw response through the Dispatcher and decrypt the packet.
534
+ # Receives the raw response through the Dispatcher and decrypt the packet (if required).
486
535
  #
536
+ # @param encrypt [Boolean] true if the packet is encrypted, false otherwise
487
537
  # @return [String] the raw unencrypted packet
488
- def recv_encrypt
489
- raw_response = dispatcher.recv_packet
538
+ def recv_packet(encrypt: false)
490
539
  begin
491
- transform_response = RubySMB::SMB2::Packet::TransformHeader.read(raw_response)
492
- rescue IOError
493
- raise RubySMB::Error::InvalidPacket, 'Not a SMB2 TransformHeader packet'
540
+ raw_response = dispatcher.recv_packet
541
+ rescue RubySMB::Error::CommunicationError => e
542
+ if encrypt
543
+ raise RubySMB::Error::EncryptionError, "Communication error with the "\
544
+ "remote host: #{e.message}. The server supports encryption but was "\
545
+ "not able to handle the encrypted request."
546
+ else
547
+ raise e
548
+ end
494
549
  end
495
- begin
496
- smb3_decrypt(transform_response)
497
- rescue RubySMB::Error::RubySMBError => e
498
- raise RubySMB::Error::EncryptionError, "Error while decrypting #{transform_response.class.name} packet (SMB #@dialect}): #{e}"
550
+ if encrypt
551
+ begin
552
+ transform_response = RubySMB::SMB2::Packet::TransformHeader.read(raw_response)
553
+ rescue IOError
554
+ raise RubySMB::Error::InvalidPacket, 'Not a SMB2 TransformHeader packet'
555
+ end
556
+ begin
557
+ raw_response = smb3_decrypt(transform_response)
558
+ rescue RubySMB::Error::RubySMBError => e
559
+ raise RubySMB::Error::EncryptionError, "Error while decrypting #{transform_response.class.name} packet (SMB #@dialect}): #{e}"
560
+ end
499
561
  end
562
+ raw_response
500
563
  end
501
564
 
502
565
  # Connects to the supplied share
@@ -520,7 +583,7 @@ module RubySMB
520
583
  # @param [String] host
521
584
  def net_share_enum_all(host)
522
585
  tree = tree_connect("\\\\#{host}\\IPC$")
523
- named_pipe = tree.open_file(filename: "srvsvc", write: true, read: true)
586
+ named_pipe = tree.open_pipe(filename: "srvsvc", write: true, read: true)
524
587
  named_pipe.net_share_enum_all(host)
525
588
  end
526
589
 
@@ -537,6 +600,7 @@ module RubySMB
537
600
  self.smb2_message_id = 0
538
601
  self.client_encryption_key = nil
539
602
  self.server_encryption_key = nil
603
+ self.server_supports_multi_credit = false
540
604
  end
541
605
 
542
606
  # Requests a NetBIOS Session Service using the provided name.
@@ -574,7 +638,7 @@ module RubySMB
574
638
  session_request.session_header.session_packet_type = RubySMB::Nbss::SESSION_REQUEST
575
639
  session_request.called_name = called_name
576
640
  session_request.calling_name = calling_name
577
- session_request.session_header.packet_length =
641
+ session_request.session_header.stream_protocol_length =
578
642
  session_request.num_bytes - session_request.session_header.num_bytes
579
643
  session_request
580
644
  end
@@ -60,8 +60,7 @@ module RubySMB
60
60
  raise RubySMB::Error::InvalidPacket.new(
61
61
  expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
62
62
  expected_cmd: RubySMB::SMB1::Packet::SessionSetupLegacyResponse::COMMAND,
63
- received_proto: packet.smb_header.protocol,
64
- received_cmd: packet.smb_header.command
63
+ packet: packet
65
64
  )
66
65
  end
67
66
  packet
@@ -154,8 +153,7 @@ module RubySMB
154
153
  raise RubySMB::Error::InvalidPacket.new(
155
154
  expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
156
155
  expected_cmd: RubySMB::SMB1::Packet::SessionSetupResponse::COMMAND,
157
- received_proto: packet.smb_header.protocol,
158
- received_cmd: packet.smb_header.command
156
+ packet: packet
159
157
  )
160
158
  end
161
159
  packet
@@ -168,8 +166,7 @@ module RubySMB
168
166
  raise RubySMB::Error::InvalidPacket.new(
169
167
  expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
170
168
  expected_cmd: RubySMB::SMB1::Packet::SessionSetupResponse::COMMAND,
171
- received_proto: packet.smb_header.protocol,
172
- received_cmd: packet.smb_header.command
169
+ packet: packet
173
170
  )
174
171
  end
175
172
 
@@ -216,8 +213,8 @@ module RubySMB
216
213
  raw = smb2_ntlmssp_authenticate(type3_message, @session_id)
217
214
  response = smb2_ntlmssp_final_packet(raw)
218
215
 
219
- if @smb3 && !@encryption_required && response.session_flags.encrypt_data == 1
220
- @encryption_required = true
216
+ if @smb3 && !@session_encrypt_data && response.session_flags.encrypt_data == 1
217
+ @session_encrypt_data = true
221
218
  end
222
219
  ######
223
220
  # DEBUG
@@ -236,8 +233,7 @@ module RubySMB
236
233
  raise RubySMB::Error::InvalidPacket.new(
237
234
  expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
238
235
  expected_cmd: RubySMB::SMB2::Packet::SessionSetupResponse::COMMAND,
239
- received_proto: packet.smb2_header.protocol,
240
- received_cmd: packet.smb2_header.command
236
+ packet: packet
241
237
  )
242
238
  end
243
239
 
@@ -251,8 +247,7 @@ module RubySMB
251
247
  raise RubySMB::Error::InvalidPacket.new(
252
248
  expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
253
249
  expected_cmd: RubySMB::SMB2::Packet::SessionSetupResponse::COMMAND,
254
- received_proto: packet.smb2_header.protocol,
255
- received_cmd: packet.smb2_header.command
250
+ packet: packet
256
251
  )
257
252
  end
258
253
 
@@ -21,8 +21,7 @@ module RubySMB
21
21
  raise RubySMB::Error::InvalidPacket.new(
22
22
  expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
23
23
  expected_cmd: RubySMB::SMB1::Packet::EchoResponse::COMMAND,
24
- received_proto: response.smb_header.protocol,
25
- received_cmd: response.smb_header.command
24
+ packet: response
26
25
  )
27
26
  end
28
27
  response
@@ -40,8 +39,7 @@ module RubySMB
40
39
  raise RubySMB::Error::InvalidPacket.new(
41
40
  expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
42
41
  expected_cmd: RubySMB::SMB2::Packet::EchoResponse::COMMAND,
43
- received_proto: response.smb2_header.protocol,
44
- received_cmd: response.smb2_header.command
42
+ packet: response
45
43
  )
46
44
  end
47
45
  response
@@ -18,12 +18,11 @@ module RubySMB
18
18
  # This is only valid for SMB1.
19
19
  response_packet.dialects = request_packet.dialects if response_packet.respond_to? :dialects=
20
20
  version = parse_negotiate_response(response_packet)
21
- case @dialect
22
- when '0x0300', '0x0302'
23
- @encryption_algorithm = RubySMB::SMB2::EncryptionCapabilities::ENCRYPTION_ALGORITHM_MAP[RubySMB::SMB2::EncryptionCapabilities::AES_128_CCM]
24
- when '0x0311'
25
- parse_smb3_encryption_data(request_packet, response_packet)
21
+ if @dialect == '0x0311'
22
+ update_preauth_hash(request_packet)
23
+ update_preauth_hash(response_packet)
26
24
  end
25
+
27
26
  # If the response contains the SMB2 wildcard revision number dialect;
28
27
  # it indicates that the server implements SMB 2.1 or future dialect
29
28
  # revisions and expects the client to send a subsequent SMB2 Negotiate
@@ -84,16 +83,14 @@ module RubySMB
84
83
  expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
85
84
  expected_cmd: RubySMB::SMB1::Packet::NegotiateResponseExtended::COMMAND,
86
85
  expected_custom: "extended_security=1",
87
- received_proto: packet.smb_header.protocol,
88
- received_cmd: packet.smb_header.command,
86
+ packet: packet,
89
87
  received_custom: "extended_security=#{extended_security}"
90
88
  )
91
89
  elsif packet.packet_smb_version == 'SMB2'
92
90
  raise RubySMB::Error::InvalidPacket.new(
93
91
  expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
94
92
  expected_cmd: RubySMB::SMB2::Packet::NegotiateResponse::COMMAND,
95
- received_proto: packet.smb2_header.protocol,
96
- received_cmd: packet.smb2_header.command
93
+ packet: packet
97
94
  )
98
95
  else
99
96
  raise RubySMB::Error::InvalidPacket, 'Unknown SMB protocol version'
@@ -122,6 +119,7 @@ module RubySMB
122
119
  # protocol overhead every time.
123
120
  self.server_max_buffer_size = packet.parameter_block.max_buffer_size - 260
124
121
  self.negotiated_smb_version = 1
122
+ self.session_encrypt_data = false
125
123
  'SMB1'
126
124
  when RubySMB::SMB2::Packet::NegotiateResponse
127
125
  self.smb1 = false
@@ -137,6 +135,23 @@ module RubySMB
137
135
  # This value is used in SMB1 only but calculate a valid value anyway
138
136
  self.server_max_buffer_size = [self.server_max_read_size, self.server_max_write_size, self.server_max_transact_size].min
139
137
  self.negotiated_smb_version = self.smb2 ? 2 : 3
138
+ self.server_guid = packet.server_guid
139
+ self.server_start_time = packet.server_start_time.to_time if packet.server_start_time != 0
140
+ self.server_system_time = packet.system_time.to_time if packet.system_time != 0
141
+ self.server_supports_multi_credit = self.dialect != '0x0202' && packet&.capabilities&.large_mtu == 1
142
+ case self.dialect
143
+ when '0x02ff'
144
+ when '0x0300', '0x0302'
145
+ if packet&.capabilities&.encryption == 1
146
+ self.encryption_algorithm = RubySMB::SMB2::EncryptionCapabilities::ENCRYPTION_ALGORITHM_MAP[RubySMB::SMB2::EncryptionCapabilities::AES_128_CCM]
147
+ end
148
+ self.session_encrypt_data = self.session_encrypt_data && !self.encryption_algorithm.nil?
149
+ when '0x0311'
150
+ parse_smb3_capabilities(packet)
151
+ self.session_encrypt_data = self.session_encrypt_data && !self.encryption_algorithm.nil?
152
+ else
153
+ self.session_encrypt_data = false
154
+ end
140
155
  return "SMB#{self.negotiated_smb_version}"
141
156
  else
142
157
  error = 'Unable to negotiate with remote host'
@@ -149,7 +164,7 @@ module RubySMB
149
164
  end
150
165
  end
151
166
 
152
- def parse_smb3_encryption_data(request_packet, response_packet)
167
+ def parse_smb3_capabilities(response_packet)
153
168
  nc = response_packet.find_negotiate_context(
154
169
  RubySMB::SMB2::NegotiateContext::SMB2_PREAUTH_INTEGRITY_CAPABILITIES
155
170
  )
@@ -179,8 +194,6 @@ module RubySMB
179
194
  'Unable to retrieve the encryption cipher list supported by the server from the Negotiate response'
180
195
  )
181
196
  end
182
- update_preauth_hash(request_packet)
183
- update_preauth_hash(response_packet)
184
197
 
185
198
  nc = response_packet.find_negotiate_context(
186
199
  RubySMB::SMB2::NegotiateContext::SMB2_COMPRESSION_CAPABILITIES
@@ -198,6 +211,12 @@ module RubySMB
198
211
  # while being guaranteed to work with any modern Windows system. We can get more sophisticated
199
212
  # with switching this on and off at a later date if the need arises.
200
213
  packet.smb_header.flags2.extended_security = 1
214
+ # Recent Mac OS X requires the unicode flag to be set on the Negotiate
215
+ # SMB Header request, even if this packet does not contain string fields
216
+ # (see Flags2 SMB_FLAGS2_UNICODE definition in "2.2.3.1 The SMB Header"
217
+ # documentation:
218
+ # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/69a29f73-de0c-45a6-a1aa-8ceeea42217f
219
+ packet.smb_header.flags2.unicode = 1
201
220
  # There is no real good reason to ever send an SMB1 Negotiate packet
202
221
  # to Negotiate strictly SMB2, but the protocol WILL support it
203
222
  packet.add_dialect(SMB1_DIALECT_SMB1_DEFAULT) if smb1