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,50 @@
1
+ module RubySMB
2
+ module SMB2
3
+ module Packet
4
+
5
+ # An SMB2 TreeConnectResponse Packet as defined in
6
+ # [2.2.10 SMB2 TREE_CONNECT Response](https://msdn.microsoft.com/en-us/library/cc246499.aspx)
7
+ class TreeConnectResponse < RubySMB::GenericPacket
8
+ endian :little
9
+ smb2_header :smb2_header
10
+ uint16 :structure_size, label: 'Structure Size', initial_value: 16
11
+ uint8 :share_type, label: 'Share Type', initial_value: 0x01
12
+ uint8 :reserved, label: 'Reserved Space', initial_value: 0x00
13
+ share_flags :share_flags
14
+ share_capabilities :capabilities
15
+ directory_access_mask :maximal_access, label: 'Maximal Access'
16
+
17
+ def initialize_instance
18
+ super
19
+ smb2_header.command = RubySMB::SMB2::Commands::TREE_CONNECT
20
+ smb2_header.flags.reply = 1
21
+ end
22
+
23
+ # Returns the ACCESS_MASK for the Maximal Share Access Rights. The packet
24
+ # defaults this to a {RubySMB::SMB2::BitField::DirectoryAccessMask}. If it is anything other than
25
+ # a directory that has been connected to, it will re-cast it as a {RubySMB::SMB2::BitField::FileAccessMask}
26
+ #
27
+ # @return [RubySMB::SMB2::BitField::DirectoryAccessMask] if a directory was connected to
28
+ # @return [RubySMB::SMB2::BitField::FileAccessMask] if anything else was connected to
29
+ def access_rights
30
+ if is_directory?
31
+ self.maximal_access
32
+ else
33
+ mask = self.maximal_access.to_binary_s
34
+ RubySMB::SMB2::BitField::FileAccessMask.read(mask)
35
+ end
36
+ end
37
+
38
+ # Checks if the remote Tree is a directory
39
+ #
40
+ # @return [Boolean]
41
+ def is_directory?
42
+ self.share_type == 0x01
43
+ end
44
+
45
+
46
+
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,21 @@
1
+ module RubySMB
2
+ module SMB2
3
+ module Packet
4
+
5
+ # An SMB2 TreeDisconnectRequest Packet as defined in
6
+ # [2.2.11 SMB2 TREE_DISCONNECT Request](https://msdn.microsoft.com/en-us/library/cc246500.aspx)
7
+ class TreeDisconnectRequest < RubySMB::GenericPacket
8
+ endian :little
9
+ smb2_header :smb2_header
10
+ uint16 :structure_size, label: 'Structure Size', initial_value: 4
11
+
12
+
13
+ def initialize_instance
14
+ super
15
+ smb2_header.command = RubySMB::SMB2::Commands::TREE_DISCONNECT
16
+ end
17
+
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ module RubySMB
2
+ module SMB2
3
+ module Packet
4
+
5
+ # An SMB2 TreeDisconnectResponse Packet as defined in
6
+ # [2.2.12 SMB2 TREE_DISCONNECT Response](https://msdn.microsoft.com/en-us/library/cc246501.aspx)
7
+ class TreeDisconnectResponse < RubySMB::GenericPacket
8
+ endian :little
9
+ smb2_header :smb2_header
10
+ uint16 :structure_size, label: 'Structure Size', initial_value: 4
11
+
12
+
13
+ def initialize_instance
14
+ super
15
+ smb2_header.command = RubySMB::SMB2::Commands::TREE_DISCONNECT
16
+ smb2_header.flags.reply = 1
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+ end
@@ -11,9 +11,9 @@ module RubySMB
11
11
  uint16 :command, label: 'Command'
12
12
  uint16 :credits, label: 'Credit Request/Response'
13
13
  smb2_header_flags :flags, label: 'Flags'
14
- uint32 :next_command, label: 'Command Chain Offset', initial_value: 0
15
- uint64 :message_id, label: 'Message ID'
16
- uint32 :process_id, label: 'Process ID', initial_value: 0x0000feff
14
+ uint32 :next_command, label: 'Command Chain Offset', initial_value: 0
15
+ uint64 :message_id, label: 'Message ID', initial_value: 0
16
+ uint32 :process_id, label: 'Process ID', initial_value: 0x0000feff
17
17
  uint32 :tree_id, label: 'Tree ID'
18
18
  uint64 :session_id, label: 'Session ID'
19
19
  string :signature, label: 'Signature', length: 16
@@ -0,0 +1,51 @@
1
+ module RubySMB
2
+ module SMB2
3
+
4
+ # An SMB2 connected remote Tree, as returned by a
5
+ # [RubySMB::SMB2::Packet::TreeConnectRequest]
6
+ class Tree
7
+
8
+ # The client this Tree is connected through
9
+ # @!attribute [rw] client
10
+ # @return [RubySMB::Client]
11
+ attr_accessor :client
12
+
13
+ # The current Maximal Share Permissions
14
+ # @!attribute [rw] permissions
15
+ # @return [RubySMB::SMB2::BitField::DirectoryAccessMask]
16
+ attr_accessor :permissions
17
+
18
+ # The share path associated with this Tree
19
+ # @!attribute [rw] share
20
+ # @return [String]
21
+ attr_accessor :share
22
+
23
+ # The Tree ID for this Tree
24
+ # @!attribute [rw] id
25
+ # @return [Integer]
26
+ attr_accessor :id
27
+
28
+ def initialize(client:, share:, response:)
29
+ @client = client
30
+ @share = share
31
+ @id = response.smb2_header.tree_id
32
+ @permissions = response.maximal_access
33
+ end
34
+
35
+ # Disconnects this Tree from the current session
36
+ #
37
+ # @return [WindowsError::ErrorCode] the NTStatus sent back by the server.
38
+ def disconnect!
39
+ request = RubySMB::SMB2::Packet::TreeDisconnectRequest.new
40
+ request.smb2_header.tree_id = self.id
41
+ request.smb2_header.credit_charge = 1
42
+ request.smb2_header.credits = 256
43
+ raw_response = self.client.send_recv(request)
44
+ response = RubySMB::SMB2::Packet::TreeDisconnectResponse.read(raw_response)
45
+ response.status_code
46
+ end
47
+
48
+
49
+ end
50
+ end
51
+ end
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '0.0.8'.freeze
2
+ VERSION = '0.0.9'
3
3
  end
data/ruby_smb.gemspec CHANGED
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
31
31
  spec.add_development_dependency 'rake'
32
32
  spec.add_development_dependency 'yard'
33
33
 
34
- spec.add_runtime_dependency 'rubyntlm', '~> 0.5'
34
+ spec.add_runtime_dependency 'rubyntlm'
35
35
  spec.add_runtime_dependency 'bit-struct'
36
36
  spec.add_runtime_dependency 'windows_error'
37
37
  spec.add_runtime_dependency 'bindata'
@@ -49,8 +49,8 @@ RSpec.describe RubySMB::Client do
49
49
  end
50
50
 
51
51
  describe '#send_recv' do
52
- let(:smb1_request) { RubySMB::SMB1::Packet::SessionSetupRequest.new }
53
- let(:smb2_request) { RubySMB::SMB2::Packet::SessionSetupRequest.new }
52
+ let(:smb1_request) { RubySMB::SMB1::Packet::TreeConnectRequest.new }
53
+ let(:smb2_request) { RubySMB::SMB2::Packet::TreeConnectRequest.new }
54
54
 
55
55
  before(:each) do
56
56
  expect(dispatcher).to receive(:send_packet).and_return(nil)
@@ -570,7 +570,12 @@ RSpec.describe RubySMB::Client do
570
570
 
571
571
  context 'Signing' do
572
572
  describe '#smb2_sign' do
573
- let(:request1) { RubySMB::SMB2::Packet::SessionSetupRequest.new }
573
+ let(:request1) {
574
+ packet = RubySMB::SMB2::Packet::SessionSetupRequest.new
575
+ packet.smb2_header.flags.signed = 1
576
+ packet.smb2_header.signature = "\x00" * 16
577
+ packet
578
+ }
574
579
  let(:fake_hmac) { "\x31\x07\x78\x3e\x35\xd7\x0e\x89\x08\x43\x8a\x18\xcd\x78\x52\x39".force_encoding("ASCII-8BIT") }
575
580
 
576
581
  context 'if signing is required and we have a session key' do
@@ -610,7 +615,7 @@ RSpec.describe RubySMB::Client do
610
615
  smb1_client.signing_required = true
611
616
  raw = request1.to_binary_s
612
617
  adjusted_request = RubySMB::SMB1::Packet::SessionSetupRequest.read(raw)
613
- adjusted_request.smb_header.security_features = smb1_client.sequence_counter
618
+ adjusted_request.smb_header.security_features = [smb1_client.sequence_counter].pack('Q<')
614
619
  expect(OpenSSL::Digest::MD5).to receive(:digest).with(smb1_client.session_key + adjusted_request.to_binary_s).and_return(fake_sig)
615
620
  expect(smb1_client.smb1_sign(request1).smb_header.security_features).to eq fake_sig
616
621
  end
@@ -634,5 +639,110 @@ RSpec.describe RubySMB::Client do
634
639
  end
635
640
  end
636
641
 
642
+ context '#increment_smb_message_id' do
643
+ let(:request_packet) { RubySMB::SMB2::Packet::NegotiateRequest.new }
637
644
 
645
+ it 'sets the message_id on the packet header to the client message_id' do
646
+ id = client.smb2_message_id
647
+ expect(client.increment_smb_message_id(request_packet).smb2_header.message_id).to eq id
648
+ end
649
+
650
+ it 'increments the client message id' do
651
+ client.smb2_message_id = 1
652
+ expect{ client.increment_smb_message_id(request_packet) }.to change{ client.smb2_message_id}.by(1)
653
+ end
654
+ end
655
+
656
+ context 'connecting to a share' do
657
+ let(:path) { '\\192.168.1.1\example' }
658
+ let(:tree_id) { 2049 }
659
+ context 'with SMB1' do
660
+ let(:request) { RubySMB::SMB1::Packet::TreeConnectRequest.new }
661
+ let(:response) {
662
+ packet = RubySMB::SMB1::Packet::TreeConnectResponse.new
663
+ packet.smb_header.tid = tree_id
664
+ packet.parameter_block.access_rights.read("\xff\x01\x1f\x00")
665
+ packet.data_block.service = 'A:'
666
+ packet
667
+ }
668
+
669
+ describe '#smb1_tree_connect' do
670
+ it 'builds and sends a TreeconnectRequest for the supplied share' do
671
+ allow(RubySMB::SMB1::Packet::TreeConnectRequest).to receive(:new).and_return(request)
672
+ modified_request = request
673
+ modified_request.data_block.path = path
674
+ expect(smb1_client).to receive(:send_recv).with(modified_request).and_return(response.to_binary_s)
675
+ smb1_client.smb1_tree_connect(path)
676
+ end
677
+
678
+ it 'sends the response to #smb1_tree_from_response' do
679
+ expect(smb1_client).to receive(:send_recv).and_return(response.to_binary_s)
680
+ expect(smb1_client).to receive(:smb1_tree_from_response).with(path, response)
681
+ smb1_client.smb1_tree_connect(path)
682
+ end
683
+ end
684
+
685
+ describe '#smb1_tree_from_response' do
686
+ it 'raises an InvalidPacket exception if the command is not TREE_CONNECT' do
687
+ response.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_NEGOTIATE
688
+ expect{ smb1_client.smb1_tree_from_response(path,response) }.to raise_error(RubySMB::Error::InvalidPacket)
689
+ end
690
+
691
+ it 'raises an UnexpectedStatusCode exception if we do not get STATUS_SUCCESS' do
692
+ response.smb_header.nt_status = 0xc0000015
693
+ expect{ smb1_client.smb1_tree_from_response(path,response) }.to raise_error(RubySMB::Error::UnexpectedStatusCode, 'STATUS_NONEXISTENT_SECTOR')
694
+ end
695
+
696
+ it 'creates a new Tree from itself, the share path, and the response packet' do
697
+ expect(RubySMB::SMB1::Tree).to receive(:new).with(client:smb1_client, share:path, response:response)
698
+ smb1_client.smb1_tree_from_response(path,response)
699
+ end
700
+ end
701
+ end
702
+
703
+ context 'with SMB2' do
704
+ let(:request) { RubySMB::SMB2::Packet::TreeConnectRequest.new }
705
+ let(:response) {
706
+ packet = RubySMB::SMB2::Packet::TreeConnectResponse.new
707
+ packet.smb2_header.tree_id = tree_id
708
+ packet.maximal_access.read("\xff\x01\x1f\x00")
709
+ packet.share_type = 0x01
710
+ packet
711
+ }
712
+
713
+ describe '#smb2_tree_connect' do
714
+ it 'builds and sends a TreeconnectRequest for the supplied share' do
715
+ allow(RubySMB::SMB2::Packet::TreeConnectRequest).to receive(:new).and_return(request)
716
+ modified_request = request
717
+ modified_request.encode_path(path)
718
+ expect(smb2_client).to receive(:send_recv).with(modified_request).and_return(response.to_binary_s)
719
+ smb2_client.smb2_tree_connect(path)
720
+ end
721
+
722
+ it 'sends the response to #smb2_tree_from_response' do
723
+ expect(smb2_client).to receive(:send_recv).and_return(response.to_binary_s)
724
+ expect(smb2_client).to receive(:smb2_tree_from_response).with(path, response)
725
+ smb2_client.smb2_tree_connect(path)
726
+ end
727
+ end
728
+
729
+ describe '#smb2_tree_from_response' do
730
+ it 'raises an InvalidPacket exception if the command is not TREE_CONNECT' do
731
+ response.smb2_header.command = RubySMB::SMB2::Commands::NEGOTIATE
732
+ expect{ smb2_client.smb2_tree_from_response(path,response) }.to raise_error(RubySMB::Error::InvalidPacket)
733
+ end
734
+
735
+ it 'raises an UnexpectedStatusCode exception if we do not get STATUS_SUCCESS' do
736
+ response.smb2_header.nt_status = 0xc0000015
737
+ expect{ smb2_client.smb2_tree_from_response(path,response) }.to raise_error(RubySMB::Error::UnexpectedStatusCode, 'STATUS_NONEXISTENT_SECTOR')
738
+ end
739
+
740
+ it 'creates a new Tree from itself, the share path, and the response packet' do
741
+ expect(RubySMB::SMB2::Tree).to receive(:new).with(client:smb2_client, share:path, response:response)
742
+ smb2_client.smb2_tree_from_response(path,response)
743
+ end
744
+ end
745
+
746
+ end
747
+ end
638
748
  end
@@ -0,0 +1,190 @@
1
+ RSpec.describe RubySMB::SMB1::BitField::DirectoryAccessMask do
2
+ subject(:flags) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :read_attr }
5
+ it { is_expected.to respond_to :delete_child }
6
+ it { is_expected.to respond_to :traverse }
7
+ it { is_expected.to respond_to :write_ea }
8
+ it { is_expected.to respond_to :read_ea }
9
+ it { is_expected.to respond_to :add_subdir }
10
+ it { is_expected.to respond_to :add_file }
11
+ it { is_expected.to respond_to :list }
12
+ it { is_expected.to respond_to :write_attr }
13
+ it { is_expected.to respond_to :synchronize }
14
+ it { is_expected.to respond_to :write_owner }
15
+ it { is_expected.to respond_to :write_dac }
16
+ it { is_expected.to respond_to :read_control }
17
+ it { is_expected.to respond_to :delete_access }
18
+ it { is_expected.to respond_to :generic_read }
19
+ it { is_expected.to respond_to :generic_write }
20
+ it { is_expected.to respond_to :generic_execute }
21
+ it { is_expected.to respond_to :generic_all }
22
+ it { is_expected.to respond_to :maximum }
23
+ it { is_expected.to respond_to :system_security }
24
+
25
+ it 'is little endian' do
26
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
27
+ end
28
+
29
+ describe '#list' do
30
+ it 'should be a 1-bit field per the SMB spec' do
31
+ expect(flags.list).to be_a BinData::Bit1
32
+ end
33
+
34
+ it_behaves_like 'bit field with one flag set', :list, 'V', 0x00000001
35
+ end
36
+
37
+
38
+ describe '#add_file' do
39
+ it 'should be a 1-bit field per the SMB spec' do
40
+ expect(flags.add_file).to be_a BinData::Bit1
41
+ end
42
+
43
+ it_behaves_like 'bit field with one flag set', :add_file, 'V', 0x00000002
44
+ end
45
+
46
+ describe '#add_subdir' do
47
+ it 'should be a 1-bit field per the SMB spec' do
48
+ expect(flags.add_subdir).to be_a BinData::Bit1
49
+ end
50
+
51
+ it_behaves_like 'bit field with one flag set', :add_subdir, 'V', 0x00000004
52
+ end
53
+
54
+ describe '#read_ea' do
55
+ it 'should be a 1-bit field per the SMB spec' do
56
+ expect(flags.read_ea).to be_a BinData::Bit1
57
+ end
58
+
59
+ it_behaves_like 'bit field with one flag set', :read_ea, 'V', 0x00000008
60
+ end
61
+
62
+ describe '#write_ea' do
63
+ it 'should be a 1-bit field per the SMB spec' do
64
+ expect(flags.write_ea).to be_a BinData::Bit1
65
+ end
66
+
67
+ it_behaves_like 'bit field with one flag set', :write_ea, 'V', 0x00000010
68
+ end
69
+
70
+ describe '#traverse' do
71
+ it 'should be a 1-bit field per the SMB spec' do
72
+ expect(flags.traverse).to be_a BinData::Bit1
73
+ end
74
+
75
+ it_behaves_like 'bit field with one flag set', :traverse, 'V', 0x00000020
76
+ end
77
+
78
+ describe '#delete_child' do
79
+ it 'should be a 1-bit field per the SMB spec' do
80
+ expect(flags.delete_child).to be_a BinData::Bit1
81
+ end
82
+
83
+ it_behaves_like 'bit field with one flag set', :delete_child, 'V', 0x00000040
84
+ end
85
+
86
+ describe '#read_attr' do
87
+ it 'should be a 1-bit field per the SMB spec' do
88
+ expect(flags.read_attr).to be_a BinData::Bit1
89
+ end
90
+
91
+ it_behaves_like 'bit field with one flag set', :read_attr, 'V', 0x00000080
92
+ end
93
+
94
+ describe '#write_attr' do
95
+ it 'should be a 1-bit field per the SMB spec' do
96
+ expect(flags.write_attr).to be_a BinData::Bit1
97
+ end
98
+
99
+ it_behaves_like 'bit field with one flag set', :write_attr, 'V', 0x00000100
100
+ end
101
+
102
+ describe '#delete_access' do
103
+ it 'should be a 1-bit field per the SMB spec' do
104
+ expect(flags.delete_access).to be_a BinData::Bit1
105
+ end
106
+
107
+ it_behaves_like 'bit field with one flag set', :delete_access, 'V', 0x00010000
108
+ end
109
+
110
+ describe '#read_control' do
111
+ it 'should be a 1-bit field per the SMB spec' do
112
+ expect(flags.read_control).to be_a BinData::Bit1
113
+ end
114
+
115
+ it_behaves_like 'bit field with one flag set', :read_control, 'V', 0x00020000
116
+ end
117
+
118
+ describe '#write_dac' do
119
+ it 'should be a 1-bit field per the SMB spec' do
120
+ expect(flags.write_dac).to be_a BinData::Bit1
121
+ end
122
+
123
+ it_behaves_like 'bit field with one flag set', :write_dac, 'V', 0x00040000
124
+ end
125
+
126
+ describe '#write_owner' do
127
+ it 'should be a 1-bit field per the SMB spec' do
128
+ expect(flags.write_owner).to be_a BinData::Bit1
129
+ end
130
+
131
+ it_behaves_like 'bit field with one flag set', :write_owner, 'V', 0x00080000
132
+ end
133
+
134
+ describe '#synchronize' do
135
+ it 'should be a 1-bit field per the SMB spec' do
136
+ expect(flags.synchronize).to be_a BinData::Bit1
137
+ end
138
+
139
+ it_behaves_like 'bit field with one flag set', :synchronize, 'V', 0x00100000
140
+ end
141
+
142
+ describe '#system_security' do
143
+ it 'should be a 1-bit field per the SMB spec' do
144
+ expect(flags.system_security).to be_a BinData::Bit1
145
+ end
146
+
147
+ it_behaves_like 'bit field with one flag set', :system_security, 'V', 0x01000000
148
+ end
149
+
150
+ describe '#maximum' do
151
+ it 'should be a 1-bit field per the SMB spec' do
152
+ expect(flags.maximum).to be_a BinData::Bit1
153
+ end
154
+
155
+ it_behaves_like 'bit field with one flag set', :maximum, 'V', 0x02000000
156
+ end
157
+
158
+ describe '#generic_all' do
159
+ it 'should be a 1-bit field per the SMB spec' do
160
+ expect(flags.generic_all).to be_a BinData::Bit1
161
+ end
162
+
163
+ it_behaves_like 'bit field with one flag set', :generic_all, 'V', 0x10000000
164
+ end
165
+
166
+ describe '#generic_execute' do
167
+ it 'should be a 1-bit field per the SMB spec' do
168
+ expect(flags.generic_execute).to be_a BinData::Bit1
169
+ end
170
+
171
+ it_behaves_like 'bit field with one flag set', :generic_execute, 'V', 0x20000000
172
+ end
173
+
174
+ describe '#generic_write' do
175
+ it 'should be a 1-bit field per the SMB spec' do
176
+ expect(flags.generic_write).to be_a BinData::Bit1
177
+ end
178
+
179
+ it_behaves_like 'bit field with one flag set', :generic_write, 'V', 0x40000000
180
+ end
181
+
182
+ describe '#generic_read' do
183
+ it 'should be a 1-bit field per the SMB spec' do
184
+ expect(flags.generic_read).to be_a BinData::Bit1
185
+ end
186
+
187
+ it_behaves_like 'bit field with one flag set', :generic_read, 'V', 0x80000000
188
+ end
189
+
190
+ end