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