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