ruby_smb 3.0.0 → 3.0.4

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 (102) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/examples/anonymous_auth.rb +29 -6
  4. data/examples/auth_capture.rb +28 -0
  5. data/examples/file_server.rb +76 -0
  6. data/examples/read_file.rb +51 -10
  7. data/examples/tree_connect.rb +49 -8
  8. data/lib/ruby_smb/client/authentication.rb +11 -3
  9. data/lib/ruby_smb/client.rb +16 -2
  10. data/lib/ruby_smb/create_actions.rb +21 -0
  11. data/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_encrypt_file_srv_request.rb +20 -0
  12. data/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_encrypt_file_srv_response.rb +20 -0
  13. data/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_open_file_raw_request.rb +21 -0
  14. data/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_open_file_raw_response.rb +21 -0
  15. data/lib/ruby_smb/dcerpc/encrypting_file_system.rb +44 -0
  16. data/lib/ruby_smb/dcerpc/print_system/rpc_add_printer_driver_ex_request.rb +22 -0
  17. data/lib/ruby_smb/dcerpc/print_system/rpc_add_printer_driver_ex_response.rb +20 -0
  18. data/lib/ruby_smb/dcerpc/print_system/rpc_enum_printer_drivers_request.rb +24 -0
  19. data/lib/ruby_smb/dcerpc/print_system/rpc_enum_printer_drivers_response.rb +23 -0
  20. data/lib/ruby_smb/dcerpc/print_system/rpc_get_printer_driver_directory_request.rb +24 -0
  21. data/lib/ruby_smb/dcerpc/print_system/rpc_get_printer_driver_directory_response.rb +22 -0
  22. data/lib/ruby_smb/dcerpc/print_system.rb +69 -0
  23. data/lib/ruby_smb/dcerpc.rb +2 -2
  24. data/lib/ruby_smb/field/nt_status.rb +20 -1
  25. data/lib/ruby_smb/fscc/file_information/file_ea_information.rb +14 -0
  26. data/lib/ruby_smb/fscc/file_information/file_network_open_information.rb +22 -0
  27. data/lib/ruby_smb/fscc/file_information/file_stream_information.rb +16 -0
  28. data/lib/ruby_smb/fscc/file_information.rb +29 -0
  29. data/lib/ruby_smb/fscc/file_system_information/file_fs_attribute_information.rb +46 -0
  30. data/lib/ruby_smb/fscc/file_system_information/file_fs_volume_information.rb +19 -0
  31. data/lib/ruby_smb/fscc/file_system_information.rb +22 -0
  32. data/lib/ruby_smb/fscc.rb +1 -0
  33. data/lib/ruby_smb/generic_packet.rb +6 -0
  34. data/lib/ruby_smb/gss/provider/authenticator.rb +4 -0
  35. data/lib/ruby_smb/gss/provider/ntlm.rb +13 -3
  36. data/lib/ruby_smb/server/server_client/negotiation.rb +0 -2
  37. data/lib/ruby_smb/server/server_client/session_setup.rb +43 -32
  38. data/lib/ruby_smb/server/server_client/share_io.rb +28 -0
  39. data/lib/ruby_smb/server/server_client/tree_connect.rb +60 -0
  40. data/lib/ruby_smb/server/server_client.rb +214 -24
  41. data/lib/ruby_smb/server/session.rb +71 -0
  42. data/lib/ruby_smb/server/share/provider/disk.rb +437 -0
  43. data/lib/ruby_smb/server/share/provider/pipe.rb +27 -0
  44. data/lib/ruby_smb/server/share/provider/processor.rb +76 -0
  45. data/lib/ruby_smb/server/share/provider.rb +38 -0
  46. data/lib/ruby_smb/server/share.rb +11 -0
  47. data/lib/ruby_smb/server.rb +35 -3
  48. data/lib/ruby_smb/signing.rb +37 -11
  49. data/lib/ruby_smb/smb1/commands.rb +4 -0
  50. data/lib/ruby_smb/smb1/tree.rb +87 -79
  51. data/lib/ruby_smb/smb1.rb +0 -1
  52. data/lib/ruby_smb/smb2/bit_field/smb2_header_flags.rb +2 -1
  53. data/lib/ruby_smb/smb2/commands.rb +4 -0
  54. data/lib/ruby_smb/smb2/create_context/request.rb +64 -0
  55. data/lib/ruby_smb/smb2/create_context/response.rb +62 -0
  56. data/lib/ruby_smb/smb2/create_context.rb +74 -22
  57. data/lib/ruby_smb/smb2/packet/create_request.rb +44 -11
  58. data/lib/ruby_smb/smb2/packet/create_response.rb +17 -3
  59. data/lib/ruby_smb/smb2/packet/query_directory_request.rb +1 -1
  60. data/lib/ruby_smb/smb2/packet/query_directory_response.rb +2 -2
  61. data/lib/ruby_smb/smb2/packet/query_info_request.rb +43 -0
  62. data/lib/ruby_smb/smb2/packet/query_info_response.rb +23 -0
  63. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +1 -1
  64. data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +1 -0
  65. data/lib/ruby_smb/smb2/packet.rb +2 -0
  66. data/lib/ruby_smb/smb2/tree.rb +80 -70
  67. data/lib/ruby_smb/smb2.rb +11 -0
  68. data/lib/ruby_smb/smb_error.rb +110 -0
  69. data/lib/ruby_smb/version.rb +1 -1
  70. data/lib/ruby_smb.rb +2 -0
  71. data/ruby_smb.gemspec +1 -1
  72. data/spec/lib/ruby_smb/client_spec.rb +10 -0
  73. data/spec/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_encrypt_file_srv_request_spec.rb +30 -0
  74. data/spec/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_encrypt_file_srv_response_spec.rb +30 -0
  75. data/spec/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_open_file_raw_request_spec.rb +38 -0
  76. data/spec/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_open_file_raw_response_spec.rb +38 -0
  77. data/spec/lib/ruby_smb/dcerpc/print_system/driver_container_spec.rb +41 -0
  78. data/spec/lib/ruby_smb/dcerpc/print_system/driver_info2_spec.rb +64 -0
  79. data/spec/lib/ruby_smb/dcerpc/print_system/rpc_add_printer_driver_ex_request_spec.rb +59 -0
  80. data/spec/lib/ruby_smb/dcerpc/print_system/rpc_add_printer_driver_ex_response_spec.rb +30 -0
  81. data/spec/lib/ruby_smb/dcerpc/print_system/rpc_enum_printer_drivers_request_spec.rb +62 -0
  82. data/spec/lib/ruby_smb/dcerpc/print_system/rpc_enum_printer_drivers_response_spec.rb +54 -0
  83. data/spec/lib/ruby_smb/dcerpc/print_system/rpc_get_printer_driver_directory_request_spec.rb +62 -0
  84. data/spec/lib/ruby_smb/dcerpc/print_system/rpc_get_printer_driver_directory_response_spec.rb +46 -0
  85. data/spec/lib/ruby_smb/field/nt_status_spec.rb +6 -2
  86. data/spec/lib/ruby_smb/gss/provider/ntlm/authenticator_spec.rb +4 -0
  87. data/spec/lib/ruby_smb/server/server_client_spec.rb +36 -53
  88. data/spec/lib/ruby_smb/server/session_spec.rb +38 -0
  89. data/spec/lib/ruby_smb/server/share/provider/disk_spec.rb +61 -0
  90. data/spec/lib/ruby_smb/server/share/provider/pipe_spec.rb +31 -0
  91. data/spec/lib/ruby_smb/server/share/provider_spec.rb +13 -0
  92. data/spec/lib/ruby_smb/smb1/tree_spec.rb +3 -3
  93. data/spec/lib/ruby_smb/smb2/bit_field/header_flags_spec.rb +8 -2
  94. data/spec/lib/ruby_smb/smb2/{create_context_spec.rb → create_context/create_context_request_spec.rb} +1 -1
  95. data/spec/lib/ruby_smb/smb2/packet/create_request_spec.rb +5 -5
  96. data/spec/lib/ruby_smb/smb2/packet/create_response_spec.rb +9 -5
  97. data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +3 -2
  98. data/spec/lib/ruby_smb/smb2/tree_spec.rb +3 -3
  99. data.tar.gz.sig +0 -0
  100. metadata +71 -7
  101. metadata.gz.sig +0 -0
  102. data/lib/ruby_smb/smb1/create_actions.rb +0 -20
@@ -0,0 +1,60 @@
1
+ module RubySMB
2
+ class Server
3
+ class ServerClient
4
+ MAX_TREE_CONNECTIONS = 1000
5
+ module TreeConnect
6
+ def do_tree_connect_smb2(request, session)
7
+ response = RubySMB::SMB2::Packet::TreeConnectResponse.new
8
+ response.smb2_header.credits = 1
9
+ if session.tree_connect_table.length >= MAX_TREE_CONNECTIONS
10
+ logger.warn('Connection has reached the maximum number of tree connections')
11
+ response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_INSUFFICIENT_RESOURCES
12
+ return response
13
+ end
14
+
15
+ share_name = request.path.encode('UTF-8').split('\\', 4).last
16
+ share_provider = @server.shares[share_name]
17
+
18
+ if share_provider.nil?
19
+ logger.warn("Received TREE_CONNECT request for non-existent share: #{share_name}")
20
+ response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_BAD_NETWORK_NAME
21
+ return response
22
+ end
23
+ logger.debug("Received TREE_CONNECT request for share: #{share_name}")
24
+
25
+ response.share_type = case share_provider.type
26
+ when Share::TYPE_DISK
27
+ RubySMB::SMB2::Packet::TreeConnectResponse::SMB2_SHARE_TYPE_DISK
28
+ when Share::TYPE_PIPE
29
+ RubySMB::SMB2::Packet::TreeConnectResponse::SMB2_SHARE_TYPE_PIPE
30
+ when Share::TYPE_PRINT
31
+ RubySMB::SMB2::Packet::TreeConnectResponse::SMB2_SHARE_TYPE_PRINT
32
+ end
33
+
34
+ tree_id = rand(0xffffffff)
35
+ tree_id = rand(0xffffffff) while session.tree_connect_table.include?(tree_id)
36
+
37
+ response.smb2_header.tree_id = tree_id
38
+ session.tree_connect_table[tree_id] = share_processor = share_provider.new_processor(self, session)
39
+ response.maximal_access = share_processor.maximal_access
40
+
41
+ response
42
+ end
43
+
44
+ def do_tree_disconnect_smb2(request, session)
45
+ share_processor = session.tree_connect_table.delete(request.smb2_header.tree_id)
46
+ if share_processor.nil?
47
+ response = SMB2::Packet::ErrorPacket.new
48
+ response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_NETWORK_NAME_DELETED
49
+ return response
50
+ end
51
+
52
+ logger.debug("Received TREE_DISCONNECT request for share: #{share_processor.provider.name}")
53
+ share_processor.disconnect!
54
+ response = RubySMB::SMB2::Packet::TreeDisconnectResponse.new
55
+ response
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -8,27 +8,30 @@ module RubySMB
8
8
  require 'ruby_smb/signing'
9
9
  require 'ruby_smb/server/server_client/negotiation'
10
10
  require 'ruby_smb/server/server_client/session_setup'
11
+ require 'ruby_smb/server/server_client/share_io'
12
+ require 'ruby_smb/server/server_client/tree_connect'
11
13
 
12
14
  include RubySMB::Signing
13
15
  include RubySMB::Server::ServerClient::Negotiation
14
16
  include RubySMB::Server::ServerClient::SessionSetup
17
+ include RubySMB::Server::ServerClient::ShareIO
18
+ include RubySMB::Server::ServerClient::TreeConnect
15
19
 
16
- attr_reader :dialect, :identity, :state, :session_key
20
+ attr_reader :dialect, :session_table
17
21
 
18
22
  # @param [Server] server the server that accepted this connection
19
23
  # @param [Dispatcher::Socket] dispatcher the connection's socket dispatcher
20
24
  def initialize(server, dispatcher)
21
25
  @server = server
22
26
  @dispatcher = dispatcher
23
- @state = :negotiate
24
27
  @dialect = nil
25
- @session_id = nil
26
- @session_key = nil
27
28
  @gss_authenticator = server.gss_provider.new_authenticator(self)
28
- @identity = nil
29
- @tree_connections = {}
30
29
  @preauth_integrity_hash_algorithm = nil
31
30
  @preauth_integrity_hash_value = nil
31
+ @in_packet_queue = []
32
+
33
+ # session id => session instance
34
+ @session_table = {}
32
35
  end
33
36
 
34
37
  #
@@ -51,18 +54,75 @@ module RubySMB
51
54
  end
52
55
 
53
56
  #
54
- # Handle an authenticated request. This is the main handler for all requests after the connection has been
55
- # authenticated.
57
+ # Handle a request after the dialect has been negotiated. This is the main
58
+ # handler for all requests after the connection has been established. If a
59
+ # request handler raises NotImplementedError, the server will respond to
60
+ # the client with NT Status STATUS_NOT_SUPPORTED.
56
61
  #
57
62
  # @param [String] raw_request the request that should be handled
58
- def handle_authenticated(raw_request)
63
+ def handle_smb(raw_request)
59
64
  response = nil
60
65
 
61
66
  case raw_request[0...4].unpack1('L>')
62
67
  when RubySMB::SMB1::SMB_PROTOCOL_ID
63
- raise NotImplementedError
68
+ begin
69
+ header = RubySMB::SMB1::SMBHeader.read(raw_request)
70
+ rescue IOError => e
71
+ logger.error("Caught a #{e.class} while reading the SMB1 header (#{e.message})")
72
+ disconnect!
73
+ return
74
+ end
75
+
76
+ begin
77
+ response = handle_smb1(raw_request, header)
78
+ rescue NotImplementedError
79
+ logger.error("Caught a NotImplementedError while handling a #{SMB1::Commands.name(header.command)} request")
80
+ response = RubySMB::SMB1::Packet::EmptyPacket.new
81
+ response.smb_header.nt_status = WindowsError::NTStatus::STATUS_NOT_SUPPORTED
82
+ end
83
+
84
+ unless response.nil?
85
+ # set these header fields if they were not initialized
86
+ if response.is_a?(SMB1::Packet::EmptyPacket)
87
+ response.smb_header.command = header.command if response.smb_header.command == 0
88
+ response.smb_header.flags.reply = 1
89
+ end
90
+
91
+ response.smb_header.pid_high = header.pid_high if response.smb_header.pid_high == 0
92
+ response.smb_header.tid = header.tid if response.smb_header.tid == 0
93
+ response.smb_header.pid_low = header.pid_low if response.smb_header.pid_low == 0
94
+ response.smb_header.uid = header.uid if response.smb_header.uid == 0
95
+ response.smb_header.mid = header.mid if response.smb_header.mid == 0
96
+ end
64
97
  when RubySMB::SMB2::SMB2_PROTOCOL_ID
65
- raise NotImplementedError
98
+ begin
99
+ header = RubySMB::SMB2::SMB2Header.read(raw_request)
100
+ rescue IOError => e
101
+ logger.error("Caught a #{e.class} while reading the SMB2 header (#{e.message})")
102
+ disconnect!
103
+ return
104
+ end
105
+
106
+ begin
107
+ response = handle_smb2(raw_request, header)
108
+ rescue NotImplementedError
109
+ logger.error("Caught a NotImplementedError while handling a #{SMB2::Commands.name(header.command)} request")
110
+ response = SMB2::Packet::ErrorPacket.new
111
+ 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
125
+ end
66
126
  end
67
127
 
68
128
  if response.nil?
@@ -95,25 +155,33 @@ module RubySMB
95
155
  break
96
156
  end
97
157
 
98
- case @state
99
- when :negotiate
158
+ if @dialect.nil?
100
159
  handle_negotiate(raw_request)
101
- when :session_setup
102
- handle_session_setup(raw_request)
103
- when :authenticated
104
- handle_authenticated(raw_request)
160
+ logger.info("Negotiated dialect: #{RubySMB::Dialect[@dialect].full_name}") unless @dialect.nil?
161
+ else
162
+ handle_smb(raw_request)
105
163
  end
106
164
 
107
165
  break if @dispatcher.tcp_socket.closed?
108
166
  end
167
+
168
+ disconnect!
109
169
  end
110
170
 
111
171
  #
112
172
  # Disconnect the remote client.
113
173
  #
114
174
  def disconnect!
115
- @state = nil
116
- @dispatcher.tcp_socket.close
175
+ @dialect = nil
176
+ @dispatcher.tcp_socket.close unless @dispatcher.tcp_socket.closed?
177
+ end
178
+
179
+ #
180
+ # The logger object associated with this instance.
181
+ #
182
+ # @return [Logger]
183
+ def logger
184
+ @server.logger
117
185
  end
118
186
 
119
187
  #
@@ -121,7 +189,24 @@ module RubySMB
121
189
  #
122
190
  # @return [String] the raw packet
123
191
  def recv_packet
124
- @dispatcher.recv_packet
192
+ return @in_packet_queue.shift if @in_packet_queue.length > 0
193
+
194
+ packet = @dispatcher.recv_packet
195
+ 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
207
+ end
208
+
209
+ packet
125
210
  end
126
211
 
127
212
  #
@@ -129,12 +214,20 @@ module RubySMB
129
214
  #
130
215
  # @param [GenericPacket] packet the packet to send
131
216
  def send_packet(packet)
132
- if @state == :authenticated && @identity != Gss::Provider::IDENTITY_ANONYMOUS && !@session_key.nil?
133
- case metadialect.family
217
+ case metadialect&.order
218
+ when Dialect::ORDER_SMB1
219
+ session_id = packet.smb_header.uid
220
+ when Dialect::ORDER_SMB2
221
+ session_id = packet.smb2_header.session_id
222
+ end
223
+ session = @session_table[session_id]
224
+
225
+ unless session.nil? || session.is_anonymous || session.key.nil?
226
+ case metadialect&.family
134
227
  when Dialect::FAMILY_SMB2
135
- packet = smb2_sign(packet)
228
+ packet = Signing::smb2_sign(packet, session.key)
136
229
  when Dialect::FAMILY_SMB3
137
- packet = smb3_sign(packet)
230
+ packet = Signing::smb3_sign(packet, session.key, @dialect, @preauth_integrity_hash_value)
138
231
  end
139
232
  end
140
233
 
@@ -157,6 +250,103 @@ module RubySMB
157
250
  @preauth_integrity_hash_value + data.to_binary_s
158
251
  )
159
252
  end
253
+
254
+ private
255
+
256
+ #
257
+ # Handle an SMB version 1 message.
258
+ #
259
+ # @param [String] raw_request The bytes of the entire SMB request.
260
+ # @param [RubySMB::SMB1::SMBHeader] header The request header.
261
+ # @return [RubySMB::GenericPacket]
262
+ def handle_smb1(raw_request, header)
263
+ # session = @session_table[header.uid]
264
+ session = nil
265
+
266
+ case header.command
267
+ when SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
268
+ dispatcher, request_class = :do_session_setup_smb1, SMB1::Packet::SessionSetupRequest
269
+ else
270
+ logger.warn("The SMB1 #{SMB1::Commands.name(header.command)} command is not supported")
271
+ raise NotImplementedError
272
+ end
273
+
274
+ begin
275
+ request = request_class.read(raw_request)
276
+ rescue IOError, RubySMB::Error::InvalidPacket => e
277
+ logger.error("Caught a #{e.class} while reading the SMB1 #{request_class} (#{e.message})")
278
+ response = RubySMB::SMB1::Packet::EmptyPacket.new
279
+ end
280
+
281
+ if request.is_a?(SMB1::Packet::EmptyPacket)
282
+ logger.error("Received an error packet for SMB1 command: #{SMB1::Commands.name(header.command)}")
283
+ response.smb_header.nt_status = WindowsError::NTStatus::STATUS_DATA_ERROR
284
+ return response
285
+ end
286
+
287
+ logger.debug("Dispatching request to #{dispatcher} (session: #{session.inspect})")
288
+ send(dispatcher, request, session)
289
+ end
290
+
291
+ #
292
+ # Handle an SMB version 2 or 3 message.
293
+ #
294
+ # @param [String] raw_request The bytes of the entire SMB request.
295
+ # @param [RubySMB::SMB2::SMB2Header] header The request header.
296
+ # @return [RubySMB::GenericPacket]
297
+ # @raise [NotImplementedError] Raised when the requested operation is not
298
+ # supported.
299
+ def handle_smb2(raw_request, header)
300
+ session = @session_table[header.session_id]
301
+
302
+ if session.nil? && !(header.command == SMB2::Commands::SESSION_SETUP && header.session_id == 0)
303
+ response = SMB2::Packet::ErrorPacket.new
304
+ response.smb2_header.nt_status = WindowsError::NTStatus::STATUS_USER_SESSION_DELETED
305
+ return response
306
+ end
307
+
308
+ case header.command
309
+ when SMB2::Commands::CLOSE
310
+ dispatcher, request_class = :do_close_smb2, SMB2::Packet::CloseRequest
311
+ when SMB2::Commands::CREATE
312
+ dispatcher, request_class = :do_create_smb2, SMB2::Packet::CreateRequest
313
+ when SMB2::Commands::IOCTL
314
+ dispatcher, request_class = :do_ioctl_smb2, SMB2::Packet::IoctlRequest
315
+ when SMB2::Commands::LOGOFF
316
+ dispatcher, request_class = :do_logoff_smb2, SMB2::Packet::LogoffRequest
317
+ when SMB2::Commands::QUERY_DIRECTORY
318
+ dispatcher, request_class = :do_query_directory_smb2, SMB2::Packet::QueryDirectoryRequest
319
+ when SMB2::Commands::QUERY_INFO
320
+ dispatcher, request_class = :do_query_info_smb2, SMB2::Packet::QueryInfoRequest
321
+ when SMB2::Commands::READ
322
+ dispatcher, request_class = :do_read_smb2, SMB2::Packet::ReadRequest
323
+ when SMB2::Commands::SESSION_SETUP
324
+ dispatcher, request_class = :do_session_setup_smb2, SMB2::Packet::SessionSetupRequest
325
+ when SMB2::Commands::TREE_CONNECT
326
+ dispatcher, request_class = :do_tree_connect_smb2, SMB2::Packet::TreeConnectRequest
327
+ when SMB2::Commands::TREE_DISCONNECT
328
+ dispatcher, request_class = :do_tree_disconnect_smb2, SMB2::Packet::TreeDisconnectRequest
329
+ else
330
+ logger.warn("The SMB2 #{SMB2::Commands.name(header.command)} command is not supported")
331
+ raise NotImplementedError
332
+ end
333
+
334
+ begin
335
+ request = request_class.read(raw_request)
336
+ rescue IOError, RubySMB::Error::InvalidPacket => e
337
+ logger.error("Caught a #{e.class} while reading the SMB2 #{request_class} (#{e.message})")
338
+ response = RubySMB::SMB2::Packet::ErrorPacket.new
339
+ end
340
+
341
+ if request.is_a?(SMB2::Packet::ErrorPacket)
342
+ logger.error("Received an error packet for SMB2 command: #{SMB2::Commands.name(header.command)}")
343
+ response.smb_header.nt_status = WindowsError::NTStatus::STATUS_DATA_ERROR
344
+ return response
345
+ end
346
+
347
+ logger.debug("Dispatching request to #{dispatcher} (session: #{session.inspect})")
348
+ send(dispatcher, request, session)
349
+ end
160
350
  end
161
351
  end
162
352
  end
@@ -0,0 +1,71 @@
1
+ module RubySMB
2
+ class Server
3
+ # The object representing a single anonymous, guest or authenticated session.
4
+ # @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/ea10b7ae-b053-4e4c-ab31-a48f7d0a79af
5
+ class Session
6
+ # @param [Integer] id This session's unique identifier.
7
+ # @param [String] key This session's key.
8
+ # @param [Symbol] state The state that this session is in.
9
+ # @param user_id The identity of the user associated with this session.
10
+ def initialize(id, key: nil, state: :in_progress, user_id: nil)
11
+ @id = id
12
+ @key = key
13
+ @user_id = user_id
14
+ @state = state
15
+ @signing_required = false
16
+ # tree id => provider processor instance
17
+ @tree_connect_table = {}
18
+ @creation_time = Time.now
19
+ end
20
+
21
+ def inspect
22
+ "#<Session id: #{@id.inspect}, user_id: #{@user_id.inspect}, state: #{@state.inspect}>"
23
+ end
24
+
25
+ # Whether or not this session is anonymous.
26
+ # @return [Boolean]
27
+ def is_anonymous
28
+ @user_id == Gss::Provider::IDENTITY_ANONYMOUS
29
+ end
30
+
31
+ def logoff!
32
+ @tree_connect_table.values.each { |share_processor| share_processor.disconnect! }
33
+ @tree_connect_table.clear
34
+ end
35
+
36
+ # This session's unique identifier.
37
+ # @!attribute [rw] id
38
+ # @return [Integer]
39
+ attr_accessor :id
40
+
41
+ # This session's key.
42
+ # @!attribute [rw] key
43
+ # @return [String]
44
+ attr_accessor :key
45
+
46
+ # The identity of the authenticated user.
47
+ # @!attribute [rw] user_id
48
+ attr_accessor :user_id
49
+
50
+ # The state that the session is in, (:expired, :in_progress, :valid, etc.).
51
+ # @!attribute [rw] state
52
+ # @return [Symbol]
53
+ attr_accessor :state
54
+
55
+ # Whether or not this session requires messages to be signed.
56
+ # @!attribute [rw] signing_required
57
+ # @return [Boolean]
58
+ attr_accessor :signing_required
59
+
60
+ # The table of tree/share connections in use by this session.
61
+ # @!attribute [rw] tree_connect_table
62
+ # @return [Hash]
63
+ attr_accessor :tree_connect_table
64
+
65
+ # The time at which this session was created.
66
+ # @!attribute [r] creation_time
67
+ # @return [Time]
68
+ attr_reader :creation_time
69
+ end
70
+ end
71
+ end