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