Skip to content

Commit

Permalink
Land #233, add ms-dfsnm defs and functions
Browse files Browse the repository at this point in the history
  • Loading branch information
space-r7 committed Jul 6, 2022
2 parents 5feba41 + 264b593 commit 8a4f191
Show file tree
Hide file tree
Showing 15 changed files with 390 additions and 12 deletions.
1 change: 1 addition & 0 deletions lib/ruby_smb/dcerpc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ module Dcerpc
require 'ruby_smb/dcerpc/epm'
require 'ruby_smb/dcerpc/drsr'
require 'ruby_smb/dcerpc/sec_trailer'
require 'ruby_smb/dcerpc/dfsnm'
require 'ruby_smb/dcerpc/request'
require 'ruby_smb/dcerpc/response'
require 'ruby_smb/dcerpc/rpc_auth3'
Expand Down
84 changes: 84 additions & 0 deletions lib/ruby_smb/dcerpc/dfsnm.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
module RubySMB
module Dcerpc
module Dfsnm

UUID = '4fc742e0-4a10-11cf-8273-00aa004ae673'
VER_MAJOR = 3
VER_MINOR = 0

# Operation numbers
NETR_DFS_ADD_STD_ROOT = 0x000c
NETR_DFS_REMOVE_STD_ROOT = 0x000d

require 'ruby_smb/dcerpc/dfsnm/netr_dfs_add_std_root_request'
require 'ruby_smb/dcerpc/dfsnm/netr_dfs_add_std_root_response'
require 'ruby_smb/dcerpc/dfsnm/netr_dfs_remove_std_root_request'
require 'ruby_smb/dcerpc/dfsnm/netr_dfs_remove_std_root_response'

# Create a new stand-alone DFS namespace.
#
# @param server_name [String] The host name of the DFS root target.
# @param root_share [String] The DFS root target share name.
# @param comment [String] A comment associated with the DFS namespace.
# @return nothing is returned on success
# @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a
# NetrDfsAddStdRootResponse packet
# @raise [RubySMB::Dcerpc::Error::DfsnmError] if the response error status
# is not ERROR_SUCCESS
def netr_dfs_add_std_root(server_name, root_share, comment: '')
netr_dfs_add_std_root_request = NetrDfsAddStdRootRequest.new(
server_name: server_name,
root_share: root_share,
comment: comment
)
response = dcerpc_request(netr_dfs_add_std_root_request)
begin
netr_dfs_add_std_root_response = NetrDfsAddStdRootResponse.read(response)
rescue IOError
raise RubySMB::Dcerpc::Error::InvalidPacket, 'Error reading NetrDfsAddStdRootResponse'
end
unless netr_dfs_add_std_root_response.error_status == WindowsError::Win32::ERROR_SUCCESS
status_code = WindowsError::Win32.find_by_retval(netr_dfs_add_std_root_response.error_status.value).first
raise RubySMB::Dcerpc::Error::DfsnmError.new(
"Error returned with netr_dfs_add_std_root: #{status_code}",
status_code: status_code
)
end

nil
end

# Delete the specified stand-alone DFS namespace.
#
# @param server_name [String] The host name of the DFS root target.
# @param root_share [String] The DFS root target share name.
# @return nothing is returned on success
# @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a
# NetrDfsRemoveStdRootResponse packet
# @raise [RubySMB::Dcerpc::Error::DfsnmError] if the response error status
# is not ERROR_SUCCESS
def netr_dfs_remove_std_root(server_name, root_share)
netr_dfs_remove_std_root_request = NetrDfsRemoveStdRootRequest.new(
server_name: server_name,
root_share: root_share
)
response = dcerpc_request(netr_dfs_remove_std_root_request)
begin
netr_dfs_remove_std_root_response = NetrDfsRemoveStdRootResponse.read(response)
rescue IOError
raise RubySMB::Dcerpc::Error::InvalidPacket, 'Error reading NetrDfsRemoveStdRootResponse'
end
unless netr_dfs_remove_std_root_response.error_status == WindowsError::Win32::ERROR_SUCCESS
status_code = WindowsError::Win32.find_by_retval(netr_dfs_remove_std_root_response.error_status.value).first
raise RubySMB::Dcerpc::Error::DfsnmError.new(
"Error returned with netr_dfs_remove_std_root: #{status_code}",
status_code: status_code
)
end

nil
end

end
end
end
24 changes: 24 additions & 0 deletions lib/ruby_smb/dcerpc/dfsnm/netr_dfs_add_std_root_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module RubySMB
module Dcerpc
module Dfsnm

# [3.1.4.4.1 NetrDfsAddStdRoot (Opnum 12)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dfsnm/b18ef17a-7a9c-4e22-b1bf-6a4d07e87b2d)
class NetrDfsAddStdRootRequest < BinData::Record
attr_reader :opnum

endian :little

ndr_conf_var_wide_stringz :server_name
ndr_conf_var_wide_stringz :root_share
ndr_conf_var_wide_stringz :comment
ndr_uint32 :api_flags

def initialize_instance
super
@opnum = NETR_DFS_ADD_STD_ROOT
end
end

end
end
end
21 changes: 21 additions & 0 deletions lib/ruby_smb/dcerpc/dfsnm/netr_dfs_add_std_root_response.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module RubySMB
module Dcerpc
module Dfsnm

# [3.1.4.4.1 NetrDfsAddStdRoot (Opnum 12)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dfsnm/b18ef17a-7a9c-4e22-b1bf-6a4d07e87b2d)
class NetrDfsAddStdRootResponse < BinData::Record
attr_reader :opnum

endian :little

ndr_uint32 :error_status

def initialize_instance
super
@opnum = NETR_DFS_ADD_STD_ROOT
end
end

end
end
end
23 changes: 23 additions & 0 deletions lib/ruby_smb/dcerpc/dfsnm/netr_dfs_remove_std_root_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module RubySMB
module Dcerpc
module Dfsnm

# [3.1.4.4.2 NetrDfsRemoveStdRoot (Opnum 13)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dfsnm/e9da023d-554a-49bc-837a-69f22d59fd18)
class NetrDfsRemoveStdRootRequest < BinData::Record
attr_reader :opnum

endian :little

ndr_conf_var_wide_stringz :server_name
ndr_conf_var_wide_stringz :root_share
ndr_uint32 :api_flags

def initialize_instance
super
@opnum = NETR_DFS_REMOVE_STD_ROOT
end
end

end
end
end
21 changes: 21 additions & 0 deletions lib/ruby_smb/dcerpc/dfsnm/netr_dfs_remove_std_root_response.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module RubySMB
module Dcerpc
module Dfsnm

# [3.1.4.4.2 NetrDfsRemoveStdRoot (Opnum 13)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dfsnm/e9da023d-554a-49bc-837a-69f22d59fd18)
class NetrDfsRemoveStdRootResponse < BinData::Record
attr_reader :opnum

endian :little

ndr_uint32 :error_status

def initialize_instance
super
@opnum = NETR_DFS_REMOVE_STD_ROOT
end
end

end
end
end
11 changes: 11 additions & 0 deletions lib/ruby_smb/dcerpc/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,17 @@ class CommunicationError < DcerpcError; end

# Raised when an error is returned during a Epm operation
class EpmError < DcerpcError; end

# Raised when an error is returned during a Dfsnm operation
class DfsnmError < DcerpcError
include RubySMB::Error::UnexpectedStatusCode::Mixin

def initialize(msg, status_code: nil)
self.status_code = status_code unless status_code.nil?

super(msg)
end
end
end
end
end
7 changes: 6 additions & 1 deletion lib/ruby_smb/dcerpc/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,12 @@ class Request < BinData::Record
drs_domain_controller_info_request Drsr::DRS_DOMAIN_CONTROLLER_INFO
drs_crack_names_request Drsr::DRS_CRACK_NAMES
drs_get_nc_changes_request Drsr::DRS_GET_NC_CHANGES
string :default
string :default
end
choice 'Dfsnm', selection: -> { opnum } do
netr_dfs_add_std_root_request Dfsnm::NETR_DFS_ADD_STD_ROOT
netr_dfs_remove_std_root_request Dfsnm::NETR_DFS_REMOVE_STD_ROOT
string :default
end
string :default
end
Expand Down
32 changes: 21 additions & 11 deletions lib/ruby_smb/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,20 +70,30 @@ def get_cmd(packet)

# Raised when a response packet has a NTStatus code that was unexpected.
class UnexpectedStatusCode < RubySMBError
attr_reader :status_code
module Mixin
attr_reader :status_code

def initialize(status_code)
case status_code
when WindowsError::ErrorCode
@status_code = status_code
when Integer
@status_code = WindowsError::NTStatus.find_by_retval(status_code).first
if @status_code.nil?
@status_code = WindowsError::ErrorCode.new("0x#{status_code.to_s(16)}", status_code, "Unknown 0x#{status_code.to_s(16)}")
private

def status_code=(status_code)
case status_code
when WindowsError::ErrorCode
@status_code = status_code
when Integer
@status_code = WindowsError::NTStatus.find_by_retval(status_code).first
if @status_code.nil?
@status_code = WindowsError::ErrorCode.new("0x#{status_code.to_s(16).rjust(8, '0')}", status_code, "Unknown status: 0x#{status_code.to_s(16)}")
end
else
raise ArgumentError, "Status code must be a WindowsError::ErrorCode or an Integer, got #{status_code.class}"
end
else
raise ArgumentError, "Status code must be a WindowsError::ErrorCode or an Integer, got #{status_code.class}"
end
end

include Mixin

def initialize(status_code)
self.status_code = status_code
super
end

Expand Down
2 changes: 2 additions & 0 deletions lib/ruby_smb/smb1/pipe.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ def initialize(tree:, response:, name:)
extend RubySMB::Dcerpc::Samr
when 'wkssvc', '\\wkssvc'
extend RubySMB::Dcerpc::Wkssvc
when 'netdfs', '\\netdfs'
extend RubySMB::Dcerpc::Dfsnm
end
super(tree: tree, response: response, name: name)
end
Expand Down
2 changes: 2 additions & 0 deletions lib/ruby_smb/smb2/pipe.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ def initialize(tree:, response:, name:)
extend RubySMB::Dcerpc::Samr
when 'wkssvc', '\\wkssvc'
extend RubySMB::Dcerpc::Wkssvc
when 'netdfs', '\\netdfs'
extend RubySMB::Dcerpc::Dfsnm
end
super(tree: tree, response: response, name: name)
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
RSpec.describe RubySMB::Dcerpc::Dfsnm::NetrDfsAddStdRootRequest do
subject(:packet) { described_class.new }

it { is_expected.to respond_to :server_name }
it { is_expected.to respond_to :root_share }
it { is_expected.to respond_to :comment }
it { is_expected.to respond_to :api_flags }
it { is_expected.to respond_to :opnum }

it 'is little endian' do
expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
end
it 'is a BinData::Record' do
expect(packet).to be_a(BinData::Record)
end
describe '#server_name' do
it 'is a NdrConfVarWideStringz structure' do
expect(packet.server_name).to be_a RubySMB::Dcerpc::Ndr::NdrConfVarWideStringz
end
end
describe '#root_share' do
it 'is a NdrConfVarWideStringz structure' do
expect(packet.root_share).to be_a RubySMB::Dcerpc::Ndr::NdrConfVarWideStringz
end
end
describe '#comment' do
it 'is a NdrConfVarWideStringz structure' do
expect(packet.comment).to be_a RubySMB::Dcerpc::Ndr::NdrConfVarWideStringz
end
end
describe '#api_flags' do
it 'is a NdrUint32 structure' do
expect(packet.api_flags).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
end
end
describe '#initialize_instance' do
it 'sets #opnum to NETR_DFS_ADD_STD_ROOT constant' do
expect(packet.opnum).to eq(RubySMB::Dcerpc::Dfsnm::NETR_DFS_ADD_STD_ROOT)
end
end
it 'reads itself' do
new_packet = described_class.new({
server_name: 'serverName',
root_share: 'rootShare',
comment: 'comment'
})
expected_output = {
server_name: 'serverName'.encode('utf-16le'),
root_share: 'rootShare'.encode('utf-16le'),
comment: 'comment'.encode('utf-16le'),
api_flags: 0
}
expect(packet.read(new_packet.to_binary_s)).to eq(expected_output)
end
end


Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
RSpec.describe RubySMB::Dcerpc::Dfsnm::NetrDfsAddStdRootResponse do
subject(:packet) { described_class.new }

it { is_expected.to respond_to :error_status }
it { is_expected.to respond_to :opnum }

it 'is little endian' do
expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
end
it 'is a BinData::Record' do
expect(packet).to be_a(BinData::Record)
end
describe '#error_status' do
it 'is a NdrUint32 structure' do
expect(packet.error_status).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
end
end
describe '#initialize_instance' do
it 'sets #opnum to NETR_DFS_ADD_STD_ROOT constant' do
expect(packet.opnum).to eq(RubySMB::Dcerpc::Dfsnm::NETR_DFS_ADD_STD_ROOT)
end
end
it 'reads itself' do
new_packet = described_class.new({
error_status: 0
})
expected_output = {
error_status: 0
}
expect(packet.read(new_packet.to_binary_s)).to eq(expected_output)
end
end


Loading

0 comments on commit 8a4f191

Please sign in to comment.