ruby_smb 1.0.3 → 2.0.1
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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.travis.yml +3 -2
- data/Gemfile +6 -2
- data/README.md +35 -47
- data/examples/enum_registry_key.rb +28 -0
- data/examples/enum_registry_values.rb +30 -0
- data/examples/negotiate.rb +51 -8
- data/examples/pipes.rb +2 -1
- data/examples/read_file_encryption.rb +56 -0
- data/examples/read_registry_key_value.rb +32 -0
- data/lib/ruby_smb.rb +4 -1
- data/lib/ruby_smb/client.rb +233 -22
- data/lib/ruby_smb/client/authentication.rb +70 -33
- data/lib/ruby_smb/client/echo.rb +20 -2
- data/lib/ruby_smb/client/encryption.rb +62 -0
- data/lib/ruby_smb/client/negotiation.rb +172 -24
- data/lib/ruby_smb/client/signing.rb +19 -0
- data/lib/ruby_smb/client/tree_connect.rb +24 -18
- data/lib/ruby_smb/client/utils.rb +8 -7
- data/lib/ruby_smb/client/winreg.rb +46 -0
- data/lib/ruby_smb/crypto.rb +30 -0
- data/lib/ruby_smb/dcerpc.rb +38 -0
- data/lib/ruby_smb/dcerpc/bind.rb +2 -2
- data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
- data/lib/ruby_smb/dcerpc/error.rb +3 -0
- data/lib/ruby_smb/dcerpc/ndr.rb +95 -16
- data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
- data/lib/ruby_smb/dcerpc/request.rb +28 -9
- data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +35 -0
- data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
- data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
- data/lib/ruby_smb/dcerpc/winreg.rb +340 -0
- data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
- data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
- data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
- data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
- data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
- data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
- data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
- data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
- data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +39 -0
- data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
- data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
- data/lib/ruby_smb/dispatcher/socket.rb +4 -3
- data/lib/ruby_smb/error.rb +68 -2
- data/lib/ruby_smb/generic_packet.rb +33 -4
- data/lib/ruby_smb/smb1/commands.rb +1 -1
- data/lib/ruby_smb/smb1/file.rb +66 -15
- data/lib/ruby_smb/smb1/packet/close_request.rb +2 -5
- data/lib/ruby_smb/smb1/packet/close_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/echo_request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/echo_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/empty_packet.rb +10 -1
- data/lib/ruby_smb/smb1/packet/logoff_request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/logoff_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/negotiate_request.rb +2 -5
- data/lib/ruby_smb/smb1/packet/negotiate_response.rb +3 -7
- data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +4 -4
- data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/nt_create_andx_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/nt_trans/create_request.rb +2 -1
- data/lib/ruby_smb/smb1/packet/nt_trans/create_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/nt_trans/request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/nt_trans/response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/read_andx_request.rb +2 -5
- data/lib/ruby_smb/smb1/packet/read_andx_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +2 -1
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +3 -2
- data/lib/ruby_smb/smb1/packet/session_setup_request.rb +2 -5
- data/lib/ruby_smb/smb1/packet/session_setup_response.rb +3 -2
- data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_request.rb +0 -1
- data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_response.rb +3 -2
- data/lib/ruby_smb/smb1/packet/trans/request.rb +2 -5
- data/lib/ruby_smb/smb1/packet/trans/response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_request.rb +1 -1
- data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_response.rb +1 -1
- data/lib/ruby_smb/smb1/packet/trans2/find_first2_request.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +8 -2
- data/lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +8 -2
- data/lib/ruby_smb/smb1/packet/trans2/open2_request.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans2/request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/trans2/request_secondary.rb +2 -4
- data/lib/ruby_smb/smb1/packet/trans2/response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/tree_connect_response.rb +13 -3
- data/lib/ruby_smb/smb1/packet/tree_disconnect_request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/tree_disconnect_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/write_andx_request.rb +3 -6
- data/lib/ruby_smb/smb1/packet/write_andx_response.rb +2 -1
- data/lib/ruby_smb/smb1/pipe.rb +87 -6
- data/lib/ruby_smb/smb1/tree.rb +50 -3
- data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
- data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
- data/lib/ruby_smb/smb2/file.rb +103 -25
- data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
- data/lib/ruby_smb/smb2/packet.rb +2 -0
- data/lib/ruby_smb/smb2/packet/close_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/close_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
- data/lib/ruby_smb/smb2/packet/create_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/create_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/echo_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/echo_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/error_packet.rb +15 -3
- data/lib/ruby_smb/smb2/packet/ioctl_request.rb +2 -5
- data/lib/ruby_smb/smb2/packet/ioctl_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/logoff_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/logoff_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -17
- data/lib/ruby_smb/smb2/packet/negotiate_response.rb +52 -5
- data/lib/ruby_smb/smb2/packet/query_directory_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/query_directory_response.rb +8 -2
- data/lib/ruby_smb/smb2/packet/read_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/read_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/session_setup_request.rb +2 -5
- data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/set_info_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/set_info_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
- data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +93 -10
- data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +10 -22
- data/lib/ruby_smb/smb2/packet/tree_disconnect_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/write_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/write_response.rb +2 -1
- data/lib/ruby_smb/smb2/pipe.rb +86 -12
- data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
- data/lib/ruby_smb/smb2/tree.rb +65 -21
- data/lib/ruby_smb/version.rb +1 -1
- data/ruby_smb.gemspec +5 -3
- data/spec/lib/ruby_smb/client_spec.rb +1612 -108
- data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
- data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +410 -0
- data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
- data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +98 -0
- data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
- data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +108 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +90 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +39 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +150 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
- data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +710 -0
- data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
- data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +2 -2
- data/spec/lib/ruby_smb/error_spec.rb +59 -0
- data/spec/lib/ruby_smb/generic_packet_spec.rb +52 -4
- data/spec/lib/ruby_smb/smb1/file_spec.rb +191 -2
- data/spec/lib/ruby_smb/smb1/packet/empty_packet_spec.rb +68 -0
- data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +11 -2
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +11 -2
- data/spec/lib/ruby_smb/smb1/packet/tree_connect_response_spec.rb +40 -0
- data/spec/lib/ruby_smb/smb1/pipe_spec.rb +272 -149
- data/spec/lib/ruby_smb/smb1/tree_spec.rb +44 -7
- data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
- data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
- data/spec/lib/ruby_smb/smb2/file_spec.rb +323 -6
- data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
- data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
- data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +78 -0
- data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
- data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
- data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +8 -0
- data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
- data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
- data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -22
- data/spec/lib/ruby_smb/smb2/pipe_spec.rb +286 -149
- data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +261 -2
- metadata +191 -83
- metadata.gz.sig +0 -0
- data/lib/ruby_smb/smb1/dcerpc.rb +0 -67
- data/lib/ruby_smb/smb2/dcerpc.rb +0 -70
- data/spec/lib/ruby_smb/smb1/packet/error_packet_spec.rb +0 -37
data/lib/ruby_smb/smb1/tree.rb
CHANGED
@@ -39,11 +39,20 @@ module RubySMB
|
|
39
39
|
# Disconnects this Tree from the current session
|
40
40
|
#
|
41
41
|
# @return [WindowsError::ErrorCode] the NTStatus sent back by the server.
|
42
|
+
# @raise [RubySMB::Error::InvalidPacket] if the response is not a TreeDisconnectResponse packet
|
42
43
|
def disconnect!
|
43
44
|
request = RubySMB::SMB1::Packet::TreeDisconnectRequest.new
|
44
45
|
request = set_header_fields(request)
|
45
46
|
raw_response = client.send_recv(request)
|
46
47
|
response = RubySMB::SMB1::Packet::TreeDisconnectResponse.read(raw_response)
|
48
|
+
unless response.valid?
|
49
|
+
raise RubySMB::Error::InvalidPacket.new(
|
50
|
+
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
51
|
+
expected_cmd: RubySMB::SMB1::Packet::TreeDisconnectResponse::COMMAND,
|
52
|
+
received_proto: response.smb_header.protocol,
|
53
|
+
received_cmd: response.smb_header.command
|
54
|
+
)
|
55
|
+
end
|
47
56
|
response.status_code
|
48
57
|
end
|
49
58
|
|
@@ -113,11 +122,25 @@ module RubySMB
|
|
113
122
|
|
114
123
|
raw_response = @client.send_recv(nt_create_andx_request)
|
115
124
|
response = RubySMB::SMB1::Packet::NtCreateAndxResponse.read(raw_response)
|
116
|
-
unless response.
|
117
|
-
|
125
|
+
unless response.valid?
|
126
|
+
if response.is_a?(RubySMB::SMB1::Packet::EmptyPacket) &&
|
127
|
+
response.smb_header.protocol == RubySMB::SMB1::SMB_PROTOCOL_ID &&
|
128
|
+
response.smb_header.command == response.original_command
|
129
|
+
raise RubySMB::Error::InvalidPacket.new(
|
130
|
+
'The response seems to be an SMB1 NtCreateAndxResponse but an '\
|
131
|
+
'error occurs while parsing it. It is probably missing the '\
|
132
|
+
'required extended information.'
|
133
|
+
)
|
134
|
+
end
|
135
|
+
raise RubySMB::Error::InvalidPacket.new(
|
136
|
+
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
137
|
+
expected_cmd: RubySMB::SMB1::Packet::NtCreateAndxResponse::COMMAND,
|
138
|
+
received_proto: response.smb_header.protocol,
|
139
|
+
received_cmd: response.smb_header.command
|
140
|
+
)
|
118
141
|
end
|
119
142
|
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
120
|
-
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
143
|
+
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
121
144
|
end
|
122
145
|
|
123
146
|
case response.parameter_block.resource_type
|
@@ -140,6 +163,8 @@ module RubySMB
|
|
140
163
|
# @param pattern [String] search pattern
|
141
164
|
# @param type [Class] file information class
|
142
165
|
# @return [Array] array of directory structures
|
166
|
+
# @raise [RubySMB::Error::InvalidPacket] if the response is not a Trans2 packet
|
167
|
+
# @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
|
143
168
|
def list(directory: '\\', pattern: '*', unicode: true,
|
144
169
|
type: RubySMB::SMB1::Packet::Trans2::FindInformationLevel::FindFileFullDirectoryInfo)
|
145
170
|
find_first_request = RubySMB::SMB1::Packet::Trans2::FindFirst2Request.new
|
@@ -166,6 +191,17 @@ module RubySMB
|
|
166
191
|
|
167
192
|
raw_response = client.send_recv(find_first_request)
|
168
193
|
response = RubySMB::SMB1::Packet::Trans2::FindFirst2Response.read(raw_response)
|
194
|
+
unless response.valid?
|
195
|
+
raise RubySMB::Error::InvalidPacket.new(
|
196
|
+
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
197
|
+
expected_cmd: RubySMB::SMB1::Packet::Trans2::FindFirst2Response::COMMAND,
|
198
|
+
received_proto: response.smb_header.protocol,
|
199
|
+
received_cmd: response.smb_header.command
|
200
|
+
)
|
201
|
+
end
|
202
|
+
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
203
|
+
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
204
|
+
end
|
169
205
|
|
170
206
|
results = response.results(type, unicode: unicode)
|
171
207
|
|
@@ -190,6 +226,17 @@ module RubySMB
|
|
190
226
|
|
191
227
|
raw_response = client.send_recv(find_next_request)
|
192
228
|
response = RubySMB::SMB1::Packet::Trans2::FindNext2Response.read(raw_response)
|
229
|
+
unless response.valid?
|
230
|
+
raise RubySMB::Error::InvalidPacket.new(
|
231
|
+
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
232
|
+
expected_cmd: RubySMB::SMB1::Packet::Trans2::FindNext2Response::COMMAND,
|
233
|
+
received_proto: response.smb_header.protocol,
|
234
|
+
received_cmd: response.smb_header.command
|
235
|
+
)
|
236
|
+
end
|
237
|
+
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
238
|
+
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
239
|
+
end
|
193
240
|
|
194
241
|
results += response.results(type, unicode: unicode)
|
195
242
|
|
@@ -4,7 +4,8 @@ module RubySMB
|
|
4
4
|
# The SessionsFlags bit-field for a {RubySMB::SMB2::Packet::SessionSetupResponse}
|
5
5
|
class SessionFlags < BinData::Record
|
6
6
|
endian :little
|
7
|
-
|
7
|
+
bit5 :reserved3, label: 'Reserved', initial_value: 0
|
8
|
+
bit1 :encrypt_data, label: 'Encrypt Data', initial_value: 0
|
8
9
|
bit1 :null, label: 'ASYNC Command', initial_value: 0
|
9
10
|
bit1 :guest, label: 'Is Guest?', initial_value: 0
|
10
11
|
resume_byte_alignment
|
@@ -8,7 +8,7 @@ module RubySMB
|
|
8
8
|
bit2 :reserved1, label: 'Reserved Space'
|
9
9
|
bit1 :vdo_caching, label: 'VDO Caching'
|
10
10
|
bit1 :auto_caching, label: 'Auto Caching'
|
11
|
-
bit2 :reserved2
|
11
|
+
bit2 :reserved2, label: 'Reserved Space'
|
12
12
|
bit1 :dfs_root, label: 'DFS Root'
|
13
13
|
bit1 :dfs, label: 'DFS'
|
14
14
|
# byte boundary
|
@@ -20,9 +20,11 @@ module RubySMB
|
|
20
20
|
bit1 :namespace_caching, label: 'Namespace Caching'
|
21
21
|
bit1 :shared_delete, label: 'Force Shared Delete'
|
22
22
|
bit1 :restrict_exclusive_opens, label: 'Restrict Exclusive Opens'
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
# byte boundary
|
24
|
+
bit5 :reserved3, label: 'Reserved Space'
|
25
|
+
bit1 :identity_remoting, label: 'Identity Remoting'
|
26
|
+
bit2 :reserved4, label: 'Reserved Space'
|
27
|
+
bit8 :reserved5, label: 'Reserved Space'
|
26
28
|
|
27
29
|
def caching_type
|
28
30
|
if vdo_caching == 1 && auto_caching.zero?
|
data/lib/ruby_smb/smb2/file.rb
CHANGED
@@ -52,7 +52,12 @@ module RubySMB
|
|
52
52
|
# @return [RubySMB::SMB2::Tree]
|
53
53
|
attr_accessor :tree
|
54
54
|
|
55
|
-
|
55
|
+
# Whether or not the share associated with this tree connect needs to be encrypted (SMB 3.x)
|
56
|
+
# @!attribute [rw] tree_connect_encrypt_data
|
57
|
+
# @return [Boolean]
|
58
|
+
attr_accessor :tree_connect_encrypt_data
|
59
|
+
|
60
|
+
def initialize(tree:, response:, name:, encrypt: false)
|
56
61
|
raise ArgumentError, 'No Tree Provided' if tree.nil?
|
57
62
|
raise ArgumentError, 'No Response Provided' if response.nil?
|
58
63
|
|
@@ -66,6 +71,7 @@ module RubySMB
|
|
66
71
|
@last_write = response.last_write.to_datetime
|
67
72
|
@size = response.end_of_file
|
68
73
|
@size_on_disk = response.allocation_size
|
74
|
+
@tree_connect_encrypt_data = encrypt
|
69
75
|
end
|
70
76
|
|
71
77
|
# Appends the supplied data to the end of the file.
|
@@ -79,11 +85,24 @@ module RubySMB
|
|
79
85
|
# Closes the handle to the remote file.
|
80
86
|
#
|
81
87
|
# @return [WindowsError::ErrorCode] the NTStatus code returned by the operation
|
88
|
+
# @raise [RubySMB::Error::InvalidPacket] if the response is not a CloseResponse packet
|
89
|
+
# @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
|
82
90
|
def close
|
83
91
|
close_request = set_header_fields(RubySMB::SMB2::Packet::CloseRequest.new)
|
84
|
-
raw_response = tree.client.send_recv(close_request)
|
92
|
+
raw_response = tree.client.send_recv(close_request, encrypt: @tree_connect_encrypt_data)
|
85
93
|
response = RubySMB::SMB2::Packet::CloseResponse.read(raw_response)
|
86
|
-
response.
|
94
|
+
unless response.valid?
|
95
|
+
raise RubySMB::Error::InvalidPacket.new(
|
96
|
+
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
97
|
+
expected_cmd: RubySMB::SMB2::Packet::CloseResponse::COMMAND,
|
98
|
+
received_proto: response.smb2_header.protocol,
|
99
|
+
received_cmd: response.smb2_header.command
|
100
|
+
)
|
101
|
+
end
|
102
|
+
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
103
|
+
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
104
|
+
end
|
105
|
+
response.status_code
|
87
106
|
end
|
88
107
|
|
89
108
|
# Read from the file, a specific number of bytes
|
@@ -93,6 +112,8 @@ module RubySMB
|
|
93
112
|
# @param bytes [Integer] the number of bytes to read
|
94
113
|
# @param offset [Integer] the byte offset in the file to start reading from
|
95
114
|
# @return [String] the data read from the file
|
115
|
+
# @raise [RubySMB::Error::InvalidPacket] if the response is not a ReadResponse packet
|
116
|
+
# @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
|
96
117
|
def read(bytes: size, offset: 0)
|
97
118
|
atomic_read_size = if bytes > tree.client.server_max_read_size
|
98
119
|
tree.client.server_max_read_size
|
@@ -101,8 +122,19 @@ module RubySMB
|
|
101
122
|
end
|
102
123
|
|
103
124
|
read_request = read_packet(read_length: atomic_read_size, offset: offset)
|
104
|
-
raw_response = tree.client.send_recv(read_request)
|
125
|
+
raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
|
105
126
|
response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
|
127
|
+
unless response.valid?
|
128
|
+
raise RubySMB::Error::InvalidPacket.new(
|
129
|
+
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
130
|
+
expected_cmd: RubySMB::SMB2::Packet::ReadResponse::COMMAND,
|
131
|
+
received_proto: response.smb2_header.protocol,
|
132
|
+
received_cmd: response.smb2_header.command
|
133
|
+
)
|
134
|
+
end
|
135
|
+
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
136
|
+
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
137
|
+
end
|
106
138
|
|
107
139
|
data = response.buffer.to_binary_s
|
108
140
|
|
@@ -113,8 +145,19 @@ module RubySMB
|
|
113
145
|
atomic_read_size = remaining_bytes if remaining_bytes < tree.client.server_max_read_size
|
114
146
|
|
115
147
|
read_request = read_packet(read_length: atomic_read_size, offset: offset)
|
116
|
-
raw_response = tree.client.send_recv(read_request)
|
148
|
+
raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
|
117
149
|
response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
|
150
|
+
unless response.valid?
|
151
|
+
raise RubySMB::Error::InvalidPacket.new(
|
152
|
+
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
153
|
+
expected_cmd: RubySMB::SMB2::Packet::ReadResponse::COMMAND,
|
154
|
+
received_proto: response.smb2_header.protocol,
|
155
|
+
received_cmd: response.smb2_header.command
|
156
|
+
)
|
157
|
+
end
|
158
|
+
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
159
|
+
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
160
|
+
end
|
118
161
|
|
119
162
|
data << response.buffer.to_binary_s
|
120
163
|
remaining_bytes -= atomic_read_size
|
@@ -133,17 +176,21 @@ module RubySMB
|
|
133
176
|
read_request.offset = offset
|
134
177
|
read_request
|
135
178
|
end
|
136
|
-
|
179
|
+
|
137
180
|
def send_recv_read(read_length: 0, offset: 0)
|
138
181
|
read_request = read_packet(read_length: read_length, offset: offset)
|
139
|
-
raw_response = tree.client.send_recv(read_request)
|
182
|
+
raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
|
140
183
|
response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
184
|
+
unless response.valid?
|
185
|
+
raise RubySMB::Error::InvalidPacket.new(
|
186
|
+
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
187
|
+
expected_cmd: RubySMB::SMB2::Packet::ReadResponse::COMMAND,
|
188
|
+
received_proto: response.smb2_header.protocol,
|
189
|
+
received_cmd: response.smb2_header.command
|
190
|
+
)
|
191
|
+
end
|
192
|
+
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
193
|
+
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
147
194
|
end
|
148
195
|
response.buffer.to_binary_s
|
149
196
|
end
|
@@ -151,9 +198,18 @@ module RubySMB
|
|
151
198
|
# Delete a file on close
|
152
199
|
#
|
153
200
|
# @return [WindowsError::ErrorCode] the NTStatus Response code
|
201
|
+
# @raise [RubySMB::Error::InvalidPacket] if the response is not a SetInfoResponse packet
|
154
202
|
def delete
|
155
|
-
raw_response = tree.client.send_recv(delete_packet)
|
203
|
+
raw_response = tree.client.send_recv(delete_packet, encrypt: @tree_connect_encrypt_data)
|
156
204
|
response = RubySMB::SMB2::Packet::SetInfoResponse.read(raw_response)
|
205
|
+
unless response.valid?
|
206
|
+
raise RubySMB::Error::InvalidPacket.new(
|
207
|
+
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
208
|
+
expected_cmd: RubySMB::SMB2::Packet::SetInfoResponse::COMMAND,
|
209
|
+
received_proto: response.smb2_header.protocol,
|
210
|
+
received_cmd: response.smb2_header.command
|
211
|
+
)
|
212
|
+
end
|
157
213
|
response.smb2_header.nt_status.to_nt_status
|
158
214
|
end
|
159
215
|
|
@@ -182,6 +238,7 @@ module RubySMB
|
|
182
238
|
# @param data [String] the data to write to the file
|
183
239
|
# @param offset [Integer] the offset in the file to start writing from
|
184
240
|
# @return [WindowsError::ErrorCode] the NTStatus code returned from the operation
|
241
|
+
# @raise [RubySMB::Error::InvalidPacket] if the response is not a WriteResponse packet
|
185
242
|
def write(data:'', offset: 0)
|
186
243
|
buffer = data.dup
|
187
244
|
bytes = data.length
|
@@ -193,8 +250,16 @@ module RubySMB
|
|
193
250
|
|
194
251
|
while buffer.length > 0 do
|
195
252
|
write_request = write_packet(data: buffer.slice!(0,atomic_write_size), offset: offset)
|
196
|
-
raw_response = tree.client.send_recv(write_request)
|
253
|
+
raw_response = tree.client.send_recv(write_request, encrypt: @tree_connect_encrypt_data)
|
197
254
|
response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
|
255
|
+
unless response.valid?
|
256
|
+
raise RubySMB::Error::InvalidPacket.new(
|
257
|
+
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
258
|
+
expected_cmd: RubySMB::SMB2::Packet::WriteResponse::COMMAND,
|
259
|
+
received_proto: response.smb2_header.protocol,
|
260
|
+
received_cmd: response.smb2_header.command
|
261
|
+
)
|
262
|
+
end
|
198
263
|
status = response.smb2_header.nt_status.to_nt_status
|
199
264
|
|
200
265
|
offset+= atomic_write_size
|
@@ -215,28 +280,41 @@ module RubySMB
|
|
215
280
|
write_request.buffer = data
|
216
281
|
write_request
|
217
282
|
end
|
218
|
-
|
283
|
+
|
219
284
|
def send_recv_write(data:'', offset: 0)
|
220
285
|
pkt = write_packet(data: data, offset: offset)
|
221
|
-
raw_response = tree.client.send_recv(pkt)
|
286
|
+
raw_response = tree.client.send_recv(pkt, encrypt: @tree_connect_encrypt_data)
|
222
287
|
response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
288
|
+
unless response.valid?
|
289
|
+
raise RubySMB::Error::InvalidPacket.new(
|
290
|
+
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
291
|
+
expected_cmd: RubySMB::SMB2::Packet::WriteResponse::COMMAND,
|
292
|
+
received_proto: response.smb2_header.protocol,
|
293
|
+
received_cmd: response.smb2_header.command
|
294
|
+
)
|
295
|
+
end
|
296
|
+
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
297
|
+
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
229
298
|
end
|
230
299
|
response.write_count
|
231
300
|
end
|
232
|
-
|
301
|
+
|
233
302
|
# Rename a file
|
234
303
|
#
|
235
304
|
# @param new_file_name [String] the new name
|
236
305
|
# @return [WindowsError::ErrorCode] the NTStatus Response code
|
306
|
+
# @raise [RubySMB::Error::InvalidPacket] if the response is not a SetInfoResponse packet
|
237
307
|
def rename(new_file_name)
|
238
|
-
raw_response = tree.client.send_recv(rename_packet(new_file_name))
|
308
|
+
raw_response = tree.client.send_recv(rename_packet(new_file_name), encrypt: @tree_connect_encrypt_data)
|
239
309
|
response = RubySMB::SMB2::Packet::SetInfoResponse.read(raw_response)
|
310
|
+
unless response.valid?
|
311
|
+
raise RubySMB::Error::InvalidPacket.new(
|
312
|
+
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
313
|
+
expected_cmd: RubySMB::SMB2::Packet::SetInfoResponse::COMMAND,
|
314
|
+
received_proto: response.smb2_header.protocol,
|
315
|
+
received_cmd: response.smb2_header.command
|
316
|
+
)
|
317
|
+
end
|
240
318
|
response.smb2_header.nt_status.to_nt_status
|
241
319
|
end
|
242
320
|
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module SMB2
|
3
|
+
|
4
|
+
# An SMB2 PREAUTH_INTEGRITY_CAPABILITIES context struct as defined in
|
5
|
+
# [2.2.3.1.1 SMB2_PREAUTH_INTEGRITY_CAPABILITIES](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5a07bd66-4734-4af8-abcf-5a44ff7ee0e5)
|
6
|
+
class PreauthIntegrityCapabilities < BinData::Record
|
7
|
+
SHA_512 = 0x0001
|
8
|
+
HASH_ALGORITM_MAP = {
|
9
|
+
SHA_512 => 'SHA512'
|
10
|
+
}
|
11
|
+
|
12
|
+
endian :little
|
13
|
+
|
14
|
+
uint16 :hash_algorithm_count, label: 'Hash Algorithm Count', initial_value: -> { hash_algorithms.size }
|
15
|
+
uint16 :salt_length, label: 'Salt Length', initial_value: -> { salt.num_bytes }
|
16
|
+
array :hash_algorithms, label: 'Hash Algorithms', type: :uint16, initial_length: -> { hash_algorithm_count }
|
17
|
+
string :salt, label: 'Salt', read_length: -> { salt_length }
|
18
|
+
end
|
19
|
+
|
20
|
+
# An SMB2 ENCRYPTION_CAPABILITIES context struct as defined in
|
21
|
+
# [2.2.3.1.2 SMB2_ENCRYPTION_CAPABILITIES](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/16693be7-2b27-4d3b-804b-f605bde5bcdd)
|
22
|
+
class EncryptionCapabilities < BinData::Record
|
23
|
+
AES_128_CCM = 0x0001
|
24
|
+
AES_128_GCM = 0x0002
|
25
|
+
ENCRYPTION_ALGORITHM_MAP = {
|
26
|
+
AES_128_CCM => 'AES-128-CCM',
|
27
|
+
AES_128_GCM => 'AES-128-GCM'
|
28
|
+
}
|
29
|
+
|
30
|
+
endian :little
|
31
|
+
|
32
|
+
uint16 :cipher_count, label: 'Cipher Count', initial_value: -> { ciphers.size }
|
33
|
+
array :ciphers, label: 'Ciphers', type: :uint16, initial_length: -> { cipher_count }
|
34
|
+
end
|
35
|
+
|
36
|
+
# An SMB2 COMPRESSION_CAPABILITIES context struct as defined in
|
37
|
+
# [2.2.3.1.3 SMB2_COMPRESSION_CAPABILITIES](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/78e0c942-ab41-472b-b117-4a95ebe88271)
|
38
|
+
class CompressionCapabilities < BinData::Record
|
39
|
+
# Flags
|
40
|
+
# Chained compression is not supported.
|
41
|
+
SMB2_COMPRESSION_CAPABILITIES_FLAG_NONE = 0x00000000
|
42
|
+
# Chained compression is supported on this connection.
|
43
|
+
SMB2_COMPRESSION_CAPABILITIES_FLAG_CHAINED = 0x00000001
|
44
|
+
|
45
|
+
# Compression Algorithms
|
46
|
+
NONE = 0x0000
|
47
|
+
LZNT1 = 0x0001
|
48
|
+
LZ77 = 0x0002
|
49
|
+
LZ77_Huffman = 0x0003
|
50
|
+
Pattern_V1 = 0x0004
|
51
|
+
COMPRESSION_ALGORITHM_MAP = {
|
52
|
+
NONE => 'NONE',
|
53
|
+
LZNT1 => 'LZNT1',
|
54
|
+
LZ77 => 'LZ77',
|
55
|
+
LZ77_Huffman => 'LZ77_Huffman',
|
56
|
+
Pattern_V1 => 'Pattern_V1'
|
57
|
+
}
|
58
|
+
|
59
|
+
endian :little
|
60
|
+
|
61
|
+
uint16 :compression_algorithm_count, label: 'Compression Algorithm Count', initial_value: -> { compression_algorithms.size }
|
62
|
+
uint16 :padding, label: 'Padding', initial_value: 0
|
63
|
+
uint32 :flags, label: 'Flags'
|
64
|
+
array :compression_algorithms, label: 'Compression Algorithms', type: :uint16, initial_length: -> { compression_algorithm_count }
|
65
|
+
end
|
66
|
+
|
67
|
+
# An SMB2 NETNAME_NEGOTIATE_CONTEXT_ID context struct as defined in
|
68
|
+
# [2.2.3.1.4 SMB2_NETNAME_NEGOTIATE_CONTEXT_ID](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/ca6726bd-b9cf-43d9-b0bc-d127d3c993b3)
|
69
|
+
class NetnameNegotiateContextId < BinData::Record
|
70
|
+
endian :little
|
71
|
+
|
72
|
+
stringz16 :net_name, label: 'Net Name'
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
# An SMB2 NEGOTIATE_CONTEXT struct as defined in
|
77
|
+
# [2.2.3.1 SMB2 NEGOTIATE_CONTEXT Request Values](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/15332256-522e-4a53-8cd7-0bd17678a2f7)
|
78
|
+
class NegotiateContext < BinData::Record
|
79
|
+
# The NegotiateContext Data field contains a list of preauthentication integrity hash functions as well as an optional salt value, as specified in section 2.2.3.1.1.
|
80
|
+
SMB2_PREAUTH_INTEGRITY_CAPABILITIES = 0x0001
|
81
|
+
# The NegotiateContext Data field contains a list of encryption algorithms, as specified in section 2.2.3.1.2.
|
82
|
+
SMB2_ENCRYPTION_CAPABILITIES = 0x0002
|
83
|
+
# The NegotiateContext Data field contains a list of compression algorithms, as specified in section 2.2.3.1.3.
|
84
|
+
SMB2_COMPRESSION_CAPABILITIES = 0x0003
|
85
|
+
# The NegotiateContext Data field contains the server name to which the client connects.
|
86
|
+
SMB2_NETNAME_NEGOTIATE_CONTEXT_ID = 0x0005
|
87
|
+
|
88
|
+
endian :little
|
89
|
+
|
90
|
+
string :pad, label: 'Padding', length: -> { pad_length }
|
91
|
+
uint16 :context_type, label: 'Context Type'
|
92
|
+
uint16 :data_length, label: 'Data Length', initial_value: -> { data.num_bytes }
|
93
|
+
uint32 :reserved, label: 'Reserved', initial_value: 0
|
94
|
+
choice :data, label: 'Data', selection: -> { context_type } do
|
95
|
+
preauth_integrity_capabilities SMB2_PREAUTH_INTEGRITY_CAPABILITIES, label: 'Preauthentication Integrity Capabilities'
|
96
|
+
encryption_capabilities SMB2_ENCRYPTION_CAPABILITIES, label: 'Encryption Capabilities'
|
97
|
+
compression_capabilities SMB2_COMPRESSION_CAPABILITIES, label: 'Compression Capabilities'
|
98
|
+
netname_negotiate_context_id SMB2_NETNAME_NEGOTIATE_CONTEXT_ID, label: 'Netname Negotiate Context ID'
|
99
|
+
end
|
100
|
+
|
101
|
+
def pad_length
|
102
|
+
offset = pad.abs_offset % 8
|
103
|
+
(8 - offset) % 8
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|