• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) Gabriel Potter
5
6"""
7[MS-PAC]
8
9https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/166d8064-c863-41e1-9c23-edaaa5f36962
10Up to date with version: 23.0
11"""
12
13import struct
14
15from scapy.config import conf
16from scapy.error import log_runtime
17from scapy.fields import (
18    ConditionalField,
19    FieldLenField,
20    FieldListField,
21    FlagsField,
22    LEIntEnumField,
23    LELongField,
24    LEIntField,
25    LEShortField,
26    MultipleTypeField,
27    PacketField,
28    PacketListField,
29    StrField,
30    StrFieldUtf16,
31    StrFixedLenField,
32    StrLenFieldUtf16,
33    UTCTimeField,
34    XStrField,
35    XStrLenField,
36)
37from scapy.packet import Packet
38from scapy.layers.kerberos import (
39    _AUTHORIZATIONDATA_VALUES,
40    _KRB_S_TYPES,
41)
42from scapy.layers.dcerpc import (
43    NDRByteField,
44    NDRConfFieldListField,
45    NDRConfPacketListField,
46    NDRConfStrLenField,
47    NDRConfVarStrLenFieldUtf16,
48    NDRConfVarStrNullFieldUtf16,
49    NDRConformantString,
50    NDRFieldListField,
51    NDRFullPointerField,
52    NDRInt3264EnumField,
53    NDRIntField,
54    NDRLongField,
55    NDRPacket,
56    NDRPacketField,
57    NDRSerialization1Header,
58    NDRSerializeType1PacketLenField,
59    NDRShortField,
60    NDRSignedLongField,
61    NDRUnionField,
62    _NDRConfField,
63    ndr_deserialize1,
64    ndr_serialize1,
65)
66from scapy.layers.ntlm import (
67    _NTLMPayloadField,
68    _NTLMPayloadPacket,
69)
70from scapy.layers.smb2 import WINNT_SID
71
72# sect 2.4
73
74
75class PAC_INFO_BUFFER(Packet):
76    fields_desc = [
77        LEIntEnumField(
78            "ulType",
79            0x00000001,
80            {
81                0x00000001: "Logon information",
82                0x00000002: "Credentials information",
83                0x00000006: "Server Signature",
84                0x00000007: "KDC Signature",
85                0x0000000A: "Client name and ticket information",
86                0x0000000B: "Constrained delegation information",
87                0x0000000C: "UPN and DNS information",
88                0x0000000D: "Client claims information",
89                0x0000000E: "Device information",
90                0x0000000F: "Device claims information",
91                0x00000010: "Ticket Signature",
92                0x00000011: "PAC Attributes",
93                0x00000012: "PAC Requestor",
94                0x00000013: "Extended KDC Signature",
95            },
96        ),
97        LEIntField("cbBufferSize", None),
98        LELongField("Offset", None),
99    ]
100
101    def default_payload_class(self, payload):
102        return conf.padding_layer
103
104
105_PACTYPES = {}
106
107
108# sect 2.5 - NDR PACKETS
109
110
111class RPC_UNICODE_STRING(NDRPacket):
112    ALIGNMENT = (4, 8)
113    fields_desc = [
114        NDRShortField("Length", None, size_of="Buffer", adjust=lambda _, x: (x * 2)),
115        NDRShortField(
116            "MaximumLength", None, size_of="Buffer", adjust=lambda _, x: (x * 2)
117        ),
118        NDRFullPointerField(
119            NDRConfVarStrLenFieldUtf16(
120                "Buffer",
121                "",
122                size_is=lambda pkt: (pkt.MaximumLength // 2),
123                length_is=lambda pkt: (pkt.Length // 2),
124            ),
125            deferred=True,
126        ),
127    ]
128
129
130class FILETIME(NDRPacket):
131    ALIGNMENT = (4, 4)
132    fields_desc = [NDRIntField("dwLowDateTime", 0), NDRIntField("dwHighDateTime", 0)]
133
134
135class GROUP_MEMBERSHIP(NDRPacket):
136    ALIGNMENT = (4, 4)
137    fields_desc = [NDRIntField("RelativeId", 0), NDRIntField("Attributes", 0)]
138
139
140class CYPHER_BLOCK(NDRPacket):
141    fields_desc = [StrFixedLenField("data", "", length=8)]
142
143
144class USER_SESSION_KEY(NDRPacket):
145    fields_desc = [PacketListField("data", [], CYPHER_BLOCK, count_from=lambda _: 2)]
146
147
148class RPC_SID_IDENTIFIER_AUTHORITY(NDRPacket):
149    fields_desc = [StrFixedLenField("Value", "", length=6)]
150
151
152class SID(NDRPacket):
153    ALIGNMENT = (4, 8)
154    DEPORTED_CONFORMANTS = ["SubAuthority"]
155    fields_desc = [
156        NDRByteField("Revision", 0),
157        NDRByteField("SubAuthorityCount", None, size_of="SubAuthority"),
158        NDRPacketField(
159            "IdentifierAuthority",
160            RPC_SID_IDENTIFIER_AUTHORITY(),
161            RPC_SID_IDENTIFIER_AUTHORITY,
162        ),
163        NDRConfFieldListField(
164            "SubAuthority",
165            [],
166            NDRIntField("", 0),
167            size_is=lambda pkt: pkt.SubAuthorityCount,
168            conformant_in_struct=True,
169        ),
170    ]
171
172    def summary(self):
173        return WINNT_SID.summary(self)
174
175
176class KERB_SID_AND_ATTRIBUTES(NDRPacket):
177    ALIGNMENT = (4, 8)
178    fields_desc = [
179        NDRFullPointerField(NDRPacketField("Sid", SID(), SID), deferred=True),
180        NDRIntField("Attributes", 0),
181    ]
182
183
184class KERB_VALIDATION_INFO(NDRPacket):
185    ALIGNMENT = (4, 8)
186    fields_desc = [
187        NDRPacketField("LogonTime", FILETIME(), FILETIME),
188        NDRPacketField("LogoffTime", FILETIME(), FILETIME),
189        NDRPacketField("KickOffTime", FILETIME(), FILETIME),
190        NDRPacketField("PasswordLastSet", FILETIME(), FILETIME),
191        NDRPacketField("PasswordCanChange", FILETIME(), FILETIME),
192        NDRPacketField("PasswordMustChange", FILETIME(), FILETIME),
193        NDRPacketField("EffectiveName", RPC_UNICODE_STRING(), RPC_UNICODE_STRING),
194        NDRPacketField("FullName", RPC_UNICODE_STRING(), RPC_UNICODE_STRING),
195        NDRPacketField("LogonScript", RPC_UNICODE_STRING(), RPC_UNICODE_STRING),
196        NDRPacketField("ProfilePath", RPC_UNICODE_STRING(), RPC_UNICODE_STRING),
197        NDRPacketField("HomeDirectory", RPC_UNICODE_STRING(), RPC_UNICODE_STRING),
198        NDRPacketField("HomeDirectoryDrive", RPC_UNICODE_STRING(), RPC_UNICODE_STRING),
199        NDRShortField("LogonCount", 0),
200        NDRShortField("BadPasswordCount", 0),
201        NDRIntField("UserId", 0),
202        NDRIntField("PrimaryGroupId", 0),
203        NDRIntField("GroupCount", None, size_of="GroupIds"),
204        NDRFullPointerField(
205            NDRConfPacketListField(
206                "GroupIds",
207                [GROUP_MEMBERSHIP()],
208                GROUP_MEMBERSHIP,
209                size_is=lambda pkt: pkt.GroupCount,
210            ),
211            deferred=True,
212        ),
213        NDRIntField("UserFlags", 0),
214        NDRPacketField("UserSessionKey", USER_SESSION_KEY(), USER_SESSION_KEY),
215        NDRPacketField("LogonServer", RPC_UNICODE_STRING(), RPC_UNICODE_STRING),
216        NDRPacketField("LogonDomainName", RPC_UNICODE_STRING(), RPC_UNICODE_STRING),
217        NDRFullPointerField(NDRPacketField("LogonDomainId", SID(), SID), deferred=True),
218        NDRFieldListField("Reserved1", [], NDRIntField("", 0), length_is=lambda _: 2),
219        NDRIntField("UserAccountControl", 0),
220        NDRFieldListField("Reserved3", [], NDRIntField("", 0), length_is=lambda _: 7),
221        NDRIntField("SidCount", None, size_of="ExtraSids"),
222        NDRFullPointerField(
223            NDRConfPacketListField(
224                "ExtraSids",
225                [KERB_SID_AND_ATTRIBUTES()],
226                KERB_SID_AND_ATTRIBUTES,
227                size_is=lambda pkt: pkt.SidCount,
228            ),
229            deferred=True,
230        ),
231        NDRFullPointerField(
232            NDRPacketField("ResourceGroupDomainSid", SID(), SID), deferred=True
233        ),
234        NDRIntField("ResourceGroupCount", None, size_of="ResourceGroupIds"),
235        NDRFullPointerField(
236            NDRConfPacketListField(
237                "ResourceGroupIds",
238                [GROUP_MEMBERSHIP()],
239                GROUP_MEMBERSHIP,
240                size_is=lambda pkt: pkt.ResourceGroupCount,
241            ),
242            deferred=True,
243        ),
244    ]
245
246
247_PACTYPES[1] = KERB_VALIDATION_INFO
248
249# sect 2.6
250
251
252class PAC_CREDENTIAL_INFO(Packet):
253    fields_desc = [
254        LEIntField("Version", 0),
255        LEIntEnumField(
256            "EncryptionType",
257            1,
258            {
259                0x00000001: "DES-CBC-CRC",
260                0x00000003: "DES-CBC-MD5",
261                0x00000011: "AES128_CTS_HMAC_SHA1_96",
262                0x00000012: "AES256_CTS_HMAC_SHA1_96",
263                0x00000017: "RC4-HMAC",
264            },
265        ),
266        XStrField("SerializedData", b""),
267    ]
268
269
270_PACTYPES[2] = PAC_CREDENTIAL_INFO
271
272# sect 2.7
273
274
275class PAC_CLIENT_INFO(Packet):
276    fields_desc = [
277        UTCTimeField(
278            "ClientId", None, fmt="<Q", epoch=[1601, 1, 1, 0, 0, 0], custom_scaling=1e7
279        ),
280        FieldLenField("NameLength", None, length_of="Name", fmt="<H"),
281        StrLenFieldUtf16("Name", b"", length_from=lambda pkt: pkt.NameLength),
282    ]
283
284
285_PACTYPES[0xA] = PAC_CLIENT_INFO
286
287# sect 2.8
288
289
290class PAC_SIGNATURE_DATA(Packet):
291    fields_desc = [
292        LEIntEnumField(
293            "SignatureType",
294            None,
295            _KRB_S_TYPES,
296        ),
297        XStrLenField(
298            "Signature",
299            b"",
300            length_from=lambda pkt: {
301                0x1: 4,
302                0xFFFFFF76: 16,
303                0x0000000F: 12,
304                0x00000010: 12,
305            }.get(pkt.SignatureType, 0),
306        ),
307        StrField("RODCIdentifier", b""),
308    ]
309
310
311_PACTYPES[6] = PAC_SIGNATURE_DATA
312_PACTYPES[7] = PAC_SIGNATURE_DATA
313_PACTYPES[0x10] = PAC_SIGNATURE_DATA
314_PACTYPES[0x13] = PAC_SIGNATURE_DATA
315
316# sect 2.9
317
318
319class S4U_DELEGATION_INFO(NDRPacket):
320    ALIGNMENT = (4, 8)
321    fields_desc = [
322        NDRPacketField("S4U2proxyTarget", RPC_UNICODE_STRING(), RPC_UNICODE_STRING),
323        NDRIntField("TransitedListSize", None, size_of="S4UTransitedServices"),
324        NDRFullPointerField(
325            NDRConfPacketListField(
326                "S4UTransitedServices",
327                [RPC_UNICODE_STRING()],
328                RPC_UNICODE_STRING,
329                size_is=lambda pkt: pkt.TransitedListSize,
330            ),
331            deferred=True,
332        ),
333    ]
334
335
336# sect 2.10
337
338
339def _pac_post_build(self, p, pay_offset, fields):
340    """Util function to build the offset and populate the lengths"""
341    for field_name, value in self.fields["Payload"]:
342        length = self.get_field("Payload").fields_map[field_name].i2len(self, value)
343        offset = fields[field_name]
344        # Length
345        if self.getfieldval(field_name + "Len") is None:
346            p = p[:offset] + struct.pack("<H", length) + p[offset + 2 :]
347        # Offset
348        if self.getfieldval(field_name + "BufferOffset") is None:
349            p = p[: offset + 2] + struct.pack("<H", pay_offset) + p[offset + 4 :]
350        pay_offset += length
351    return p
352
353
354class UPN_DNS_INFO(_NTLMPayloadPacket):
355    fields_desc = [
356        LEShortField("UpnLen", None),
357        LEShortField("UpnBufferOffset", None),
358        LEShortField("DnsDomainNameLen", None),
359        LEShortField("DnsDomainNameBufferOffset", None),
360        FlagsField(
361            "Flags",
362            0,
363            -32,
364            [
365                "U",
366                "S",  # Extended
367            ],
368        ),
369        ConditionalField(
370            # Extended
371            LEShortField("SamNameLen", None),
372            lambda pkt: pkt.Flags.S,
373        ),
374        ConditionalField(
375            # Extended
376            LEShortField("SamNameBufferOffset", None),
377            lambda pkt: pkt.Flags.S,
378        ),
379        ConditionalField(
380            # Extended
381            LEShortField("SidLen", None),
382            lambda pkt: pkt.Flags.S,
383        ),
384        ConditionalField(
385            # Extended
386            LEShortField("SidBufferOffset", None),
387            lambda pkt: pkt.Flags.S,
388        ),
389        MultipleTypeField(
390            [
391                (
392                    # Extended
393                    _NTLMPayloadField(
394                        "Payload",
395                        20,
396                        [
397                            StrFieldUtf16("Upn", b""),
398                            StrFieldUtf16("DnsDomainName", b""),
399                            StrFieldUtf16("SamName", b""),
400                            PacketField("Sid", WINNT_SID(), WINNT_SID),
401                        ],
402                    ),
403                    lambda pkt: pkt.Flags.S,
404                )
405            ],
406            # Not-extended
407            _NTLMPayloadField(
408                "Payload",
409                12,
410                [
411                    StrFieldUtf16("Upn", b""),
412                    StrFieldUtf16("DnsDomainName", b""),
413                ],
414            ),
415        ),
416    ]
417
418    def post_build(self, pkt, pay):
419        # type: (bytes, bytes) -> bytes
420        offset = 12
421        fields = {
422            "Upn": 0,
423            "DnsDomainName": 4,
424        }
425        if self.Flags.S:
426            offset = 20
427            fields["SamName"] = 12
428            fields["Sid"] = 16
429        return (
430            _pac_post_build(
431                self,
432                pkt,
433                offset,
434                fields,
435            )
436            + pay
437        )
438
439
440_PACTYPES[0xC] = UPN_DNS_INFO
441
442# sect 2.11 - NDR PACKETS
443
444try:
445    from enum import IntEnum
446except ImportError:
447    IntEnum = object
448
449
450class CLAIM_TYPE(IntEnum):
451    CLAIM_TYPE_INT64 = 1
452    CLAIM_TYPE_UINT64 = 2
453    CLAIM_TYPE_STRING = 3
454    CLAIM_TYPE_BOOLEAN = 6
455
456
457class CLAIMS_SOURCE_TYPE(IntEnum):
458    CLAIMS_SOURCE_TYPE_AD = 1
459    CLAIMS_SOURCE_TYPE_CERTIFICATE = 2
460
461
462class CLAIMS_COMPRESSION_FORMAT(IntEnum):
463    COMPRESSION_FORMAT_NONE = 0
464    COMPRESSION_FORMAT_LZNT1 = 2
465    COMPRESSION_FORMAT_XPRESS = 3
466    COMPRESSION_FORMAT_XPRESS_HUFF = 4
467
468
469class CLAIM_ENTRY_sub0(NDRPacket):
470    ALIGNMENT = (4, 8)
471    fields_desc = [
472        NDRIntField("ValueCount", None, size_of="Int64Values"),
473        NDRFullPointerField(
474            NDRConfFieldListField(
475                "Int64Values",
476                [],
477                NDRSignedLongField,
478                size_is=lambda pkt: pkt.ValueCount,
479            ),
480            deferred=True,
481        ),
482    ]
483
484
485class CLAIM_ENTRY_sub1(NDRPacket):
486    ALIGNMENT = (4, 8)
487    fields_desc = [
488        NDRIntField("ValueCount", None, size_of="Uint64Values"),
489        NDRFullPointerField(
490            NDRConfFieldListField(
491                "Uint64Values", [], NDRLongField, size_is=lambda pkt: pkt.ValueCount
492            ),
493            deferred=True,
494        ),
495    ]
496
497
498class CLAIM_ENTRY_sub2(NDRPacket):
499    ALIGNMENT = (4, 8)
500    fields_desc = [
501        NDRIntField("ValueCount", None, size_of="StringValues"),
502        NDRFullPointerField(
503            NDRConfFieldListField(
504                "StringValues",
505                [],
506                NDRFullPointerField(
507                    NDRConfVarStrNullFieldUtf16("StringVal", ""),
508                    deferred=True,
509                ),
510                size_is=lambda pkt: pkt.ValueCount,
511            ),
512            deferred=True,
513        ),
514    ]
515
516
517class CLAIM_ENTRY_sub3(NDRPacket):
518    ALIGNMENT = (4, 8)
519    fields_desc = [
520        NDRIntField("ValueCount", None, size_of="BooleanValues"),
521        NDRFullPointerField(
522            NDRConfFieldListField(
523                "BooleanValues", [], NDRLongField, size_is=lambda pkt: pkt.ValueCount
524            ),
525            deferred=True,
526        ),
527    ]
528
529
530class CLAIM_ENTRY(NDRPacket):
531    ALIGNMENT = (4, 8)
532    fields_desc = [
533        NDRFullPointerField(NDRConfVarStrNullFieldUtf16("Id", ""), deferred=True),
534        NDRInt3264EnumField("Type", 0, CLAIM_TYPE),
535        NDRUnionField(
536            [
537                (
538                    NDRPacketField("Values", CLAIM_ENTRY_sub0(), CLAIM_ENTRY_sub0),
539                    (
540                        (
541                            lambda pkt: getattr(pkt, "Type", None)
542                            == CLAIM_TYPE.CLAIM_TYPE_INT64
543                        ),
544                        (lambda _, val: val.tag == CLAIM_TYPE.CLAIM_TYPE_INT64),
545                    ),
546                ),
547                (
548                    NDRPacketField("Values", CLAIM_ENTRY_sub1(), CLAIM_ENTRY_sub1),
549                    (
550                        (
551                            lambda pkt: getattr(pkt, "Type", None)
552                            == CLAIM_TYPE.CLAIM_TYPE_UINT64
553                        ),
554                        (lambda _, val: val.tag == CLAIM_TYPE.CLAIM_TYPE_UINT64),
555                    ),
556                ),
557                (
558                    NDRPacketField("Values", CLAIM_ENTRY_sub2(), CLAIM_ENTRY_sub2),
559                    (
560                        (
561                            lambda pkt: getattr(pkt, "Type", None)
562                            == CLAIM_TYPE.CLAIM_TYPE_STRING
563                        ),
564                        (lambda _, val: val.tag == CLAIM_TYPE.CLAIM_TYPE_STRING),
565                    ),
566                ),
567                (
568                    NDRPacketField("Values", CLAIM_ENTRY_sub3(), CLAIM_ENTRY_sub3),
569                    (
570                        (
571                            lambda pkt: getattr(pkt, "Type", None)
572                            == CLAIM_TYPE.CLAIM_TYPE_BOOLEAN
573                        ),
574                        (lambda _, val: val.tag == CLAIM_TYPE.CLAIM_TYPE_BOOLEAN),
575                    ),
576                ),
577            ],
578            StrFixedLenField("Values", "", length=0),
579            align=(2, 8),
580            switch_fmt=("H", "I"),
581        ),
582    ]
583
584
585class CLAIMS_ARRAY(NDRPacket):
586    ALIGNMENT = (4, 8)
587    fields_desc = [
588        NDRInt3264EnumField("usClaimsSourceType", 0, CLAIMS_SOURCE_TYPE),
589        NDRIntField("ulClaimsCount", None, size_of="ClaimEntries"),
590        NDRFullPointerField(
591            NDRConfPacketListField(
592                "ClaimEntries",
593                [CLAIM_ENTRY()],
594                CLAIM_ENTRY,
595                size_is=lambda pkt: pkt.ulClaimsCount,
596            ),
597            deferred=True,
598        ),
599    ]
600
601
602class CLAIMS_SET(NDRPacket):
603    ALIGNMENT = (4, 8)
604    fields_desc = [
605        NDRIntField("ulClaimsArrayCount", None, size_of="ClaimsArrays"),
606        NDRFullPointerField(
607            NDRConfPacketListField(
608                "ClaimsArrays",
609                [CLAIMS_ARRAY()],
610                CLAIMS_ARRAY,
611                size_is=lambda pkt: pkt.ulClaimsArrayCount,
612            ),
613            deferred=True,
614        ),
615        NDRShortField("usReservedType", 0),
616        NDRIntField("ulReservedFieldSize", None, size_of="ReservedField"),
617        NDRFullPointerField(
618            NDRConfStrLenField(
619                "ReservedField", "", size_is=lambda pkt: pkt.ulReservedFieldSize
620            ),
621            deferred=True,
622        ),
623    ]
624
625
626class _CLAIMSClaimSet(_NDRConfField, NDRSerializeType1PacketLenField):
627    CONFORMANT_STRING = True
628    LENGTH_FROM = True
629
630    def m2i(self, pkt, s):
631        if pkt.usCompressionFormat == CLAIMS_COMPRESSION_FORMAT.COMPRESSION_FORMAT_NONE:
632            return ndr_deserialize1(s, CLAIMS_SET, ndr64=False)
633        else:
634            # TODO: There are 3 funky compression formats... see sect 2.2.18.4
635            return NDRConformantString(value=s)
636
637    def i2m(self, pkt, val):
638        val = val[0]
639        if pkt.usCompressionFormat == CLAIMS_COMPRESSION_FORMAT.COMPRESSION_FORMAT_NONE:
640            return ndr_serialize1(val)
641        else:
642            # funky
643            return bytes(val)
644
645    def valueof(self, pkt, x):
646        if pkt.usCompressionFormat == CLAIMS_COMPRESSION_FORMAT.COMPRESSION_FORMAT_NONE:
647            return self._subval(x)[0]
648        else:
649            return x
650
651
652class CLAIMS_SET_METADATA(NDRPacket):
653    ALIGNMENT = (4, 8)
654    fields_desc = [
655        NDRIntField("ulClaimsSetSize", None, size_of="ClaimsSet"),
656        NDRFullPointerField(
657            _CLAIMSClaimSet(
658                "ClaimsSet", None, None, size_is=lambda pkt: pkt.ulClaimsSetSize
659            ),
660            deferred=True,
661        ),
662        NDRInt3264EnumField(
663            "usCompressionFormat",
664            0,
665            CLAIMS_COMPRESSION_FORMAT,
666        ),
667        # this size_of is technically wrong. we just assume it's uncompressed...
668        NDRIntField("ulUncompressedClaimsSetSize", None, size_of="ClaimsSet"),
669        NDRShortField("usReservedType", 0),
670        NDRIntField("ulReservedFieldSize", None, size_of="ReservedField"),
671        NDRFullPointerField(
672            NDRConfStrLenField(
673                "ReservedField", "", size_is=lambda pkt: pkt.ulReservedFieldSize
674            ),
675            deferred=True,
676        ),
677    ]
678
679
680class PAC_CLIENT_CLAIMS_INFO(NDRPacket):
681    fields_desc = [NDRPacketField("Claims", CLAIMS_SET_METADATA(), CLAIMS_SET_METADATA)]
682
683
684if IntEnum != object:
685    # If not available, ignore. I can't be bothered
686    _PACTYPES[0xD] = PAC_CLIENT_CLAIMS_INFO
687
688
689# sect 2.12 - NDR PACKETS
690
691
692class DOMAIN_GROUP_MEMBERSHIP(NDRPacket):
693    ALIGNMENT = (4, 8)
694    fields_desc = [
695        NDRFullPointerField(NDRPacketField("DomainId", SID(), SID), deferred=True),
696        NDRIntField("GroupCount", 0),
697        NDRFullPointerField(
698            NDRConfPacketListField(
699                "GroupIds",
700                [GROUP_MEMBERSHIP()],
701                GROUP_MEMBERSHIP,
702                size_is=lambda pkt: pkt.GroupCount,
703            ),
704            deferred=True,
705        ),
706    ]
707
708
709class PAC_DEVICE_INFO(NDRPacket):
710    ALIGNMENT = (4, 8)
711    fields_desc = [
712        NDRIntField("UserId", 0),
713        NDRIntField("PrimaryGroupId", 0),
714        NDRFullPointerField(
715            NDRPacketField("AccountDomainId", SID(), SID), deferred=True
716        ),
717        NDRIntField("AccountGroupCount", 0),
718        NDRFullPointerField(
719            NDRConfPacketListField(
720                "AccountGroupIds",
721                [GROUP_MEMBERSHIP()],
722                GROUP_MEMBERSHIP,
723                size_is=lambda pkt: pkt.AccountGroupCount,
724            ),
725            deferred=True,
726        ),
727        NDRIntField("SidCount", 0),
728        NDRFullPointerField(
729            NDRConfPacketListField(
730                "ExtraSids",
731                [KERB_SID_AND_ATTRIBUTES()],
732                KERB_SID_AND_ATTRIBUTES,
733                size_is=lambda pkt: pkt.SidCount,
734            ),
735            deferred=True,
736        ),
737        NDRIntField("DomainGroupCount", 0),
738        NDRFullPointerField(
739            NDRConfPacketListField(
740                "DomainGroup",
741                [DOMAIN_GROUP_MEMBERSHIP()],
742                DOMAIN_GROUP_MEMBERSHIP,
743                size_is=lambda pkt: pkt.DomainGroupCount,
744            ),
745            deferred=True,
746        ),
747    ]
748
749
750_PACTYPES[0xE] = PAC_DEVICE_INFO
751
752# sect 2.14 - PAC_ATTRIBUTES_INFO
753
754
755class PAC_ATTRIBUTES_INFO(Packet):
756    fields_desc = [
757        LEIntField("FlagsLength", 2),
758        FieldListField(
759            "Flags",
760            ["PAC_WAS_REQUESTED"],
761            FlagsField(
762                "",
763                0,
764                -32,
765                {
766                    0x00000001: "PAC_WAS_REQUESTED",
767                    0x00000002: "PAC_WAS_GIVEN_IMPLICITLY",
768                },
769            ),
770            count_from=lambda pkt: (pkt.FlagsLength + 7) // 8,
771        ),
772    ]
773
774
775_PACTYPES[0x11] = PAC_ATTRIBUTES_INFO
776
777# sect 2.15 - PAC_REQUESTOR
778
779
780class PAC_REQUESTOR(Packet):
781    fields_desc = [
782        PacketField("Sid", WINNT_SID(), WINNT_SID),
783    ]
784
785
786_PACTYPES[0x12] = PAC_REQUESTOR
787
788# sect 2.3
789
790
791class _PACTYPEBuffers(PacketListField):
792    def addfield(self, pkt, s, val):
793        # we use this field to set Offset and cbBufferSize
794        res = b""
795        if len(val) != len(pkt.Payloads):
796            log_runtime.warning("Size of 'Buffers' does not match size of 'Payloads' !")
797            return super(_PACTYPEBuffers, self).addfield(pkt, s, val)
798        offset = 16 * len(pkt.Payloads) + 8
799        for i, v in enumerate(val):
800            x = self.i2m(pkt, v)
801            pay = pkt.Payloads[i]
802            if isinstance(pay, NDRPacket) or isinstance(pay, NDRSerialization1Header):
803                lgth = len(ndr_serialize1(pay))
804            else:
805                lgth = len(pay)
806            if v.cbBufferSize is None:
807                x = x[:4] + struct.pack("<I", lgth) + x[8:]
808            if v.Offset is None:
809                x = x[:8] + struct.pack("<Q", offset) + x[16:]
810            offset += lgth
811            offset += (-offset) % 8  # Account for padding
812            res += x
813        return s + res
814
815
816class _PACTYPEPayloads(PacketListField):
817    def i2m(self, pkt, val):
818        if isinstance(val, NDRPacket) or isinstance(val, NDRSerialization1Header):
819            s = ndr_serialize1(val)
820        else:
821            s = bytes(val)
822        return s + b"\x00" * ((-len(s)) % 8)
823
824    def getfield(self, pkt, s):
825        if not pkt or not s:
826            return s, []
827        result = []
828        for i in range(len(pkt.Buffers)):
829            buf = pkt.Buffers[i]
830            offset = buf.Offset - 16 * len(pkt.Buffers) - 8
831            try:
832                cls = _PACTYPES[buf.ulType]
833                if buf.cbBufferSize == 0:
834                    # empty size
835                    raise KeyError
836                if issubclass(cls, NDRPacket):
837                    val = ndr_deserialize1(
838                        s[offset : offset + buf.cbBufferSize],
839                        cls,
840                        ndr64=False,
841                    )
842                else:
843                    val = cls(s[offset : offset + buf.cbBufferSize])
844                if conf.raw_layer in val:
845                    pad = conf.padding_layer(load=val[conf.raw_layer].load)
846                    lay = val[conf.raw_layer].underlayer
847                    if not lay:
848                        val.show()
849                        raise ValueError("Dissection failed")
850                    lay.remove_payload()
851                    lay.add_payload(pad)
852            except KeyError:
853                val = conf.padding_layer(s[offset : offset + buf.cbBufferSize])
854            result.append(val)
855        return b"", result
856
857
858class PACTYPE(Packet):
859    name = "PACTYPE - PAC"
860    fields_desc = [
861        FieldLenField("cBuffers", None, count_of="Buffers", fmt="<I"),
862        LEIntField("Version", 0x00000000),
863        _PACTYPEBuffers(
864            "Buffers",
865            [PAC_INFO_BUFFER()],
866            PAC_INFO_BUFFER,
867            count_from=lambda pkt: pkt.cBuffers,
868        ),
869        _PACTYPEPayloads("Payloads", [], None),
870    ]
871
872
873_AUTHORIZATIONDATA_VALUES[128] = PACTYPE  # AD-WIN2K-PAC
874