ruby_smb 3.0.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/examples/auth_capture.rb +28 -0
- data/examples/file_server.rb +76 -0
- data/lib/ruby_smb/create_actions.rb +21 -0
- data/lib/ruby_smb/field/nt_status.rb +20 -1
- data/lib/ruby_smb/fscc/file_information/file_ea_information.rb +14 -0
- data/lib/ruby_smb/fscc/file_information/file_network_open_information.rb +22 -0
- data/lib/ruby_smb/fscc/file_information/file_stream_information.rb +16 -0
- data/lib/ruby_smb/fscc/file_information.rb +29 -0
- data/lib/ruby_smb/fscc/file_system_information/file_fs_attribute_information.rb +46 -0
- data/lib/ruby_smb/fscc/file_system_information/file_fs_volume_information.rb +19 -0
- data/lib/ruby_smb/fscc/file_system_information.rb +22 -0
- data/lib/ruby_smb/fscc.rb +1 -0
- data/lib/ruby_smb/generic_packet.rb +6 -0
- data/lib/ruby_smb/gss/provider/authenticator.rb +4 -0
- data/lib/ruby_smb/gss/provider/ntlm.rb +13 -3
- data/lib/ruby_smb/server/server_client/negotiation.rb +0 -2
- data/lib/ruby_smb/server/server_client/session_setup.rb +43 -32
- data/lib/ruby_smb/server/server_client/share_io.rb +28 -0
- data/lib/ruby_smb/server/server_client/tree_connect.rb +60 -0
- data/lib/ruby_smb/server/server_client.rb +214 -24
- data/lib/ruby_smb/server/session.rb +71 -0
- data/lib/ruby_smb/server/share/provider/disk.rb +437 -0
- data/lib/ruby_smb/server/share/provider/pipe.rb +27 -0
- data/lib/ruby_smb/server/share/provider/processor.rb +76 -0
- data/lib/ruby_smb/server/share/provider.rb +38 -0
- data/lib/ruby_smb/server/share.rb +11 -0
- data/lib/ruby_smb/server.rb +35 -3
- data/lib/ruby_smb/signing.rb +37 -11
- data/lib/ruby_smb/smb1/commands.rb +4 -0
- data/lib/ruby_smb/smb1.rb +0 -1
- data/lib/ruby_smb/smb2/bit_field/smb2_header_flags.rb +2 -1
- data/lib/ruby_smb/smb2/commands.rb +4 -0
- data/lib/ruby_smb/smb2/create_context/request.rb +64 -0
- data/lib/ruby_smb/smb2/create_context/response.rb +62 -0
- data/lib/ruby_smb/smb2/create_context.rb +74 -22
- data/lib/ruby_smb/smb2/packet/create_request.rb +44 -11
- data/lib/ruby_smb/smb2/packet/create_response.rb +17 -3
- data/lib/ruby_smb/smb2/packet/query_directory_request.rb +1 -1
- data/lib/ruby_smb/smb2/packet/query_directory_response.rb +2 -2
- data/lib/ruby_smb/smb2/packet/query_info_request.rb +43 -0
- data/lib/ruby_smb/smb2/packet/query_info_response.rb +23 -0
- data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +1 -1
- data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +1 -0
- data/lib/ruby_smb/smb2/packet.rb +2 -0
- data/lib/ruby_smb/smb2.rb +11 -0
- data/lib/ruby_smb/smb_error.rb +110 -0
- data/lib/ruby_smb/version.rb +1 -1
- data/lib/ruby_smb.rb +2 -0
- data/ruby_smb.gemspec +1 -1
- data/spec/lib/ruby_smb/field/nt_status_spec.rb +6 -2
- data/spec/lib/ruby_smb/gss/provider/ntlm/authenticator_spec.rb +4 -0
- data/spec/lib/ruby_smb/server/server_client_spec.rb +36 -53
- data/spec/lib/ruby_smb/server/session_spec.rb +38 -0
- data/spec/lib/ruby_smb/server/share/provider/disk_spec.rb +61 -0
- data/spec/lib/ruby_smb/server/share/provider/pipe_spec.rb +31 -0
- data/spec/lib/ruby_smb/server/share/provider_spec.rb +13 -0
- data/spec/lib/ruby_smb/smb2/bit_field/header_flags_spec.rb +8 -2
- data/spec/lib/ruby_smb/smb2/{create_context_spec.rb → create_context/create_context_request_spec.rb} +1 -1
- data/spec/lib/ruby_smb/smb2/packet/create_request_spec.rb +5 -5
- data/spec/lib/ruby_smb/smb2/packet/create_response_spec.rb +9 -5
- data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +3 -2
- data.tar.gz.sig +0 -0
- metadata +35 -7
- metadata.gz.sig +0 -0
- data/lib/ruby_smb/smb1/create_actions.rb +0 -20
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'windows_error'
|
2
|
+
|
3
|
+
module RubySMB
|
4
|
+
# SMB Error codes as defined in
|
5
|
+
# [2.2.2.4 SMB Error Classes and Codes](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb/6ab6ca20-b404-41fd-b91a-2ed39e3762ea)
|
6
|
+
module SMBError
|
7
|
+
# Returns all the {WindowsError::ErrorCode} objects that match
|
8
|
+
# the return value supplied.
|
9
|
+
#
|
10
|
+
# @param [Integer] retval the return value you want the error code for
|
11
|
+
# @raise [ArgumentError] if something other than a Integer is supplied
|
12
|
+
# @return [Array<WindowsError::ErrorCode>] all Win32 ErrorCodes that matched
|
13
|
+
def self.find_by_retval(retval)
|
14
|
+
raise ArgumentError, "Invalid Return Code!" unless retval.kind_of? Integer
|
15
|
+
error_codes = []
|
16
|
+
self.constants.each do |constant_name|
|
17
|
+
error_code = self.const_get(constant_name)
|
18
|
+
if error_code == retval
|
19
|
+
error_codes << error_code
|
20
|
+
end
|
21
|
+
end
|
22
|
+
error_codes
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# CONSTANTS
|
27
|
+
#
|
28
|
+
|
29
|
+
# (0x00000000) The client request is successful.
|
30
|
+
STATUS_SUCCESS = WindowsError::ErrorCode.new('STATUS_SUCCESS', 0x00000000, 'The client request is successful.')
|
31
|
+
|
32
|
+
# (0x00010002) An invalid SMB client request is received by the server.
|
33
|
+
STATUS_INVALID_SMB = WindowsError::ErrorCode.new('STATUS_INVALID_SMB', 0x00010002, 'An invalid SMB client request is received by the server.')
|
34
|
+
|
35
|
+
# (0x00050002) The client request received by the server contains an invalid TID value.
|
36
|
+
STATUS_SMB_BAD_TID = WindowsError::ErrorCode.new('STATUS_SMB_BAD_TID', 0x00050002, 'The client request received by the server contains an invalid TID value.')
|
37
|
+
|
38
|
+
# (0x00160002) The client request received by the server contains an unknown SMB command code.
|
39
|
+
STATUS_SMB_BAD_COMMAND = WindowsError::ErrorCode.new('STATUS_SMB_BAD_COMMAND', 0x00160002, 'The client request received by the server contains an unknown SMB command code.')
|
40
|
+
|
41
|
+
# (0x005B0002) The client request to the server contains an invalid UID value.
|
42
|
+
STATUS_SMB_BAD_UID = WindowsError::ErrorCode.new('STATUS_SMB_BAD_UID', 0x005B0002, 'The client request to the server contains an invalid UID value.')
|
43
|
+
|
44
|
+
# (0x00FB0002) The client request received by the server is for a non-standard SMB operation (for example, an SMB_COM_READ_MPX request on a non-disk share). The client SHOULD send another request with a different SMB command to perform this operation.
|
45
|
+
STATUS_SMB_USE_STANDARD = WindowsError::ErrorCode.new('STATUS_SMB_USE_STANDARD', 0x00FB0002, 'The client request received by the server is for a non-standard SMB operation (for example, an SMB_COM_READ_MPX request on a non-disk share). The client SHOULD send another request with a different SMB command to perform this operation.')
|
46
|
+
|
47
|
+
# (0x80000005) The data was too large to fit into the specified buffer.
|
48
|
+
STATUS_BUFFER_OVERFLOW = WindowsError::ErrorCode.new('STATUS_BUFFER_OVERFLOW', 0x80000005, 'The data was too large to fit into the specified buffer.')
|
49
|
+
|
50
|
+
# (0x80000006) No more files were found that match the file specification.
|
51
|
+
STATUS_NO_MORE_FILES = WindowsError::ErrorCode.new('STATUS_NO_MORE_FILES', 0x80000006, 'No more files were found that match the file specification.')
|
52
|
+
|
53
|
+
# (0x8000002D) The create operation stopped after reaching a symbolic link.
|
54
|
+
STATUS_STOPPED_ON_SYMLINK = WindowsError::ErrorCode.new('STATUS_STOPPED_ON_SYMLINK', 0x8000002D, 'The create operation stopped after reaching a symbolic link.')
|
55
|
+
|
56
|
+
# (0xC0000002) The requested operation is not implemented.
|
57
|
+
STATUS_NOT_IMPLEMENTED = WindowsError::ErrorCode.new('STATUS_NOT_IMPLEMENTED', 0xC0000002, 'The requested operation is not implemented.')
|
58
|
+
|
59
|
+
# (0xC000000D) The parameter specified in the request is not valid.
|
60
|
+
STATUS_INVALID_PARAMETER = WindowsError::ErrorCode.new('STATUS_INVALID_PARAMETER', 0xC000000D, 'The parameter specified in the request is not valid.')
|
61
|
+
|
62
|
+
# (0xC000000E) A device that does not exist was specified.
|
63
|
+
STATUS_NO_SUCH_DEVICE = WindowsError::ErrorCode.new('STATUS_NO_SUCH_DEVICE', 0xC000000E, 'A device that does not exist was specified.')
|
64
|
+
|
65
|
+
# (0xC0000010) The specified request is not a valid operation for the target device.
|
66
|
+
STATUS_INVALID_DEVICE_REQUEST = WindowsError::ErrorCode.new('STATUS_INVALID_DEVICE_REQUEST', 0xC0000010, 'The specified request is not a valid operation for the target device.')
|
67
|
+
|
68
|
+
# (0xC0000016) If extended security has been negotiated, then this error code can be returned in the SMB_COM_SESSION_SETUP_ANDX response from the server to indicate that additional authentication information is to be exchanged. See section 2.2.4.6 for details.
|
69
|
+
STATUS_MORE_PROCESSING_REQUIRED = WindowsError::ErrorCode.new('STATUS_MORE_PROCESSING_REQUIRED', 0xC0000016, 'If extended security has been negotiated, then this error code can be returned in the SMB_COM_SESSION_SETUP_ANDX response from the server to indicate that additional authentication information is to be exchanged. See section 2.2.4.6 for details.')
|
70
|
+
|
71
|
+
# (0xC0000022) The client did not have the required permission needed for the operation.
|
72
|
+
STATUS_ACCESS_DENIED = WindowsError::ErrorCode.new('STATUS_ACCESS_DENIED', 0xC0000022, 'The client did not have the required permission needed for the operation.')
|
73
|
+
|
74
|
+
# (0xC0000023) The buffer is too small to contain the entry. No information has been written to the buffer.
|
75
|
+
STATUS_BUFFER_TOO_SMALL = WindowsError::ErrorCode.new('STATUS_BUFFER_TOO_SMALL', 0xC0000023, 'The buffer is too small to contain the entry. No information has been written to the buffer.')
|
76
|
+
|
77
|
+
# (0xC0000034) The object name is not found.
|
78
|
+
STATUS_OBJECT_NAME_NOT_FOUND = WindowsError::ErrorCode.new('STATUS_OBJECT_NAME_NOT_FOUND', 0xC0000034, 'The object name is not found.')
|
79
|
+
|
80
|
+
# (0xC0000035) The object name already exists.
|
81
|
+
STATUS_OBJECT_NAME_COLLISION = WindowsError::ErrorCode.new('STATUS_OBJECT_NAME_COLLISION', 0xC0000035, 'The object name already exists.')
|
82
|
+
|
83
|
+
# (0xC000003A) The path to the directory specified was not found. This error is also returned on a create request if the operation requires the creation of more than one new directory level for the path specified.
|
84
|
+
STATUS_OBJECT_PATH_NOT_FOUND = WindowsError::ErrorCode.new('STATUS_OBJECT_PATH_NOT_FOUND', 0xC000003A, 'The path to the directory specified was not found. This error is also returned on a create request if the operation requires the creation of more than one new directory level for the path specified.')
|
85
|
+
|
86
|
+
# (0xC00000A5) A specified impersonation level is invalid. This error is also used to indicate that a required impersonation level was not provided.
|
87
|
+
STATUS_BAD_IMPERSONATION_LEVEL = WindowsError::ErrorCode.new('STATUS_BAD_IMPERSONATION_LEVEL', 0xC00000A5, 'A specified impersonation level is invalid. This error is also used to indicate that a required impersonation level was not provided.')
|
88
|
+
|
89
|
+
# (0xC00000B5) The specified I/O operation was not completed before the time-out period expired.
|
90
|
+
STATUS_IO_TIMEOUT = WindowsError::ErrorCode.new('STATUS_IO_TIMEOUT', 0xC00000B5, 'The specified I/O operation was not completed before the time-out period expired.')
|
91
|
+
|
92
|
+
# (0xC00000BA) The file that was specified as a target is a directory and the caller specified that it could be anything but a directory.
|
93
|
+
STATUS_FILE_IS_A_DIRECTORY = WindowsError::ErrorCode.new('STATUS_FILE_IS_A_DIRECTORY', 0xC00000BA, 'The file that was specified as a target is a directory and the caller specified that it could be anything but a directory.')
|
94
|
+
|
95
|
+
# (0xC00000BB) The client request is not supported.
|
96
|
+
STATUS_NOT_SUPPORTED = WindowsError::ErrorCode.new('STATUS_NOT_SUPPORTED', 0xC00000BB, 'The client request is not supported.')
|
97
|
+
|
98
|
+
# (0xC00000C9) The network name specified by the client has been deleted on the server. This error is returned if the client specifies an incorrect TID or the share on the server represented by the TID was deleted.
|
99
|
+
STATUS_NETWORK_NAME_DELETED = WindowsError::ErrorCode.new('STATUS_NETWORK_NAME_DELETED', 0xC00000C9, 'The network name specified by the client has been deleted on the server. This error is returned if the client specifies an incorrect TID or the share on the server represented by the TID was deleted.')
|
100
|
+
|
101
|
+
# (0xC0000203) The user session specified by the client has been deleted on the server. This error is returned by the server if the client sends an incorrect UID.
|
102
|
+
STATUS_USER_SESSION_DELETED = WindowsError::ErrorCode.new('STATUS_USER_SESSION_DELETED', 0xC0000203, 'The user session specified by the client has been deleted on the server. This error is returned by the server if the client sends an incorrect UID.')
|
103
|
+
|
104
|
+
# (0xC000035C) The client's session has expired; therefore, the client MUST re-authenticate to continue accessing remote resources.
|
105
|
+
STATUS_NETWORK_SESSION_EXPIRED = WindowsError::ErrorCode.new('STATUS_NETWORK_SESSION_EXPIRED', 0xC000035C, 'The client\'s session has expired; therefore, the client MUST re-authenticate to continue accessing remote resources.')
|
106
|
+
|
107
|
+
# (0xC000205A) The client has requested too many UID values from the server or the client already has an SMB session setup with this UID value.
|
108
|
+
STATUS_SMB_TOO_MANY_UIDS = WindowsError::ErrorCode.new('STATUS_SMB_TOO_MANY_UIDS', 0xC000205A, 'The client has requested too many UID values from the server or the client already has an SMB session setup with this UID value.')
|
109
|
+
end
|
110
|
+
end
|
data/lib/ruby_smb/version.rb
CHANGED
data/lib/ruby_smb.rb
CHANGED
@@ -12,6 +12,7 @@ require 'windows_error/nt_status'
|
|
12
12
|
# [[MS-SMB2] Server Message Block (SMB) Protocol Versions 2 and 3](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
|
13
13
|
module RubySMB
|
14
14
|
require 'ruby_smb/error'
|
15
|
+
require 'ruby_smb/create_actions'
|
15
16
|
require 'ruby_smb/dispositions'
|
16
17
|
require 'ruby_smb/impersonation_levels'
|
17
18
|
require 'ruby_smb/gss'
|
@@ -28,4 +29,5 @@ module RubySMB
|
|
28
29
|
require 'ruby_smb/compression'
|
29
30
|
require 'ruby_smb/server'
|
30
31
|
require 'ruby_smb/dialect'
|
32
|
+
require 'ruby_smb/smb_error'
|
31
33
|
end
|
data/ruby_smb.gemspec
CHANGED
@@ -34,7 +34,7 @@ Gem::Specification.new do |spec|
|
|
34
34
|
spec.add_development_dependency 'yard'
|
35
35
|
|
36
36
|
spec.add_runtime_dependency 'rubyntlm'
|
37
|
-
spec.add_runtime_dependency 'windows_error'
|
37
|
+
spec.add_runtime_dependency 'windows_error', '>= 0.1.3'
|
38
38
|
spec.add_runtime_dependency 'bindata'
|
39
39
|
spec.add_runtime_dependency 'openssl-ccm'
|
40
40
|
spec.add_runtime_dependency 'openssl-cmac'
|
@@ -3,8 +3,12 @@ RSpec.describe RubySMB::Field::NtStatus do
|
|
3
3
|
|
4
4
|
it { is_expected.to respond_to :to_nt_status }
|
5
5
|
|
6
|
-
it '
|
7
|
-
expect(nt_status).to be_a BinData::Uint32le
|
6
|
+
it 'uses an internal Unsigned 32-bit little endian integer' do
|
7
|
+
expect(nt_status.val).to be_a BinData::Uint32le
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'returns an integer value' do
|
11
|
+
expect(nt_status.value).to be_a Integer
|
8
12
|
end
|
9
13
|
|
10
14
|
describe '#to_nt_status' do
|
@@ -13,6 +13,10 @@ RSpec.describe RubySMB::Gss::Provider::NTLM::Authenticator do
|
|
13
13
|
Net::NTLM::Message::Type2.new.response(user: username, password: '', domain: domain)
|
14
14
|
end
|
15
15
|
|
16
|
+
before(:each) do
|
17
|
+
allow(authenticator).to receive(:logger).and_return(Logger.new(IO::NULL))
|
18
|
+
end
|
19
|
+
|
16
20
|
describe '#initialize' do
|
17
21
|
it 'defaults to a null session key' do
|
18
22
|
expect(authenticator.session_key).to be_nil
|
@@ -5,33 +5,24 @@ RSpec.describe RubySMB::Server::ServerClient do
|
|
5
5
|
subject(:server_client) { described_class.new(server, dispatcher) }
|
6
6
|
|
7
7
|
it { is_expected.to respond_to :dialect }
|
8
|
-
it { is_expected.to respond_to :
|
9
|
-
it { is_expected.to respond_to :state }
|
10
|
-
it { is_expected.to respond_to :session_key }
|
8
|
+
it { is_expected.to respond_to :session_table }
|
11
9
|
|
12
10
|
describe '#disconnect!' do
|
13
11
|
it 'closes the socket' do
|
12
|
+
expect(dispatcher.tcp_socket).to receive(:closed?).with(no_args).and_return(false)
|
14
13
|
expect(dispatcher.tcp_socket).to receive(:close).with(no_args).and_return(nil)
|
15
14
|
server_client.disconnect!
|
16
15
|
end
|
17
16
|
end
|
18
17
|
|
19
18
|
describe '#initialize' do
|
20
|
-
it 'starts in the negotiate state' do
|
21
|
-
expect(server_client.state).to eq :negotiate
|
22
|
-
end
|
23
|
-
|
24
19
|
it 'starts without a dialect' do
|
25
20
|
expect(server_client.dialect).to be_nil
|
26
21
|
expect(server_client.metadialect).to be_nil
|
27
22
|
end
|
28
23
|
|
29
|
-
it 'starts without
|
30
|
-
expect(server_client.
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'starts without a session_key' do
|
34
|
-
expect(server_client.session_key).to be_nil
|
24
|
+
it 'starts without any sessions' do
|
25
|
+
expect(server_client.session_table).to be_empty
|
35
26
|
end
|
36
27
|
|
37
28
|
it 'creates a new authenticator instance' do
|
@@ -66,33 +57,30 @@ RSpec.describe RubySMB::Server::ServerClient do
|
|
66
57
|
before(:each) do
|
67
58
|
expect(server_client).to receive(:recv_packet).and_return(packet)
|
68
59
|
# this hook should ensure that the dispatcher loop returns after processing a single request
|
69
|
-
expect(dispatcher.tcp_socket).to receive(:closed?).and_return(true)
|
60
|
+
expect(dispatcher.tcp_socket).to receive(:closed?).with(no_args).and_return(true)
|
61
|
+
expect(server_client).to receive(:disconnect!).with(no_args).and_return(nil)
|
70
62
|
end
|
71
63
|
|
72
|
-
it 'calls #handle_negotiate when the
|
64
|
+
it 'calls #handle_negotiate when the dialect is nil' do
|
73
65
|
expect(server_client).to receive(:handle_negotiate).with(packet).and_return(nil)
|
74
|
-
server_client.instance_eval { @
|
75
|
-
server_client.run
|
76
|
-
end
|
77
|
-
|
78
|
-
it 'calls #handle_session_setup when the state is session_setup' do
|
79
|
-
expect(server_client).to receive(:handle_session_setup).with(packet).and_return(nil)
|
80
|
-
server_client.instance_eval { @state = :session_setup }
|
66
|
+
server_client.instance_eval { @dialect = nil }
|
81
67
|
server_client.run
|
82
68
|
end
|
83
69
|
|
84
|
-
it 'calls #
|
85
|
-
expect(server_client).to receive(:
|
86
|
-
server_client.instance_eval { @
|
70
|
+
it 'calls #handle_smb when the dialect is not nil' do
|
71
|
+
expect(server_client).to receive(:handle_smb).with(packet).and_return(nil)
|
72
|
+
server_client.instance_eval { @dialect = true }
|
87
73
|
server_client.run
|
88
74
|
end
|
89
75
|
end
|
90
76
|
|
91
77
|
describe '#send_packet' do
|
92
|
-
let(:
|
78
|
+
let(:session_id) { rand(0xffffffff) }
|
79
|
+
let(:packet) { RubySMB::SMB2::Packet::SessionSetupResponse.new(smb2_header: { session_id: session_id }) }
|
93
80
|
|
94
81
|
before(:each) do
|
95
82
|
expect(dispatcher).to receive(:send_packet).with(packet).and_return(nil)
|
83
|
+
server_client.session_table[session_id] = RubySMB::Server::Session.new(session_id)
|
96
84
|
end
|
97
85
|
|
98
86
|
it 'sends a packet to the dispatcher' do
|
@@ -105,40 +93,35 @@ RSpec.describe RubySMB::Server::ServerClient do
|
|
105
93
|
server_client.instance_eval { @dialect = dialect }
|
106
94
|
end
|
107
95
|
|
108
|
-
context 'and the
|
96
|
+
context 'and the identity is anonymous' do
|
109
97
|
before(:each) do
|
110
|
-
server_client.
|
98
|
+
server_client.session_table[session_id].user_id = RubySMB::Gss::Provider::IDENTITY_ANONYMOUS
|
111
99
|
end
|
112
100
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
it 'does not sign packets' do
|
119
|
-
expect(server_client).to_not receive(:smb2_sign)
|
120
|
-
expect(server_client).to_not receive(:smb3_sign)
|
121
|
-
server_client.send_packet(packet)
|
122
|
-
end
|
101
|
+
it 'does not sign packets' do
|
102
|
+
expect(RubySMB::Signing).to_not receive(:smb2_sign)
|
103
|
+
expect(RubySMB::Signing).to_not receive(:smb3_sign)
|
104
|
+
server_client.send_packet(packet)
|
123
105
|
end
|
106
|
+
end
|
124
107
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
108
|
+
context 'and the identity is not anonymous' do
|
109
|
+
before(:each) do
|
110
|
+
server_client.session_table[session_id].user_id = 'WORKGROUP\RubySMB'
|
111
|
+
server_client.session_table[session_id].key = Random.new.bytes(16)
|
112
|
+
end
|
129
113
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
end
|
140
|
-
server_client.send_packet(packet)
|
114
|
+
it 'does sign packets' do
|
115
|
+
dialect_family = RubySMB::Dialect[dialect].family
|
116
|
+
session = server_client.session_table[session_id]
|
117
|
+
if dialect_family == RubySMB::Dialect::FAMILY_SMB2
|
118
|
+
expect(RubySMB::Signing).to receive(:smb2_sign).with(packet, session.key).and_return(packet)
|
119
|
+
expect(RubySMB::Signing).to_not receive(:smb3_sign)
|
120
|
+
elsif dialect_family == RubySMB::Dialect::FAMILY_SMB3
|
121
|
+
expect(RubySMB::Signing).to receive(:smb3_sign).with(packet, session.key, dialect, any_args).and_return(packet)
|
122
|
+
expect(RubySMB::Signing).to_not receive(:smb2_sign)
|
141
123
|
end
|
124
|
+
server_client.send_packet(packet)
|
142
125
|
end
|
143
126
|
end
|
144
127
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
RSpec.describe RubySMB::Server::Session do
|
2
|
+
let(:user_id) { 'WORKGROUP\RubySMB' }
|
3
|
+
subject(:session) { described_class.new(rand(0xffffffff), user_id: user_id) }
|
4
|
+
|
5
|
+
it { is_expected.to respond_to :id }
|
6
|
+
it { is_expected.to respond_to :key }
|
7
|
+
it { is_expected.to respond_to :signing_required }
|
8
|
+
it { is_expected.to respond_to :tree_connect_table }
|
9
|
+
it { is_expected.to respond_to :creation_time }
|
10
|
+
|
11
|
+
describe '#initialize' do
|
12
|
+
it 'starts with no signing required' do
|
13
|
+
expect(session.signing_required).to be_falsey
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'starts with no tree connections' do
|
17
|
+
expect(session.tree_connect_table).to be_empty
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'starts in the in progress state' do
|
21
|
+
expect(session.state).to be :in_progress
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#is_anonymous' do
|
26
|
+
it 'is false' do
|
27
|
+
expect(session.is_anonymous).to be_falsey
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when the identity is set to anonymous' do
|
31
|
+
let(:user_id) { RubySMB::Gss::Provider::IDENTITY_ANONYMOUS }
|
32
|
+
|
33
|
+
it 'is true' do
|
34
|
+
expect(session.is_anonymous).to be_truthy
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
RSpec.describe RubySMB::Server::Share::Provider::Disk do
|
2
|
+
let(:name) { 'share' }
|
3
|
+
let(:path) { Dir.getwd }
|
4
|
+
subject(:share_provider) { described_class.new(name, path) }
|
5
|
+
|
6
|
+
it { is_expected.to respond_to :name }
|
7
|
+
it { is_expected.to respond_to :path }
|
8
|
+
it { is_expected.to respond_to :type }
|
9
|
+
|
10
|
+
describe '#TYPE' do
|
11
|
+
it 'is TYPE_DISK' do
|
12
|
+
expect(described_class::TYPE).to eq RubySMB::Server::Share::TYPE_DISK
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#initialize' do
|
17
|
+
it 'sets the name' do
|
18
|
+
expect(share_provider.name).to eq name
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'sets the path' do
|
22
|
+
expect(share_provider.path).to be_a Pathname
|
23
|
+
expect(share_provider.path).to eq Pathname.new(path)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'sets the type correctly' do
|
27
|
+
expect(share_provider.type).to eq RubySMB::Server::Share::TYPE_DISK
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'raises an ArgumentError for an invalid path' do
|
31
|
+
# __FILE__ is not a directory so it's invalid
|
32
|
+
expect { described_class.new(name, __FILE__) }.to raise_error(ArgumentError)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
RSpec.describe RubySMB::Server::Share::Provider::Disk::Processor do
|
38
|
+
let(:session) { RubySMB::Server::Session.new(rand(0xffffffff)) }
|
39
|
+
let(:share_provider) { RubySMB::Server::Share::Provider::Disk.new('share', Dir.getwd) }
|
40
|
+
subject(:share_processor) { described_class.new(share_provider, nil, session) }
|
41
|
+
|
42
|
+
it { is_expected.to respond_to :provider }
|
43
|
+
|
44
|
+
describe '#maximal_access' do
|
45
|
+
# no path specified should be the root of the share
|
46
|
+
context 'with no path specified' do
|
47
|
+
let(:maximal_access) { share_processor.maximal_access }
|
48
|
+
it 'is a FileAccessMask' do
|
49
|
+
expect(maximal_access).to be_a RubySMB::SMB2::BitField::FileAccessMask
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'marks the data as readable' do
|
53
|
+
expect(maximal_access.read_data).to eq 1
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'marks the data attributes as readable' do
|
57
|
+
expect(maximal_access.read_attr).to eq 1
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
RSpec.describe RubySMB::Server::Share::Provider::Pipe do
|
2
|
+
let(:name) { 'share' }
|
3
|
+
subject(:share_provider) { described_class.new(name) }
|
4
|
+
|
5
|
+
it { is_expected.to respond_to :name }
|
6
|
+
it { is_expected.to respond_to :type }
|
7
|
+
|
8
|
+
describe '#TYPE' do
|
9
|
+
it 'is TYPE_PIPE' do
|
10
|
+
expect(described_class::TYPE).to eq RubySMB::Server::Share::TYPE_PIPE
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#initialize' do
|
15
|
+
it 'sets the name' do
|
16
|
+
expect(share_provider.name).to eq name
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'sets the type correctly' do
|
20
|
+
expect(share_provider.type).to eq RubySMB::Server::Share::TYPE_PIPE
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
RSpec.describe RubySMB::Server::Share::Provider::Pipe::Processor do
|
26
|
+
let(:session) { RubySMB::Server::Session.new(rand(0xffffffff)) }
|
27
|
+
let(:share_provider) { RubySMB::Server::Share::Provider::Pipe.new('share') }
|
28
|
+
subject(:share_processor) { described_class.new(share_provider, nil, session) }
|
29
|
+
|
30
|
+
it { is_expected.to respond_to :provider }
|
31
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
RSpec.describe RubySMB::Server::Share::Provider::Base do
|
2
|
+
let(:name) { 'share' }
|
3
|
+
subject(:share_provider) { described_class.new(name) }
|
4
|
+
|
5
|
+
it { is_expected.to respond_to :name }
|
6
|
+
it { is_expected.to respond_to :type }
|
7
|
+
|
8
|
+
describe '#initialize' do
|
9
|
+
it 'sets the name' do
|
10
|
+
expect(share_provider.name).to eq name
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -44,8 +44,14 @@ RSpec.describe RubySMB::SMB2::BitField::Smb2HeaderFlags do
|
|
44
44
|
end
|
45
45
|
|
46
46
|
describe '#reserved3' do
|
47
|
-
it 'should be a
|
48
|
-
expect(flags.reserved3).to be_a BinData::
|
47
|
+
it 'should be a 1-bit field per the SMB spec' do
|
48
|
+
expect(flags.reserved3).to be_a BinData::Bit1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#priority' do
|
53
|
+
it 'should be a 3-bit field per the SMB spec' do
|
54
|
+
expect(flags.priority).to be_a BinData::Bit3
|
49
55
|
end
|
50
56
|
end
|
51
57
|
|
@@ -80,22 +80,22 @@ RSpec.describe RubySMB::SMB2::Packet::CreateRequest do
|
|
80
80
|
expect(packet.name_offset).to eq packet.name.abs_offset
|
81
81
|
end
|
82
82
|
|
83
|
-
it 'tracks the offset to #context in #
|
84
|
-
expect(packet.
|
83
|
+
it 'tracks the offset to #context in #contexts_offset' do
|
84
|
+
expect(packet.contexts_offset).to eq packet.contexts.abs_offset
|
85
85
|
end
|
86
86
|
|
87
87
|
it 'tracks the length of #name in #name_length' do
|
88
88
|
expect(packet.name_length).to eq packet.name.length
|
89
89
|
end
|
90
90
|
|
91
|
-
it 'tracks the length of #context in #
|
92
|
-
expect(packet.
|
91
|
+
it 'tracks the length of #context in #contexts_length' do
|
92
|
+
expect(packet.contexts_length).to eq packet.contexts.do_num_bytes
|
93
93
|
end
|
94
94
|
|
95
95
|
describe '#name' do
|
96
96
|
it 'encodes any input into UTF-16LE' do
|
97
97
|
packet.name = 'Hello'
|
98
|
-
expect(packet.name.to_binary_s).to eq "H\x00e\x00l\x00l\x00o\x00"
|
98
|
+
expect(packet.name.to_binary_s { |name| name.abs_offset = 0; name.write_now! }).to eq "H\x00e\x00l\x00l\x00o\x00"
|
99
99
|
end
|
100
100
|
end
|
101
101
|
end
|
@@ -54,11 +54,15 @@ RSpec.describe RubySMB::SMB2::Packet::CreateResponse do
|
|
54
54
|
expect(packet.file_id).to be_a RubySMB::Field::Smb2Fileid
|
55
55
|
end
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
|
57
|
+
describe '#contexts' do
|
58
|
+
# with no context, the offset and length should be 0
|
59
|
+
# see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/d166aa9e-0b53-410e-b35e-3933d8131927
|
60
|
+
it 'should have the contexts_offset set to 0' do
|
61
|
+
expect(packet.contexts_offset).to eq 0
|
62
|
+
end
|
60
63
|
|
61
|
-
|
62
|
-
|
64
|
+
it 'should have the contexts_length set to ' do
|
65
|
+
expect(packet.contexts_length).to eq 0
|
66
|
+
end
|
63
67
|
end
|
64
68
|
end
|
@@ -30,8 +30,9 @@ RSpec.describe RubySMB::SMB2::Packet::QueryDirectoryResponse do
|
|
30
30
|
expect(packet.structure_size).to eq 9
|
31
31
|
end
|
32
32
|
|
33
|
-
it 'has an offset pointer
|
34
|
-
expect(packet.
|
33
|
+
it 'has an offset pointer of 0 for an empty buffer field' do
|
34
|
+
expect(packet.buffer).to be_empty
|
35
|
+
expect(packet.buffer_offset).to eq 0
|
35
36
|
end
|
36
37
|
|
37
38
|
it 'has a length value for the buffer field' do
|
data.tar.gz.sig
CHANGED
Binary file
|