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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/examples/auth_capture.rb +28 -0
  4. data/examples/file_server.rb +76 -0
  5. data/lib/ruby_smb/create_actions.rb +21 -0
  6. data/lib/ruby_smb/field/nt_status.rb +20 -1
  7. data/lib/ruby_smb/fscc/file_information/file_ea_information.rb +14 -0
  8. data/lib/ruby_smb/fscc/file_information/file_network_open_information.rb +22 -0
  9. data/lib/ruby_smb/fscc/file_information/file_stream_information.rb +16 -0
  10. data/lib/ruby_smb/fscc/file_information.rb +29 -0
  11. data/lib/ruby_smb/fscc/file_system_information/file_fs_attribute_information.rb +46 -0
  12. data/lib/ruby_smb/fscc/file_system_information/file_fs_volume_information.rb +19 -0
  13. data/lib/ruby_smb/fscc/file_system_information.rb +22 -0
  14. data/lib/ruby_smb/fscc.rb +1 -0
  15. data/lib/ruby_smb/generic_packet.rb +6 -0
  16. data/lib/ruby_smb/gss/provider/authenticator.rb +4 -0
  17. data/lib/ruby_smb/gss/provider/ntlm.rb +13 -3
  18. data/lib/ruby_smb/server/server_client/negotiation.rb +0 -2
  19. data/lib/ruby_smb/server/server_client/session_setup.rb +43 -32
  20. data/lib/ruby_smb/server/server_client/share_io.rb +28 -0
  21. data/lib/ruby_smb/server/server_client/tree_connect.rb +60 -0
  22. data/lib/ruby_smb/server/server_client.rb +214 -24
  23. data/lib/ruby_smb/server/session.rb +71 -0
  24. data/lib/ruby_smb/server/share/provider/disk.rb +437 -0
  25. data/lib/ruby_smb/server/share/provider/pipe.rb +27 -0
  26. data/lib/ruby_smb/server/share/provider/processor.rb +76 -0
  27. data/lib/ruby_smb/server/share/provider.rb +38 -0
  28. data/lib/ruby_smb/server/share.rb +11 -0
  29. data/lib/ruby_smb/server.rb +35 -3
  30. data/lib/ruby_smb/signing.rb +37 -11
  31. data/lib/ruby_smb/smb1/commands.rb +4 -0
  32. data/lib/ruby_smb/smb1.rb +0 -1
  33. data/lib/ruby_smb/smb2/bit_field/smb2_header_flags.rb +2 -1
  34. data/lib/ruby_smb/smb2/commands.rb +4 -0
  35. data/lib/ruby_smb/smb2/create_context/request.rb +64 -0
  36. data/lib/ruby_smb/smb2/create_context/response.rb +62 -0
  37. data/lib/ruby_smb/smb2/create_context.rb +74 -22
  38. data/lib/ruby_smb/smb2/packet/create_request.rb +44 -11
  39. data/lib/ruby_smb/smb2/packet/create_response.rb +17 -3
  40. data/lib/ruby_smb/smb2/packet/query_directory_request.rb +1 -1
  41. data/lib/ruby_smb/smb2/packet/query_directory_response.rb +2 -2
  42. data/lib/ruby_smb/smb2/packet/query_info_request.rb +43 -0
  43. data/lib/ruby_smb/smb2/packet/query_info_response.rb +23 -0
  44. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +1 -1
  45. data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +1 -0
  46. data/lib/ruby_smb/smb2/packet.rb +2 -0
  47. data/lib/ruby_smb/smb2.rb +11 -0
  48. data/lib/ruby_smb/smb_error.rb +110 -0
  49. data/lib/ruby_smb/version.rb +1 -1
  50. data/lib/ruby_smb.rb +2 -0
  51. data/ruby_smb.gemspec +1 -1
  52. data/spec/lib/ruby_smb/field/nt_status_spec.rb +6 -2
  53. data/spec/lib/ruby_smb/gss/provider/ntlm/authenticator_spec.rb +4 -0
  54. data/spec/lib/ruby_smb/server/server_client_spec.rb +36 -53
  55. data/spec/lib/ruby_smb/server/session_spec.rb +38 -0
  56. data/spec/lib/ruby_smb/server/share/provider/disk_spec.rb +61 -0
  57. data/spec/lib/ruby_smb/server/share/provider/pipe_spec.rb +31 -0
  58. data/spec/lib/ruby_smb/server/share/provider_spec.rb +13 -0
  59. data/spec/lib/ruby_smb/smb2/bit_field/header_flags_spec.rb +8 -2
  60. data/spec/lib/ruby_smb/smb2/{create_context_spec.rb → create_context/create_context_request_spec.rb} +1 -1
  61. data/spec/lib/ruby_smb/smb2/packet/create_request_spec.rb +5 -5
  62. data/spec/lib/ruby_smb/smb2/packet/create_response_spec.rb +9 -5
  63. data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +3 -2
  64. data.tar.gz.sig +0 -0
  65. metadata +35 -7
  66. metadata.gz.sig +0 -0
  67. 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
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '3.0.0'.freeze
2
+ VERSION = '3.0.1'.freeze
3
3
  end
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 'is a Unsigned 32-bit little endian integer' do
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 :identity }
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 an identity' do
30
- expect(server_client.identity).to be_nil
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 state is negotiate' do
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 { @state = :negotiate }
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 #authenticated when the state is authenticated' do
85
- expect(server_client).to receive(:handle_authenticated).with(packet).and_return(nil)
86
- server_client.instance_eval { @state = :authenticated }
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(:packet) { RubySMB::GenericPacket.new }
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 state is authenticated' do
96
+ context 'and the identity is anonymous' do
109
97
  before(:each) do
110
- server_client.instance_eval { @state = :authenticated }
98
+ server_client.session_table[session_id].user_id = RubySMB::Gss::Provider::IDENTITY_ANONYMOUS
111
99
  end
112
100
 
113
- context 'and the identity is anonymous' do
114
- before(:each) do
115
- server_client.instance_eval { @identity = RubySMB::Gss::Provider::IDENTITY_ANONYMOUS }
116
- end
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
- context 'and the identity is not anonymous' do
126
- before(:each) do
127
- server_client.instance_eval { @identity = 'WORKGROUP\RubySMB'; @session_key = Random.new.bytes(16) }
128
- end
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
- it 'does sign packets' do
131
- packet = RubySMB::GenericPacket.new
132
- dialect_family = RubySMB::Dialect[dialect].family
133
- if dialect_family == RubySMB::Dialect::FAMILY_SMB2
134
- expect(server_client).to receive(:smb2_sign).with(packet).and_return(packet)
135
- expect(server_client).to_not receive(:smb3_sign)
136
- elsif dialect_family == RubySMB::Dialect::FAMILY_SMB3
137
- expect(server_client).to receive(:smb3_sign).with(packet).and_return(packet)
138
- expect(server_client).to_not receive(:smb2_sign)
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 4-bit field per the SMB spec' do
48
- expect(flags.reserved3).to be_a BinData::Bit4
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
 
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe RubySMB::SMB2::CreateContext do
3
+ RSpec.describe RubySMB::SMB2::CreateContext::CreateContextRequest do
4
4
  subject(:struct) { described_class.new }
5
5
 
6
6
  it { is_expected.to respond_to :next_offset }
@@ -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 #context_offset' do
84
- expect(packet.context_offset).to eq packet.context.abs_offset
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 #context_length' do
92
- expect(packet.context_length).to eq packet.context.do_num_bytes
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
- it 'tracks the offset to #context in #context_offset' do
58
- expect(packet.context_offset).to eq packet.context.abs_offset
59
- end
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
- it 'tracks the length of #context in #context_length' do
62
- expect(packet.context_length).to eq packet.context.do_num_bytes
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 to the buffer field' do
34
- expect(packet.buffer_offset).to eq packet.buffer.abs_offset
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