# SPDX-License-Identifier: GPL-2.0-or-later # This file is part of Scapy # See https://scapy.net/ for more information # Copyright (C) GuillaumeF # @Date: 2016-10-18 # @Last modified by: GuillaumeF # @Last modified by: Sebastien Mainand # @Last modified time: 2016-12-08 11:16:27 # @Last modified time: 2017-07-05 # scapy.contrib.description = OPC Data Access # scapy.contrib.status = loads """ Opc Data Access Spec: Google 'OPCDA3.00.pdf' RPC PDU encodings: - DCE 1.1 RPC: https://pubs.opengroup.org/onlinepubs/9629399/toc.pdf - http://pubs.opengroup.org/onlinepubs/9629399/chap12.htm DCOM Remote Protocol. [MS-DCOM]: Distributed Component Object Model (DCOM) Remote Protocol https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dcom/4a893f3d-bd29-48cd-9f43-d9777a4415b0 XXX TODO: does not appear to have been linked to RPC """ import struct from scapy.config import conf from scapy.fields import ( BitEnumField, ByteEnumField, ByteField, ConditionalField, Field, FieldLenField, FlagsField, IntEnumField, IntField, LEIntEnumField, LEIntField, LELongField, LEShortField, MultipleTypeField, PacketField, PacketLenField, PacketListField, ShortEnumField, ShortField, StrField, StrFixedLenField, StrLenField, UUIDField, _FieldContainer, _PacketField, ) from scapy.packet import Packet from scapy.layers.ntlm import NTLM_Header # Defined values _tagOPCDataSource = { 1: "OPC_DS_CACHE", 2: "OPC_DS_DEVICE" } _tagOPCBrowseType = { 1: "OPC_BRANCH", 2: "OPC_LEAF", 3: "OPC_FLAT" } _tagOPCNameSpaceType = { 1: "OPC_NS_HIERARCHIAL", 2: "OPC_NS_FLAT" } _tagOPCBrowseDirection = { 1: "OPC_BROWSE_UP", 2: "OPC_BROWSE_DOWN", 3: "OPC_BROWSE_TO" } _tagOPCEuType = { 0: "OPC_NOENUM", 1: "OPC_ANALOG", 2: "OPC_ENUMERATED" } _tagOPCServerState = { 1: "OPC_STATUS_RUNNING", 2: "OPC_STATUS_FAILED", 3: "OPC_STATUS_NOCONFIG", 4: "OPC_STATUS_SUSPENDED", 5: "OPC_STATUS_TEST", 6: "OPC_STATUS_COMM_FAULT" } _tagOPCEnumScope = { 1: "OPC_ENUM_PRIVATE_CONNECTIONS", 2: "OPC_ENUM_PUBLIC_CONNECTIONS", 3: "OPC_ENUM_ALL_CONNECTIONS", 4: "OPC_ENUM_PRIVATE", 5: "OPC_ENUM_PUBLIC", 6: "OPC_ENUM_ALL" } _pfc_flags = [ "firstFragment", # First fragment "lastFragment", # Last fragment "pendingCancel", # Cancel was pending at sender "reserved", # "concurrentMultiplexing", # supports concurrent multiplexing # of a single connection "didNotExecute", # only meaningful on `fault' packet if true, # guaranteed call did not execute "maybe", # `maybe' call semantics requested "objectUuid" # if true, a non-nil object UUID was specified # in the handle, and is present in the optional # object field. If false, the object field # is omitted ] _faultStatus = { 382312475: 'rpc_s_fault_object_not_found', 382312497: 'rpc_s_call_cancelled', 382312564: 'rpc_s_fault_addr_error', 382312565: 'rpc_s_fault_context_mismatch', 382312566: 'rpc_s_fault_fp_div_by_zero', 382312567: 'rpc_s_fault_fp_error', 382312568: 'rpc_s_fault_fp_overflow', 382312569: 'rpc_s_fault_fp_underflow', 382312570: 'rpc_s_fault_ill_inst', 382312571: 'rpc_s_fault_int_div_by_zero', 382312572: 'rpc_s_fault_int_overflow', 382312573: 'rpc_s_fault_invalid_bound', 382312574: 'rpc_s_fault_invalid_tag', 382312575: 'rpc_s_fault_pipe_closed', 382312576: 'rpc_s_fault_pipe_comm_error', 382312577: 'rpc_s_fault_pipe_discipline', 382312578: 'rpc_s_fault_pipe_empty', 382312579: 'rpc_s_fault_pipe_memory', 382312580: 'rpc_s_fault_pipe_order', 382312582: 'rpc_s_fault_remote_no_memory', 382312583: 'rpc_s_fault_unspec', 382312723: 'rpc_s_fault_user_defined', 382312726: 'rpc_s_fault_tx_open_failed', 382312814: 'rpc_s_fault_codeset_conv_error', 382312816: 'rpc_s_fault_no_client_stub', 469762049: 'nca_s_fault_int_div_by_zero', 469762050: 'nca_s_fault_addr_error', 469762051: 'nca_s_fault_fp_div_zero', 469762052: 'nca_s_fault_fp_underflow', 469762053: 'nca_s_fault_fp_overflow', 469762054: 'nca_s_fault_invalid_tag', 469762055: 'nca_s_fault_invalid_bound', 469762061: 'nca_s_fault_cancel', 469762062: 'nca_s_fault_ill_inst', 469762063: 'nca_s_fault_fp_error', 469762064: 'nca_s_fault_int_overflow', 469762068: 'nca_s_fault_pipe_empty', 469762069: 'nca_s_fault_pipe_closed', 469762070: 'nca_s_fault_pipe_order', 469762071: 'nca_s_fault_pipe_discipline', 469762072: 'nca_s_fault_pipe_comm_error', 469762073: 'nca_s_fault_pipe_memory', 469762074: 'nca_s_fault_context_mismatch', 469762075: 'nca_s_fault_remote_no_memory', 469762081: 'ncs_s_fault_user_defined', 469762082: 'nca_s_fault_tx_open_failed', 469762083: 'nca_s_fault_codeset_conv_error', 469762084: 'nca_s_fault_object_not_found', 469762085: 'nca_s_fault_no_client_stub', } _defResult = { 0: 'ACCEPTANCE', 1: 'USER_REJECTION', 2: 'PROVIDER_REJECTION', } _defReason = { 0: 'REASON_NOT_SPECIFIED', 1: 'ABSTRACT_SYNTAX_NOT_SUPPORTED', 2: 'PROPOSED_TRANSFER_SYNTAXES_NOT_SUPPORTED', 3: 'LOCAL_LIMIT_EXCEEDED', } _rejectBindNack = { 0: 'REASON_NOT_SPECIFIED', 1: 'TEMPORARY_CONGESTION', 2: 'LOCAL_LIMIT_EXCEEDED', 3: 'CALLED_PADDR_UNKNOWN', 4: 'PROTOCOL_VERSION_NOT_SUPPORTED', 5: 'DEFAULT_CONTEXT_NOT_SUPPORTED', 6: 'USER_DATA_NOT_READABLE', 7: 'NO_PSAP_AVAILABLE' } _rejectStatus = { 469762056: 'nca_rpc_version_mismatch', 469762057: 'nca_unspec_reject', 469762058: 'nca_s_bad_actid', 469762059: 'nca_who_are_you_failed', 469762060: 'nca_manager_not_entered', 469827586: 'nca_op_rng_error', 469827587: 'nca_unk_if', 469827590: 'nca_wrong_boot_time', 469827593: 'nca_s_you_crashed', 469827595: 'nca_proto_error', 469827603: 'nca_out_args_too_big', 469827604: 'nca_server_too_busy', 469827607: 'nca_unsupported_type', 469762076: 'nca_invalid_pres_context_id', 469762077: 'nca_unsupported_authn_level', 469762079: 'nca_invalid_checksum', 469762080: 'nca_invalid_crc' } _pduType = { 0: "REQUEST", 1: "PING", 2: "RESPONSE", 3: "FAULT", 4: "WORKING", 5: "NOCALL", 6: "REJECT", 7: "ACK", 8: "CI_CANCEL", 9: "FACK", 10: "CANCEL_ACK", 11: "BIND", 12: "BIND_ACK", 13: "BIND_NACK", 14: "ALTER_CONTEXT", 15: "ALTER_CONTEXT_RESP", 17: "SHUTDOWN", 18: "CO_CANCEL", 19: "ORPHANED", # Not documented 16: "Auth3", } _authentification_protocol = { 0: 'None', 1: 'OsfDcePrivateKeyAuthentication', } # Util def _make_le(pkt_cls): """ Make all fields in a packet LE. """ flds = [f.copy() for f in pkt_cls.fields_desc] for f in flds: if isinstance(f, _FieldContainer): f = f.fld if isinstance(f, UUIDField): f.uuid_fmt = UUIDField.FORMAT_LE elif isinstance(f, _PacketField): f.cls = globals().get(f.cls.__name__ + "LE", f.cls) elif not isinstance(f, StrField): f.fmt = "<" + f.fmt.replace(">", "").replace("!", "") f.struct = struct.Struct(f.fmt) class LEPacket(pkt_cls): fields_desc = flds name = pkt_cls().name + " (LE)" LEPacket.__name__ = pkt_cls.__name__ + "LE" return LEPacket # Sub class for dissection class AuthentificationProtocol(Packet): name = 'authentificationProtocol' def extract_padding(self, p): return b"", p def guess_payload_class(self, payload): if self.underlayer and hasattr(self.underlayer, "authLength"): authLength = self.underlayer.authLength if authLength != 0: try: return _authentification_protocol[authLength] except Exception: pass return conf.raw_layer class OsfDcePrivateKeyAuthentification(Packet): name = "OsfDcePrivateKeyAuthentication" # TODO def extract_padding(self, p): return b"", p class OPCHandle(Packet): def __init__(self, name, default): Field.__init__(self, name, default, "16s") def extract_padding(self, p): return b"", p class LenStringPacket(Packet): # Among other things, can be (port_any_t - DCE 1.1 RPC - p592) name = "len string packet" fields_desc = [ FieldLenField('length', 0, length_of='data', fmt="H"), MultipleTypeField( [(StrFixedLenField('data', '', length=2), lambda pkt: not pkt.length)], StrLenField('data', '', length_from=lambda pkt: pkt.length) ) ] def extract_padding(self, p): return b"", p LenStringPacketLE = _make_le(LenStringPacket) class SyntaxId(Packet): name = "syntax Id" fields_desc = [ UUIDField('interfaceUUID', str('0001' * 8), uuid_fmt=UUIDField.FORMAT_BE), ShortField('versionMajor', 0), ShortField('versionMinor', 0), ] def extract_padding(self, p): return b"", p SyntaxIdLE = _make_le(SyntaxId) class ResultElement(Packet): # p_result_list_t - DCE 1.1 RPC - p591 name = "p_result_t" fields_desc = [ ShortEnumField('resultContextNegotiation', 0, _defResult), ConditionalField(ShortEnumField('reason', 0, _defReason), lambda pkt:pkt.resultContextNegotiation != 0), PacketField('transferSyntax', '\x00' * 20, SyntaxId), ] def extract_padding(self, p): return b"", p ResultElementLE = _make_le(ResultElement) class ResultList(Packet): # p_result_list_t - DCE 1.1 RPC - p592 name = "p_result_list_t" fields_desc = [ ByteField('nbResult', 0), ByteField('reserved', 0), ShortField('reserved2', 0), PacketListField('resultList', None, ResultElement, count_from=lambda pkt:pkt.nbResult), ] def extract_padding(self, p): return b"", p ResultListLE = _make_le(ResultList) class ContextElement(Packet): name = "context element" fields_desc = [ ShortField('contxtId', 0), ByteField('nbTransferSyn', 0), ByteField('reserved', 0), PacketField('abstractSyntax', None, SyntaxId), PacketListField('transferSyntax', None, SyntaxId, count_from=lambda pkt:pkt.nbTransferSyn), ] def extract_padding(self, p): return b"", p ContextElementLE = _make_le(ContextElement) # Only in Little-Endian class STDOBJREF(Packet): name = 'stdObjRef' fields_desc = [ LEIntEnumField('flags', 1, {0: 'PINGING', 8: 'SORF_NOPING'}), LEIntField('cPublicRefs', 0), LELongField('OXID', 0), LELongField('OID', 0), PacketField('IPID', None, UUIDField), ] class StringBinding(Packet): name = 'String Binding' fields_desc = [ LEShortField('wTowerId', 0), # Not enough information to continue ] class DualStringArray(Packet): name = "Dual String Array" fields_desc = [ ShortField('wNumEntries', 0), ShortField('wSecurityOffset', 0), StrFixedLenField('StringBinding', '', # Simplify the problem length_from=lambda pkt:pkt.wSecurityOffset), ] DualStringArrayLE = _make_le(DualStringArray) class OBJREF_STANDARD(Packet): name = "objetref stanDard" fields_desc = [ PacketField('std', None, STDOBJREF), PacketField('saResAddr', None, DualStringArray), ] OBJREF_STANDARDLE = _make_le(OBJREF_STANDARD) class OBJREF_HANDLER(Packet): name = "objetref stanDard" fields_desc = [ PacketField('std', None, STDOBJREF), UUIDField('clsid', str('0001' * 8), uuid_fmt=UUIDField.FORMAT_BE), PacketField('saResAddr', None, DualStringArray), ] OBJREF_HANDLERLE = _make_le(OBJREF_HANDLER) class OBJREF_CUSTOM(Packet): name = "objetref stanDard" fields_desc = [ UUIDField('clsid', str('0001' * 8), uuid_fmt=UUIDField.FORMAT_BE), IntField('cbExtension', 0), IntField('reserved', 0), ] OBJREF_CUSTOMLE = _make_le(OBJREF_CUSTOM) class OBJREF_EXTENDED(Packet): name = "objetref stanDard" fields_desc = [ ] OBJREF_EXTENDEDLE = _make_le(OBJREF_EXTENDED) # Packet for the interfaces defined _objref_flag = { 1: 'OBJREF_STANDARD', 2: 'OBJREF_HANDLER', 4: 'OBJREF_CUSTOM', 8: 'OBJREF_EXTENDED', } _objref_pdu = { 1: [OBJREF_STANDARD, OBJREF_STANDARDLE], 2: [OBJREF_HANDLER, OBJREF_HANDLERLE], 4: [OBJREF_CUSTOM, OBJREF_CUSTOMLE], 8: [OBJREF_EXTENDED, OBJREF_EXTENDEDLE], } class IRemoteSCMActivator_RemoteCreateInstance(Packet): name = 'RemoteCreateInstance' fields_desc = [ ShortField('versionMajor', 0), ShortField('versionMinor', 0), IntEnumField('flag', 1, _objref_flag), IntField('reserved', 0), ] def guess_payload_class(self, payload): try: return _objref_pdu[self.flag][ self.__class__.__name__.endswith("LE") ] except Exception: pass IRemoteSCMActivator_RemoteCreateInstanceLE = _make_le( IRemoteSCMActivator_RemoteCreateInstance ) IRemoteSCMActivator = { # 0: 'Reserved for local use', # 1: 'Reserved for local use', # 2: 'Reserved for local use', # 3: [IRemoteSCMActivator_RemoteGetClassObject, # IRemoteSCMActivator_RemoteGetClassObject], 4: [IRemoteSCMActivator_RemoteCreateInstance, IRemoteSCMActivator_RemoteCreateInstanceLE], } # UUID defined for DCOM # Specifies the Distributed Component Object Model (DCOM) Remote Protocol # https://msdn.microsoft.com/en-us/library/cc226801.aspx # MS-Dcom.pdf 1.9 # OPC DA Specification.pdf _standardDcomEndpoint = { # MS-DCOM '4d9f4ab8-7d1c-11cf-861e-0020af6e7c57': "IActivation", '000001A0-0000-0000-C000-000000000046': IRemoteSCMActivator, '99fcfec4-5260-101b-bbcb-00aa0021347a': "IObjectExporter", '00000000-0000-0000-C000-000000000046': "IUnknown", '00000131-0000-0000-C000-000000000046': "IRemUnknown_IUnknown", '00000143-0000-0000-C000-000000000046': "IRemUnknown2_IRemUnknown", # OLE for Process Control '63D5F430-CFE4-11d1-B2C8-0060083BA1FB': "CATID_OPCDAServer10", '63D5F432-CFE4-11d1-B2C8-0060083BA1FB': "CATID_OPCDAServer20", 'CC603642-66D7-48f1-B69A-B625E73652D7': "CATID_OPCDAServer30", '39c13a4d-011e-11d0-9675-0020afd8adb3': "IOPCServer_IUnknown", '39c13a4e-011e-11d0-9675-0020afd8adb3': "IOPCServerPublicGroups_IUnknown", '39c13a4f-011e-11d0-9675-0020afd8adb3': "IOPCBrowseServerAddrSpace_IUnknown", # noqa: E501 '39c13a50-011e-11d0-9675-0020afd8adb3': "IOPCGroupStateMgt_IUnknown", '39c13a51-011e-11d0-9675-0020afd8adb3': "IOPCPublicGroupStateMgt_IUnknown", '39c13a52-011e-11d0-9675-0020afd8adb3': "IOPCSyncIO_IUnknown", '39c13a53-011e-11d0-9675-0020afd8adb3': "IOPCAsyncIO_IUnknown", '39c13a54-011e-11d0-9675-0020afd8adb3': "IOPCItemMgt_IUnknown", '39c13a55-011e-11d0-9675-0020afd8adb3': "IEnumOPCItemAttributes_IUnknown", '39c13a70-011e-11d0-9675-0020afd8adb3': "IOPCDataCallback_IUnknown", '39c13a71-011e-11d0-9675-0020afd8adb3': "IOPCAsyncIO2_IUnknown", '39c13a72-011e-11d0-9675-0020afd8adb3': "IOPCItemProperties_IUnknown", '5946DA93-8B39-4ec8-AB3D-AA73DF5BC86F': "IOPCItemDeadbandMgt_IUnknown", '3E22D313-F08B-41a5-86C8-95E95CB49FFC': "IOPCItemSamplingMgt_IUnknown", '39227004-A18F-4b57-8B0A-5235670F4468': "IOPCBrowse_IUnknown", '85C0B427-2893-4cbc-BD78-E5FC5146F08F': "IOPCItemIO_IUnknown", '730F5F0F-55B1-4c81-9E18-FF8A0904E1FA': "IOPCSyncIO2_IOPCSyncIO", '0967B97B-36EF-423e-B6F8-6BFF1E40D39D': "IOPCAsyncIO3_IOPCAsyncIO2", '8E368666-D72E-4f78-87ED-647611C61C9F': "IOPCGroupStateMgt2_IOPCGroupStateMgt", # noqa: E501 '3B540B51-0378-4551-ADCC-EA9B104302BF': "library_OPCDA", # Other '000001a5-0000-0000-c000-000000000046': "ActivationContextInfo", '00000338-0000-0000-c000-000000000046': "ActivationPropertiesIn", } # [MS-NLMP] 2.2.2.1 _attribute_type = { 0: 'EndOfList', 1: 'NetBIOSComputerName', 2: 'NetBIOSDomainName', 3: 'DNSComputername', 4: 'DNSDomainName', 6: 'Flags', 7: 'TimeStamp', 8: 'Restrictions', 9: 'TargetName', 10: 'ChannelBindings' } # Maybe depend of LE or BE _negociate_flags = [ "negociate_0x01", "negociate_version", "negociate_0x04", "negociate_0x08", "negociate_0x10", "negociate_128", "negociate_key_exchange", "negociate_56", "target_type_domain", "target_type_server", "taget_type_share", "negociate_extended_security", "negociate_identity", "negociate_0x002", "request_non_nt", "negociate_target_info", "negociate_0x000001", "negociate_ntlm_key", "negociate_nt_only", "negociate_anonymous", "negociate_oem_doamin", "negociate_oem_workstation", "negociate_0x00004", "negociate_always_sign", "negociate_unicode", "negociate_oem", "request_target", "negociate_00000008", "negociate_sign", "negociate_seal", "negociate_datagram", "negociate_lan_manager_key", ] class AV_PAIR(Packet): name = "AV_PAIR" fields_desc = [ ShortEnumField('avID', 2, _attribute_type), ShortField('avLen', 0), StrLenField('value', '', length_from=lambda pkt:pkt.avLen), ] def extract_padding(self, p): return b"", p AV_PAIRLE = _make_le(AV_PAIR) # NTLM _opcDa_auth_classes = { 10: [NTLM_Header, NTLM_Header], } class OpcDaAuth3(Packet): name = "Auth3" fields_desc = [ ShortField('code', 5840), ShortField('code2', 5840), ByteField('authType', 10), ByteField('authLevel', 2), ByteField('authPadLen', 0), ByteField('authReserved', 0), IntField('authContextId', 0), ] def guess_payload_class(self, payload): try: return _opcDa_auth_classes[self.authType][ self.__class__.__name__.endswith("LE") ] except Exception: pass OpcDaAuth3LE = _make_le(OpcDaAuth3) # A client sends a request PDU when it wants to execute a remote operation. # In a multi-PDU request, the request consists of a series of request PDUs # with the same sequence number and monotonically increasing fragment # numbers. The body of a request PDU contains data that represents input # parameters for the operation. class RequestStubData(Packet): name = 'RequestStubData' fields_desc = [ ShortField('versionMajor', 0), ShortField('versionMinor', 0), StrField('stubdata', ''), ] def extract_padding(self, p): return b"", p RequestStubDataLE = _make_le(RequestStubData) def _opc_stubdata_length(pkt): if not pkt.underlayer or not isinstance(pkt.underlayer, OpcDaHeaderN): return 0 stub_data_length = pkt.underlayer.fragLength - 24 stub_data_length -= pkt.underlayer.authLength if (OpcDaHeaderMessage in pkt.firstlayer() and pkt.firstlayer()[OpcDaHeaderMessage].pfc_flags & 'objectUuid'): stub_data_length -= 36 return max(0, stub_data_length) class OpcDaRequest(Packet): # DCE 1.1 RPC - 12.6.4.9 name = "OpcDaRequest" fields_desc = [ IntField('allocHint', 0), ShortField('contextId', 0), ShortField('opNum', 0), ConditionalField( UUIDField('uuid', str('0001' * 8), uuid_fmt=UUIDField.FORMAT_BE), lambda pkt: OpcDaHeaderMessage in pkt.firstlayer() and pkt.firstlayer()[OpcDaHeaderMessage].pfc_flags & 'objectUuid' ), PacketLenField('stubData', None, RequestStubData, length_from=lambda pkt: _opc_stubdata_length(pkt)), PacketField('authentication', None, AuthentificationProtocol), ] def extract_padding(self, p): return b"", p OpcDaRequestLE = _make_le(OpcDaRequest) # A client sends a ping PDU when it wants to inquire about an outstanding # request. # A ping PDU contains no body data. class OpcDaPing(Packet): # DCE 1.1 RPC - 12.5.3.7 name = "OpcDaPing" fields_desc = [] def extract_padding(self, p): return b"", p # A server sends a response PDU if an operation invoked by an idempotent, # broadcast or at-most-once request executes successfully. Servers do not send # responses for maybe or broadcast/maybe requests. A multi-PDU response # consists of a series of response PDUs with the same sequence number and # monotonically increasing fragment numbers. class OpcDaResponse(Packet): # DCE 1.1 RPC - 12.6.4.10 name = "OpcDaResponse" fields_desc = [ IntField('allocHint', 0), ShortField('contextId', 0), ByteField('cancelCount', 0), ByteField('reserved', 0), StrLenField('stubData', None, length_from=lambda pkt:pkt.allocHint - 32), PacketField('authentication', None, AuthentificationProtocol), ] def extract_padding(self, p): return b"", p OpcDaResponseLE = _make_le(OpcDaResponse) # The fault PDU is used to indicate either an RPC run-time, RPC stub, or # RPC-specific exception to the client. # Length of the stubdata equal allochint less header class OpcDaFault(Packet): # DCE 1.1 RPC - 12.6.4.7 name = "OpcDaFault" fields_desc = [ IntField('allocHint', 0), ShortField('contextId', 0), ByteField('cancelCount', 0), ByteField('reserved', 0), IntEnumField('Group', 0, _faultStatus), IntField('reserved2', 0), StrLenField('stubData', None, length_from=lambda pkt:pkt.allocHint - 32), PacketField('authentication', None, AuthentificationProtocol), ] def extract_padding(self, p): return b"", p OpcDaFaultLE = _make_le(OpcDaFault) # A server sends a working PDU in reply to a ping PDU. This reply indicates # that the server is processing the client's call. # A working PDU contains no body data. class OpcDaWorking(Packet): name = "OpcDaWorking" def extract_padding(self, p): return OpcDaFack # A nocall PDU can optionally carry a body whose format is the same as the # optional fack PDU body. class OpcDaNoCall(Packet): name = "OpcDaNoCall" def extract_padding(self, p): return OpcDaFack class OpcDaNoCallLE(Packet): name = "OpcDaNoCall" def extract_padding(self, p): return OpcDaFackLE # A server sends a reject PDU if an RPC request is rejected. The body of # a reject PDU contains a status code indicating why a callee is rejecting # a request PDU from a caller. class OpcDaReject(Packet): # DCE 1.1 RPC - 12.5.3.8 name = "OpcDaReject" fields_desc = [ IntField('allocHint', 0), ShortField('contextId', 0), ByteField('cancelCount', 0), ByteField('reserved', 0), IntEnumField('Group', 0, _rejectStatus), StrLenField('stubData', None, length_from=lambda pkt:pkt.allocHint - 32), PacketField('authentication', None, AuthentificationProtocol), ] def extract_padding(self, p): return b"", p OpcDaRejectLE = _make_le(OpcDaReject) # A client sends an ack PDU after it has received a response to # an at-most-once request. An ack PDU explicitly acknowledges that the # client has received the response; it tells the server to cease resending # the response and discard the response PDU. (A client can also implicitly # acknowledge receipt of a response by sending a new request.) # An ack PDU contains no body data. class OpcDaAck(Packet): name = "OpcDaAck" def extract_padding(self, p): return b"", p # The cancel PDU is used to forward a cancel. class OpcDaCl_cancel(Packet): # DCE 1.1 RPC - 12.5.3.3 name = "OpcDaCl_cancel" fields_desc = [ PacketField('authentication', None, AuthentificationProtocol), IntField('version', 0), IntField('cancelId', 0), ] def extract_padding(self, p): return b"", p OpcDaCl_cancelLE = _make_le(OpcDaCl_cancel) # Both clients and servers send fack PDUs. # A client sends a fack PDU after it has received a fragment of a multi-PDU # response. A fack PDU explicitly acknowledges that the client has received # the fragment; it may tell the sender to stop sending for a while. # A server sends a fack PDU after it has received a fragment of a multi-PDU # request. A fack PDU explicitly acknowledges that the server has received the # fragment; it may tell the sender to stop sending for a while. class OpcDaFack(Packet): # DCE 1.1 RPC - 12.5.3.4 name = "OpcDaFack" fields_desc = [ ShortField('version', 0), ByteField('pad', 0), ShortField('windowSize', 0), IntField('maxTsdu', 0), IntField('maxFragSize', 0), ShortField('serialNum', 0), FieldLenField('selackLen', 0, count_of='selack', fmt="H"), PacketListField('selack', None, IntField, count_from=lambda pkt:pkt.selackLen), ] def extract_padding(self, p): return b"", p OpcDaFackLE = _make_le(OpcDaFack) # A server sends a cancel_ack PDU after it has received a cancel PDU. # A cancel_ack PDU acknowledges that the server has cancelled or orphaned # a remote call or indicates that the server is not accepting cancels. # A ancel_ack PDUs can optionally have a body. A cancel_ack PDU without a body # acknowledges orphaning of a call, whereas a cancel_ack PDU with a body # acknowledges cancellation of a call. Orphaned calls do not perform any # further processing. Canceled calls transparently deliver a notification to # the server manager routine without altering the run-time system state of the # call. The run-time system's processing of a cancelled call continues # uninterrupted. class OpcDaCancel_ack(Packet): # DCE 1.1 RPC - 12.5.3.2 name = "OpcDaCancel_ack" fields_desc = [ IntField('version', 0), IntField('cancelId', 0), ByteField('accepting', 1) ] def extract_padding(self, p): return b"", p OpcDaCancel_ackLE = _make_le(OpcDaCancel_ack) # The bind PDU is used to initiate the presentation negotiation for the body # data, and optionally, authentication. The presentation negotiation follows # the model of the OSI presentation layer. class OpcDaBind(Packet): # DCE 1.1 RPC - 12.6.4.3 name = "OpcDaBind" fields_desc = [ ShortField('maxXmitFrag', 5840), ShortField('maxRecvtFrag', 5840), IntField('assocGroupId', 0), ByteField('nbContextElement', 1), ByteField('reserved', 0), ShortField('reserved2', 0), PacketListField('contextItem', None, ContextElement, count_from=lambda pkt:pkt.nbContextElement), PacketField('authentication', None, AuthentificationProtocol), ] def extract_padding(self, p): return b"", p OpcDaBindLE = _make_le(OpcDaBind) # The bind_ack PDU is returned by the server when it accepts a bind request # initiated by the client's bind PDU. It contains the results of presentation # context and fragment size negotiations. It may also contain a new # association group identifier if one was requested by the client. class OpcDaBind_ack(Packet): # DCE 1.1 RPC - 12.6.4.4 name = "OpcDaBind_ack" fields_desc = [ ShortField('maxXmitFrag', 5840), ShortField('maxRecvtFrag', 5840), IntField('assocGroupId', 0), PacketField('portSpec', '\x00\x00\x00\x00', LenStringPacket), IntField('pad2', 0), PacketField('resultList', None, ResultList), PacketField('authentication', None, AuthentificationProtocol), ] def extract_padding(self, p): return b"", p OpcDaBind_ackLE = _make_le(OpcDaBind_ack) # The bind_nak PDU is returned by the server when it rejects an association # request initiated by the client's bind PDU. The provider_reject_reason field # holds the rejection reason code. When the reject reason is # protocol_version_not_supported, the versions field contains a list of # run-time protocol versions supported by the server. class OpcDaBind_nak(Packet): # DCE 1.1 RPC - 12.6.4.5 name = "OpcDaBind_nak" fields_desc = [ ShortEnumField("providerRejectReason", 0, _rejectBindNack) ] # To complete def extract_padding(self, p): return b"", p OpcDaBind_nakLE = _make_le(OpcDaBind_nak) # The alter_context PDU is used to request additional presentation negotiation # for another interface and/or version, or to negotiate a new security # context, or both. class OpcDaAlter_context(Packet): # DCE 1.1 RPC - 12.6.4.1 name = "OpcDaAlter_context" fields_desc = [ ShortField('maxXmitFrag', 5840), ShortField('maxRecvtFrag', 5840), IntField('assocGroupId', 0), # PacketField('authentication', None, AuthentificationProtocol), ] # To complete def extract_padding(self, p): return b"", p OpcDaAlter_contextLE = _make_le(OpcDaAlter_context) class OpcDaAlter_Context_Resp(Packet): # DCE 1.1 RPC - 12.6.4.2 name = "OpcDaAlter_Context_Resp" fields_desc = [ ShortField('maxXmitFrag', 5840), ShortField('maxRecvtFrag', 5840), IntField('assocGroupId', 0), PacketField('portSpec', '\x00\x00\x00\x00', LenStringPacket), IntField('pad2', 0), PacketField('resultList', None, ResultList), PacketField('authentication', None, AuthentificationProtocol), ] # To complete def extract_padding(self, p): return b"", p OpcDaAlter_Context_RespLE = _make_le(OpcDaAlter_Context_Resp) # The shutdown PDU is sent by the server to request that a client terminate the # connection, freeing the related resources. # The shutdown PDU never contains an authentication verifier even if # authentication services are in use. class OpcDaShutdown(Packet): # DCE 1.1 RPC - 12.6.4.11 name = "OpcDaShutdown" def extract_padding(self, p): return b"", p # The cancel PDU is used to forward a cancel. class OpcDaCo_cancel(Packet): # DCE 1.1 RPC - 12.5.3.3 name = "OpcDaCO_cancel" fields_desc = [ PacketField('authentication', None, AuthentificationProtocol), IntField('version', 0), IntField('cancelId', 0), ] def extract_padding(self, p): return b"", p OpcDaCo_cancelLE = _make_le(OpcDaCo_cancel) # The orphaned PDU is used by a client to notify a server that it is aborting a # request in progress that has not been entirely transmitted yet, or that it # is aborting a (possibly lengthy) response in progress. class OpcDaOrphaned(AuthentificationProtocol): name = "OpcDaOrphaned" # DCE 1.1 RPC sect 12 _opcDa_pdu_classes = { 0: [OpcDaRequest, OpcDaRequestLE], 1: [OpcDaPing, OpcDaPing], 2: [OpcDaResponse, OpcDaResponseLE], 3: [OpcDaFault, OpcDaFaultLE], 4: [OpcDaWorking, OpcDaWorking], 5: [OpcDaNoCall, OpcDaNoCallLE], 6: [OpcDaReject, OpcDaRejectLE], 7: [OpcDaAck, OpcDaAck], 8: [OpcDaCl_cancel, OpcDaCl_cancelLE], 9: [OpcDaFack, OpcDaFackLE], 10: [OpcDaCancel_ack, OpcDaCancel_ackLE], 11: [OpcDaBind, OpcDaBindLE], 12: [OpcDaBind_ack, OpcDaBind_ackLE], 13: [OpcDaBind_nak, OpcDaBind_nakLE], 14: [OpcDaAlter_context, OpcDaAlter_contextLE], 15: [OpcDaAlter_Context_Resp, OpcDaAlter_Context_RespLE], 17: [OpcDaShutdown, OpcDaShutdown], 18: [OpcDaCo_cancel, OpcDaCo_cancelLE], 19: [OpcDaOrphaned, OpcDaOrphaned], # Not in the official documentation 16: [OpcDaAuth3, OpcDaAuth3LE], } class OpcDaHeaderN(Packet): # Last 3 fields of the common fields, used for dispatching the PDUs name = "OpcDaHeaderNext" fields_desc = [ ShortField('fragLength', 0), ShortEnumField('authLength', 0, _authentification_protocol), IntField('callID', 0) ] def guess_payload_class(self, payload): if self.underlayer: try: return _opcDa_pdu_classes[self.underlayer.pduType][ self.__class__.__name__.endswith("LE") ] except AttributeError: pass return conf.raw_layer OpcDaHeaderNLE = _make_le(OpcDaHeaderN) _opcda_next_header = { 0: OpcDaHeaderN, 1: OpcDaHeaderNLE } class OpcDaHeaderMessage(Packet): # An actual RPC PDU # DCE 1.1 RPC - 12.6.3.1 name = "OpcDaHeader" deprecated_fields = { "pdu_type": ("pduType", "2.5.0"), } fields_desc = [ ByteField('versionMajor', 0), ByteField('versionMinor', 0), ByteEnumField("pduType", 0, _pduType), FlagsField('pfc_flags', 0, 8, _pfc_flags), # Non-Delivery Report/Receipt (NDR) Format Label # DCE 1.1 RPC - 14.1 BitEnumField('integerRepresentation', 1, 4, {0: "bigEndian", 1: "littleEndian"}), BitEnumField('characterRepresentation', 0, 4, {0: "ascii", 1: "ebcdic"}), ByteEnumField('floatingPointRepresentation', 0, {0: "ieee", 1: "vax", 2: "cray", 3: "ibm"}), ShortField('res', 0), ] def guess_payload_class(self, payload): try: return _opcda_next_header[self.integerRepresentation] except Exception: pass # try: # return _opcDa_pdu_classes[self.pduType][self.integerRepresentation] # except Exception: # pass class OpcDaMessage(Packet): name = "OpcDaMessage" fields_desc = [ PacketField('OpcDaMessage', None, OpcDaHeaderMessage) ]