ruby_smb 3.0.6 → 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/examples/file_server.rb +8 -1
  4. data/examples/virtual_file_server.rb +143 -0
  5. data/lib/ruby_smb/client/encryption.rb +16 -4
  6. data/lib/ruby_smb/client/negotiation.rb +10 -8
  7. data/lib/ruby_smb/fscc/file_information/file_access_information.rb +15 -0
  8. data/lib/ruby_smb/fscc/file_information/file_alignment_information.rb +45 -0
  9. data/lib/ruby_smb/fscc/file_information/file_all_information.rb +23 -0
  10. data/lib/ruby_smb/fscc/file_information/file_basic_information.rb +20 -0
  11. data/lib/ruby_smb/fscc/file_information/file_both_directory_information.rb +3 -3
  12. data/lib/ruby_smb/fscc/file_information/file_directory_information.rb +3 -3
  13. data/lib/ruby_smb/fscc/file_information/file_ea_information.rb +1 -0
  14. data/lib/ruby_smb/fscc/file_information/file_full_directory_information.rb +3 -3
  15. data/lib/ruby_smb/fscc/file_information/file_id_both_directory_information.rb +3 -3
  16. data/lib/ruby_smb/fscc/file_information/file_id_full_directory_information.rb +3 -3
  17. data/lib/ruby_smb/fscc/file_information/file_internal_information.rb +15 -0
  18. data/lib/ruby_smb/fscc/file_information/file_mode_information.rb +29 -0
  19. data/lib/ruby_smb/fscc/file_information/file_name_information.rb +16 -0
  20. data/lib/ruby_smb/fscc/file_information/file_names_information.rb +1 -1
  21. data/lib/ruby_smb/fscc/file_information/file_normalized_name_information.rb +16 -0
  22. data/lib/ruby_smb/fscc/file_information/file_position_information.rb +15 -0
  23. data/lib/ruby_smb/fscc/file_information/file_rename_information.rb +1 -1
  24. data/lib/ruby_smb/fscc/file_information/file_standard_information.rb +20 -0
  25. data/lib/ruby_smb/fscc/file_information/file_stream_information.rb +3 -0
  26. data/lib/ruby_smb/fscc/file_information.rb +43 -6
  27. data/lib/ruby_smb/fscc/file_system_information/file_fs_attribute_information.rb +1 -0
  28. data/lib/ruby_smb/fscc/file_system_information/file_fs_volume_information.rb +1 -0
  29. data/lib/ruby_smb/fscc/file_system_information.rb +4 -0
  30. data/lib/ruby_smb/gss/provider/ntlm.rb +20 -3
  31. data/lib/ruby_smb/gss/provider.rb +10 -1
  32. data/lib/ruby_smb/server/server_client/encryption.rb +66 -0
  33. data/lib/ruby_smb/server/server_client/negotiation.rb +14 -3
  34. data/lib/ruby_smb/server/server_client/session_setup.rb +21 -4
  35. data/lib/ruby_smb/server/server_client/share_io.rb +17 -0
  36. data/lib/ruby_smb/server/server_client/tree_connect.rb +40 -3
  37. data/lib/ruby_smb/server/server_client.rb +156 -38
  38. data/lib/ruby_smb/server/session.rb +5 -1
  39. data/lib/ruby_smb/server/share/provider/disk/file_system.rb +28 -0
  40. data/lib/ruby_smb/server/share/provider/disk/processor/close.rb +46 -0
  41. data/lib/ruby_smb/server/share/provider/disk/processor/create.rb +143 -0
  42. data/lib/ruby_smb/server/share/provider/disk/processor/query.rb +359 -0
  43. data/lib/ruby_smb/server/share/provider/disk/processor/read.rb +70 -0
  44. data/lib/ruby_smb/server/share/provider/disk/processor.rb +223 -0
  45. data/lib/ruby_smb/server/share/provider/disk.rb +12 -418
  46. data/lib/ruby_smb/server/share/provider/pipe.rb +2 -2
  47. data/lib/ruby_smb/server/share/provider/processor.rb +16 -0
  48. data/lib/ruby_smb/server/share/provider/virtual_disk/virtual_file.rb +85 -0
  49. data/lib/ruby_smb/server/share/provider/virtual_disk/virtual_pathname.rb +196 -0
  50. data/lib/ruby_smb/server/share/provider/virtual_disk/virtual_stat.rb +175 -0
  51. data/lib/ruby_smb/server/share/provider/virtual_disk.rb +116 -0
  52. data/lib/ruby_smb/server/share/provider.rb +1 -0
  53. data/lib/ruby_smb/server.rb +13 -3
  54. data/lib/ruby_smb/signing.rb +18 -4
  55. data/lib/ruby_smb/smb1/commands.rb +1 -0
  56. data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +11 -1
  57. data/lib/ruby_smb/smb1/packet/nt_trans/create_request.rb +1 -1
  58. data/lib/ruby_smb/smb1/packet/read_andx_response.rb +5 -4
  59. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +12 -4
  60. data/lib/ruby_smb/smb1/packet/trans2/data_block.rb +9 -1
  61. data/lib/ruby_smb/smb1/packet/trans2/find_first2_request.rb +52 -51
  62. data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +37 -37
  63. data/lib/ruby_smb/smb1/packet/trans2/find_information_level/find_file_both_directory_info.rb +48 -0
  64. data/lib/ruby_smb/smb1/packet/trans2/find_information_level.rb +28 -15
  65. data/lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb +51 -51
  66. data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +36 -36
  67. data/lib/ruby_smb/smb1/packet/trans2/open2_request.rb +40 -39
  68. data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +40 -40
  69. data/lib/ruby_smb/smb1/packet/trans2/query_file_information_request.rb +60 -0
  70. data/lib/ruby_smb/smb1/packet/trans2/query_file_information_response.rb +59 -0
  71. data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_level/query_fs_attribute_info.rb +31 -0
  72. data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_level.rb +40 -0
  73. data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_request.rb +46 -0
  74. data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_response.rb +59 -0
  75. data/lib/ruby_smb/smb1/packet/trans2/query_information_level/query_file_basic_info.rb +23 -0
  76. data/lib/ruby_smb/smb1/packet/trans2/query_information_level/query_file_standard_info.rb +22 -0
  77. data/lib/ruby_smb/smb1/packet/trans2/query_information_level.rb +62 -0
  78. data/lib/ruby_smb/smb1/packet/trans2/query_path_information_request.rb +65 -0
  79. data/lib/ruby_smb/smb1/packet/trans2/query_path_information_response.rb +59 -0
  80. data/lib/ruby_smb/smb1/packet/trans2/request.rb +24 -8
  81. data/lib/ruby_smb/smb1/packet/trans2/request_secondary.rb +4 -4
  82. data/lib/ruby_smb/smb1/packet/trans2/response.rb +29 -20
  83. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb +42 -42
  84. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +23 -23
  85. data/lib/ruby_smb/smb1/packet/trans2/subcommands.rb +23 -5
  86. data/lib/ruby_smb/smb1/packet/trans2.rb +4 -0
  87. data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +4 -1
  88. data/lib/ruby_smb/smb2/negotiate_context.rb +10 -1
  89. data/lib/ruby_smb/smb2/packet/transform_header.rb +7 -7
  90. data/lib/ruby_smb/smb2/tree.rb +1 -0
  91. data/lib/ruby_smb/smb2.rb +1 -0
  92. data/lib/ruby_smb/version.rb +1 -1
  93. data/spec/lib/ruby_smb/client_spec.rb +31 -8
  94. data/spec/lib/ruby_smb/fscc/file_information/file_access_information_spec.rb +21 -0
  95. data/spec/lib/ruby_smb/fscc/file_information/file_alignment_information_spec.rb +21 -0
  96. data/spec/lib/ruby_smb/fscc/file_information/file_all_information_spec.rb +61 -0
  97. data/spec/lib/ruby_smb/fscc/file_information/file_basic_information_spec.rb +41 -0
  98. data/spec/lib/ruby_smb/fscc/file_information/file_both_directory_information_spec.rb +59 -10
  99. data/spec/lib/ruby_smb/fscc/file_information/file_directory_information_spec.rb +30 -12
  100. data/spec/lib/ruby_smb/fscc/file_information/file_ea_information_spec.rb +21 -0
  101. data/spec/lib/ruby_smb/fscc/file_information/file_full_directory_information_spec.rb +30 -12
  102. data/spec/lib/ruby_smb/fscc/file_information/file_id_both_directory_information_spec.rb +63 -10
  103. data/spec/lib/ruby_smb/fscc/file_information/file_id_full_directory_information_spec.rb +30 -12
  104. data/spec/lib/ruby_smb/fscc/file_information/file_internal_information_spec.rb +21 -0
  105. data/spec/lib/ruby_smb/fscc/file_information/file_mode_information_spec.rb +21 -0
  106. data/spec/lib/ruby_smb/fscc/file_information/file_name_information_spec.rb +44 -0
  107. data/spec/lib/ruby_smb/fscc/file_information/file_names_information_spec.rb +30 -12
  108. data/spec/lib/ruby_smb/fscc/file_information/file_network_open_information_spec.rb +51 -0
  109. data/spec/lib/ruby_smb/fscc/file_information/file_normalized_name_information_spec.rb +44 -0
  110. data/spec/lib/ruby_smb/fscc/file_information/file_position_information_spec.rb +21 -0
  111. data/spec/lib/ruby_smb/fscc/file_information/file_rename_information_spec.rb +1 -1
  112. data/spec/lib/ruby_smb/fscc/file_information/file_standard_information_spec.rb +41 -0
  113. data/spec/lib/ruby_smb/fscc/file_information/file_stream_information_spec.rb +51 -0
  114. data/spec/lib/ruby_smb/fscc/file_information_spec.rb +14 -0
  115. data/spec/lib/ruby_smb/fscc/file_system_information/file_fs_attribute_information_spec.rb +46 -0
  116. data/spec/lib/ruby_smb/fscc/file_system_information/file_fs_volume_information_spec.rb +51 -0
  117. data/spec/lib/ruby_smb/fscc/file_system_information_spec.rb +14 -0
  118. data/spec/lib/ruby_smb/server/server_client_spec.rb +15 -0
  119. data/spec/lib/ruby_smb/server/share/provider/virtual_disk/virtual_pathname_spec.rb +581 -0
  120. data/spec/lib/ruby_smb/server/share/provider/virtual_disk/virtual_stat_spec.rb +207 -0
  121. data/spec/lib/ruby_smb/server/share/provider/virtual_disk_spec.rb +122 -0
  122. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_request_spec.rb +2 -2
  123. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +36 -2
  124. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_request_spec.rb +2 -2
  125. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +35 -1
  126. data/spec/lib/ruby_smb/smb1/packet/trans2/query_file_information_request_spec.rb +74 -0
  127. data/spec/lib/ruby_smb/smb1/packet/trans2/query_file_information_response_spec.rb +96 -0
  128. data/spec/lib/ruby_smb/smb1/packet/trans2/query_fs_information_request_spec.rb +62 -0
  129. data/spec/lib/ruby_smb/smb1/packet/trans2/query_fs_information_response_spec.rb +88 -0
  130. data/spec/lib/ruby_smb/smb1/packet/trans2/query_path_information_request_spec.rb +79 -0
  131. data/spec/lib/ruby_smb/smb1/packet/trans2/query_path_information_response_spec.rb +96 -0
  132. data/spec/lib/ruby_smb/smb1/packet/trans2/request_spec.rb +2 -2
  133. data/spec/lib/ruby_smb/smb1/packet/trans2/response_spec.rb +3 -3
  134. data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_request_spec.rb +3 -2
  135. data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_response_spec.rb +7 -2
  136. data/spec/lib/ruby_smb/smb1/tree_spec.rb +3 -3
  137. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +2 -2
  138. data.tar.gz.sig +0 -0
  139. metadata +88 -2
  140. metadata.gz.sig +0 -0
@@ -6,18 +6,20 @@ module RubySMB
6
6
 
7
7
  require 'ruby_smb/dialect'
8
8
  require 'ruby_smb/signing'
9
+ require 'ruby_smb/server/server_client/encryption'
9
10
  require 'ruby_smb/server/server_client/negotiation'
10
11
  require 'ruby_smb/server/server_client/session_setup'
11
12
  require 'ruby_smb/server/server_client/share_io'
12
13
  require 'ruby_smb/server/server_client/tree_connect'
13
14
 
14
15
  include RubySMB::Signing
16
+ include RubySMB::Server::ServerClient::Encryption
15
17
  include RubySMB::Server::ServerClient::Negotiation
16
18
  include RubySMB::Server::ServerClient::SessionSetup
17
19
  include RubySMB::Server::ServerClient::ShareIO
18
20
  include RubySMB::Server::ServerClient::TreeConnect
19
21
 
20
- attr_reader :dialect, :session_table
22
+ attr_reader :dialect, :dispatcher, :session_table
21
23
 
22
24
  # @param [Server] server the server that accepted this connection
23
25
  # @param [Dispatcher::Socket] dispatcher the connection's socket dispatcher
@@ -25,6 +27,8 @@ module RubySMB
25
27
  @server = server
26
28
  @dispatcher = dispatcher
27
29
  @dialect = nil
30
+ @sequence_counter = 0
31
+ @cipher_id = 0
28
32
  @gss_authenticator = server.gss_provider.new_authenticator(self)
29
33
  @preauth_integrity_hash_algorithm = nil
30
34
  @preauth_integrity_hash_value = nil
@@ -53,6 +57,14 @@ module RubySMB
53
57
  @dispatcher.tcp_socket.getpeername
54
58
  end
55
59
 
60
+ def peerhost
61
+ ::Socket::unpack_sockaddr_in(getpeername)[1]
62
+ end
63
+
64
+ def peerport
65
+ ::Socket::unpack_sockaddr_in(getpeername)[0]
66
+ end
67
+
56
68
  #
57
69
  # Handle a request after the dialect has been negotiated. This is the main
58
70
  # handler for all requests after the connection has been established. If a
@@ -75,8 +87,10 @@ module RubySMB
75
87
 
76
88
  begin
77
89
  response = handle_smb1(raw_request, header)
78
- rescue NotImplementedError
79
- logger.error("Caught a NotImplementedError while handling a #{SMB1::Commands.name(header.command)} request")
90
+ rescue NotImplementedError => e
91
+ message = "Caught a NotImplementedError while handling a #{SMB1::Commands.name(header.command)} request"
92
+ message << " (#{e.message})" if e.message
93
+ logger.error(message)
80
94
  response = RubySMB::SMB1::Packet::EmptyPacket.new
81
95
  response.smb_header.nt_status = WindowsError::NTStatus::STATUS_NOT_SUPPORTED
82
96
  end
@@ -86,6 +100,12 @@ module RubySMB
86
100
  if response.is_a?(SMB1::Packet::EmptyPacket)
87
101
  response.smb_header.command = header.command if response.smb_header.command == 0
88
102
  response.smb_header.flags.reply = 1
103
+ nt_status = response.smb_header.nt_status.to_i
104
+ message = "Sending an error packet for SMB1 command: #{SMB1::Commands.name(header.command)}, status: 0x#{nt_status.to_s(16).rjust(8, '0')}"
105
+ if (nt_status_name = WindowsError::NTStatus.find_by_retval(nt_status).first&.name)
106
+ message << " (#{nt_status_name})"
107
+ end
108
+ logger.info(message)
89
109
  end
90
110
 
91
111
  response.smb_header.pid_high = header.pid_high if response.smb_header.pid_high == 0
@@ -95,33 +115,23 @@ module RubySMB
95
115
  response.smb_header.mid = header.mid if response.smb_header.mid == 0
96
116
  end
97
117
  when RubySMB::SMB2::SMB2_PROTOCOL_ID
118
+ response = _handle_smb2(raw_request)
119
+ when RubySMB::SMB2::SMB2_TRANSFORM_PROTOCOL_ID
98
120
  begin
99
- header = RubySMB::SMB2::SMB2Header.read(raw_request)
121
+ header = RubySMB::SMB2::Packet::TransformHeader.read(raw_request)
100
122
  rescue IOError => e
101
- logger.error("Caught a #{e.class} while reading the SMB2 header (#{e.message})")
123
+ logger.error("Caught a #{e.class} while reading the SMB3 Transform header")
102
124
  disconnect!
103
125
  return
104
126
  end
105
127
 
106
128
  begin
107
- response = handle_smb2(raw_request, header)
129
+ response = handle_smb3_transform(raw_request, header)
108
130
  rescue NotImplementedError
109
- logger.error("Caught a NotImplementedError while handling a #{SMB2::Commands.name(header.command)} request")
131
+ logger.error("Caught a NotImplementedError while handling a SMB3 Transform request")
110
132
  response = SMB2::Packet::ErrorPacket.new
111
133
  response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_NOT_SUPPORTED
112
- end
113
-
114
- unless response.nil?
115
- # set these header fields if they were not initialized
116
- if response.is_a?(SMB2::Packet::ErrorPacket)
117
- response.smb2_header.command = header.command if response.smb2_header.command == 0
118
- response.smb2_header.flags.reply = 1
119
- end
120
-
121
- response.smb2_header.credits = 1 if response.smb2_header.credits == 0
122
- response.smb2_header.message_id = header.message_id if response.smb2_header.message_id == 0
123
- response.smb2_header.session_id = header.session_id if response.smb2_header.session_id == 0
124
- response.smb2_header.tree_id = header.tree_id if response.smb2_header.tree_id == 0
134
+ response.smb2_header.session_id = header.session_id
125
135
  end
126
136
  end
127
137
 
@@ -193,17 +203,8 @@ module RubySMB
193
203
 
194
204
  packet = @dispatcher.recv_packet
195
205
  if packet && packet.length >= 4 && packet[0...4].unpack1('L>') == RubySMB::SMB2::SMB2_PROTOCOL_ID
196
- header = RubySMB::SMB2::SMB2Header.read(packet)
197
- unless header.next_command == 0
198
- until header.next_command == 0
199
- @in_packet_queue.push(packet[0...header.next_command])
200
- packet = packet[header.next_command..-1]
201
- header = RubySMB::SMB2::SMB2Header.read(packet)
202
- end
203
-
204
- @in_packet_queue.push(packet)
205
- packet = @in_packet_queue.shift
206
- end
206
+ @in_packet_queue += split_smb2_chain(packet)
207
+ packet = @in_packet_queue.shift
207
208
  end
208
209
 
209
210
  packet
@@ -214,16 +215,24 @@ module RubySMB
214
215
  #
215
216
  # @param [GenericPacket] packet the packet to send
216
217
  def send_packet(packet)
217
- case metadialect&.order
218
- when Dialect::ORDER_SMB1
218
+ case metadialect&.family
219
+ when Dialect::FAMILY_SMB1
219
220
  session_id = packet.smb_header.uid
220
- when Dialect::ORDER_SMB2
221
+ when Dialect::FAMILY_SMB2
221
222
  session_id = packet.smb2_header.session_id
223
+ when Dialect::FAMILY_SMB3
224
+ if packet.is_a?(RubySMB::SMB2::Packet::TransformHeader)
225
+ session_id = packet.session_id
226
+ else
227
+ session_id = packet.smb2_header.session_id
228
+ end
222
229
  end
223
230
  session = @session_table[session_id]
224
231
 
225
- unless session.nil? || session.is_anonymous || session.key.nil?
232
+ unless session.nil? || session.is_anonymous || session.key.nil? || packet.is_a?(RubySMB::SMB2::Packet::TransformHeader)
226
233
  case metadialect&.family
234
+ when Dialect::FAMILY_SMB1
235
+ packet = Signing::smb1_sign(packet, session.key, @sequence_counter)
227
236
  when Dialect::FAMILY_SMB2
228
237
  packet = Signing::smb2_sign(packet, session.key)
229
238
  when Dialect::FAMILY_SMB3
@@ -231,6 +240,7 @@ module RubySMB
231
240
  end
232
241
  end
233
242
 
243
+ @sequence_counter += 1
234
244
  @dispatcher.send_packet(packet)
235
245
  end
236
246
 
@@ -260,12 +270,36 @@ module RubySMB
260
270
  # @param [RubySMB::SMB1::SMBHeader] header The request header.
261
271
  # @return [RubySMB::GenericPacket]
262
272
  def handle_smb1(raw_request, header)
263
- # session = @session_table[header.uid]
264
- session = nil
273
+ session = @session_table[header.uid]
274
+
275
+ if session.nil? && !(header.command == SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX && header.uid == 0)
276
+ response = SMB1::Packet::EmptyPacket.new
277
+ response.smb_header.nt_status = WindowsError::NTStatus::STATUS_USER_SESSION_DELETED
278
+ return response
279
+ end
280
+ if session&.state == :expired
281
+ response = SMB1::Packet::EmptyPacket.new
282
+ response.smb_header.nt_status = WindowsError::NTStatus::STATUS_NETWORK_SESSION_EXPIRED
283
+ return response
284
+ end
265
285
 
266
286
  case header.command
287
+ when SMB1::Commands::SMB_COM_CLOSE
288
+ dispatcher, request_class = :do_close_smb1, SMB1::Packet::CloseRequest
289
+ when SMB1::Commands::SMB_COM_TREE_DISCONNECT
290
+ dispatcher, request_class = :do_tree_disconnect_smb1, SMB1::Packet::TreeDisconnectRequest
291
+ when SMB1::Commands::SMB_COM_LOGOFF_ANDX
292
+ dispatcher, request_class = :do_logoff_andx_smb1, SMB1::Packet::LogoffRequest
293
+ when SMB1::Commands::SMB_COM_NT_CREATE_ANDX
294
+ dispatcher, request_class = :do_nt_create_andx_smb1, SMB1::Packet::NtCreateAndxRequest
295
+ when SMB1::Commands::SMB_COM_READ_ANDX
296
+ dispatcher, request_class = :do_read_andx_smb1, SMB1::Packet::ReadAndxRequest
267
297
  when SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
268
- dispatcher, request_class = :do_session_setup_smb1, SMB1::Packet::SessionSetupRequest
298
+ dispatcher, request_class = :do_session_setup_andx_smb1, SMB1::Packet::SessionSetupRequest
299
+ when SMB1::Commands::SMB_COM_TRANSACTION2
300
+ dispatcher, request_class = :do_transactions2_smb1, SMB1::Packet::Trans2::Request
301
+ when SMB1::Commands::SMB_COM_TREE_CONNECT
302
+ dispatcher, request_class = :do_tree_connect_smb1, SMB1::Packet::TreeConnectRequest
269
303
  else
270
304
  logger.warn("The SMB1 #{SMB1::Commands.name(header.command)} command is not supported")
271
305
  raise NotImplementedError
@@ -276,10 +310,13 @@ module RubySMB
276
310
  rescue IOError, RubySMB::Error::InvalidPacket => e
277
311
  logger.error("Caught a #{e.class} while reading the SMB1 #{request_class} (#{e.message})")
278
312
  response = RubySMB::SMB1::Packet::EmptyPacket.new
313
+ response.smb_header.nt_status = WindowsError::NTStatus::STATUS_DATA_ERROR
314
+ return response
279
315
  end
280
316
 
281
317
  if request.is_a?(SMB1::Packet::EmptyPacket)
282
318
  logger.error("Received an error packet for SMB1 command: #{SMB1::Commands.name(header.command)}")
319
+ response = RubySMB::SMB1::Packet::EmptyPacket.new
283
320
  response.smb_header.nt_status = WindowsError::NTStatus::STATUS_DATA_ERROR
284
321
  return response
285
322
  end
@@ -304,6 +341,11 @@ module RubySMB
304
341
  response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_USER_SESSION_DELETED
305
342
  return response
306
343
  end
344
+ if session&.state == :expired
345
+ response = SMB2::Packet::ErrorPacket.new
346
+ response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_NETWORK_SESSION_EXPIRED
347
+ return response
348
+ end
307
349
 
308
350
  case header.command
309
351
  when SMB2::Commands::CLOSE
@@ -347,6 +389,82 @@ module RubySMB
347
389
  logger.debug("Dispatching request to #{dispatcher} (session: #{session.inspect})")
348
390
  send(dispatcher, request, session)
349
391
  end
392
+
393
+ def _handle_smb2(raw_request)
394
+ begin
395
+ header = RubySMB::SMB2::SMB2Header.read(raw_request)
396
+ rescue IOError => e
397
+ logger.error("Caught a #{e.class} while reading the SMB2 header (#{e.message})")
398
+ disconnect!
399
+ return
400
+ end
401
+
402
+ begin
403
+ response = handle_smb2(raw_request, header)
404
+ rescue NotImplementedError
405
+ logger.error("Caught a NotImplementedError while handling a #{SMB2::Commands.name(header.command)} request")
406
+ response = SMB2::Packet::ErrorPacket.new
407
+ response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_NOT_SUPPORTED
408
+ end
409
+
410
+ unless response.nil?
411
+ # set these header fields if they were not initialized
412
+ if response.is_a?(SMB2::Packet::ErrorPacket)
413
+ response.smb2_header.command = header.command if response.smb2_header.command == 0
414
+ response.smb2_header.flags.reply = 1
415
+ nt_status = response.smb2_header.nt_status.to_i
416
+ message = "Sending an error packet for SMB2 command: #{SMB2::Commands.name(header.command)}, status: 0x#{nt_status.to_s(16).rjust(8, '0')}"
417
+ if (nt_status_name = WindowsError::NTStatus.find_by_retval(nt_status).first&.name)
418
+ message << " (#{nt_status_name})"
419
+ end
420
+ logger.info(message)
421
+ end
422
+
423
+ response.smb2_header.credits = 1 if response.smb2_header.credits == 0
424
+ response.smb2_header.message_id = header.message_id if response.smb2_header.message_id == 0
425
+ response.smb2_header.session_id = header.session_id if response.smb2_header.session_id == 0
426
+ response.smb2_header.tree_id = header.tree_id if response.smb2_header.tree_id == 0
427
+ end
428
+
429
+ response
430
+ end
431
+
432
+ def handle_smb3_transform(raw_request, header)
433
+ session = @session_table[header.session_id]
434
+ if session.nil?
435
+ response = SMB2::Packet::ErrorPacket.new
436
+ response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_USER_SESSION_DELETED
437
+ return response
438
+ end
439
+
440
+ chain = split_smb2_chain(smb3_decrypt(header, session))
441
+ chain[0...-1].each do |pt_raw_request|
442
+ pt_response = _handle_smb2(pt_raw_request)
443
+ return if pt_response.nil?
444
+
445
+ send_packet(smb3_encrypt(pt_response, session))
446
+ end
447
+
448
+ pt_response = _handle_smb2(chain.last)
449
+ return if pt_response.nil?
450
+
451
+ smb3_encrypt(pt_response, session)
452
+ end
453
+
454
+ def split_smb2_chain(buffer)
455
+ chain = []
456
+ header = RubySMB::SMB2::SMB2Header.read(buffer)
457
+ unless header.next_command == 0
458
+ until header.next_command == 0
459
+ chain << buffer[0...header.next_command]
460
+ buffer = buffer[header.next_command..-1]
461
+ header = RubySMB::SMB2::SMB2Header.read(buffer)
462
+ end
463
+ end
464
+
465
+ chain << buffer
466
+ chain
467
+ end
350
468
  end
351
469
  end
352
470
  end
@@ -64,7 +64,7 @@ module RubySMB
64
64
  attr_accessor :tree_connect_table
65
65
 
66
66
  # Untyped hash for storing additional arbitrary metadata about the current session
67
- # @!attribute [rw] metadaa
67
+ # @!attribute [rw] metadata
68
68
  # @return [Hash]
69
69
  attr_accessor :metadata
70
70
 
@@ -72,6 +72,10 @@ module RubySMB
72
72
  # @!attribute [r] creation_time
73
73
  # @return [Time]
74
74
  attr_reader :creation_time
75
+
76
+ # Whether or not the authenticated user is a guest.
77
+ # @!attribute [rw] is_guest
78
+ attr_accessor :is_guest
75
79
  end
76
80
  end
77
81
  end
@@ -0,0 +1,28 @@
1
+ module RubySMB
2
+ class Server
3
+ module Share
4
+ module Provider
5
+ class Disk < Base
6
+ module FileSystem
7
+ # define attributes of the file system to emulate
8
+ FileSystem = Struct.new(
9
+ :name,
10
+ :max_name_bytes,
11
+ :case_sensitive_search,
12
+ :case_preserved_names,
13
+ :unicode_on_disk
14
+ )
15
+
16
+ NTFS = FileSystem.new(
17
+ 'NTFS',
18
+ 255,
19
+ true,
20
+ true,
21
+ true
22
+ )
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,46 @@
1
+ require 'ruby_smb/server/share/provider/processor'
2
+
3
+ module RubySMB
4
+ class Server
5
+ module Share
6
+ module Provider
7
+ class Disk < Base
8
+ class Processor < Provider::Processor::Base
9
+ module Close
10
+ def do_close_smb1(request)
11
+ # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/99b767e2-8f0e-438b-ace5-4323940f2dc8
12
+ handle = @handles.delete(request.parameter_block.fid)
13
+ if handle.nil?
14
+ response = RubySMB::SMB1::Packet::EmptyPacket.new
15
+ response.smb_header.nt_status = WindowsError::NTStatus::STATUS_INVALID_HANDLE
16
+ return response
17
+ end
18
+
19
+ handle.file.close if handle.file
20
+
21
+ response = RubySMB::SMB1::Packet::CloseResponse.new
22
+ response
23
+ end
24
+
25
+ def do_close_smb2(request)
26
+ handle = @handles.delete(request.file_id.to_binary_s)
27
+ if handle.nil?
28
+ response = RubySMB::SMB2::Packet::ErrorPacket.new
29
+ response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_FILE_CLOSED
30
+ return response
31
+ end
32
+
33
+ handle.file.close if handle.file
34
+
35
+ response = RubySMB::SMB2::Packet::CloseResponse.new
36
+ set_common_info(response, handle.local_path)
37
+ response.flags = 1
38
+ response
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,143 @@
1
+ require 'zlib'
2
+ require 'ruby_smb/server/share/provider/processor'
3
+
4
+ module RubySMB
5
+ class Server
6
+ module Share
7
+ module Provider
8
+ class Disk < Base
9
+ class Processor < Provider::Processor::Base
10
+ module Create
11
+ def do_nt_create_andx_smb1(request)
12
+ path = request.data_block.file_name.snapshot
13
+ path = path.encode.gsub(/\/|\\/, File::SEPARATOR)
14
+ path = path.delete_prefix(File::SEPARATOR)
15
+ local_path = get_local_path(path)
16
+ unless local_path && (local_path.file? || local_path.directory?)
17
+ logger.warn("Requested path does not exist: #{local_path}")
18
+ response = SMB1::Packet::EmptyPacket.new
19
+ response.smb_header.nt_status = WindowsError::NTStatus::STATUS_NO_SUCH_FILE
20
+ return response
21
+ end
22
+
23
+ response = SMB1::Packet::NtCreateAndxResponse.new
24
+ block = response.parameter_block
25
+ block.fid = rand(0xffff)
26
+ # fields are slightly different so #set_common_info can't be used :(
27
+ begin
28
+ block.create_time = local_path.birthtime
29
+ rescue NotImplementedError
30
+ logger.warn("The file system does not support #birthtime for #{path}")
31
+ end
32
+
33
+ block.last_access_time = local_path.atime
34
+ block.last_write_time = local_path.mtime
35
+ block.last_change_time = local_path.ctime
36
+ if local_path.file?
37
+ block.end_of_file = local_path.size
38
+ block.allocation_size = get_allocation_size(local_path)
39
+ end
40
+
41
+ @handles[response.parameter_block.fid] = Handle.new(path, local_path, false, local_path.file? ? local_path.open : nil)
42
+ response
43
+ end
44
+
45
+ def do_create_smb2(request)
46
+ unless request.create_disposition == RubySMB::Dispositions::FILE_OPEN
47
+ logger.warn("Can not handle CREATE request for disposition: #{request.create_disposition}")
48
+ raise NotImplementedError
49
+ end
50
+
51
+ # process the delayed io fields
52
+ request.name.read_now!
53
+ unless request.contexts_offset == 0
54
+ request.contexts.read_now!
55
+ request.contexts.each do |context|
56
+ context.name.read_now!
57
+ context.data.read_now!
58
+ end
59
+ end
60
+
61
+ path = request.name.snapshot
62
+ local_path = get_local_path(path)
63
+ unless local_path && (local_path.file? || local_path.directory?)
64
+ logger.warn("Requested path does not exist: #{local_path}")
65
+ response = RubySMB::SMB2::Packet::ErrorPacket.new
66
+ response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_OBJECT_NAME_NOT_FOUND
67
+ return response
68
+ end
69
+
70
+ durable = false
71
+ response = RubySMB::SMB2::Packet::CreateResponse.new
72
+ response.create_action = RubySMB::CreateActions::FILE_OPENED
73
+ set_common_info(response, local_path)
74
+ response.file_id.persistent = Zlib::crc32(path)
75
+ response.file_id.volatile = rand(0xffffffff)
76
+
77
+ request.contexts.each do |req_ctx|
78
+ case req_ctx.name
79
+ when SMB2::CreateContext::CREATE_DURABLE_HANDLE
80
+ # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/9adbc354-5fad-40e7-9a62-4a4b6c1ff8a0
81
+ next if request.contexts.any? { |ctx| ctx.name == SMB2::CreateContext::CREATE_DURABLE_HANDLE_RECONNECT }
82
+
83
+ if request.contexts.any? { |ctx| [ SMB2::CreateContext::CREATE_DURABLE_HANDLE_V2, SMB2::CreateContext::CREATE_DURABLE_HANDLE_RECONNECT_v2 ].include?(ctx.name) }
84
+ response = RubySMB::SMB2::Packet::ErrorPacket.new
85
+ response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_INVALID_PARAMETER
86
+ return response
87
+ end
88
+
89
+ durable = true
90
+ res_ctx = SMB2::CreateContext::CreateDurableHandleResponse.new
91
+ when SMB2::CreateContext::CREATE_DURABLE_HANDLE_V2
92
+ # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/33e6800a-adf5-4221-af27-7e089b9e81d1
93
+ if request.contexts.any? { |ctx| [ SMB2::CreateContext::CREATE_DURABLE_HANDLE, SMB2::CreateContext::CREATE_DURABLE_HANDLE_RECONNECT, SMB2::CreateContext::CREATE_DURABLE_HANDLE_RECONNECT_v2 ].include?(ctx.name) }
94
+ response = RubySMB::SMB2::Packet::ErrorPacket.new
95
+ response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_INVALID_PARAMETER
96
+ return response
97
+ end
98
+
99
+ durable = true
100
+ res_ctx = SMB2::CreateContext::CreateDurableHandleV2Response.new(
101
+ timeout: 1000,
102
+ flags: req_ctx.data.flags
103
+ )
104
+ when SMB2::CreateContext::CREATE_QUERY_MAXIMAL_ACCESS
105
+ res_ctx = SMB2::CreateContext::CreateQueryMaximalAccessResponse.new(
106
+ maximal_access: maximal_access(path)
107
+ )
108
+ when SMB2::CreateContext::CREATE_QUERY_ON_DISK_ID
109
+ res_ctx = SMB2::CreateContext::CreateQueryOnDiskIdResponse.new(
110
+ disk_file_id: local_path.stat.ino,
111
+ volume_id: local_path.stat.dev
112
+ )
113
+ else
114
+ logger.warn("Can not handle CREATE context: #{req_ctx.name}")
115
+ next
116
+ end
117
+
118
+ response.contexts << SMB2::CreateContext::CreateContextResponse.new(name: res_ctx.class::NAME, data: res_ctx)
119
+ end
120
+
121
+ if response.contexts.length > 0
122
+ # fixup the offsets
123
+ response.contexts[0...-1].each do |ctx|
124
+ ctx.next_offset = ctx.num_bytes
125
+ end
126
+ response.contexts[-1].next_offset = 0
127
+ response.contexts_offset = response.buffer.abs_offset
128
+ response.contexts_length = response.buffer.num_bytes
129
+ else
130
+ response.contexts_offset = 0
131
+ response.contexts_length = 0
132
+ end
133
+
134
+ @handles[response.file_id.to_binary_s] = Handle.new(path, local_path, durable, local_path.file? ? local_path.open : nil)
135
+ response
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end