ruby_smb 3.1.2 → 3.1.5

Sign up to get free protection for your applications and to get access to all the features.
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