ruby_smb 3.0.6 → 3.1.2
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/file_server.rb +8 -1
- data/examples/virtual_file_server.rb +143 -0
- data/lib/ruby_smb/client/encryption.rb +16 -4
- data/lib/ruby_smb/client/negotiation.rb +10 -8
- data/lib/ruby_smb/fscc/file_information/file_access_information.rb +15 -0
- data/lib/ruby_smb/fscc/file_information/file_alignment_information.rb +45 -0
- data/lib/ruby_smb/fscc/file_information/file_all_information.rb +23 -0
- data/lib/ruby_smb/fscc/file_information/file_basic_information.rb +20 -0
- data/lib/ruby_smb/fscc/file_information/file_both_directory_information.rb +3 -3
- data/lib/ruby_smb/fscc/file_information/file_directory_information.rb +3 -3
- data/lib/ruby_smb/fscc/file_information/file_ea_information.rb +1 -0
- data/lib/ruby_smb/fscc/file_information/file_full_directory_information.rb +3 -3
- data/lib/ruby_smb/fscc/file_information/file_id_both_directory_information.rb +3 -3
- data/lib/ruby_smb/fscc/file_information/file_id_full_directory_information.rb +3 -3
- data/lib/ruby_smb/fscc/file_information/file_internal_information.rb +15 -0
- data/lib/ruby_smb/fscc/file_information/file_mode_information.rb +29 -0
- data/lib/ruby_smb/fscc/file_information/file_name_information.rb +16 -0
- data/lib/ruby_smb/fscc/file_information/file_names_information.rb +1 -1
- data/lib/ruby_smb/fscc/file_information/file_normalized_name_information.rb +16 -0
- data/lib/ruby_smb/fscc/file_information/file_position_information.rb +15 -0
- data/lib/ruby_smb/fscc/file_information/file_rename_information.rb +1 -1
- data/lib/ruby_smb/fscc/file_information/file_standard_information.rb +20 -0
- data/lib/ruby_smb/fscc/file_information/file_stream_information.rb +3 -0
- data/lib/ruby_smb/fscc/file_information.rb +43 -6
- data/lib/ruby_smb/fscc/file_system_information/file_fs_attribute_information.rb +1 -0
- data/lib/ruby_smb/fscc/file_system_information/file_fs_volume_information.rb +1 -0
- data/lib/ruby_smb/fscc/file_system_information.rb +4 -0
- data/lib/ruby_smb/gss/provider/ntlm.rb +20 -3
- data/lib/ruby_smb/gss/provider.rb +10 -1
- data/lib/ruby_smb/server/server_client/encryption.rb +66 -0
- data/lib/ruby_smb/server/server_client/negotiation.rb +14 -3
- data/lib/ruby_smb/server/server_client/session_setup.rb +21 -4
- data/lib/ruby_smb/server/server_client/share_io.rb +17 -0
- data/lib/ruby_smb/server/server_client/tree_connect.rb +40 -3
- data/lib/ruby_smb/server/server_client.rb +156 -38
- data/lib/ruby_smb/server/session.rb +5 -1
- data/lib/ruby_smb/server/share/provider/disk/file_system.rb +28 -0
- data/lib/ruby_smb/server/share/provider/disk/processor/close.rb +46 -0
- data/lib/ruby_smb/server/share/provider/disk/processor/create.rb +143 -0
- data/lib/ruby_smb/server/share/provider/disk/processor/query.rb +359 -0
- data/lib/ruby_smb/server/share/provider/disk/processor/read.rb +70 -0
- data/lib/ruby_smb/server/share/provider/disk/processor.rb +223 -0
- data/lib/ruby_smb/server/share/provider/disk.rb +12 -418
- data/lib/ruby_smb/server/share/provider/pipe.rb +2 -2
- data/lib/ruby_smb/server/share/provider/processor.rb +16 -0
- data/lib/ruby_smb/server/share/provider/virtual_disk/virtual_file.rb +85 -0
- data/lib/ruby_smb/server/share/provider/virtual_disk/virtual_pathname.rb +196 -0
- data/lib/ruby_smb/server/share/provider/virtual_disk/virtual_stat.rb +175 -0
- data/lib/ruby_smb/server/share/provider/virtual_disk.rb +116 -0
- data/lib/ruby_smb/server/share/provider.rb +1 -0
- data/lib/ruby_smb/server.rb +13 -3
- data/lib/ruby_smb/signing.rb +18 -4
- data/lib/ruby_smb/smb1/commands.rb +1 -0
- data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +11 -1
- data/lib/ruby_smb/smb1/packet/nt_trans/create_request.rb +1 -1
- data/lib/ruby_smb/smb1/packet/read_andx_response.rb +5 -4
- data/lib/ruby_smb/smb1/packet/session_setup_request.rb +12 -4
- data/lib/ruby_smb/smb1/packet/trans2/data_block.rb +9 -1
- data/lib/ruby_smb/smb1/packet/trans2/find_first2_request.rb +52 -51
- data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +37 -37
- data/lib/ruby_smb/smb1/packet/trans2/find_information_level/find_file_both_directory_info.rb +48 -0
- data/lib/ruby_smb/smb1/packet/trans2/find_information_level.rb +28 -15
- data/lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb +51 -51
- data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +36 -36
- data/lib/ruby_smb/smb1/packet/trans2/open2_request.rb +40 -39
- data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +40 -40
- data/lib/ruby_smb/smb1/packet/trans2/query_file_information_request.rb +60 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_file_information_response.rb +59 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_level/query_fs_attribute_info.rb +31 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_level.rb +40 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_request.rb +46 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_response.rb +59 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_information_level/query_file_basic_info.rb +23 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_information_level/query_file_standard_info.rb +22 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_information_level.rb +62 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_path_information_request.rb +65 -0
- data/lib/ruby_smb/smb1/packet/trans2/query_path_information_response.rb +59 -0
- data/lib/ruby_smb/smb1/packet/trans2/request.rb +24 -8
- data/lib/ruby_smb/smb1/packet/trans2/request_secondary.rb +4 -4
- data/lib/ruby_smb/smb1/packet/trans2/response.rb +29 -20
- data/lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb +42 -42
- data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +23 -23
- data/lib/ruby_smb/smb1/packet/trans2/subcommands.rb +23 -5
- data/lib/ruby_smb/smb1/packet/trans2.rb +4 -0
- data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +4 -1
- data/lib/ruby_smb/smb2/negotiate_context.rb +10 -1
- data/lib/ruby_smb/smb2/packet/transform_header.rb +7 -7
- data/lib/ruby_smb/smb2/tree.rb +1 -0
- data/lib/ruby_smb/smb2.rb +1 -0
- data/lib/ruby_smb/version.rb +1 -1
- data/spec/lib/ruby_smb/client_spec.rb +31 -8
- data/spec/lib/ruby_smb/fscc/file_information/file_access_information_spec.rb +21 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_alignment_information_spec.rb +21 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_all_information_spec.rb +61 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_basic_information_spec.rb +41 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_both_directory_information_spec.rb +59 -10
- data/spec/lib/ruby_smb/fscc/file_information/file_directory_information_spec.rb +30 -12
- data/spec/lib/ruby_smb/fscc/file_information/file_ea_information_spec.rb +21 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_full_directory_information_spec.rb +30 -12
- data/spec/lib/ruby_smb/fscc/file_information/file_id_both_directory_information_spec.rb +63 -10
- data/spec/lib/ruby_smb/fscc/file_information/file_id_full_directory_information_spec.rb +30 -12
- data/spec/lib/ruby_smb/fscc/file_information/file_internal_information_spec.rb +21 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_mode_information_spec.rb +21 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_name_information_spec.rb +44 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_names_information_spec.rb +30 -12
- data/spec/lib/ruby_smb/fscc/file_information/file_network_open_information_spec.rb +51 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_normalized_name_information_spec.rb +44 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_position_information_spec.rb +21 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_rename_information_spec.rb +1 -1
- data/spec/lib/ruby_smb/fscc/file_information/file_standard_information_spec.rb +41 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_stream_information_spec.rb +51 -0
- data/spec/lib/ruby_smb/fscc/file_information_spec.rb +14 -0
- data/spec/lib/ruby_smb/fscc/file_system_information/file_fs_attribute_information_spec.rb +46 -0
- data/spec/lib/ruby_smb/fscc/file_system_information/file_fs_volume_information_spec.rb +51 -0
- data/spec/lib/ruby_smb/fscc/file_system_information_spec.rb +14 -0
- data/spec/lib/ruby_smb/server/server_client_spec.rb +15 -0
- data/spec/lib/ruby_smb/server/share/provider/virtual_disk/virtual_pathname_spec.rb +581 -0
- data/spec/lib/ruby_smb/server/share/provider/virtual_disk/virtual_stat_spec.rb +207 -0
- data/spec/lib/ruby_smb/server/share/provider/virtual_disk_spec.rb +122 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +36 -2
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +35 -1
- data/spec/lib/ruby_smb/smb1/packet/trans2/query_file_information_request_spec.rb +74 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/query_file_information_response_spec.rb +96 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/query_fs_information_request_spec.rb +62 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/query_fs_information_response_spec.rb +88 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/query_path_information_request_spec.rb +79 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/query_path_information_response_spec.rb +96 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/request_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/trans2/response_spec.rb +3 -3
- data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_request_spec.rb +3 -2
- data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_response_spec.rb +7 -2
- data/spec/lib/ruby_smb/smb1/tree_spec.rb +3 -3
- data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +2 -2
- data.tar.gz.sig +0 -0
- metadata +88 -2
- 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
|
-
|
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::
|
121
|
+
header = RubySMB::SMB2::Packet::TransformHeader.read(raw_request)
|
100
122
|
rescue IOError => e
|
101
|
-
logger.error("Caught a #{e.class} while reading the
|
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 =
|
129
|
+
response = handle_smb3_transform(raw_request, header)
|
108
130
|
rescue NotImplementedError
|
109
|
-
logger.error("Caught a NotImplementedError while handling a
|
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
|
-
|
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
|
-
|
197
|
-
|
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&.
|
218
|
-
when Dialect::
|
218
|
+
case metadialect&.family
|
219
|
+
when Dialect::FAMILY_SMB1
|
219
220
|
session_id = packet.smb_header.uid
|
220
|
-
when Dialect::
|
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
|
-
|
264
|
-
|
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 = :
|
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]
|
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
|