ruby_smb 3.1.2 → 3.1.5

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 (54) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/examples/file_server.rb +6 -68
  4. data/examples/virtual_file_server.rb +10 -62
  5. data/lib/ruby_smb/client/authentication.rb +29 -4
  6. data/lib/ruby_smb/client/negotiation.rb +2 -0
  7. data/lib/ruby_smb/client.rb +18 -3
  8. data/lib/ruby_smb/dcerpc/error.rb +13 -0
  9. data/lib/ruby_smb/dcerpc/fault.rb +83 -0
  10. data/lib/ruby_smb/dcerpc/ndr.rb +19 -8
  11. data/lib/ruby_smb/dcerpc/request.rb +15 -10
  12. data/lib/ruby_smb/dcerpc/rrp_rpc_unicode_string.rb +5 -0
  13. data/lib/ruby_smb/dcerpc/samr/samr_create_user2_in_domain_request.rb +24 -0
  14. data/lib/ruby_smb/dcerpc/samr/samr_create_user2_in_domain_response.rb +24 -0
  15. data/lib/ruby_smb/dcerpc/samr/samr_delete_user_request.rb +21 -0
  16. data/lib/ruby_smb/dcerpc/samr/samr_delete_user_response.rb +22 -0
  17. data/lib/ruby_smb/dcerpc/samr/samr_enumerate_domains_in_sam_server_request.rb +25 -0
  18. data/lib/ruby_smb/dcerpc/samr/samr_enumerate_domains_in_sam_server_response.rb +25 -0
  19. data/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_response.rb +0 -31
  20. data/lib/ruby_smb/dcerpc/samr/samr_get_alias_membership_response.rb +1 -14
  21. data/lib/ruby_smb/dcerpc/samr/samr_lookup_names_in_domain_request.rb +23 -0
  22. data/lib/ruby_smb/dcerpc/samr/samr_lookup_names_in_domain_response.rb +23 -0
  23. data/lib/ruby_smb/dcerpc/samr/samr_set_information_user2_request.rb +23 -0
  24. data/lib/ruby_smb/dcerpc/samr/samr_set_information_user2_response.rb +21 -0
  25. data/lib/ruby_smb/dcerpc/samr.rb +453 -83
  26. data/lib/ruby_smb/dcerpc.rb +1 -0
  27. data/lib/ruby_smb/error.rb +4 -0
  28. data/lib/ruby_smb/gss.rb +1 -0
  29. data/lib/ruby_smb/ntlm/client.rb +74 -0
  30. data/lib/ruby_smb/ntlm.rb +1 -0
  31. data/lib/ruby_smb/server/cli.rb +121 -0
  32. data/lib/ruby_smb/server.rb +1 -0
  33. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +11 -0
  34. data/lib/ruby_smb/smb1/pipe.rb +4 -1
  35. data/lib/ruby_smb/smb2/pipe.rb +4 -2
  36. data/lib/ruby_smb/version.rb +1 -1
  37. data/spec/lib/ruby_smb/client_spec.rb +1 -0
  38. data/spec/lib/ruby_smb/dcerpc/samr/samr_create_user2_in_domain_request_spec.rb +69 -0
  39. data/spec/lib/ruby_smb/dcerpc/samr/samr_create_user2_in_domain_response_spec.rb +69 -0
  40. data/spec/lib/ruby_smb/dcerpc/samr/samr_delete_user_request_spec.rb +42 -0
  41. data/spec/lib/ruby_smb/dcerpc/samr/samr_delete_user_response_spec.rb +51 -0
  42. data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_domains_in_sam_server_request_spec.rb +60 -0
  43. data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_domains_in_sam_server_response_spec.rb +75 -0
  44. data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_response_spec.rb +0 -195
  45. data/spec/lib/ruby_smb/dcerpc/samr/samr_lookup_names_in_domain_request_spec.rb +62 -0
  46. data/spec/lib/ruby_smb/dcerpc/samr/samr_lookup_names_in_domain_response_spec.rb +54 -0
  47. data/spec/lib/ruby_smb/dcerpc/samr/samr_set_information_user2_request_spec.rb +67 -0
  48. data/spec/lib/ruby_smb/dcerpc/samr/samr_set_information_user2_response_spec.rb +35 -0
  49. data/spec/lib/ruby_smb/dcerpc/samr_spec.rb +194 -0
  50. data/spec/lib/ruby_smb/ntlm/client/session_spec.rb +114 -0
  51. data/spec/lib/ruby_smb/ntlm/client_spec.rb +36 -0
  52. data.tar.gz.sig +0 -0
  53. metadata +39 -2
  54. metadata.gz.sig +0 -0
@@ -0,0 +1,74 @@
1
+ module RubySMB::NTLM
2
+ module Message
3
+ def deflag
4
+ security_buffers.inject(head_size) do |cur, a|
5
+ a[1].offset = cur
6
+ cur += a[1].data_size
7
+ has_flag?(:UNICODE) ? cur + cur % 2 : cur
8
+ end
9
+ end
10
+
11
+ def serialize
12
+ deflag
13
+ @alist.map { |n, f| f.serialize }.join + security_buffers.map { |n, f| f.value + (has_flag?(:UNICODE) ? "\x00".b * (f.value.length % 2) : '') }.join
14
+ end
15
+ end
16
+
17
+ class Client < Net::NTLM::Client
18
+ class Session < Net::NTLM::Client::Session
19
+ def authenticate!
20
+ calculate_user_session_key!
21
+ type3_opts = {
22
+ :lm_response => is_anonymous? ? "\x00".b : lmv2_resp,
23
+ :ntlm_response => is_anonymous? ? '' : ntlmv2_resp,
24
+ :domain => domain,
25
+ :user => username,
26
+ :workstation => workstation,
27
+ :flag => (challenge_message.flag & client.flags)
28
+ }
29
+ t3 = Net::NTLM::Message::Type3.create type3_opts
30
+ t3.extend(Message)
31
+ if negotiate_key_exchange?
32
+ t3.enable(:session_key)
33
+ rc4 = OpenSSL::Cipher.new("rc4")
34
+ rc4.encrypt
35
+ rc4.key = user_session_key
36
+ sk = rc4.update exported_session_key
37
+ sk << rc4.final
38
+ t3.session_key = sk
39
+ end
40
+ t3
41
+ end
42
+
43
+ def is_anonymous?
44
+ username == '' && password == ''
45
+ end
46
+
47
+ private
48
+
49
+ def use_oem_strings?
50
+ # @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/99d90ff4-957f-4c8a-80e4-5bfe5a9a9832
51
+ !challenge_message.has_flag?(:UNICODE) && challenge_message.has_flag?(:OEM)
52
+ end
53
+
54
+ def calculate_user_session_key!
55
+ if is_anonymous?
56
+ # see MS-NLMP section 3.4
57
+ @user_session_key = "\x00".b * 16
58
+ else
59
+ @user_session_key = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_hash, nt_proof_str)
60
+ end
61
+ end
62
+ end
63
+
64
+ def init_context(resp = nil, channel_binding = nil)
65
+ if resp.nil?
66
+ @session = nil
67
+ type1_message
68
+ else
69
+ @session = Client::Session.new(self, Net::NTLM::Message.decode64(resp), channel_binding)
70
+ @session.authenticate!
71
+ end
72
+ end
73
+ end
74
+ end
data/lib/ruby_smb/ntlm.rb CHANGED
@@ -59,3 +59,4 @@ module RubySMB
59
59
  end
60
60
  end
61
61
 
62
+ require 'ruby_smb/ntlm/client'
@@ -0,0 +1,121 @@
1
+ require 'optparse'
2
+
3
+ module RubySMB
4
+ class Server
5
+ module Cli
6
+ DEFAULT_OPTIONS = {
7
+ allow_anonymous: true,
8
+ allow_guests: false,
9
+ domain: nil,
10
+ username: 'RubySMB',
11
+ password: 'password',
12
+ share_name: 'home',
13
+ smbv1: true,
14
+ smbv2: true,
15
+ smbv3: true
16
+ }.freeze
17
+
18
+ # Parse options from the command line. The resulting option hash is suitable for passing to {build}.
19
+ #
20
+ # @yield [options, parser] A block that can be used to update the built option parser.
21
+ # @yieldparam [Hash<Symbol => >] options The options hash that should be assigned to.
22
+ # @yieldparam [OptionParser] parser The built option parser.
23
+ # @param [Hash] defaults Default option values to use.
24
+ # @return [Hash<Symbol => >] The options hash.
25
+ # * :allow_anonymous [Boolean] Whether or not to allow anonymous authentication.
26
+ # * :allow_guest [Boolean] Whether or not to allow guest authentication.
27
+ # * :username [String] The username of the account to add for authentication.
28
+ # * :password [String] The password of the account to add for authentication.
29
+ # * :domain [String] The domain of the account to add for authentication.
30
+ # * :smbv1 [Boolean] Whether or not to enable SMBv1 dialects.
31
+ # * :smbv2 [Boolean] Whether or not to enable SMBv2 dialects.
32
+ # * :smbv3 [Boolean] Whether or not to enable SMBv3 dialects.
33
+ # * :share_name [String] The name of the share to add.
34
+ def self.parse(defaults: {}, &block)
35
+ defaults = DEFAULT_OPTIONS.merge(defaults)
36
+ options = defaults.clone
37
+ OptionParser.new do |parser|
38
+ parser.on("--share-name SHARE_NAME", "The share name (default: #{defaults[:share_name]})") do |share|
39
+ options[:share_name] = share
40
+ end
41
+
42
+ parser.on("--[no-]anonymous", "Allow anonymous access (default: #{defaults[:allow_anonymous]})") do |allow_anonymous|
43
+ options[:allow_anonymous] = allow_anonymous
44
+ end
45
+
46
+ parser.on("--[no-]guests", "Allow guest accounts (default: #{defaults[:allow_guests]})") do |allow_guests|
47
+ options[:allow_guests] = allow_guests
48
+ end
49
+
50
+ parser.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{defaults[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
51
+ options[:smbv1] = smbv1
52
+ end
53
+
54
+ parser.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{defaults[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
55
+ options[:smbv2] = smbv2
56
+ end
57
+
58
+ parser.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{defaults[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
59
+ options[:smbv3] = smbv3
60
+ end
61
+
62
+ parser.on("--username USERNAME", "The account's username (default: #{defaults[:username]})") do |username|
63
+ if username.include?('\\')
64
+ options[:domain], options[:username] = username.split('\\', 2)
65
+ else
66
+ options[:username] = username
67
+ end
68
+ end
69
+
70
+ parser.on("--password PASSWORD", "The account's password (default: #{defaults[:password]})") do |password|
71
+ options[:password] = password
72
+ end
73
+
74
+ block.call(options, parser) if block_given?
75
+ end.parse!
76
+
77
+ options
78
+ end
79
+
80
+ # Build a server instance from the specified options. The NTLM provider will be used for authentication.
81
+ #
82
+ # @param [Hash] options the options to use while building the server. See the return value of {parse} for a
83
+ # comprehensive list of keys.
84
+ def self.build(options)
85
+ ntlm_provider = RubySMB::Gss::Provider::NTLM.new(
86
+ allow_anonymous: options[:allow_anonymous],
87
+ allow_guests: options[:allow_guests]
88
+ )
89
+ ntlm_provider.put_account(options[:username], options[:password], domain: options[:domain]) # password can also be an NTLM hash
90
+
91
+ server = RubySMB::Server.new(
92
+ gss_provider: ntlm_provider,
93
+ logger: :stdout
94
+ )
95
+ server.dialects.filter! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB1 } unless options[:smbv1]
96
+ server.dialects.filter! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB2 } unless options[:smbv2]
97
+ server.dialects.filter! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB3 } unless options[:smbv3]
98
+
99
+ server
100
+ end
101
+
102
+ # Run the server forever. At least 1 SMB dialect must be enabled.
103
+ #
104
+ # @param [RubySMB::Server] server The server instance to run.
105
+ # @param [#puts] out The stream to write standard output messages to.
106
+ # @param [#puts] err The stream to write error messages to.
107
+ def self.run(server, out: $stdout, err: $stderr)
108
+ if server.dialects.empty?
109
+ err.puts 'at least one version must be enabled'
110
+ return
111
+ end
112
+
113
+ out.puts 'server is running'
114
+ server.run do |server_client|
115
+ out.puts 'received connection'
116
+ true
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -6,6 +6,7 @@ module RubySMB
6
6
  # Currently, the server only supports negotiating and authenticating requests. No other server functionality is
7
7
  # available at this time. The negotiating and authentication is supported for SMB versions 1 through 3.1.1.
8
8
  class Server
9
+ require 'ruby_smb/server/cli'
9
10
  require 'ruby_smb/server/server_client'
10
11
  require 'ruby_smb/server/session'
11
12
  require 'ruby_smb/server/share'
@@ -40,6 +40,17 @@ module RubySMB
40
40
  parameter_block :parameter_block
41
41
  data_block :data_block
42
42
 
43
+ # Takes the specified security buffer string and sets it in the {RubySMB::SMB1::Packet::SessionSetupRequest::DataBlock#security_blob}
44
+ # field. It also automatically sets the length in
45
+ # {RubySMB::SMB1::Packet::SessionSetupRequest::ParameterBlock#security_blob_length}
46
+ #
47
+ # @param buffer [String] the security buffer
48
+ # @return [void]
49
+ def set_security_buffer(buffer)
50
+ parameter_block.security_blob_length = buffer.length
51
+ data_block.security_blob = buffer
52
+ end
53
+
43
54
  # Takes an NTLM Type 1 Message and creates the GSS Security Blob
44
55
  # for it and sets it in the {RubySMB::SMB1::Packet::SessionSetupRequest::DataBlock#security_blob}
45
56
  # field. It also automatically sets the length in
@@ -142,7 +142,10 @@ module RubySMB
142
142
 
143
143
  def dcerpc_response_from_raw_response(raw_data)
144
144
  dcerpc_response = RubySMB::Dcerpc::Response.read(raw_data)
145
- unless dcerpc_response.pdu_header.ptype == RubySMB::Dcerpc::PTypes::RESPONSE
145
+ if dcerpc_response.pdu_header.ptype == RubySMB::Dcerpc::PTypes::FAULT
146
+ status = dcerpc_response.stub.unpack('V').first
147
+ raise RubySMB::Dcerpc::Error::FaultError.new('A fault occurred', status: status)
148
+ elsif dcerpc_response.pdu_header.ptype != RubySMB::Dcerpc::PTypes::RESPONSE
146
149
  raise RubySMB::Dcerpc::Error::InvalidPacket, "Not a Response packet"
147
150
  end
148
151
  dcerpc_response
@@ -135,12 +135,14 @@ module RubySMB
135
135
  end
136
136
  end
137
137
 
138
-
139
138
  private
140
139
 
141
140
  def dcerpc_response_from_raw_response(raw_data)
142
141
  dcerpc_response = RubySMB::Dcerpc::Response.read(raw_data)
143
- unless dcerpc_response.pdu_header.ptype == RubySMB::Dcerpc::PTypes::RESPONSE
142
+ if dcerpc_response.pdu_header.ptype == RubySMB::Dcerpc::PTypes::FAULT
143
+ status = dcerpc_response.stub.unpack('V').first
144
+ raise RubySMB::Dcerpc::Error::FaultError.new('A fault occurred', status: status)
145
+ elsif dcerpc_response.pdu_header.ptype != RubySMB::Dcerpc::PTypes::RESPONSE
144
146
  raise RubySMB::Dcerpc::Error::InvalidPacket, "Not a Response packet"
145
147
  end
146
148
  dcerpc_response
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '3.1.2'.freeze
2
+ VERSION = '3.1.5'.freeze
3
3
  end
@@ -1881,6 +1881,7 @@ RSpec.describe RubySMB::Client do
1881
1881
  before :example do
1882
1882
  smb2_client.smb3 = true
1883
1883
  smb2_client.session_encrypt_data = false
1884
+ smb2_client.preauth_integrity_hash_value = ''
1884
1885
  end
1885
1886
 
1886
1887
  it 'sets the session_encrypt_data parameter to true if the server requires encryption' do
@@ -0,0 +1,69 @@
1
+ RSpec.describe RubySMB::Dcerpc::Samr::SamrCreateUser2InDomainRequest do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :domain_handle }
5
+ it { is_expected.to respond_to :name }
6
+ it { is_expected.to respond_to :account_type }
7
+ it { is_expected.to respond_to :desired_access }
8
+ it { is_expected.to respond_to :opnum }
9
+
10
+ it 'is little endian' do
11
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
12
+ end
13
+
14
+ it 'is a BinData::Record' do
15
+ expect(packet).to be_a(BinData::Record)
16
+ end
17
+
18
+ describe '#domain_handle' do
19
+ it 'is a SamprHandle structure' do
20
+ expect(packet.domain_handle).to be_a RubySMB::Dcerpc::Samr::SamprHandle
21
+ end
22
+ end
23
+
24
+ describe '#name' do
25
+ it 'is a RpcUnicodeString' do
26
+ expect(packet.name).to be_a RubySMB::Dcerpc::RpcUnicodeString
27
+ end
28
+ end
29
+
30
+ describe '#account_type' do
31
+ it 'is a NdrUint32 structure' do
32
+ expect(packet.account_type).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
33
+ end
34
+ end
35
+
36
+ describe '#desired_access' do
37
+ it 'is a NdrUint32 structure' do
38
+ expect(packet.desired_access).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
39
+ end
40
+ end
41
+
42
+ describe '#initialize_instance' do
43
+ it 'sets #opnum to SAMR_CREATE_USER2_IN_DOMAIN constant' do
44
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Samr::SAMR_CREATE_USER2_IN_DOMAIN)
45
+ end
46
+ end
47
+
48
+ it 'reads itself' do
49
+ new_packet = described_class.new({
50
+ domain_handle: {
51
+ context_handle_attributes: 0,
52
+ context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
53
+ },
54
+ name: 'test',
55
+ account_type: 1,
56
+ desired_access: 2
57
+ })
58
+ expected_output = {
59
+ domain_handle: {
60
+ context_handle_attributes: 0,
61
+ context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
62
+ },
63
+ name: {:buffer=>"test".encode('utf-16le'), :buffer_length=>8, :maximum_length=>8},
64
+ account_type: 1,
65
+ desired_access: 2
66
+ }
67
+ expect(packet.read(new_packet.to_binary_s)).to eq(expected_output)
68
+ end
69
+ end
@@ -0,0 +1,69 @@
1
+ RSpec.describe RubySMB::Dcerpc::Samr::SamrCreateUser2InDomainResponse do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :user_handle }
5
+ it { is_expected.to respond_to :granted_access }
6
+ it { is_expected.to respond_to :relative_id }
7
+ it { is_expected.to respond_to :error_status }
8
+ it { is_expected.to respond_to :opnum }
9
+
10
+ it 'is little endian' do
11
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
12
+ end
13
+
14
+ it 'is a BinData::Record' do
15
+ expect(packet).to be_a(BinData::Record)
16
+ end
17
+
18
+ describe '#user_handle' do
19
+ it 'is a SamprHandle structure' do
20
+ expect(packet.user_handle).to be_a RubySMB::Dcerpc::Samr::SamprHandle
21
+ end
22
+ end
23
+
24
+ describe '#granted_access' do
25
+ it 'is a NdrUint32' do
26
+ expect(packet.granted_access).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
27
+ end
28
+ end
29
+
30
+ describe '#relative_id' do
31
+ it 'is a NdrUint32 structure' do
32
+ expect(packet.relative_id).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
33
+ end
34
+ end
35
+
36
+ describe '#error_status' do
37
+ it 'is a NdrUint32 structure' do
38
+ expect(packet.error_status).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
39
+ end
40
+ end
41
+
42
+ describe '#initialize_instance' do
43
+ it 'sets #opnum to SAMR_CREATE_USER2_IN_DOMAIN constant' do
44
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Samr::SAMR_CREATE_USER2_IN_DOMAIN)
45
+ end
46
+ end
47
+
48
+ it 'reads itself' do
49
+ new_packet = described_class.new({
50
+ user_handle: {
51
+ context_handle_attributes: 0,
52
+ context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
53
+ },
54
+ granted_access: 1,
55
+ relative_id: 2,
56
+ error_status: 0x11223344
57
+ })
58
+ expected_output = {
59
+ user_handle: {
60
+ context_handle_attributes: 0,
61
+ context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
62
+ },
63
+ granted_access: 1,
64
+ relative_id: 2,
65
+ error_status: 0x11223344
66
+ }
67
+ expect(packet.read(new_packet.to_binary_s)).to eq(expected_output)
68
+ end
69
+ end
@@ -0,0 +1,42 @@
1
+ RSpec.describe RubySMB::Dcerpc::Samr::SamrDeleteUserRequest do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :user_handle }
5
+ it { is_expected.to respond_to :opnum }
6
+
7
+ it 'is little endian' do
8
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
9
+ end
10
+
11
+ it 'is a BinData::Record' do
12
+ expect(packet).to be_a(BinData::Record)
13
+ end
14
+
15
+ describe '#user_handle' do
16
+ it 'is a SamprHandle structure' do
17
+ expect(packet.user_handle).to be_a RubySMB::Dcerpc::Samr::SamprHandle
18
+ end
19
+ end
20
+
21
+ describe '#initialize_instance' do
22
+ it 'sets #opnum to SAMR_DELETE_USER constant' do
23
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Samr::SAMR_DELETE_USER)
24
+ end
25
+ end
26
+
27
+ it 'reads itself' do
28
+ new_packet = described_class.new({
29
+ user_handle: {
30
+ context_handle_attributes: 0,
31
+ context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
32
+ }
33
+ })
34
+ expected_output = {
35
+ user_handle: {
36
+ context_handle_attributes: 0,
37
+ context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
38
+ }
39
+ }
40
+ expect(packet.read(new_packet.to_binary_s)).to eq(expected_output)
41
+ end
42
+ end
@@ -0,0 +1,51 @@
1
+ RSpec.describe RubySMB::Dcerpc::Samr::SamrDeleteUserResponse do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :user_handle }
5
+ it { is_expected.to respond_to :error_status }
6
+ it { is_expected.to respond_to :opnum }
7
+
8
+ it 'is little endian' do
9
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
10
+ end
11
+
12
+ it 'is a BinData::Record' do
13
+ expect(packet).to be_a(BinData::Record)
14
+ end
15
+
16
+ describe '#user_handle' do
17
+ it 'is a SamprHandle structure' do
18
+ expect(packet.user_handle).to be_a RubySMB::Dcerpc::Samr::SamprHandle
19
+ end
20
+ end
21
+
22
+ describe '#error_status' do
23
+ it 'is a NdrUint32 structure' do
24
+ expect(packet.error_status).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
25
+ end
26
+ end
27
+
28
+ describe '#initialize_instance' do
29
+ it 'sets #opnum to SAMR_DELETE_USER constant' do
30
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Samr::SAMR_DELETE_USER)
31
+ end
32
+ end
33
+
34
+ it 'reads itself' do
35
+ new_packet = described_class.new({
36
+ user_handle: {
37
+ context_handle_attributes: 0,
38
+ context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
39
+ },
40
+ error_status: 0x11223344
41
+ })
42
+ expected_output = {
43
+ user_handle: {
44
+ context_handle_attributes: 0,
45
+ context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
46
+ },
47
+ error_status: 0x11223344
48
+ }
49
+ expect(packet.read(new_packet.to_binary_s)).to eq(expected_output)
50
+ end
51
+ end
@@ -0,0 +1,60 @@
1
+ RSpec.describe RubySMB::Dcerpc::Samr::SamrEnumerateDomainsInSamServerRequest do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :server_handle }
5
+ it { is_expected.to respond_to :enumeration_context }
6
+ it { is_expected.to respond_to :prefered_maximum_length }
7
+ it { is_expected.to respond_to :opnum }
8
+
9
+ it 'is little endian' do
10
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
11
+ end
12
+
13
+ it 'is a BinData::Record' do
14
+ expect(packet).to be_a(BinData::Record)
15
+ end
16
+
17
+ describe '#server_handle' do
18
+ it 'is a SamprHandle structure' do
19
+ expect(packet.server_handle).to be_a RubySMB::Dcerpc::Samr::SamprHandle
20
+ end
21
+ end
22
+
23
+ describe '#enumeration_context' do
24
+ it 'is a NdrUint32 structure' do
25
+ expect(packet.enumeration_context).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
26
+ end
27
+ end
28
+
29
+ describe '#prefered_maximum_length' do
30
+ it 'is a NdrUint32 structure' do
31
+ expect(packet.prefered_maximum_length).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
32
+ end
33
+ end
34
+
35
+ describe '#initialize_instance' do
36
+ it 'sets #opnum to SAMR_ENUMERATE_DOMAINS_IN_SAM_SERVER constant' do
37
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Samr::SAMR_ENUMERATE_DOMAINS_IN_SAM_SERVER)
38
+ end
39
+ end
40
+
41
+ it 'reads itself' do
42
+ new_packet = described_class.new({
43
+ server_handle: {
44
+ context_handle_attributes: 0,
45
+ context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
46
+ },
47
+ enumeration_context: 1,
48
+ prefered_maximum_length: 2
49
+ })
50
+ expected_output = {
51
+ server_handle: {
52
+ context_handle_attributes: 0,
53
+ context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
54
+ },
55
+ enumeration_context: 1,
56
+ prefered_maximum_length: 2
57
+ }
58
+ expect(packet.read(new_packet.to_binary_s)).to eq(expected_output)
59
+ end
60
+ end
@@ -0,0 +1,75 @@
1
+ RSpec.describe RubySMB::Dcerpc::Samr::SamrEnumerateDomainsInSamServerResponse do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :enumeration_context }
5
+ it { is_expected.to respond_to :buffer }
6
+ it { is_expected.to respond_to :count_returned }
7
+ it { is_expected.to respond_to :error_status }
8
+ it { is_expected.to respond_to :opnum }
9
+
10
+ it 'is little endian' do
11
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
12
+ end
13
+
14
+ it 'is a BinData::Record' do
15
+ expect(packet).to be_a(BinData::Record)
16
+ end
17
+
18
+ describe '#enumeration_context' do
19
+ it 'is a NdrUint32 structure' do
20
+ expect(packet.enumeration_context).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
21
+ end
22
+ end
23
+
24
+ describe '#buffer' do
25
+ it 'is a PsamprEnumerationBuffer structure' do
26
+ expect(packet.buffer).to be_a RubySMB::Dcerpc::Samr::PsamprEnumerationBuffer
27
+ end
28
+ end
29
+
30
+ describe '#count_returned' do
31
+ it 'is a NdrUint32 structure' do
32
+ expect(packet.count_returned).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
33
+ end
34
+ end
35
+
36
+ describe '#error_status' do
37
+ it 'is a NdrUint32 structure' do
38
+ expect(packet.error_status).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
39
+ end
40
+ end
41
+
42
+ describe '#initialize_instance' do
43
+ it 'sets #opnum to SAMR_ENUMERATE_DOMAINS_IN_SAM_SERVER constant' do
44
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Samr::SAMR_ENUMERATE_DOMAINS_IN_SAM_SERVER)
45
+ end
46
+ end
47
+
48
+ it 'reads itself' do
49
+ new_packet = described_class.new({
50
+ enumeration_context: 1,
51
+ buffer: {
52
+ entries_read: 2,
53
+ buffer: [
54
+ {relative_id: 500, name: { buffer_length: 26, maximum_length: 26, buffer: "Builtin".encode('utf-16le') }},
55
+ {relative_id: 501, name: { buffer_length: 10, maximum_length: 10, buffer: "RUBYSMB".encode('utf-16le') }},
56
+ ]
57
+ },
58
+ count_returned: 2,
59
+ error_status: 0x11223344
60
+ })
61
+ expected_output = {
62
+ enumeration_context: 1,
63
+ buffer: {
64
+ entries_read: 2,
65
+ buffer: [
66
+ {relative_id: 500, name: { buffer_length: 26, maximum_length: 26, buffer: "Builtin".encode('utf-16le') }},
67
+ {relative_id: 501, name: { buffer_length: 10, maximum_length: 10, buffer: "RUBYSMB".encode('utf-16le') }},
68
+ ]
69
+ },
70
+ count_returned: 2,
71
+ error_status: 0x11223344
72
+ }
73
+ expect(packet.read(new_packet.to_binary_s)).to eq(expected_output)
74
+ end
75
+ end