ruby_smb 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/examples/tree_connect.rb +27 -0
  5. data/lib/ruby_smb/client.rb +25 -2
  6. data/lib/ruby_smb/client/authentication.rb +5 -6
  7. data/lib/ruby_smb/client/negotiation.rb +2 -3
  8. data/lib/ruby_smb/client/signing.rb +6 -2
  9. data/lib/ruby_smb/client/tree_connect.rb +86 -0
  10. data/lib/ruby_smb/generic_packet.rb +1 -1
  11. data/lib/ruby_smb/smb1.rb +1 -1
  12. data/lib/ruby_smb/smb1/bit_field.rb +4 -0
  13. data/lib/ruby_smb/smb1/bit_field/directory_access_mask.rb +38 -0
  14. data/lib/ruby_smb/smb1/bit_field/file_access_mask.rb +38 -0
  15. data/lib/ruby_smb/smb1/bit_field/optional_support.rb +19 -0
  16. data/lib/ruby_smb/smb1/bit_field/tree_connect_flags.rb +18 -0
  17. data/lib/ruby_smb/smb1/commands.rb +2 -0
  18. data/lib/ruby_smb/smb1/packet.rb +4 -0
  19. data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +34 -0
  20. data/lib/ruby_smb/smb1/packet/tree_connect_response.rb +74 -0
  21. data/lib/ruby_smb/smb1/packet/tree_disconnect_request.rb +29 -0
  22. data/lib/ruby_smb/smb1/packet/tree_disconnect_response.rb +30 -0
  23. data/lib/ruby_smb/smb1/tree.rb +55 -0
  24. data/lib/ruby_smb/smb2.rb +1 -0
  25. data/lib/ruby_smb/smb2/bit_field.rb +4 -0
  26. data/lib/ruby_smb/smb2/bit_field/directory_access_mask.rb +38 -0
  27. data/lib/ruby_smb/smb2/bit_field/file_access_mask.rb +38 -0
  28. data/lib/ruby_smb/smb2/bit_field/share_capabailities.rb +21 -0
  29. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +75 -0
  30. data/lib/ruby_smb/smb2/packet.rb +5 -0
  31. data/lib/ruby_smb/smb2/packet/error_packet.rb +15 -0
  32. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +27 -0
  33. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +50 -0
  34. data/lib/ruby_smb/smb2/packet/tree_disconnect_request.rb +21 -0
  35. data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +22 -0
  36. data/lib/ruby_smb/smb2/smb2_header.rb +3 -3
  37. data/lib/ruby_smb/smb2/tree.rb +51 -0
  38. data/lib/ruby_smb/version.rb +1 -1
  39. data/ruby_smb.gemspec +1 -1
  40. data/spec/lib/ruby_smb/client_spec.rb +114 -4
  41. data/spec/lib/ruby_smb/smb1/bit_field/directory_access_mask_spec.rb +190 -0
  42. data/spec/lib/ruby_smb/smb1/bit_field/file_access_mask_spec.rb +190 -0
  43. data/spec/lib/ruby_smb/smb1/bit_field/optional_support_spec.rb +52 -0
  44. data/spec/lib/ruby_smb/smb1/bit_field/tree_connect_flags_spec.rb +37 -0
  45. data/spec/lib/ruby_smb/smb1/packet/tree_connect_request_spec.rb +53 -0
  46. data/spec/lib/ruby_smb/smb1/packet/tree_connect_response_spec.rb +86 -0
  47. data/spec/lib/ruby_smb/smb1/packet/tree_disconnect_request_spec.rb +40 -0
  48. data/spec/lib/ruby_smb/smb1/packet/tree_disconnect_response_spec.rb +40 -0
  49. data/spec/lib/ruby_smb/smb1/tree_spec.rb +55 -0
  50. data/spec/lib/ruby_smb/smb2/bit_field/directory_access_mask_spec.rb +190 -0
  51. data/spec/lib/ruby_smb/smb2/bit_field/file_access_mask_spec.rb +190 -0
  52. data/spec/lib/ruby_smb/smb2/bit_field/share_capabilities_spec.rb +54 -0
  53. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +184 -0
  54. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +49 -0
  55. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +57 -0
  56. data/spec/lib/ruby_smb/smb2/packet/tree_disconnect_request_spec.rb +30 -0
  57. data/spec/lib/ruby_smb/smb2/packet/tree_disconnect_response_spec.rb +30 -0
  58. data/spec/lib/ruby_smb/smb2/tree_spec.rb +54 -0
  59. metadata +63 -6
  60. metadata.gz.sig +0 -0
@@ -0,0 +1,54 @@
1
+ RSpec.describe RubySMB::SMB2::BitField::ShareCapabilities do
2
+ subject(:capabilities) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :asymmetric }
5
+ it { is_expected.to respond_to :cluster }
6
+ it { is_expected.to respond_to :scaleout }
7
+ it { is_expected.to respond_to :continuous }
8
+ it { is_expected.to respond_to :dfs }
9
+
10
+
11
+ it 'is little endian' do
12
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
13
+ end
14
+
15
+ describe '#dfs' do
16
+ it 'is a 1-bit flag' do
17
+ expect(capabilities.dfs).to be_a BinData::Bit1
18
+ end
19
+
20
+ it_behaves_like 'bit field with one flag set', :dfs, 'V', 0x00000008
21
+ end
22
+
23
+ describe '#continuous' do
24
+ it 'is a 1-bit flag' do
25
+ expect(capabilities.continuous).to be_a BinData::Bit1
26
+ end
27
+
28
+ it_behaves_like 'bit field with one flag set', :continuous, 'V', 0x00000010
29
+ end
30
+
31
+ describe '#scaleout' do
32
+ it 'is a 1-bit flag' do
33
+ expect(capabilities.scaleout).to be_a BinData::Bit1
34
+ end
35
+
36
+ it_behaves_like 'bit field with one flag set', :scaleout, 'V', 0x00000020
37
+ end
38
+
39
+ describe '#cluster' do
40
+ it 'is a 1-bit flag' do
41
+ expect(capabilities.cluster).to be_a BinData::Bit1
42
+ end
43
+
44
+ it_behaves_like 'bit field with one flag set', :cluster, 'V', 0x00000040
45
+ end
46
+
47
+ describe '#asymmetric' do
48
+ it 'is a 1-bit flag' do
49
+ expect(capabilities.asymmetric).to be_a BinData::Bit1
50
+ end
51
+
52
+ it_behaves_like 'bit field with one flag set', :asymmetric, 'V', 0x00000080
53
+ end
54
+ end
@@ -0,0 +1,184 @@
1
+ RSpec.describe RubySMB::SMB2::BitField::ShareFlags do
2
+ subject(:flags) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :dfs_root }
5
+ it { is_expected.to respond_to :dfs }
6
+ it { is_expected.to respond_to :encrypt }
7
+ it { is_expected.to respond_to :hash_v2 }
8
+ it { is_expected.to respond_to :hash_v1 }
9
+ it { is_expected.to respond_to :force_oplock }
10
+ it { is_expected.to respond_to :access_based_enum }
11
+ it { is_expected.to respond_to :namespace_caching }
12
+ it { is_expected.to respond_to :shared_delete }
13
+ it { is_expected.to respond_to :restrict_exclusive_opens }
14
+
15
+ it 'is little endian' do
16
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
17
+ end
18
+
19
+ describe '#dfs' do
20
+ it 'should be a 1-bit field per the SMB spec' do
21
+ expect(flags.dfs).to be_a BinData::Bit1
22
+ end
23
+
24
+ it_behaves_like 'bit field with one flag set', :dfs, 'V', 0x00000001
25
+ end
26
+
27
+
28
+ describe '#dfs_root' do
29
+ it 'should be a 1-bit field per the SMB spec' do
30
+ expect(flags.dfs_root).to be_a BinData::Bit1
31
+ end
32
+
33
+ it_behaves_like 'bit field with one flag set', :dfs_root, 'V', 0x00000002
34
+ end
35
+
36
+ describe '#restrict_exclusive_opens' do
37
+ it 'should be a 1-bit field per the SMB spec' do
38
+ expect(flags.restrict_exclusive_opens).to be_a BinData::Bit1
39
+ end
40
+
41
+ it_behaves_like 'bit field with one flag set', :restrict_exclusive_opens, 'V', 0x00000100
42
+ end
43
+
44
+ describe '#shared_delete' do
45
+ it 'should be a 1-bit field per the SMB spec' do
46
+ expect(flags.shared_delete).to be_a BinData::Bit1
47
+ end
48
+
49
+ it_behaves_like 'bit field with one flag set', :shared_delete, 'V', 0x00000200
50
+ end
51
+
52
+ describe '#namespace_caching' do
53
+ it 'should be a 1-bit field per the SMB spec' do
54
+ expect(flags.namespace_caching).to be_a BinData::Bit1
55
+ end
56
+
57
+ it_behaves_like 'bit field with one flag set', :namespace_caching, 'V', 0x00000400
58
+ end
59
+
60
+ describe '#access_based_enum' do
61
+ it 'should be a 1-bit field per the SMB spec' do
62
+ expect(flags.access_based_enum).to be_a BinData::Bit1
63
+ end
64
+
65
+ it_behaves_like 'bit field with one flag set', :access_based_enum, 'V', 0x00000800
66
+ end
67
+
68
+ describe '#force_oplock' do
69
+ it 'should be a 1-bit field per the SMB spec' do
70
+ expect(flags.force_oplock).to be_a BinData::Bit1
71
+ end
72
+
73
+ it_behaves_like 'bit field with one flag set', :force_oplock, 'V', 0x00001000
74
+ end
75
+
76
+ describe '#hash_v1' do
77
+ it 'should be a 1-bit field per the SMB spec' do
78
+ expect(flags.hash_v1).to be_a BinData::Bit1
79
+ end
80
+
81
+ it_behaves_like 'bit field with one flag set', :hash_v1, 'V', 0x00002000
82
+ end
83
+
84
+ describe '#hash_v2' do
85
+ it 'should be a 1-bit field per the SMB spec' do
86
+ expect(flags.hash_v2).to be_a BinData::Bit1
87
+ end
88
+
89
+ it_behaves_like 'bit field with one flag set', :hash_v2, 'V', 0x00004000
90
+ end
91
+
92
+ describe '#encrypt' do
93
+ it 'should be a 1-bit field per the SMB spec' do
94
+ expect(flags.encrypt).to be_a BinData::Bit1
95
+ end
96
+
97
+ it_behaves_like 'bit field with one flag set', :encrypt, 'V', 0x00008000
98
+ end
99
+
100
+ describe '#set_manual_caching' do
101
+ it 'turns off the caching bits' do
102
+ flags.set_manual_caching
103
+ expect(flags.vdo_caching).to eq 0
104
+ expect(flags.auto_caching).to eq 0
105
+ end
106
+
107
+ it 'has a value of 0x00000000' do
108
+ flags.set_manual_caching
109
+ flag_val = flags.to_binary_s
110
+ flag_val = flag_val.unpack('V').first
111
+ expect(flag_val).to eq 0x00000000
112
+ end
113
+ end
114
+
115
+ describe '#set_auto_caching' do
116
+ it 'turns on the auto_caching bit' do
117
+ flags.set_auto_caching
118
+ expect(flags.vdo_caching).to eq 0
119
+ expect(flags.auto_caching).to eq 1
120
+ end
121
+
122
+ it 'has a value of 0x00000010' do
123
+ flags.set_auto_caching
124
+ flag_val = flags.to_binary_s
125
+ flag_val = flag_val.unpack('V').first
126
+ expect(flag_val).to eq 0x00000010
127
+ end
128
+ end
129
+
130
+ describe '#set_vdo_caching' do
131
+ it 'turns on the vdo_caching bits' do
132
+ flags.set_vdo_caching
133
+ expect(flags.vdo_caching).to eq 1
134
+ expect(flags.auto_caching).to eq 0
135
+ end
136
+
137
+ it 'has a value of 0x00000020' do
138
+ flags.set_vdo_caching
139
+ flag_val = flags.to_binary_s
140
+ flag_val = flag_val.unpack('V').first
141
+ expect(flag_val).to eq 0x00000020
142
+ end
143
+ end
144
+
145
+ describe '#set_no_caching' do
146
+ it 'turns on all the caching bits' do
147
+ flags.set_no_caching
148
+ expect(flags.vdo_caching).to eq 1
149
+ expect(flags.auto_caching).to eq 1
150
+ end
151
+
152
+ it 'has a value of 0x00000030' do
153
+ flags.set_no_caching
154
+ flag_val = flags.to_binary_s
155
+ flag_val = flag_val.unpack('V').first
156
+ expect(flag_val).to eq 0x00000030
157
+ end
158
+ end
159
+
160
+ describe '#caching_type' do
161
+ it 'returns VDO if only that flag is set' do
162
+ flags.vdo_caching = 1
163
+ expect(flags.caching_type).to eq :vdo
164
+ end
165
+
166
+ it 'returns Auto if only that flag is set' do
167
+ flags.auto_caching = 1
168
+ expect(flags.caching_type).to eq :auto
169
+ end
170
+
171
+ it 'returns No Caching if both caching flags are set' do
172
+ flags.auto_caching = 1
173
+ flags.vdo_caching = 1
174
+ expect(flags.caching_type).to eq :no_caching
175
+ end
176
+
177
+ it 'returns Manual if neither caching flags are set' do
178
+ flags.auto_caching = 0
179
+ flags.vdo_caching = 0
180
+ expect(flags.caching_type).to eq :manual
181
+ end
182
+
183
+ end
184
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RubySMB::SMB2::Packet::TreeConnectRequest do
4
+
5
+ subject(:packet) { described_class.new }
6
+
7
+ it { is_expected.to respond_to :smb2_header }
8
+ it { is_expected.to respond_to :structure_size }
9
+ it { is_expected.to respond_to :flags }
10
+ it { is_expected.to respond_to :path_offset }
11
+ it { is_expected.to respond_to :path_length }
12
+ it { is_expected.to respond_to :path }
13
+
14
+
15
+ it 'is little endian' do
16
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
17
+ end
18
+
19
+ describe '#smb2_header' do
20
+ subject(:header) { packet.smb2_header }
21
+
22
+ it 'is a standard SMB Header' do
23
+ expect(header).to be_a RubySMB::SMB2::SMB2Header
24
+ end
25
+
26
+ it 'should have the command set to SMB_COM_NEGOTIATE' do
27
+ expect(header.command).to eq RubySMB::SMB2::Commands::TREE_CONNECT
28
+ end
29
+
30
+ it 'should not have the response flag set' do
31
+ expect(header.flags.reply).to eq 0
32
+ end
33
+ end
34
+
35
+ describe '#encode_path' do
36
+ let(:path) { "\\192.168.1.1\\example" }
37
+ let(:encoded_path) { path.encode("utf-16le").force_encoding("binary") }
38
+
39
+ it 'sets the path string to a UTF-16LE version of the supplied string' do
40
+ packet.encode_path(path)
41
+ expect(packet.path).to eq encoded_path
42
+ end
43
+
44
+ it 'updates the #path_length' do
45
+ packet.encode_path(path)
46
+ expect(packet.path_length).to eq encoded_path.length
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RubySMB::SMB2::Packet::TreeConnectResponse do
4
+
5
+ subject(:packet) { described_class.new }
6
+
7
+ it { is_expected.to respond_to :smb2_header }
8
+ it { is_expected.to respond_to :structure_size }
9
+ it { is_expected.to respond_to :share_type }
10
+ it { is_expected.to respond_to :share_flags }
11
+ it { is_expected.to respond_to :capabilities }
12
+ it { is_expected.to respond_to :maximal_access }
13
+
14
+ it 'is little endian' do
15
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
16
+ end
17
+
18
+ describe '#smb2_header' do
19
+ subject(:header) { packet.smb2_header }
20
+
21
+ it 'is a standard SMB Header' do
22
+ expect(header).to be_a RubySMB::SMB2::SMB2Header
23
+ end
24
+
25
+ it 'should have the command set to SMB_COM_NEGOTIATE' do
26
+ expect(header.command).to eq RubySMB::SMB2::Commands::TREE_CONNECT
27
+ end
28
+
29
+ it 'should have the response flag set' do
30
+ expect(header.flags.reply).to eq 1
31
+ end
32
+ end
33
+
34
+ describe '#is_directory?' do
35
+ it 'returns true if #share_type is 0x01' do
36
+ packet.share_type = 0x01
37
+ expect(packet.is_directory?).to be true
38
+ end
39
+
40
+ it 'returns false if #share_type is not 0x01' do
41
+ packet.share_type = 0x02
42
+ expect(packet.is_directory?).to be false
43
+ end
44
+ end
45
+
46
+ describe '#access_rights' do
47
+ it 'is a DirectoryAccessMask if the Tree is a directory' do
48
+ allow(packet).to receive(:is_directory?).and_return(true)
49
+ expect(packet.access_rights).to be_a RubySMB::SMB2::BitField::DirectoryAccessMask
50
+ end
51
+
52
+ it 'is a FileAccessMask if the Tree is not a directory' do
53
+ allow(packet).to receive(:is_directory?).and_return(false)
54
+ expect(packet.access_rights).to be_a RubySMB::SMB2::BitField::FileAccessMask
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RubySMB::SMB2::Packet::TreeDisconnectRequest do
4
+
5
+ subject(:packet) { described_class.new }
6
+
7
+ it { is_expected.to respond_to :smb2_header }
8
+ it { is_expected.to respond_to :structure_size }
9
+
10
+ it 'is little endian' do
11
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
12
+ end
13
+
14
+ describe '#smb2_header' do
15
+ subject(:header) { packet.smb2_header }
16
+
17
+ it 'is a standard SMB Header' do
18
+ expect(header).to be_a RubySMB::SMB2::SMB2Header
19
+ end
20
+
21
+ it 'should have the command set to SMB_COM_NEGOTIATE' do
22
+ expect(header.command).to eq RubySMB::SMB2::Commands::TREE_DISCONNECT
23
+ end
24
+
25
+ it 'should not have the response flag set' do
26
+ expect(header.flags.reply).to eq 0
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RubySMB::SMB2::Packet::TreeDisconnectResponse do
4
+
5
+ subject(:packet) { described_class.new }
6
+
7
+ it { is_expected.to respond_to :smb2_header }
8
+ it { is_expected.to respond_to :structure_size }
9
+
10
+ it 'is little endian' do
11
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
12
+ end
13
+
14
+ describe '#smb2_header' do
15
+ subject(:header) { packet.smb2_header }
16
+
17
+ it 'is a standard SMB Header' do
18
+ expect(header).to be_a RubySMB::SMB2::SMB2Header
19
+ end
20
+
21
+ it 'should have the command set to SMB_COM_NEGOTIATE' do
22
+ expect(header.command).to eq RubySMB::SMB2::Commands::TREE_DISCONNECT
23
+ end
24
+
25
+ it 'should have the response flag set' do
26
+ expect(header.flags.reply).to eq 1
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RubySMB::SMB2::Tree do
4
+ let(:dispatcher) { RubySMB::Dispatcher::Socket.new(nil) }
5
+
6
+ let(:client) { RubySMB::Client.new(dispatcher, username: 'msfadmin', password: 'msfadmin') }
7
+ let(:tree_id) { 2049 }
8
+ let(:path) { '\\192.168.1.1\example' }
9
+ let(:response) {
10
+ packet = RubySMB::SMB2::Packet::TreeConnectResponse.new
11
+ packet.smb2_header.tree_id = tree_id
12
+ packet.maximal_access.read("\xff\x01\x1f\x00")
13
+ packet.share_type = 0x01
14
+ packet
15
+ }
16
+ subject(:tree) {
17
+ described_class.new(client:client, share:path, response:response )
18
+ }
19
+
20
+ it { is_expected.to respond_to :client }
21
+ it { is_expected.to respond_to :permissions }
22
+ it { is_expected.to respond_to :share }
23
+ it { is_expected.to respond_to :id }
24
+
25
+ it 'inherits the client that spawned it' do
26
+ expect(tree.client).to eq client
27
+ end
28
+
29
+ it 'inherits the permissions from the response packet' do
30
+ expect(tree.permissions).to eq response.maximal_access
31
+ end
32
+
33
+ it 'inherits the Tree id from the response packet' do
34
+ expect(tree.id).to eq response.smb2_header.tree_id
35
+ end
36
+
37
+ describe '#disconnect!' do
38
+ let(:disco_req) { RubySMB::SMB2::Packet::TreeDisconnectRequest.new }
39
+ let(:disco_resp) { RubySMB::SMB2::Packet::TreeDisconnectResponse.new }
40
+
41
+ it 'sends a TreeDisconnectRequest with the Tree ID in the header' do
42
+ allow(RubySMB::SMB2::Packet::TreeDisconnectRequest).to receive(:new).and_return(disco_req)
43
+ modified_req = disco_req
44
+ modified_req.smb2_header.tree_id = tree.id
45
+ expect(client).to receive(:send_recv).with(modified_req).and_return(disco_resp.to_binary_s)
46
+ tree.disconnect!
47
+ end
48
+
49
+ it 'returns the NTStatus code from the response' do
50
+ allow(client).to receive(:send_recv).and_return(disco_resp.to_binary_s)
51
+ expect(tree.disconnect!).to eq disco_resp.status_code
52
+ end
53
+ end
54
+ end