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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/examples/anonymous_auth.rb +29 -6
- data/examples/auth_capture.rb +28 -0
- data/examples/file_server.rb +76 -0
- data/examples/read_file.rb +51 -10
- data/examples/tree_connect.rb +49 -8
- data/lib/ruby_smb/client/authentication.rb +11 -3
- data/lib/ruby_smb/client.rb +16 -2
- data/lib/ruby_smb/create_actions.rb +21 -0
- data/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_encrypt_file_srv_request.rb +20 -0
- data/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_encrypt_file_srv_response.rb +20 -0
- data/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_open_file_raw_request.rb +21 -0
- data/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_open_file_raw_response.rb +21 -0
- data/lib/ruby_smb/dcerpc/encrypting_file_system.rb +44 -0
- data/lib/ruby_smb/dcerpc/print_system/rpc_add_printer_driver_ex_request.rb +22 -0
- data/lib/ruby_smb/dcerpc/print_system/rpc_add_printer_driver_ex_response.rb +20 -0
- data/lib/ruby_smb/dcerpc/print_system/rpc_enum_printer_drivers_request.rb +24 -0
- data/lib/ruby_smb/dcerpc/print_system/rpc_enum_printer_drivers_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/print_system/rpc_get_printer_driver_directory_request.rb +24 -0
- data/lib/ruby_smb/dcerpc/print_system/rpc_get_printer_driver_directory_response.rb +22 -0
- data/lib/ruby_smb/dcerpc/print_system.rb +69 -0
- data/lib/ruby_smb/dcerpc.rb +2 -2
- data/lib/ruby_smb/field/nt_status.rb +20 -1
- data/lib/ruby_smb/fscc/file_information/file_ea_information.rb +14 -0
- data/lib/ruby_smb/fscc/file_information/file_network_open_information.rb +22 -0
- data/lib/ruby_smb/fscc/file_information/file_stream_information.rb +16 -0
- data/lib/ruby_smb/fscc/file_information.rb +29 -0
- data/lib/ruby_smb/fscc/file_system_information/file_fs_attribute_information.rb +46 -0
- data/lib/ruby_smb/fscc/file_system_information/file_fs_volume_information.rb +19 -0
- data/lib/ruby_smb/fscc/file_system_information.rb +22 -0
- data/lib/ruby_smb/fscc.rb +1 -0
- data/lib/ruby_smb/generic_packet.rb +6 -0
- data/lib/ruby_smb/gss/provider/authenticator.rb +4 -0
- data/lib/ruby_smb/gss/provider/ntlm.rb +13 -3
- data/lib/ruby_smb/server/server_client/negotiation.rb +0 -2
- data/lib/ruby_smb/server/server_client/session_setup.rb +43 -32
- data/lib/ruby_smb/server/server_client/share_io.rb +28 -0
- data/lib/ruby_smb/server/server_client/tree_connect.rb +60 -0
- data/lib/ruby_smb/server/server_client.rb +214 -24
- data/lib/ruby_smb/server/session.rb +71 -0
- data/lib/ruby_smb/server/share/provider/disk.rb +437 -0
- data/lib/ruby_smb/server/share/provider/pipe.rb +27 -0
- data/lib/ruby_smb/server/share/provider/processor.rb +76 -0
- data/lib/ruby_smb/server/share/provider.rb +38 -0
- data/lib/ruby_smb/server/share.rb +11 -0
- data/lib/ruby_smb/server.rb +35 -3
- data/lib/ruby_smb/signing.rb +37 -11
- data/lib/ruby_smb/smb1/commands.rb +4 -0
- data/lib/ruby_smb/smb1/tree.rb +87 -79
- data/lib/ruby_smb/smb1.rb +0 -1
- data/lib/ruby_smb/smb2/bit_field/smb2_header_flags.rb +2 -1
- data/lib/ruby_smb/smb2/commands.rb +4 -0
- data/lib/ruby_smb/smb2/create_context/request.rb +64 -0
- data/lib/ruby_smb/smb2/create_context/response.rb +62 -0
- data/lib/ruby_smb/smb2/create_context.rb +74 -22
- data/lib/ruby_smb/smb2/packet/create_request.rb +44 -11
- data/lib/ruby_smb/smb2/packet/create_response.rb +17 -3
- data/lib/ruby_smb/smb2/packet/query_directory_request.rb +1 -1
- data/lib/ruby_smb/smb2/packet/query_directory_response.rb +2 -2
- data/lib/ruby_smb/smb2/packet/query_info_request.rb +43 -0
- data/lib/ruby_smb/smb2/packet/query_info_response.rb +23 -0
- data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +1 -1
- data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +1 -0
- data/lib/ruby_smb/smb2/packet.rb +2 -0
- data/lib/ruby_smb/smb2/tree.rb +80 -70
- data/lib/ruby_smb/smb2.rb +11 -0
- data/lib/ruby_smb/smb_error.rb +110 -0
- data/lib/ruby_smb/version.rb +1 -1
- data/lib/ruby_smb.rb +2 -0
- data/ruby_smb.gemspec +1 -1
- data/spec/lib/ruby_smb/client_spec.rb +10 -0
- data/spec/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_encrypt_file_srv_request_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_encrypt_file_srv_response_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_open_file_raw_request_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_open_file_raw_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/print_system/driver_container_spec.rb +41 -0
- data/spec/lib/ruby_smb/dcerpc/print_system/driver_info2_spec.rb +64 -0
- data/spec/lib/ruby_smb/dcerpc/print_system/rpc_add_printer_driver_ex_request_spec.rb +59 -0
- data/spec/lib/ruby_smb/dcerpc/print_system/rpc_add_printer_driver_ex_response_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/print_system/rpc_enum_printer_drivers_request_spec.rb +62 -0
- data/spec/lib/ruby_smb/dcerpc/print_system/rpc_enum_printer_drivers_response_spec.rb +54 -0
- data/spec/lib/ruby_smb/dcerpc/print_system/rpc_get_printer_driver_directory_request_spec.rb +62 -0
- data/spec/lib/ruby_smb/dcerpc/print_system/rpc_get_printer_driver_directory_response_spec.rb +46 -0
- data/spec/lib/ruby_smb/field/nt_status_spec.rb +6 -2
- data/spec/lib/ruby_smb/gss/provider/ntlm/authenticator_spec.rb +4 -0
- data/spec/lib/ruby_smb/server/server_client_spec.rb +36 -53
- data/spec/lib/ruby_smb/server/session_spec.rb +38 -0
- data/spec/lib/ruby_smb/server/share/provider/disk_spec.rb +61 -0
- data/spec/lib/ruby_smb/server/share/provider/pipe_spec.rb +31 -0
- data/spec/lib/ruby_smb/server/share/provider_spec.rb +13 -0
- data/spec/lib/ruby_smb/smb1/tree_spec.rb +3 -3
- data/spec/lib/ruby_smb/smb2/bit_field/header_flags_spec.rb +8 -2
- data/spec/lib/ruby_smb/smb2/{create_context_spec.rb → create_context/create_context_request_spec.rb} +1 -1
- data/spec/lib/ruby_smb/smb2/packet/create_request_spec.rb +5 -5
- data/spec/lib/ruby_smb/smb2/packet/create_response_spec.rb +9 -5
- data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +3 -2
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +3 -3
- data.tar.gz.sig +0 -0
- metadata +71 -7
- metadata.gz.sig +0 -0
- 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, :
|
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
|
55
|
-
#
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
99
|
-
when :negotiate
|
158
|
+
if @dialect.nil?
|
100
159
|
handle_negotiate(raw_request)
|
101
|
-
|
102
|
-
|
103
|
-
|
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
|
-
@
|
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
|
-
@
|
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
|
-
|
133
|
-
|
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
|