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