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