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
@@ -4,7 +4,10 @@ module RubySMB
|
|
4
4
|
# The FileStreamInformation
|
5
5
|
# [2.4.43 FileStreamInformation](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/f8762be6-3ab9-411e-a7d6-5cc68f70c78d)
|
6
6
|
class FileStreamInformation < BinData::Record
|
7
|
+
CLASS_LEVEL = Fscc::FileInformation::FILE_STREAM_INFORMATION
|
8
|
+
|
7
9
|
endian :little
|
10
|
+
|
8
11
|
uint32 :next_entry_offset, label: 'Next Entry Offset'
|
9
12
|
uint32 :stream_name_length, label: 'Stream Name Length', initial_value: -> { stream_name.do_num_bytes }
|
10
13
|
int64 :stream_size, label: 'Stream Size'
|
@@ -17,10 +17,26 @@ module RubySMB
|
|
17
17
|
# contents of a directory.
|
18
18
|
FILE_BOTH_DIRECTORY_INFORMATION = 0x03
|
19
19
|
|
20
|
+
# Information class used to query or set file information.
|
21
|
+
FILE_BASIC_INFORMATION = 0x04
|
22
|
+
|
23
|
+
# Information class is used to query file information.
|
24
|
+
FILE_STANDARD_INFORMATION = 0x05
|
25
|
+
|
26
|
+
# Information class used to query for the file system's 64-bit file ID.
|
27
|
+
FILE_INTERNAL_INFORMATION = 0x06
|
28
|
+
|
20
29
|
# Information class used to query for the size of the extended attributes
|
21
30
|
# (EA) for a file.
|
22
31
|
FILE_EA_INFORMATION = 0x07
|
23
32
|
|
33
|
+
# Information class used to query the access rights of a file that were
|
34
|
+
# granted when the file was opened.
|
35
|
+
FILE_ACCESS_INFORMATION = 0x08
|
36
|
+
|
37
|
+
# Information class is used locally to query the name of a file.
|
38
|
+
FILE_NAME_INFORMATION = 0x09
|
39
|
+
|
24
40
|
# Information class used to rename a file.
|
25
41
|
FILE_RENAME_INFORMATION = 0x0A
|
26
42
|
|
@@ -31,6 +47,17 @@ module RubySMB
|
|
31
47
|
# Information class used to mark a file for deletion.
|
32
48
|
FILE_DISPOSITION_INFORMATION = 0x0D
|
33
49
|
|
50
|
+
# Information class used to query or set the position of the file pointer
|
51
|
+
# within a file.
|
52
|
+
FILE_POSITION_INFORMATION = 0x0E
|
53
|
+
|
54
|
+
# Information class used to query or set the mode of the file.
|
55
|
+
FILE_MODE_INFORMATION = 0x10
|
56
|
+
|
57
|
+
# Information class used to query the buffer alignment required by the
|
58
|
+
# underlying device.
|
59
|
+
FILE_ALIGNMENT_INFORMATION = 0x11
|
60
|
+
|
34
61
|
# Information class used to enumerate the data streams of a file or a
|
35
62
|
# directory.
|
36
63
|
FILE_STREAM_INFORMATION = 0x16
|
@@ -57,6 +84,10 @@ module RubySMB
|
|
57
84
|
FILE_NORMALIZED_NAME_INFORMATION = 0x30
|
58
85
|
|
59
86
|
|
87
|
+
# Information class is used to query a collection of file information
|
88
|
+
# structures.
|
89
|
+
FILE_ALL_INFORMATION = 0x12
|
90
|
+
|
60
91
|
# These Information Classes can be used by SMB1 using the pass-through
|
61
92
|
# Information Levels when available on the server (CAP_INFOLEVEL_PASSTHRU
|
62
93
|
# capability flag in an SMB_COM_NEGOTIATE server response). The constant
|
@@ -65,12 +96,8 @@ module RubySMB
|
|
65
96
|
# [2.2.2.3.5 Pass-through Information Level Codes](https://msdn.microsoft.com/en-us/library/ff470158.aspx)
|
66
97
|
SMB_INFO_PASSTHROUGH = 0x03e8
|
67
98
|
|
68
|
-
|
69
|
-
|
70
|
-
class FileNameInformation < BinData::Record
|
71
|
-
endian :little
|
72
|
-
uint32 :file_name_length, label: 'File Name Length', initial_value: -> { file_name.do_num_bytes }
|
73
|
-
string16 :file_name, label: 'File Name', read_length: -> { file_name_length }
|
99
|
+
def self.name(value)
|
100
|
+
constants.select { |c| c.upcase == c }.find { |c| const_get(c) == value }
|
74
101
|
end
|
75
102
|
|
76
103
|
require 'ruby_smb/fscc/file_information/file_directory_information'
|
@@ -79,11 +106,21 @@ module RubySMB
|
|
79
106
|
require 'ruby_smb/fscc/file_information/file_id_full_directory_information'
|
80
107
|
require 'ruby_smb/fscc/file_information/file_both_directory_information'
|
81
108
|
require 'ruby_smb/fscc/file_information/file_id_both_directory_information'
|
109
|
+
require 'ruby_smb/fscc/file_information/file_name_information'
|
82
110
|
require 'ruby_smb/fscc/file_information/file_names_information'
|
111
|
+
require 'ruby_smb/fscc/file_information/file_normalized_name_information'
|
83
112
|
require 'ruby_smb/fscc/file_information/file_rename_information'
|
84
113
|
require 'ruby_smb/fscc/file_information/file_network_open_information'
|
85
114
|
require 'ruby_smb/fscc/file_information/file_ea_information'
|
86
115
|
require 'ruby_smb/fscc/file_information/file_stream_information'
|
116
|
+
require 'ruby_smb/fscc/file_information/file_basic_information'
|
117
|
+
require 'ruby_smb/fscc/file_information/file_standard_information'
|
118
|
+
require 'ruby_smb/fscc/file_information/file_internal_information'
|
119
|
+
require 'ruby_smb/fscc/file_information/file_access_information'
|
120
|
+
require 'ruby_smb/fscc/file_information/file_position_information'
|
121
|
+
require 'ruby_smb/fscc/file_information/file_mode_information'
|
122
|
+
require 'ruby_smb/fscc/file_information/file_alignment_information'
|
123
|
+
require 'ruby_smb/fscc/file_information/file_all_information'
|
87
124
|
end
|
88
125
|
end
|
89
126
|
end
|
@@ -7,6 +7,7 @@ module RubySMB
|
|
7
7
|
CLASS_LEVEL = FileSystemInformation::FILE_FS_ATTRIBUTE_INFORMATION
|
8
8
|
|
9
9
|
endian :little
|
10
|
+
|
10
11
|
struct :file_system_attributes, label: 'File System Attributes' do
|
11
12
|
bit1 :file_supports_reparse_points, label: 'FS Supports Reparse Points'
|
12
13
|
bit1 :file_supports_sparse_files, label: 'FS Supports Sparse Files'
|
@@ -7,6 +7,7 @@ module RubySMB
|
|
7
7
|
CLASS_LEVEL = FileSystemInformation::FILE_FS_VOLUME_INFORMATION
|
8
8
|
|
9
9
|
endian :little
|
10
|
+
|
10
11
|
file_time :volume_creation_time, label: 'Volume Creation Time'
|
11
12
|
uint32 :volume_serial_number, label: 'Volume Serial Number'
|
12
13
|
uint32 :volume_label_length, label: 'Volume Label Length', initial_value: -> { volume_label.do_num_bytes }
|
@@ -15,6 +15,10 @@ module RubySMB
|
|
15
15
|
FILE_FS_VOLUME_FLAGS_INFORMATION = 10
|
16
16
|
FILE_FS_SECTOR_SIZE_INFORMATION = 11
|
17
17
|
|
18
|
+
def self.name(value)
|
19
|
+
constants.select { |c| c.upcase == c }.find { |c| const_get(c) == value }
|
20
|
+
end
|
21
|
+
|
18
22
|
require 'ruby_smb/fscc/file_system_information/file_fs_attribute_information'
|
19
23
|
require 'ruby_smb/fscc/file_system_information/file_fs_volume_information'
|
20
24
|
end
|
@@ -78,6 +78,10 @@ module RubySMB
|
|
78
78
|
msg.flag |= NTLM::NEGOTIATE_FLAGS.fetch(flag)
|
79
79
|
end
|
80
80
|
|
81
|
+
if type1_msg.flag & NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY] == NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY]
|
82
|
+
msg.flag |= NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY]
|
83
|
+
end
|
84
|
+
|
81
85
|
@server_challenge = @provider.generate_server_challenge
|
82
86
|
msg.challenge = @server_challenge.unpack1('Q<') # 64-bit unsigned, little endian (uint64_t)
|
83
87
|
target_info = Net::NTLM::TargetInfo.new('')
|
@@ -109,6 +113,7 @@ module RubySMB
|
|
109
113
|
def process_ntlm_type3(type3_msg)
|
110
114
|
if type3_msg.user == '' && type3_msg.domain == ''
|
111
115
|
if @provider.allow_anonymous
|
116
|
+
@session_key = "\x00".b * 16 # see MS-NLMP section 3.4
|
112
117
|
return WindowsError::NTStatus::STATUS_SUCCESS
|
113
118
|
end
|
114
119
|
|
@@ -122,6 +127,12 @@ module RubySMB
|
|
122
127
|
domain: type3_msg.domain
|
123
128
|
)
|
124
129
|
if account.nil?
|
130
|
+
if @provider.allow_guests
|
131
|
+
logger.info("NTLM authentication request succeeded for #{dbg_string} (guest)")
|
132
|
+
@session_key = "\x00".b * 16 # see MS-NLMP section 3.4
|
133
|
+
return WindowsError::NTStatus::STATUS_SUCCESS
|
134
|
+
end
|
135
|
+
|
125
136
|
logger.info("NTLM authentication request failed for #{dbg_string} (no account)")
|
126
137
|
return WindowsError::NTStatus::STATUS_LOGON_FAILURE
|
127
138
|
end
|
@@ -229,25 +240,31 @@ module RubySMB
|
|
229
240
|
domain: type3_msg.domain
|
230
241
|
)
|
231
242
|
if account.nil?
|
232
|
-
if
|
243
|
+
if type3_msg.user == ''
|
244
|
+
is_guest = false
|
233
245
|
identity = IDENTITY_ANONYMOUS
|
246
|
+
else
|
247
|
+
is_guest = true
|
248
|
+
identity = Account.new(type3_msg.user.encode(''.encoding), '', type3_msg.domain.encode(''.encoding)).to_s
|
234
249
|
end
|
235
250
|
else
|
251
|
+
is_guest = false
|
236
252
|
identity = account.to_s
|
237
253
|
end
|
238
254
|
end
|
239
255
|
|
240
|
-
Result.new(buffer, nt_status, identity)
|
256
|
+
Result.new(buffer, nt_status, identity, is_guest)
|
241
257
|
end
|
242
258
|
end
|
243
259
|
|
244
260
|
# @param [Boolean] allow_anonymous whether or not to allow anonymous authentication attempts
|
245
261
|
# @param [String] default_domain the default domain to use for authentication, unless specified 'WORKGROUP' will
|
246
262
|
# be used
|
247
|
-
def initialize(allow_anonymous: false, default_domain: 'WORKGROUP')
|
263
|
+
def initialize(allow_anonymous: false, allow_guests: false, default_domain: 'WORKGROUP')
|
248
264
|
raise ArgumentError, 'Must specify a default domain' unless default_domain
|
249
265
|
|
250
266
|
@allow_anonymous = allow_anonymous
|
267
|
+
@allow_guests = allow_guests
|
251
268
|
@default_domain = default_domain
|
252
269
|
@accounts = []
|
253
270
|
@generate_server_challenge = -> { SecureRandom.bytes(8) }
|
@@ -7,7 +7,11 @@ module RubySMB
|
|
7
7
|
# A special constant implying that the authenticated user is anonymous.
|
8
8
|
IDENTITY_ANONYMOUS = :anonymous
|
9
9
|
# The result of a processed GSS request.
|
10
|
-
Result = Struct.new(:buffer, :nt_status, :identity)
|
10
|
+
Result = Struct.new(:buffer, :nt_status, :identity, :is_guest) do
|
11
|
+
def is_anonymous
|
12
|
+
identity == Gss::Provider::IDENTITY_ANONYMOUS
|
13
|
+
end
|
14
|
+
end
|
11
15
|
|
12
16
|
#
|
13
17
|
# The base class for a GSS authentication provider. This class defines a common interface and is not usable as a
|
@@ -26,6 +30,11 @@ module RubySMB
|
|
26
30
|
# Whether or not anonymous authentication attempts should be permitted.
|
27
31
|
#
|
28
32
|
attr_accessor :allow_anonymous
|
33
|
+
|
34
|
+
#
|
35
|
+
# Whether or not unknown users should be allowed to authenticate as guests.
|
36
|
+
#
|
37
|
+
attr_accessor :allow_guests
|
29
38
|
end
|
30
39
|
end
|
31
40
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module RubySMB
|
2
|
+
class Server
|
3
|
+
class ServerClient
|
4
|
+
# Contains the methods for handling encryption / decryption
|
5
|
+
module Encryption
|
6
|
+
def smb3_encrypt(data, session)
|
7
|
+
encryption_algorithm = SMB2::EncryptionCapabilities::ENCRYPTION_ALGORITHM_MAP[@cipher_id]
|
8
|
+
raise RubySMB::Error::EncryptionError.new('The encryption algorithm has not been set') if encryption_algorithm.nil?
|
9
|
+
|
10
|
+
key_bit_len = OpenSSL::Cipher.new(encryption_algorithm).key_len * 8
|
11
|
+
|
12
|
+
case @dialect
|
13
|
+
when '0x0300', '0x0302'
|
14
|
+
server_encryption_key = RubySMB::Crypto::KDF.counter_mode(
|
15
|
+
session.key,
|
16
|
+
"SMB2AESCCM\x00",
|
17
|
+
"ServerOut\x00",
|
18
|
+
length: key_bit_len
|
19
|
+
)
|
20
|
+
when '0x0311'
|
21
|
+
server_encryption_key = RubySMB::Crypto::KDF.counter_mode(
|
22
|
+
session.key,
|
23
|
+
"SMBS2CCipherKey\x00",
|
24
|
+
@preauth_integrity_hash_value,
|
25
|
+
length: key_bit_len
|
26
|
+
)
|
27
|
+
else
|
28
|
+
raise RubySMB::Error::EncryptionError.new('Dialect is incompatible with SMBv3 decryption')
|
29
|
+
end
|
30
|
+
|
31
|
+
th = RubySMB::SMB2::Packet::TransformHeader.new(flags: 1, session_id: session.id)
|
32
|
+
th.encrypt(data, server_encryption_key, algorithm: encryption_algorithm)
|
33
|
+
th
|
34
|
+
end
|
35
|
+
|
36
|
+
def smb3_decrypt(encrypted_request, session)
|
37
|
+
encryption_algorithm = SMB2::EncryptionCapabilities::ENCRYPTION_ALGORITHM_MAP[@cipher_id]
|
38
|
+
raise RubySMB::Error::EncryptionError.new('The encryption algorithm has not been set') if encryption_algorithm.nil?
|
39
|
+
|
40
|
+
key_bit_len = OpenSSL::Cipher.new(encryption_algorithm).key_len * 8
|
41
|
+
|
42
|
+
case @dialect
|
43
|
+
when '0x0300', '0x0302'
|
44
|
+
client_encryption_key = RubySMB::Crypto::KDF.counter_mode(
|
45
|
+
session.key,
|
46
|
+
"SMB2AESCCM\x00",
|
47
|
+
"ServerIn \x00",
|
48
|
+
length: key_bit_len
|
49
|
+
)
|
50
|
+
when '0x0311'
|
51
|
+
client_encryption_key = RubySMB::Crypto::KDF.counter_mode(
|
52
|
+
session.key,
|
53
|
+
"SMBC2SCipherKey\x00",
|
54
|
+
@preauth_integrity_hash_value,
|
55
|
+
length: key_bit_len
|
56
|
+
)
|
57
|
+
else
|
58
|
+
raise RubySMB::Error::EncryptionError.new('Dialect is incompatible with SMBv3 encryption')
|
59
|
+
end
|
60
|
+
|
61
|
+
encrypted_request.decrypt(client_encryption_key, algorithm: encryption_algorithm)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -119,14 +119,25 @@ module RubySMB
|
|
119
119
|
)
|
120
120
|
|
121
121
|
nc = request.find_negotiate_context(SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES)
|
122
|
-
|
123
|
-
|
122
|
+
ciphers = nc&.data&.ciphers
|
123
|
+
if ciphers
|
124
|
+
cipher = ciphers.find { |cipher| SMB2::EncryptionCapabilities::ENCRYPTION_ALGORITHM_MAP.include?(cipher) }
|
125
|
+
@cipher_id = cipher unless cipher.nil?
|
126
|
+
end
|
127
|
+
|
124
128
|
contexts << SMB2::NegotiateContext.new(
|
125
129
|
context_type: SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES,
|
126
130
|
data: {
|
127
|
-
ciphers: [
|
131
|
+
ciphers: [ @cipher_id ]
|
128
132
|
}
|
129
133
|
)
|
134
|
+
elsif dialect == '0x0300' || dialect == '0x0302'
|
135
|
+
if request.capabilities.encryption == 1
|
136
|
+
response.capabilities.encryption = 1
|
137
|
+
@cipher_id = SMB2::EncryptionCapabilities::AES_128_CCM
|
138
|
+
else
|
139
|
+
response.capabilities = 0
|
140
|
+
end
|
130
141
|
end
|
131
142
|
|
132
143
|
# the order in which the response is built is important to ensure it is valid
|
@@ -2,7 +2,7 @@ module RubySMB
|
|
2
2
|
class Server
|
3
3
|
class ServerClient
|
4
4
|
module SessionSetup
|
5
|
-
def
|
5
|
+
def do_session_setup_andx_smb1(request, session)
|
6
6
|
session_id = request.smb_header.uid
|
7
7
|
if session_id == 0
|
8
8
|
session_id = rand(1..0x10000)
|
@@ -41,6 +41,17 @@ module RubySMB
|
|
41
41
|
response
|
42
42
|
end
|
43
43
|
|
44
|
+
alias :do_session_setup_smb1 :do_session_setup_andx_smb1
|
45
|
+
|
46
|
+
def do_logoff_andx_smb1(request, session)
|
47
|
+
# see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/00fc0299-496c-4330-9089-67358994f272
|
48
|
+
@session_table.delete(request.smb_header.uid)
|
49
|
+
session.logoff!
|
50
|
+
|
51
|
+
response = SMB1::Packet::LogoffResponse.new
|
52
|
+
response
|
53
|
+
end
|
54
|
+
|
44
55
|
def do_session_setup_smb2(request, session)
|
45
56
|
session_id = request.smb2_header.session_id
|
46
57
|
if session_id == 0
|
@@ -67,11 +78,16 @@ module RubySMB
|
|
67
78
|
|
68
79
|
update_preauth_hash(request) if @dialect == '0x0311'
|
69
80
|
if gss_result.nt_status == WindowsError::NTStatus::STATUS_SUCCESS
|
70
|
-
response.smb2_header.credits = 32
|
71
81
|
session.state = :valid
|
72
82
|
session.user_id = gss_result.identity
|
83
|
+
session.is_guest = !!gss_result.is_guest
|
73
84
|
session.key = @gss_authenticator.session_key
|
74
|
-
session.signing_required = request.security_mode.signing_required == 1
|
85
|
+
session.signing_required = request.security_mode.signing_required == 1 || (!session.is_guest && !session.is_anonymous)
|
86
|
+
|
87
|
+
response.smb2_header.credits = 32
|
88
|
+
@cipher_id = 0 if session.is_anonymous || session.is_guest # disable encryption for anonymous users and guest users which have a null session key
|
89
|
+
response.session_flags.encrypt_data = 1 unless @cipher_id == 0
|
90
|
+
response.session_flags.guest = session.is_guest
|
75
91
|
elsif gss_result.nt_status == WindowsError::NTStatus::STATUS_MORE_PROCESSING_REQUIRED && @dialect == '0x0311'
|
76
92
|
update_preauth_hash(response)
|
77
93
|
end
|
@@ -80,7 +96,8 @@ module RubySMB
|
|
80
96
|
end
|
81
97
|
|
82
98
|
def do_logoff_smb2(request, session)
|
83
|
-
|
99
|
+
# see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/a6fbc502-75a5-42ef-a88c-c67b44817850
|
100
|
+
@session_table.delete(session.id)
|
84
101
|
session.logoff!
|
85
102
|
|
86
103
|
response = SMB2::Packet::LogoffResponse.new
|
@@ -2,6 +2,23 @@ module RubySMB
|
|
2
2
|
class Server
|
3
3
|
class ServerClient
|
4
4
|
module ShareIO
|
5
|
+
def proxy_share_io_smb1(request, session)
|
6
|
+
share_processor = session.tree_connect_table[request.smb_header.tid]
|
7
|
+
if share_processor.nil?
|
8
|
+
response = SMB1::Packet::EmptyPacket.new
|
9
|
+
response.smb_header.nt_status = WindowsError::NTStatus::STATUS_NETWORK_NAME_DELETED
|
10
|
+
return response
|
11
|
+
end
|
12
|
+
|
13
|
+
logger.debug("Received #{SMB1::Commands.name(request.smb_header.command)} request for share: #{share_processor.provider.name}")
|
14
|
+
share_processor.send(__callee__, request)
|
15
|
+
end
|
16
|
+
|
17
|
+
alias :do_close_smb1 :proxy_share_io_smb1
|
18
|
+
alias :do_nt_create_andx_smb1 :proxy_share_io_smb1
|
19
|
+
alias :do_read_andx_smb1 :proxy_share_io_smb1
|
20
|
+
alias :do_transactions2_smb1 :proxy_share_io_smb1
|
21
|
+
|
5
22
|
def proxy_share_io_smb2(request, session)
|
6
23
|
# see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/9a639360-87be-4d49-a1dd-4c6be0c020bd
|
7
24
|
share_processor = session.tree_connect_table[request.smb2_header.tree_id]
|
@@ -3,6 +3,43 @@ module RubySMB
|
|
3
3
|
class ServerClient
|
4
4
|
MAX_TREE_CONNECTIONS = 1000
|
5
5
|
module TreeConnect
|
6
|
+
def do_tree_connect_smb1(request, session)
|
7
|
+
# see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/b062f3e3-1b65-4a9a-854a-0ee432499d8f
|
8
|
+
response = RubySMB::SMB1::Packet::TreeConnectResponse.new
|
9
|
+
|
10
|
+
share_name = request.data_block.path.encode('UTF-8').split('\\', 4).last
|
11
|
+
share_provider = @server.shares.transform_keys(&:downcase)[share_name.downcase]
|
12
|
+
if share_provider.nil?
|
13
|
+
logger.warn("Received TREE_CONNECT request for non-existent share: #{share_name}")
|
14
|
+
response.smb_header.nt_status = WindowsError::NTStatus::STATUS_OBJECT_PATH_NOT_FOUND
|
15
|
+
return response
|
16
|
+
end
|
17
|
+
logger.debug("Received TREE_CONNECT request for share: #{share_name}")
|
18
|
+
|
19
|
+
tree_id = rand(1..0xfffe)
|
20
|
+
tree_id = rand(1..0xfffe) while session.tree_connect_table.include?(tree_id)
|
21
|
+
|
22
|
+
response.smb_header.tid = tree_id
|
23
|
+
session.tree_connect_table[tree_id] = share_processor = share_provider.new_processor(self, session)
|
24
|
+
response.parameter_block.access_rights = share_processor.maximal_access
|
25
|
+
|
26
|
+
response
|
27
|
+
end
|
28
|
+
|
29
|
+
def do_tree_disconnect_smb1(request, session)
|
30
|
+
share_processor = session.tree_connect_table.delete(request.smb_header.tid)
|
31
|
+
if share_processor.nil?
|
32
|
+
response = RubySMB::SMB1::Packet::EmptyPacket.new
|
33
|
+
response.smb_header.nt_status = WindowsError::NTStatus::STATUS_NETWORK_NAME_DELETED
|
34
|
+
return response
|
35
|
+
end
|
36
|
+
|
37
|
+
logger.debug("Received TREE_DISCONNECT request for share: #{share_processor.provider.name}")
|
38
|
+
share_processor.disconnect!
|
39
|
+
response = RubySMB::SMB1::Packet::TreeDisconnectResponse.new
|
40
|
+
response
|
41
|
+
end
|
42
|
+
|
6
43
|
def do_tree_connect_smb2(request, session)
|
7
44
|
response = RubySMB::SMB2::Packet::TreeConnectResponse.new
|
8
45
|
response.smb2_header.credits = 1
|
@@ -13,7 +50,7 @@ module RubySMB
|
|
13
50
|
end
|
14
51
|
|
15
52
|
share_name = request.path.encode('UTF-8').split('\\', 4).last
|
16
|
-
share_provider = @server.shares[share_name]
|
53
|
+
share_provider = @server.shares.transform_keys(&:downcase)[share_name.downcase]
|
17
54
|
|
18
55
|
if share_provider.nil?
|
19
56
|
logger.warn("Received TREE_CONNECT request for non-existent share: #{share_name}")
|
@@ -31,8 +68,8 @@ module RubySMB
|
|
31
68
|
RubySMB::SMB2::Packet::TreeConnectResponse::SMB2_SHARE_TYPE_PRINT
|
32
69
|
end
|
33
70
|
|
34
|
-
tree_id = rand(
|
35
|
-
tree_id = rand(
|
71
|
+
tree_id = rand(1..0xfffffffe)
|
72
|
+
tree_id = rand(1..0xfffffffe) while session.tree_connect_table.include?(tree_id)
|
36
73
|
|
37
74
|
response.smb2_header.tree_id = tree_id
|
38
75
|
session.tree_connect_table[tree_id] = share_processor = share_provider.new_processor(self, session)
|