• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# SPDX-License-Identifier: GPL-2.0-or-later
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) Gabriel Potter
5
6# scapy.contrib.description = DCE/RPC
7# scapy.contrib.status = loads
8
9"""
10DCE/RPC
11Distributed Computing Environment / Remote Procedure Calls
12
13Based on [C706] - aka DCE/RPC 1.1
14https://pubs.opengroup.org/onlinepubs/9629399/toc.pdf
15
16And on [MS-RPCE]
17https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rpce/290c38b1-92fe-4229-91e6-4fc376610c15
18
19.. note::
20    Please read the documentation over
21    `DCE/RPC <https://scapy.readthedocs.io/en/latest/layers/dcerpc.html>`_
22"""
23
24from functools import partial
25
26import collections
27import struct
28from enum import IntEnum
29from uuid import UUID
30from scapy.base_classes import Packet_metaclass
31
32from scapy.config import conf
33from scapy.compat import bytes_encode, plain_str
34from scapy.error import log_runtime
35from scapy.layers.dns import DNSStrField
36from scapy.layers.ntlm import (
37    NTLM_Header,
38    NTLMSSP_MESSAGE_SIGNATURE,
39)
40from scapy.packet import (
41    Packet,
42    Raw,
43    bind_bottom_up,
44    bind_layers,
45    bind_top_down,
46    NoPayload,
47)
48from scapy.fields import (
49    _FieldContainer,
50    BitEnumField,
51    BitField,
52    ByteEnumField,
53    ByteField,
54    ConditionalField,
55    EnumField,
56    Field,
57    FieldLenField,
58    FieldListField,
59    FlagsField,
60    IntField,
61    LEIntEnumField,
62    LEIntField,
63    LELongField,
64    LEShortEnumField,
65    LEShortField,
66    LenField,
67    MultipleTypeField,
68    PacketField,
69    PacketLenField,
70    PacketListField,
71    PadField,
72    ReversePadField,
73    ShortEnumField,
74    ShortField,
75    SignedByteField,
76    StrField,
77    StrFixedLenField,
78    StrLenField,
79    StrLenFieldUtf16,
80    StrNullField,
81    StrNullFieldUtf16,
82    TrailerField,
83    UUIDEnumField,
84    UUIDField,
85    XByteField,
86    XLEIntField,
87    XLELongField,
88    XLEShortField,
89    XShortField,
90    XStrFixedLenField,
91)
92from scapy.sessions import DefaultSession
93from scapy.supersocket import StreamSocket
94
95from scapy.layers.kerberos import (
96    KRB_InnerToken,
97    Kerberos,
98)
99from scapy.layers.gssapi import (
100    GSS_S_COMPLETE,
101    GSSAPI_BLOB_SIGNATURE,
102    GSSAPI_BLOB,
103    SSP,
104)
105from scapy.layers.inet import TCP
106
107from scapy.contrib.rtps.common_types import (
108    EField,
109    EPacket,
110    EPacketField,
111    EPacketListField,
112)
113
114# Typing imports
115from typing import (
116    Optional,
117)
118
119# the alignment of auth_pad
120# This is 4 in [C706] 13.2.6.1 but was updated to 16 in [MS-RPCE] 2.2.2.11
121_COMMON_AUTH_PAD = 16
122# the alignment of the NDR Type 1 serialization private header
123# ([MS-RPCE] sect 2.2.6.2)
124_TYPE1_S_PAD = 8
125
126# DCE/RPC Packet
127DCE_RPC_TYPE = {
128    0: "request",
129    1: "ping",
130    2: "response",
131    3: "fault",
132    4: "working",
133    5: "no_call",
134    6: "reject",
135    7: "acknowledge",
136    8: "connectionless_cancel",
137    9: "frag_ack",
138    10: "cancel_ack",
139    11: "bind",
140    12: "bind_ack",
141    13: "bind_nak",
142    14: "alter_context",
143    15: "alter_context_resp",
144    16: "auth3",
145    17: "shutdown",
146    18: "co_cancel",
147    19: "orphaned",
148}
149_DCE_RPC_4_FLAGS1 = [
150    "reserved_01",
151    "last_frag",
152    "frag",
153    "no_frag_ack",
154    "maybe",
155    "idempotent",
156    "broadcast",
157    "reserved_7",
158]
159_DCE_RPC_4_FLAGS2 = [
160    "reserved_0",
161    "cancel_pending",
162    "reserved_2",
163    "reserved_3",
164    "reserved_4",
165    "reserved_5",
166    "reserved_6",
167    "reserved_7",
168]
169DCE_RPC_TRANSFER_SYNTAXES = {
170    UUID("00000000-0000-0000-0000-000000000000"): "NULL",
171    UUID("6cb71c2c-9812-4540-0300-000000000000"): "Bind Time Feature Negotiation",
172    UUID("8a885d04-1ceb-11c9-9fe8-08002b104860"): "NDR 2.0",
173    UUID("71710533-beba-4937-8319-b5dbef9ccc36"): "NDR64",
174}
175DCE_RPC_INTERFACES_NAMES = {}
176DCE_RPC_INTERFACES_NAMES_rev = {}
177
178
179class DCERPC_Transport(IntEnum):
180    NCACN_IP_TCP = 1
181    NCACN_NP = 2
182    # TODO: add more.. if people use them?
183
184
185def _dce_rpc_endianness(pkt):
186    """
187    Determine the right endianness sign for a given DCE/RPC packet
188    """
189    if pkt.endian == 0:  # big endian
190        return ">"
191    elif pkt.endian == 1:  # little endian
192        return "<"
193    else:
194        return "!"
195
196
197class _EField(EField):
198    def __init__(self, fld):
199        super(_EField, self).__init__(fld, endianness_from=_dce_rpc_endianness)
200
201
202class DceRpc(Packet):
203    """DCE/RPC packet"""
204
205    @classmethod
206    def dispatch_hook(cls, _pkt=None, *args, **kargs):
207        if _pkt and len(_pkt) >= 1:
208            ver = ord(_pkt[0:1])
209            if ver == 4:
210                return DceRpc4
211            elif ver == 5:
212                return DceRpc5
213        return DceRpc5
214
215
216bind_bottom_up(TCP, DceRpc, sport=135)
217bind_layers(TCP, DceRpc, dport=135)
218
219
220class _DceRpcPayload(Packet):
221    @property
222    def endianness(self):
223        if not self.underlayer:
224            return "!"
225        return _dce_rpc_endianness(self.underlayer)
226
227
228# sect 12.5
229
230_drep = [
231    BitEnumField("endian", 1, 4, ["big", "little"]),
232    BitEnumField("encoding", 0, 4, ["ASCII", "EBCDIC"]),
233    ByteEnumField("float", 0, ["IEEE", "VAX", "CRAY", "IBM"]),
234    ByteField("reserved1", 0),
235]
236
237
238class DceRpc4(DceRpc):
239    """
240    DCE/RPC v4 'connection-less' packet
241    """
242
243    name = "DCE/RPC v4"
244    fields_desc = (
245        [
246            ByteEnumField(
247                "rpc_vers", 4, {4: "4 (connection-less)", 5: "5 (connection-oriented)"}
248            ),
249            ByteEnumField("ptype", 0, DCE_RPC_TYPE),
250            FlagsField("flags1", 0, 8, _DCE_RPC_4_FLAGS1),
251            FlagsField("flags2", 0, 8, _DCE_RPC_4_FLAGS2),
252        ]
253        + _drep
254        + [
255            XByteField("serial_hi", 0),
256            _EField(UUIDField("object", None)),
257            _EField(UUIDField("if_id", None)),
258            _EField(UUIDField("act_id", None)),
259            _EField(IntField("server_boot", 0)),
260            _EField(IntField("if_vers", 1)),
261            _EField(IntField("seqnum", 0)),
262            _EField(ShortField("opnum", 0)),
263            _EField(XShortField("ihint", 0xFFFF)),
264            _EField(XShortField("ahint", 0xFFFF)),
265            _EField(LenField("len", None, fmt="H")),
266            _EField(ShortField("fragnum", 0)),
267            ByteEnumField("auth_proto", 0, ["none", "OSF DCE Private Key"]),
268            XByteField("serial_lo", 0),
269        ]
270    )
271
272
273# Exceptionally, we define those 3 here.
274
275
276class NL_AUTH_MESSAGE(Packet):
277    # [MS-NRPC] sect 2.2.1.3.1
278    name = "NL_AUTH_MESSAGE"
279    fields_desc = [
280        LEIntEnumField(
281            "MessageType",
282            0x00000000,
283            {
284                0x00000000: "Request",
285                0x00000001: "Response",
286            },
287        ),
288        FlagsField(
289            "Flags",
290            0,
291            -32,
292            [
293                "NETBIOS_DOMAIN_NAME",
294                "NETBIOS_COMPUTER_NAME",
295                "DNS_DOMAIN_NAME",
296                "DNS_HOST_NAME",
297                "NETBIOS_COMPUTER_NAME_UTF8",
298            ],
299        ),
300        ConditionalField(
301            StrNullField("NetbiosDomainName", ""),
302            lambda pkt: pkt.Flags.NETBIOS_DOMAIN_NAME,
303        ),
304        ConditionalField(
305            StrNullField("NetbiosComputerName", ""),
306            lambda pkt: pkt.Flags.NETBIOS_COMPUTER_NAME,
307        ),
308        ConditionalField(
309            DNSStrField("DnsDomainName", ""),
310            lambda pkt: pkt.Flags.DNS_DOMAIN_NAME,
311        ),
312        ConditionalField(
313            DNSStrField("DnsHostName", ""),
314            lambda pkt: pkt.Flags.DNS_HOST_NAME,
315        ),
316        ConditionalField(
317            # What the fuck? Why are they doing this
318            # The spec is just wrong
319            DNSStrField("NetbiosComputerNameUtf8", ""),
320            lambda pkt: pkt.Flags.NETBIOS_COMPUTER_NAME_UTF8,
321        ),
322    ]
323
324
325class NL_AUTH_SIGNATURE(Packet):
326    # [MS-NRPC] sect 2.2.1.3.2/2.2.1.3.3
327    name = "NL_AUTH_(SHA2_)SIGNATURE"
328    fields_desc = [
329        LEShortEnumField(
330            "SignatureAlgorithm",
331            0x0077,
332            {
333                0x0077: "HMAC-MD5",
334                0x0013: "HMAC-SHA256",
335            },
336        ),
337        LEShortEnumField(
338            "SealAlgorithm",
339            0xFFFF,
340            {
341                0xFFFF: "Unencrypted",
342                0x007A: "RC4",
343                0x001A: "AES-128",
344            },
345        ),
346        XLEShortField("Pad", 0xFFFF),
347        ShortField("Flags", 0),
348        XStrFixedLenField("SequenceNumber", b"", length=8),
349        XStrFixedLenField("Checksum", b"", length=8),
350        ConditionalField(
351            XStrFixedLenField("Confounder", b"", length=8),
352            lambda pkt: pkt.SealAlgorithm != 0xFFFF,
353        ),
354        MultipleTypeField(
355            [
356                (
357                    StrFixedLenField("Reserved2", b"", length=24),
358                    lambda pkt: pkt.SignatureAlgorithm == 0x0013,
359                ),
360            ],
361            StrField("Reserved2", b""),
362        ),
363    ]
364
365
366# [MS-RPCE] sect 2.2.1.1.7
367# https://learn.microsoft.com/en-us/windows/win32/rpc/authentication-service-constants
368# rpcdce.h
369
370
371class RPC_C_AUTHN(IntEnum):
372    NONE = 0x00
373    DCE_PRIVATE = 0x01
374    DCE_PUBLIC = 0x02
375    DEC_PUBLIC = 0x04
376    GSS_NEGOTIATE = 0x09
377    WINNT = 0x0A
378    GSS_SCHANNEL = 0x0E
379    GSS_KERBEROS = 0x10
380    DPA = 0x11
381    MSN = 0x12
382    KERNEL = 0x14
383    DIGEST = 0x15
384    NEGO_EXTENDED = 0x1E
385    PKU2U = 0x1F
386    LIVE_SSP = 0x20
387    LIVEXP_SSP = 0x23
388    CLOUD_AP = 0x24
389    NETLOGON = 0x44
390    MSONLINE = 0x52
391    MQ = 0x64
392    DEFAULT = 0xFFFFFFFF
393
394
395class RPC_C_AUTHN_LEVEL(IntEnum):
396    DEFAULT = 0x0
397    NONE = 0x1
398    CONNECT = 0x2
399    CALL = 0x3
400    PKT = 0x4
401    PKT_INTEGRITY = 0x5
402    PKT_PRIVACY = 0x6
403
404
405DCE_C_AUTHN_LEVEL = RPC_C_AUTHN_LEVEL  # C706 name
406
407
408# C706 sect 13.2.6.1
409
410
411class CommonAuthVerifier(Packet):
412    name = "Common Authentication Verifier"
413    fields_desc = [
414        ByteEnumField(
415            "auth_type",
416            0,
417            RPC_C_AUTHN,
418        ),
419        ByteEnumField("auth_level", 0, RPC_C_AUTHN_LEVEL),
420        ByteField("auth_pad_length", None),
421        ByteField("auth_reserved", 0),
422        XLEIntField("auth_context_id", 0),
423        MultipleTypeField(
424            [
425                # SPNEGO
426                (
427                    PacketLenField(
428                        "auth_value",
429                        GSSAPI_BLOB(),
430                        GSSAPI_BLOB,
431                        length_from=lambda pkt: pkt.parent.auth_len,
432                    ),
433                    lambda pkt: pkt.auth_type == 0x09 and pkt.parent and
434                    # Bind/Alter
435                    pkt.parent.ptype in [11, 12, 13, 14, 15, 16],
436                ),
437                (
438                    PacketLenField(
439                        "auth_value",
440                        GSSAPI_BLOB_SIGNATURE(),
441                        GSSAPI_BLOB_SIGNATURE,
442                        length_from=lambda pkt: pkt.parent.auth_len,
443                    ),
444                    lambda pkt: pkt.auth_type == 0x09
445                    and pkt.parent
446                    and (
447                        # Other
448                        not pkt.parent
449                        or pkt.parent.ptype not in [11, 12, 13, 14, 15, 16]
450                    ),
451                ),
452                # Kerberos
453                (
454                    PacketLenField(
455                        "auth_value",
456                        Kerberos(),
457                        Kerberos,
458                        length_from=lambda pkt: pkt.parent.auth_len,
459                    ),
460                    lambda pkt: pkt.auth_type == 0x10 and pkt.parent and
461                    # Bind/Alter
462                    pkt.parent.ptype in [11, 12, 13, 14, 15, 16],
463                ),
464                (
465                    PacketLenField(
466                        "auth_value",
467                        KRB_InnerToken(),
468                        KRB_InnerToken,
469                        length_from=lambda pkt: pkt.parent.auth_len,
470                    ),
471                    lambda pkt: pkt.auth_type == 0x10
472                    and pkt.parent
473                    and (
474                        # Other
475                        not pkt.parent
476                        or pkt.parent.ptype not in [11, 12, 13, 14, 15, 16]
477                    ),
478                ),
479                # NTLM
480                (
481                    PacketLenField(
482                        "auth_value",
483                        NTLM_Header(),
484                        NTLM_Header,
485                        length_from=lambda pkt: pkt.parent.auth_len,
486                    ),
487                    lambda pkt: pkt.auth_type in [0x0A, 0xFF] and pkt.parent and
488                    # Bind/Alter
489                    pkt.parent.ptype in [11, 12, 13, 14, 15, 16],
490                ),
491                (
492                    PacketLenField(
493                        "auth_value",
494                        NTLMSSP_MESSAGE_SIGNATURE(),
495                        NTLMSSP_MESSAGE_SIGNATURE,
496                        length_from=lambda pkt: pkt.parent.auth_len,
497                    ),
498                    lambda pkt: pkt.auth_type in [0x0A, 0xFF]
499                    and pkt.parent
500                    and (
501                        # Other
502                        not pkt.parent
503                        or pkt.parent.ptype not in [11, 12, 13, 14, 15, 16]
504                    ),
505                ),
506                # NetLogon
507                (
508                    PacketLenField(
509                        "auth_value",
510                        NL_AUTH_MESSAGE(),
511                        NL_AUTH_MESSAGE,
512                        length_from=lambda pkt: pkt.parent.auth_len,
513                    ),
514                    lambda pkt: pkt.auth_type == 0x44 and pkt.parent and
515                    # Bind/Alter
516                    pkt.parent.ptype in [11, 12, 13, 14, 15],
517                ),
518                (
519                    PacketLenField(
520                        "auth_value",
521                        NL_AUTH_SIGNATURE(),
522                        NL_AUTH_SIGNATURE,
523                        length_from=lambda pkt: pkt.parent.auth_len,
524                    ),
525                    lambda pkt: pkt.auth_type == 0x44
526                    and (
527                        # Other
528                        not pkt.parent
529                        or pkt.parent.ptype not in [11, 12, 13, 14, 15]
530                    ),
531                ),
532            ],
533            PacketLenField(
534                "auth_value",
535                None,
536                conf.raw_layer,
537                length_from=lambda pkt: pkt.parent and pkt.parent.auth_len or 0,
538            ),
539        ),
540    ]
541
542    def is_protected(self):
543        if not self.auth_value:
544            return False
545        if self.parent and self.parent.ptype in [11, 12, 13, 14, 15, 16]:
546            return False
547        return True
548
549    def is_ssp(self):
550        if not self.auth_value:
551            return False
552        if self.parent and self.parent.ptype not in [11, 12, 13, 14, 15, 16]:
553            return False
554        return True
555
556    def default_payload_class(self, pkt):
557        return conf.padding_layer
558
559
560# [MS-RPCE] sect 2.2.2.13 - Verification Trailer
561_SECTRAILER_MAGIC = b"\x8a\xe3\x13\x71\x02\xf4\x36\x71"
562
563
564class DceRpcSecVTCommand(Packet):
565    name = "Verification trailer command"
566    fields_desc = [
567        BitField("SEC_VT_MUST_PROCESS_COMMAND", 0, 1, tot_size=-2),
568        BitField("SEC_VT_COMMAND_END", 0, 1),
569        BitEnumField(
570            "Command",
571            0,
572            -14,
573            {
574                0x0001: "SEC_VT_COMMAND_BITMASK_1",
575                0x0002: "SEC_VT_COMMAND_PCONTEXT",
576                0x0003: "SEC_VT_COMMAND_HEADER2",
577            },
578            end_tot_size=-2,
579        ),
580        LEShortField("Length", None),
581    ]
582
583    def guess_payload_class(self, payload):
584        if self.Command == 0x0001:
585            return DceRpcSecVTBitmask
586        elif self.Command == 0x0002:
587            return DceRpcSecVTPcontext
588        elif self.Command == 0x0003:
589            return DceRpcSecVTHeader2
590        return conf.raw_payload
591
592
593# [MS-RPCE] sect 2.2.2.13.2
594
595
596class DceRpcSecVTBitmask(Packet):
597    name = "rpc_sec_vt_bitmask"
598    fields_desc = [
599        LEIntField("bits", 1),
600    ]
601
602    def default_payload_class(self, pkt):
603        return conf.padding_layer
604
605
606# [MS-RPCE] sect 2.2.2.13.4
607
608
609class DceRpcSecVTPcontext(Packet):
610    name = "rpc_sec_vt_pcontext"
611    fields_desc = [
612        UUIDEnumField(
613            "InterfaceId",
614            None,
615            (
616                DCE_RPC_INTERFACES_NAMES.get,
617                lambda x: DCE_RPC_INTERFACES_NAMES_rev.get(x.lower()),
618            ),
619            uuid_fmt=UUIDField.FORMAT_LE,
620        ),
621        LEIntField("Version", 0),
622        UUIDEnumField(
623            "TransferSyntax",
624            None,
625            DCE_RPC_TRANSFER_SYNTAXES,
626            uuid_fmt=UUIDField.FORMAT_LE,
627        ),
628        LEIntField("TransferVersion", 0),
629    ]
630
631    def default_payload_class(self, pkt):
632        return conf.padding_layer
633
634
635# [MS-RPCE] sect 2.2.2.13.3
636
637
638class DceRpcSecVTHeader2(Packet):
639    name = "rpc_sec_vt_header2"
640    fields_desc = [
641        ByteField("PTYPE", 0),
642        ByteField("Reserved1", 0),
643        LEShortField("Reserved2", 0),
644        LEIntField("drep", 0),
645        LEIntField("call_id", 0),
646        LEShortField("p_cont_id", 0),
647        LEShortField("opnum", 0),
648    ]
649
650    def default_payload_class(self, pkt):
651        return conf.padding_layer
652
653
654class DceRpcSecVT(Packet):
655    name = "Verification trailer"
656    fields_desc = [
657        XStrFixedLenField("rpc_sec_verification_trailer", _SECTRAILER_MAGIC, length=8),
658        PacketListField("commands", [], DceRpcSecVTCommand),
659    ]
660
661
662class _VerifTrailerField(PacketField):
663    def getfield(
664        self,
665        pkt,
666        s,
667    ):
668        if _SECTRAILER_MAGIC in s:
669            # a bit ugly
670            ind = s.index(_SECTRAILER_MAGIC)
671            sectrailer_bytes, remain = bytes(s[:-ind]), bytes(s[-ind:])
672            vt_trailer = self.m2i(pkt, sectrailer_bytes)
673            if not isinstance(vt_trailer.payload, NoPayload):
674                # bad parse
675                return s, None
676            return remain, vt_trailer
677        return s, None
678
679
680# sect 12.6.3
681
682
683_DCE_RPC_5_FLAGS = {
684    0x01: "PFC_FIRST_FRAG",
685    0x02: "PFC_LAST_FRAG",
686    0x04: "PFC_PENDING_CANCEL",
687    0x08: "PFC_RESERVED_1",
688    0x10: "PFC_CONC_MPX",
689    0x20: "PFC_DID_NOT_EXECUTE",
690    0x40: "PFC_MAYBE",
691    0x80: "PFC_OBJECT_UUID",
692}
693
694# [MS-RPCE] sect 2.2.2.3
695
696_DCE_RPC_5_FLAGS_2 = _DCE_RPC_5_FLAGS.copy()
697_DCE_RPC_5_FLAGS_2[0x04] = "PFC_SUPPORT_HEADER_SIGN"
698
699
700_DCE_RPC_ERROR_CODES = {
701    # Appendix N
702    0x1C010001: "nca_s_comm_failure",
703    0x1C010002: "nca_s_op_rng_error",
704    0x1C010003: "nca_s_unk_if",
705    0x1C010006: "nca_s_wrong_boot_time",
706    0x1C010009: "nca_s_you_crashed",
707    0x1C01000B: "nca_s_proto_error",
708    0x1C010013: "nca_s_out_args_too_big",
709    0x1C010014: "nca_s_server_too_busy",
710    0x1C010015: "nca_s_fault_string_too_long",
711    0x1C010017: "nca_s_unsupported_type",
712    0x1C000001: "nca_s_fault_int_div_by_zero",
713    0x1C000002: "nca_s_fault_addr_error",
714    0x1C000003: "nca_s_fault_fp_div_zero",
715    0x1C000004: "nca_s_fault_fp_underflow",
716    0x1C000005: "nca_s_fault_fp_overflow",
717    0x1C000006: "nca_s_fault_invalid_tag",
718    0x1C000007: "nca_s_fault_invalid_bound",
719    0x1C000008: "nca_s_rpc_version_mismatch",
720    0x1C000009: "nca_s_unspec_reject",
721    0x1C00000A: "nca_s_bad_actid",
722    0x1C00000B: "nca_s_who_are_you_failed",
723    0x1C00000C: "nca_s_manager_not_entered",
724    0x1C00000D: "nca_s_fault_cancel",
725    0x1C00000E: "nca_s_fault_ill_inst",
726    0x1C00000F: "nca_s_fault_fp_error",
727    0x1C000010: "nca_s_fault_int_overflow",
728    0x1C000012: "nca_s_fault_unspec",
729    0x1C000013: "nca_s_fault_remote_comm_failure",
730    0x1C000014: "nca_s_fault_pipe_empty",
731    0x1C000015: "nca_s_fault_pipe_closed",
732    0x1C000016: "nca_s_fault_pipe_order",
733    0x1C000017: "nca_s_fault_pipe_discipline",
734    0x1C000018: "nca_s_fault_pipe_comm_error",
735    0x1C000019: "nca_s_fault_pipe_memory",
736    0x1C00001A: "nca_s_fault_context_mismatch",
737    0x1C00001B: "nca_s_fault_remote_no_memory",
738    0x1C00001C: "nca_s_invalid_pres_context_id",
739    0x1C00001D: "nca_s_unsupported_authn_level",
740    0x1C00001F: "nca_s_invalid_checksum",
741    0x1C000020: "nca_s_invalid_crc",
742    0x1C000021: "nca_s_fault_user_defined",
743    0x1C000022: "nca_s_fault_tx_open_failed",
744    0x1C000023: "nca_s_fault_codeset_conv_error",
745    0x1C000024: "nca_s_fault_object_not_found",
746    0x1C000025: "nca_s_fault_no_client_stub",
747    # [MS-ERREF]
748    0x000006D3: "RPC_S_UNKNOWN_AUTHN_SERVICE",
749    0x000006F7: "RPC_X_BAD_STUB_DATA",
750    # [MS-RPCE]
751    0x000006D8: "EPT_S_CANT_PERFORM_OP",
752}
753
754_DCE_RPC_REJECTION_REASONS = {
755    0: "REASON_NOT_SPECIFIED",
756    1: "TEMPORARY_CONGESTION",
757    2: "LOCAL_LIMIT_EXCEEDED",
758    3: "CALLED_PADDR_UNKNOWN",
759    4: "PROTOCOL_VERSION_NOT_SUPPORTED",
760    5: "DEFAULT_CONTEXT_NOT_SUPPORTED",
761    6: "USER_DATA_NOT_READABLE",
762    7: "NO_PSAP_AVAILABLE",
763    8: "AUTHENTICATION_TYPE_NOT_RECOGNIZED",
764    9: "INVALID_CHECKSUM",
765}
766
767
768class DceRpc5(DceRpc):
769    """
770    DCE/RPC v5 'connection-oriented' packet
771    """
772
773    name = "DCE/RPC v5"
774    fields_desc = (
775        [
776            ByteEnumField(
777                "rpc_vers", 5, {4: "4 (connection-less)", 5: "5 (connection-oriented)"}
778            ),
779            ByteField("rpc_vers_minor", 0),
780            ByteEnumField("ptype", 0, DCE_RPC_TYPE),
781            MultipleTypeField(
782                # [MS-RPCE] sect 2.2.2.3
783                [
784                    (
785                        FlagsField("pfc_flags", 0x3, 8, _DCE_RPC_5_FLAGS_2),
786                        lambda pkt: pkt.ptype in [11, 12, 13, 14, 15, 16],
787                    )
788                ],
789                FlagsField("pfc_flags", 0x3, 8, _DCE_RPC_5_FLAGS),
790            ),
791        ]
792        + _drep
793        + [
794            ByteField("reserved2", 0),
795            _EField(ShortField("frag_len", None)),
796            _EField(
797                FieldLenField(
798                    "auth_len",
799                    None,
800                    fmt="H",
801                    length_of="auth_verifier",
802                    adjust=lambda pkt, x: 0 if not x else (x - 8),
803                )
804            ),
805            _EField(IntField("call_id", None)),
806            # Now let's proceed with trailer fields, i.e. at the end of the PACKET
807            # (below all payloads, etc.). Have a look at Figure 3 in sect 2.2.2.13
808            # of [MS-RPCE] but note the following:
809            # - auth_verifier includes sec_trailer + the authentication token
810            # - auth_padding is the authentication padding
811            # - vt_trailer is the verification trailer
812            ConditionalField(
813                TrailerField(
814                    PacketLenField(
815                        "auth_verifier",
816                        None,
817                        CommonAuthVerifier,
818                        length_from=lambda pkt: pkt.auth_len + 8,
819                    )
820                ),
821                lambda pkt: pkt.auth_len != 0,
822            ),
823            ConditionalField(
824                TrailerField(
825                    StrLenField(
826                        "auth_padding",
827                        None,
828                        length_from=lambda pkt: pkt.auth_verifier.auth_pad_length,
829                    )
830                ),
831                lambda pkt: pkt.auth_len != 0,
832            ),
833            TrailerField(
834                _VerifTrailerField("vt_trailer", None, DceRpcSecVT),
835            ),
836        ]
837    )
838
839    def do_dissect(self, s):
840        # Overload do_dissect to only include the current layer in dissection.
841        # This allows to support TrailerFields, even in the case where multiple DceRpc5
842        # packets are concatenated
843        frag_len = self.get_field("frag_len").getfield(self, s[8:10])[1]
844        s, remain = s[:frag_len], s[frag_len:]
845        return super(DceRpc5, self).do_dissect(s) + remain
846
847    def extract_padding(self, s):
848        # Now, take any data that doesn't fit in the current fragment and make it
849        # padding. The caller is responsible for looking for eventual padding and
850        # creating the next fragment, etc.
851        pay_len = self.frag_len - len(self.original) + len(s)
852        return s[:pay_len], s[pay_len:]
853
854    def post_build(self, pkt, pay):
855        if (
856            self.auth_verifier
857            and self.auth_padding is None
858            and self.auth_verifier.auth_pad_length is None
859        ):
860            # Compute auth_len and add padding
861            auth_len = self.get_field("auth_len").getfield(self, pkt[10:12])[1] + 8
862            auth_verifier, pay = pay[-auth_len:], pay[:-auth_len]
863            pdu_len = len(pay)
864            if self.payload:
865                pdu_len -= len(self.payload.self_build())
866            padlen = (-pdu_len) % _COMMON_AUTH_PAD
867            auth_verifier = (
868                auth_verifier[:2] + struct.pack("B", padlen) + auth_verifier[3:]
869            )
870            pay = pay + (padlen * b"\x00") + auth_verifier
871        if self.frag_len is None:
872            # Compute frag_len
873            length = len(pkt) + len(pay)
874            pkt = (
875                pkt[:8]
876                + self.get_field("frag_len").addfield(self, b"", length)
877                + pkt[10:]
878            )
879        return pkt + pay
880
881    def answers(self, pkt):
882        return isinstance(pkt, DceRpc5) and pkt[DceRpc5].call_id == self.call_id
883
884    @classmethod
885    def tcp_reassemble(cls, data, _, session):
886        if data[0:1] != b"\x05":
887            return
888        endian = struct.unpack("!B", data[4:5])[0] >> 4
889        if endian not in [0, 1]:
890            return
891        length = struct.unpack(("<" if endian else ">") + "H", data[8:10])[0]
892        if len(data) >= length:
893            if conf.dcerpc_session_enable:
894                # If DCE/RPC sessions are enabled, use them !
895                if "dcerpcsess" not in session:
896                    session["dcerpcsess"] = dcerpcsess = DceRpcSession()
897                else:
898                    dcerpcsess = session["dcerpcsess"]
899                return dcerpcsess.process(DceRpc5(data))
900            return DceRpc5(data)
901
902
903# sec 12.6.3.1
904
905
906class DceRpc5AbstractSyntax(EPacket):
907    name = "Presentation Syntax (p_syntax_id_t)"
908    fields_desc = [
909        _EField(
910            UUIDEnumField(
911                "if_uuid",
912                None,
913                (
914                    # Those are dynamic
915                    DCE_RPC_INTERFACES_NAMES.get,
916                    lambda x: DCE_RPC_INTERFACES_NAMES_rev.get(x.lower()),
917                ),
918            )
919        ),
920        _EField(IntField("if_version", 3)),
921    ]
922
923
924class DceRpc5TransferSyntax(EPacket):
925    name = "Presentation Transfer Syntax (p_syntax_id_t)"
926    fields_desc = [
927        _EField(
928            UUIDEnumField(
929                "if_uuid",
930                None,
931                DCE_RPC_TRANSFER_SYNTAXES,
932            )
933        ),
934        _EField(IntField("if_version", 3)),
935    ]
936
937
938class DceRpc5Context(EPacket):
939    name = "Presentation Context (p_cont_elem_t)"
940    fields_desc = [
941        _EField(ShortField("cont_id", 0)),
942        FieldLenField("n_transfer_syn", None, count_of="transfer_syntaxes", fmt="B"),
943        ByteField("reserved", 0),
944        EPacketField("abstract_syntax", None, DceRpc5AbstractSyntax),
945        EPacketListField(
946            "transfer_syntaxes",
947            None,
948            DceRpc5TransferSyntax,
949            count_from=lambda pkt: pkt.n_transfer_syn,
950            endianness_from=_dce_rpc_endianness,
951        ),
952    ]
953
954
955class DceRpc5Result(EPacket):
956    name = "Context negotiation Result"
957    fields_desc = [
958        _EField(
959            ShortEnumField(
960                "result", 0, ["acceptance", "user_rejection", "provider_rejection"]
961            )
962        ),
963        _EField(
964            ShortEnumField(
965                "reason",
966                0,
967                _DCE_RPC_REJECTION_REASONS,
968            )
969        ),
970        EPacketField("transfer_syntax", None, DceRpc5TransferSyntax),
971    ]
972
973
974class DceRpc5PortAny(EPacket):
975    name = "Port Any (port_any_t)"
976    fields_desc = [
977        _EField(FieldLenField("length", None, length_of="port_spec", fmt="H")),
978        _EField(StrLenField("port_spec", b"", length_from=lambda pkt: pkt.length)),
979    ]
980
981
982# sec 12.6.4.3
983
984
985class DceRpc5Bind(_DceRpcPayload):
986    name = "DCE/RPC v5 - Bind"
987    fields_desc = [
988        _EField(ShortField("max_xmit_frag", 5840)),
989        _EField(ShortField("max_recv_frag", 8192)),
990        _EField(IntField("assoc_group_id", 0)),
991        # p_cont_list_t
992        _EField(
993            FieldLenField("n_context_elem", None, count_of="context_elem", fmt="B")
994        ),
995        StrFixedLenField("reserved", 0, length=3),
996        EPacketListField(
997            "context_elem",
998            [],
999            DceRpc5Context,
1000            endianness_from=_dce_rpc_endianness,
1001            count_from=lambda pkt: pkt.n_context_elem,
1002        ),
1003    ]
1004
1005
1006bind_layers(DceRpc5, DceRpc5Bind, ptype=11)
1007
1008# sec 12.6.4.4
1009
1010
1011class DceRpc5BindAck(_DceRpcPayload):
1012    name = "DCE/RPC v5 - Bind Ack"
1013    fields_desc = [
1014        _EField(ShortField("max_xmit_frag", 5840)),
1015        _EField(ShortField("max_recv_frag", 8192)),
1016        _EField(IntField("assoc_group_id", 0)),
1017        PadField(
1018            EPacketField("sec_addr", None, DceRpc5PortAny),
1019            align=4,
1020        ),
1021        # p_result_list_t
1022        _EField(FieldLenField("n_results", None, count_of="results", fmt="B")),
1023        StrFixedLenField("reserved", 0, length=3),
1024        EPacketListField(
1025            "results",
1026            [],
1027            DceRpc5Result,
1028            endianness_from=_dce_rpc_endianness,
1029            count_from=lambda pkt: pkt.n_results,
1030        ),
1031    ]
1032
1033
1034bind_layers(DceRpc5, DceRpc5BindAck, ptype=12)
1035
1036# sec 12.6.4.5
1037
1038
1039class DceRpc5Version(EPacket):
1040    name = "version_t"
1041    fields_desc = [
1042        ByteField("major", 0),
1043        ByteField("minor", 0),
1044    ]
1045
1046
1047class DceRpc5BindNak(_DceRpcPayload):
1048    name = "DCE/RPC v5 - Bind Nak"
1049    fields_desc = [
1050        _EField(
1051            ShortEnumField("provider_reject_reason", 0, _DCE_RPC_REJECTION_REASONS)
1052        ),
1053        # p_rt_versions_supported_t
1054        _EField(FieldLenField("n_protocols", None, count_of="protocols", fmt="B")),
1055        EPacketListField(
1056            "protocols",
1057            [],
1058            DceRpc5Version,
1059            count_from=lambda pkt: pkt.n_protocols,
1060            endianness_from=_dce_rpc_endianness,
1061        ),
1062        # [MS-RPCE] sect 2.2.2.9
1063        ConditionalField(
1064            ReversePadField(
1065                _EField(
1066                    UUIDEnumField(
1067                        "signature",
1068                        None,
1069                        {
1070                            UUID(
1071                                "90740320-fad0-11d3-82d7-009027b130ab"
1072                            ): "Extended Error",
1073                        },
1074                    )
1075                ),
1076                align=8,
1077            ),
1078            lambda pkt: pkt.fields.get("signature", None)
1079            or (
1080                pkt.underlayer
1081                and pkt.underlayer.frag_len >= 24 + pkt.n_protocols * 2 + 16
1082            ),
1083        ),
1084    ]
1085
1086
1087bind_layers(DceRpc5, DceRpc5BindNak, ptype=13)
1088
1089
1090# sec 12.6.4.1
1091
1092
1093class DceRpc5AlterContext(_DceRpcPayload):
1094    name = "DCE/RPC v5 - AlterContext"
1095    fields_desc = DceRpc5Bind.fields_desc
1096
1097
1098bind_layers(DceRpc5, DceRpc5AlterContext, ptype=14)
1099
1100
1101# sec 12.6.4.2
1102
1103
1104class DceRpc5AlterContextResp(_DceRpcPayload):
1105    name = "DCE/RPC v5 - AlterContextResp"
1106    fields_desc = DceRpc5BindAck.fields_desc
1107
1108
1109bind_layers(DceRpc5, DceRpc5AlterContextResp, ptype=15)
1110
1111# [MS-RPCE] sect 2.2.2.10 - rpc_auth_3
1112
1113
1114class DceRpc5Auth3(Packet):
1115    name = "DCE/RPC v5 - Auth3"
1116    fields_desc = [StrFixedLenField("pad", b"", length=4)]
1117
1118
1119bind_layers(DceRpc5, DceRpc5Auth3, ptype=16)
1120
1121# sec 12.6.4.7
1122
1123
1124class DceRpc5Fault(_DceRpcPayload):
1125    name = "DCE/RPC v5 - Fault"
1126    fields_desc = [
1127        _EField(IntField("alloc_hint", 0)),
1128        _EField(ShortField("cont_id", 0)),
1129        ByteField("cancel_count", 0),
1130        FlagsField("reserved", 0, -8, {0x1: "RPC extended error"}),
1131        _EField(LEIntEnumField("status", 0, _DCE_RPC_ERROR_CODES)),
1132        IntField("reserved2", 0),
1133    ]
1134
1135
1136bind_layers(DceRpc5, DceRpc5Fault, ptype=3)
1137
1138
1139# sec 12.6.4.9
1140
1141
1142class DceRpc5Request(_DceRpcPayload):
1143    name = "DCE/RPC v5 - Request"
1144    fields_desc = [
1145        _EField(IntField("alloc_hint", 0)),
1146        _EField(ShortField("cont_id", 0)),
1147        _EField(ShortField("opnum", 0)),
1148        ConditionalField(
1149            PadField(
1150                _EField(UUIDField("object", None)),
1151                align=8,
1152            ),
1153            lambda pkt: pkt.underlayer and pkt.underlayer.pfc_flags.PFC_OBJECT_UUID,
1154        ),
1155    ]
1156
1157
1158bind_layers(DceRpc5, DceRpc5Request, ptype=0)
1159
1160# sec 12.6.4.10
1161
1162
1163class DceRpc5Response(_DceRpcPayload):
1164    name = "DCE/RPC v5 - Response"
1165    fields_desc = [
1166        _EField(IntField("alloc_hint", 0)),
1167        _EField(ShortField("cont_id", 0)),
1168        ByteField("cancel_count", 0),
1169        ByteField("reserved", 0),
1170    ]
1171
1172
1173bind_layers(DceRpc5, DceRpc5Response, ptype=2)
1174
1175# --- API
1176
1177DceRpcOp = collections.namedtuple("DceRpcOp", ["request", "response"])
1178DCE_RPC_INTERFACES = {}
1179
1180
1181class DceRpcInterface:
1182    def __init__(self, name, uuid, version_tuple, if_version, opnums):
1183        self.name = name
1184        self.uuid = uuid
1185        self.major_version, self.minor_version = version_tuple
1186        self.if_version = if_version
1187        self.opnums = opnums
1188
1189    def __repr__(self):
1190        return "<DCE/RPC Interface %s v%s.%s>" % (
1191            self.name,
1192            self.major_version,
1193            self.minor_version,
1194        )
1195
1196
1197def register_dcerpc_interface(name, uuid, version, opnums):
1198    """
1199    Register a DCE/RPC interface
1200    """
1201    version_tuple = tuple(map(int, version.split(".")))
1202    assert len(version_tuple) == 2, "Version should be in format 'X.X' !"
1203    if_version = (version_tuple[1] << 16) + version_tuple[0]
1204    if (uuid, if_version) in DCE_RPC_INTERFACES:
1205        # Interface is already registered.
1206        interface = DCE_RPC_INTERFACES[(uuid, if_version)]
1207        if interface.name == name:
1208            if interface.if_version == if_version and set(opnums) - set(
1209                interface.opnums
1210            ):
1211                # Interface is an extension of a previous interface
1212                interface.opnums.update(opnums)
1213                return
1214            elif interface.if_version != if_version:
1215                # Interface has a different version
1216                pass
1217            else:
1218                log_runtime.warning(
1219                    "This interface is already registered: %s. Skip" % interface
1220                )
1221                return
1222        else:
1223            raise ValueError(
1224                "An interface with the same UUID is already registered: %s" % interface
1225            )
1226    DCE_RPC_INTERFACES_NAMES[uuid] = name
1227    DCE_RPC_INTERFACES_NAMES_rev[name.lower()] = uuid
1228    DCE_RPC_INTERFACES[(uuid, if_version)] = DceRpcInterface(
1229        name,
1230        uuid,
1231        version_tuple,
1232        if_version,
1233        opnums,
1234    )
1235    # bind for build
1236    for opnum, operations in opnums.items():
1237        bind_top_down(DceRpc5Request, operations.request, opnum=opnum)
1238
1239
1240def find_dcerpc_interface(name):
1241    """
1242    Find an interface object through the name in the IDL
1243    """
1244    try:
1245        return next(x for x in DCE_RPC_INTERFACES.values() if x.name == name)
1246    except StopIteration:
1247        raise AttributeError("Unknown interface !")
1248
1249
1250COM_INTERFACES = {}
1251
1252
1253class ComInterface:
1254    def __init__(self, name, uuid, opnums):
1255        self.name = name
1256        self.uuid = uuid
1257        self.opnums = opnums
1258
1259    def __repr__(self):
1260        return "<COM Interface %s>" % (self.name,)
1261
1262
1263def register_com_interface(name, uuid, opnums):
1264    """
1265    Register a COM interface
1266    """
1267    COM_INTERFACES[uuid] = ComInterface(
1268        name,
1269        uuid,
1270        opnums,
1271    )
1272
1273
1274# --- NDR fields - [C706] chap 14
1275
1276
1277def _set_ctx_on(f, obj):
1278    if isinstance(f, _NDRPacket):
1279        f.ndr64 = obj.ndr64
1280        f.ndrendian = obj.ndrendian
1281    if isinstance(f, list):
1282        for x in f:
1283            if isinstance(x, _NDRPacket):
1284                x.ndr64 = obj.ndr64
1285                x.ndrendian = obj.ndrendian
1286
1287
1288def _e(ndrendian):
1289    return {"big": ">", "little": "<"}[ndrendian]
1290
1291
1292class _NDRPacket(Packet):
1293    __slots__ = ["ndr64", "ndrendian", "deferred_pointers", "request_packet"]
1294
1295    def __init__(self, *args, **kwargs):
1296        self.ndr64 = kwargs.pop("ndr64", False)
1297        self.ndrendian = kwargs.pop("ndrendian", "little")
1298        # request_packet is used in the session, so that a response packet
1299        # can resolve union arms if the case parameter is in the request.
1300        self.request_packet = kwargs.pop("request_packet", None)
1301        self.deferred_pointers = []
1302        super(_NDRPacket, self).__init__(*args, **kwargs)
1303
1304    def do_dissect(self, s):
1305        _up = self.parent or self.underlayer
1306        if _up and isinstance(_up, _NDRPacket):
1307            self.ndr64 = _up.ndr64
1308            self.ndrendian = _up.ndrendian
1309        else:
1310            # See comment above NDRConstructedType
1311            return NDRConstructedType([]).read_deferred_pointers(
1312                self, super(_NDRPacket, self).do_dissect(s)
1313            )
1314        return super(_NDRPacket, self).do_dissect(s)
1315
1316    def post_dissect(self, s):
1317        if self.deferred_pointers:
1318            # Can't trust the cache if there were deferred pointers
1319            self.raw_packet_cache = None
1320        return s
1321
1322    def do_build(self):
1323        _up = self.parent or self.underlayer
1324        for f in self.fields.values():
1325            _set_ctx_on(f, self)
1326        if not _up or not isinstance(_up, _NDRPacket):
1327            # See comment above NDRConstructedType
1328            return NDRConstructedType([]).add_deferred_pointers(
1329                self, super(_NDRPacket, self).do_build()
1330            )
1331        return super(_NDRPacket, self).do_build()
1332
1333    def default_payload_class(self, pkt):
1334        return conf.padding_layer
1335
1336    def clone_with(self, *args, **kwargs):
1337        pkt = super(_NDRPacket, self).clone_with(*args, **kwargs)
1338        # We need to copy deferred_pointers to not break pointer deferral
1339        # on build.
1340        pkt.deferred_pointers = self.deferred_pointers
1341        pkt.ndr64 = self.ndr64
1342        pkt.ndrendian = self.ndrendian
1343        return pkt
1344
1345    def copy(self):
1346        pkt = super(_NDRPacket, self).copy()
1347        pkt.deferred_pointers = self.deferred_pointers
1348        pkt.ndr64 = self.ndr64
1349        pkt.ndrendian = self.ndrendian
1350        return pkt
1351
1352    def show2(self, dump=False, indent=3, lvl="", label_lvl=""):
1353        return self.__class__(
1354            bytes(self), ndr64=self.ndr64, ndrendian=self.ndrendian
1355        ).show(dump, indent, lvl, label_lvl)
1356
1357    def getfield_and_val(self, attr):
1358        try:
1359            return Packet.getfield_and_val(self, attr)
1360        except ValueError:
1361            if self.request_packet:
1362                # Try to resolve the field from the request on failure
1363                try:
1364                    return self.request_packet.getfield_and_val(attr)
1365                except AttributeError:
1366                    pass
1367            raise
1368
1369    def valueof(self, request):
1370        """
1371        Util to get the value of a NDRField, ignoring arrays, pointers, etc.
1372        """
1373        val = self
1374        for ndr_field in request.split("."):
1375            fld, fval = val.getfield_and_val(ndr_field)
1376            val = fld.valueof(val, fval)
1377        return val
1378
1379
1380class _NDRAlign:
1381    def padlen(self, flen, pkt):
1382        return -flen % self._align[pkt.ndr64]
1383
1384    def original_length(self, pkt):
1385        # Find the length of the NDR frag to be able to pad properly
1386        while pkt:
1387            par = pkt.parent or pkt.underlayer
1388            if par and isinstance(par, _NDRPacket):
1389                pkt = par
1390            else:
1391                break
1392        return len(pkt.original)
1393
1394
1395class NDRAlign(_NDRAlign, ReversePadField):
1396    """
1397    ReversePadField modified to fit NDR.
1398
1399    - If no align size is specified, use the one from the inner field
1400    - Size is calculated from the beginning of the NDR stream
1401    """
1402
1403    def __init__(self, fld, align, padwith=None):
1404        super(NDRAlign, self).__init__(fld, align=align, padwith=padwith)
1405
1406
1407class _VirtualField(Field):
1408    # Hold a value but doesn't show up when building/dissecting
1409    def addfield(self, pkt, s, x):
1410        return s
1411
1412    def getfield(self, pkt, s):
1413        return s, None
1414
1415
1416class _NDRPacketMetaclass(Packet_metaclass):
1417    def __new__(cls, name, bases, dct):
1418        newcls = super(_NDRPacketMetaclass, cls).__new__(cls, name, bases, dct)
1419        conformants = dct.get("DEPORTED_CONFORMANTS", [])
1420        if conformants:
1421            amount = len(conformants)
1422            if amount == 1:
1423                newcls.fields_desc.insert(
1424                    0,
1425                    _VirtualField("max_count", None),
1426                )
1427            else:
1428                newcls.fields_desc.insert(
1429                    0,
1430                    FieldListField(
1431                        "max_counts",
1432                        [],
1433                        _VirtualField("", 0),
1434                        count_from=lambda _: amount,
1435                    ),
1436                )
1437        return newcls  # type: ignore
1438
1439
1440class NDRPacket(_NDRPacket, metaclass=_NDRPacketMetaclass):
1441    """
1442    A NDR Packet. Handles pointer size & endianness
1443    """
1444
1445    __slots__ = ["_align"]
1446
1447    # NDR64 pad structures
1448    # [MS-RPCE] 2.2.5.3.4.1
1449    ALIGNMENT = (1, 1)
1450    # [C706] sect 14.3.7 - Conformants max_count can be added to the beginning
1451    DEPORTED_CONFORMANTS = []
1452
1453
1454# Primitive types
1455
1456
1457class _NDRValueOf:
1458    def valueof(self, pkt, x):
1459        return x
1460
1461
1462class _NDRLenField(_NDRValueOf, Field):
1463    """
1464    Field similar to FieldLenField that takes size_of and adjust as arguments,
1465    and take the value of a size on build.
1466    """
1467
1468    __slots__ = ["size_of", "adjust"]
1469
1470    def __init__(self, *args, **kwargs):
1471        self.size_of = kwargs.pop("size_of", None)
1472        self.adjust = kwargs.pop("adjust", lambda _, x: x)
1473        super(_NDRLenField, self).__init__(*args, **kwargs)
1474
1475    def i2m(self, pkt, x):
1476        if x is None and pkt is not None and self.size_of is not None:
1477            fld, fval = pkt.getfield_and_val(self.size_of)
1478            f = fld.i2len(pkt, fval)
1479            x = self.adjust(pkt, f)
1480        elif x is None:
1481            x = 0
1482        return x
1483
1484
1485class NDRByteField(_NDRLenField, ByteField):
1486    pass
1487
1488
1489class NDRSignedByteField(_NDRLenField, SignedByteField):
1490    pass
1491
1492
1493class _NDRField(_NDRLenField):
1494    FMT = ""
1495    ALIGN = (0, 0)
1496
1497    def getfield(self, pkt, s):
1498        return NDRAlign(
1499            Field("", 0, fmt=_e(pkt.ndrendian) + self.FMT), align=self.ALIGN
1500        ).getfield(pkt, s)
1501
1502    def addfield(self, pkt, s, val):
1503        return NDRAlign(
1504            Field("", 0, fmt=_e(pkt.ndrendian) + self.FMT), align=self.ALIGN
1505        ).addfield(pkt, s, self.i2m(pkt, val))
1506
1507
1508class NDRShortField(_NDRField):
1509    FMT = "H"
1510    ALIGN = (2, 2)
1511
1512
1513class NDRSignedShortField(_NDRField):
1514    FMT = "h"
1515    ALIGN = (2, 2)
1516
1517
1518class NDRIntField(_NDRField):
1519    FMT = "I"
1520    ALIGN = (4, 4)
1521
1522
1523class NDRSignedIntField(_NDRField):
1524    FMT = "i"
1525    ALIGN = (4, 4)
1526
1527
1528class NDRLongField(_NDRField):
1529    FMT = "Q"
1530    ALIGN = (8, 8)
1531
1532
1533class NDRSignedLongField(_NDRField):
1534    FMT = "q"
1535    ALIGN = (8, 8)
1536
1537
1538class NDRIEEEFloatField(_NDRField):
1539    FMT = "f"
1540    ALIGN = (4, 4)
1541
1542
1543class NDRIEEEDoubleField(_NDRField):
1544    FMT = "d"
1545    ALIGN = (8, 8)
1546
1547
1548# Enum types
1549
1550
1551class _NDREnumField(_NDRValueOf, EnumField):
1552    # [MS-RPCE] sect 2.2.5.2 - Enums are 4 octets in NDR64
1553    FMTS = ["H", "I"]
1554
1555    def getfield(self, pkt, s):
1556        fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1557        return NDRAlign(Field("", 0, fmt=fmt), align=(2, 4)).getfield(pkt, s)
1558
1559    def addfield(self, pkt, s, val):
1560        fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1561        return NDRAlign(Field("", 0, fmt=fmt), align=(2, 4)).addfield(
1562            pkt, s, self.i2m(pkt, val)
1563        )
1564
1565
1566class NDRInt3264EnumField(NDRAlign):
1567    def __init__(self, *args, **kwargs):
1568        super(NDRInt3264EnumField, self).__init__(
1569            _NDREnumField(*args, **kwargs), align=(2, 4)
1570        )
1571
1572
1573class NDRIntEnumField(_NDRValueOf, NDRAlign):
1574    # v1_enum are always 4-octets, even in NDR32
1575    def __init__(self, *args, **kwargs):
1576        super(NDRIntEnumField, self).__init__(
1577            LEIntEnumField(*args, **kwargs), align=(4, 4)
1578        )
1579
1580
1581# Special types
1582
1583
1584class NDRInt3264Field(_NDRLenField):
1585    FMTS = ["I", "Q"]
1586
1587    def getfield(self, pkt, s):
1588        fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1589        return NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(pkt, s)
1590
1591    def addfield(self, pkt, s, val):
1592        fmt = _e(pkt.ndrendian) + self.FMTS[pkt.ndr64]
1593        return NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(
1594            pkt, s, self.i2m(pkt, val)
1595        )
1596
1597
1598class NDRSignedInt3264Field(NDRInt3264Field):
1599    FMTS = ["i", "q"]
1600
1601
1602# Pointer types
1603
1604
1605class NDRPointer(_NDRPacket):
1606    fields_desc = [
1607        MultipleTypeField(
1608            [(XLELongField("referent_id", 1), lambda pkt: pkt and pkt.ndr64)],
1609            XLEIntField("referent_id", 1),
1610        ),
1611        PacketField("value", None, conf.raw_layer),
1612    ]
1613
1614
1615class NDRFullPointerField(_FieldContainer):
1616    """
1617    A NDR Full/Unique pointer field encapsulation.
1618
1619    :param deferred: This pointer is deferred. This means that it's representation
1620                     will not appear after the pointer.
1621                     See [C706] 14.3.12.3 - Algorithm for Deferral of Referents
1622    """
1623
1624    EMBEDDED = False
1625
1626    def __init__(self, fld, deferred=False, fmt="I"):
1627        self.fld = fld
1628        self.default = None
1629        self.deferred = deferred
1630
1631    def getfield(self, pkt, s):
1632        fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
1633        remain, referent_id = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(
1634            pkt, s
1635        )
1636        if not self.EMBEDDED and referent_id == 0:
1637            return remain, None
1638        if self.deferred:
1639            # deferred
1640            ptr = NDRPointer(
1641                ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, referent_id=referent_id
1642            )
1643            pkt.deferred_pointers.append((ptr, partial(self.fld.getfield, pkt)))
1644            return remain, ptr
1645        remain, val = self.fld.getfield(pkt, remain)
1646        return remain, NDRPointer(
1647            ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, referent_id=referent_id, value=val
1648        )
1649
1650    def addfield(self, pkt, s, val):
1651        if val is not None and not isinstance(val, NDRPointer):
1652            raise ValueError(
1653                "Expected NDRPointer in %s. You are using it wrong!" % self.name
1654            )
1655        fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
1656        fld = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8))
1657        if not self.EMBEDDED and val is None:
1658            return fld.addfield(pkt, s, 0)
1659        else:
1660            _set_ctx_on(val.value, pkt)
1661            s = fld.addfield(pkt, s, val.referent_id)
1662        if self.deferred:
1663            # deferred
1664            pkt.deferred_pointers.append(
1665                ((lambda s: self.fld.addfield(pkt, s, val.value)), val)
1666            )
1667            return s
1668        return self.fld.addfield(pkt, s, val.value)
1669
1670    def any2i(self, pkt, x):
1671        # User-friendly helper
1672        if x is not None and not isinstance(x, NDRPointer):
1673            return NDRPointer(
1674                referent_id=0x20000,
1675                value=self.fld.any2i(pkt, x),
1676            )
1677        return x
1678
1679    # Can't use i2repr = Field.i2repr and so on on PY2 :/
1680    def i2repr(self, pkt, val):
1681        return repr(val)
1682
1683    def i2h(self, pkt, x):
1684        return x
1685
1686    def h2i(self, pkt, x):
1687        return x
1688
1689    def i2len(self, pkt, x):
1690        if x is None:
1691            return 0
1692        return self.fld.i2len(pkt, x.value)
1693
1694    def valueof(self, pkt, x):
1695        if x is None:
1696            return x
1697        return self.fld.valueof(pkt, x.value)
1698
1699
1700class NDRRefEmbPointerField(NDRFullPointerField):
1701    """
1702    A NDR Embedded Reference pointer
1703    """
1704
1705    EMBEDDED = True
1706
1707
1708# Constructed types
1709
1710
1711# Note: this is utterly complex and will drive you crazy
1712
1713# If you have a NDRPacket that contains a deferred pointer on the top level
1714# (only happens in non DCE/RPC structures, such as in MS-PAC, where you have an NDR
1715# structure encapsulated in a non-NDR structure), there will be left-over deferred
1716# pointers when exiting dissection/build (deferred pointers are only computed when
1717# reaching a field that extends NDRConstructedType, which is normal: if you follow
1718# the DCE/RPC spec, pointers are never deferred in root structures)
1719# Therefore there is a special case forcing the build/dissection of any leftover
1720# pointers in NDRPacket, if Scapy detects that they won't be handled by any parent.
1721
1722# Implementation notes: I chose to set 'handles_deferred' inside the FIELD, rather
1723# than inside the PACKET. This is faster to compute because whether a constructed type
1724# should handle deferral or not is computed only once when loading, therefore Scapy
1725# knows in advance whether to handle deferred pointers or not. But it is technically
1726# incorrect: with this approach, a structure (packet) cannot be used in 2 code paths
1727# that have different pointer managements. I mean by that that if there was a
1728# structure that was directly embedded in a RPC request without a pointer but also
1729# embedded with a pointer in another RPC request, it would break.
1730# Fortunately this isn't the case: structures are never reused for 2 purposes.
1731# (or at least I never seen that... <i hope this works>)
1732
1733
1734class NDRConstructedType(object):
1735    def __init__(self, fields):
1736        self.handles_deferred = False
1737        self.ndr_fields = fields
1738        self.rec_check_deferral()
1739
1740    def rec_check_deferral(self):
1741        # We iterate through the fields within this constructed type.
1742        # If we have a pointer, mark this field as handling deferrance
1743        # and make all sub-constructed types not.
1744        for f in self.ndr_fields:
1745            if isinstance(f, NDRFullPointerField) and f.deferred:
1746                self.handles_deferred = True
1747            if isinstance(f, NDRConstructedType):
1748                f.rec_check_deferral()
1749                if f.handles_deferred:
1750                    self.handles_deferred = True
1751                    f.handles_deferred = False
1752
1753    def getfield(self, pkt, s):
1754        s, fval = super(NDRConstructedType, self).getfield(pkt, s)
1755        if isinstance(fval, _NDRPacket):
1756            # If a sub-packet we just dissected has deferred pointers,
1757            # pass it to parent packet to propagate.
1758            pkt.deferred_pointers.extend(fval.deferred_pointers)
1759            del fval.deferred_pointers[:]
1760        if self.handles_deferred:
1761            # This field handles deferral !
1762            s = self.read_deferred_pointers(pkt, s)
1763        return s, fval
1764
1765    def read_deferred_pointers(self, pkt, s):
1766        # Now read content of the pointers that were deferred
1767        q = collections.deque()
1768        q.extend(pkt.deferred_pointers)
1769        del pkt.deferred_pointers[:]
1770        while q:
1771            # Recursively resolve pointers that were deferred
1772            ptr, getfld = q.popleft()
1773            s, val = getfld(s)
1774            ptr.value = val
1775            if isinstance(val, _NDRPacket):
1776                # Pointer resolves to a packet.. that may have deferred pointers?
1777                q.extend(val.deferred_pointers)
1778                del val.deferred_pointers[:]
1779        return s
1780
1781    def addfield(self, pkt, s, val):
1782        s = super(NDRConstructedType, self).addfield(pkt, s, val)
1783        if isinstance(val, _NDRPacket):
1784            # If a sub-packet we just dissected has deferred pointers,
1785            # pass it to parent packet to propagate.
1786            pkt.deferred_pointers.extend(val.deferred_pointers)
1787            del val.deferred_pointers[:]
1788        if self.handles_deferred:
1789            # This field handles deferral !
1790            s = self.add_deferred_pointers(pkt, s)
1791        return s
1792
1793    def add_deferred_pointers(self, pkt, s):
1794        # Now add content of pointers that were deferred
1795        q = collections.deque()
1796        q.extend(pkt.deferred_pointers)
1797        del pkt.deferred_pointers[:]
1798        while q:
1799            addfld, fval = q.popleft()
1800            s = addfld(s)
1801            if isinstance(fval, NDRPointer) and isinstance(fval.value, _NDRPacket):
1802                q.extend(fval.value.deferred_pointers)
1803                del fval.value.deferred_pointers[:]
1804        return s
1805
1806
1807class _NDRPacketField(_NDRValueOf, PacketField):
1808    def m2i(self, pkt, m):
1809        return self.cls(m, ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, _parent=pkt)
1810
1811
1812# class _NDRPacketPadField(PadField):
1813#     def padlen(self, flen, pkt):
1814#         if pkt.ndr64:
1815#             return -flen % self._align[1]
1816#         else:
1817#             return 0
1818
1819
1820class NDRPacketField(NDRConstructedType, NDRAlign):
1821    def __init__(self, name, default, pkt_cls, **kwargs):
1822        self.DEPORTED_CONFORMANTS = pkt_cls.DEPORTED_CONFORMANTS
1823        self.fld = _NDRPacketField(name, default, pkt_cls=pkt_cls, **kwargs)
1824        NDRAlign.__init__(
1825            self,
1826            # There is supposed to be padding after a struct in NDR64?
1827            # _NDRPacketPadField(fld, align=pkt_cls.ALIGNMENT),
1828            self.fld,
1829            align=pkt_cls.ALIGNMENT,
1830        )
1831        NDRConstructedType.__init__(self, pkt_cls.fields_desc)
1832
1833    def getfield(self, pkt, x):
1834        # Handle deformed conformants max_count here
1835        if self.DEPORTED_CONFORMANTS:
1836            # C706 14.3.2: "In other words, the size information precedes the
1837            # structure and is aligned independently of the structure alignment."
1838            fld = NDRInt3264Field("", 0)
1839            max_counts = []
1840            for _ in self.DEPORTED_CONFORMANTS:
1841                x, max_count = fld.getfield(pkt, x)
1842                max_counts.append(max_count)
1843            res, val = super(NDRPacketField, self).getfield(pkt, x)
1844            if len(max_counts) == 1:
1845                val.max_count = max_counts[0]
1846            else:
1847                val.max_counts = max_counts
1848            return res, val
1849        return super(NDRPacketField, self).getfield(pkt, x)
1850
1851    def addfield(self, pkt, s, x):
1852        # Handle deformed conformants max_count here
1853        if self.DEPORTED_CONFORMANTS:
1854            mcfld = NDRInt3264Field("", 0)
1855            if len(self.DEPORTED_CONFORMANTS) == 1:
1856                max_counts = [x.max_count]
1857            else:
1858                max_counts = x.max_counts
1859            for fldname, max_count in zip(self.DEPORTED_CONFORMANTS, max_counts):
1860                if max_count is None:
1861                    fld, val = x.getfield_and_val(fldname)
1862                    max_count = fld.i2len(x, val)
1863                s = mcfld.addfield(pkt, s, max_count)
1864            return super(NDRPacketField, self).addfield(pkt, s, x)
1865        return super(NDRPacketField, self).addfield(pkt, s, x)
1866
1867
1868# Array types
1869
1870
1871class _NDRPacketListField(NDRConstructedType, PacketListField):
1872    """
1873    A PacketListField for NDR that can optionally pack the packets into NDRPointers
1874    """
1875
1876    islist = 1
1877    holds_packets = 1
1878
1879    __slots__ = ["ptr_pack", "fld"]
1880
1881    def __init__(self, name, default, pkt_cls, **kwargs):
1882        self.ptr_pack = kwargs.pop("ptr_pack", False)
1883        if self.ptr_pack:
1884            self.fld = NDRFullPointerField(
1885                NDRPacketField("", None, pkt_cls), deferred=True
1886            )
1887        else:
1888            self.fld = NDRPacketField("", None, pkt_cls)
1889        PacketListField.__init__(self, name, default, pkt_cls=pkt_cls, **kwargs)
1890        NDRConstructedType.__init__(self, [self.fld])
1891
1892    def m2i(self, pkt, s):
1893        remain, val = self.fld.getfield(pkt, s)
1894        # A mistake here would be to use / instead of add_payload. It adds a copy
1895        # which breaks pointer defferal. Same applies elsewhere
1896        val.add_payload(conf.padding_layer(remain))
1897        return val
1898
1899    def any2i(self, pkt, x):
1900        # User-friendly helper
1901        if isinstance(x, list):
1902            x = [self.fld.any2i(pkt, y) for y in x]
1903        return super(_NDRPacketListField, self).any2i(pkt, x)
1904
1905    def i2m(self, pkt, val):
1906        return self.fld.addfield(pkt, b"", val)
1907
1908    def i2len(self, pkt, x):
1909        return len(x)
1910
1911    def valueof(self, pkt, x):
1912        return [self.fld.valueof(pkt, y) for y in x]
1913
1914
1915class NDRFieldListField(NDRConstructedType, FieldListField):
1916    """
1917    A FieldListField for NDR
1918    """
1919
1920    islist = 1
1921
1922    def __init__(self, *args, **kwargs):
1923        kwargs.pop("ptr_pack", None)  # TODO: unimplemented
1924        if "length_is" in kwargs:
1925            kwargs["count_from"] = kwargs.pop("length_is")
1926        elif "size_is" in kwargs:
1927            kwargs["count_from"] = kwargs.pop("size_is")
1928        FieldListField.__init__(self, *args, **kwargs)
1929        NDRConstructedType.__init__(self, [self.field])
1930
1931    def i2len(self, pkt, x):
1932        return len(x)
1933
1934    def valueof(self, pkt, x):
1935        return [self.field.valueof(pkt, y) for y in x]
1936
1937
1938class NDRVaryingArray(_NDRPacket):
1939    fields_desc = [
1940        MultipleTypeField(
1941            [(LELongField("offset", 0), lambda pkt: pkt and pkt.ndr64)],
1942            LEIntField("offset", 0),
1943        ),
1944        MultipleTypeField(
1945            [
1946                (
1947                    LELongField("actual_count", None),
1948                    lambda pkt: pkt and pkt.ndr64,
1949                )
1950            ],
1951            LEIntField("actual_count", None),
1952        ),
1953        PacketField("value", None, conf.raw_layer),
1954    ]
1955
1956
1957class _NDRVarField(object):
1958    """
1959    NDR Varying Array / String field
1960    """
1961
1962    LENGTH_FROM = False
1963    COUNT_FROM = False
1964
1965    def __init__(self, *args, **kwargs):
1966        # size is either from the length_is, if specified, or the "actual_count"
1967        self.from_actual = "length_is" not in kwargs
1968        length_is = kwargs.pop("length_is", lambda pkt: pkt.actual_count)
1969        if self.LENGTH_FROM:
1970            kwargs["length_from"] = length_is
1971        elif self.COUNT_FROM:
1972            kwargs["count_from"] = length_is
1973        super(_NDRVarField, self).__init__(*args, **kwargs)
1974
1975    def getfield(self, pkt, s):
1976        fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
1977        remain, offset = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(pkt, s)
1978        remain, actual_count = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(
1979            pkt, remain
1980        )
1981        final = NDRVaryingArray(
1982            ndr64=pkt.ndr64,
1983            ndrendian=pkt.ndrendian,
1984            offset=offset,
1985            actual_count=actual_count,
1986        )
1987        if self.from_actual:
1988            remain, val = super(_NDRVarField, self).getfield(final, remain)
1989        else:
1990            remain, val = super(_NDRVarField, self).getfield(pkt, remain)
1991        final.value = super(_NDRVarField, self).i2h(pkt, val)
1992        return remain, final
1993
1994    def addfield(self, pkt, s, val):
1995        if not isinstance(val, NDRVaryingArray):
1996            raise ValueError(
1997                "Expected NDRVaryingArray in %s. You are using it wrong!" % self.name
1998            )
1999        fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2000        _set_ctx_on(val.value, pkt)
2001        s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(pkt, s, val.offset)
2002        s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(
2003            pkt,
2004            s,
2005            val.actual_count is None
2006            and super(_NDRVarField, self).i2len(pkt, val.value)
2007            or val.actual_count,
2008        )
2009        return super(_NDRVarField, self).addfield(
2010            pkt, s, super(_NDRVarField, self).h2i(pkt, val.value)
2011        )
2012
2013    def i2len(self, pkt, x):
2014        return super(_NDRVarField, self).i2len(pkt, x.value)
2015
2016    def any2i(self, pkt, x):
2017        # User-friendly helper
2018        if not isinstance(x, NDRVaryingArray):
2019            return NDRVaryingArray(
2020                value=super(_NDRVarField, self).any2i(pkt, x),
2021            )
2022        return x
2023
2024    # Can't use i2repr = Field.i2repr and so on on PY2 :/
2025    def i2repr(self, pkt, val):
2026        return repr(val)
2027
2028    def i2h(self, pkt, x):
2029        return x
2030
2031    def h2i(self, pkt, x):
2032        return x
2033
2034    def valueof(self, pkt, x):
2035        return super(_NDRVarField, self).valueof(pkt, x.value)
2036
2037
2038class NDRConformantArray(_NDRPacket):
2039    fields_desc = [
2040        MultipleTypeField(
2041            [(LELongField("max_count", None), lambda pkt: pkt and pkt.ndr64)],
2042            LEIntField("max_count", None),
2043        ),
2044        MultipleTypeField(
2045            [
2046                (
2047                    PacketListField(
2048                        "value",
2049                        [],
2050                        conf.raw_layer,
2051                        count_from=lambda pkt: pkt.max_count,
2052                    ),
2053                    (
2054                        lambda pkt: pkt.fields.get("value", None)
2055                        and isinstance(pkt.fields["value"][0], Packet),
2056                        lambda _, val: val and isinstance(val[0], Packet),
2057                    ),
2058                )
2059            ],
2060            FieldListField(
2061                "value", [], LEIntField("", 0), count_from=lambda pkt: pkt.max_count
2062            ),
2063        ),
2064    ]
2065
2066
2067class NDRConformantString(_NDRPacket):
2068    fields_desc = [
2069        MultipleTypeField(
2070            [(LELongField("max_count", None), lambda pkt: pkt and pkt.ndr64)],
2071            LEIntField("max_count", None),
2072        ),
2073        StrField("value", ""),
2074    ]
2075
2076
2077class _NDRConfField(object):
2078    """
2079    NDR Conformant Array / String field
2080    """
2081
2082    CONFORMANT_STRING = False
2083    LENGTH_FROM = False
2084    COUNT_FROM = False
2085
2086    def __init__(self, *args, **kwargs):
2087        self.conformant_in_struct = kwargs.pop("conformant_in_struct", False)
2088        # size_is/max_is end up here, and is what defines a conformant field.
2089        if "size_is" in kwargs:
2090            size_is = kwargs.pop("size_is")
2091            if self.LENGTH_FROM:
2092                kwargs["length_from"] = size_is
2093            elif self.COUNT_FROM:
2094                kwargs["count_from"] = size_is
2095        super(_NDRConfField, self).__init__(*args, **kwargs)
2096
2097    def getfield(self, pkt, s):
2098        # [C706] - 14.3.7 Structures Containing Arrays
2099        fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2100        if self.conformant_in_struct:
2101            return super(_NDRConfField, self).getfield(pkt, s)
2102        remain, max_count = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).getfield(
2103            pkt, s
2104        )
2105        remain, val = super(_NDRConfField, self).getfield(pkt, remain)
2106        return remain, (
2107            NDRConformantString if self.CONFORMANT_STRING else NDRConformantArray
2108        )(ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, max_count=max_count, value=val)
2109
2110    def addfield(self, pkt, s, val):
2111        if self.conformant_in_struct:
2112            return super(_NDRConfField, self).addfield(pkt, s, val)
2113        if self.CONFORMANT_STRING and not isinstance(val, NDRConformantString):
2114            raise ValueError(
2115                "Expected NDRConformantString in %s. You are using it wrong!"
2116                % self.name
2117            )
2118        elif not self.CONFORMANT_STRING and not isinstance(val, NDRConformantArray):
2119            raise ValueError(
2120                "Expected NDRConformantArray in %s. You are using it wrong!" % self.name
2121            )
2122        fmt = _e(pkt.ndrendian) + ["I", "Q"][pkt.ndr64]
2123        _set_ctx_on(val.value, pkt)
2124        if val.value and isinstance(val.value[0], NDRVaryingArray):
2125            value = val.value[0]
2126        else:
2127            value = val.value
2128        s = NDRAlign(Field("", 0, fmt=fmt), align=(4, 8)).addfield(
2129            pkt,
2130            s,
2131            val.max_count is None
2132            and super(_NDRConfField, self).i2len(pkt, value)
2133            or val.max_count,
2134        )
2135        return super(_NDRConfField, self).addfield(pkt, s, value)
2136
2137    def _subval(self, x):
2138        if self.conformant_in_struct:
2139            value = x
2140        elif (
2141            not self.CONFORMANT_STRING
2142            and x.value
2143            and isinstance(x.value[0], NDRVaryingArray)
2144        ):
2145            value = x.value[0]
2146        else:
2147            value = x.value
2148        return value
2149
2150    def i2len(self, pkt, x):
2151        return super(_NDRConfField, self).i2len(pkt, self._subval(x))
2152
2153    def any2i(self, pkt, x):
2154        # User-friendly helper
2155        if self.conformant_in_struct:
2156            return super(_NDRConfField, self).any2i(pkt, x)
2157        if self.CONFORMANT_STRING and not isinstance(x, NDRConformantString):
2158            return NDRConformantString(
2159                value=super(_NDRConfField, self).any2i(pkt, x),
2160            )
2161        elif not isinstance(x, NDRConformantArray):
2162            return NDRConformantArray(
2163                value=super(_NDRConfField, self).any2i(pkt, x),
2164            )
2165        return x
2166
2167    # Can't use i2repr = Field.i2repr and so on on PY2 :/
2168    def i2repr(self, pkt, val):
2169        return repr(val)
2170
2171    def i2h(self, pkt, x):
2172        return x
2173
2174    def h2i(self, pkt, x):
2175        return x
2176
2177    def valueof(self, pkt, x):
2178        return super(_NDRConfField, self).valueof(pkt, self._subval(x))
2179
2180
2181class NDRVarPacketListField(_NDRVarField, _NDRPacketListField):
2182    """
2183    NDR Varying PacketListField. Unused
2184    """
2185
2186    COUNT_FROM = True
2187
2188
2189class NDRConfPacketListField(_NDRConfField, _NDRPacketListField):
2190    """
2191    NDR Conformant PacketListField
2192    """
2193
2194    COUNT_FROM = True
2195
2196
2197class NDRConfVarPacketListField(_NDRConfField, _NDRVarField, _NDRPacketListField):
2198    """
2199    NDR Conformant Varying PacketListField
2200    """
2201
2202    COUNT_FROM = True
2203
2204
2205class NDRConfFieldListField(_NDRConfField, NDRFieldListField):
2206    """
2207    NDR Conformant FieldListField
2208    """
2209
2210    COUNT_FROM = True
2211
2212
2213class NDRConfVarFieldListField(_NDRConfField, _NDRVarField, NDRFieldListField):
2214    """
2215    NDR Conformant Varying FieldListField
2216    """
2217
2218    COUNT_FROM = True
2219
2220
2221# NDR String fields
2222
2223
2224class _NDRUtf16(Field):
2225    def h2i(self, pkt, x):
2226        encoding = {"big": "utf-16be", "little": "utf-16le"}[pkt.ndrendian]
2227        return plain_str(x).encode(encoding)
2228
2229    def i2h(self, pkt, x):
2230        encoding = {"big": "utf-16be", "little": "utf-16le"}[pkt.ndrendian]
2231        return bytes_encode(x).decode(encoding, errors="replace")
2232
2233
2234class NDRConfStrLenField(_NDRConfField, _NDRValueOf, StrLenField):
2235    """
2236    NDR Conformant StrLenField.
2237
2238    This is not a "string" per NDR, but an a conformant byte array
2239    (e.g. tower_octet_string)
2240    """
2241
2242    CONFORMANT_STRING = True
2243    LENGTH_FROM = True
2244
2245
2246class NDRConfStrLenFieldUtf16(_NDRConfField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16):
2247    """
2248    NDR Conformant StrLenField.
2249
2250    See NDRConfLenStrField for comment.
2251    """
2252
2253    CONFORMANT_STRING = True
2254    ON_WIRE_SIZE_UTF16 = False
2255    LENGTH_FROM = True
2256
2257
2258class NDRVarStrLenField(_NDRVarField, StrLenField):
2259    """
2260    NDR Varying StrLenField
2261    """
2262
2263    LENGTH_FROM = True
2264
2265
2266class NDRVarStrLenFieldUtf16(_NDRVarField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16):
2267    """
2268    NDR Varying StrLenField
2269    """
2270
2271    ON_WIRE_SIZE_UTF16 = False
2272    LENGTH_FROM = True
2273
2274
2275class NDRConfVarStrLenField(_NDRConfField, _NDRVarField, _NDRValueOf, StrLenField):
2276    """
2277    NDR Conformant Varying StrLenField
2278    """
2279
2280    LENGTH_FROM = True
2281
2282
2283class NDRConfVarStrLenFieldUtf16(
2284    _NDRConfField, _NDRVarField, _NDRValueOf, StrLenFieldUtf16, _NDRUtf16
2285):
2286    """
2287    NDR Conformant Varying StrLenField
2288    """
2289
2290    ON_WIRE_SIZE_UTF16 = False
2291    LENGTH_FROM = True
2292
2293
2294class NDRConfVarStrNullField(_NDRConfField, _NDRVarField, _NDRValueOf, StrNullField):
2295    """
2296    NDR Conformant Varying StrNullField
2297    """
2298
2299    NULLFIELD = True
2300
2301
2302class NDRConfVarStrNullFieldUtf16(
2303    _NDRConfField, _NDRVarField, _NDRValueOf, StrNullFieldUtf16, _NDRUtf16
2304):
2305    """
2306    NDR Conformant Varying StrNullFieldUtf16
2307    """
2308
2309    ON_WIRE_SIZE_UTF16 = False
2310    NULLFIELD = True
2311
2312
2313# Union type
2314
2315
2316class NDRUnion(_NDRPacket):
2317    fields_desc = [
2318        IntField("tag", 0),
2319        PacketField("value", None, conf.raw_layer),
2320    ]
2321
2322
2323class _NDRUnionField(MultipleTypeField):
2324    __slots__ = ["switch_fmt", "align"]
2325
2326    def __init__(self, flds, dflt, align, switch_fmt):
2327        self.switch_fmt = switch_fmt
2328        self.align = align
2329        super(_NDRUnionField, self).__init__(flds, dflt)
2330
2331    def getfield(self, pkt, s):
2332        fmt = _e(pkt.ndrendian) + self.switch_fmt[pkt.ndr64]
2333        remain, tag = NDRAlign(Field("", 0, fmt=fmt), align=self.align).getfield(pkt, s)
2334        fld, _ = super(_NDRUnionField, self)._find_fld_pkt_val(pkt, NDRUnion(tag=tag))
2335        remain, val = fld.getfield(pkt, remain)
2336        return remain, NDRUnion(
2337            tag=tag, value=val, ndr64=pkt.ndr64, ndrendian=pkt.ndrendian, _parent=pkt
2338        )
2339
2340    def addfield(self, pkt, s, val):
2341        fmt = _e(pkt.ndrendian) + self.switch_fmt[pkt.ndr64]
2342        if not isinstance(val, NDRUnion):
2343            raise ValueError(
2344                "Expected NDRUnion in %s. You are using it wrong!" % self.name
2345            )
2346        _set_ctx_on(val.value, pkt)
2347        # First, align the whole tag+union against the align param
2348        s = NDRAlign(Field("", 0, fmt=fmt), align=self.align).addfield(pkt, s, val.tag)
2349        # Then, compute the subfield with its own alignment
2350        return super(_NDRUnionField, self).addfield(pkt, s, val)
2351
2352    def _find_fld_pkt_val(self, pkt, val):
2353        fld, val = super(_NDRUnionField, self)._find_fld_pkt_val(pkt, val)
2354        return fld, val.value
2355
2356    # Can't use i2repr = Field.i2repr and so on on PY2 :/
2357    def i2repr(self, pkt, val):
2358        return repr(val)
2359
2360    def i2h(self, pkt, x):
2361        return x
2362
2363    def h2i(self, pkt, x):
2364        return x
2365
2366    def valueof(self, pkt, x):
2367        fld, val = self._find_fld_pkt_val(pkt, x)
2368        return fld.valueof(pkt, x.value)
2369
2370
2371class NDRUnionField(NDRConstructedType, _NDRUnionField):
2372    def __init__(self, flds, dflt, align, switch_fmt):
2373        _NDRUnionField.__init__(self, flds, dflt, align=align, switch_fmt=switch_fmt)
2374        NDRConstructedType.__init__(self, [x[0] for x in flds] + [dflt])
2375
2376    def any2i(self, pkt, x):
2377        # User-friendly helper
2378        if x:
2379            if not isinstance(x, NDRUnion):
2380                raise ValueError("Invalid value for %s; should be NDRUnion" % self.name)
2381            else:
2382                x.value = _NDRUnionField.any2i(self, pkt, x)
2383        return x
2384
2385
2386# Misc
2387
2388
2389class NDRRecursiveField(Field):
2390    """
2391    A special Field that is used for pointer recursion
2392    """
2393
2394    def __init__(self, name, fmt="I"):
2395        super(NDRRecursiveField, self).__init__(name, None, fmt=fmt)
2396
2397    def getfield(self, pkt, s):
2398        return NDRFullPointerField(
2399            NDRPacketField("", None, pkt.__class__), deferred=True
2400        ).getfield(pkt, s)
2401
2402    def addfield(self, pkt, s, val):
2403        return NDRFullPointerField(
2404            NDRPacketField("", None, pkt.__class__), deferred=True
2405        ).addfield(pkt, s, val)
2406
2407
2408# The very few NDR-specific structures
2409
2410
2411class NDRContextHandle(NDRPacket):
2412    ALIGNMENT = (4, 4)
2413    fields_desc = [
2414        LEIntField("attributes", 0),
2415        StrFixedLenField("uuid", b"", length=16),
2416    ]
2417
2418    def guess_payload_class(self, payload):
2419        return conf.padding_layer
2420
2421
2422# --- Type Serialization Version 1 - [MSRPCE] sect 2.2.6
2423
2424
2425def _get_ndrtype1_endian(pkt):
2426    if pkt.underlayer is None:
2427        return "<"
2428    return {0x00: ">", 0x10: "<"}.get(pkt.underlayer.Endianness, "<")
2429
2430
2431class NDRSerialization1Header(Packet):
2432    fields_desc = [
2433        ByteField("Version", 1),
2434        ByteEnumField("Endianness", 0x10, {0x00: "big", 0x10: "little"}),
2435        LEShortField("CommonHeaderLength", 8),
2436        XLEIntField("Filler", 0xCCCCCCCC),
2437    ]
2438
2439
2440class NDRSerialization1PrivateHeader(Packet):
2441    fields_desc = [
2442        EField(
2443            LEIntField("ObjectBufferLength", 0), endianness_from=_get_ndrtype1_endian
2444        ),
2445        XLEIntField("Filler", 0),
2446    ]
2447
2448
2449def ndr_deserialize1(b, cls, ndr64=False):
2450    """
2451    Deserialize Type Serialization Version 1 according to [MS-RPCE] sect 2.2.6
2452    """
2453    if issubclass(cls, NDRPacket):
2454        # We use an intermediary class for two reasons:
2455        # - it properly sets deferred pointers
2456        # - it uses NDRPacketField which handles deported conformant fields
2457        class _cls(NDRPacket):
2458            fields_desc = [
2459                NDRFullPointerField(NDRPacketField("pkt", None, cls)),
2460            ]
2461
2462        hdr = NDRSerialization1Header(b[:8]) / NDRSerialization1PrivateHeader(b[8:16])
2463        endian = {0x00: "big", 0x10: "little"}[hdr.Endianness]
2464        padlen = (-hdr.ObjectBufferLength) % _TYPE1_S_PAD
2465        # padlen should be 0 (pad included in length), but some implementations
2466        # implement apparently misread the spec
2467        return (
2468            hdr
2469            / _cls(
2470                b[16 : 20 + hdr.ObjectBufferLength],
2471                ndr64=ndr64,
2472                ndrendian=endian,
2473            ).pkt
2474            / conf.padding_layer(b[20 + padlen + hdr.ObjectBufferLength :])
2475        )
2476    return NDRSerialization1Header(b[:8]) / cls(b[8:])
2477
2478
2479def ndr_serialize1(pkt):
2480    """
2481    Serialize Type Serialization Version 1
2482    """
2483    pkt = pkt.copy()
2484    endian = getattr(pkt, "ndrendian", "little")
2485    if not isinstance(pkt, NDRSerialization1Header):
2486        if not isinstance(pkt, NDRPacket):
2487            return bytes(NDRSerialization1Header(Endianness=endian) / pkt)
2488        if isinstance(pkt, NDRPointer):
2489            cls = pkt.value.__class__
2490        else:
2491            cls = pkt.__class__
2492        val = pkt
2493        pkt_len = len(pkt)
2494        # ObjectBufferLength:
2495        # > It MUST include the padding length and exclude the header itself
2496        pkt = NDRSerialization1Header(
2497            Endianness=endian
2498        ) / NDRSerialization1PrivateHeader(
2499            ObjectBufferLength=pkt_len + (-pkt_len) % _TYPE1_S_PAD
2500        )
2501    else:
2502        cls = pkt.value.__class__
2503        val = pkt.payload.payload
2504        pkt.payload.remove_payload()
2505
2506    # See above about why we need an intermediary class
2507    class _cls(NDRPacket):
2508        fields_desc = [
2509            NDRFullPointerField(NDRPacketField("pkt", None, cls)),
2510        ]
2511
2512    ret = bytes(pkt / _cls(pkt=val))
2513    return ret + (-len(ret) % _TYPE1_S_PAD) * b"\x00"
2514
2515
2516class _NDRSerializeType1:
2517    def __init__(self, *args, **kwargs):
2518        super(_NDRSerializeType1, self).__init__(*args, **kwargs)
2519
2520    def i2m(self, pkt, val):
2521        return ndr_serialize1(val)
2522
2523    def m2i(self, pkt, s):
2524        return ndr_deserialize1(s, self.cls, ndr64=False)
2525
2526    def i2len(self, pkt, val):
2527        return len(self.i2m(pkt, val))
2528
2529
2530class NDRSerializeType1PacketField(_NDRSerializeType1, PacketField):
2531    __slots__ = ["ptr"]
2532
2533
2534class NDRSerializeType1PacketLenField(_NDRSerializeType1, PacketLenField):
2535    __slots__ = ["ptr"]
2536
2537
2538class NDRSerializeType1PacketListField(_NDRSerializeType1, PacketListField):
2539    __slots__ = ["ptr"]
2540
2541
2542# --- DCE/RPC session
2543
2544
2545class DceRpcSession(DefaultSession):
2546    """
2547    A DCE/RPC session within a TCP socket.
2548    """
2549
2550    def __init__(self, *args, **kwargs):
2551        self.rpc_bind_interface = None
2552        self.ndr64 = False
2553        self.ndrendian = "little"
2554        self.support_header_signing = kwargs.pop("support_header_signing", True)
2555        self.header_sign = conf.dcerpc_force_header_signing
2556        self.ssp = kwargs.pop("ssp", None)
2557        self.sspcontext = kwargs.pop("sspcontext", None)
2558        self.auth_level = kwargs.pop("auth_level", None)
2559        self.auth_context_id = kwargs.pop("auth_context_id", 0)
2560        self.map_callid_opnum = {}
2561        self.frags = collections.defaultdict(lambda: b"")
2562        self.sniffsspcontexts = {}  # Unfinished contexts for passive
2563        if conf.dcerpc_session_enable and conf.winssps_passive:
2564            for ssp in conf.winssps_passive:
2565                self.sniffsspcontexts[ssp] = None
2566        super(DceRpcSession, self).__init__(*args, **kwargs)
2567
2568    def _up_pkt(self, pkt):
2569        """
2570        Common function to handle the DCE/RPC session: what interfaces are bind,
2571        opnums, etc.
2572        """
2573        opnum = None
2574        opts = {}
2575        if DceRpc5Bind in pkt or DceRpc5AlterContext in pkt:
2576            # bind => get which RPC interface
2577            for ctx in pkt.context_elem:
2578                if_uuid = ctx.abstract_syntax.if_uuid
2579                if_version = ctx.abstract_syntax.if_version
2580                try:
2581                    self.rpc_bind_interface = DCE_RPC_INTERFACES[(if_uuid, if_version)]
2582                except KeyError:
2583                    self.rpc_bind_interface = None
2584                    log_runtime.warning(
2585                        "Unknown RPC interface %s. Try loading the IDL" % if_uuid
2586                    )
2587        elif DceRpc5BindAck in pkt or DceRpc5AlterContextResp in pkt:
2588            # bind ack => is it NDR64
2589            for res in pkt.results:
2590                if res.result == 0:  # Accepted
2591                    self.ndrendian = {0: "big", 1: "little"}[pkt[DceRpc5].endian]
2592                    if res.transfer_syntax.sprintf("%if_uuid%") == "NDR64":
2593                        self.ndr64 = True
2594        elif DceRpc5Request in pkt:
2595            # request => match opnum with callID
2596            opnum = pkt.opnum
2597            self.map_callid_opnum[pkt.call_id] = opnum, pkt[DceRpc5Request].payload
2598        elif DceRpc5Response in pkt:
2599            # response => get opnum from table
2600            try:
2601                opnum, opts["request_packet"] = self.map_callid_opnum[pkt.call_id]
2602                del self.map_callid_opnum[pkt.call_id]
2603            except KeyError:
2604                log_runtime.info("Unknown call_id %s in DCE/RPC session" % pkt.call_id)
2605        # Bind / Alter request/response specific
2606        if (
2607            DceRpc5Bind in pkt
2608            or DceRpc5AlterContext in pkt
2609            or DceRpc5BindAck in pkt
2610            or DceRpc5AlterContextResp in pkt
2611        ):
2612            # Detect if "Header Signing" is in use
2613            if pkt.pfc_flags & 0x04:  # PFC_SUPPORT_HEADER_SIGN
2614                self.header_sign = True
2615        return opnum, opts
2616
2617    # [C706] sect 12.6.2 - Fragmentation and Reassembly
2618    # Since the connection-oriented transport guarantees sequentiality, the receiver
2619    # will always receive the fragments in order.
2620
2621    def _defragment(self, pkt):
2622        """
2623        Function to defragment DCE/RPC packets.
2624        """
2625        uid = pkt.call_id
2626        if pkt.pfc_flags.PFC_FIRST_FRAG and pkt.pfc_flags.PFC_LAST_FRAG:
2627            # Not fragmented
2628            return pkt
2629        if pkt.pfc_flags.PFC_FIRST_FRAG or uid in self.frags:
2630            # Packet is fragmented
2631            self.frags[uid] += pkt[DceRpc5].payload.payload.original
2632            if pkt.pfc_flags.PFC_LAST_FRAG:
2633                pkt[DceRpc5].payload.remove_payload()
2634                pkt[DceRpc5].payload /= self.frags[uid]
2635                return pkt
2636        else:
2637            # Not fragmented
2638            return pkt
2639
2640    def _fragment(self, pkt):
2641        """
2642        Function to fragment DCE/RPC packets.
2643        """
2644        # unimplemented
2645        pass
2646
2647    # [MS-RPCE] sect 3.3.1.5.2.2
2648
2649    # The PDU header, PDU body, and sec_trailer MUST be passed in the input message, in
2650    # this order, to GSS_WrapEx, GSS_UnwrapEx, GSS_GetMICEx, and GSS_VerifyMICEx. For
2651    # integrity protection the sign flag for that PDU segment MUST be set to TRUE, else
2652    # it MUST be set to FALSE. For confidentiality protection, the conf_req_flag for
2653    # that PDU segment MUST be set to TRUE, else it MUST be set to FALSE.
2654
2655    # If the authentication level is RPC_C_AUTHN_LEVEL_PKT_PRIVACY, the PDU body will
2656    # be encrypted.
2657    # The PDU body from the output message of GSS_UnwrapEx represents the plain text
2658    # version of the PDU body. The PDU header and sec_trailer output from the output
2659    # message SHOULD be ignored.
2660    # Similarly the signature output SHOULD be ignored.
2661
2662    def in_pkt(self, pkt):
2663        # Defragment
2664        pkt = self._defragment(pkt)
2665        if not pkt:
2666            return
2667        # Get opnum and options
2668        opnum, opts = self._up_pkt(pkt)
2669        # Check for encrypted payloads
2670        body = None
2671        if conf.raw_layer in pkt.payload:
2672            body = bytes(pkt.payload[conf.raw_layer])
2673        # If we are doing passive sniffing
2674        if conf.dcerpc_session_enable and conf.winssps_passive:
2675            # We have Windows SSPs, and no current context
2676            if pkt.auth_verifier and pkt.auth_verifier.is_ssp():
2677                # This is a bind/alter/auth3 req/resp
2678                for ssp in self.sniffsspcontexts:
2679                    self.sniffsspcontexts[ssp], status = ssp.GSS_Passive(
2680                        self.sniffsspcontexts[ssp],
2681                        pkt.auth_verifier.auth_value,
2682                    )
2683                    if status == GSS_S_COMPLETE:
2684                        self.auth_level = DCE_C_AUTHN_LEVEL(
2685                            int(pkt.auth_verifier.auth_level)
2686                        )
2687                        self.ssp = ssp
2688                        self.sspcontext = self.sniffsspcontexts[ssp]
2689                        self.sniffsspcontexts[ssp] = None
2690            elif (
2691                self.sspcontext
2692                and pkt.auth_verifier
2693                and pkt.auth_verifier.is_protected()
2694                and body
2695            ):
2696                # This is a request/response
2697                if self.sspcontext.passive:
2698                    self.ssp.GSS_Passive_set_Direction(
2699                        self.sspcontext,
2700                        IsAcceptor=DceRpc5Response in pkt,
2701                    )
2702        if pkt.auth_verifier and pkt.auth_verifier.is_protected() and body:
2703            if self.sspcontext is None:
2704                return pkt
2705            if self.auth_level in (
2706                RPC_C_AUTHN_LEVEL.PKT_INTEGRITY,
2707                RPC_C_AUTHN_LEVEL.PKT_PRIVACY,
2708            ):
2709                # note: 'vt_trailer' is included in the pdu body
2710                # [MS-RPCE] sect 2.2.2.13
2711                # "The data structures MUST only appear in a request PDU, and they
2712                # SHOULD be placed in the PDU immediately after the stub data but
2713                # before the authentication padding octets. Therefore, for security
2714                # purposes, the verification trailer is considered part of the PDU
2715                # body."
2716                if pkt.vt_trailer:
2717                    body += bytes(pkt.vt_trailer)
2718                # Account for padding when computing checksum/encryption
2719                if pkt.auth_padding:
2720                    body += pkt.auth_padding
2721
2722                # Build pdu_header and sec_trailer
2723                pdu_header = pkt.copy()
2724                sec_trailer = pdu_header.auth_verifier
2725                # sec_trailer: include the sec_trailer but not the Authentication token
2726                authval_len = len(sec_trailer.auth_value)
2727                # Discard everything out of the header
2728                pdu_header.auth_padding = None
2729                pdu_header.auth_verifier = None
2730                pdu_header.payload.payload = NoPayload()
2731                pdu_header.vt_trailer = None
2732
2733                # [MS-RPCE] sect 2.2.2.12
2734                if self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY:
2735                    _msgs = self.ssp.GSS_UnwrapEx(
2736                        self.sspcontext,
2737                        [
2738                            # "PDU header"
2739                            SSP.WRAP_MSG(
2740                                conf_req_flag=False,
2741                                sign=self.header_sign,
2742                                data=bytes(pdu_header),
2743                            ),
2744                            # "PDU body"
2745                            SSP.WRAP_MSG(
2746                                conf_req_flag=True,
2747                                sign=True,
2748                                data=body,
2749                            ),
2750                            # "sec_trailer"
2751                            SSP.WRAP_MSG(
2752                                conf_req_flag=False,
2753                                sign=self.header_sign,
2754                                data=bytes(sec_trailer)[:-authval_len],
2755                            ),
2756                        ],
2757                        pkt.auth_verifier.auth_value,
2758                    )
2759                    body = _msgs[1].data  # PDU body
2760                elif self.auth_level == RPC_C_AUTHN_LEVEL.PKT_INTEGRITY:
2761                    self.ssp.GSS_VerifyMICEx(
2762                        self.sspcontext,
2763                        [
2764                            # "PDU header"
2765                            SSP.MIC_MSG(
2766                                sign=self.header_sign,
2767                                data=bytes(pdu_header),
2768                            ),
2769                            # "PDU body"
2770                            SSP.MIC_MSG(
2771                                sign=True,
2772                                data=body,
2773                            ),
2774                            # "sec_trailer"
2775                            SSP.MIC_MSG(
2776                                sign=self.header_sign,
2777                                data=bytes(sec_trailer)[:-authval_len],
2778                            ),
2779                        ],
2780                        pkt.auth_verifier.auth_value,
2781                    )
2782                # Put padding back into the header
2783                if pkt.auth_padding:
2784                    padlen = len(pkt.auth_padding)
2785                    body, pkt.auth_padding = body[:-padlen], body[-padlen:]
2786                # Put back vt_trailer into the header
2787                if pkt.vt_trailer:
2788                    vtlen = len(pkt.vt_trailer)
2789                    body, pkt.vt_trailer = body[:-vtlen], body[-vtlen:]
2790        # Try to parse the payload
2791        if opnum is not None and self.rpc_bind_interface:
2792            # use opnum to parse the payload
2793            is_response = DceRpc5Response in pkt
2794            try:
2795                cls = self.rpc_bind_interface.opnums[opnum][is_response]
2796            except KeyError:
2797                log_runtime.warning(
2798                    "Unknown opnum %s for interface %s"
2799                    % (opnum, self.rpc_bind_interface)
2800                )
2801                pkt.payload[conf.raw_layer].load = body
2802                return pkt
2803            if body:
2804                # Dissect payload using class
2805                payload = cls(body, ndr64=self.ndr64, ndrendian=self.ndrendian, **opts)
2806                pkt.payload[conf.raw_layer].underlayer.remove_payload()
2807                pkt /= payload
2808            elif not cls.fields_desc:
2809                # Request class has no payload
2810                pkt /= cls(ndr64=self.ndr64, ndrendian=self.ndrendian, **opts)
2811        elif body:
2812            pkt.payload[conf.raw_layer].load = body
2813        return pkt
2814
2815    def out_pkt(self, pkt):
2816        assert DceRpc5 in pkt
2817        self._up_pkt(pkt)
2818        if pkt.auth_verifier is not None:
2819            # Verifier already set
2820            return [pkt]
2821        if self.sspcontext and isinstance(
2822            pkt.payload, (DceRpc5Request, DceRpc5Response)
2823        ):
2824            body = bytes(pkt.payload.payload)
2825            signature = None
2826            if self.auth_level in (
2827                RPC_C_AUTHN_LEVEL.PKT_INTEGRITY,
2828                RPC_C_AUTHN_LEVEL.PKT_PRIVACY,
2829            ):
2830                # Account for padding when computing checksum/encryption
2831                if pkt.auth_padding is None:
2832                    padlen = (-len(body)) % _COMMON_AUTH_PAD  # authdata padding
2833                    pkt.auth_padding = b"\x00" * padlen
2834                else:
2835                    padlen = len(pkt.auth_padding)
2836                # Remember that vt_trailer is included in the PDU
2837                if pkt.vt_trailer:
2838                    body += bytes(pkt.vt_trailer)
2839                # Remember that padding IS SIGNED & ENCRYPTED
2840                body += pkt.auth_padding
2841                # Add the auth_verifier
2842                pkt.auth_verifier = CommonAuthVerifier(
2843                    auth_type=self.ssp.auth_type,
2844                    auth_level=self.auth_level,
2845                    auth_context_id=self.auth_context_id,
2846                    auth_pad_length=padlen,
2847                    # Note: auth_value should have the correct length because when
2848                    # using PFC_SUPPORT_HEADER_SIGN, auth_len (and frag_len) is
2849                    # included in the token.. but this creates a dependency loop as
2850                    # you'd need to know the token length to compute the token.
2851                    # Windows solves this by setting the 'Maximum Signature Length'
2852                    # (or something similar) beforehand, instead of the real length.
2853                    # See `gensec_sig_size` in samba.
2854                    auth_value=b"\x00"
2855                    * self.ssp.MaximumSignatureLength(self.sspcontext),
2856                )
2857                # Build pdu_header and sec_trailer
2858                pdu_header = pkt.copy()
2859                pdu_header.auth_len = len(pdu_header.auth_verifier) - 8
2860                pdu_header.frag_len = len(pdu_header)
2861                sec_trailer = pdu_header.auth_verifier
2862                # sec_trailer: include the sec_trailer but not the Authentication token
2863                authval_len = len(sec_trailer.auth_value)
2864                # sec_trailer.auth_value = None
2865                # Discard everything out of the header
2866                pdu_header.auth_padding = None
2867                pdu_header.auth_verifier = None
2868                pdu_header.payload.payload = NoPayload()
2869                pdu_header.vt_trailer = None
2870                signature = None
2871                # [MS-RPCE] sect 2.2.2.12
2872                if self.auth_level == RPC_C_AUTHN_LEVEL.PKT_PRIVACY:
2873                    _msgs, signature = self.ssp.GSS_WrapEx(
2874                        self.sspcontext,
2875                        [
2876                            # "PDU header"
2877                            SSP.WRAP_MSG(
2878                                conf_req_flag=False,
2879                                sign=self.header_sign,
2880                                data=bytes(pdu_header),
2881                            ),
2882                            # "PDU body"
2883                            SSP.WRAP_MSG(
2884                                conf_req_flag=True,
2885                                sign=True,
2886                                data=body,
2887                            ),
2888                            # "sec_trailer"
2889                            SSP.WRAP_MSG(
2890                                conf_req_flag=False,
2891                                sign=self.header_sign,
2892                                data=bytes(sec_trailer)[:-authval_len],
2893                            ),
2894                        ],
2895                    )
2896                    s = _msgs[1].data  # PDU body
2897                elif self.auth_level == RPC_C_AUTHN_LEVEL.PKT_INTEGRITY:
2898                    signature = self.ssp.GSS_GetMICEx(
2899                        self.sspcontext,
2900                        [
2901                            # "PDU header"
2902                            SSP.MIC_MSG(
2903                                sign=self.header_sign,
2904                                data=bytes(pdu_header),
2905                            ),
2906                            # "PDU body"
2907                            SSP.MIC_MSG(
2908                                sign=True,
2909                                data=body,
2910                            ),
2911                            # "sec_trailer"
2912                            SSP.MIC_MSG(
2913                                sign=self.header_sign,
2914                                data=bytes(sec_trailer)[:-authval_len],
2915                            ),
2916                        ],
2917                        pkt.auth_verifier.auth_value,
2918                    )
2919                    s = body
2920                else:
2921                    raise ValueError("Impossible")
2922                # Put padding back in the header
2923                if padlen:
2924                    s, pkt.auth_padding = s[:-padlen], s[-padlen:]
2925                # Put back vt_trailer into the header
2926                if pkt.vt_trailer:
2927                    vtlen = len(pkt.vt_trailer)
2928                    s, pkt.vt_trailer = s[:-vtlen], s[-vtlen:]
2929            else:
2930                s = body
2931
2932            # now inject the encrypted payload into the packet
2933            pkt.payload.payload = conf.raw_layer(load=s)
2934            # and the auth_value
2935            if signature:
2936                pkt.auth_verifier.auth_value = signature
2937            else:
2938                pkt.auth_verifier = None
2939        return [pkt]
2940
2941    def process(self, pkt: Packet) -> Optional[Packet]:
2942        pkt = super(DceRpcSession, self).process(pkt)
2943        if pkt is not None and DceRpc5 in pkt:
2944            return self.in_pkt(pkt)
2945        return pkt
2946
2947
2948class DceRpcSocket(StreamSocket):
2949    """
2950    A Wrapper around StreamSocket that uses a DceRpcSession
2951    """
2952
2953    def __init__(self, *args, **kwargs):
2954        self.session = DceRpcSession(
2955            ssp=kwargs.pop("ssp", None),
2956            auth_level=kwargs.pop("auth_level", None),
2957            auth_context_id=kwargs.pop("auth_context_id", None),
2958            support_header_signing=kwargs.pop("support_header_signing", True),
2959        )
2960        super(DceRpcSocket, self).__init__(*args, **kwargs)
2961
2962    def send(self, x, **kwargs):
2963        for pkt in self.session.out_pkt(x):
2964            return super(DceRpcSocket, self).send(pkt, **kwargs)
2965
2966    def recv(self, x=None):
2967        pkt = super(DceRpcSocket, self).recv(x)
2968        if pkt is not None:
2969            return self.session.in_pkt(pkt)
2970
2971
2972# --- TODO cleanup below
2973
2974# Heuristically way to find the payload class
2975#
2976# To add a possible payload to a DCE/RPC packet, one must first create the
2977# packet class, then instead of binding layers using bind_layers, he must
2978# call DceRpcPayload.register_possible_payload() with the payload class as
2979# parameter.
2980#
2981# To be able to decide if the payload class is capable of handling the rest of
2982# the dissection, the classmethod can_handle() should be implemented in the
2983# payload class. This method is given the rest of the string to dissect as
2984# first argument, and the DceRpc packet instance as second argument. Based on
2985# this information, the method must return True if the class is capable of
2986# handling the dissection, False otherwise
2987
2988
2989class DceRpc4Payload(Packet):
2990    """Dummy class which use the dispatch_hook to find the payload class"""
2991
2992    _payload_class = []
2993
2994    @classmethod
2995    def dispatch_hook(cls, _pkt, _underlayer=None, *args, **kargs):
2996        """dispatch_hook to choose among different registered payloads"""
2997        for klass in cls._payload_class:
2998            if hasattr(klass, "can_handle") and klass.can_handle(_pkt, _underlayer):
2999                return klass
3000        log_runtime.warning("DCE/RPC payload class not found or undefined (using Raw)")
3001        return Raw
3002
3003    @classmethod
3004    def register_possible_payload(cls, pay):
3005        """Method to call from possible DCE/RPC endpoint to register it as
3006        possible payload"""
3007        cls._payload_class.append(pay)
3008
3009
3010bind_layers(DceRpc4, DceRpc4Payload)
3011