ruby_smb 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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