@@ -9,37 +9,91 @@ module Epm
99 # Operation numbers
1010 EPT_MAP = 0x0003
1111
12+ # MS-RPCE specific error codes
13+ STATUS_NO_ELEMENTS = 0x16C9A0D6
14+
1215 require 'ruby_smb/dcerpc/epm/epm_twrt'
1316 require 'ruby_smb/dcerpc/epm/epm_ept_map_request'
1417 require 'ruby_smb/dcerpc/epm/epm_ept_map_response'
1518
16- # Retrieve the service port number given a DCERPC interface UUID
17- # See:
18- # [2.2.1.2.5 ept_map Method](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rpce/ab744583-430e-4055-8901-3c6bc007e791)
19- # [https://pubs.opengroup.org/onlinepubs/9629399/apdxo.htm](https://pubs.opengroup.org/onlinepubs/9629399/apdxo.htm)
19+ # Map a service to a connection end point.
2020 #
21- # @param uuid [String] The interface UUID
22- # @param maj_ver [Integer] The interface Major version
23- # @param min_ver [Integer] The interface Minor version
24- # @param max_towers [Integer] The maximum number of elements to be returned
25- # @return [Hash] A hash with the host and port
26- # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a
27- # EpmEptMap packet
28- # @raise [RubySMB::Dcerpc::Error::EpmError] if the response error status
29- # is not STATUS_SUCCESS
30- def get_host_port_from_ept_mapper ( uuid :, maj_ver :, min_ver :, max_towers : 1 )
31- decoded_tower = EpmDecodedTowerOctetString . new (
32- interface_identifier : {
33- interface : uuid ,
34- major_version : maj_ver ,
35- minor_version : min_ver
36- } ,
37- data_representation : {
38- interface : Ndr ::UUID ,
39- major_version : Ndr ::VER_MAJOR ,
40- minor_version : Ndr ::VER_MINOR
41- }
42- )
21+ # @param uuid [String] The object UUID of the interface.
22+ # @param maj_ver [Integer] The major version number of the interface.
23+ # @param min_ver [Integer] The minor version number of the interface.
24+ # @param max_towers [Integer] The maximum number of results to obtain.
25+ # @param protocol [Symbol] The protocol of endpoint to obtain.
26+ #
27+ # @return [Array<Hash<Symbol,>>] The mapped endpoints. The hash keys will
28+ # depend on the protocol that was selected but an endpoint key will
29+ # always be present.
30+ # @raise [NotImplementedError] Raised if the *protocol* argument is not
31+ # supported.
32+ def ept_map ( uuid :, maj_ver :, min_ver : 0 , max_towers : 1 , protocol : :ncacn_ip_tcp )
33+ interface_identifier = {
34+ interface : uuid ,
35+ major_version : maj_ver ,
36+ minor_version : min_ver
37+ }
38+ data_representation = {
39+ interface : Ndr ::UUID ,
40+ major_version : Ndr ::VER_MAJOR ,
41+ minor_version : Ndr ::VER_MINOR
42+ }
43+
44+ case protocol
45+ when :ncacn_ip_tcp
46+ decoded_tower = EpmDecodedTowerOctetString . new (
47+ interface_identifier : interface_identifier ,
48+ data_representation : data_representation ,
49+ pipe_or_port : {
50+ identifier : 7 , # 0x07: DOD TCP port
51+ pipe_or_port : 0
52+ } ,
53+ host_or_addr : {
54+ identifier : 9 , # 0x09: DOD IP v4 address (big-endian)
55+ host_or_addr : 0
56+ }
57+ )
58+
59+ process_tower = lambda do |tower |
60+ port = tower . pipe_or_port . pipe_or_port . value
61+ address = IPAddr . new ( tower . host_or_addr . host_or_addr . value , Socket ::AF_INET )
62+ {
63+ port : port ,
64+ address : address ,
65+ # https://learn.microsoft.com/en-us/windows/win32/midl/ncacn-ip-tcp
66+ endpoint : "ncacn_ip_tcp:#{ address } [#{ port } ]"
67+ }
68+ end
69+ when :ncacn_np
70+ decoded_tower = EpmDecodedTowerOctetString . new (
71+ interface_identifier : interface_identifier ,
72+ data_representation : data_representation ,
73+ pipe_or_port : {
74+ identifier : 0x0f , # 0x0f: NetBIOS pipe name
75+ pipe_or_port : [ 0 ]
76+ } ,
77+ host_or_addr : {
78+ identifier : 0x11 , # 0x11: MS NetBIOS host name
79+ host_or_addr : [ 0 ]
80+ }
81+ )
82+
83+ process_tower = lambda do |tower |
84+ pipe = tower . pipe_or_port . pipe_or_port [ ...-1 ] . pack ( 'C*' )
85+ host = tower . host_or_addr . host_or_addr [ ...-1 ] . pack ( 'C*' )
86+ {
87+ pipe : pipe ,
88+ host : host ,
89+ # https://learn.microsoft.com/en-us/windows/win32/midl/ncacn-nb-nb
90+ endpoint : "ncacn_np:#{ host } [#{ pipe } ]"
91+ }
92+ end
93+ else
94+ raise NotImplementedError , "Unsupported protocol: #{ protocol } "
95+ end
96+
4397 tower = EpmTwrt . new ( decoded_tower )
4498 ept_map_request = EpmEptMapRequest . new (
4599 obj : Uuid . new ,
@@ -53,21 +107,31 @@ def get_host_port_from_ept_mapper(uuid:, maj_ver:, min_ver:, max_towers: 1)
53107 rescue IOError
54108 raise RubySMB ::Dcerpc ::Error ::InvalidPacket , 'Error reading EptMapResponse'
55109 end
56- unless ept_map_response . error_status == WindowsError ::NTStatus ::STATUS_SUCCESS
110+
111+ if ept_map_response . error_status == STATUS_NO_ELEMENTS
112+ raise RubySMB ::Dcerpc ::Error ::EpmError ,
113+ "Error returned with ept_map: " \
114+ "(0x16c9a0d6) STATUS_NO_ELEMENTS: There are no elements that satisfy the specified search criteria."
115+ elsif ept_map_response . error_status != WindowsError ::NTStatus ::STATUS_SUCCESS
57116 raise RubySMB ::Dcerpc ::Error ::EpmError ,
58117 "Error returned with ept_map: " \
59118 "#{ WindowsError ::NTStatus . find_by_retval ( ept_map_response . error_status . value ) . join ( ',' ) } "
60119 end
61- tower_binary = ept_map_response . towers [ 0 ] . tower_octet_string . to_binary_s
62- begin
63- decoded_tower = EpmDecodedTowerOctetString . read ( tower_binary )
64- rescue IOError
65- raise RubySMB ::Dcerpc ::Error ::InvalidPacket , 'Error reading EpmDecodedTowerOctetString'
120+
121+ ept_map_response . towers . map do |tower |
122+ tower_binary = tower . tower_octet_string . to_binary_s
123+ begin
124+ decoded_tower = EpmDecodedTowerOctetString . read ( tower_binary )
125+ rescue IOError
126+ raise RubySMB ::Dcerpc ::Error ::InvalidPacket , 'Error reading EpmDecodedTowerOctetString'
127+ end
128+
129+ process_tower . ( decoded_tower )
66130 end
67- {
68- port : decoded_tower . pipe_or_port . pipe_or_port . to_i ,
69- host : decoded_tower . host_or_addr . host_or_addr . to_i
70- }
131+ end
132+
133+ def ept_map_endpoint ( endpoint , ** kwargs )
134+ ept_map ( uuid : endpoint :: UUID , maj_ver : endpoint :: VER_MAJOR , min_ver : endpoint :: VER_MINOR , ** kwargs )
71135 end
72136 end
73137 end
0 commit comments