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
6r"""
7Kerberos V5
8
9Implements parts of:
10
11- Kerberos Network Authentication Service (V5): RFC4120
12- Kerberos Version 5 GSS-API: RFC1964, RFC4121
13- Kerberos Pre-Authentication: RFC6113 (FAST)
14- Kerberos Principal Name Canonicalization and Cross-Realm Referrals: RFC6806
15- Microsoft Windows 2000 Kerberos Change Password and Set Password Protocols: RFC3244
16- User to User Kerberos Authentication: draft-ietf-cat-user2user-03
17- Public Key Cryptography Based User-to-User Authentication (PKU2U): draft-zhu-pku2u-09
18- Initial and Pass Through Authentication Using Kerberos V5 (IAKERB):
19 draft-ietf-kitten-iakerb-03
20- Kerberos Protocol Extensions: [MS-KILE]
21- Kerberos Protocol Extensions: Service for User: [MS-SFU]
22- Kerberos Key Distribution Center Proxy Protocol: [MS-KKDCP]
23
24
25.. note::
26 You will find more complete documentation for this layer over at
27 `Kerberos <https://scapy.readthedocs.io/en/latest/layers/kerberos.html>`_
28
29Example decryption::
30
31 >>> from scapy.libs.rfc3961 import Key, EncryptionType
32 >>> pkt = Ether(hex_bytes("525400695813525400216c2b08004500015da71840008006dc\
33 83c0a87a9cc0a87a11c209005854f6ab2392c25bd650182014b6e00000000001316a8201\
34 2d30820129a103020105a20302010aa3633061304ca103020102a24504433041a0030201\
35 12a23a043848484decb01c9b62a1cabfbc3f2d1ed85aa5e093ba8358a8cea34d4393af93\
36 bf211e274fa58e814878db9f0d7a28d94e7327660db4f3704b3011a10402020080a20904\
37 073005a0030101ffa481b73081b4a00703050040810010a1123010a003020101a1093007\
38 1b0577696e3124a20e1b0c444f4d41494e2e4c4f43414ca321301fa003020102a1183016\
39 1b066b72627467741b0c444f4d41494e2e4c4f43414ca511180f32303337303931333032\
40 343830355aa611180f32303337303931333032343830355aa7060204701cc5d1a8153013\
41 0201120201110201170201180202ff79020103a91d301b3019a003020114a11204105749\
42 4e31202020202020202020202020"))
43 >>> enc = pkt[Kerberos].root.padata[0].padataValue
44 >>> k = Key(enc.etype.val, key=hex_bytes("7fada4e566ae4fb270e2800a23a\
45 e87127a819d42e69b5e22de0ddc63da80096d"))
46 >>> enc.decrypt(k)
47"""
48
49from collections import namedtuple, deque
50from datetime import datetime, timedelta, timezone
51from enum import IntEnum
52
53import os
54import re
55import socket
56import struct
57
58from scapy.error import warning
59import scapy.asn1.mib # noqa: F401
60from scapy.asn1.ber import BER_id_dec, BER_Decoding_Error
61from scapy.asn1.asn1 import (
62 ASN1_BIT_STRING,
63 ASN1_BOOLEAN,
64 ASN1_Class,
65 ASN1_GENERAL_STRING,
66 ASN1_GENERALIZED_TIME,
67 ASN1_INTEGER,
68 ASN1_STRING,
69 ASN1_Codecs,
70)
71from scapy.asn1fields import (
72 ASN1F_BOOLEAN,
73 ASN1F_CHOICE,
74 ASN1F_FLAGS,
75 ASN1F_GENERAL_STRING,
76 ASN1F_GENERALIZED_TIME,
77 ASN1F_INTEGER,
78 ASN1F_OID,
79 ASN1F_PACKET,
80 ASN1F_SEQUENCE,
81 ASN1F_SEQUENCE_OF,
82 ASN1F_STRING,
83 ASN1F_STRING_PacketField,
84 ASN1F_enum_INTEGER,
85 ASN1F_optional,
86)
87from scapy.asn1packet import ASN1_Packet
88from scapy.automaton import Automaton, ATMT
89from scapy.config import conf
90from scapy.compat import bytes_encode
91from scapy.error import log_runtime
92from scapy.fields import (
93 ConditionalField,
94 FieldLenField,
95 FlagsField,
96 IntEnumField,
97 LEIntEnumField,
98 LenField,
99 LEShortEnumField,
100 LEShortField,
101 LongField,
102 MultipleTypeField,
103 PacketField,
104 PacketLenField,
105 PacketListField,
106 PadField,
107 ShortEnumField,
108 ShortField,
109 StrField,
110 StrFieldUtf16,
111 StrFixedLenEnumField,
112 XByteField,
113 XLEIntField,
114 XLEIntEnumField,
115 XLEShortField,
116 XStrFixedLenField,
117 XStrLenField,
118 XStrField,
119)
120from scapy.packet import Packet, bind_bottom_up, bind_top_down, bind_layers
121from scapy.supersocket import StreamSocket, SuperSocket
122from scapy.utils import strrot, strxor
123from scapy.volatile import GeneralizedTime, RandNum, RandBin
124
125from scapy.layers.gssapi import (
126 GSSAPI_BLOB,
127 GSS_C_FLAGS,
128 GSS_C_NO_CHANNEL_BINDINGS,
129 GSS_S_BAD_BINDINGS,
130 GSS_S_BAD_MECH,
131 GSS_S_COMPLETE,
132 GSS_S_CONTINUE_NEEDED,
133 GSS_S_DEFECTIVE_TOKEN,
134 GSS_S_FAILURE,
135 GSS_S_FLAGS,
136 GssChannelBindings,
137 SSP,
138 _GSSAPI_OIDS,
139 _GSSAPI_SIGNATURE_OIDS,
140)
141from scapy.layers.inet import TCP, UDP
142from scapy.layers.smb import _NV_VERSION
143from scapy.layers.smb2 import STATUS_ERREF
144from scapy.layers.x509 import X509_AlgorithmIdentifier
145
146# Typing imports
147from typing import (
148 Optional,
149)
150
151
152# kerberos APPLICATION
153
154
155class ASN1_Class_KRB(ASN1_Class):
156 name = "Kerberos"
157 # APPLICATION + CONSTRUCTED = 0x40 | 0x20
158 Token = 0x60 | 0 # GSSAPI
159 Ticket = 0x60 | 1
160 Authenticator = 0x60 | 2
161 EncTicketPart = 0x60 | 3
162 AS_REQ = 0x60 | 10
163 AS_REP = 0x60 | 11
164 TGS_REQ = 0x60 | 12
165 TGS_REP = 0x60 | 13
166 AP_REQ = 0x60 | 14
167 AP_REP = 0x60 | 15
168 PRIV = 0x60 | 21
169 CRED = 0x60 | 22
170 EncASRepPart = 0x60 | 25
171 EncTGSRepPart = 0x60 | 26
172 EncAPRepPart = 0x60 | 27
173 EncKrbPrivPart = 0x60 | 28
174 EncKrbCredPart = 0x60 | 29
175 ERROR = 0x60 | 30
176
177
178# RFC4120 sect 5.2
179
180
181KerberosString = ASN1F_GENERAL_STRING
182Realm = KerberosString
183Int32 = ASN1F_INTEGER
184UInt32 = ASN1F_INTEGER
185
186_PRINCIPAL_NAME_TYPES = {
187 0: "NT-UNKNOWN",
188 1: "NT-PRINCIPAL",
189 2: "NT-SRV-INST",
190 3: "NT-SRV-HST",
191 4: "NT-SRV-XHST",
192 5: "NT-UID",
193 6: "NT-X500-PRINCIPAL",
194 7: "NT-SMTP-NAME",
195 10: "NT-ENTERPRISE",
196}
197
198
199class PrincipalName(ASN1_Packet):
200 ASN1_codec = ASN1_Codecs.BER
201 ASN1_root = ASN1F_SEQUENCE(
202 ASN1F_enum_INTEGER(
203 "nameType",
204 0,
205 _PRINCIPAL_NAME_TYPES,
206 explicit_tag=0xA0,
207 ),
208 ASN1F_SEQUENCE_OF("nameString", [], KerberosString, explicit_tag=0xA1),
209 )
210
211 def toString(self):
212 """
213 Convert a PrincipalName back into its string representation.
214 """
215 return "/".join(x.val.decode() for x in self.nameString)
216
217 @staticmethod
218 def fromUPN(upn: str):
219 """
220 Create a PrincipalName from a UPN string.
221 """
222 user, _ = _parse_upn(upn)
223 return PrincipalName(
224 nameString=[ASN1_GENERAL_STRING(user)],
225 nameType=ASN1_INTEGER(1), # NT-PRINCIPAL
226 )
227
228 @staticmethod
229 def fromSPN(spn: str):
230 """
231 Create a PrincipalName from a SPN string.
232 """
233 spn, _ = _parse_spn(spn)
234 if spn.startswith("krbtgt"):
235 return PrincipalName(
236 nameString=[ASN1_GENERAL_STRING(x) for x in spn.split("/")],
237 nameType=ASN1_INTEGER(2), # NT-SRV-INST
238 )
239 elif "/" in spn:
240 return PrincipalName(
241 nameString=[ASN1_GENERAL_STRING(x) for x in spn.split("/")],
242 nameType=ASN1_INTEGER(3), # NT-SRV-HST
243 )
244 else:
245 # In case of U2U
246 return PrincipalName(
247 nameString=[ASN1_GENERAL_STRING(spn)],
248 nameType=ASN1_INTEGER(1), # NT-PRINCIPAL
249 )
250
251
252KerberosTime = ASN1F_GENERALIZED_TIME
253Microseconds = ASN1F_INTEGER
254
255
256# https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-1
257
258_KRB_E_TYPES = {
259 1: "DES-CBC-CRC",
260 2: "DES-CBC-MD4",
261 3: "DES-CBC-MD5",
262 5: "DES3-CBC-MD5",
263 7: "DES3-CBC-SHA1",
264 9: "DSAWITHSHA1-CMSOID",
265 10: "MD5WITHRSAENCRYPTION-CMSOID",
266 11: "SHA1WITHRSAENCRYPTION-CMSOID",
267 12: "RC2CBC-ENVOID",
268 13: "RSAENCRYPTION-ENVOID",
269 14: "RSAES-OAEP-ENV-OID",
270 15: "DES-EDE3-CBC-ENV-OID",
271 16: "DES3-CBC-SHA1-KD",
272 17: "AES128-CTS-HMAC-SHA1-96",
273 18: "AES256-CTS-HMAC-SHA1-96",
274 19: "AES128-CTS-HMAC-SHA256-128",
275 20: "AES256-CTS-HMAC-SHA384-192",
276 23: "RC4-HMAC",
277 24: "RC4-HMAC-EXP",
278 25: "CAMELLIA128-CTS-CMAC",
279 26: "CAMELLIA256-CTS-CMAC",
280}
281
282# https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-2
283
284_KRB_S_TYPES = {
285 1: "CRC32",
286 2: "RSA-MD4",
287 3: "RSA-MD4-DES",
288 4: "DES-MAC",
289 5: "DES-MAC-K",
290 6: "RSA-MD4-DES-K",
291 7: "RSA-MD5",
292 8: "RSA-MD5-DES",
293 9: "RSA-MD5-DES3",
294 10: "SHA1",
295 12: "HMAC-SHA1-DES3-KD",
296 13: "HMAC-SHA1-DES3",
297 14: "SHA1",
298 15: "HMAC-SHA1-96-AES128",
299 16: "HMAC-SHA1-96-AES256",
300 17: "CMAC-CAMELLIA128",
301 18: "CMAC-CAMELLIA256",
302 19: "HMAC-SHA256-128-AES128",
303 20: "HMAC-SHA384-192-AES256",
304 # RFC 4121
305 0x8003: "KRB-AUTHENTICATOR",
306 # [MS-KILE]
307 0xFFFFFF76: "MD5",
308 -138: "MD5",
309}
310
311
312class EncryptedData(ASN1_Packet):
313 ASN1_codec = ASN1_Codecs.BER
314 ASN1_root = ASN1F_SEQUENCE(
315 ASN1F_enum_INTEGER("etype", 0x17, _KRB_E_TYPES, explicit_tag=0xA0),
316 ASN1F_optional(UInt32("kvno", None, explicit_tag=0xA1)),
317 ASN1F_STRING("cipher", "", explicit_tag=0xA2),
318 )
319
320 def get_usage(self):
321 """
322 Get current key usage number and encrypted class
323 """
324 # RFC 4120 sect 7.5.1
325 if self.underlayer:
326 if isinstance(self.underlayer, PADATA):
327 patype = self.underlayer.padataType
328 if patype == 2:
329 # AS-REQ PA-ENC-TIMESTAMP padata timestamp
330 return 1, PA_ENC_TS_ENC
331 elif patype == 138:
332 # RFC6113 PA-ENC-TS-ENC
333 return 54, PA_ENC_TS_ENC
334 elif isinstance(self.underlayer, KRB_Ticket):
335 # AS-REP Ticket and TGS-REP Ticket
336 return 2, EncTicketPart
337 elif isinstance(self.underlayer, KRB_AS_REP):
338 # AS-REP encrypted part
339 return 3, EncASRepPart
340 elif isinstance(self.underlayer, KRB_AP_REQ) and isinstance(
341 self.underlayer.underlayer, PADATA
342 ):
343 # TGS-REQ PA-TGS-REQ Authenticator
344 return 7, KRB_Authenticator
345 elif isinstance(self.underlayer, KRB_TGS_REP):
346 # TGS-REP encrypted part
347 return 8, EncTGSRepPart
348 elif isinstance(self.underlayer, KRB_AP_REQ):
349 # AP-REQ Authenticator
350 return 11, KRB_Authenticator
351 elif isinstance(self.underlayer, KRB_AP_REP):
352 # AP-REP encrypted part
353 return 12, EncAPRepPart
354 elif isinstance(self.underlayer, KRB_PRIV):
355 # KRB-PRIV encrypted part
356 return 13, EncKrbPrivPart
357 elif isinstance(self.underlayer, KRB_CRED):
358 # KRB-CRED encrypted part
359 return 14, EncKrbCredPart
360 elif isinstance(self.underlayer, KrbFastArmoredReq):
361 # KEY_USAGE_FAST_ENC
362 return 51, KrbFastReq
363 elif isinstance(self.underlayer, KrbFastArmoredRep):
364 # KEY_USAGE_FAST_REP
365 return 52, KrbFastResponse
366 raise ValueError(
367 "Could not guess key usage number. Please specify key_usage_number"
368 )
369
370 def decrypt(self, key, key_usage_number=None, cls=None):
371 """
372 Decrypt and return the data contained in cipher.
373
374 :param key: the key to use for decryption
375 :param key_usage_number: (optional) specify the key usage number.
376 Guessed otherwise
377 :param cls: (optional) the class of the decrypted payload
378 Guessed otherwise (or bytes)
379 """
380 if key_usage_number is None:
381 key_usage_number, cls = self.get_usage()
382 d = key.decrypt(key_usage_number, self.cipher.val)
383 if cls:
384 try:
385 return cls(d)
386 except BER_Decoding_Error:
387 if cls == EncASRepPart:
388 # https://datatracker.ietf.org/doc/html/rfc4120#section-5.4.2
389 # "Compatibility note: Some implementations unconditionally send an
390 # encrypted EncTGSRepPart (application tag number 26) in this field
391 # regardless of whether the reply is a AS-REP or a TGS-REP. In the
392 # interest of compatibility, implementors MAY relax the check on the
393 # tag number of the decrypted ENC-PART."
394 try:
395 res = EncTGSRepPart(d)
396 # https://github.com/krb5/krb5/blob/48ccd81656381522d1f9ccb8705c13f0266a46ab/src/lib/krb5/asn.1/asn1_k_encode.c#L1128
397 # This is a bug because as the RFC clearly says above, we're
398 # perfectly in our right to be strict on this. (MAY)
399 log_runtime.warning(
400 "Implementation bug detected. This looks like MIT Kerberos."
401 )
402 return res
403 except BER_Decoding_Error:
404 pass
405 raise
406 return d
407
408 def encrypt(self, key, text, confounder=None, key_usage_number=None):
409 """
410 Encrypt text and set it into cipher.
411
412 :param key: the key to use for encryption
413 :param text: the bytes value to encode
414 :param confounder: (optional) specify the confounder bytes. Random otherwise
415 :param key_usage_number: (optional) specify the key usage number.
416 Guessed otherwise
417 """
418 if key_usage_number is None:
419 key_usage_number = self.get_usage()[0]
420 self.etype = key.etype
421 self.cipher = ASN1_STRING(
422 key.encrypt(key_usage_number, text, confounder=confounder)
423 )
424
425
426class EncryptionKey(ASN1_Packet):
427 ASN1_codec = ASN1_Codecs.BER
428 ASN1_root = ASN1F_SEQUENCE(
429 ASN1F_enum_INTEGER("keytype", 0, _KRB_E_TYPES, explicit_tag=0xA0),
430 ASN1F_STRING("keyvalue", "", explicit_tag=0xA1),
431 )
432
433 def toKey(self):
434 from scapy.libs.rfc3961 import Key
435
436 return Key(
437 etype=self.keytype.val,
438 key=self.keyvalue.val,
439 )
440
441 @classmethod
442 def fromKey(self, key):
443 return EncryptionKey(
444 keytype=key.etype,
445 keyvalue=key.key,
446 )
447
448
449class _Checksum_Field(ASN1F_STRING_PacketField):
450 def m2i(self, pkt, s):
451 val = super(_Checksum_Field, self).m2i(pkt, s)
452 if not val[0].val:
453 return val
454 if pkt.cksumtype.val == 0x8003:
455 # Special case per RFC 4121
456 return KRB_AuthenticatorChecksum(val[0].val, _underlayer=pkt), val[1]
457 return val
458
459
460class Checksum(ASN1_Packet):
461 ASN1_codec = ASN1_Codecs.BER
462 ASN1_root = ASN1F_SEQUENCE(
463 ASN1F_enum_INTEGER(
464 "cksumtype",
465 0,
466 _KRB_S_TYPES,
467 explicit_tag=0xA0,
468 ),
469 _Checksum_Field("checksum", "", explicit_tag=0xA1),
470 )
471
472 def get_usage(self):
473 """
474 Get current key usage number
475 """
476 # RFC 4120 sect 7.5.1
477 if self.underlayer:
478 if isinstance(self.underlayer, KRB_Authenticator):
479 # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator cksum
480 # (n°10 should never happen as we use RFC4121)
481 return 6
482 elif isinstance(self.underlayer, PA_FOR_USER):
483 # [MS-SFU] sect 2.2.1
484 return 17
485 elif isinstance(self.underlayer, PA_S4U_X509_USER):
486 # [MS-SFU] sect 2.2.2
487 return 26
488 elif isinstance(self.underlayer, AD_KDCIssued):
489 # AD-KDC-ISSUED checksum
490 return 19
491 elif isinstance(self.underlayer, KrbFastArmoredReq):
492 # KEY_USAGE_FAST_REQ_CHKSUM
493 return 50
494 elif isinstance(self.underlayer, KrbFastFinished):
495 # KEY_USAGE_FAST_FINISHED
496 return 53
497 raise ValueError(
498 "Could not guess key usage number. Please specify key_usage_number"
499 )
500
501 def verify(self, key, text, key_usage_number=None):
502 """
503 Decrypt and return the data contained in cipher.
504
505 :param key: the key to use to check the checksum
506 :param text: the bytes to verify
507 :param key_usage_number: (optional) specify the key usage number.
508 Guessed otherwise
509 """
510 if key_usage_number is None:
511 key_usage_number = self.get_usage()
512 key.verify_checksum(key_usage_number, text, self.checksum.val)
513
514 def make(self, key, text, key_usage_number=None, cksumtype=None):
515 """
516 Encrypt text and set it into cipher.
517
518 :param key: the key to use to make the checksum
519 :param text: the bytes to make a checksum of
520 :param key_usage_number: (optional) specify the key usage number.
521 Guessed otherwise
522 """
523 if key_usage_number is None:
524 key_usage_number = self.get_usage()
525 self.cksumtype = cksumtype or key.cksumtype
526 self.checksum = ASN1_STRING(
527 key.make_checksum(
528 keyusage=key_usage_number,
529 text=text,
530 cksumtype=self.cksumtype,
531 )
532 )
533
534
535KerberosFlags = ASN1F_FLAGS
536
537_ADDR_TYPES = {
538 # RFC4120 sect 7.5.3
539 0x02: "IPv4",
540 0x03: "Directional",
541 0x05: "ChaosNet",
542 0x06: "XNS",
543 0x07: "ISO",
544 0x0C: "DECNET Phase IV",
545 0x10: "AppleTalk DDP",
546 0x14: "NetBios",
547 0x18: "IPv6",
548}
549
550
551class HostAddress(ASN1_Packet):
552 ASN1_codec = ASN1_Codecs.BER
553 ASN1_root = ASN1F_SEQUENCE(
554 ASN1F_enum_INTEGER(
555 "addrType",
556 0,
557 _ADDR_TYPES,
558 explicit_tag=0xA0,
559 ),
560 ASN1F_STRING("address", "", explicit_tag=0xA1),
561 )
562
563
564HostAddresses = lambda name, **kwargs: ASN1F_SEQUENCE_OF(
565 name, [], HostAddress, **kwargs
566)
567
568
569_AUTHORIZATIONDATA_VALUES = {
570 # Filled below
571}
572
573
574class _AuthorizationData_value_Field(ASN1F_STRING_PacketField):
575 def m2i(self, pkt, s):
576 val = super(_AuthorizationData_value_Field, self).m2i(pkt, s)
577 if not val[0].val:
578 return val
579 if pkt.adType.val in _AUTHORIZATIONDATA_VALUES:
580 return (
581 _AUTHORIZATIONDATA_VALUES[pkt.adType.val](val[0].val, _underlayer=pkt),
582 val[1],
583 )
584 return val
585
586
587_AD_TYPES = {
588 # RFC4120 sect 7.5.4
589 1: "AD-IF-RELEVANT",
590 2: "AD-INTENDED-FOR-SERVER",
591 3: "AD-INTENDED-FOR-APPLICATION-CLASS",
592 4: "AD-KDC-ISSUED",
593 5: "AD-AND-OR",
594 6: "AD-MANDATORY-TICKET-EXTENSIONS",
595 7: "AD-IN-TICKET-EXTENSIONS",
596 8: "AD-MANDATORY-FOR-KDC",
597 64: "OSF-DCE",
598 65: "SESAME",
599 66: "AD-OSD-DCE-PKI-CERTID",
600 128: "AD-WIN2K-PAC",
601 129: "AD-ETYPE-NEGOTIATION",
602 # [MS-KILE] additions
603 141: "KERB-AUTH-DATA-TOKEN-RESTRICTIONS",
604 142: "KERB-LOCAL",
605 143: "AD-AUTH-DATA-AP-OPTIONS",
606 144: "KERB-AUTH-DATA-CLIENT-TARGET",
607}
608
609
610class AuthorizationDataItem(ASN1_Packet):
611 ASN1_codec = ASN1_Codecs.BER
612 ASN1_root = ASN1F_SEQUENCE(
613 ASN1F_enum_INTEGER(
614 "adType",
615 0,
616 _AD_TYPES,
617 explicit_tag=0xA0,
618 ),
619 _AuthorizationData_value_Field("adData", "", explicit_tag=0xA1),
620 )
621
622
623class AuthorizationData(ASN1_Packet):
624 ASN1_codec = ASN1_Codecs.BER
625 ASN1_root = ASN1F_SEQUENCE_OF(
626 "seq", [AuthorizationDataItem()], AuthorizationDataItem
627 )
628
629 def getAuthData(self, adType):
630 return next((x.adData for x in self.seq if x.adType == adType), None)
631
632
633AD_IF_RELEVANT = AuthorizationData
634_AUTHORIZATIONDATA_VALUES[1] = AD_IF_RELEVANT
635
636
637class AD_KDCIssued(ASN1_Packet):
638 ASN1_codec = ASN1_Codecs.BER
639 ASN1_root = ASN1F_SEQUENCE(
640 ASN1F_PACKET("adChecksum", Checksum(), Checksum, explicit_tag=0xA0),
641 ASN1F_optional(
642 Realm("iRealm", "", explicit_tag=0xA1),
643 ),
644 ASN1F_optional(ASN1F_PACKET("iSname", None, PrincipalName, explicit_tag=0xA2)),
645 ASN1F_PACKET("elements", None, AuthorizationData, explicit_tag=0xA3),
646 )
647
648
649_AUTHORIZATIONDATA_VALUES[4] = AD_KDCIssued
650
651
652class AD_AND_OR(ASN1_Packet):
653 ASN1_codec = ASN1_Codecs.BER
654 ASN1_root = ASN1F_SEQUENCE(
655 Int32("conditionCount", 0, explicit_tag=0xA0),
656 ASN1F_PACKET("elements", None, AuthorizationData, explicit_tag=0xA1),
657 )
658
659
660_AUTHORIZATIONDATA_VALUES[5] = AD_AND_OR
661
662ADMANDATORYFORKDC = AuthorizationData
663_AUTHORIZATIONDATA_VALUES[8] = ADMANDATORYFORKDC
664
665
666# https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xml
667_PADATA_TYPES = {
668 1: "PA-TGS-REQ",
669 2: "PA-ENC-TIMESTAMP",
670 3: "PA-PW-SALT",
671 11: "PA-ETYPE-INFO",
672 14: "PA-PK-AS-REQ-OLD",
673 15: "PA-PK-AS-REP-OLD",
674 16: "PA-PK-AS-REQ",
675 17: "PA-PK-AS-REP",
676 19: "PA-ETYPE-INFO2",
677 20: "PA-SVR-REFERRAL-INFO",
678 111: "TD-CMS-DIGEST-ALGORITHMS",
679 128: "PA-PAC-REQUEST",
680 129: "PA-FOR-USER",
681 130: "PA-FOR-X509-USER",
682 131: "PA-FOR-CHECK_DUPS",
683 132: "PA-AS-CHECKSUM",
684 133: "PA-FX-COOKIE",
685 134: "PA-AUTHENTICATION-SET",
686 135: "PA-AUTH-SET-SELECTED",
687 136: "PA-FX-FAST",
688 137: "PA-FX-ERROR",
689 138: "PA-ENCRYPTED-CHALLENGE",
690 141: "PA-OTP-CHALLENGE",
691 142: "PA-OTP-REQUEST",
692 143: "PA-OTP-CONFIRM",
693 144: "PA-OTP-PIN-CHANGE",
694 145: "PA-EPAK-AS-REQ",
695 146: "PA-EPAK-AS-REP",
696 147: "PA-PKINIT-KX",
697 148: "PA-PKU2U-NAME",
698 149: "PA-REQ-ENC-PA-REP",
699 150: "PA-AS-FRESHNESS",
700 151: "PA-SPAKE",
701 161: "KERB-KEY-LIST-REQ",
702 162: "KERB-KEY-LIST-REP",
703 165: "PA-SUPPORTED-ENCTYPES",
704 166: "PA-EXTENDED-ERROR",
705 167: "PA-PAC-OPTIONS",
706 170: "KERB-SUPERSEDED-BY-USER",
707 171: "KERB-DMSA-KEY-PACKAGE",
708}
709
710_PADATA_CLASSES = {
711 # Filled elsewhere in this file
712}
713
714
715# RFC4120
716
717
718class _PADATA_value_Field(ASN1F_STRING_PacketField):
719 """
720 A special field that properly dispatches PA-DATA values according to
721 padata-type and if the paquet is a request or a response.
722 """
723
724 def m2i(self, pkt, s):
725 val = super(_PADATA_value_Field, self).m2i(pkt, s)
726 if pkt.padataType.val in _PADATA_CLASSES:
727 cls = _PADATA_CLASSES[pkt.padataType.val]
728 if isinstance(cls, tuple):
729 parent = pkt.underlayer or pkt.parent
730 is_reply = False
731 if parent is not None:
732 if isinstance(parent, (KRB_AS_REP, KRB_TGS_REP)):
733 is_reply = True
734 else:
735 parent = parent.underlayer or parent.parent
736 is_reply = isinstance(parent, KRB_ERROR)
737 cls = cls[is_reply]
738 if not val[0].val:
739 return val
740 return cls(val[0].val, _underlayer=pkt), val[1]
741 return val
742
743
744class PADATA(ASN1_Packet):
745 ASN1_codec = ASN1_Codecs.BER
746 ASN1_root = ASN1F_SEQUENCE(
747 ASN1F_enum_INTEGER("padataType", 0, _PADATA_TYPES, explicit_tag=0xA1),
748 _PADATA_value_Field(
749 "padataValue",
750 "",
751 explicit_tag=0xA2,
752 ),
753 )
754
755
756# RFC 4120 sect 5.2.7.2
757
758
759class PA_ENC_TS_ENC(ASN1_Packet):
760 ASN1_codec = ASN1_Codecs.BER
761 ASN1_root = ASN1F_SEQUENCE(
762 KerberosTime("patimestamp", GeneralizedTime(), explicit_tag=0xA0),
763 ASN1F_optional(Microseconds("pausec", 0, explicit_tag=0xA1)),
764 )
765
766
767_PADATA_CLASSES[2] = EncryptedData # PA-ENC-TIMESTAMP
768_PADATA_CLASSES[138] = EncryptedData # PA-ENCRYPTED-CHALLENGE
769
770
771# RFC 4120 sect 5.2.7.4
772
773
774class ETYPE_INFO_ENTRY(ASN1_Packet):
775 ASN1_codec = ASN1_Codecs.BER
776 ASN1_root = ASN1F_SEQUENCE(
777 ASN1F_enum_INTEGER("etype", 0x1, _KRB_E_TYPES, explicit_tag=0xA0),
778 ASN1F_optional(
779 ASN1F_STRING("salt", "", explicit_tag=0xA1),
780 ),
781 )
782
783
784class ETYPE_INFO(ASN1_Packet):
785 ASN1_codec = ASN1_Codecs.BER
786 ASN1_root = ASN1F_SEQUENCE_OF("seq", [ETYPE_INFO_ENTRY()], ETYPE_INFO_ENTRY)
787
788
789_PADATA_CLASSES[11] = ETYPE_INFO
790
791# RFC 4120 sect 5.2.7.5
792
793
794class ETYPE_INFO_ENTRY2(ASN1_Packet):
795 ASN1_codec = ASN1_Codecs.BER
796 ASN1_root = ASN1F_SEQUENCE(
797 ASN1F_enum_INTEGER("etype", 0x1, _KRB_E_TYPES, explicit_tag=0xA0),
798 ASN1F_optional(
799 KerberosString("salt", "", explicit_tag=0xA1),
800 ),
801 ASN1F_optional(
802 ASN1F_STRING("s2kparams", "", explicit_tag=0xA2),
803 ),
804 )
805
806
807class ETYPE_INFO2(ASN1_Packet):
808 ASN1_codec = ASN1_Codecs.BER
809 ASN1_root = ASN1F_SEQUENCE_OF("seq", [ETYPE_INFO_ENTRY2()], ETYPE_INFO_ENTRY2)
810
811
812_PADATA_CLASSES[19] = ETYPE_INFO2
813
814
815# RFC8636 - PKINIT Algorithm Agility
816
817
818class TD_CMS_DIGEST_ALGORITHMS(ASN1_Packet):
819 ASN1_codec = ASN1_Codecs.BER
820 ASN1_root = ASN1F_SEQUENCE_OF("seq", [], X509_AlgorithmIdentifier)
821
822
823_PADATA_CLASSES[111] = TD_CMS_DIGEST_ALGORITHMS
824
825
826# PADATA Extended with RFC6113
827
828
829class PA_AUTHENTICATION_SET_ELEM(ASN1_Packet):
830 ASN1_codec = ASN1_Codecs.BER
831 ASN1_root = ASN1F_SEQUENCE(
832 Int32("paType", 0, explicit_tag=0xA0),
833 ASN1F_optional(
834 ASN1F_STRING("paHint", "", explicit_tag=0xA1),
835 ),
836 ASN1F_optional(
837 ASN1F_STRING("paValue", "", explicit_tag=0xA2),
838 ),
839 )
840
841
842class PA_AUTHENTICATION_SET(ASN1_Packet):
843 ASN1_codec = ASN1_Codecs.BER
844 ASN1_root = ASN1F_SEQUENCE_OF(
845 "elems", [PA_AUTHENTICATION_SET_ELEM()], PA_AUTHENTICATION_SET_ELEM
846 )
847
848
849_PADATA_CLASSES[134] = PA_AUTHENTICATION_SET
850
851
852# [MS-KILE] sect 2.2.3
853
854
855class PA_PAC_REQUEST(ASN1_Packet):
856 ASN1_codec = ASN1_Codecs.BER
857 ASN1_root = ASN1F_SEQUENCE(
858 ASN1F_BOOLEAN("includePac", True, explicit_tag=0xA0),
859 )
860
861
862_PADATA_CLASSES[128] = PA_PAC_REQUEST
863
864
865# [MS-KILE] sect 2.2.5
866
867
868class LSAP_TOKEN_INFO_INTEGRITY(Packet):
869 fields_desc = [
870 FlagsField(
871 "Flags",
872 0,
873 -32,
874 {
875 0x00000001: "UAC-Restricted",
876 },
877 ),
878 LEIntEnumField(
879 "TokenIL",
880 0x00002000,
881 {
882 0x00000000: "Untrusted",
883 0x00001000: "Low",
884 0x00002000: "Medium",
885 0x00003000: "High",
886 0x00004000: "System",
887 0x00005000: "Protected process",
888 },
889 ),
890 XStrFixedLenField("MachineID", b"", length=32),
891 ]
892
893
894# [MS-KILE] sect 2.2.6
895
896
897class _KerbAdRestrictionEntry_Field(ASN1F_STRING_PacketField):
898 def m2i(self, pkt, s):
899 val = super(_KerbAdRestrictionEntry_Field, self).m2i(pkt, s)
900 if not val[0].val:
901 return val
902 if pkt.restrictionType.val == 0x0000: # LSAP_TOKEN_INFO_INTEGRITY
903 return LSAP_TOKEN_INFO_INTEGRITY(val[0].val, _underlayer=pkt), val[1]
904 return val
905
906
907class KERB_AD_RESTRICTION_ENTRY(ASN1_Packet):
908 name = "KERB-AD-RESTRICTION-ENTRY"
909 ASN1_codec = ASN1_Codecs.BER
910 ASN1_root = ASN1F_SEQUENCE(
911 ASN1F_SEQUENCE(
912 ASN1F_enum_INTEGER(
913 "restrictionType",
914 0,
915 {0: "LSAP_TOKEN_INFO_INTEGRITY"},
916 explicit_tag=0xA0,
917 ),
918 _KerbAdRestrictionEntry_Field("restriction", b"", explicit_tag=0xA1),
919 )
920 )
921
922
923_AUTHORIZATIONDATA_VALUES[141] = KERB_AD_RESTRICTION_ENTRY
924
925
926# [MS-KILE] sect 3.2.5.8
927
928
929class KERB_AUTH_DATA_AP_OPTIONS(Packet):
930 name = "KERB-AUTH-DATA-AP-OPTIONS"
931 fields_desc = [
932 LEIntEnumField(
933 "apOptions",
934 0x4000,
935 {
936 0x4000: "KERB_AP_OPTIONS_CBT",
937 0x8000: "KERB_AP_OPTIONS_UNVERIFIED_TARGET_NAME",
938 },
939 ),
940 ]
941
942
943_AUTHORIZATIONDATA_VALUES[143] = KERB_AUTH_DATA_AP_OPTIONS
944
945
946# This has no doc..? [MS-KILE] only mentions its name.
947
948
949class KERB_AUTH_DATA_CLIENT_TARGET(Packet):
950 name = "KERB-AD-TARGET-PRINCIPAL"
951 fields_desc = [
952 StrFieldUtf16("spn", ""),
953 ]
954
955
956_AUTHORIZATIONDATA_VALUES[144] = KERB_AUTH_DATA_CLIENT_TARGET
957
958
959# RFC6806 sect 6
960
961
962class KERB_AD_LOGIN_ALIAS(ASN1_Packet):
963 ASN1_codec = ASN1_Codecs.BER
964 ASN1_root = ASN1F_SEQUENCE(ASN1F_SEQUENCE_OF("loginAliases", [], PrincipalName))
965
966
967_AUTHORIZATIONDATA_VALUES[80] = KERB_AD_LOGIN_ALIAS
968
969
970# [MS-KILE] sect 2.2.8
971
972
973class PA_SUPPORTED_ENCTYPES(Packet):
974 fields_desc = [
975 FlagsField(
976 "flags",
977 0,
978 -32,
979 [
980 "DES-CBC-CRC",
981 "DES-CBC-MD5",
982 "RC4-HMAC",
983 "AES128-CTS-HMAC-SHA1-96",
984 "AES256-CTS-HMAC-SHA1-96",
985 ]
986 + ["bit_%d" % i for i in range(11)]
987 + [
988 "FAST-supported",
989 "Compount-identity-supported",
990 "Claims-supported",
991 "Resource-SID-compression-disabled",
992 ],
993 )
994 ]
995
996
997_PADATA_CLASSES[165] = PA_SUPPORTED_ENCTYPES
998
999# [MS-KILE] sect 2.2.10
1000
1001
1002class PA_PAC_OPTIONS(ASN1_Packet):
1003 ASN1_codec = ASN1_Codecs.BER
1004 ASN1_root = ASN1F_SEQUENCE(
1005 KerberosFlags(
1006 "options",
1007 "",
1008 [
1009 "Claims",
1010 "Branch-Aware",
1011 "Forward-to-Full-DC",
1012 "Resource-based-constrained-delegation", # [MS-SFU] 2.2.5
1013 ],
1014 explicit_tag=0xA0,
1015 )
1016 )
1017
1018
1019_PADATA_CLASSES[167] = PA_PAC_OPTIONS
1020
1021# [MS-KILE] sect 2.2.11
1022
1023
1024class KERB_KEY_LIST_REQ(ASN1_Packet):
1025 ASN1_codec = ASN1_Codecs.BER
1026 ASN1_root = ASN1F_SEQUENCE_OF(
1027 "keytypes",
1028 [],
1029 ASN1F_enum_INTEGER("", 0, _KRB_E_TYPES),
1030 )
1031
1032
1033_PADATA_CLASSES[161] = KERB_KEY_LIST_REQ
1034
1035# [MS-KILE] sect 2.2.12
1036
1037
1038class KERB_KEY_LIST_REP(ASN1_Packet):
1039 ASN1_codec = ASN1_Codecs.BER
1040 ASN1_root = ASN1F_SEQUENCE_OF(
1041 "keys",
1042 [],
1043 ASN1F_PACKET("", None, EncryptionKey),
1044 )
1045
1046
1047_PADATA_CLASSES[162] = KERB_KEY_LIST_REP
1048
1049# [MS-KILE] sect 2.2.13
1050
1051
1052class KERB_SUPERSEDED_BY_USER(ASN1_Packet):
1053 ASN1_codec = ASN1_Codecs.BER
1054 ASN1_root = ASN1F_SEQUENCE(
1055 ASN1F_PACKET("name", None, PrincipalName, explicit_tag=0xA0),
1056 Realm("realm", None, explicit_tag=0xA1),
1057 )
1058
1059
1060_PADATA_CLASSES[170] = KERB_SUPERSEDED_BY_USER
1061
1062
1063# [MS-KILE] sect 2.2.14
1064
1065
1066class KERB_DMSA_KEY_PACKAGE(ASN1_Packet):
1067 ASN1_codec = ASN1_Codecs.BER
1068 ASN1_root = ASN1F_SEQUENCE(
1069 ASN1F_SEQUENCE_OF(
1070 "currentKeys",
1071 [],
1072 ASN1F_PACKET("", None, EncryptionKey),
1073 explicit_tag=0xA0,
1074 ),
1075 ASN1F_optional(
1076 ASN1F_SEQUENCE_OF(
1077 "previousKeys",
1078 [],
1079 ASN1F_PACKET("", None, EncryptionKey),
1080 explicit_tag=0xA1,
1081 ),
1082 ),
1083 KerberosTime("expirationInterval", GeneralizedTime(), explicit_tag=0xA2),
1084 KerberosTime("fetchInterval", GeneralizedTime(), explicit_tag=0xA4),
1085 )
1086
1087
1088_PADATA_CLASSES[171] = KERB_DMSA_KEY_PACKAGE
1089
1090
1091# RFC6113 sect 5.4.1
1092
1093
1094class _KrbFastArmor_value_Field(ASN1F_STRING_PacketField):
1095 def m2i(self, pkt, s):
1096 val = super(_KrbFastArmor_value_Field, self).m2i(pkt, s)
1097 if not val[0].val:
1098 return val
1099 if pkt.armorType.val == 1: # FX_FAST_ARMOR_AP_REQUEST
1100 return KRB_AP_REQ(val[0].val, _underlayer=pkt), val[1]
1101 return val
1102
1103
1104class KrbFastArmor(ASN1_Packet):
1105 ASN1_codec = ASN1_Codecs.BER
1106 ASN1_root = ASN1F_SEQUENCE(
1107 ASN1F_enum_INTEGER(
1108 "armorType", 1, {1: "FX_FAST_ARMOR_AP_REQUEST"}, explicit_tag=0xA0
1109 ),
1110 _KrbFastArmor_value_Field("armorValue", "", explicit_tag=0xA1),
1111 )
1112
1113
1114# RFC6113 sect 5.4.2
1115
1116
1117class KrbFastArmoredReq(ASN1_Packet):
1118 ASN1_codec = ASN1_Codecs.BER
1119 ASN1_root = ASN1F_SEQUENCE(
1120 ASN1F_SEQUENCE(
1121 ASN1F_optional(
1122 ASN1F_PACKET("armor", None, KrbFastArmor, explicit_tag=0xA0)
1123 ),
1124 ASN1F_PACKET("reqChecksum", Checksum(), Checksum, explicit_tag=0xA1),
1125 ASN1F_PACKET("encFastReq", None, EncryptedData, explicit_tag=0xA2),
1126 )
1127 )
1128
1129
1130class PA_FX_FAST_REQUEST(ASN1_Packet):
1131 ASN1_codec = ASN1_Codecs.BER
1132 ASN1_root = ASN1F_CHOICE(
1133 "armoredData",
1134 ASN1_STRING(""),
1135 ASN1F_PACKET("req", KrbFastArmoredReq, KrbFastArmoredReq, implicit_tag=0xA0),
1136 )
1137
1138
1139# RFC6113 sect 5.4.3
1140
1141
1142class KrbFastArmoredRep(ASN1_Packet):
1143 ASN1_codec = ASN1_Codecs.BER
1144 ASN1_root = ASN1F_SEQUENCE(
1145 ASN1F_SEQUENCE(
1146 ASN1F_PACKET("encFastRep", None, EncryptedData, explicit_tag=0xA0),
1147 )
1148 )
1149
1150
1151class PA_FX_FAST_REPLY(ASN1_Packet):
1152 ASN1_codec = ASN1_Codecs.BER
1153 ASN1_root = ASN1F_CHOICE(
1154 "armoredData",
1155 ASN1_STRING(""),
1156 ASN1F_PACKET("req", KrbFastArmoredRep, KrbFastArmoredRep, implicit_tag=0xA0),
1157 )
1158
1159
1160class KrbFastFinished(ASN1_Packet):
1161 ASN1_codec = ASN1_Codecs.BER
1162 ASN1_root = ASN1F_SEQUENCE(
1163 KerberosTime("timestamp", GeneralizedTime(), explicit_tag=0xA0),
1164 Microseconds("usec", 0, explicit_tag=0xA1),
1165 Realm("crealm", "", explicit_tag=0xA2),
1166 ASN1F_PACKET("cname", None, PrincipalName, explicit_tag=0xA3),
1167 ASN1F_PACKET("ticketChecksum", Checksum(), Checksum, explicit_tag=0xA4),
1168 )
1169
1170
1171class KrbFastResponse(ASN1_Packet):
1172 ASN1_codec = ASN1_Codecs.BER
1173 ASN1_root = ASN1F_SEQUENCE(
1174 ASN1F_SEQUENCE_OF("padata", [PADATA()], PADATA, explicit_tag=0xA0),
1175 ASN1F_optional(
1176 ASN1F_PACKET("strengthenKey", None, EncryptionKey, explicit_tag=0xA1)
1177 ),
1178 ASN1F_optional(
1179 ASN1F_PACKET(
1180 "finished", KrbFastFinished(), KrbFastFinished, explicit_tag=0xA2
1181 )
1182 ),
1183 UInt32("nonce", 0, explicit_tag=0xA3),
1184 )
1185
1186
1187_PADATA_CLASSES[136] = (PA_FX_FAST_REQUEST, PA_FX_FAST_REPLY)
1188
1189# RFC 4556
1190
1191
1192# sect 3.2.1
1193
1194
1195class ExternalPrincipalIdentifier(ASN1_Packet):
1196 ASN1_codec = ASN1_Codecs.BER
1197 ASN1_root = ASN1F_SEQUENCE(
1198 ASN1F_optional(
1199 ASN1F_STRING("subjectName", "", implicit_tag=0xA0),
1200 ),
1201 ASN1F_optional(
1202 ASN1F_STRING("issuerAndSerialNumber", "", implicit_tag=0xA1),
1203 ),
1204 ASN1F_optional(
1205 ASN1F_STRING("subjectKeyIdentifier", "", implicit_tag=0xA2),
1206 ),
1207 )
1208
1209
1210class PA_PK_AS_REQ(ASN1_Packet):
1211 ASN1_codec = ASN1_Codecs.BER
1212 ASN1_root = ASN1F_SEQUENCE(
1213 ASN1F_STRING("signedAuthpack", "", implicit_tag=0xA0),
1214 ASN1F_optional(
1215 ASN1F_SEQUENCE_OF(
1216 "trustedCertifiers",
1217 [ExternalPrincipalIdentifier()],
1218 ExternalPrincipalIdentifier,
1219 explicit_tag=0xA1,
1220 ),
1221 ),
1222 ASN1F_optional(
1223 ASN1F_STRING("kdcPkId", "", implicit_tag=0xA2),
1224 ),
1225 )
1226
1227
1228_PADATA_CLASSES[16] = PA_PK_AS_REQ
1229
1230# sect 3.2.3
1231
1232
1233class DHRepInfo(ASN1_Packet):
1234 ASN1_codec = ASN1_Codecs.BER
1235 ASN1_root = ASN1F_SEQUENCE(
1236 ASN1F_STRING("dhSignedData", "", implicit_tag=0xA0),
1237 ASN1F_optional(
1238 ASN1F_STRING("serverDHNonce", "", explicit_tag=0xA1),
1239 ),
1240 )
1241
1242
1243class EncKeyPack(ASN1_Packet):
1244 ASN1_codec = ASN1_Codecs.BER
1245 ASN1_root = ASN1F_STRING("encKeyPack", "")
1246
1247
1248class PA_PK_AS_REP(ASN1_Packet):
1249 ASN1_codec = ASN1_Codecs.BER
1250 ASN1_root = ASN1F_CHOICE(
1251 "rep",
1252 ASN1_STRING(""),
1253 ASN1F_PACKET("dhInfo", DHRepInfo(), DHRepInfo, explicit_tag=0xA0),
1254 ASN1F_PACKET("encKeyPack", EncKeyPack(), EncKeyPack, explicit_tag=0xA1),
1255 )
1256
1257
1258_PADATA_CLASSES[17] = PA_PK_AS_REP
1259
1260# [MS-SFU]
1261
1262
1263# sect 2.2.1
1264class PA_FOR_USER(ASN1_Packet):
1265 ASN1_codec = ASN1_Codecs.BER
1266 ASN1_root = ASN1F_SEQUENCE(
1267 ASN1F_PACKET("userName", PrincipalName(), PrincipalName, explicit_tag=0xA0),
1268 Realm("userRealm", "", explicit_tag=0xA1),
1269 ASN1F_PACKET("cksum", Checksum(), Checksum, explicit_tag=0xA2),
1270 KerberosString("authPackage", "Kerberos", explicit_tag=0xA3),
1271 )
1272
1273
1274_PADATA_CLASSES[129] = PA_FOR_USER
1275
1276
1277# sect 2.2.2
1278
1279
1280class S4UUserID(ASN1_Packet):
1281 ASN1_codec = ASN1_Codecs.BER
1282 ASN1_root = ASN1F_SEQUENCE(
1283 UInt32("nonce", 0, explicit_tag=0xA0),
1284 ASN1F_optional(
1285 ASN1F_PACKET("cname", None, PrincipalName, explicit_tag=0xA1),
1286 ),
1287 Realm("crealm", "", explicit_tag=0xA2),
1288 ASN1F_optional(
1289 ASN1F_STRING("subjectCertificate", None, explicit_tag=0xA3),
1290 ),
1291 ASN1F_optional(
1292 ASN1F_FLAGS(
1293 "options",
1294 "",
1295 [
1296 "reserved",
1297 "KDC_CHECK_LOGON_HOUR_RESTRICTIONS",
1298 "USE_REPLY_KEY_USAGE",
1299 "NT_AUTH_POLICY_NOT_REQUIRED",
1300 "UNCONDITIONAL_DELEGATION",
1301 ],
1302 explicit_tag=0xA4,
1303 )
1304 ),
1305 )
1306
1307
1308class PA_S4U_X509_USER(ASN1_Packet):
1309 ASN1_codec = ASN1_Codecs.BER
1310 ASN1_root = ASN1F_SEQUENCE(
1311 ASN1F_PACKET("userId", S4UUserID(), S4UUserID, explicit_tag=0xA0),
1312 ASN1F_PACKET("checksum", Checksum(), Checksum, explicit_tag=0xA1),
1313 )
1314
1315
1316_PADATA_CLASSES[130] = PA_S4U_X509_USER
1317
1318
1319# Back to RFC4120
1320
1321# sect 5.10
1322KRB_MSG_TYPES = {
1323 1: "Ticket",
1324 2: "Authenticator",
1325 3: "EncTicketPart",
1326 10: "AS-REQ",
1327 11: "AS-REP",
1328 12: "TGS-REQ",
1329 13: "TGS-REP",
1330 14: "AP-REQ",
1331 15: "AP-REP",
1332 16: "KRB-TGT-REQ", # U2U
1333 17: "KRB-TGT-REP", # U2U
1334 20: "KRB-SAFE",
1335 21: "KRB-PRIV",
1336 22: "KRB-CRED",
1337 25: "EncASRepPart",
1338 26: "EncTGSRepPart",
1339 27: "EncAPRepPart",
1340 28: "EncKrbPrivPart",
1341 29: "EnvKrbCredPart",
1342 30: "KRB-ERROR",
1343}
1344
1345# sect 5.3
1346
1347
1348class KRB_Ticket(ASN1_Packet):
1349 ASN1_codec = ASN1_Codecs.BER
1350 ASN1_root = ASN1F_SEQUENCE(
1351 ASN1F_SEQUENCE(
1352 ASN1F_INTEGER("tktVno", 5, explicit_tag=0xA0),
1353 Realm("realm", "", explicit_tag=0xA1),
1354 ASN1F_PACKET("sname", PrincipalName(), PrincipalName, explicit_tag=0xA2),
1355 ASN1F_PACKET("encPart", EncryptedData(), EncryptedData, explicit_tag=0xA3),
1356 ),
1357 implicit_tag=ASN1_Class_KRB.Ticket,
1358 )
1359
1360 def getSPN(self):
1361 return "%s@%s" % (
1362 self.sname.toString(),
1363 self.realm.val.decode(),
1364 )
1365
1366
1367class TransitedEncoding(ASN1_Packet):
1368 ASN1_codec = ASN1_Codecs.BER
1369 ASN1_root = ASN1F_SEQUENCE(
1370 Int32("trType", 0, explicit_tag=0xA0),
1371 ASN1F_STRING("contents", "", explicit_tag=0xA1),
1372 )
1373
1374
1375_TICKET_FLAGS = [
1376 "reserved",
1377 "forwardable",
1378 "forwarded",
1379 "proxiable",
1380 "proxy",
1381 "may-postdate",
1382 "postdated",
1383 "invalid",
1384 "renewable",
1385 "initial",
1386 "pre-authent",
1387 "hw-authent",
1388 "transited-since-policy-checked",
1389 "ok-as-delegate",
1390 "unused",
1391 "canonicalize", # RFC6806
1392 "anonymous", # RFC6112 + RFC8129
1393]
1394
1395
1396class EncTicketPart(ASN1_Packet):
1397 ASN1_codec = ASN1_Codecs.BER
1398 ASN1_root = ASN1F_SEQUENCE(
1399 ASN1F_SEQUENCE(
1400 KerberosFlags(
1401 "flags",
1402 "",
1403 _TICKET_FLAGS,
1404 explicit_tag=0xA0,
1405 ),
1406 ASN1F_PACKET("key", EncryptionKey(), EncryptionKey, explicit_tag=0xA1),
1407 Realm("crealm", "", explicit_tag=0xA2),
1408 ASN1F_PACKET("cname", PrincipalName(), PrincipalName, explicit_tag=0xA3),
1409 ASN1F_PACKET(
1410 "transited", TransitedEncoding(), TransitedEncoding, explicit_tag=0xA4
1411 ),
1412 KerberosTime("authtime", GeneralizedTime(), explicit_tag=0xA5),
1413 ASN1F_optional(
1414 KerberosTime("starttime", GeneralizedTime(), explicit_tag=0xA6)
1415 ),
1416 KerberosTime("endtime", GeneralizedTime(), explicit_tag=0xA7),
1417 ASN1F_optional(
1418 KerberosTime("renewTill", GeneralizedTime(), explicit_tag=0xA8),
1419 ),
1420 ASN1F_optional(
1421 HostAddresses("addresses", explicit_tag=0xA9),
1422 ),
1423 ASN1F_optional(
1424 ASN1F_PACKET(
1425 "authorizationData", None, AuthorizationData, explicit_tag=0xAA
1426 ),
1427 ),
1428 ),
1429 implicit_tag=ASN1_Class_KRB.EncTicketPart,
1430 )
1431
1432
1433# sect 5.4.1
1434
1435
1436class KRB_KDC_REQ_BODY(ASN1_Packet):
1437 ASN1_codec = ASN1_Codecs.BER
1438 ASN1_root = ASN1F_SEQUENCE(
1439 KerberosFlags(
1440 "kdcOptions",
1441 "",
1442 [
1443 "reserved",
1444 "forwardable",
1445 "forwarded",
1446 "proxiable",
1447 "proxy",
1448 "allow-postdate",
1449 "postdated",
1450 "unused7",
1451 "renewable",
1452 "unused9",
1453 "unused10",
1454 "opt-hardware-auth",
1455 "unused12",
1456 "unused13",
1457 "cname-in-addl-tkt", # [MS-SFU] sect 2.2.3
1458 "canonicalize", # RFC6806
1459 "request-anonymous", # RFC6112 + RFC8129
1460 ]
1461 + ["unused%d" % i for i in range(17, 26)]
1462 + [
1463 "disable-transited-check",
1464 "renewable-ok",
1465 "enc-tkt-in-skey",
1466 "unused29",
1467 "renew",
1468 "validate",
1469 ],
1470 explicit_tag=0xA0,
1471 ),
1472 ASN1F_optional(ASN1F_PACKET("cname", None, PrincipalName, explicit_tag=0xA1)),
1473 Realm("realm", "", explicit_tag=0xA2),
1474 ASN1F_optional(
1475 ASN1F_PACKET("sname", None, PrincipalName, explicit_tag=0xA3),
1476 ),
1477 ASN1F_optional(KerberosTime("from_", None, explicit_tag=0xA4)),
1478 KerberosTime("till", GeneralizedTime(), explicit_tag=0xA5),
1479 ASN1F_optional(KerberosTime("rtime", GeneralizedTime(), explicit_tag=0xA6)),
1480 UInt32("nonce", 0, explicit_tag=0xA7),
1481 ASN1F_SEQUENCE_OF("etype", [], Int32, explicit_tag=0xA8),
1482 ASN1F_optional(
1483 HostAddresses("addresses", explicit_tag=0xA9),
1484 ),
1485 ASN1F_optional(
1486 ASN1F_PACKET(
1487 "encAuthorizationData", None, EncryptedData, explicit_tag=0xAA
1488 ),
1489 ),
1490 ASN1F_optional(
1491 ASN1F_SEQUENCE_OF("additionalTickets", [], KRB_Ticket, explicit_tag=0xAB)
1492 ),
1493 )
1494
1495
1496KRB_KDC_REQ = ASN1F_SEQUENCE(
1497 ASN1F_INTEGER("pvno", 5, explicit_tag=0xA1),
1498 ASN1F_enum_INTEGER("msgType", 10, KRB_MSG_TYPES, explicit_tag=0xA2),
1499 ASN1F_optional(ASN1F_SEQUENCE_OF("padata", [], PADATA, explicit_tag=0xA3)),
1500 ASN1F_PACKET("reqBody", KRB_KDC_REQ_BODY(), KRB_KDC_REQ_BODY, explicit_tag=0xA4),
1501)
1502
1503
1504class KrbFastReq(ASN1_Packet):
1505 # RFC6113 sect 5.4.2
1506 ASN1_codec = ASN1_Codecs.BER
1507 ASN1_root = ASN1F_SEQUENCE(
1508 KerberosFlags(
1509 "fastOptions",
1510 "",
1511 [
1512 "RESERVED",
1513 "hide-client-names",
1514 ]
1515 + ["res%d" % i for i in range(2, 16)]
1516 + ["kdc-follow-referrals"],
1517 explicit_tag=0xA0,
1518 ),
1519 ASN1F_SEQUENCE_OF("padata", [PADATA()], PADATA, explicit_tag=0xA1),
1520 ASN1F_PACKET("reqBody", None, KRB_KDC_REQ_BODY, explicit_tag=0xA2),
1521 )
1522
1523
1524class KRB_AS_REQ(ASN1_Packet):
1525 ASN1_codec = ASN1_Codecs.BER
1526 ASN1_root = ASN1F_SEQUENCE(
1527 KRB_KDC_REQ,
1528 implicit_tag=ASN1_Class_KRB.AS_REQ,
1529 )
1530
1531
1532class KRB_TGS_REQ(ASN1_Packet):
1533 ASN1_codec = ASN1_Codecs.BER
1534 ASN1_root = ASN1F_SEQUENCE(
1535 KRB_KDC_REQ,
1536 implicit_tag=ASN1_Class_KRB.TGS_REQ,
1537 )
1538 msgType = ASN1_INTEGER(12)
1539
1540
1541# sect 5.4.2
1542
1543KRB_KDC_REP = ASN1F_SEQUENCE(
1544 ASN1F_INTEGER("pvno", 5, explicit_tag=0xA0),
1545 ASN1F_enum_INTEGER("msgType", 11, KRB_MSG_TYPES, explicit_tag=0xA1),
1546 ASN1F_optional(
1547 ASN1F_SEQUENCE_OF("padata", [], PADATA, explicit_tag=0xA2),
1548 ),
1549 Realm("crealm", "", explicit_tag=0xA3),
1550 ASN1F_PACKET("cname", None, PrincipalName, explicit_tag=0xA4),
1551 ASN1F_PACKET("ticket", None, KRB_Ticket, explicit_tag=0xA5),
1552 ASN1F_PACKET("encPart", None, EncryptedData, explicit_tag=0xA6),
1553)
1554
1555
1556class KRB_AS_REP(ASN1_Packet):
1557 ASN1_codec = ASN1_Codecs.BER
1558 ASN1_root = ASN1F_SEQUENCE(
1559 KRB_KDC_REP,
1560 implicit_tag=ASN1_Class_KRB.AS_REP,
1561 )
1562
1563
1564class KRB_TGS_REP(ASN1_Packet):
1565 ASN1_codec = ASN1_Codecs.BER
1566 ASN1_root = ASN1F_SEQUENCE(
1567 KRB_KDC_REP,
1568 implicit_tag=ASN1_Class_KRB.TGS_REP,
1569 )
1570
1571 def getUPN(self):
1572 return "%s@%s" % (
1573 self.cname.toString(),
1574 self.crealm.val.decode(),
1575 )
1576
1577
1578class LastReqItem(ASN1_Packet):
1579 ASN1_codec = ASN1_Codecs.BER
1580 ASN1_root = ASN1F_SEQUENCE(
1581 Int32("lrType", 0, explicit_tag=0xA0),
1582 KerberosTime("lrValue", GeneralizedTime(), explicit_tag=0xA1),
1583 )
1584
1585
1586EncKDCRepPart = ASN1F_SEQUENCE(
1587 ASN1F_PACKET("key", None, EncryptionKey, explicit_tag=0xA0),
1588 ASN1F_SEQUENCE_OF("lastReq", [], LastReqItem, explicit_tag=0xA1),
1589 UInt32("nonce", 0, explicit_tag=0xA2),
1590 ASN1F_optional(
1591 KerberosTime("keyExpiration", GeneralizedTime(), explicit_tag=0xA3),
1592 ),
1593 KerberosFlags(
1594 "flags",
1595 "",
1596 _TICKET_FLAGS,
1597 explicit_tag=0xA4,
1598 ),
1599 KerberosTime("authtime", GeneralizedTime(), explicit_tag=0xA5),
1600 ASN1F_optional(
1601 KerberosTime("starttime", GeneralizedTime(), explicit_tag=0xA6),
1602 ),
1603 KerberosTime("endtime", GeneralizedTime(), explicit_tag=0xA7),
1604 ASN1F_optional(
1605 KerberosTime("renewTill", GeneralizedTime(), explicit_tag=0xA8),
1606 ),
1607 Realm("srealm", "", explicit_tag=0xA9),
1608 ASN1F_PACKET("sname", PrincipalName(), PrincipalName, explicit_tag=0xAA),
1609 ASN1F_optional(
1610 HostAddresses("caddr", explicit_tag=0xAB),
1611 ),
1612 # RFC6806 sect 11
1613 ASN1F_optional(
1614 ASN1F_SEQUENCE_OF("encryptedPaData", [], PADATA, explicit_tag=0xAC),
1615 ),
1616)
1617
1618
1619class EncASRepPart(ASN1_Packet):
1620 ASN1_codec = ASN1_Codecs.BER
1621 ASN1_root = ASN1F_SEQUENCE(
1622 EncKDCRepPart,
1623 implicit_tag=ASN1_Class_KRB.EncASRepPart,
1624 )
1625
1626
1627class EncTGSRepPart(ASN1_Packet):
1628 ASN1_codec = ASN1_Codecs.BER
1629 ASN1_root = ASN1F_SEQUENCE(
1630 EncKDCRepPart,
1631 implicit_tag=ASN1_Class_KRB.EncTGSRepPart,
1632 )
1633
1634
1635# sect 5.5.1
1636
1637
1638class KRB_AP_REQ(ASN1_Packet):
1639 ASN1_codec = ASN1_Codecs.BER
1640 ASN1_root = ASN1F_SEQUENCE(
1641 ASN1F_SEQUENCE(
1642 ASN1F_INTEGER("pvno", 5, explicit_tag=0xA0),
1643 ASN1F_enum_INTEGER("msgType", 14, KRB_MSG_TYPES, explicit_tag=0xA1),
1644 KerberosFlags(
1645 "apOptions",
1646 "",
1647 [
1648 "reserved",
1649 "use-session-key",
1650 "mutual-required",
1651 ],
1652 explicit_tag=0xA2,
1653 ),
1654 ASN1F_PACKET("ticket", None, KRB_Ticket, explicit_tag=0xA3),
1655 ASN1F_PACKET("authenticator", None, EncryptedData, explicit_tag=0xA4),
1656 ),
1657 implicit_tag=ASN1_Class_KRB.AP_REQ,
1658 )
1659
1660
1661_PADATA_CLASSES[1] = KRB_AP_REQ
1662
1663
1664class KRB_Authenticator(ASN1_Packet):
1665 ASN1_codec = ASN1_Codecs.BER
1666 ASN1_root = ASN1F_SEQUENCE(
1667 ASN1F_SEQUENCE(
1668 ASN1F_INTEGER("authenticatorPvno", 5, explicit_tag=0xA0),
1669 Realm("crealm", "", explicit_tag=0xA1),
1670 ASN1F_PACKET("cname", None, PrincipalName, explicit_tag=0xA2),
1671 ASN1F_optional(
1672 ASN1F_PACKET("cksum", None, Checksum, explicit_tag=0xA3),
1673 ),
1674 Microseconds("cusec", 0, explicit_tag=0xA4),
1675 KerberosTime("ctime", GeneralizedTime(), explicit_tag=0xA5),
1676 ASN1F_optional(
1677 ASN1F_PACKET("subkey", None, EncryptionKey, explicit_tag=0xA6),
1678 ),
1679 ASN1F_optional(
1680 UInt32("seqNumber", 0, explicit_tag=0xA7),
1681 ),
1682 ASN1F_optional(
1683 ASN1F_PACKET(
1684 "encAuthorizationData", None, AuthorizationData, explicit_tag=0xA8
1685 ),
1686 ),
1687 ),
1688 implicit_tag=ASN1_Class_KRB.Authenticator,
1689 )
1690
1691
1692# sect 5.5.2
1693
1694
1695class KRB_AP_REP(ASN1_Packet):
1696 ASN1_codec = ASN1_Codecs.BER
1697 ASN1_root = ASN1F_SEQUENCE(
1698 ASN1F_SEQUENCE(
1699 ASN1F_INTEGER("pvno", 5, explicit_tag=0xA0),
1700 ASN1F_enum_INTEGER("msgType", 15, KRB_MSG_TYPES, explicit_tag=0xA1),
1701 ASN1F_PACKET("encPart", None, EncryptedData, explicit_tag=0xA2),
1702 ),
1703 implicit_tag=ASN1_Class_KRB.AP_REP,
1704 )
1705
1706
1707class EncAPRepPart(ASN1_Packet):
1708 ASN1_codec = ASN1_Codecs.BER
1709 ASN1_root = ASN1F_SEQUENCE(
1710 ASN1F_SEQUENCE(
1711 KerberosTime("ctime", GeneralizedTime(), explicit_tag=0xA0),
1712 Microseconds("cusec", 0, explicit_tag=0xA1),
1713 ASN1F_optional(
1714 ASN1F_PACKET("subkey", None, EncryptionKey, explicit_tag=0xA2),
1715 ),
1716 ASN1F_optional(
1717 UInt32("seqNumber", 0, explicit_tag=0xA3),
1718 ),
1719 ),
1720 implicit_tag=ASN1_Class_KRB.EncAPRepPart,
1721 )
1722
1723
1724# sect 5.7
1725
1726
1727class KRB_PRIV(ASN1_Packet):
1728 ASN1_codec = ASN1_Codecs.BER
1729 ASN1_root = ASN1F_SEQUENCE(
1730 ASN1F_SEQUENCE(
1731 ASN1F_INTEGER("pvno", 5, explicit_tag=0xA0),
1732 ASN1F_enum_INTEGER("msgType", 21, KRB_MSG_TYPES, explicit_tag=0xA1),
1733 ASN1F_PACKET("encPart", None, EncryptedData, explicit_tag=0xA3),
1734 ),
1735 implicit_tag=ASN1_Class_KRB.PRIV,
1736 )
1737
1738
1739class EncKrbPrivPart(ASN1_Packet):
1740 ASN1_codec = ASN1_Codecs.BER
1741 ASN1_root = ASN1F_SEQUENCE(
1742 ASN1F_SEQUENCE(
1743 ASN1F_STRING("userData", ASN1_STRING(""), explicit_tag=0xA0),
1744 ASN1F_optional(
1745 KerberosTime("timestamp", None, explicit_tag=0xA1),
1746 ),
1747 ASN1F_optional(
1748 Microseconds("usec", None, explicit_tag=0xA2),
1749 ),
1750 ASN1F_optional(
1751 UInt32("seqNumber", None, explicit_tag=0xA3),
1752 ),
1753 ASN1F_PACKET("sAddress", None, HostAddress, explicit_tag=0xA4),
1754 ASN1F_optional(
1755 ASN1F_PACKET("cAddress", None, HostAddress, explicit_tag=0xA5),
1756 ),
1757 ),
1758 implicit_tag=ASN1_Class_KRB.EncKrbPrivPart,
1759 )
1760
1761
1762# sect 5.8
1763
1764
1765class KRB_CRED(ASN1_Packet):
1766 ASN1_codec = ASN1_Codecs.BER
1767 ASN1_root = ASN1F_SEQUENCE(
1768 ASN1F_SEQUENCE(
1769 ASN1F_INTEGER("pvno", 5, explicit_tag=0xA0),
1770 ASN1F_enum_INTEGER("msgType", 22, KRB_MSG_TYPES, explicit_tag=0xA1),
1771 ASN1F_SEQUENCE_OF("tickets", [KRB_Ticket()], KRB_Ticket, explicit_tag=0xA2),
1772 ASN1F_PACKET("encPart", None, EncryptedData, explicit_tag=0xA3),
1773 ),
1774 implicit_tag=ASN1_Class_KRB.CRED,
1775 )
1776
1777
1778class KrbCredInfo(ASN1_Packet):
1779 ASN1_codec = ASN1_Codecs.BER
1780 ASN1_root = ASN1F_SEQUENCE(
1781 ASN1F_PACKET("key", EncryptionKey(), EncryptionKey, explicit_tag=0xA0),
1782 ASN1F_optional(
1783 Realm("prealm", None, explicit_tag=0xA1),
1784 ),
1785 ASN1F_optional(
1786 ASN1F_PACKET("pname", None, PrincipalName, explicit_tag=0xA2),
1787 ),
1788 ASN1F_optional(
1789 KerberosFlags(
1790 "flags",
1791 None,
1792 _TICKET_FLAGS,
1793 explicit_tag=0xA3,
1794 ),
1795 ),
1796 ASN1F_optional(
1797 KerberosTime("authtime", None, explicit_tag=0xA4),
1798 ),
1799 ASN1F_optional(KerberosTime("starttime", None, explicit_tag=0xA5)),
1800 ASN1F_optional(
1801 KerberosTime("endtime", None, explicit_tag=0xA6),
1802 ),
1803 ASN1F_optional(
1804 KerberosTime("renewTill", None, explicit_tag=0xA7),
1805 ),
1806 ASN1F_optional(
1807 Realm("srealm", None, explicit_tag=0xA8),
1808 ),
1809 ASN1F_optional(
1810 ASN1F_PACKET("sname", None, PrincipalName, explicit_tag=0xA9),
1811 ),
1812 ASN1F_optional(
1813 HostAddresses("caddr", explicit_tag=0xAA),
1814 ),
1815 )
1816
1817
1818class EncKrbCredPart(ASN1_Packet):
1819 ASN1_codec = ASN1_Codecs.BER
1820 ASN1_root = ASN1F_SEQUENCE(
1821 ASN1F_SEQUENCE(
1822 ASN1F_SEQUENCE_OF(
1823 "ticketInfo",
1824 [KrbCredInfo()],
1825 KrbCredInfo,
1826 explicit_tag=0xA0,
1827 ),
1828 ASN1F_optional(
1829 UInt32("nonce", None, explicit_tag=0xA1),
1830 ),
1831 ASN1F_optional(
1832 KerberosTime("timestamp", None, explicit_tag=0xA2),
1833 ),
1834 ASN1F_optional(
1835 Microseconds("usec", None, explicit_tag=0xA3),
1836 ),
1837 ASN1F_optional(
1838 ASN1F_PACKET("sAddress", None, HostAddress, explicit_tag=0xA4),
1839 ),
1840 ASN1F_optional(
1841 ASN1F_PACKET("cAddress", None, HostAddress, explicit_tag=0xA5),
1842 ),
1843 ),
1844 implicit_tag=ASN1_Class_KRB.EncKrbCredPart,
1845 )
1846
1847
1848# sect 5.9.1
1849
1850
1851class MethodData(ASN1_Packet):
1852 ASN1_codec = ASN1_Codecs.BER
1853 ASN1_root = ASN1F_SEQUENCE_OF("seq", [PADATA()], PADATA)
1854
1855
1856class _KRBERROR_data_Field(ASN1F_STRING_PacketField):
1857 def m2i(self, pkt, s):
1858 val = super(_KRBERROR_data_Field, self).m2i(pkt, s)
1859 if not val[0].val:
1860 return val
1861 if pkt.errorCode.val in [14, 24, 25, 36]:
1862 # 14: KDC_ERR_ETYPE_NOSUPP
1863 # 24: KDC_ERR_PREAUTH_FAILED
1864 # 25: KDC_ERR_PREAUTH_REQUIRED
1865 # 36: KRB_AP_ERR_BADMATCH
1866 return MethodData(val[0].val, _underlayer=pkt), val[1]
1867 elif pkt.errorCode.val in [6, 7, 12, 13, 18, 29, 41, 60]:
1868 # 6: KDC_ERR_C_PRINCIPAL_UNKNOWN
1869 # 7: KDC_ERR_S_PRINCIPAL_UNKNOWN
1870 # 12: KDC_ERR_POLICY
1871 # 13: KDC_ERR_BADOPTION
1872 # 18: KDC_ERR_CLIENT_REVOKED
1873 # 29: KDC_ERR_SVC_UNAVAILABLE
1874 # 41: KRB_AP_ERR_MODIFIED
1875 # 60: KRB_ERR_GENERIC
1876 try:
1877 return KERB_ERROR_DATA(val[0].val, _underlayer=pkt), val[1]
1878 except BER_Decoding_Error:
1879 if pkt.errorCode.val in [18, 12]:
1880 # Some types can also happen in FAST sessions
1881 # 18: KDC_ERR_CLIENT_REVOKED
1882 return MethodData(val[0].val, _underlayer=pkt), val[1]
1883 elif pkt.errorCode.val == 7:
1884 # This looks like an undocumented structure.
1885 # 7: KDC_ERR_S_PRINCIPAL_UNKNOWN
1886 return KERB_ERROR_UNK(val[0].val, _underlayer=pkt), val[1]
1887 raise
1888 elif pkt.errorCode.val == 69:
1889 # KRB_AP_ERR_USER_TO_USER_REQUIRED
1890 return KRB_TGT_REP(val[0].val, _underlayer=pkt), val[1]
1891 return val
1892
1893
1894class KRB_ERROR(ASN1_Packet):
1895 ASN1_codec = ASN1_Codecs.BER
1896 ASN1_root = ASN1F_SEQUENCE(
1897 ASN1F_SEQUENCE(
1898 ASN1F_INTEGER("pvno", 5, explicit_tag=0xA0),
1899 ASN1F_enum_INTEGER("msgType", 30, KRB_MSG_TYPES, explicit_tag=0xA1),
1900 ASN1F_optional(
1901 KerberosTime("ctime", None, explicit_tag=0xA2),
1902 ),
1903 ASN1F_optional(
1904 Microseconds("cusec", None, explicit_tag=0xA3),
1905 ),
1906 KerberosTime("stime", GeneralizedTime(), explicit_tag=0xA4),
1907 Microseconds("susec", 0, explicit_tag=0xA5),
1908 ASN1F_enum_INTEGER(
1909 "errorCode",
1910 0,
1911 {
1912 # RFC4120 sect 7.5.9
1913 0: "KDC_ERR_NONE",
1914 1: "KDC_ERR_NAME_EXP",
1915 2: "KDC_ERR_SERVICE_EXP",
1916 3: "KDC_ERR_BAD_PVNO",
1917 4: "KDC_ERR_C_OLD_MAST_KVNO",
1918 5: "KDC_ERR_S_OLD_MAST_KVNO",
1919 6: "KDC_ERR_C_PRINCIPAL_UNKNOWN",
1920 7: "KDC_ERR_S_PRINCIPAL_UNKNOWN",
1921 8: "KDC_ERR_PRINCIPAL_NOT_UNIQUE",
1922 9: "KDC_ERR_NULL_KEY",
1923 10: "KDC_ERR_CANNOT_POSTDATE",
1924 11: "KDC_ERR_NEVER_VALID",
1925 12: "KDC_ERR_POLICY",
1926 13: "KDC_ERR_BADOPTION",
1927 14: "KDC_ERR_ETYPE_NOSUPP",
1928 15: "KDC_ERR_SUMTYPE_NOSUPP",
1929 16: "KDC_ERR_PADATA_TYPE_NOSUPP",
1930 17: "KDC_ERR_TRTYPE_NOSUPP",
1931 18: "KDC_ERR_CLIENT_REVOKED",
1932 19: "KDC_ERR_SERVICE_REVOKED",
1933 20: "KDC_ERR_TGT_REVOKED",
1934 21: "KDC_ERR_CLIENT_NOTYET",
1935 22: "KDC_ERR_SERVICE_NOTYET",
1936 23: "KDC_ERR_KEY_EXPIRED",
1937 24: "KDC_ERR_PREAUTH_FAILED",
1938 25: "KDC_ERR_PREAUTH_REQUIRED",
1939 26: "KDC_ERR_SERVER_NOMATCH",
1940 27: "KDC_ERR_MUST_USE_USER2USER",
1941 28: "KDC_ERR_PATH_NOT_ACCEPTED",
1942 29: "KDC_ERR_SVC_UNAVAILABLE",
1943 31: "KRB_AP_ERR_BAD_INTEGRITY",
1944 32: "KRB_AP_ERR_TKT_EXPIRED",
1945 33: "KRB_AP_ERR_TKT_NYV",
1946 34: "KRB_AP_ERR_REPEAT",
1947 35: "KRB_AP_ERR_NOT_US",
1948 36: "KRB_AP_ERR_BADMATCH",
1949 37: "KRB_AP_ERR_SKEW",
1950 38: "KRB_AP_ERR_BADADDR",
1951 39: "KRB_AP_ERR_BADVERSION",
1952 40: "KRB_AP_ERR_MSG_TYPE",
1953 41: "KRB_AP_ERR_MODIFIED",
1954 42: "KRB_AP_ERR_BADORDER",
1955 44: "KRB_AP_ERR_BADKEYVER",
1956 45: "KRB_AP_ERR_NOKEY",
1957 46: "KRB_AP_ERR_MUT_FAIL",
1958 47: "KRB_AP_ERR_BADDIRECTION",
1959 48: "KRB_AP_ERR_METHOD",
1960 49: "KRB_AP_ERR_BADSEQ",
1961 50: "KRB_AP_ERR_INAPP_CKSUM",
1962 51: "KRB_AP_PATH_NOT_ACCEPTED",
1963 52: "KRB_ERR_RESPONSE_TOO_BIG",
1964 60: "KRB_ERR_GENERIC",
1965 61: "KRB_ERR_FIELD_TOOLONG",
1966 62: "KDC_ERROR_CLIENT_NOT_TRUSTED",
1967 63: "KDC_ERROR_KDC_NOT_TRUSTED",
1968 64: "KDC_ERROR_INVALID_SIG",
1969 65: "KDC_ERR_KEY_TOO_WEAK",
1970 66: "KDC_ERR_CERTIFICATE_MISMATCH",
1971 67: "KRB_AP_ERR_NO_TGT",
1972 68: "KDC_ERR_WRONG_REALM",
1973 69: "KRB_AP_ERR_USER_TO_USER_REQUIRED",
1974 70: "KDC_ERR_CANT_VERIFY_CERTIFICATE",
1975 71: "KDC_ERR_INVALID_CERTIFICATE",
1976 72: "KDC_ERR_REVOKED_CERTIFICATE",
1977 73: "KDC_ERR_REVOCATION_STATUS_UNKNOWN",
1978 74: "KDC_ERR_REVOCATION_STATUS_UNAVAILABLE",
1979 75: "KDC_ERR_CLIENT_NAME_MISMATCH",
1980 76: "KDC_ERR_KDC_NAME_MISMATCH",
1981 # draft-ietf-kitten-iakerb
1982 85: "KRB_AP_ERR_IAKERB_KDC_NOT_FOUND",
1983 86: "KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE",
1984 # RFC6113
1985 90: "KDC_ERR_PREAUTH_EXPIRED",
1986 91: "KDC_ERR_MORE_PREAUTH_DATA_REQUIRED",
1987 92: "KDC_ERR_PREAUTH_BAD_AUTHENTICATION_SET",
1988 93: "KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS",
1989 },
1990 explicit_tag=0xA6,
1991 ),
1992 ASN1F_optional(Realm("crealm", None, explicit_tag=0xA7)),
1993 ASN1F_optional(
1994 ASN1F_PACKET("cname", None, PrincipalName, explicit_tag=0xA8),
1995 ),
1996 Realm("realm", "", explicit_tag=0xA9),
1997 ASN1F_PACKET("sname", PrincipalName(), PrincipalName, explicit_tag=0xAA),
1998 ASN1F_optional(KerberosString("eText", "", explicit_tag=0xAB)),
1999 ASN1F_optional(_KRBERROR_data_Field("eData", "", explicit_tag=0xAC)),
2000 ),
2001 implicit_tag=ASN1_Class_KRB.ERROR,
2002 )
2003
2004 def getSPN(self):
2005 return "%s@%s" % (
2006 self.sname.toString(),
2007 self.realm.val.decode(),
2008 )
2009
2010
2011# PA-FX-ERROR
2012_PADATA_CLASSES[137] = KRB_ERROR
2013
2014
2015# [MS-KILE] sect 2.2.1
2016
2017
2018class KERB_EXT_ERROR(Packet):
2019 fields_desc = [
2020 XLEIntEnumField("status", 0, STATUS_ERREF),
2021 XLEIntField("reserved", 0),
2022 XLEIntField("flags", 0x00000001),
2023 ]
2024
2025
2026# [MS-KILE] sect 2.2.2
2027
2028
2029class _Error_Field(ASN1F_STRING_PacketField):
2030 def m2i(self, pkt, s):
2031 val = super(_Error_Field, self).m2i(pkt, s)
2032 if not val[0].val:
2033 return val
2034 if pkt.dataType.val == 3: # KERB_ERR_TYPE_EXTENDED
2035 return KERB_EXT_ERROR(val[0].val, _underlayer=pkt), val[1]
2036 return val
2037
2038
2039class KERB_ERROR_DATA(ASN1_Packet):
2040 ASN1_codec = ASN1_Codecs.BER
2041 ASN1_root = ASN1F_SEQUENCE(
2042 ASN1F_enum_INTEGER(
2043 "dataType",
2044 2,
2045 {
2046 1: "KERB_AP_ERR_TYPE_NTSTATUS", # from the wdk
2047 2: "KERB_AP_ERR_TYPE_SKEW_RECOVERY",
2048 3: "KERB_ERR_TYPE_EXTENDED",
2049 },
2050 explicit_tag=0xA1,
2051 ),
2052 ASN1F_optional(_Error_Field("dataValue", None, explicit_tag=0xA2)),
2053 )
2054
2055
2056# This looks like an undocumented structure.
2057
2058
2059class KERB_ERROR_UNK(ASN1_Packet):
2060 ASN1_codec = ASN1_Codecs.BER
2061 ASN1_root = ASN1F_SEQUENCE(
2062 ASN1F_SEQUENCE(
2063 ASN1F_enum_INTEGER(
2064 "dataType",
2065 0,
2066 {
2067 -128: "KDC_ERR_MUST_USE_USER2USER",
2068 },
2069 explicit_tag=0xA0,
2070 ),
2071 ASN1F_STRING("dataValue", None, explicit_tag=0xA1),
2072 )
2073 )
2074
2075
2076# Kerberos U2U - draft-ietf-cat-user2user-03
2077
2078
2079class KRB_TGT_REQ(ASN1_Packet):
2080 ASN1_codec = ASN1_Codecs.BER
2081 ASN1_root = ASN1F_SEQUENCE(
2082 ASN1F_INTEGER("pvno", 5, explicit_tag=0xA0),
2083 ASN1F_enum_INTEGER("msgType", 16, KRB_MSG_TYPES, explicit_tag=0xA1),
2084 ASN1F_optional(
2085 ASN1F_PACKET("sname", None, PrincipalName, explicit_tag=0xA2),
2086 ),
2087 ASN1F_optional(
2088 Realm("realm", None, explicit_tag=0xA3),
2089 ),
2090 )
2091
2092
2093class KRB_TGT_REP(ASN1_Packet):
2094 ASN1_codec = ASN1_Codecs.BER
2095 ASN1_root = ASN1F_SEQUENCE(
2096 ASN1F_INTEGER("pvno", 5, explicit_tag=0xA0),
2097 ASN1F_enum_INTEGER("msgType", 17, KRB_MSG_TYPES, explicit_tag=0xA1),
2098 ASN1F_PACKET("ticket", None, KRB_Ticket, explicit_tag=0xA2),
2099 )
2100
2101
2102# draft-ietf-kitten-iakerb-03 sect 4
2103
2104
2105class KRB_FINISHED(ASN1_Packet):
2106 ASN1_codec = ASN1_Codecs.BER
2107 ASN1_root = ASN1F_SEQUENCE(
2108 ASN1F_PACKET("gssMic", Checksum(), Checksum, explicit_tag=0xA1),
2109 )
2110
2111
2112# RFC 6542 sect 3.1
2113
2114
2115class KRB_GSS_EXT(Packet):
2116 fields_desc = [
2117 IntEnumField(
2118 "type",
2119 0,
2120 {
2121 # https://www.iana.org/assignments/kerberos-v-gss-api/kerberos-v-gss-api.xhtml
2122 0x00000000: "GSS_EXTS_CHANNEL_BINDING", # RFC 6542 sect 3.2
2123 0x00000001: "GSS_EXTS_IAKERB_FINISHED", # not standard
2124 0x00000002: "GSS_EXTS_FINISHED", # PKU2U / IAKERB
2125 },
2126 ),
2127 FieldLenField("length", None, length_of="data", fmt="!I"),
2128 MultipleTypeField(
2129 [
2130 (
2131 PacketField("data", KRB_FINISHED(), KRB_FINISHED),
2132 lambda pkt: pkt.type == 0x00000002,
2133 ),
2134 ],
2135 XStrLenField("data", b"", length_from=lambda pkt: pkt.length),
2136 ),
2137 ]
2138
2139
2140# RFC 4121 sect 4.1.1
2141
2142
2143class KRB_AuthenticatorChecksum(Packet):
2144 fields_desc = [
2145 FieldLenField("Lgth", None, length_of="Bnd", fmt="<I"),
2146 XStrLenField("Bnd", b"\x00" * 16, length_from=lambda pkt: pkt.Lgth),
2147 FlagsField(
2148 "Flags",
2149 0,
2150 -32,
2151 {
2152 0x01: "GSS_C_DELEG_FLAG",
2153 0x02: "GSS_C_MUTUAL_FLAG",
2154 0x04: "GSS_C_REPLAY_FLAG",
2155 0x08: "GSS_C_SEQUENCE_FLAG",
2156 0x10: "GSS_C_CONF_FLAG", # confidentiality
2157 0x20: "GSS_C_INTEG_FLAG", # integrity
2158 # RFC4757
2159 0x1000: "GSS_C_DCE_STYLE",
2160 0x2000: "GSS_C_IDENTIFY_FLAG",
2161 0x4000: "GSS_C_EXTENDED_ERROR_FLAG",
2162 },
2163 ),
2164 ConditionalField(
2165 LEShortField("DlgOpt", 0),
2166 lambda pkt: pkt.Flags.GSS_C_DELEG_FLAG,
2167 ),
2168 ConditionalField(
2169 FieldLenField("Dlgth", None, length_of="Deleg"),
2170 lambda pkt: pkt.Flags.GSS_C_DELEG_FLAG,
2171 ),
2172 ConditionalField(
2173 PacketLenField(
2174 "Deleg", KRB_CRED(), KRB_CRED, length_from=lambda pkt: pkt.Dlgth
2175 ),
2176 lambda pkt: pkt.Flags.GSS_C_DELEG_FLAG,
2177 ),
2178 # Extensions: RFC 6542 sect 3.1
2179 PacketListField("Exts", KRB_GSS_EXT(), KRB_GSS_EXT),
2180 ]
2181
2182
2183# Kerberos V5 GSS-API - RFC1964 and RFC4121
2184
2185_TOK_IDS = {
2186 # RFC 1964
2187 b"\x01\x00": "KRB-AP-REQ",
2188 b"\x02\x00": "KRB-AP-REP",
2189 b"\x03\x00": "KRB-ERROR",
2190 b"\x01\x01": "GSS_GetMIC-RFC1964",
2191 b"\x02\x01": "GSS_Wrap-RFC1964",
2192 b"\x01\x02": "GSS_Delete_sec_context-RFC1964",
2193 # U2U: [draft-ietf-cat-user2user-03]
2194 b"\x04\x00": "KRB-TGT-REQ",
2195 b"\x04\x01": "KRB-TGT-REP",
2196 # RFC 4121
2197 b"\x04\x04": "GSS_GetMIC",
2198 b"\x05\x04": "GSS_Wrap",
2199 # IAKERB: [draft-ietf-kitten-iakerb-03]
2200 b"\x05\x01": "IAKERB_PROXY",
2201}
2202_SGN_ALGS = {
2203 0x00: "DES MAC MD5",
2204 0x01: "MD2.5",
2205 0x02: "DES MAC",
2206 # RFC 4757
2207 0x11: "HMAC",
2208}
2209_SEAL_ALGS = {
2210 0: "DES",
2211 0xFFFF: "none",
2212 # RFC 4757
2213 0x10: "RC4",
2214}
2215
2216
2217# RFC 1964 - sect 1.1
2218
2219# See https://www.iana.org/assignments/kerberos-v-gss-api/kerberos-v-gss-api.xhtml
2220_InitialContextTokens = {} # filled below
2221
2222
2223class KRB_InnerToken(Packet):
2224 name = "Kerberos v5 InnerToken"
2225 fields_desc = [
2226 StrFixedLenEnumField("TOK_ID", b"\x01\x00", _TOK_IDS, length=2),
2227 PacketField(
2228 "root",
2229 KRB_AP_REQ(),
2230 lambda x, _parent: _InitialContextTokens[_parent.TOK_ID](x),
2231 ),
2232 ]
2233
2234 def mysummary(self):
2235 return self.sprintf(
2236 "Kerberos %s" % _TOK_IDS.get(self.TOK_ID, repr(self.TOK_ID))
2237 )
2238
2239 def guess_payload_class(self, payload):
2240 if self.TOK_ID in [b"\x01\x01", b"\x02\x01", b"\x04\x04", b"\x05\x04"]:
2241 return conf.padding_layer
2242 return Kerberos
2243
2244 @classmethod
2245 def dispatch_hook(cls, _pkt=None, *args, **kargs):
2246 if _pkt and len(_pkt) >= 13:
2247 # Older RFC1964 variants of the token have KRB_GSSAPI_Token wrapper
2248 if _pkt[2:13] == b"\x06\t*\x86H\x86\xf7\x12\x01\x02\x02":
2249 return KRB_GSSAPI_Token
2250 return cls
2251
2252
2253# RFC 4121 - sect 4.1
2254
2255
2256class KRB_GSSAPI_Token(GSSAPI_BLOB):
2257 name = "Kerberos GSSAPI-Token"
2258 ASN1_codec = ASN1_Codecs.BER
2259 ASN1_root = ASN1F_SEQUENCE(
2260 ASN1F_OID("MechType", "1.2.840.113554.1.2.2"),
2261 ASN1F_PACKET(
2262 "innerToken",
2263 KRB_InnerToken(),
2264 KRB_InnerToken,
2265 implicit_tag=0x0,
2266 ),
2267 implicit_tag=ASN1_Class_KRB.Token,
2268 )
2269
2270
2271# RFC 1964 - sect 1.2.1
2272
2273
2274class KRB_GSS_MIC_RFC1964(Packet):
2275 name = "Kerberos v5 MIC Token (RFC1964)"
2276 fields_desc = [
2277 LEShortEnumField("SGN_ALG", 0, _SGN_ALGS),
2278 XLEIntField("Filler", 0xFFFFFFFF),
2279 XStrFixedLenField("SND_SEQ", b"", length=8),
2280 PadField( # sect 1.2.2.3
2281 XStrFixedLenField("SGN_CKSUM", b"", length=8),
2282 align=8,
2283 padwith=b"\x04",
2284 ),
2285 ]
2286
2287 def default_payload_class(self, payload):
2288 return conf.padding_layer
2289
2290
2291_InitialContextTokens[b"\x01\x01"] = KRB_GSS_MIC_RFC1964
2292
2293# RFC 1964 - sect 1.2.2
2294
2295
2296class KRB_GSS_Wrap_RFC1964(Packet):
2297 name = "Kerberos v5 GSS_Wrap (RFC1964)"
2298 fields_desc = [
2299 LEShortEnumField("SGN_ALG", 0, _SGN_ALGS),
2300 LEShortEnumField("SEAL_ALG", 0, _SEAL_ALGS),
2301 XLEShortField("Filler", 0xFFFF),
2302 XStrFixedLenField("SND_SEQ", b"", length=8),
2303 PadField( # sect 1.2.2.3
2304 XStrFixedLenField("SGN_CKSUM", b"", length=8),
2305 align=8,
2306 padwith=b"\x04",
2307 ),
2308 # sect 1.2.2.3
2309 XStrFixedLenField("CONFOUNDER", b"", length=8),
2310 ]
2311
2312 def default_payload_class(self, payload):
2313 return conf.padding_layer
2314
2315
2316_InitialContextTokens[b"\x02\x01"] = KRB_GSS_Wrap_RFC1964
2317
2318
2319# RFC 1964 - sect 1.2.2
2320
2321
2322class KRB_GSS_Delete_sec_context_RFC1964(Packet):
2323 name = "Kerberos v5 GSS_Delete_sec_context (RFC1964)"
2324 fields_desc = KRB_GSS_MIC_RFC1964.fields_desc
2325
2326
2327_InitialContextTokens[b"\x01\x02"] = KRB_GSS_Delete_sec_context_RFC1964
2328
2329
2330# RFC 4121 - sect 4.2.2
2331_KRB5_GSS_Flags = [
2332 "SentByAcceptor",
2333 "Sealed",
2334 "AcceptorSubkey",
2335]
2336
2337
2338# RFC 4121 - sect 4.2.6.1
2339
2340
2341class KRB_GSS_MIC(Packet):
2342 name = "Kerberos v5 MIC Token"
2343 fields_desc = [
2344 FlagsField("Flags", 0, 8, _KRB5_GSS_Flags),
2345 XStrFixedLenField("Filler", b"\xff\xff\xff\xff\xff", length=5),
2346 LongField("SND_SEQ", 0), # Big endian
2347 XStrField("SGN_CKSUM", b"\x00" * 12),
2348 ]
2349
2350 def default_payload_class(self, payload):
2351 return conf.padding_layer
2352
2353
2354_InitialContextTokens[b"\x04\x04"] = KRB_GSS_MIC
2355
2356
2357# RFC 4121 - sect 4.2.6.2
2358
2359
2360class KRB_GSS_Wrap(Packet):
2361 name = "Kerberos v5 Wrap Token"
2362 fields_desc = [
2363 FlagsField("Flags", 0, 8, _KRB5_GSS_Flags),
2364 XByteField("Filler", 0xFF),
2365 ShortField("EC", 0), # Big endian
2366 ShortField("RRC", 0), # Big endian
2367 LongField("SND_SEQ", 0), # Big endian
2368 MultipleTypeField(
2369 [
2370 (
2371 XStrField("Data", b""),
2372 lambda pkt: pkt.Flags.Sealed,
2373 )
2374 ],
2375 XStrLenField("Data", b"", length_from=lambda pkt: pkt.EC),
2376 ),
2377 ]
2378
2379 def default_payload_class(self, payload):
2380 return conf.padding_layer
2381
2382
2383_InitialContextTokens[b"\x05\x04"] = KRB_GSS_Wrap
2384
2385
2386# Kerberos IAKERB - draft-ietf-kitten-iakerb-03
2387
2388
2389class IAKERB_HEADER(ASN1_Packet):
2390 ASN1_codec = ASN1_Codecs.BER
2391 ASN1_root = ASN1F_SEQUENCE(
2392 Realm("targetRealm", "", explicit_tag=0xA1),
2393 ASN1F_optional(
2394 ASN1F_STRING("cookie", None, explicit_tag=0xA2),
2395 ),
2396 )
2397
2398
2399_InitialContextTokens[b"\x05\x01"] = IAKERB_HEADER
2400
2401
2402# Register for GSSAPI
2403
2404# Kerberos 5
2405_GSSAPI_OIDS["1.2.840.113554.1.2.2"] = KRB_InnerToken
2406_GSSAPI_SIGNATURE_OIDS["1.2.840.113554.1.2.2"] = KRB_InnerToken
2407# Kerberos 5 - U2U
2408_GSSAPI_OIDS["1.2.840.113554.1.2.2.3"] = KRB_InnerToken
2409# Kerberos 5 - IAKERB
2410_GSSAPI_OIDS["1.3.6.1.5.2.5"] = KRB_InnerToken
2411
2412
2413# Entry class
2414
2415# RFC4120 sect 5.10
2416
2417
2418class Kerberos(ASN1_Packet):
2419 ASN1_codec = ASN1_Codecs.BER
2420 ASN1_root = ASN1F_CHOICE(
2421 "root",
2422 None,
2423 # RFC4120
2424 KRB_GSSAPI_Token, # [APPLICATION 0]
2425 KRB_Ticket, # [APPLICATION 1]
2426 KRB_Authenticator, # [APPLICATION 2]
2427 KRB_AS_REQ, # [APPLICATION 10]
2428 KRB_AS_REP, # [APPLICATION 11]
2429 KRB_TGS_REQ, # [APPLICATION 12]
2430 KRB_TGS_REP, # [APPLICATION 13]
2431 KRB_AP_REQ, # [APPLICATION 14]
2432 KRB_AP_REP, # [APPLICATION 15]
2433 # RFC4120
2434 KRB_ERROR, # [APPLICATION 30]
2435 )
2436
2437 def mysummary(self):
2438 return self.root.summary()
2439
2440
2441bind_bottom_up(UDP, Kerberos, sport=88)
2442bind_bottom_up(UDP, Kerberos, dport=88)
2443bind_layers(UDP, Kerberos, sport=88, dport=88)
2444
2445_InitialContextTokens[b"\x01\x00"] = KRB_AP_REQ
2446_InitialContextTokens[b"\x02\x00"] = KRB_AP_REP
2447_InitialContextTokens[b"\x03\x00"] = KRB_ERROR
2448_InitialContextTokens[b"\x04\x00"] = KRB_TGT_REQ
2449_InitialContextTokens[b"\x04\x01"] = KRB_TGT_REP
2450
2451
2452# RFC4120 sect 7.2.2
2453
2454
2455class KerberosTCPHeader(Packet):
2456 # According to RFC 5021, first bit to 1 has a special meaning and
2457 # negotiates Kerberos TCP extensions... But apart from rfc6251 no one used that
2458 # https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-4
2459 fields_desc = [LenField("len", None, fmt="!I")]
2460
2461 @classmethod
2462 def tcp_reassemble(cls, data, *args, **kwargs):
2463 if len(data) < 4:
2464 return None
2465 length = struct.unpack("!I", data[:4])[0]
2466 if len(data) == length + 4:
2467 return cls(data)
2468
2469
2470bind_layers(KerberosTCPHeader, Kerberos)
2471
2472bind_bottom_up(TCP, KerberosTCPHeader, sport=88)
2473bind_layers(TCP, KerberosTCPHeader, dport=88)
2474
2475
2476# RFC3244 sect 2
2477
2478
2479class KPASSWD_REQ(Packet):
2480 fields_desc = [
2481 ShortField("len", None),
2482 ShortField("pvno", 0xFF80),
2483 ShortField("apreqlen", None),
2484 PacketLenField(
2485 "apreq", KRB_AP_REQ(), KRB_AP_REQ, length_from=lambda pkt: pkt.apreqlen
2486 ),
2487 ConditionalField(
2488 PacketLenField(
2489 "krbpriv",
2490 KRB_PRIV(),
2491 KRB_PRIV,
2492 length_from=lambda pkt: pkt.len - 6 - pkt.apreqlen,
2493 ),
2494 lambda pkt: pkt.apreqlen != 0,
2495 ),
2496 ConditionalField(
2497 PacketLenField(
2498 "error", KRB_ERROR(), KRB_ERROR, length_from=lambda pkt: pkt.len - 6
2499 ),
2500 lambda pkt: pkt.apreqlen == 0,
2501 ),
2502 ]
2503
2504 def post_build(self, p, pay):
2505 if self.len is None:
2506 p = struct.pack("!H", len(p)) + p[2:]
2507 if self.apreqlen is None and self.krbpriv is not None:
2508 p = p[:4] + struct.pack("!H", len(self.apreq)) + p[6:]
2509 return p + pay
2510
2511
2512class ChangePasswdData(ASN1_Packet):
2513 ASN1_codec = ASN1_Codecs.BER
2514 ASN1_root = ASN1F_SEQUENCE(
2515 ASN1F_STRING("newpasswd", ASN1_STRING(""), explicit_tag=0xA0),
2516 ASN1F_optional(
2517 ASN1F_PACKET("targname", None, PrincipalName, explicit_tag=0xA1)
2518 ),
2519 ASN1F_optional(Realm("targrealm", None, explicit_tag=0xA2)),
2520 )
2521
2522
2523class KPASSWD_REP(Packet):
2524 fields_desc = [
2525 ShortField("len", None),
2526 ShortField("pvno", 0x0001),
2527 ShortField("apreplen", None),
2528 PacketLenField(
2529 "aprep", KRB_AP_REP(), KRB_AP_REP, length_from=lambda pkt: pkt.apreplen
2530 ),
2531 ConditionalField(
2532 PacketLenField(
2533 "krbpriv",
2534 KRB_PRIV(),
2535 KRB_PRIV,
2536 length_from=lambda pkt: pkt.len - 6 - pkt.apreplen,
2537 ),
2538 lambda pkt: pkt.apreplen != 0,
2539 ),
2540 ConditionalField(
2541 PacketLenField(
2542 "error", KRB_ERROR(), KRB_ERROR, length_from=lambda pkt: pkt.len - 6
2543 ),
2544 lambda pkt: pkt.apreplen == 0,
2545 ),
2546 ]
2547
2548 def post_build(self, p, pay):
2549 if self.len is None:
2550 p = struct.pack("!H", len(p)) + p[2:]
2551 if self.apreplen is None and self.krbpriv is not None:
2552 p = p[:4] + struct.pack("!H", len(self.aprep)) + p[6:]
2553 return p + pay
2554
2555 def answers(self, other):
2556 return isinstance(other, KPASSWD_REQ)
2557
2558
2559KPASSWD_RESULTS = {
2560 0: "KRB5_KPASSWD_SUCCESS",
2561 1: "KRB5_KPASSWD_MALFORMED",
2562 2: "KRB5_KPASSWD_HARDERROR",
2563 3: "KRB5_KPASSWD_AUTHERROR",
2564 4: "KRB5_KPASSWD_SOFTERROR",
2565 5: "KRB5_KPASSWD_ACCESSDENIED",
2566 6: "KRB5_KPASSWD_BAD_VERSION",
2567 7: "KRB5_KPASSWD_INITIAL_FLAG_NEEDED",
2568}
2569
2570
2571class KPasswdRepData(Packet):
2572 fields_desc = [
2573 ShortEnumField("resultCode", 0, KPASSWD_RESULTS),
2574 StrField("resultString", ""),
2575 ]
2576
2577
2578class Kpasswd(Packet):
2579 @classmethod
2580 def dispatch_hook(cls, _pkt=None, *args, **kargs):
2581 if _pkt and len(_pkt) >= 4:
2582 if _pkt[2:4] == b"\xff\x80":
2583 return KPASSWD_REQ
2584 elif _pkt[2:4] == b"\x00\x01":
2585 asn1_tag = BER_id_dec(_pkt[6:8])[0] & 0x1F
2586 if asn1_tag == 14:
2587 return KPASSWD_REQ
2588 elif asn1_tag == 15:
2589 return KPASSWD_REP
2590 return KPASSWD_REQ
2591
2592
2593bind_bottom_up(UDP, Kpasswd, sport=464)
2594bind_bottom_up(UDP, Kpasswd, dport=464)
2595bind_top_down(UDP, KPASSWD_REQ, sport=464, dport=464)
2596bind_top_down(UDP, KPASSWD_REP, sport=464, dport=464)
2597
2598
2599class KpasswdTCPHeader(Packet):
2600 fields_desc = [LenField("len", None, fmt="!I")]
2601
2602 @classmethod
2603 def tcp_reassemble(cls, data, *args, **kwargs):
2604 if len(data) < 4:
2605 return None
2606 length = struct.unpack("!I", data[:4])[0]
2607 if len(data) == length + 4:
2608 return cls(data)
2609
2610
2611bind_layers(KpasswdTCPHeader, Kpasswd)
2612
2613bind_bottom_up(TCP, KpasswdTCPHeader, sport=464)
2614bind_layers(TCP, KpasswdTCPHeader, dport=464)
2615
2616# [MS-KKDCP]
2617
2618
2619class _KerbMessage_Field(ASN1F_STRING_PacketField):
2620 def m2i(self, pkt, s):
2621 val = super(_KerbMessage_Field, self).m2i(pkt, s)
2622 if not val[0].val:
2623 return val
2624 return KerberosTCPHeader(val[0].val, _underlayer=pkt), val[1]
2625
2626
2627class KDC_PROXY_MESSAGE(ASN1_Packet):
2628 ASN1_codec = ASN1_Codecs.BER
2629 ASN1_root = ASN1F_SEQUENCE(
2630 _KerbMessage_Field("kerbMessage", "", explicit_tag=0xA0),
2631 ASN1F_optional(Realm("targetDomain", None, explicit_tag=0xA1)),
2632 ASN1F_optional(
2633 ASN1F_FLAGS(
2634 "dclocatorHint",
2635 None,
2636 FlagsField("", 0, -32, _NV_VERSION).names,
2637 explicit_tag=0xA2,
2638 )
2639 ),
2640 )
2641
2642
2643class KdcProxySocket(SuperSocket):
2644 """
2645 This is a wrapper of a HTTP_Client that does KKDCP proxying,
2646 disguised as a SuperSocket to be compatible with the rest of the KerberosClient.
2647 """
2648
2649 def __init__(
2650 self,
2651 url,
2652 targetDomain,
2653 dclocatorHint=None,
2654 no_check_certificate=False,
2655 **kwargs,
2656 ):
2657 self.url = url
2658 self.targetDomain = targetDomain
2659 self.dclocatorHint = dclocatorHint
2660 self.no_check_certificate = no_check_certificate
2661 self.queue = deque()
2662 super(KdcProxySocket, self).__init__(**kwargs)
2663
2664 def recv(self, x=None):
2665 return self.queue.popleft()
2666
2667 def send(self, x, **kwargs):
2668 from scapy.layers.http import HTTP_Client
2669
2670 cli = HTTP_Client(no_check_certificate=self.no_check_certificate)
2671 try:
2672 # sr it via the web client
2673 resp = cli.request(
2674 self.url,
2675 Method="POST",
2676 data=bytes(
2677 # Wrap request in KDC_PROXY_MESSAGE
2678 KDC_PROXY_MESSAGE(
2679 kerbMessage=bytes(x),
2680 targetDomain=ASN1_GENERAL_STRING(self.targetDomain.encode()),
2681 # dclocatorHint is optional
2682 dclocatorHint=self.dclocatorHint,
2683 )
2684 ),
2685 http_headers={
2686 "Cache-Control": "no-cache",
2687 "Pragma": "no-cache",
2688 "User-Agent": "kerberos/1.0",
2689 },
2690 )
2691 if resp and conf.raw_layer in resp:
2692 # Parse the payload
2693 resp = KDC_PROXY_MESSAGE(resp.load).kerbMessage
2694 # We have an answer, queue it.
2695 self.queue.append(resp)
2696 else:
2697 raise EOFError
2698 finally:
2699 cli.close()
2700
2701 @staticmethod
2702 def select(sockets, remain=None):
2703 return [x for x in sockets if isinstance(x, KdcProxySocket) and x.queue]
2704
2705
2706# Util functions
2707
2708
2709class KerberosClient(Automaton):
2710 """
2711 :param mode: the mode to use for the client (default: AS_REQ).
2712 :param ip: the IP of the DC (default: discovered by dclocator)
2713 :param upn: the UPN of the client.
2714 :param password: the password of the client.
2715 :param key: the Key of the client (instead of the password)
2716 :param realm: the realm of the domain. (default: from the UPN)
2717 :param spn: the SPN to request in a TGS-REQ
2718 :param ticket: the existing ticket to use in a TGS-REQ
2719 :param host: the name of the host doing the request
2720 :param renew: sets the Renew flag in a TGS-REQ
2721 :param additional_tickets: in U2U or S4U2Proxy, the additional tickets
2722 :param u2u: sets the U2U flag
2723 :param for_user: the UPN of another user in TGS-REQ, to do a S4U2Self
2724 :param s4u2proxy: sets the S4U2Proxy flag
2725 :param dmsa: sets the 'unconditional delegation' mode for DMSA TGT retrieval
2726 :param kdc_proxy: specify a KDC proxy url
2727 :param kdc_proxy_no_check_certificate: do not check the KDC proxy certificate
2728 :param fast: use FAST armoring
2729 :param armor_ticket: an external ticket to use for armoring
2730 :param armor_ticket_upn: the UPN of the client of the armoring ticket
2731 :param armor_ticket_skey: the session Key object of the armoring ticket
2732 :param etypes: specify the list of encryption types to support
2733 :param port: the Kerberos port (default 88)
2734 :param timeout: timeout of each request (default 5)
2735 """
2736
2737 RES_AS_MODE = namedtuple("AS_Result", ["asrep", "sessionkey", "kdcrep"])
2738 RES_TGS_MODE = namedtuple("TGS_Result", ["tgsrep", "sessionkey", "kdcrep"])
2739
2740 class MODE(IntEnum):
2741 AS_REQ = 0
2742 TGS_REQ = 1
2743 GET_SALT = 2
2744
2745 def __init__(
2746 self,
2747 mode=MODE.AS_REQ,
2748 ip=None,
2749 upn=None,
2750 password=None,
2751 key=None,
2752 realm=None,
2753 spn=None,
2754 ticket=None,
2755 host=None,
2756 renew=False,
2757 additional_tickets=[],
2758 u2u=False,
2759 for_user=None,
2760 s4u2proxy=False,
2761 dmsa=False,
2762 kdc_proxy=None,
2763 kdc_proxy_no_check_certificate=False,
2764 fast=False,
2765 armor_ticket=None,
2766 armor_ticket_upn=None,
2767 armor_ticket_skey=None,
2768 key_list_req=[],
2769 etypes=None,
2770 port=88,
2771 timeout=5,
2772 **kwargs,
2773 ):
2774 import scapy.libs.rfc3961 # Trigger error if any # noqa: F401
2775 from scapy.layers.ldap import dclocator
2776
2777 if not upn:
2778 raise ValueError("Invalid upn")
2779 if not spn:
2780 raise ValueError("Invalid spn")
2781 if realm is None:
2782 if mode in [self.MODE.AS_REQ, self.MODE.GET_SALT]:
2783 _, realm = _parse_upn(upn)
2784 elif mode == self.MODE.TGS_REQ:
2785 _, realm = _parse_spn(spn)
2786 if not realm and ticket:
2787 # if no realm is specified, but there's a ticket, take the realm
2788 # of the ticket.
2789 realm = ticket.realm.val.decode()
2790 else:
2791 raise ValueError("Invalid realm")
2792
2793 if mode in [self.MODE.AS_REQ, self.MODE.GET_SALT]:
2794 if not host:
2795 raise ValueError("Invalid host")
2796 elif mode == self.MODE.TGS_REQ:
2797 if not ticket:
2798 raise ValueError("Invalid ticket")
2799
2800 if not ip and not kdc_proxy:
2801 # No KDC IP provided. Find it by querying the DNS
2802 ip = dclocator(
2803 realm,
2804 timeout=timeout,
2805 # Use connect mode instead of ldap for compatibility
2806 # with MIT kerberos servers
2807 mode="connect",
2808 port=port,
2809 debug=kwargs.get("debug", 0),
2810 ).ip
2811
2812 if fast:
2813 if mode == self.MODE.AS_REQ:
2814 # Requires an external ticket
2815 if not armor_ticket or not armor_ticket_upn or not armor_ticket_skey:
2816 raise ValueError(
2817 "Implicit armoring is not possible on AS-REQ: "
2818 "please provide the 3 required armor arguments"
2819 )
2820 elif mode == self.MODE.TGS_REQ:
2821 if armor_ticket and (not armor_ticket_upn or not armor_ticket_skey):
2822 raise ValueError(
2823 "Cannot specify armor_ticket without armor_ticket_{upn,skey}"
2824 )
2825
2826 if mode == self.MODE.GET_SALT:
2827 if etypes is not None:
2828 raise ValueError("Cannot specify etypes in GET_SALT mode !")
2829
2830 from scapy.libs.rfc3961 import EncryptionType
2831
2832 etypes = [
2833 EncryptionType.AES256_CTS_HMAC_SHA1_96,
2834 EncryptionType.AES128_CTS_HMAC_SHA1_96,
2835 ]
2836 elif etypes is None:
2837 from scapy.libs.rfc3961 import EncryptionType
2838
2839 etypes = [
2840 EncryptionType.AES256_CTS_HMAC_SHA1_96,
2841 EncryptionType.AES128_CTS_HMAC_SHA1_96,
2842 EncryptionType.RC4_HMAC,
2843 EncryptionType.RC4_HMAC_EXP,
2844 EncryptionType.DES_CBC_MD5,
2845 ]
2846 self.etypes = etypes
2847
2848 self.mode = mode
2849
2850 self.result = None # Result
2851
2852 self._timeout = timeout
2853 self._ip = ip
2854 self._port = port
2855 self.kdc_proxy = kdc_proxy
2856 self.kdc_proxy_no_check_certificate = kdc_proxy_no_check_certificate
2857
2858 if self.mode in [self.MODE.AS_REQ, self.MODE.GET_SALT]:
2859 self.host = host.upper()
2860 self.password = password and bytes_encode(password)
2861 self.spn = spn
2862 self.upn = upn
2863 self.realm = realm.upper()
2864 self.ticket = ticket
2865 self.fast = fast
2866 self.armor_ticket = armor_ticket
2867 self.armor_ticket_upn = armor_ticket_upn
2868 self.armor_ticket_skey = armor_ticket_skey
2869 self.key_list_req = key_list_req
2870 self.renew = renew
2871 self.additional_tickets = additional_tickets # U2U + S4U2Proxy
2872 self.u2u = u2u # U2U
2873 self.for_user = for_user # FOR-USER
2874 self.s4u2proxy = s4u2proxy # S4U2Proxy
2875 self.dmsa = dmsa # DMSA
2876 self.key = key
2877 self.subkey = None # In the AP-REQ authenticator
2878 self.replykey = None # Key used for reply
2879 # See RFC4120 - sect 7.2.2
2880 # This marks whether we should follow-up after an EOF
2881 self.should_followup = False
2882 # This marks that we sent a FAST-req and are awaiting for an answer
2883 self.fast_req_sent = False
2884 # Session parameters
2885 self.pre_auth = False
2886 self.fast_rep = None
2887 self.fast_error = None
2888 self.fast_skey = None # The random subkey used for fast
2889 self.fast_armorkey = None # The armor key
2890 self.fxcookie = None
2891
2892 sock = self._connect()
2893 super(KerberosClient, self).__init__(
2894 sock=sock,
2895 **kwargs,
2896 )
2897
2898 def _connect(self):
2899 if self.kdc_proxy:
2900 # If we are using a KDC Proxy, wrap the socket with the KdcProxySocket,
2901 # that takes our messages and transport them over HTTP.
2902 sock = KdcProxySocket(
2903 url=self.kdc_proxy,
2904 targetDomain=self.realm,
2905 no_check_certificate=self.kdc_proxy_no_check_certificate,
2906 )
2907 else:
2908 sock = socket.socket()
2909 sock.settimeout(self._timeout)
2910 sock.connect((self._ip, self._port))
2911 sock = StreamSocket(sock, KerberosTCPHeader)
2912 return sock
2913
2914 def send(self, pkt):
2915 super(KerberosClient, self).send(KerberosTCPHeader() / pkt)
2916
2917 def _base_kdc_req(self, now_time):
2918 kdcreq = KRB_KDC_REQ_BODY(
2919 etype=[ASN1_INTEGER(x) for x in self.etypes],
2920 additionalTickets=None,
2921 # Windows default
2922 kdcOptions="forwardable+renewable+canonicalize+renewable-ok",
2923 cname=None,
2924 realm=ASN1_GENERAL_STRING(self.realm),
2925 till=ASN1_GENERALIZED_TIME(now_time + timedelta(hours=10)),
2926 rtime=ASN1_GENERALIZED_TIME(now_time + timedelta(hours=10)),
2927 nonce=ASN1_INTEGER(RandNum(0, 0x7FFFFFFF)._fix()),
2928 )
2929 if self.renew:
2930 kdcreq.kdcOptions.set(30, 1) # set 'renew' (bit 30)
2931 return kdcreq
2932
2933 def calc_fast_armorkey(self):
2934 """
2935 Calculate and return the FAST armorkey
2936 """
2937 # Generate a random key of the same type than ticket_skey
2938 from scapy.libs.rfc3961 import Key, KRB_FX_CF2
2939
2940 if self.mode == self.MODE.AS_REQ:
2941 # AS-REQ mode
2942 self.fast_skey = Key.new_random_key(self.armor_ticket_skey.etype)
2943
2944 self.fast_armorkey = KRB_FX_CF2(
2945 self.fast_skey,
2946 self.armor_ticket_skey,
2947 b"subkeyarmor",
2948 b"ticketarmor",
2949 )
2950 elif self.mode == self.MODE.TGS_REQ:
2951 # TGS-REQ: 2 cases
2952
2953 self.subkey = Key.new_random_key(self.key.etype)
2954
2955 if not self.armor_ticket:
2956 # Case 1: Implicit armoring
2957 self.fast_armorkey = KRB_FX_CF2(
2958 self.subkey,
2959 self.key,
2960 b"subkeyarmor",
2961 b"ticketarmor",
2962 )
2963 else:
2964 # Case 2: Explicit armoring, in "Compounded Identity mode".
2965 # This is a Microsoft extension: see [MS-KILE] sect 3.3.5.7.4
2966
2967 self.fast_skey = Key.new_random_key(self.armor_ticket_skey.etype)
2968
2969 explicit_armor_key = KRB_FX_CF2(
2970 self.fast_skey,
2971 self.armor_ticket_skey,
2972 b"subkeyarmor",
2973 b"ticketarmor",
2974 )
2975
2976 self.fast_armorkey = KRB_FX_CF2(
2977 explicit_armor_key,
2978 self.subkey,
2979 b"explicitarmor",
2980 b"tgsarmor",
2981 )
2982
2983 def _fast_wrap(self, kdc_req, padata, now_time, pa_tgsreq_ap=None):
2984 """
2985 :param kdc_req: the KDC_REQ_BODY to wrap
2986 :param padata: the list of PADATA to wrap
2987 :param now_time: the current timestamp used by the client
2988 """
2989
2990 # Create the PA Fast request wrapper
2991 pafastreq = PA_FX_FAST_REQUEST(
2992 armoredData=KrbFastArmoredReq(
2993 reqChecksum=Checksum(),
2994 encFastReq=EncryptedData(),
2995 )
2996 )
2997
2998 if self.armor_ticket is not None:
2999 # EXPLICIT mode only (AS-REQ or TGS-REQ)
3000
3001 pafastreq.armoredData.armor = KrbFastArmor(
3002 armorType=1, # FX_FAST_ARMOR_AP_REQUEST
3003 armorValue=KRB_AP_REQ(
3004 ticket=self.armor_ticket,
3005 authenticator=EncryptedData(),
3006 ),
3007 )
3008
3009 # Populate the authenticator. Note the client is the wrapper
3010 _, crealm = _parse_upn(self.armor_ticket_upn)
3011 authenticator = KRB_Authenticator(
3012 crealm=ASN1_GENERAL_STRING(crealm),
3013 cname=PrincipalName.fromUPN(self.armor_ticket_upn),
3014 cksum=None,
3015 ctime=ASN1_GENERALIZED_TIME(now_time),
3016 cusec=ASN1_INTEGER(0),
3017 subkey=EncryptionKey.fromKey(self.fast_skey),
3018 seqNumber=ASN1_INTEGER(0),
3019 encAuthorizationData=None,
3020 )
3021 pafastreq.armoredData.armor.armorValue.authenticator.encrypt(
3022 self.armor_ticket_skey,
3023 authenticator,
3024 )
3025
3026 # Sign the fast request wrapper
3027 if self.mode == self.MODE.TGS_REQ:
3028 # "for a TGS-REQ, it is performed over the type AP-
3029 # REQ in the PA-TGS-REQ padata of the TGS request"
3030 pafastreq.armoredData.reqChecksum.make(
3031 self.fast_armorkey,
3032 bytes(pa_tgsreq_ap),
3033 )
3034 else:
3035 # "For an AS-REQ, it is performed over the type KDC-REQ-
3036 # BODY for the req-body field of the KDC-REQ structure of the
3037 # containing message"
3038 pafastreq.armoredData.reqChecksum.make(
3039 self.fast_armorkey,
3040 bytes(kdc_req),
3041 )
3042
3043 # Build and encrypt the Fast request
3044 fastreq = KrbFastReq(
3045 padata=padata,
3046 reqBody=kdc_req,
3047 )
3048 pafastreq.armoredData.encFastReq.encrypt(
3049 self.fast_armorkey,
3050 fastreq,
3051 )
3052
3053 # Return the PADATA
3054 return PADATA(
3055 padataType=ASN1_INTEGER(136), # PA-FX-FAST
3056 padataValue=pafastreq,
3057 )
3058
3059 def as_req(self):
3060 now_time = datetime.now(timezone.utc).replace(microsecond=0)
3061
3062 # 1. Build and populate KDC-REQ
3063 kdc_req = self._base_kdc_req(now_time=now_time)
3064 kdc_req.addresses = [
3065 HostAddress(
3066 addrType=ASN1_INTEGER(20), # Netbios
3067 address=ASN1_STRING(self.host.ljust(16, " ")),
3068 )
3069 ]
3070 kdc_req.cname = PrincipalName.fromUPN(self.upn)
3071 kdc_req.sname = PrincipalName.fromSPN(self.spn)
3072
3073 # 2. Build the list of PADATA
3074 padata = [
3075 PADATA(
3076 padataType=ASN1_INTEGER(128), # PA-PAC-REQUEST
3077 padataValue=PA_PAC_REQUEST(includePac=ASN1_BOOLEAN(-1)),
3078 )
3079 ]
3080
3081 # Cookie support
3082 if self.fxcookie:
3083 padata.insert(
3084 0,
3085 PADATA(
3086 padataType=133, # PA-FX-COOKIE
3087 padataValue=self.fxcookie,
3088 ),
3089 )
3090
3091 # FAST
3092 if self.fast:
3093 # Calculate the armor key
3094 self.calc_fast_armorkey()
3095
3096 # [MS-KILE] sect 3.2.5.5
3097 # "When sending the AS-REQ, add a PA-PAC-OPTIONS [167]"
3098 padata.append(
3099 PADATA(
3100 padataType=ASN1_INTEGER(167), # PA-PAC-OPTIONS
3101 padataValue=PA_PAC_OPTIONS(
3102 options="Claims",
3103 ),
3104 )
3105 )
3106
3107 # Pre-auth is requested
3108 if self.pre_auth:
3109 if self.fast:
3110 # Special FAST factor
3111 # RFC6113 sect 5.4.6
3112 from scapy.libs.rfc3961 import KRB_FX_CF2
3113
3114 # Calculate the 'challenge key'
3115 ts_key = KRB_FX_CF2(
3116 self.fast_armorkey,
3117 self.key,
3118 b"clientchallengearmor",
3119 b"challengelongterm",
3120 )
3121 pafactor = PADATA(
3122 padataType=138, # PA-ENCRYPTED-CHALLENGE
3123 padataValue=EncryptedData(),
3124 )
3125 else:
3126 # Usual 'timestamp' factor
3127 ts_key = self.key
3128 pafactor = PADATA(
3129 padataType=2, # PA-ENC-TIMESTAMP
3130 padataValue=EncryptedData(),
3131 )
3132 pafactor.padataValue.encrypt(
3133 ts_key,
3134 PA_ENC_TS_ENC(patimestamp=ASN1_GENERALIZED_TIME(now_time)),
3135 )
3136 padata.insert(
3137 0,
3138 pafactor,
3139 )
3140
3141 # FAST support
3142 if self.fast:
3143 # We are using RFC6113's FAST armoring. The PADATA's are therefore
3144 # hidden inside the encrypted section.
3145 padata = [
3146 self._fast_wrap(
3147 kdc_req=kdc_req,
3148 padata=padata,
3149 now_time=now_time,
3150 )
3151 ]
3152
3153 # 3. Build the request
3154 asreq = Kerberos(
3155 root=KRB_AS_REQ(
3156 padata=padata,
3157 reqBody=kdc_req,
3158 )
3159 )
3160
3161 # Note the reply key
3162 self.replykey = self.key
3163
3164 return asreq
3165
3166 def tgs_req(self):
3167 now_time = datetime.now(timezone.utc).replace(microsecond=0)
3168
3169 # Compute armor key for FAST
3170 if self.fast:
3171 self.calc_fast_armorkey()
3172
3173 # 1. Build and populate KDC-REQ
3174 kdc_req = self._base_kdc_req(now_time=now_time)
3175 kdc_req.sname = PrincipalName.fromSPN(self.spn)
3176
3177 # Additional tickets
3178 if self.additional_tickets:
3179 kdc_req.additionalTickets = self.additional_tickets
3180
3181 # U2U
3182 if self.u2u:
3183 kdc_req.kdcOptions.set(28, 1) # set 'enc-tkt-in-skey' (bit 28)
3184
3185 # 2. Build the list of PADATA
3186 padata = []
3187
3188 # [MS-SFU] FOR-USER extension
3189 if self.for_user is not None:
3190 from scapy.libs.rfc3961 import ChecksumType, EncryptionType
3191
3192 # [MS-SFU] note 4:
3193 # "Windows Vista, Windows Server 2008, Windows 7, and Windows Server
3194 # 2008 R2 send the PA-S4U-X509-USER padata type alone if the user's
3195 # certificate is available.
3196 # If the user's certificate is not available, it sends both the
3197 # PA-S4U-X509-USER padata type and the PA-FOR-USER padata type.
3198 # When the PA-S4U-X509-USER padata type is used without the user's
3199 # certificate, the certificate field is not present."
3200
3201 # 1. Add PA_S4U_X509_USER
3202 pasfux509 = PA_S4U_X509_USER(
3203 userId=S4UUserID(
3204 nonce=kdc_req.nonce,
3205 # [MS-SFU] note 5:
3206 # "Windows S4U clients always set this option."
3207 options="USE_REPLY_KEY_USAGE",
3208 cname=PrincipalName.fromUPN(self.for_user),
3209 crealm=ASN1_GENERAL_STRING(_parse_upn(self.for_user)[1]),
3210 subjectCertificate=None, # TODO
3211 ),
3212 checksum=Checksum(),
3213 )
3214
3215 if self.dmsa:
3216 # DMSA = set UNCONDITIONAL_DELEGATION to 1
3217 pasfux509.userId.options.set(4, 1)
3218
3219 if self.key.etype in [EncryptionType.RC4_HMAC, EncryptionType.RC4_HMAC_EXP]:
3220 # "if the key's encryption type is RC4_HMAC_NT (23) the checksum type
3221 # is rsa-md4 (2) as defined in section 6.2.6 of [RFC3961]."
3222 pasfux509.checksum.make(
3223 self.key,
3224 bytes(pasfux509.userId),
3225 cksumtype=ChecksumType.RSA_MD4,
3226 )
3227 else:
3228 pasfux509.checksum.make(
3229 self.key,
3230 bytes(pasfux509.userId),
3231 )
3232 padata.append(
3233 PADATA(
3234 padataType=ASN1_INTEGER(130), # PA-FOR-X509-USER
3235 padataValue=pasfux509,
3236 )
3237 )
3238
3239 # 2. Add PA_FOR_USER
3240 if True: # XXX user's certificate is not available.
3241 paforuser = PA_FOR_USER(
3242 userName=PrincipalName.fromUPN(self.for_user),
3243 userRealm=ASN1_GENERAL_STRING(_parse_upn(self.for_user)[1]),
3244 cksum=Checksum(),
3245 )
3246 S4UByteArray = struct.pack( # [MS-SFU] sect 2.2.1
3247 "<I", paforuser.userName.nameType.val
3248 ) + (
3249 (
3250 "".join(x.val for x in paforuser.userName.nameString)
3251 + paforuser.userRealm.val
3252 + paforuser.authPackage.val
3253 ).encode()
3254 )
3255 paforuser.cksum.make(
3256 self.key,
3257 S4UByteArray,
3258 cksumtype=ChecksumType.HMAC_MD5,
3259 )
3260 padata.append(
3261 PADATA(
3262 padataType=ASN1_INTEGER(129), # PA-FOR-USER
3263 padataValue=paforuser,
3264 )
3265 )
3266
3267 # [MS-SFU] S4U2proxy - sect 3.1.5.2.1
3268 if self.s4u2proxy:
3269 # "PA-PAC-OPTIONS with resource-based constrained-delegation bit set"
3270 padata.append(
3271 PADATA(
3272 padataType=ASN1_INTEGER(167), # PA-PAC-OPTIONS
3273 padataValue=PA_PAC_OPTIONS(
3274 options="Resource-based-constrained-delegation",
3275 ),
3276 )
3277 )
3278 # "kdc-options field: MUST include the new cname-in-addl-tkt options flag"
3279 kdc_req.kdcOptions.set(14, 1)
3280
3281 # [MS-KILE] 2.2.11 KERB-KEY-LIST-REQ
3282 if self.key_list_req:
3283 padata.append(
3284 PADATA(
3285 padataType=ASN1_INTEGER(161), # KERB-KEY-LIST-REQ
3286 padataValue=KERB_KEY_LIST_REQ(
3287 keytypes=[ASN1_INTEGER(x) for x in self.key_list_req]
3288 ),
3289 )
3290 )
3291
3292 # 3. Build the AP-req inside a PA
3293 apreq = KRB_AP_REQ(ticket=self.ticket, authenticator=EncryptedData())
3294 pa_tgs_req = PADATA(
3295 padataType=ASN1_INTEGER(1), # PA-TGS-REQ
3296 padataValue=apreq,
3297 )
3298
3299 # 4. Populate it's authenticator
3300 _, crealm = _parse_upn(self.upn)
3301 authenticator = KRB_Authenticator(
3302 crealm=ASN1_GENERAL_STRING(crealm),
3303 cname=PrincipalName.fromUPN(self.upn),
3304 cksum=None,
3305 ctime=ASN1_GENERALIZED_TIME(now_time),
3306 cusec=ASN1_INTEGER(0),
3307 subkey=EncryptionKey.fromKey(self.subkey) if self.subkey else None,
3308 seqNumber=None,
3309 encAuthorizationData=None,
3310 )
3311
3312 # Compute checksum
3313 if self.key.cksumtype:
3314 authenticator.cksum = Checksum()
3315 authenticator.cksum.make(
3316 self.key,
3317 bytes(kdc_req),
3318 )
3319
3320 # Encrypt authenticator
3321 apreq.authenticator.encrypt(self.key, authenticator)
3322
3323 # 5. Process FAST if required
3324 if self.fast:
3325 padata = [
3326 self._fast_wrap(
3327 kdc_req=kdc_req,
3328 padata=padata,
3329 now_time=now_time,
3330 pa_tgsreq_ap=apreq,
3331 )
3332 ]
3333
3334 # 6. Add the final PADATA
3335 padata.append(pa_tgs_req)
3336
3337 # 7. Build the request
3338 tgsreq = Kerberos(
3339 root=KRB_TGS_REQ(
3340 padata=padata,
3341 reqBody=kdc_req,
3342 )
3343 )
3344
3345 # Note the reply key
3346 if self.subkey:
3347 self.replykey = self.subkey
3348 else:
3349 self.replykey = self.key
3350
3351 return tgsreq
3352
3353 @ATMT.state(initial=1)
3354 def BEGIN(self):
3355 pass
3356
3357 @ATMT.condition(BEGIN)
3358 def should_send_as_req(self):
3359 if self.mode in [self.MODE.AS_REQ, self.MODE.GET_SALT]:
3360 raise self.SENT_AS_REQ()
3361
3362 @ATMT.condition(BEGIN)
3363 def should_send_tgs_req(self):
3364 if self.mode == self.MODE.TGS_REQ:
3365 raise self.SENT_TGS_REQ()
3366
3367 @ATMT.action(should_send_as_req)
3368 def send_as_req(self):
3369 self.send(self.as_req())
3370
3371 @ATMT.action(should_send_tgs_req)
3372 def send_tgs_req(self):
3373 self.send(self.tgs_req())
3374
3375 @ATMT.state()
3376 def SENT_AS_REQ(self):
3377 pass
3378
3379 @ATMT.state()
3380 def SENT_TGS_REQ(self):
3381 pass
3382
3383 def _process_padatas_and_key(self, padatas):
3384 from scapy.libs.rfc3961 import EncryptionType, Key, KRB_FX_CF2
3385
3386 etype = None
3387 salt = b""
3388 # 1. Process pa-data
3389 if padatas is not None:
3390 for padata in padatas:
3391 if padata.padataType == 0x13 and etype is None: # PA-ETYPE-INFO2
3392 elt = padata.padataValue.seq[0]
3393 if elt.etype.val in self.etypes:
3394 etype = elt.etype.val
3395 if etype != EncryptionType.RC4_HMAC:
3396 salt = elt.salt.val
3397 elif padata.padataType == 133: # PA-FX-COOKIE
3398 self.fxcookie = padata.padataValue
3399 elif padata.padataType == 136: # PA-FX-FAST
3400 if isinstance(padata.padataValue, PA_FX_FAST_REPLY):
3401 self.fast_rep = (
3402 padata.padataValue.armoredData.encFastRep.decrypt(
3403 self.fast_armorkey,
3404 )
3405 )
3406 elif padata.padataType == 137: # PA-FX-ERROR
3407 self.fast_error = padata.padataValue
3408 elif padata.padataType == 130: # PA-FOR-X509-USER
3409 # Verify S4U checksum
3410 key_usage_number = None
3411 pasfux509 = padata.padataValue
3412 # [MS-SFU] sect 2.2.2
3413 # "In a reply, indicates that it was signed with key usage 27"
3414 if pasfux509.userId.options.val[2] == "1": # USE_REPLY_KEY_USAGE
3415 key_usage_number = 27
3416 pasfux509.checksum.verify(
3417 self.key,
3418 bytes(pasfux509.userId),
3419 key_usage_number=key_usage_number,
3420 )
3421
3422 # 2. Update the current key if necessary
3423
3424 # Compute key if not already provided
3425 if self.key is None and etype is not None:
3426 self.key = Key.string_to_key(
3427 etype,
3428 self.password,
3429 salt,
3430 )
3431
3432 # Update the key with the fast reply, if necessary
3433 if self.fast_rep and self.fast_rep.strengthenKey:
3434 # "The strengthen-key field MAY be set in an AS reply"
3435 self.replykey = KRB_FX_CF2(
3436 self.fast_rep.strengthenKey.toKey(),
3437 self.replykey,
3438 b"strengthenkey",
3439 b"replykey",
3440 )
3441
3442 @ATMT.receive_condition(SENT_AS_REQ, prio=0)
3443 def receive_salt_mode(self, pkt):
3444 # This is only for "Salt-Mode", a mode where we get the salt then
3445 # exit.
3446 if self.mode == self.MODE.GET_SALT:
3447 if Kerberos not in pkt:
3448 raise self.FINAL()
3449 if not isinstance(pkt.root, KRB_ERROR):
3450 log_runtime.error("Pre-auth is likely disabled !")
3451 raise self.FINAL()
3452 if pkt.root.errorCode == 25: # KDC_ERR_PREAUTH_REQUIRED
3453 for padata in pkt.root.eData.seq:
3454 if padata.padataType == 0x13: # PA-ETYPE-INFO2
3455 elt = padata.padataValue.seq[0]
3456 if elt.etype.val in self.etypes:
3457 self.result = elt.salt.val
3458 raise self.FINAL()
3459 else:
3460 log_runtime.error("Failed to retrieve the salt !")
3461 raise self.FINAL()
3462
3463 @ATMT.receive_condition(SENT_AS_REQ, prio=1)
3464 def receive_krb_error_as_req(self, pkt):
3465 # We check for Kerberos errors.
3466 # There is a special case for PREAUTH_REQUIRED error, which means that preauth
3467 # is required and we need to do a second exchange.
3468 if Kerberos in pkt and isinstance(pkt.root, KRB_ERROR):
3469 # Process PAs if available
3470 if pkt.root.eData and isinstance(pkt.root.eData, MethodData):
3471 self._process_padatas_and_key(pkt.root.eData.seq)
3472
3473 # Special case for FAST errors
3474 if self.fast_rep:
3475 # This is actually a fast response error !
3476 frep, self.fast_rep = self.fast_rep, None
3477 # Re-process PAs
3478 self._process_padatas_and_key(frep.padata)
3479 # Extract real Kerberos error from FAST message
3480 ferr = Kerberos(root=self.fast_error)
3481 self.fast_error = None
3482 # Recurse
3483 self.receive_krb_error_as_req(ferr)
3484 return
3485
3486 if pkt.root.errorCode == 25: # KDC_ERR_PREAUTH_REQUIRED
3487 if not self.key:
3488 log_runtime.error(
3489 "Got 'KDC_ERR_PREAUTH_REQUIRED', "
3490 "but no possible key could be computed."
3491 )
3492 raise self.FINAL()
3493 self.should_followup = True
3494 self.pre_auth = True
3495 raise self.BEGIN()
3496 else:
3497 log_runtime.error("Received KRB_ERROR")
3498 pkt.show()
3499 raise self.FINAL()
3500
3501 @ATMT.receive_condition(SENT_AS_REQ, prio=2)
3502 def receive_as_rep(self, pkt):
3503 if Kerberos in pkt and isinstance(pkt.root, KRB_AS_REP):
3504 raise self.FINAL().action_parameters(pkt)
3505
3506 @ATMT.eof(SENT_AS_REQ)
3507 def retry_after_eof_in_apreq(self):
3508 if self.should_followup:
3509 # Reconnect and Restart
3510 self.should_followup = False
3511 self.update_sock(self._connect())
3512 raise self.BEGIN()
3513 else:
3514 log_runtime.error("Socket was closed in an unexpected state")
3515 raise self.FINAL()
3516
3517 @ATMT.action(receive_as_rep)
3518 def decrypt_as_rep(self, pkt):
3519 self._process_padatas_and_key(pkt.root.padata)
3520 if not self.pre_auth:
3521 log_runtime.warning("Pre-authentication was disabled for this account !")
3522
3523 # Process FAST response
3524 if self.fast_rep:
3525 # Verify the ticket-checksum
3526 self.fast_rep.finished.ticketChecksum.verify(
3527 self.fast_armorkey,
3528 bytes(pkt.root.ticket),
3529 )
3530 self.fast_rep = None
3531 elif self.fast:
3532 raise ValueError("Answer was not FAST ! Is it supported?")
3533
3534 # Decrypt AS-REP response
3535 enc = pkt.root.encPart
3536 res = enc.decrypt(self.replykey)
3537 self.result = self.RES_AS_MODE(pkt.root, res.key.toKey(), res)
3538
3539 @ATMT.receive_condition(SENT_TGS_REQ)
3540 def receive_krb_error_tgs_req(self, pkt):
3541 if Kerberos in pkt and isinstance(pkt.root, KRB_ERROR):
3542 # Process PAs if available
3543 if pkt.root.eData and isinstance(pkt.root.eData, MethodData):
3544 self._process_padatas_and_key(pkt.root.eData.seq)
3545
3546 if self.fast_rep:
3547 # This is actually a fast response error !
3548 frep, self.fast_rep = self.fast_rep, None
3549 # Re-process PAs
3550 self._process_padatas_and_key(frep.padata)
3551 # Extract real Kerberos error from FAST message
3552 ferr = Kerberos(root=self.fast_error)
3553 self.fast_error = None
3554 # Recurse
3555 self.receive_krb_error_tgs_req(ferr)
3556 return
3557
3558 if (
3559 pkt.root.errorCode == 0x07
3560 and isinstance(pkt.root.eData, KERB_ERROR_UNK)
3561 and pkt.root.eData.dataType == -128
3562 ):
3563 log_runtime.warning(
3564 "KDC requires U2U for SPN '%s' !" % pkt.root.getSPN()
3565 )
3566 else:
3567 log_runtime.warning("Received KRB_ERROR")
3568 pkt.show()
3569 raise self.FINAL()
3570
3571 @ATMT.receive_condition(SENT_TGS_REQ)
3572 def receive_tgs_rep(self, pkt):
3573 if Kerberos in pkt and isinstance(pkt.root, KRB_TGS_REP):
3574 if (
3575 not self.renew
3576 and not self.dmsa
3577 and pkt.root.ticket.sname.nameString[0].val == b"krbtgt"
3578 ):
3579 log_runtime.warning("Received a cross-realm referral ticket !")
3580 raise self.FINAL().action_parameters(pkt)
3581
3582 @ATMT.action(receive_tgs_rep)
3583 def decrypt_tgs_rep(self, pkt):
3584 self._process_padatas_and_key(pkt.root.padata)
3585
3586 # Process FAST response
3587 if self.fast_rep:
3588 # Verify the ticket-checksum
3589 self.fast_rep.finished.ticketChecksum.verify(
3590 self.fast_armorkey,
3591 bytes(pkt.root.ticket),
3592 )
3593 self.fast_rep = None
3594 elif self.fast:
3595 raise ValueError("Answer was not FAST ! Is it supported?")
3596
3597 # Decrypt TGS-REP response
3598 enc = pkt.root.encPart
3599 if self.subkey:
3600 # "In a TGS-REP message, the key
3601 # usage value is 8 if the TGS session key is used, or 9 if a TGS
3602 # authenticator subkey is used."
3603 res = enc.decrypt(self.replykey, key_usage_number=9, cls=EncTGSRepPart)
3604 else:
3605 res = enc.decrypt(self.replykey)
3606
3607 # Store result
3608 self.result = self.RES_TGS_MODE(pkt.root, res.key.toKey(), res)
3609
3610 @ATMT.state(final=1)
3611 def FINAL(self):
3612 pass
3613
3614
3615def _parse_upn(upn):
3616 """
3617 Extract the username and realm from full UPN
3618 """
3619 m = re.match(r"^([^@\\/]+)(@|\\)([^@\\/]+)$", upn)
3620 if not m:
3621 err = "Invalid UPN: '%s'" % upn
3622 if "/" in upn:
3623 err += ". Did you mean '%s' ?" % upn.replace("/", "\\")
3624 elif "@" not in upn and "\\" not in upn:
3625 err += ". Provide domain as so: '%s@domain.local'" % upn
3626 raise ValueError(err)
3627 if m.group(2) == "@":
3628 user = m.group(1)
3629 domain = m.group(3)
3630 else:
3631 user = m.group(3)
3632 domain = m.group(1)
3633 return user, domain
3634
3635
3636def _parse_spn(spn):
3637 """
3638 Extract ServiceName and realm from full SPN
3639 """
3640 # See [MS-ADTS] sect 2.2.21 for SPN format. We discard the servicename.
3641 m = re.match(r"^((?:[^@\\/]+)/(?:[^@\\/]+))(?:/[^@\\/]+)?(?:@([^@\\/]+))?$", spn)
3642 if not m:
3643 try:
3644 # If SPN is a UPN, we are doing U2U :D
3645 return _parse_upn(spn)
3646 except ValueError:
3647 raise ValueError("Invalid SPN: '%s'" % spn)
3648 return m.group(1), m.group(2)
3649
3650
3651def _spn_are_equal(spn1, spn2):
3652 """
3653 Check that two SPNs are equal.
3654 """
3655 spn1, _ = _parse_spn(spn1)
3656 spn2, _ = _parse_spn(spn2)
3657 return spn1.lower() == spn2.lower()
3658
3659
3660def krb_as_req(
3661 upn, spn=None, ip=None, key=None, password=None, realm=None, host="WIN10", **kwargs
3662):
3663 r"""
3664 Kerberos AS-Req
3665
3666 :param upn: the user principal name formatted as "DOMAIN\user", "DOMAIN/user"
3667 or "user@DOMAIN"
3668 :param spn: (optional) the full service principal name.
3669 Defaults to "krbtgt/<realm>"
3670 :param ip: the KDC ip. (optional. If not provided, Scapy will query the DNS for
3671 _kerberos._tcp.dc._msdcs.domain.local).
3672 :param key: (optional) pass the Key object.
3673 :param password: (optional) otherwise, pass the user's password
3674 :param realm: (optional) the realm to use. Otherwise use the one from UPN.
3675 :param host: (optional) the host performing the AS-Req. WIN10 by default.
3676
3677 :return: returns a named tuple (asrep=<...>, sessionkey=<...>)
3678
3679 Example::
3680
3681 >>> # The KDC is on 192.168.122.17, we ask a TGT for user1
3682 >>> krb_as_req("user1@DOMAIN.LOCAL", "192.168.122.17", password="Password1")
3683
3684 Equivalent::
3685
3686 >>> from scapy.libs.rfc3961 import Key, EncryptionType
3687 >>> key = Key(EncryptionType.AES256_CTS_HMAC_SHA1_96, key=hex_bytes("6d0748c546
3688 ...: f4e99205e78f8da7681d4ec5520ae4815543720c2a647c1ae814c9"))
3689 >>> krb_as_req("user1@DOMAIN.LOCAL", "192.168.122.17", key=key)
3690 """
3691 if realm is None:
3692 _, realm = _parse_upn(upn)
3693 if key is None:
3694 if password is None:
3695 try:
3696 from prompt_toolkit import prompt
3697
3698 password = prompt("Enter password: ", is_password=True)
3699 except ImportError:
3700 password = input("Enter password: ")
3701 cli = KerberosClient(
3702 mode=KerberosClient.MODE.AS_REQ,
3703 realm=realm,
3704 ip=ip,
3705 spn=spn or "krbtgt/" + realm,
3706 host=host,
3707 upn=upn,
3708 password=password,
3709 key=key,
3710 **kwargs,
3711 )
3712 cli.run()
3713 cli.stop()
3714 return cli.result
3715
3716
3717def krb_tgs_req(
3718 upn,
3719 spn,
3720 sessionkey,
3721 ticket,
3722 ip=None,
3723 renew=False,
3724 realm=None,
3725 additional_tickets=[],
3726 u2u=False,
3727 etypes=None,
3728 for_user=None,
3729 s4u2proxy=False,
3730 **kwargs,
3731):
3732 r"""
3733 Kerberos TGS-Req
3734
3735 :param upn: the user principal name formatted as "DOMAIN\user", "DOMAIN/user"
3736 or "user@DOMAIN"
3737 :param spn: the full service principal name (e.g. "cifs/srv1")
3738 :param sessionkey: the session key retrieved from the tgt
3739 :param ticket: the tgt ticket
3740 :param ip: the KDC ip. (optional. If not provided, Scapy will query the DNS for
3741 _kerberos._tcp.dc._msdcs.domain.local).
3742 :param renew: ask for renewal
3743 :param realm: (optional) the realm to use. Otherwise use the one from SPN.
3744 :param additional_tickets: (optional) a list of additional tickets to pass.
3745 :param u2u: (optional) if specified, enable U2U and request the ticket to be
3746 signed using the session key from the first additional ticket.
3747 :param etypes: array of EncryptionType values.
3748 By default: AES128, AES256, RC4, DES_MD5
3749 :param for_user: a user principal name to request the ticket for. This is the
3750 S4U2Self extension.
3751
3752 :return: returns a named tuple (tgsrep=<...>, sessionkey=<...>)
3753
3754 Example::
3755
3756 >>> # The KDC is on 192.168.122.17, we ask a TGT for user1
3757 >>> krb_as_req("user1@DOMAIN.LOCAL", "192.168.122.17", password="Password1")
3758
3759 Equivalent::
3760
3761 >>> from scapy.libs.rfc3961 import Key, EncryptionType
3762 >>> key = Key(EncryptionType.AES256_CTS_HMAC_SHA1_96, key=hex_bytes("6d0748c546
3763 ...: f4e99205e78f8da7681d4ec5520ae4815543720c2a647c1ae814c9"))
3764 >>> krb_as_req("user1@DOMAIN.LOCAL", "192.168.122.17", key=key)
3765 """
3766 cli = KerberosClient(
3767 mode=KerberosClient.MODE.TGS_REQ,
3768 realm=realm,
3769 upn=upn,
3770 ip=ip,
3771 spn=spn,
3772 key=sessionkey,
3773 ticket=ticket,
3774 renew=renew,
3775 additional_tickets=additional_tickets,
3776 u2u=u2u,
3777 etypes=etypes,
3778 for_user=for_user,
3779 s4u2proxy=s4u2proxy,
3780 **kwargs,
3781 )
3782 cli.run()
3783 cli.stop()
3784 return cli.result
3785
3786
3787def krb_as_and_tgs(upn, spn, ip=None, key=None, password=None, **kwargs):
3788 """
3789 Kerberos AS-Req then TGS-Req
3790 """
3791 res = krb_as_req(upn=upn, ip=ip, key=key, password=password, **kwargs)
3792 if not res:
3793 return
3794 return krb_tgs_req(
3795 upn=upn,
3796 spn=spn,
3797 sessionkey=res.sessionkey,
3798 ticket=res.asrep.ticket,
3799 ip=ip,
3800 **kwargs,
3801 )
3802
3803
3804def krb_get_salt(upn, ip=None, realm=None, host="WIN10", **kwargs):
3805 """
3806 Kerberos AS-Req only to get the salt associated with the UPN.
3807 """
3808 if realm is None:
3809 _, realm = _parse_upn(upn)
3810 cli = KerberosClient(
3811 mode=KerberosClient.MODE.GET_SALT,
3812 realm=realm,
3813 ip=ip,
3814 spn="krbtgt/" + realm,
3815 upn=upn,
3816 host=host,
3817 **kwargs,
3818 )
3819 cli.run()
3820 cli.stop()
3821 return cli.result
3822
3823
3824def kpasswd(
3825 upn,
3826 targetupn=None,
3827 ip=None,
3828 password=None,
3829 newpassword=None,
3830 key=None,
3831 ticket=None,
3832 realm=None,
3833 ssp=None,
3834 setpassword=None,
3835 timeout=3,
3836 port=464,
3837 debug=0,
3838 **kwargs,
3839):
3840 """
3841 Change a password using RFC3244's Kerberos Set / Change Password.
3842
3843 :param upn: the UPN to use for authentication
3844 :param targetupn: (optional) the UPN to change the password of. If not specified,
3845 same as upn.
3846 :param ip: the KDC ip. (optional. If not provided, Scapy will query the DNS for
3847 _kerberos._tcp.dc._msdcs.domain.local).
3848 :param key: (optional) pass the Key object.
3849 :param ticket: (optional) a ticket to use. Either a TGT or ST for kadmin/changepw.
3850 :param password: (optional) otherwise, pass the user's password
3851 :param realm: (optional) the realm to use. Otherwise use the one from UPN.
3852 :param setpassword: (optional) use "Set Password" mechanism.
3853 :param ssp: (optional) a Kerberos SSP for the service kadmin/changepw@REALM.
3854 If provided, you probably don't need anything else. Otherwise built.
3855 """
3856 from scapy.layers.ldap import dclocator
3857
3858 if not realm:
3859 _, realm = _parse_upn(upn)
3860 spn = "kadmin/changepw@%s" % realm
3861 if ip is None:
3862 ip = dclocator(
3863 realm,
3864 timeout=timeout,
3865 # Use connect mode instead of ldap for compatibility
3866 # with MIT kerberos servers
3867 mode="connect",
3868 port=port,
3869 debug=debug,
3870 ).ip
3871 if ssp is None and ticket is not None:
3872 tktspn = ticket.getSPN().split("/")[0]
3873 assert tktspn in ["krbtgt", "kadmin"], "Unexpected ticket type ! %s" % tktspn
3874 if tktspn == "krbtgt":
3875 log_runtime.info(
3876 "Using 'Set Password' mode. This only works with admin privileges."
3877 )
3878 setpassword = True
3879 resp = krb_tgs_req(
3880 upn=upn,
3881 spn=spn,
3882 ticket=ticket,
3883 sessionkey=key,
3884 ip=ip,
3885 debug=debug,
3886 )
3887 if resp is None:
3888 return
3889 ticket = resp.tgsrep.ticket
3890 key = resp.sessionkey
3891 if setpassword is None:
3892 setpassword = bool(targetupn)
3893 elif setpassword and targetupn is None:
3894 targetupn = upn
3895 assert setpassword or not targetupn, "Cannot use targetupn in changepassword mode !"
3896 # Get a ticket for kadmin/changepw
3897 if ssp is None:
3898 if ticket is None:
3899 # Get a ticket for kadmin/changepw through AS-REQ
3900 resp = krb_as_req(
3901 upn=upn,
3902 spn=spn,
3903 key=key,
3904 ip=ip,
3905 password=password,
3906 debug=debug,
3907 )
3908 if resp is None:
3909 return
3910 ticket = resp.asrep.ticket
3911 key = resp.sessionkey
3912 ssp = KerberosSSP(
3913 UPN=upn,
3914 SPN=spn,
3915 ST=ticket,
3916 KEY=key,
3917 DC_IP=ip,
3918 debug=debug,
3919 **kwargs,
3920 )
3921 Context, tok, negResult = ssp.GSS_Init_sec_context(
3922 None,
3923 req_flags=0, # No GSS_C_MUTUAL_FLAG
3924 )
3925 if negResult != GSS_S_CONTINUE_NEEDED:
3926 warning("SSP failed on initial GSS_Init_sec_context !")
3927 if tok:
3928 tok.show()
3929 return
3930 apreq = tok.innerToken.root
3931 # Connect
3932 sock = socket.socket()
3933 sock.settimeout(timeout)
3934 sock.connect((ip, port))
3935 sock = StreamSocket(sock, KpasswdTCPHeader)
3936 # Do KPASSWD request
3937 if newpassword is None:
3938 try:
3939 from prompt_toolkit import prompt
3940
3941 newpassword = prompt("Enter NEW password: ", is_password=True)
3942 except ImportError:
3943 newpassword = input("Enter NEW password: ")
3944 krbpriv = KRB_PRIV(encPart=EncryptedData())
3945 krbpriv.encPart.encrypt(
3946 Context.KrbSessionKey,
3947 EncKrbPrivPart(
3948 sAddress=HostAddress(
3949 addrType=ASN1_INTEGER(2), # IPv4
3950 address=ASN1_STRING(b"\xc0\xa8\x00e"),
3951 ),
3952 userData=ASN1_STRING(
3953 bytes(
3954 ChangePasswdData(
3955 newpasswd=newpassword,
3956 targname=PrincipalName.fromUPN(targetupn),
3957 targrealm=realm,
3958 )
3959 )
3960 if setpassword
3961 else newpassword
3962 ),
3963 timestamp=None,
3964 usec=None,
3965 seqNumber=Context.SendSeqNum,
3966 ),
3967 )
3968 resp = sock.sr1(
3969 KpasswdTCPHeader()
3970 / KPASSWD_REQ(
3971 pvno=0xFF80 if setpassword else 1,
3972 apreq=apreq,
3973 krbpriv=krbpriv,
3974 ),
3975 timeout=timeout,
3976 verbose=0,
3977 )
3978 # Verify KPASSWD response
3979 if not resp:
3980 raise TimeoutError("KPASSWD_REQ timed out !")
3981 if KPASSWD_REP not in resp:
3982 resp.show()
3983 raise ValueError("Invalid response to KPASSWD_REQ !")
3984 Context, tok, negResult = ssp.GSS_Init_sec_context(Context, resp.aprep)
3985 if negResult != GSS_S_COMPLETE:
3986 warning("SSP failed on subsequent GSS_Init_sec_context !")
3987 if tok:
3988 tok.show()
3989 return
3990 # Parse answer KRB_PRIV
3991 krbanswer = resp.krbpriv.encPart.decrypt(Context.KrbSessionKey)
3992 userRep = KPasswdRepData(krbanswer.userData.val)
3993 if userRep.resultCode != 0:
3994 warning(userRep.sprintf("KPASSWD failed !"))
3995 userRep.show()
3996 return
3997 print(userRep.sprintf("%resultCode%"))
3998
3999
4000# SSP
4001
4002
4003class KerberosSSP(SSP):
4004 """
4005 The KerberosSSP
4006
4007 Client settings:
4008
4009 :param ST: the service ticket to use for access.
4010 If not provided, will be retrieved
4011 :param SPN: the SPN of the service to use
4012 :param UPN: The client UPN
4013 :param DC_IP: (optional) is ST+KEY are not provided, will need to contact
4014 the KDC at this IP. If not provided, will perform dc locator.
4015 :param TGT: (optional) pass a TGT to use to get the ST.
4016 :param KEY: the session key associated with the ST if it is provided,
4017 OR the session key associated with the TGT
4018 OR the kerberos key associated with the UPN
4019 :param PASSWORD: (optional) if a UPN is provided and not a KEY, this is the
4020 password of the UPN.
4021 :param U2U: (optional) use U2U when requesting the ST.
4022
4023 Server settings:
4024
4025 :param SPN: the SPN of the service to use.
4026 :param KEY: the kerberos key to use to decrypt the AP-req
4027 :param UPN: (optional) the UPN, if used in U2U mode.
4028 :param TGT: (optional) pass a TGT to use for U2U.
4029 :param DC_IP: (optional) if TGT is not provided, request one on the KDC at
4030 this IP using using the KEY when using U2U.
4031 """
4032
4033 oid = "1.2.840.113554.1.2.2"
4034 auth_type = 0x10
4035
4036 class STATE(SSP.STATE):
4037 INIT = 1
4038 CLI_SENT_TGTREQ = 2
4039 CLI_SENT_APREQ = 3
4040 CLI_RCVD_APREP = 4
4041 SRV_SENT_APREP = 5
4042
4043 class CONTEXT(SSP.CONTEXT):
4044 __slots__ = [
4045 "SessionKey",
4046 "ServerHostname",
4047 "U2U",
4048 "KrbSessionKey", # raw Key object
4049 "STSessionKey", # raw ST Key object (for DCE_STYLE)
4050 "SeqNum", # for AP
4051 "SendSeqNum", # for MIC
4052 "RecvSeqNum", # for MIC
4053 "IsAcceptor",
4054 "SendSealKeyUsage",
4055 "SendSignKeyUsage",
4056 "RecvSealKeyUsage",
4057 "RecvSignKeyUsage",
4058 # server-only
4059 "UPN",
4060 "PAC",
4061 ]
4062
4063 def __init__(self, IsAcceptor, req_flags=None):
4064 self.state = KerberosSSP.STATE.INIT
4065 self.SessionKey = None
4066 self.ServerHostname = None
4067 self.U2U = False
4068 self.SendSeqNum = 0
4069 self.RecvSeqNum = 0
4070 self.KrbSessionKey = None
4071 self.STSessionKey = None
4072 self.IsAcceptor = IsAcceptor
4073 self.UPN = None
4074 self.PAC = None
4075 # [RFC 4121] sect 2
4076 if IsAcceptor:
4077 self.SendSealKeyUsage = 22
4078 self.SendSignKeyUsage = 23
4079 self.RecvSealKeyUsage = 24
4080 self.RecvSignKeyUsage = 25
4081 else:
4082 self.SendSealKeyUsage = 24
4083 self.SendSignKeyUsage = 25
4084 self.RecvSealKeyUsage = 22
4085 self.RecvSignKeyUsage = 23
4086 super(KerberosSSP.CONTEXT, self).__init__(req_flags=req_flags)
4087
4088 def clifailure(self):
4089 self.__init__(self.IsAcceptor, req_flags=self.flags)
4090
4091 def __repr__(self):
4092 if self.U2U:
4093 return "KerberosSSP-U2U"
4094 return "KerberosSSP"
4095
4096 def __init__(
4097 self,
4098 ST=None,
4099 UPN=None,
4100 PASSWORD=None,
4101 U2U=False,
4102 KEY=None,
4103 SPN=None,
4104 TGT=None,
4105 DC_IP=None,
4106 SKEY_TYPE=None,
4107 debug=0,
4108 **kwargs,
4109 ):
4110 self.ST = ST
4111 self.UPN = UPN
4112 self.KEY = KEY
4113 self.SPN = SPN
4114 self.TGT = TGT
4115 self.PASSWORD = PASSWORD
4116 self.U2U = U2U
4117 self.DC_IP = DC_IP
4118 self.debug = debug
4119 if SKEY_TYPE is None:
4120 from scapy.libs.rfc3961 import EncryptionType
4121
4122 SKEY_TYPE = EncryptionType.AES128_CTS_HMAC_SHA1_96
4123 self.SKEY_TYPE = SKEY_TYPE
4124 super(KerberosSSP, self).__init__(**kwargs)
4125
4126 def GSS_GetMICEx(self, Context, msgs, qop_req=0):
4127 """
4128 [MS-KILE] sect 3.4.5.6
4129
4130 - AES: RFC4121 sect 4.2.6.1
4131 """
4132 if Context.KrbSessionKey.etype in [17, 18]: # AES
4133 # Concatenate the ToSign
4134 ToSign = b"".join(x.data for x in msgs if x.sign)
4135 sig = KRB_InnerToken(
4136 TOK_ID=b"\x04\x04",
4137 root=KRB_GSS_MIC(
4138 Flags="AcceptorSubkey"
4139 + ("+SentByAcceptor" if Context.IsAcceptor else ""),
4140 SND_SEQ=Context.SendSeqNum,
4141 ),
4142 )
4143 ToSign += bytes(sig)[:16]
4144 sig.root.SGN_CKSUM = Context.KrbSessionKey.make_checksum(
4145 keyusage=Context.SendSignKeyUsage,
4146 text=ToSign,
4147 )
4148 else:
4149 raise NotImplementedError
4150 Context.SendSeqNum += 1
4151 return sig
4152
4153 def GSS_VerifyMICEx(self, Context, msgs, signature):
4154 """
4155 [MS-KILE] sect 3.4.5.7
4156
4157 - AES: RFC4121 sect 4.2.6.1
4158 """
4159 Context.RecvSeqNum = signature.root.SND_SEQ
4160 if Context.KrbSessionKey.etype in [17, 18]: # AES
4161 # Concatenate the ToSign
4162 ToSign = b"".join(x.data for x in msgs if x.sign)
4163 ToSign += bytes(signature)[:16]
4164 sig = Context.KrbSessionKey.make_checksum(
4165 keyusage=Context.RecvSignKeyUsage,
4166 text=ToSign,
4167 )
4168 else:
4169 raise NotImplementedError
4170 if sig != signature.root.SGN_CKSUM:
4171 raise ValueError("ERROR: Checksums don't match")
4172
4173 def GSS_WrapEx(self, Context, msgs, qop_req=0):
4174 """
4175 [MS-KILE] sect 3.4.5.4
4176
4177 - AES: RFC4121 sect 4.2.6.2 and [MS-KILE] sect 3.4.5.4.1
4178 - HMAC-RC4: RFC4757 sect 7.3 and [MS-KILE] sect 3.4.5.4.1
4179 """
4180 # Is confidentiality in use?
4181 confidentiality = (Context.flags & GSS_C_FLAGS.GSS_C_CONF_FLAG) and any(
4182 x.conf_req_flag for x in msgs
4183 )
4184 if Context.KrbSessionKey.etype in [17, 18]: # AES
4185 # Build token
4186 tok = KRB_InnerToken(
4187 TOK_ID=b"\x05\x04",
4188 root=KRB_GSS_Wrap(
4189 Flags="AcceptorSubkey"
4190 + ("+SentByAcceptor" if Context.IsAcceptor else "")
4191 + ("+Sealed" if confidentiality else ""),
4192 SND_SEQ=Context.SendSeqNum,
4193 RRC=0,
4194 ),
4195 )
4196 Context.SendSeqNum += 1
4197 # Real separation starts now: RFC4121 sect 4.2.4
4198 if confidentiality:
4199 # Confidentiality is requested (see RFC4121 sect 4.3)
4200 # {"header" | encrypt(plaintext-data | filler | "header")}
4201 # 0. Roll confounder
4202 Confounder = os.urandom(Context.KrbSessionKey.ep.blocksize)
4203 # 1. Concatenate the data to be encrypted
4204 Data = b"".join(x.data for x in msgs if x.conf_req_flag)
4205 DataLen = len(Data)
4206 # 2. Add filler
4207 # [MS-KILE] sect 3.4.5.4.1 - "For AES-SHA1 ciphers, the EC must not
4208 # be zero"
4209 tok.root.EC = ((-DataLen) % Context.KrbSessionKey.ep.blocksize) or 16
4210 Filler = b"\x00" * tok.root.EC
4211 Data += Filler
4212 # 3. Add first 16 octets of the Wrap token "header"
4213 PlainHeader = bytes(tok)[:16]
4214 Data += PlainHeader
4215 # 4. Build 'ToSign', exclusively used for checksum
4216 ToSign = Confounder
4217 ToSign += b"".join(x.data for x in msgs if x.sign)
4218 ToSign += Filler
4219 ToSign += PlainHeader
4220 # 5. Finalize token for signing
4221 # "The RRC field is [...] 28 if encryption is requested."
4222 tok.root.RRC = 28
4223 # 6. encrypt() is the encryption operation (which provides for
4224 # integrity protection)
4225 Data = Context.KrbSessionKey.encrypt(
4226 keyusage=Context.SendSealKeyUsage,
4227 plaintext=Data,
4228 confounder=Confounder,
4229 signtext=ToSign,
4230 )
4231 # 7. Rotate
4232 Data = strrot(Data, tok.root.RRC + tok.root.EC)
4233 # 8. Split (token and encrypted messages)
4234 toklen = len(Data) - DataLen
4235 tok.root.Data = Data[:toklen]
4236 offset = toklen
4237 for msg in msgs:
4238 msglen = len(msg.data)
4239 if msg.conf_req_flag:
4240 msg.data = Data[offset : offset + msglen]
4241 offset += msglen
4242 return msgs, tok
4243 else:
4244 # No confidentiality is requested
4245 # {"header" | plaintext-data | get_mic(plaintext-data | "header")}
4246 # 0. Concatenate the data
4247 Data = b"".join(x.data for x in msgs if x.sign)
4248 DataLen = len(Data)
4249 # 1. Add first 16 octets of the Wrap token "header"
4250 ToSign = Data
4251 ToSign += bytes(tok)[:16]
4252 # 2. get_mic() is the checksum operation for the required
4253 # checksum mechanism
4254 Mic = Context.KrbSessionKey.make_checksum(
4255 keyusage=Context.SendSealKeyUsage,
4256 text=ToSign,
4257 )
4258 # In Wrap tokens without confidentiality, the EC field SHALL be used
4259 # to encode the number of octets in the trailing checksum
4260 tok.root.EC = 12 # len(tok.root.Data) == 12 for AES
4261 # "The RRC field ([RFC4121] section 4.2.5) is 12 if no encryption
4262 # is requested"
4263 tok.root.RRC = 12
4264 # 3. Concat and pack
4265 for msg in msgs:
4266 if msg.sign:
4267 msg.data = b""
4268 Data = Data + Mic
4269 # 4. Rotate
4270 tok.root.Data = strrot(Data, tok.root.RRC)
4271 return msgs, tok
4272 elif Context.KrbSessionKey.etype in [23, 24]: # RC4
4273 from scapy.libs.rfc3961 import (
4274 Cipher,
4275 Hmac_MD5,
4276 _rfc1964pad,
4277 decrepit_algorithms,
4278 )
4279
4280 # Build token
4281 seq = struct.pack(">I", Context.SendSeqNum)
4282 tok = KRB_InnerToken(
4283 TOK_ID=b"\x02\x01",
4284 root=KRB_GSS_Wrap_RFC1964(
4285 SGN_ALG="HMAC",
4286 SEAL_ALG="RC4" if confidentiality else "none",
4287 SND_SEQ=seq
4288 + (
4289 # See errata
4290 b"\xff\xff\xff\xff"
4291 if Context.IsAcceptor
4292 else b"\x00\x00\x00\x00"
4293 ),
4294 ),
4295 )
4296 Context.SendSeqNum += 1
4297 # 0. Concatenate data
4298 ToSign = _rfc1964pad(b"".join(x.data for x in msgs if x.sign))
4299 ToEncrypt = b"".join(x.data for x in msgs if x.conf_req_flag)
4300 Kss = Context.KrbSessionKey.key
4301 # 1. Roll confounder
4302 Confounder = os.urandom(8)
4303 # 2. Compute the 'Kseq' key
4304 Klocal = strxor(Kss, len(Kss) * b"\xf0")
4305 if Context.KrbSessionKey.etype == 24: # EXP
4306 Kcrypt = Hmac_MD5(Klocal).digest(b"fortybits\x00" + b"\x00\x00\x00\x00")
4307 Kcrypt = Kcrypt[:7] + b"\xab" * 9
4308 else:
4309 Kcrypt = Hmac_MD5(Klocal).digest(b"\x00\x00\x00\x00")
4310 Kcrypt = Hmac_MD5(Kcrypt).digest(seq)
4311 # 3. Build SGN_CKSUM
4312 tok.root.SGN_CKSUM = Context.KrbSessionKey.make_checksum(
4313 keyusage=13, # See errata
4314 text=bytes(tok)[:8] + Confounder + ToSign,
4315 )[:8]
4316 # 4. Populate token + encrypt
4317 if confidentiality:
4318 # 'encrypt' is requested
4319 rc4 = Cipher(decrepit_algorithms.ARC4(Kcrypt), mode=None).encryptor()
4320 tok.root.CONFOUNDER = rc4.update(Confounder)
4321 Data = rc4.update(ToEncrypt)
4322 # Split encrypted data
4323 offset = 0
4324 for msg in msgs:
4325 msglen = len(msg.data)
4326 if msg.conf_req_flag:
4327 msg.data = Data[offset : offset + msglen]
4328 offset += msglen
4329 else:
4330 # 'encrypt' is not requested
4331 tok.root.CONFOUNDER = Confounder
4332 # 5. Compute the 'Kseq' key
4333 if Context.KrbSessionKey.etype == 24: # EXP
4334 Kseq = Hmac_MD5(Kss).digest(b"fortybits\x00" + b"\x00\x00\x00\x00")
4335 Kseq = Kseq[:7] + b"\xab" * 9
4336 else:
4337 Kseq = Hmac_MD5(Kss).digest(b"\x00\x00\x00\x00")
4338 Kseq = Hmac_MD5(Kseq).digest(tok.root.SGN_CKSUM)
4339 # 6. Encrypt 'SND_SEQ'
4340 rc4 = Cipher(decrepit_algorithms.ARC4(Kseq), mode=None).encryptor()
4341 tok.root.SND_SEQ = rc4.update(tok.root.SND_SEQ)
4342 # 7. Include 'InitialContextToken pseudo ASN.1 header'
4343 tok = KRB_GSSAPI_Token(
4344 MechType="1.2.840.113554.1.2.2", # Kerberos 5
4345 innerToken=tok,
4346 )
4347 return msgs, tok
4348 else:
4349 raise NotImplementedError
4350
4351 def GSS_UnwrapEx(self, Context, msgs, signature):
4352 """
4353 [MS-KILE] sect 3.4.5.5
4354
4355 - AES: RFC4121 sect 4.2.6.2
4356 - HMAC-RC4: RFC4757 sect 7.3
4357 """
4358 if Context.KrbSessionKey.etype in [17, 18]: # AES
4359 confidentiality = signature.root.Flags.Sealed
4360 # Real separation starts now: RFC4121 sect 4.2.4
4361 if confidentiality:
4362 # 0. Concatenate the data
4363 Data = signature.root.Data
4364 Data += b"".join(x.data for x in msgs if x.conf_req_flag)
4365 # 1. Un-Rotate
4366 Data = strrot(Data, signature.root.RRC + signature.root.EC, right=False)
4367
4368 # 2. Function to build 'ToSign', exclusively used for checksum
4369 def MakeToSign(Confounder, DecText):
4370 offset = 0
4371 # 2.a Confounder
4372 ToSign = Confounder
4373 # 2.b Messages
4374 for msg in msgs:
4375 msglen = len(msg.data)
4376 if msg.conf_req_flag:
4377 ToSign += DecText[offset : offset + msglen]
4378 offset += msglen
4379 elif msg.sign:
4380 ToSign += msg.data
4381 # 2.c Filler & Padding
4382 ToSign += DecText[offset:]
4383 return ToSign
4384
4385 # 3. Decrypt
4386 Data = Context.KrbSessionKey.decrypt(
4387 keyusage=Context.RecvSealKeyUsage,
4388 ciphertext=Data,
4389 presignfunc=MakeToSign,
4390 )
4391 # 4. Split
4392 Data, f16header = (
4393 Data[:-16],
4394 Data[-16:],
4395 )
4396 # 5. Check header
4397 hdr = signature.copy()
4398 hdr.root.RRC = 0
4399 if f16header != bytes(hdr)[:16]:
4400 raise ValueError("ERROR: Headers don't match")
4401 # 6. Split (and ignore filler)
4402 offset = 0
4403 for msg in msgs:
4404 msglen = len(msg.data)
4405 if msg.conf_req_flag:
4406 msg.data = Data[offset : offset + msglen]
4407 offset += msglen
4408 # Case without msgs
4409 if len(msgs) == 1 and not msgs[0].data:
4410 msgs[0].data = Data
4411 return msgs
4412 else:
4413 # No confidentiality is requested
4414 # 0. Concatenate the data
4415 Data = signature.root.Data
4416 Data += b"".join(x.data for x in msgs if x.sign)
4417 # 1. Un-Rotate
4418 Data = strrot(Data, signature.root.RRC, right=False)
4419 # 2. Split
4420 Data, Mic = Data[: -signature.root.EC], Data[-signature.root.EC :]
4421 # "Both the EC field and the RRC field in
4422 # the token header SHALL be filled with zeroes for the purpose of
4423 # calculating the checksum."
4424 ToSign = Data
4425 hdr = signature.copy()
4426 hdr.root.RRC = 0
4427 hdr.root.EC = 0
4428 # Concatenate the data
4429 ToSign += bytes(hdr)[:16]
4430 # 3. Calculate the signature
4431 sig = Context.KrbSessionKey.make_checksum(
4432 keyusage=Context.RecvSealKeyUsage,
4433 text=ToSign,
4434 )
4435 # 4. Compare
4436 if sig != Mic:
4437 raise ValueError("ERROR: Checksums don't match")
4438 # Case without msgs
4439 if len(msgs) == 1 and not msgs[0].data:
4440 msgs[0].data = Data
4441 return msgs
4442 elif Context.KrbSessionKey.etype in [23, 24]: # RC4
4443 from scapy.libs.rfc3961 import (
4444 Cipher,
4445 Hmac_MD5,
4446 _rfc1964pad,
4447 decrepit_algorithms,
4448 )
4449
4450 # Drop wrapping
4451 tok = signature.innerToken
4452
4453 # Detect confidentiality
4454 confidentiality = tok.root.SEAL_ALG != 0xFFFF
4455
4456 # 0. Concatenate data
4457 ToDecrypt = b"".join(x.data for x in msgs if x.conf_req_flag)
4458 Kss = Context.KrbSessionKey.key
4459 # 1. Compute the 'Kseq' key
4460 if Context.KrbSessionKey.etype == 24: # EXP
4461 Kseq = Hmac_MD5(Kss).digest(b"fortybits\x00" + b"\x00\x00\x00\x00")
4462 Kseq = Kseq[:7] + b"\xab" * 9
4463 else:
4464 Kseq = Hmac_MD5(Kss).digest(b"\x00\x00\x00\x00")
4465 Kseq = Hmac_MD5(Kseq).digest(tok.root.SGN_CKSUM)
4466 # 2. Decrypt 'SND_SEQ'
4467 rc4 = Cipher(decrepit_algorithms.ARC4(Kseq), mode=None).encryptor()
4468 seq = rc4.update(tok.root.SND_SEQ)[:4]
4469 # 3. Compute the 'Kcrypt' key
4470 Klocal = strxor(Kss, len(Kss) * b"\xf0")
4471 if Context.KrbSessionKey.etype == 24: # EXP
4472 Kcrypt = Hmac_MD5(Klocal).digest(b"fortybits\x00" + b"\x00\x00\x00\x00")
4473 Kcrypt = Kcrypt[:7] + b"\xab" * 9
4474 else:
4475 Kcrypt = Hmac_MD5(Klocal).digest(b"\x00\x00\x00\x00")
4476 Kcrypt = Hmac_MD5(Kcrypt).digest(seq)
4477 # 4. Decrypt
4478 if confidentiality:
4479 # 'encrypt' was requested
4480 rc4 = Cipher(decrepit_algorithms.ARC4(Kcrypt), mode=None).encryptor()
4481 Confounder = rc4.update(tok.root.CONFOUNDER)
4482 Data = rc4.update(ToDecrypt)
4483 # Split encrypted data
4484 offset = 0
4485 for msg in msgs:
4486 msglen = len(msg.data)
4487 if msg.conf_req_flag:
4488 msg.data = Data[offset : offset + msglen]
4489 offset += msglen
4490 else:
4491 # 'encrypt' was not requested
4492 Confounder = tok.root.CONFOUNDER
4493 # 5. Verify SGN_CKSUM
4494 ToSign = _rfc1964pad(b"".join(x.data for x in msgs if x.sign))
4495 Context.KrbSessionKey.verify_checksum(
4496 keyusage=13, # See errata
4497 text=bytes(tok)[:8] + Confounder + ToSign,
4498 cksum=tok.root.SGN_CKSUM,
4499 )
4500 return msgs
4501 else:
4502 raise NotImplementedError
4503
4504 def GSS_Init_sec_context(
4505 self,
4506 Context: CONTEXT,
4507 token=None,
4508 req_flags: Optional[GSS_C_FLAGS] = None,
4509 chan_bindings: GssChannelBindings = GSS_C_NO_CHANNEL_BINDINGS,
4510 ):
4511 if Context is None:
4512 # New context
4513 Context = self.CONTEXT(IsAcceptor=False, req_flags=req_flags)
4514
4515 from scapy.libs.rfc3961 import Key
4516
4517 if Context.state == self.STATE.INIT and self.U2U:
4518 # U2U - Get TGT
4519 Context.state = self.STATE.CLI_SENT_TGTREQ
4520 return (
4521 Context,
4522 KRB_GSSAPI_Token(
4523 MechType="1.2.840.113554.1.2.2.3", # U2U
4524 innerToken=KRB_InnerToken(
4525 TOK_ID=b"\x04\x00",
4526 root=KRB_TGT_REQ(),
4527 ),
4528 ),
4529 GSS_S_CONTINUE_NEEDED,
4530 )
4531
4532 if Context.state in [self.STATE.INIT, self.STATE.CLI_SENT_TGTREQ]:
4533 if not self.UPN:
4534 raise ValueError("Missing UPN attribute")
4535 # Do we have a ST?
4536 if self.ST is None:
4537 # Client sends an AP-req
4538 if not self.SPN:
4539 raise ValueError("Missing SPN attribute")
4540 additional_tickets = []
4541 if self.U2U:
4542 try:
4543 # GSSAPI / Kerberos
4544 tgt_rep = token.root.innerToken.root
4545 except AttributeError:
4546 try:
4547 # Kerberos
4548 tgt_rep = token.innerToken.root
4549 except AttributeError:
4550 return Context, None, GSS_S_DEFECTIVE_TOKEN
4551 if not isinstance(tgt_rep, KRB_TGT_REP):
4552 tgt_rep.show()
4553 raise ValueError("KerberosSSP: Unexpected token !")
4554 additional_tickets = [tgt_rep.ticket]
4555 if self.TGT is not None:
4556 if not self.KEY:
4557 raise ValueError("Cannot use TGT without the KEY")
4558 # Use TGT
4559 res = krb_tgs_req(
4560 upn=self.UPN,
4561 spn=self.SPN,
4562 ip=self.DC_IP,
4563 sessionkey=self.KEY,
4564 ticket=self.TGT,
4565 additional_tickets=additional_tickets,
4566 u2u=self.U2U,
4567 debug=self.debug,
4568 )
4569 else:
4570 # Ask for TGT then ST
4571 res = krb_as_and_tgs(
4572 upn=self.UPN,
4573 spn=self.SPN,
4574 ip=self.DC_IP,
4575 key=self.KEY,
4576 password=self.PASSWORD,
4577 additional_tickets=additional_tickets,
4578 u2u=self.U2U,
4579 debug=self.debug,
4580 )
4581 if not res:
4582 # Failed to retrieve the ticket
4583 return Context, None, GSS_S_FAILURE
4584 self.ST, self.KEY = res.tgsrep.ticket, res.sessionkey
4585 elif not self.KEY:
4586 raise ValueError("Must provide KEY with ST")
4587 Context.STSessionKey = self.KEY
4588
4589 # Save ServerHostname
4590 if len(self.ST.sname.nameString) == 2:
4591 Context.ServerHostname = self.ST.sname.nameString[1].val.decode()
4592
4593 # Build the KRB-AP
4594 apOptions = ASN1_BIT_STRING("000")
4595 if Context.flags & GSS_C_FLAGS.GSS_C_MUTUAL_FLAG:
4596 apOptions.set(2, "1") # mutual-required
4597 if self.U2U:
4598 apOptions.set(1, "1") # use-session-key
4599 Context.U2U = True
4600 ap_req = KRB_AP_REQ(
4601 apOptions=apOptions,
4602 ticket=self.ST,
4603 authenticator=EncryptedData(),
4604 )
4605
4606 # Get the current time
4607 now_time = datetime.now(timezone.utc).replace(microsecond=0)
4608 # Pick a random session key
4609 Context.KrbSessionKey = Key.new_random_key(
4610 self.SKEY_TYPE,
4611 )
4612
4613 # We use a random SendSeqNum
4614 Context.SendSeqNum = RandNum(0, 0x7FFFFFFF)._fix()
4615
4616 # Get the realm of the client
4617 _, crealm = _parse_upn(self.UPN)
4618
4619 # Build and encrypt the full KRB_Authenticator
4620 ap_req.authenticator.encrypt(
4621 Context.STSessionKey,
4622 KRB_Authenticator(
4623 crealm=crealm,
4624 cname=PrincipalName.fromUPN(self.UPN),
4625 # RFC 4121 checksum
4626 cksum=Checksum(
4627 cksumtype="KRB-AUTHENTICATOR",
4628 checksum=KRB_AuthenticatorChecksum(
4629 # RFC 4121 sect 4.1.1.2
4630 # "The Bnd field contains the MD5 hash of channel bindings"
4631 Bnd=(
4632 chan_bindings.digestMD5()
4633 if chan_bindings != GSS_C_NO_CHANNEL_BINDINGS
4634 else (b"\x00" * 16)
4635 ),
4636 Flags=int(Context.flags),
4637 ),
4638 ),
4639 ctime=ASN1_GENERALIZED_TIME(now_time),
4640 cusec=ASN1_INTEGER(0),
4641 subkey=EncryptionKey.fromKey(Context.KrbSessionKey),
4642 seqNumber=Context.SendSeqNum,
4643 encAuthorizationData=AuthorizationData(
4644 seq=[
4645 AuthorizationDataItem(
4646 adType="AD-IF-RELEVANT",
4647 adData=AuthorizationData(
4648 seq=[
4649 AuthorizationDataItem(
4650 adType="KERB-AUTH-DATA-TOKEN-RESTRICTIONS",
4651 adData=KERB_AD_RESTRICTION_ENTRY(
4652 restriction=LSAP_TOKEN_INFO_INTEGRITY(
4653 MachineID=bytes(RandBin(32))
4654 )
4655 ),
4656 ),
4657 # This isn't documented, but sent on Windows :/
4658 AuthorizationDataItem(
4659 adType="KERB-LOCAL",
4660 adData=b"\x00" * 16,
4661 ),
4662 ]
4663 + (
4664 # Channel bindings
4665 [
4666 AuthorizationDataItem(
4667 adType="AD-AUTH-DATA-AP-OPTIONS",
4668 adData=KERB_AUTH_DATA_AP_OPTIONS(
4669 apOptions="KERB_AP_OPTIONS_CBT"
4670 ),
4671 )
4672 ]
4673 if chan_bindings != GSS_C_NO_CHANNEL_BINDINGS
4674 else []
4675 )
4676 ),
4677 )
4678 ]
4679 ),
4680 ),
4681 )
4682 Context.state = self.STATE.CLI_SENT_APREQ
4683 if Context.flags & GSS_C_FLAGS.GSS_C_DCE_STYLE:
4684 # Raw kerberos DCE-STYLE
4685 return Context, ap_req, GSS_S_CONTINUE_NEEDED
4686 else:
4687 # Kerberos wrapper
4688 return (
4689 Context,
4690 KRB_GSSAPI_Token(
4691 innerToken=KRB_InnerToken(
4692 root=ap_req,
4693 )
4694 ),
4695 GSS_S_CONTINUE_NEEDED,
4696 )
4697
4698 elif Context.state == self.STATE.CLI_SENT_APREQ:
4699 if isinstance(token, KRB_AP_REP):
4700 # Raw AP_REP was passed
4701 ap_rep = token
4702 else:
4703 try:
4704 # GSSAPI / Kerberos
4705 ap_rep = token.root.innerToken.root
4706 except AttributeError:
4707 try:
4708 # Kerberos
4709 ap_rep = token.innerToken.root
4710 except AttributeError:
4711 try:
4712 # Raw kerberos DCE-STYLE
4713 ap_rep = token.root
4714 except AttributeError:
4715 return Context, None, GSS_S_DEFECTIVE_TOKEN
4716 if not isinstance(ap_rep, KRB_AP_REP):
4717 return Context, None, GSS_S_DEFECTIVE_TOKEN
4718 # Retrieve SessionKey
4719 repPart = ap_rep.encPart.decrypt(Context.STSessionKey)
4720 if repPart.subkey is not None:
4721 Context.SessionKey = repPart.subkey.keyvalue.val
4722 Context.KrbSessionKey = repPart.subkey.toKey()
4723 # OK !
4724 Context.state = self.STATE.CLI_RCVD_APREP
4725 if Context.flags & GSS_C_FLAGS.GSS_C_DCE_STYLE:
4726 # [MS-KILE] sect 3.4.5.1
4727 # The client MUST generate an additional AP exchange reply message
4728 # exactly as the server would as the final message to send to the
4729 # server.
4730 now_time = datetime.now(timezone.utc).replace(microsecond=0)
4731 cli_ap_rep = KRB_AP_REP(encPart=EncryptedData())
4732 cli_ap_rep.encPart.encrypt(
4733 Context.STSessionKey,
4734 EncAPRepPart(
4735 ctime=ASN1_GENERALIZED_TIME(now_time),
4736 seqNumber=repPart.seqNumber,
4737 subkey=None,
4738 ),
4739 )
4740 return Context, cli_ap_rep, GSS_S_COMPLETE
4741 return Context, None, GSS_S_COMPLETE
4742 elif (
4743 Context.state == self.STATE.CLI_RCVD_APREP
4744 and Context.flags & GSS_C_FLAGS.GSS_C_DCE_STYLE
4745 ):
4746 # DCE_STYLE with SPNEGOSSP
4747 return Context, None, GSS_S_COMPLETE
4748 else:
4749 raise ValueError("KerberosSSP: Unknown state")
4750
4751 def GSS_Accept_sec_context(
4752 self,
4753 Context: CONTEXT,
4754 token=None,
4755 req_flags: Optional[GSS_S_FLAGS] = GSS_S_FLAGS.GSS_S_ALLOW_MISSING_BINDINGS,
4756 chan_bindings: GssChannelBindings = GSS_C_NO_CHANNEL_BINDINGS,
4757 ):
4758 if Context is None:
4759 # New context
4760 Context = self.CONTEXT(IsAcceptor=True, req_flags=req_flags)
4761
4762 from scapy.libs.rfc3961 import Key
4763 import scapy.layers.msrpce.mspac # noqa: F401
4764
4765 if Context.state == self.STATE.INIT:
4766 if self.UPN and self.SPN:
4767 raise ValueError("Cannot use SPN and UPN at the same time !")
4768 if self.SPN and self.TGT:
4769 raise ValueError("Cannot use TGT with SPN.")
4770 if self.UPN and not self.TGT:
4771 # UPN is provided: use U2U
4772 res = krb_as_req(
4773 self.UPN,
4774 self.DC_IP,
4775 key=self.KEY,
4776 password=self.PASSWORD,
4777 )
4778 self.TGT, self.KEY = res.asrep.ticket, res.sessionkey
4779
4780 # Server receives AP-req, sends AP-rep
4781 if isinstance(token, KRB_AP_REQ):
4782 # Raw AP_REQ was passed
4783 ap_req = token
4784 else:
4785 try:
4786 # GSSAPI/Kerberos
4787 ap_req = token.root.innerToken.root
4788 except AttributeError:
4789 try:
4790 # Kerberos
4791 ap_req = token.innerToken.root
4792 except AttributeError:
4793 try:
4794 # Raw kerberos
4795 ap_req = token.root
4796 except AttributeError:
4797 return Context, None, GSS_S_DEFECTIVE_TOKEN
4798
4799 if isinstance(ap_req, KRB_TGT_REQ):
4800 # Special U2U case
4801 Context.U2U = True
4802 return (
4803 None,
4804 KRB_GSSAPI_Token(
4805 MechType="1.2.840.113554.1.2.2.3", # U2U
4806 innerToken=KRB_InnerToken(
4807 TOK_ID=b"\x04\x01",
4808 root=KRB_TGT_REP(
4809 ticket=self.TGT,
4810 ),
4811 ),
4812 ),
4813 GSS_S_CONTINUE_NEEDED,
4814 )
4815 elif not isinstance(ap_req, KRB_AP_REQ):
4816 ap_req.show()
4817 raise ValueError("Unexpected type in KerberosSSP")
4818 if not self.KEY:
4819 raise ValueError("Missing KEY attribute")
4820
4821 # If using a UPN, require U2U
4822 if self.UPN and ap_req.apOptions.val[1] != "1": # use-session-key
4823 # Required but not provided. Return an error
4824 Context.U2U = True
4825 now_time = datetime.now(timezone.utc).replace(microsecond=0)
4826 err = KRB_GSSAPI_Token(
4827 innerToken=KRB_InnerToken(
4828 TOK_ID=b"\x03\x00",
4829 root=KRB_ERROR(
4830 errorCode="KRB_AP_ERR_USER_TO_USER_REQUIRED",
4831 stime=ASN1_GENERALIZED_TIME(now_time),
4832 realm=ap_req.ticket.realm,
4833 sname=ap_req.ticket.sname,
4834 eData=KRB_TGT_REP(
4835 ticket=self.TGT,
4836 ),
4837 ),
4838 )
4839 )
4840 return Context, err, GSS_S_CONTINUE_NEEDED
4841
4842 # Validate the 'serverName' of the ticket.
4843 sname = ap_req.ticket.getSPN()
4844 our_sname = self.SPN or self.UPN
4845 if not _spn_are_equal(our_sname, sname):
4846 warning("KerberosSSP: bad server name: %s != %s" % (sname, our_sname))
4847 err = KRB_GSSAPI_Token(
4848 innerToken=KRB_InnerToken(
4849 TOK_ID=b"\x03\x00",
4850 root=KRB_ERROR(
4851 errorCode="KRB_AP_ERR_BADMATCH",
4852 stime=ASN1_GENERALIZED_TIME(now_time),
4853 realm=ap_req.ticket.realm,
4854 sname=ap_req.ticket.sname,
4855 eData=None,
4856 ),
4857 )
4858 )
4859 return Context, err, GSS_S_BAD_MECH
4860
4861 # Decrypt the ticket
4862 try:
4863 tkt = ap_req.ticket.encPart.decrypt(self.KEY)
4864 except ValueError as ex:
4865 warning("KerberosSSP: %s (bad KEY?)" % ex)
4866 now_time = datetime.now(timezone.utc).replace(microsecond=0)
4867 err = KRB_GSSAPI_Token(
4868 innerToken=KRB_InnerToken(
4869 TOK_ID=b"\x03\x00",
4870 root=KRB_ERROR(
4871 errorCode="KRB_AP_ERR_MODIFIED",
4872 stime=ASN1_GENERALIZED_TIME(now_time),
4873 realm=ap_req.ticket.realm,
4874 sname=ap_req.ticket.sname,
4875 eData=None,
4876 ),
4877 )
4878 )
4879 return Context, err, GSS_S_DEFECTIVE_TOKEN
4880
4881 # Store information about the user in the Context
4882 if tkt.authorizationData and tkt.authorizationData.seq:
4883 # Get AD-IF-RELEVANT
4884 adIfRelevant = tkt.authorizationData.getAuthData(0x1)
4885 if adIfRelevant:
4886 # Get AD-WIN2K-PAC
4887 Context.PAC = adIfRelevant.getAuthData(0x80)
4888
4889 # Get AP-REQ session key
4890 Context.STSessionKey = tkt.key.toKey()
4891 authenticator = ap_req.authenticator.decrypt(Context.STSessionKey)
4892
4893 # Compute an application session key ([MS-KILE] sect 3.1.1.2)
4894 subkey = None
4895 if ap_req.apOptions.val[2] == "1": # mutual-required
4896 appkey = Key.new_random_key(
4897 self.SKEY_TYPE,
4898 )
4899 Context.KrbSessionKey = appkey
4900 Context.SessionKey = appkey.key
4901 subkey = EncryptionKey.fromKey(appkey)
4902 else:
4903 Context.KrbSessionKey = self.KEY
4904 Context.SessionKey = self.KEY.key
4905
4906 # Eventually process the "checksum"
4907 if authenticator.cksum and authenticator.cksum.cksumtype == 0x8003:
4908 # KRB-Authenticator
4909 authcksum = authenticator.cksum.checksum
4910 Context.flags = authcksum.Flags
4911 # Check channel bindings
4912 if (
4913 chan_bindings != GSS_C_NO_CHANNEL_BINDINGS
4914 and chan_bindings.digestMD5() != authcksum.Bnd
4915 and not (
4916 GSS_S_FLAGS.GSS_S_ALLOW_MISSING_BINDINGS in req_flags
4917 and authcksum.Bnd == GSS_C_NO_CHANNEL_BINDINGS
4918 )
4919 ):
4920 # Channel binding checks failed.
4921 return Context, None, GSS_S_BAD_BINDINGS
4922 elif (
4923 chan_bindings != GSS_C_NO_CHANNEL_BINDINGS
4924 and GSS_S_FLAGS.GSS_S_ALLOW_MISSING_BINDINGS not in req_flags
4925 ):
4926 # Uhoh, we required channel bindings
4927 return Context, None, GSS_S_BAD_BINDINGS
4928
4929 # Build response (RFC4120 sect 3.2.4)
4930 ap_rep = KRB_AP_REP(encPart=EncryptedData())
4931 ap_rep.encPart.encrypt(
4932 Context.STSessionKey,
4933 EncAPRepPart(
4934 ctime=authenticator.ctime,
4935 cusec=authenticator.cusec,
4936 seqNumber=None,
4937 subkey=subkey,
4938 ),
4939 )
4940 Context.state = self.STATE.SRV_SENT_APREP
4941 if Context.flags & GSS_C_FLAGS.GSS_C_DCE_STYLE:
4942 # [MS-KILE] sect 3.4.5.1
4943 return Context, ap_rep, GSS_S_CONTINUE_NEEDED
4944 return Context, ap_rep, GSS_S_COMPLETE # success
4945 elif (
4946 Context.state == self.STATE.SRV_SENT_APREP
4947 and Context.flags & GSS_C_FLAGS.GSS_C_DCE_STYLE
4948 ):
4949 # [MS-KILE] sect 3.4.5.1
4950 # The server MUST receive the additional AP exchange reply message and
4951 # verify that the message is constructed correctly.
4952 if not token:
4953 return Context, None, GSS_S_DEFECTIVE_TOKEN
4954 # Server receives AP-req, sends AP-rep
4955 if isinstance(token, KRB_AP_REP):
4956 # Raw AP_REP was passed
4957 ap_rep = token
4958 else:
4959 try:
4960 # GSSAPI/Kerberos
4961 ap_rep = token.root.innerToken.root
4962 except AttributeError:
4963 try:
4964 # Raw Kerberos
4965 ap_rep = token.root
4966 except AttributeError:
4967 return Context, None, GSS_S_DEFECTIVE_TOKEN
4968 # Decrypt the AP-REP
4969 try:
4970 ap_rep.encPart.decrypt(Context.STSessionKey)
4971 except ValueError as ex:
4972 warning("KerberosSSP: %s (bad KEY?)" % ex)
4973 return Context, None, GSS_S_DEFECTIVE_TOKEN
4974 return Context, None, GSS_S_COMPLETE # success
4975 else:
4976 raise ValueError("KerberosSSP: Unknown state %s" % repr(Context.state))
4977
4978 def GSS_Passive(self, Context: CONTEXT, token=None):
4979 if Context is None:
4980 Context = self.CONTEXT(True)
4981 Context.passive = True
4982
4983 if Context.state == self.STATE.INIT:
4984 Context, _, status = self.GSS_Accept_sec_context(Context, token)
4985 Context.state = self.STATE.CLI_SENT_APREQ
4986 return Context, GSS_S_CONTINUE_NEEDED
4987 elif Context.state == self.STATE.CLI_SENT_APREQ:
4988 Context, _, status = self.GSS_Init_sec_context(Context, token)
4989 return Context, status
4990
4991 def GSS_Passive_set_Direction(self, Context: CONTEXT, IsAcceptor=False):
4992 if Context.IsAcceptor is not IsAcceptor:
4993 return
4994 # Swap everything
4995 Context.SendSealKeyUsage, Context.RecvSealKeyUsage = (
4996 Context.RecvSealKeyUsage,
4997 Context.SendSealKeyUsage,
4998 )
4999 Context.SendSignKeyUsage, Context.RecvSignKeyUsage = (
5000 Context.RecvSignKeyUsage,
5001 Context.SendSignKeyUsage,
5002 )
5003 Context.IsAcceptor = not Context.IsAcceptor
5004
5005 def MaximumSignatureLength(self, Context: CONTEXT):
5006 if Context.flags & GSS_C_FLAGS.GSS_C_CONF_FLAG:
5007 # TODO: support DES
5008 if Context.KrbSessionKey.etype in [17, 18]: # AES
5009 return 76
5010 elif Context.KrbSessionKey.etype in [23, 24]: # RC4_HMAC
5011 return 45
5012 else:
5013 raise NotImplementedError
5014 else:
5015 return 28
5016
5017 def canMechListMIC(self, Context: CONTEXT):
5018 return bool(Context.KrbSessionKey)