1# Copyright: (c) 2020, Jordan Borean (@jborean93) <jborean93@gmail.com> 2# MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 4"""Kerberos Messages 5 6Various Kerberos message structures that is used by 'python -m spnego' to unpack raw ASN.1 values and print out a 7pretty structure for end users. This code is not used in the actual spnego authentication processes and is just used 8for debugging purposes. 9""" 10 11import base64 12import collections 13import datetime 14import enum 15import struct 16import typing 17 18from spnego._asn1 import ( 19 ASN1Value, 20 TagClass, 21 extract_asn1_tlv, 22 get_sequence_value, 23 unpack_asn1, 24 unpack_asn1_bit_string, 25 unpack_asn1_general_string, 26 unpack_asn1_generalized_time, 27 unpack_asn1_integer, 28 unpack_asn1_octet_string, 29 unpack_asn1_sequence, 30 unpack_asn1_tagged_sequence, 31) 32from spnego._text import to_text 33 34 35def _enum_labels( 36 value: typing.Union[int, str, enum.Enum], 37 enum_type: typing.Optional[typing.Type] = None, 38) -> typing.Dict[int, str]: 39 """ Gets the human friendly labels of a known enum and what value they map to. """ 40 def get_labels(v): 41 return getattr(v, 'native_labels', lambda: {})() 42 43 return get_labels(enum_type) if enum_type else get_labels(value) 44 45 46def parse_enum( 47 value: typing.Union[int, str, enum.Enum], 48 enum_type: typing.Optional[typing.Type] = None, 49) -> str: 50 """ Parses an IntEnum into a human representative object of that enum. """ 51 enum_name = 'UNKNOWN' 52 53 labels = _enum_labels(value, enum_type) 54 value = int(value) if isinstance(value, int) else value 55 56 for v, name in labels.items(): 57 if value == v: 58 enum_name = name 59 break 60 61 return "%s (%s)" % (enum_name, value) 62 63 64def parse_flags( 65 value: typing.Union[int, enum.IntFlag], 66 enum_type: typing.Optional[typing.Type] = None, 67) -> typing.Dict[str, typing.Any]: 68 """ Parses an IntFlag into each flag value that is set. """ 69 raw_value = int(value) 70 flags = [] 71 72 labels = _enum_labels(value, enum_type) 73 value = int(value) 74 75 for v, name in labels.items(): 76 if value & v == v: 77 value &= ~v 78 flags.append("%s (%d)" % (name, v)) 79 80 if value != 0: 81 flags.append('UNKNOWN (%d)' % value) 82 83 return { 84 'raw': raw_value, 85 'flags': flags, 86 } 87 88 89def parse_kerberos_token( 90 token: "KerberosV5Msg", 91 secret: typing.Optional[str] = None, 92 encoding: typing.Optional[str] = None, 93) -> typing.Dict[str, typing.Any]: 94 """ Parses a KerberosV5Msg object to a dict. """ 95 if not encoding: 96 encoding = 'utf-8' 97 98 def parse_default(value: typing.Any) -> typing.Any: 99 return value 100 101 def parse_datetime(value: datetime.datetime) -> str: 102 return value.isoformat() 103 104 def parse_text(value: bytes) -> str: 105 return to_text(value, encoding=encoding, errors='replace') 106 107 def parse_bytes(value: bytes) -> str: 108 return base64.b16encode(value).decode() 109 110 def parse_principal_name(value: PrincipalName) -> typing.Dict[str, typing.Any]: 111 return { 112 'name-type': parse_enum(value.name_type), 113 'name-string': [parse_text(v) for v in value.value], 114 } 115 116 def parse_host_address(value: HostAddress) -> typing.Dict[str, typing.Any]: 117 return { 118 'addr-type': parse_enum(value.addr_type), 119 'address': parse_text(value.value), 120 } 121 122 def parse_token(value: typing.Any) -> typing.Dict[str, typing.Any]: 123 return parse_kerberos_token(value, secret, encoding) 124 125 if isinstance(token, bytes): 126 return parse_bytes(token) 127 128 msg = {} 129 for name, attr_name, attr_type in token.PARSE_MAP: 130 attr_value = getattr(token, attr_name) 131 132 parse_args = [] 133 if isinstance(attr_type, tuple): 134 parse_args.append(attr_type[1]) 135 attr_type = attr_type[0] 136 137 parse_func = { 138 ParseType.default: parse_default, 139 ParseType.enum: parse_enum, 140 ParseType.flags: parse_flags, 141 ParseType.datetime: parse_datetime, 142 ParseType.text: parse_text, 143 ParseType.bytes: parse_bytes, 144 ParseType.principal_name: parse_principal_name, 145 ParseType.host_address: parse_host_address, 146 ParseType.token: parse_token, 147 }[attr_type] 148 149 if attr_value is None: 150 parsed_value = None 151 152 elif isinstance(attr_value, list): 153 parsed_value = [parse_func(v, *parse_args) if v is not None else None for v in attr_value] 154 155 else: 156 parsed_value = parse_func(attr_value, *parse_args) 157 158 msg[name] = parsed_value 159 160 return msg 161 162 163def unpack_hostname(value: ASN1Value) -> "HostAddress": 164 """ Unpacks an ASN.1 value to a HostAddress. """ 165 s = unpack_asn1_tagged_sequence(value) 166 167 name_type = KerberosHostAddressType(get_sequence_value(s, 0, 'HostAddress', 'addr-type', unpack_asn1_integer)) 168 name = get_sequence_value(s, 1, 'HostAddress', 'address', unpack_asn1_octet_string) 169 170 return HostAddress(name_type, name) 171 172 173def unpack_principal_name(value: ASN1Value) -> "PrincipalName": 174 """ Unpacks an ASN.1 value to a PrincipalName. """ 175 s = unpack_asn1_tagged_sequence(value) 176 177 name_type = KerberosPrincipalNameType(get_sequence_value(s, 0, 'PrincipalName', 'name-type', unpack_asn1_integer)) 178 name = [unpack_asn1_general_string(n) for n in 179 get_sequence_value(s, 1, 'PrincipalName', 'name-string', unpack_asn1_sequence)] 180 181 return PrincipalName(name_type, name) 182 183 184HostAddress = collections.namedtuple('HostAddress', ['addr_type', 'value']) 185"""Kerberos HostAddress and HostAddresses 186 187A Kerberos Host Address ASN.1 definition is defined in `RFC 4120 5.2.5`_:: 188 189 HostAddress ::= SEQUENCE { 190 addr-type [0] Int32, 191 address [1] OCTET STRING 192 } 193 194Attributes: 195 addr_type (KerberosHostAddressType): The type of address that was encoded. 196 value (bytes): The address as a byte string. 197 198.. _RFC 4120 5.2.5: 199 https://www.rfc-editor.org/rfc/rfc4120#section-5.2.5 200""" 201 202PrincipalName = collections.namedtuple('PrincipalName', ['name_type', 'value']) 203"""Kerberos Realm and PrincipalName 204 205A Kerberos Principal Name ASN.1 definition is defined in `RFC 4120 5.2.2`_:: 206 207 PrincipalName ::= SEQUENCE { 208 name-type [0] Int32, 209 name-string [1] SEQUENCE OF KerberosString 210 } 211 212Attributes: 213 name_type (KerberosPrincipalNameType): The type of name the was encoded. 214 value (List[bytes]): Each component that forms the name. 215 216.. _RFC 4120 5.2.2: 217 https://www.rfc-editor.org/rfc/rfc4120#section-5.2.2 218""" 219 220 221# https://www.rfc-editor.org/rfc/rfc4120#section-5.5.1 - ap-options 222class KerberosAPOptions(enum.IntFlag): 223 mutual_required = 0x00000020 224 use_session_key = 0x00000040 225 reserved = 0x00000080 226 227 @classmethod 228 def native_labels(cls) -> typing.Dict["KerberosAPOptions", str]: 229 return { 230 KerberosAPOptions.mutual_required: 'mutual-required', 231 KerberosAPOptions.use_session_key: 'use-session-key', 232 KerberosAPOptions.reserved: 'reserved', 233 } 234 235 236# https://www.rfc-editor.org/rfc/rfc4120#section-5.4.1 - KDCOptions 237class KerberosKDCOptions(enum.IntFlag): 238 reserved = 0x80000000 239 forwardable = 0x40000000 240 forwarded = 0x20000000 241 proxiable = 0x10000000 242 proxy = 0x08000000 243 allow_postdate = 0x04000000 244 postdated = 0x02000000 245 unused7 = 0x01000000 246 renewable = 0x00800000 247 unused9 = 0x00400000 248 unused10 = 0x00200000 249 opt_hardware_auth = 0x00100000 250 unused12 = 0x00080000 251 unused13 = 0x00040000 252 constrained_delegation = 0x00020000 253 canonicalize = 0x00010000 254 request_anonymous = 0x00008000 255 unused17 = 0x00004000 256 unused18 = 0x00002000 257 unused19 = 0x00001000 258 unused20 = 0x00000800 259 unused21 = 0x00000400 260 unused22 = 0x00000200 261 unused23 = 0x00000100 262 unused24 = 0x00000080 263 unused25 = 0x00000040 264 disable_transited_check = 0x00000020 265 renewable_ok = 0x00000010 266 enc_tkt_in_skey = 0x00000008 267 unused29 = 0x00000004 268 renew = 0x00000002 269 validate = 0x00000001 270 271 @classmethod 272 def native_labels(cls) -> typing.Dict["KerberosKDCOptions", str]: 273 return { 274 KerberosKDCOptions.reserved: 'reserved', 275 KerberosKDCOptions.forwardable: 'forwardable', 276 KerberosKDCOptions.forwarded: 'forwarded', 277 KerberosKDCOptions.proxiable: 'proxiable', 278 KerberosKDCOptions.proxy: 'proxy', 279 KerberosKDCOptions.allow_postdate: 'allow-postdate', 280 KerberosKDCOptions.postdated: 'postdated', 281 KerberosKDCOptions.unused7: 'unused7', 282 KerberosKDCOptions.renewable: 'renewable', 283 KerberosKDCOptions.unused9: 'unused9', 284 KerberosKDCOptions.unused10: 'unused10', 285 KerberosKDCOptions.opt_hardware_auth: 'opt-hardware-auth', 286 KerberosKDCOptions.unused12: 'unused12', 287 KerberosKDCOptions.unused13: 'unused13', 288 KerberosKDCOptions.constrained_delegation: 'constrained-delegation', 289 KerberosKDCOptions.canonicalize: 'canonicalize', 290 KerberosKDCOptions.request_anonymous: 'request-anonymous', 291 KerberosKDCOptions.unused17: 'unused17', 292 KerberosKDCOptions.unused18: 'unused18', 293 KerberosKDCOptions.unused19: 'unused19', 294 KerberosKDCOptions.unused20: 'unused20', 295 KerberosKDCOptions.unused21: 'unused21', 296 KerberosKDCOptions.unused22: 'unused22', 297 KerberosKDCOptions.unused23: 'unused23', 298 KerberosKDCOptions.unused24: 'unused24', 299 KerberosKDCOptions.unused25: 'unused25', 300 KerberosKDCOptions.disable_transited_check: 'disable-transited-check', 301 KerberosKDCOptions.renewable_ok: 'renewable-ok', 302 KerberosKDCOptions.enc_tkt_in_skey: 'enc-tkt-in-skey', 303 KerberosKDCOptions.unused29: 'unused29', 304 KerberosKDCOptions.renew: 'renew', 305 KerberosKDCOptions.validate: 'validate', 306 } 307 308 309# https://ldapwiki.com/wiki/Kerberos%20Encryption%20Types - etypes 310class KerberosEncryptionType(enum.IntEnum): 311 des_cbc_crc = 0x0001 312 des_cbc_md4 = 0x0002 313 des_cbc_md5 = 0x0003 314 des_cbc_raw = 0x0004 315 des3_cbc_raw = 0x0006 316 des3_cbc_sha1 = 0x0010 317 aes128_cts_hmac_sha1_96 = 0x0011 318 aes256_cts_hmac_sha1_96 = 0x0012 319 aes128_cts_hmac_sha256_128 = 0x0013 320 aes256_cts_hmac_sha384_192 = 0x0014 321 rc4_hmac = 0x0017 322 rc4_hmac_exp = 0x0018 323 camellia128_cts_cmac = 0x0019 324 camellia256_cts_cmac = 0x001a 325 326 @classmethod 327 def native_labels(cls) -> typing.Dict["KerberosEncryptionType", str]: 328 return { 329 KerberosEncryptionType.des_cbc_crc: 'DES_CBC_CRC', 330 KerberosEncryptionType.des_cbc_md4: 'DES_CBC_MD4', 331 KerberosEncryptionType.des_cbc_md5: 'DES_CBC_MD5', 332 KerberosEncryptionType.des_cbc_raw: 'DES_CBC_RAW', 333 KerberosEncryptionType.des3_cbc_raw: 'DES3_CBC_RAW', 334 KerberosEncryptionType.des3_cbc_sha1: 'DES3_CBC_SHA1', 335 KerberosEncryptionType.aes128_cts_hmac_sha1_96: 'AES128_CTS_HMAC_SHA1_96', 336 KerberosEncryptionType.aes256_cts_hmac_sha1_96: 'AES256_CTS_HMAC_SHA1_96', 337 KerberosEncryptionType.aes128_cts_hmac_sha256_128: 'AES128_CTS_HMAC_SHA256_128', 338 KerberosEncryptionType.aes256_cts_hmac_sha384_192: 'AES256_CTS_HMAC_SHA384_192', 339 KerberosEncryptionType.rc4_hmac: 'RC4_HMAC', 340 KerberosEncryptionType.rc4_hmac_exp: 'RC4_HMAC_EXP', 341 KerberosEncryptionType.camellia128_cts_cmac: 'CAMELLIA128_CTS_CMAC', 342 KerberosEncryptionType.camellia256_cts_cmac: 'CAMELLIA256_CTS_CMAC', 343 } 344 345 346# https://www.rfc-editor.org/rfc/rfc4120#section-7.5.9 347class KerberosErrorCode(enum.IntEnum): 348 none = 0 349 name_exp = 1 350 service_exp = 2 351 bad_pvno = 3 352 c_old_mast_kvno = 4 353 s_old_mast_kvno = 5 354 c_principal_unknown = 6 355 s_principal_unknown = 7 356 principal_not_unique = 8 357 null_key = 9 358 cannot_postdate = 10 359 never_valid = 11 360 policy = 12 361 badoption = 13 362 etype_nosupp = 14 363 sumtype_nosupp = 15 364 padata_type_nosupp = 16 365 trtype_nosupp = 17 366 client_revoked = 18 367 service_revoked = 19 368 tgt_revoked = 20 369 client_notyet = 21 370 service_notyet = 22 371 key_expired = 23 372 preauth_failed = 24 373 preauth_required = 25 374 server_nomatch = 26 375 must_use_user2user = 27 376 path_not_accepted = 28 377 kdc_svc_unavailable = 29 378 ap_bad_integrity = 31 379 ap_txt_expired = 32 380 ap_tkt_nyv = 33 381 ap_repeat = 34 382 ap_not_use = 35 383 ap_badmatch = 36 384 ap_skew = 37 385 ap_badaddr = 38 386 ap_badversion = 39 387 ap_msg_type = 40 388 ap_modified = 41 389 ap_badorder = 42 390 ap_badkeyver = 44 391 ap_nokey = 45 392 ap_mut_fail = 46 393 ap_baddirection = 47 394 ap_method = 48 395 ap_badseq = 49 396 ap_inapp_cksum = 50 397 ap_path_not_accepted = 51 398 response_too_big = 52 399 generic = 60 400 field_toolong = 61 401 kdc_client_not_trusted = 62 402 kdc_not_trusted = 53 403 kdc_invalid_sig = 64 404 kdc_key_too_weak = 65 405 kdc_certificate_mismatch = 66 406 ap_no_tgt = 67 407 kdc_wrong_realm = 68 408 ap_user_to_user_required = 69 409 kdc_cant_verify_certificate = 70 410 kdc_invalid_certificate = 71 411 kdc_revoked_certificate = 72 412 kdc_revocation_status_unknown = 73 413 kdc_revocation_status_unavailable = 74 414 kdc_client_name_mismatch = 75 415 kdc_name_mismatch = 76 416 417 @classmethod 418 def native_labels(cls) -> typing.Dict["KerberosErrorCode", str]: 419 return { 420 KerberosErrorCode.none: 'KDC_ERR_NONE', 421 KerberosErrorCode.name_exp: 'KDC_ERR_NAME_EXP', 422 KerberosErrorCode.service_exp: 'KDC_ERR_SERVICE_EXP', 423 KerberosErrorCode.bad_pvno: 'KDC_ERR_BAD_PVNO', 424 KerberosErrorCode.c_old_mast_kvno: 'KDC_ERR_C_OLD_MAST_KVNO', 425 KerberosErrorCode.s_old_mast_kvno: 'KDC_ERR_S_OLD_MAST_KVNO', 426 KerberosErrorCode.c_principal_unknown: 'KDC_ERR_C_PRINCIPAL_UNKNOWN', 427 KerberosErrorCode.s_principal_unknown: 'KDC_ERR_S_PRINCIPAL_UNKNOWN', 428 KerberosErrorCode.principal_not_unique: 'KDC_ERR_PRINCIPAL_NOT_UNIQUE', 429 KerberosErrorCode.null_key: 'KDC_ERR_NULL_KEY', 430 KerberosErrorCode.cannot_postdate: 'KDC_ERR_CANNOT_POSTDATE', 431 KerberosErrorCode.never_valid: 'KDC_ERR_NEVER_VALID', 432 KerberosErrorCode.policy: 'KDC_ERR_POLICY', 433 KerberosErrorCode.badoption: 'KDC_ERR_BADOPTION', 434 KerberosErrorCode.etype_nosupp: 'KDC_ERR_ETYPE_NOSUPP', 435 KerberosErrorCode.sumtype_nosupp: 'KDC_ERR_SUMTYPE_NOSUPP', 436 KerberosErrorCode.padata_type_nosupp: 'KDC_ERR_PADATA_TYPE_NOSUPP', 437 KerberosErrorCode.trtype_nosupp: 'KDC_ERR_TRTYPE_NOSUPP', 438 KerberosErrorCode.client_revoked: 'KDC_ERR_CLIENT_REVOKED', 439 KerberosErrorCode.service_revoked: 'KDC_ERR_SERVICE_REVOKED', 440 KerberosErrorCode.tgt_revoked: 'KDC_ERR_TGT_REVOKED', 441 KerberosErrorCode.client_notyet: 'KDC_ERR_CLIENT_NOTYET', 442 KerberosErrorCode.service_notyet: 'KDC_ERR_SERVICE_NOTYET', 443 KerberosErrorCode.key_expired: 'KDC_ERR_KEY_EXPIRED', 444 KerberosErrorCode.preauth_failed: 'KDC_ERR_PREAUTH_FAILED', 445 KerberosErrorCode.preauth_required: 'KDC_ERR_PREAUTH_REQUIRED', 446 KerberosErrorCode.server_nomatch: 'KDC_ERR_SERVER_NOMATCH', 447 KerberosErrorCode.must_use_user2user: 'KDC_ERR_MUST_USE_USER2USER', 448 KerberosErrorCode.path_not_accepted: 'KDC_ERR_PATH_NOT_ACCEPTED', 449 KerberosErrorCode.kdc_svc_unavailable: 'KDC_ERR_SVC_UNAVAILABLE', 450 KerberosErrorCode.ap_bad_integrity: 'KRB_AP_ERR_BAD_INTEGRITY', 451 KerberosErrorCode.ap_txt_expired: 'KRB_AP_ERR_TKT_EXPIRED', 452 KerberosErrorCode.ap_tkt_nyv: 'KRB_AP_ERR_TKT_NYV', 453 KerberosErrorCode.ap_repeat: 'KRB_AP_ERR_REPEAT', 454 KerberosErrorCode.ap_not_use: 'KRB_AP_ERR_NOT_US', 455 KerberosErrorCode.ap_badmatch: 'KRB_AP_ERR_BADMATCH', 456 KerberosErrorCode.ap_skew: 'KRB_AP_ERR_SKEW', 457 KerberosErrorCode.ap_badaddr: 'KRB_AP_ERR_BADADDR', 458 KerberosErrorCode.ap_badversion: 'KRB_AP_ERR_BADVERSION', 459 KerberosErrorCode.ap_msg_type: 'KRB_AP_ERR_MSG_TYPE', 460 KerberosErrorCode.ap_modified: 'KRB_AP_ERR_MODIFIED', 461 KerberosErrorCode.ap_badorder: 'KRB_AP_ERR_BADORDER', 462 KerberosErrorCode.ap_badkeyver: 'KRB_AP_ERR_BADKEYVER', 463 KerberosErrorCode.ap_nokey: 'KRB_AP_ERR_NOKEY', 464 KerberosErrorCode.ap_mut_fail: 'KRB_AP_ERR_MUT_FAIL', 465 KerberosErrorCode.ap_baddirection: 'KRB_AP_ERR_BADDIRECTION', 466 KerberosErrorCode.ap_method: 'KRB_AP_ERR_METHOD', 467 KerberosErrorCode.ap_badseq: 'KRB_AP_ERR_BADSEQ', 468 KerberosErrorCode.ap_inapp_cksum: 'KRB_AP_ERR_INAPP_CKSUM', 469 KerberosErrorCode.ap_path_not_accepted: 'KRB_AP_PATH_NOT_ACCEPTED', 470 KerberosErrorCode.response_too_big: 'KRB_ERR_RESPONSE_TOO_BIG', 471 KerberosErrorCode.generic: 'KRB_ERR_GENERIC', 472 KerberosErrorCode.field_toolong: 'KRB_ERR_FIELD_TOOLONG', 473 KerberosErrorCode.kdc_client_not_trusted: 'KDC_ERROR_CLIENT_NOT_TRUSTED', 474 KerberosErrorCode.kdc_not_trusted: 'KDC_ERROR_KDC_NOT_TRUSTED', 475 KerberosErrorCode.kdc_invalid_sig: 'KDC_ERROR_INVALID_SIG', 476 KerberosErrorCode.kdc_key_too_weak: 'KDC_ERR_KEY_TOO_WEAK', 477 KerberosErrorCode.kdc_certificate_mismatch: 'KDC_ERR_CERTIFICATE_MISMATCH', 478 KerberosErrorCode.ap_no_tgt: 'KRB_AP_ERR_NO_TGT', 479 KerberosErrorCode.kdc_wrong_realm: 'KDC_ERR_WRONG_REALM', 480 KerberosErrorCode.ap_user_to_user_required: 'KRB_AP_ERR_USER_TO_USER_REQUIRED', 481 KerberosErrorCode.kdc_cant_verify_certificate: 'KDC_ERR_CANT_VERIFY_CERTIFICATE', 482 KerberosErrorCode.kdc_invalid_certificate: 'KDC_ERR_INVALID_CERTIFICATE', 483 KerberosErrorCode.kdc_revoked_certificate: 'KDC_ERR_REVOKED_CERTIFICATE', 484 KerberosErrorCode.kdc_revocation_status_unknown: 'KDC_ERR_REVOCATION_STATUS_UNKNOWN', 485 KerberosErrorCode.kdc_revocation_status_unavailable: 'KDC_ERR_REVOCATION_STATUS_UNAVAILABLE', 486 KerberosErrorCode.kdc_client_name_mismatch: 'KDC_ERR_CLIENT_NAME_MISMATCH', 487 KerberosErrorCode.kdc_name_mismatch: 'KDC_ERR_KDC_NAME_MISMATCH', 488 } 489 490 491# https://www.rfc-editor.org/rfc/rfc4120#section-5.10 492class KerberosMessageType(enum.IntEnum): 493 unknown = 0 494 as_req = 10 495 as_rep = 11 496 tgs_req = 12 497 tgs_rep = 13 498 ap_req = 14 499 ap_rep = 15 500 error = 30 501 502 @classmethod 503 def native_labels(cls) -> typing.Dict["KerberosMessageType", str]: 504 return { 505 KerberosMessageType.unknown: 'UNKNOWN', 506 KerberosMessageType.as_req: 'AS-REQ', 507 KerberosMessageType.as_rep: 'AS-REP', 508 KerberosMessageType.tgs_req: 'TGS-REQ', 509 KerberosMessageType.tgs_rep: 'TGS-REP', 510 KerberosMessageType.ap_req: 'AP-REQ', 511 KerberosMessageType.ap_rep: 'AP-REP', 512 KerberosMessageType.error: 'KRB-ERROR', 513 } 514 515 516# https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml 517class KerberosPADataType(enum.IntEnum): 518 tgs_req = 1 519 enc_timestamp = 2 520 pw_salt = 3 521 reserved = 4 522 enc_unix_time = 5 523 sandia_secureid = 6 524 sesame = 7 525 osf_dce = 8 526 cybersafe_secureid = 9 527 afs3_salt = 10 528 etype_info = 11 529 sam_challenge = 12 530 sam_response = 13 531 pk_as_req_old = 14 532 pk_as_rep_old = 15 533 pk_as_req = 16 534 pk_as_rep = 17 535 pk_ocsp_response = 18 536 etype_info2 = 19 537 use_specified_kvno = 20 538 svr_referral_info = 20 539 sam_redirect = 21 540 get_from_typed_data = 22 541 td_padata = 22 542 sam_etype_info = 23 543 alt_princ = 24 544 server_referral = 25 545 sam_challenge2 = 30 546 sam_response2 = 31 547 extra_tgt = 41 548 td_pkinit_cms_certificates = 101 549 td_krb_principal = 102 550 td_krb_realm = 103 551 td_trusted_certifiers = 104 552 td_certificate_index = 105 553 td_app_defined_error = 106 554 td_req_nonce = 107 555 td_req_seq = 108 556 td_dh_parameters = 109 557 td_cms_digest_algorithms = 111 558 td_cert_digest_algorithms = 112 559 pac_request = 128 560 for_user = 128 561 for_x509_user = 130 562 for_check_dups = 131 563 as_checksum = 132 564 fx_cookie = 133 565 authentication_set = 134 566 auth_set_selected = 165 567 fx_fast = 136 568 fx_error = 137 569 encrypted_challenge = 138 570 otp_challenge = 141 571 otp_request = 142 572 otp_confirm = 143 573 otp_pin_change = 144 574 epak_as_req = 145 575 epak_as_rep = 146 576 pkinit_kx = 147 577 pku2u_name = 148 578 enc_pa_rep = 149 579 as_freshness = 150 580 spake = 151 581 kerb_key_list_req = 161 582 kerb_key_list_rep = 162 583 supported_etypes = 165 584 extended_error = 166 585 pac_options = 167 586 587 @classmethod 588 def native_labels(cls) -> typing.Dict["KerberosPADataType", str]: 589 return { 590 KerberosPADataType.tgs_req: 'PA-TGS-REQ', 591 KerberosPADataType.enc_timestamp: 'PA-ENC-TIMESTAMP', 592 KerberosPADataType.pw_salt: 'PA-PW-SALT', 593 KerberosPADataType.reserved: 'reserved', 594 KerberosPADataType.enc_unix_time: 'PA-ENC-UNIX-TIME', 595 KerberosPADataType.sandia_secureid: 'PA-SANDIA-SECUREID', 596 KerberosPADataType.sesame: 'PA-SESAME', 597 KerberosPADataType.osf_dce: 'PA-OSF-DCE', 598 KerberosPADataType.cybersafe_secureid: 'PA-CYBERSAFE-SECUREID', 599 KerberosPADataType.afs3_salt: 'PA-AFS3-SALT', 600 KerberosPADataType.etype_info: 'PA-ETYPE-INFO', 601 KerberosPADataType.sam_challenge: 'PA-SAM-CHALLENGE', 602 KerberosPADataType.sam_response: 'PA-SAM-RESPONSE', 603 KerberosPADataType.pk_as_req_old: 'PA-PK-AS-REQ_OLD', 604 KerberosPADataType.pk_as_rep_old: 'PA-PK-AS-REP_OLD', 605 KerberosPADataType.pk_as_req: 'PA-PK-AS-REQ', 606 KerberosPADataType.pk_as_rep: 'PA-PK-AS-REP', 607 KerberosPADataType.pk_ocsp_response: 'PA-PK-OCSP-RESPONSE', 608 KerberosPADataType.etype_info2: 'PA-ETYPE-INFO2', 609 KerberosPADataType.use_specified_kvno: 'PA-USE-SPECIFIED-KVNO or PA-SVR-REFERRAL-INFO', 610 KerberosPADataType.sam_redirect: 'PA-SAM-REDIRECT', 611 KerberosPADataType.get_from_typed_data: 'PA-GET-FROM-TYPED-DATA', 612 KerberosPADataType.td_padata: 'TD-PADATA', 613 KerberosPADataType.sam_etype_info: 'PA-SAM-ETYPE-INFO', 614 KerberosPADataType.alt_princ: 'PA-ALT-PRINC', 615 KerberosPADataType.server_referral: 'PA-SERVER-REFERRAL', 616 KerberosPADataType.sam_challenge2: 'PA-SAM-CHALLENGE2', 617 KerberosPADataType.sam_response2: 'PA-SAM-RESPONSE2', 618 KerberosPADataType.extra_tgt: 'PA-EXTRA-TGT', 619 KerberosPADataType.td_pkinit_cms_certificates: 'TD-PKINIT-CMS-CERTIFICATES', 620 KerberosPADataType.td_krb_principal: 'TD-KRB-PRINCIPAL', 621 KerberosPADataType.td_krb_realm: 'TD-KRB-REALM', 622 KerberosPADataType.td_trusted_certifiers: 'TD-TRUSTED-CERTIFIERS', 623 KerberosPADataType.td_certificate_index: 'TD-CERTIFICATE-INDEX', 624 KerberosPADataType.td_app_defined_error: 'TD-APP-DEFINED-ERROR', 625 KerberosPADataType.td_req_nonce: 'TD-REQ-NONCE', 626 KerberosPADataType.td_req_seq: 'TD-REQ-SEQ', 627 KerberosPADataType.td_dh_parameters: 'TD_DH_PARAMETERS', 628 KerberosPADataType.td_cms_digest_algorithms: 'TD-CMS-DIGEST-ALGORITHMS', 629 KerberosPADataType.td_cert_digest_algorithms: 'TD-CERT-DIGEST-ALGORITHMS', 630 KerberosPADataType.pac_request: 'PA-PAC-REQUEST', 631 KerberosPADataType.for_user: 'PA-FOR_USER', 632 KerberosPADataType.for_x509_user: 'PA-FOR-X509-USER', 633 KerberosPADataType.for_check_dups: 'PA-FOR-CHECK_DUPS', 634 KerberosPADataType.as_checksum: 'PA-AS-CHECKSUM', 635 KerberosPADataType.fx_cookie: 'PA-FX-COOKIE', 636 KerberosPADataType.authentication_set: 'PA-AUTHENTICATION-SET', 637 KerberosPADataType.auth_set_selected: 'PA-AUTH-SET-SELECTED', 638 KerberosPADataType.fx_fast: 'PA-FX-FAST', 639 KerberosPADataType.fx_error: 'PA-FX-ERROR', 640 KerberosPADataType.encrypted_challenge: 'PA-ENCRYPTED-CHALLENGE', 641 KerberosPADataType.otp_challenge: 'PA-OTP-CHALLENGE', 642 KerberosPADataType.otp_request: 'PA-OTP-REQUEST', 643 KerberosPADataType.otp_confirm: 'PA-OTP-CONFIRM', 644 KerberosPADataType.otp_pin_change: 'PA-OTP-PIN-CHANGE', 645 KerberosPADataType.epak_as_req: 'PA-EPAK-AS-REQ', 646 KerberosPADataType.epak_as_rep: 'PA-EPAK-AS-REP', 647 KerberosPADataType.pkinit_kx: 'PA_PKINIT_KX', 648 KerberosPADataType.pku2u_name: 'PA_PKU2U_NAME', 649 KerberosPADataType.enc_pa_rep: 'PA-REQ-ENC-PA-REP', 650 KerberosPADataType.as_freshness: 'PA_AS_FRESHNESS', 651 KerberosPADataType.spake: 'PA-SPAKE', 652 KerberosPADataType.kerb_key_list_req: 'KERB-KEY-LIST-REQ', 653 KerberosPADataType.kerb_key_list_rep: 'KERB-KEY-LIST-REP', 654 KerberosPADataType.supported_etypes: 'PA-SUPPORTED-ETYPES', 655 KerberosPADataType.extended_error: 'PA-EXTENDED_ERROR', 656 KerberosPADataType.pac_options: 'PA-PAC-OPTIONS', 657 } 658 659 660# https://www.rfc-editor.org/rfc/rfc4120#section-6.2 661class KerberosPrincipalNameType(enum.IntEnum): 662 unknown = 0 663 principal = 1 664 srv_inst = 2 665 srv_hst = 3 666 srv_xhst = 4 667 uid = 5 668 x500_principal = 6 669 smtp_name = 7 670 enterprise = 10 671 672 @classmethod 673 def native_labels(cls) -> typing.Dict["KerberosPrincipalNameType", str]: 674 return { 675 KerberosPrincipalNameType.unknown: 'NT-UNKNOWN', 676 KerberosPrincipalNameType.principal: 'NT-PRINCIPAL', 677 KerberosPrincipalNameType.srv_inst: 'NT-SRV-INST', 678 KerberosPrincipalNameType.srv_hst: 'NT-SRV-HST', 679 KerberosPrincipalNameType.srv_xhst: 'NT-SRV-XHST', 680 KerberosPrincipalNameType.uid: 'NT-UID', 681 KerberosPrincipalNameType.x500_principal: 'NT-X500-PRINCIPAL', 682 KerberosPrincipalNameType.smtp_name: 'NT-SMTP-NAME', 683 KerberosPrincipalNameType.enterprise: 'NT-ENTERPRISE', 684 } 685 686 687# https://www.rfc-editor.org/rfc/rfc4120#section-7.5.3 688class KerberosHostAddressType(enum.IntEnum): 689 ipv4 = 2 690 directional = 3 691 chaos_net = 5 692 xns = 6 693 iso = 7 694 decnet_phase_iv = 12 695 apple_talk_ddp = 16 696 netbios = 20 697 ipv6 = 26 698 699 @classmethod 700 def native_labels(cls) -> typing.Dict["KerberosHostAddressType", str]: 701 return { 702 KerberosHostAddressType.ipv4: 'IPv4', 703 KerberosHostAddressType.directional: 'Directional', 704 KerberosHostAddressType.chaos_net: 'ChaosNet', 705 KerberosHostAddressType.xns: 'XNS', 706 KerberosHostAddressType.iso: 'ISO', 707 KerberosHostAddressType.decnet_phase_iv: 'DECNET Phase IV', 708 KerberosHostAddressType.apple_talk_ddp: 'AppleTalk DDP', 709 KerberosHostAddressType.netbios: 'NetBios', 710 KerberosHostAddressType.ipv6: 'IPv6', 711 } 712 713 714class ParseType(enum.IntEnum): 715 default = 0 716 enum = 1 717 flags = 2 718 datetime = 3 719 text = 4 720 bytes = 5 721 principal_name = 6 722 host_address = 7 723 token = 8 724 725 726class _KerberosMsgType(type): 727 __registry = {} 728 729 def __init__(cls, name, bases, attributes): 730 pvno = getattr(cls, 'PVNO', 0) 731 732 if pvno not in cls.__registry: 733 cls.__registry[pvno] = {} 734 735 if hasattr(cls, 'MESSAGE_TYPE'): 736 cls.__registry[pvno][cls.MESSAGE_TYPE] = cls 737 738 def __call__(cls, sequence): 739 # The KrbAsReq msg starts at 1 not 0, so do a check for that. 740 pvno_idx = 0 741 if 0 not in sequence: 742 pvno_idx = 1 743 744 pvno = unpack_asn1_integer(sequence[pvno_idx]) 745 message_type = unpack_asn1_integer(sequence[pvno_idx + 1]) 746 new_cls = cls.__registry[pvno].get(message_type, cls) 747 return super(_KerberosMsgType, new_cls).__call__(sequence) 748 749 750class KerberosV5Msg(metaclass=_KerberosMsgType): 751 752 MESSAGE_TYPE = KerberosMessageType.unknown 753 PVNO = 5 754 755 def __init__(self, sequence: typing.Dict[int, ASN1Value]) -> None: 756 # This is only used if decoding an unknown Kerberos message type. 757 self.sequence = sequence 758 759 @staticmethod 760 def unpack(value: typing.Union[ASN1Value, bytes]) -> "KerberosV5Msg": 761 msg_sequence = unpack_asn1_tagged_sequence(value) 762 return KerberosV5Msg(msg_sequence) 763 764 765class KrbAsReq(KerberosV5Msg): 766 """The KRB_AS_REQ message. 767 768 The KRB_AS_REQ message is used when the client wishes to retrieve a the initial ticket for a service. The 769 KRB_TGS_REQ message is identical except for the tag and msg-type is used when retrieving additional tickets for a 770 service. 771 772 The ASN.1 definition for the KDC-REQ structure is defined in `RFC 4120 5.4.1`_:: 773 774 KDC-REQ ::= SEQUENCE { 775 -- NOTE: first tag is [1], not [0] 776 pvno [1] INTEGER (5) , 777 msg-type [2] INTEGER (10 -- AS -- | 12 -- TGS --), 778 padata [3] SEQUENCE OF PA-DATA OPTIONAL 779 -- NOTE: not empty --, 780 req-body [4] KDC-REQ-BODY 781 } 782 783 Args: 784 sequence: The ASN.1 sequence value as a dict to unpack. 785 786 Attributes: 787 padata (PAData): The pre-authentication data. 788 req_body (KdcReqBody): The body of the request. 789 790 .. _RFC 4120 5.4.1: 791 https://www.rfc-editor.org/rfc/rfc4120#section-5.4.1 792 """ 793 MESSAGE_TYPE = KerberosMessageType.as_req 794 795 PARSE_MAP = [ 796 ('pvno', 'PVNO', ParseType.default), 797 ('msg-type', 'MESSAGE_TYPE', ParseType.enum), 798 ('padata', 'padata', ParseType.token), 799 ('req-body', 'req_body', ParseType.token) 800 ] 801 802 def __init__(self, sequence: typing.Dict[int, ASN1Value]) -> None: 803 def unpack_padata(value): 804 return [PAData.unpack(p) for p in unpack_asn1_sequence(value)] 805 806 self.padata = get_sequence_value(sequence, 3, 'KDC-REQ', 'pa-data', unpack_padata) 807 self.req_body = get_sequence_value(sequence, 4, 'KDC-REQ', 'req-body', KdcReqBody.unpack) 808 809 810class KrbAsRep(KerberosV5Msg): 811 """The KRB_AS_REP message. 812 813 The KRB_AS_REP message is used for a reply from the KDC to a KRB_AS_REQ message. The KRB_TGS_REP message is 814 identical except for the tag and msg-type. 815 816 The ASN.1 definition for the KDC-REP structure is defined in `RFC 4120 5.4.2`_:: 817 818 KDC-REP ::= SEQUENCE { 819 pvno [0] INTEGER (5), 820 msg-type [1] INTEGER (11 -- AS -- | 13 -- TGS --), 821 padata [2] SEQUENCE OF PA-DATA OPTIONAL 822 -- NOTE: not empty --, 823 crealm [3] Realm, 824 cname [4] PrincipalName, 825 ticket [5] Ticket, 826 enc-part [6] EncryptedData 827 -- EncASRepPart or EncTGSRepPart, 828 -- as appropriate 829 } 830 831 Args: 832 sequence: The ASN.1 sequence value as a dict to unpack. 833 834 Attributes: 835 padata (PAData): The pre-authentication data. 836 crealm (bytes): The client realm. 837 cname (PrincipalName): The client principal name. 838 ticket (Ticket): The newly issued ticket. 839 enc_part (EncryptedData): The encrypted part of the message. 840 841 .. _RFC 4120 5.4.2: 842 https://www.rfc-editor.org/rfc/rfc4120#section-5.4.2 843 """ 844 MESSAGE_TYPE = KerberosMessageType.as_rep 845 846 PARSE_MAP = [ 847 ('pvno', 'PVNO', ParseType.default), 848 ('msg-type', 'MESSAGE_TYPE', ParseType.enum), 849 ('padata', 'padata', ParseType.token), 850 ('crealm', 'crealm', ParseType.text), 851 ('cname', 'cname', ParseType.principal_name), 852 ('ticket', 'ticket', ParseType.token), 853 ('enc-part', 'enc_part', ParseType.token), 854 ] 855 856 def __init__(self, sequence: typing.Dict[int, ASN1Value]) -> None: 857 def unpack_padata(value): 858 return [PAData.unpack(p) for p in unpack_asn1_sequence(value)] 859 860 self.padata = get_sequence_value(sequence, 2, 'KDC-REP', 'pa-data', unpack_padata) 861 self.crealm = get_sequence_value(sequence, 3, 'KDC-REP', 'crealm', unpack_asn1_general_string) 862 self.cname = get_sequence_value(sequence, 4, 'KDC-REP', 'cname', unpack_principal_name) 863 self.ticket = get_sequence_value(sequence, 5, 'KDC-REP', 'ticket', Ticket.unpack) 864 self.enc_part = get_sequence_value(sequence, 6, 'KDC-REP', 'enc-part', EncryptedData.unpack) 865 866 867class KrbTgsReq(KrbAsReq): 868 """ The KRB_TGS_REQ is the same as KRB_AS_REQ but with a different MESSAGE_TYPE. """ 869 MESSAGE_TYPE = KerberosMessageType.tgs_req 870 871 872class KrbTgsRep(KrbAsRep): 873 """ The KRB_TGS_REP is the same as KRB_AS_REP but with a different MESSAGE_TYPE. """ 874 MESSAGE_TYPE = KerberosMessageType.tgs_rep 875 876 877class KrbApReq(KerberosV5Msg): 878 """The KRB_AP_REQ message. 879 880 The KRB_AP_REQ message contains is used to authenticate the initiator to an acceptor. 881 882 The ASN.1 definition for the KRB_AP_REQ structure is defined in `RFC 4120 5.5.1`_:: 883 884 AP-REQ ::= [APPLICATION 14] SEQUENCE { 885 pvno [0] INTEGER (5), 886 msg-type [1] INTEGER (14), 887 ap-options [2] APOptions, 888 ticket [3] Ticket, 889 authenticator [4] EncryptedData -- Authenticator 890 } 891 892 Args: 893 sequence: The ASN.1 sequence value as a dict to unpack. 894 895 Attributes: 896 ap_options (KerberosAPOptions): Options related to the AP request. 897 ticket (Ticket): The ticket authenticating the client to the server. 898 authenticator (EncryptedData): The encrypted authenticator. 899 900 .. _RFC 4120 5.5.1: 901 https://www.rfc-editor.org/rfc/rfc4120#section-5.5.1 902 """ 903 MESSAGE_TYPE = KerberosMessageType.ap_req 904 905 PARSE_MAP = [ 906 ('pvno', 'PVNO', ParseType.default), 907 ('msg-type', 'MESSAGE_TYPE', ParseType.enum), 908 ('ap-options', 'ap_options', ParseType.flags), 909 ('ticket', 'ticket', ParseType.token), 910 ('authenticator', 'authenticator', ParseType.token), 911 ] 912 913 def __init__(self, sequence: typing.Dict[int, ASN1Value]) -> None: 914 raw_ap_options = get_sequence_value(sequence, 2, 'AP-REQ', 'ap-options', unpack_asn1_bit_string) 915 ap_options = KerberosAPOptions(struct.unpack("<I", raw_ap_options)[0]) 916 917 self.ap_options = ap_options 918 self.ticket = get_sequence_value(sequence, 3, 'AP-REQ', 'ticket', Ticket.unpack) 919 self.authenticator = get_sequence_value(sequence, 4, 'AP-REQ', 'authenticator', EncryptedData.unpack) 920 921 922class KrbApRep(KerberosV5Msg): 923 """The KRB_AP_REP message. 924 925 The KRB_AP_REP is a response to an application request `KRB_AP_REQ`. 926 927 The ASN.1 definition for the KRB_AP_REP structure is defined in `RFC 4120 5.5.2`_:: 928 929 AP-REP ::= [APPLICATION 15] SEQUENCE { 930 pvno [0] INTEGER (5), 931 msg-type [1] INTEGER (15), 932 enc-part [2] EncryptedData -- EncAPRepPart 933 } 934 935 Args: 936 sequence: The ASN.1 sequence value as a dict to unpack. 937 938 Attributes: 939 enc_part (EncryptedData): The encrypted authenticator. 940 941 .. _RFC 4120 5.5.2: 942 https://www.rfc-editor.org/rfc/rfc4120#section-5.5.2 943 """ 944 MESSAGE_TYPE = KerberosMessageType.ap_rep 945 946 PARSE_MAP = [ 947 ('pvno', 'PVNO', ParseType.default), 948 ('msg-type', 'MESSAGE_TYPE', ParseType.enum), 949 ('enc-part', 'enc_part', ParseType.token), 950 ] 951 952 def __init__(self, sequence: typing.Dict[int, ASN1Value]) -> None: 953 self.enc_part = get_sequence_value(sequence, 2, 'AP-REP', 'enc-part', EncryptedData.unpack) 954 955 956class KrbError(KerberosV5Msg): 957 """The KRB_ERROR message. 958 959 The KRB_ERROR is a message sent in the occurrence of an error. 960 961 The ASN.1 definition for the KRB_ERROR structure is defined in `RFC 4120 5.9.1`_:: 962 963 KRB-ERROR ::= [APPLICATION 30] SEQUENCE { 964 pvno [0] INTEGER (5), 965 msg-type [1] INTEGER (30), 966 ctime [2] KerberosTime OPTIONAL, 967 cusec [3] Microseconds OPTIONAL, 968 stime [4] KerberosTime, 969 susec [5] Microseconds, 970 error-code [6] Int32, 971 crealm [7] Realm OPTIONAL, 972 cname [8] PrincipalName OPTIONAL, 973 realm [9] Realm -- service realm --, 974 sname [10] PrincipalName -- service name --, 975 e-text [11] KerberosString OPTIONAL, 976 e-data [12] OCTET STRING OPTIONAL 977 } 978 979 Args: 980 sequence: The ASN.1 sequence value as a dict to unpack. 981 982 Attributes: 983 ctime (datetime.datetime): The current time on the client's host. 984 cusec (int): The microsecond part of the client's timestamp. 985 stime (datetime.datetime): The current time of the server. 986 susec (int): The microsecond part of the server's timestamp. 987 error_code (KerberosErrorCode): THe error code returned by the kerberos when a request fails. 988 crealm (bytes): The realm that issues a ticket. 989 cname (PrincipalName): The principal name in the ticket. 990 realm (bytes): The service realm. 991 sname (PrincipalName): The service name. 992 e_text (bytes): Additional text to explain the error code. 993 e_data (bytes): Additional data about the error. 994 995 .. _RFC 4120 5.9.1: 996 https://www.rfc-editor.org/rfc/rfc4120#section-5.9.1 997 """ 998 MESSAGE_TYPE = KerberosMessageType.error 999 1000 PARSE_MAP = [ 1001 ('pvno', 'PVNO', ParseType.default), 1002 ('msg-type', 'MESSAGE_TYPE', ParseType.enum), 1003 ('ctime', 'ctime', ParseType.datetime), 1004 ('cusec', 'cusec', ParseType.default), 1005 ('stime', 'stime', ParseType.datetime), 1006 ('susec', 'susec', ParseType.default), 1007 ('error-code', 'error_code', ParseType.enum), 1008 ('crealm', 'crealm', ParseType.text), 1009 ('cname', 'cname', ParseType.principal_name), 1010 ('realm', 'realm', ParseType.text), 1011 ('sname', 'sname', ParseType.principal_name), 1012 ('e-text', 'e_text', ParseType.text), 1013 ('e-data', 'e_data', ParseType.bytes), 1014 ] 1015 1016 def __init__(self, sequence: typing.Dict[int, ASN1Value]) -> None: 1017 self.ctime = get_sequence_value(sequence, 2, 'KRB-ERROR', 'ctime', unpack_asn1_generalized_time) 1018 self.cusec = get_sequence_value(sequence, 3, 'KRB-ERROR', 'cusec', unpack_asn1_integer) 1019 self.stime = get_sequence_value(sequence, 4, 'KRB-ERROR', 'stime', unpack_asn1_generalized_time) 1020 self.susec = get_sequence_value(sequence, 5, 'KRB-ERROR', 'susec', unpack_asn1_integer) 1021 self.error_code = KerberosErrorCode(get_sequence_value(sequence, 6, 'KRB-ERROR', 'error-code', 1022 unpack_asn1_integer)) 1023 self.crealm = get_sequence_value(sequence, 7, 'KRB-ERROR', 'crealm', unpack_asn1_general_string) 1024 self.cname = get_sequence_value(sequence, 8, 'KRB-ERROR', 'cname', unpack_principal_name) 1025 self.realm = get_sequence_value(sequence, 9, 'KRB-ERROR', 'realm', unpack_asn1_general_string) 1026 self.sname = get_sequence_value(sequence, 10, 'KRB-ERROR', 'realm', unpack_principal_name) 1027 self.e_text = get_sequence_value(sequence, 11, 'KRB-ERROR', 'e-text', unpack_asn1_general_string) 1028 self.e_data = get_sequence_value(sequence, 12, 'KRB-ERROR', 'e-data', unpack_asn1_octet_string) 1029 1030 1031class PAData: 1032 """Kerberos PA-DATA. 1033 1034 The ASN.1 definition for the PA-DATA structure is defined in `RFC 4120 5.2.7`_:: 1035 1036 PA-DATA ::= SEQUENCE { 1037 -- NOTE: first tag is [1], not [0] 1038 padata-type [1] Int32, 1039 padata-value [2] OCTET STRING -- might be encoded AP-REQ 1040 } 1041 1042 Args: 1043 data_type: Indicates the type of data the value represents. 1044 value: The PAData value, usually the DER encoding of another message. 1045 1046 Attributes: 1047 data_type (Union[int, KerberosPADataType]): See args. 1048 b_value (bytes): The raw bytes of padata-value, use `value` to get a structured object of these bytes if 1049 available. 1050 1051 .. RFC 4120 5.2.7: 1052 https://www.rfc-editor.org/rfc/rfc4120#section-5.2.7 1053 """ 1054 PARSE_MAP = [ 1055 ('padata-type', 'data_type', ParseType.enum), 1056 ('padata-value', 'value', ParseType.token), 1057 ] 1058 1059 def __init__(self, data_type: typing.Union[int, KerberosPADataType], value: bytes) -> None: 1060 self.data_type = data_type 1061 self.b_value = value 1062 1063 @property 1064 def value(self) -> typing.Any: 1065 if self.data_type == KerberosPADataType.tgs_req: 1066 # Special edge case for this PA type due to how the messages require the data in unpack 1067 return KrbTgsReq.unpack(unpack_asn1(unpack_asn1(self.b_value)[0].b_data)[0]) 1068 1069 unpack_func = { 1070 KerberosPADataType.enc_timestamp: (EncryptedData.unpack, False), 1071 KerberosPADataType.etype_info2: (PAETypeInfo2.unpack, True), 1072 }.get(self.data_type, None) 1073 1074 if unpack_func: 1075 b_value = unpack_asn1(self.b_value)[0] 1076 if unpack_func[1]: 1077 # Is a SEQUENCE OF (list of entries). 1078 return [unpack_func[0](v) for v in unpack_asn1_sequence(b_value)] 1079 1080 else: 1081 return unpack_func[0](b_value.b_data) 1082 1083 else: 1084 return self.b_value 1085 1086 @staticmethod 1087 def unpack(value: ASN1Value) -> "PAData": 1088 sequence = unpack_asn1_tagged_sequence(value) 1089 1090 def unpack_data_type(value): 1091 int_val = unpack_asn1_integer(value) 1092 1093 try: 1094 return KerberosPADataType(int_val) 1095 except ValueError: 1096 return int_val 1097 1098 data_type = get_sequence_value(sequence, 1, 'PA-DATA', 'padata-type', unpack_data_type) 1099 value = get_sequence_value(sequence, 2, 'PA-DATA', 'padata-value', unpack_asn1_octet_string) 1100 1101 return PAData(data_type, value) 1102 1103 1104class PAETypeInfo2: 1105 """Kerberos PA-ETYPE-INFO2 container. 1106 1107 The ASN.1 definition for the PA-ETYPE-INFO2 structure is defined in `RFC 4120 5.2.7.5`_:: 1108 1109 ETYPE-INFO2-ENTRY ::= SEQUENCE { 1110 etype [0] Int32, 1111 salt [1] KerberosString OPTIONAL, 1112 s2kparams [2] OCTET STRING OPTIONAL 1113 } 1114 1115 Args: 1116 etype: The etype that defines the cipher used. 1117 salt: The used in the cipher associated with the cryptosystem. 1118 s2kparams: Extra params to be interpreted by the cipher associated with the cryptosystem. 1119 1120 Attributes: 1121 etype (KerberosEncryptionType): See args. 1122 salt (Optional[bytes]): See args. 1123 s2kparams (Optional[bytes]): See args. 1124 1125 .. RFC 4120 5.2.7.5: 1126 https://www.rfc-editor.org/rfc/rfc4120#section-5.2.7.5 1127 """ 1128 PARSE_MAP = [ 1129 ('etype', 'etype', ParseType.enum), 1130 ('salt', 'salt', ParseType.bytes), 1131 ('s2kparams', 's2kparams', ParseType.bytes), 1132 ] 1133 1134 def __init__( 1135 self, 1136 etype: KerberosEncryptionType, 1137 salt: typing.Optional[bytes], 1138 s2kparams: typing.Optional[bytes], 1139 ) -> None: 1140 self.etype = etype 1141 self.salt = salt 1142 self.s2kparams = s2kparams 1143 1144 @staticmethod 1145 def unpack(value: ASN1Value) -> "PAETypeInfo2": 1146 sequence = unpack_asn1_tagged_sequence(value) 1147 1148 etype = KerberosEncryptionType(get_sequence_value(sequence, 0, 'PA-ETYPE-INFO2', 'etype', unpack_asn1_integer)) 1149 salt = get_sequence_value(sequence, 1, 'ETYPE-INFO2-ENTRY', 'salt', unpack_asn1_general_string) 1150 s2kparams = get_sequence_value(sequence, 2, 'ETYPE-INFO2-ENTRY', 's2kparams', unpack_asn1_octet_string) 1151 1152 return PAETypeInfo2(etype, salt, s2kparams) 1153 1154 1155class EncryptedData: 1156 """Kerberos EncryptedData container. 1157 1158 The ASN.1 definition for the EncryptedData structure is defined in `RFC 4120 5.2.9`_:: 1159 1160 EncryptedData ::= SEQUENCE { 1161 etype [0] Int32 -- EncryptionType --, 1162 kvno [1] UInt32 OPTIONAL, 1163 cipher [2] OCTET STRING -- ciphertext 1164 } 1165 1166 Args: 1167 etype: The encryption algorithm that was used to encipher the cipher. 1168 kvno: The version number of the key under which data is encrypted. It is only present in messages encrypted 1169 under long lasting keys. 1170 cipher: The enciphered text. 1171 1172 Attributes: 1173 etype (KerberosEncryptionType): See args. 1174 kvno (Optional[int]): See args. 1175 cipher (bytes): See args. 1176 1177 .. RFC 4120 5.2.9: 1178 https://www.rfc-editor.org/rfc/rfc4120#section-5.2.9 1179 """ 1180 PARSE_MAP = [ 1181 ('etype', 'etype', ParseType.enum), 1182 ('kvno', 'kvno', ParseType.default), 1183 ('cipher', 'cipher', ParseType.bytes), 1184 ] 1185 1186 def __init__(self, etype: KerberosEncryptionType, kvno: typing.Optional[int], cipher: bytes) -> None: 1187 self.etype = etype 1188 self.kvno = kvno 1189 self.cipher = cipher 1190 1191 @staticmethod 1192 def unpack(value: ASN1Value) -> "EncryptedData": 1193 sequence = unpack_asn1_tagged_sequence(value) 1194 1195 etype = KerberosEncryptionType(get_sequence_value(sequence, 0, 'EncryptedData', 'etype', unpack_asn1_integer)) 1196 kvno = get_sequence_value(sequence, 1, 'EncryptedData', 'kvno', unpack_asn1_integer) 1197 cipher = get_sequence_value(sequence, 2, 'EncryptedData', 'cipher', unpack_asn1_octet_string) 1198 1199 return EncryptedData(etype, kvno, cipher) 1200 1201 1202class Ticket: 1203 """Kerberos Ticket. 1204 1205 The ASN.1 definition for the Ticket structure is defined in `RFC 4120 5.3`_:: 1206 1207 Ticket ::= [APPLICATION 1] SEQUENCE { 1208 tkt-vno [0] INTEGER (5), 1209 realm [1] Realm, 1210 sname [2] PrincipalName, 1211 enc-part [3] EncryptedData -- EncTicketPart 1212 } 1213 1214 Args: 1215 tkt_vno: The version number for the ticket format. 1216 realm: The realm that issued a ticket. 1217 sname: All the name components of the server's identity. 1218 enc_part: The encrypted part of the ticket, it is encrypted in the key shared by Kerberos and the end server. 1219 1220 Attributes: 1221 tkt_vno (int): See args. 1222 realm (bytes): See args. 1223 sname (List[PrincipalName]): See args. 1224 enc_part (EncryptedData): See args. 1225 1226 .. _RFC 4120 5.3: 1227 https://www.rfc-editor.org/rfc/rfc4120#section-5.3 1228 """ 1229 PARSE_MAP = [ 1230 ('tkt-vno', 'tkt_vno', ParseType.default), 1231 ('realm', 'realm', ParseType.text), 1232 ('sname', 'sname', ParseType.principal_name), 1233 ('enc-part', 'enc_part', ParseType.token), 1234 ] 1235 1236 def __init__( 1237 self, 1238 tkt_vno: int, 1239 realm: bytes, 1240 sname: typing.List[PrincipalName], 1241 enc_part: EncryptedData, 1242 ) -> None: 1243 self.tkt_vno = tkt_vno 1244 self.realm = realm 1245 self.sname = sname 1246 self.enc_part = enc_part 1247 1248 @staticmethod 1249 def unpack(value: typing.Union[ASN1Value, bytes]) -> "Ticket": 1250 b_data = extract_asn1_tlv(value, TagClass.application, 1) 1251 sequence = unpack_asn1_tagged_sequence(unpack_asn1(b_data)[0]) 1252 1253 tkt_vno = get_sequence_value(sequence, 0, 'Ticket', 'tkt-vno', unpack_asn1_integer) 1254 realm = get_sequence_value(sequence, 1, 'Ticket', 'realm', unpack_asn1_general_string) 1255 sname = get_sequence_value(sequence, 2, 'Ticket', 'sname', unpack_principal_name) 1256 enc_part = get_sequence_value(sequence, 3, 'Ticket', 'enc-part', EncryptedData.unpack) 1257 1258 return Ticket(tkt_vno, realm, sname, enc_part) 1259 1260 1261class KdcReqBody: 1262 """The KRB_AS_REQ message. 1263 1264 The KRB_AS_REQ message is used when the client wishes to retrieve a the initial ticket for a service. The 1265 KRB_TGS_REQ message is identical except for the tag and msg-type is used when retrieving additional tickets for a 1266 service. 1267 1268 The ASN.1 definition for the KDC-REQ structure is defined in `RFC 4120 5.4.1`_:: 1269 1270 KDC-REQ-BODY ::= SEQUENCE { 1271 kdc-options [0] KDCOptions, 1272 cname [1] PrincipalName OPTIONAL 1273 -- Used only in AS-REQ --, 1274 realm [2] Realm 1275 -- Server's realm 1276 -- Also client's in AS-REQ --, 1277 sname [3] PrincipalName OPTIONAL, 1278 from [4] KerberosTime OPTIONAL, 1279 till [5] KerberosTime, 1280 rtime [6] KerberosTime OPTIONAL, 1281 nonce [7] UInt32, 1282 etype [8] SEQUENCE OF Int32 -- EncryptionType 1283 -- in preference order --, 1284 addresses [9] HostAddresses OPTIONAL, 1285 enc-authorization-data [10] EncryptedData OPTIONAL 1286 -- AuthorizationData --, 1287 additional-tickets [11] SEQUENCE OF Ticket OPTIONAL 1288 -- NOTE: not empty 1289 } 1290 1291 Args: 1292 kdc_options: Flags desired by the client and other behaviour desired. 1293 cname: The client name. 1294 realm: The realm part of the server's principal. 1295 sname: The service name. 1296 postdated_from: When the requested ticket is to be posted from. 1297 postdated_till: The expiration date requested by the client. 1298 rtime: The requested renew-till time. 1299 nonce: Random number generated by the client. 1300 etype: The desired encryption algorithm to be used in priority order. 1301 addresses: Addresses from which the requested ticket is to be valid. 1302 enc_authorization_data: Encrypted authorization data. 1303 additional_tickets: Additional tickets to be optionally included in a request. 1304 1305 Attributes: 1306 kdc_options (int): See args. 1307 cname (Optional[PrincipalName]): See args. 1308 realm (bytes): See args. 1309 sname (PrincipalName): See args. 1310 postdated_from (Optional[datetime.datetime]): See args. 1311 postdated_till (datetime.datetime): See args. 1312 rtime (Optional[datetime.datetime]): See args. 1313 nonce (int): See args. 1314 etype (List[KerberosEncryptionType]): See args. 1315 addresses (Optional[List[HostAddress]]): See args. 1316 enc_authorization_data (Optional[EncryptedData]): See args. 1317 additional_tickets (Optional[Ticket]): See args. 1318 1319 .. _RFC 4120 5.4.1: 1320 https://www.rfc-editor.org/rfc/rfc4120#section-5.4.1 1321 """ 1322 PARSE_MAP = [ 1323 ('kdc-options', 'kdc_options', (ParseType.flags, KerberosKDCOptions)), 1324 ('cname', 'cname', ParseType.principal_name), 1325 ('realm', 'realm', ParseType.text), 1326 ('sname', 'sname', ParseType.principal_name), 1327 ('from', 'postdated_from', ParseType.datetime), 1328 ('till', 'postdated_till', ParseType.datetime), 1329 ('rtime', 'rtime', ParseType.datetime), 1330 ('nonce', 'nonce', ParseType.default), 1331 ('etype', 'etype', ParseType.enum), 1332 ('addresses', 'addresses', ParseType.host_address), 1333 ('enc-authorization-data', 'enc_authorization_data', ParseType.token), 1334 ('additional-tickets', 'additional_tickets', ParseType.token), 1335 ] 1336 1337 def __init__( 1338 self, 1339 kdc_options: int, 1340 cname: typing.Optional[PrincipalName], 1341 realm: bytes, 1342 sname: PrincipalName, 1343 postdated_from: typing.Optional[datetime.datetime], 1344 postdated_till: datetime.datetime, 1345 rtime: typing.Optional[datetime.datetime], 1346 nonce: int, 1347 etype: typing.List[KerberosEncryptionType], 1348 addresses: typing.Optional[typing.List[HostAddress]], 1349 enc_authorization_data: typing.Optional[EncryptedData], 1350 additional_tickets: typing.Optional[typing.List[Ticket]], 1351 ) -> None: 1352 self.kdc_options = kdc_options 1353 self.cname = cname 1354 self.realm = realm 1355 self.sname = sname 1356 self.postdated_from = postdated_from 1357 self.postdated_till = postdated_till 1358 self.rtime = rtime 1359 self.nonce = nonce 1360 self.etype = etype 1361 self.addresses = addresses 1362 self.enc_authorization_data = enc_authorization_data 1363 self.additional_tickets = additional_tickets 1364 1365 @staticmethod 1366 def unpack(value: typing.Union[ASN1Value, bytes]) -> "KdcReqBody": 1367 sequence = unpack_asn1_tagged_sequence(value) 1368 1369 def unpack_kdc_options(value): 1370 b_data = unpack_asn1_bit_string(value) 1371 return struct.unpack(">I", b_data)[0] 1372 1373 def unpack_etype(value): 1374 return [KerberosEncryptionType(unpack_asn1_integer(e)) for e in unpack_asn1_sequence(value)] 1375 1376 def unpack_addresses(value): 1377 return [unpack_hostname(h) for h in unpack_asn1_sequence(value)] 1378 1379 def unpack_ticket(value): 1380 return [Ticket.unpack(t) for t in unpack_asn1_sequence(value)] 1381 1382 kdc_options = get_sequence_value(sequence, 0, 'KDC-REQ-BODY', 'kdc-options', unpack_kdc_options) 1383 cname = get_sequence_value(sequence, 1, 'KDC-REQ-BODY', 'cname', unpack_principal_name) 1384 realm = get_sequence_value(sequence, 2, 'KDC-REQ-BODY', 'realm', unpack_asn1_general_string) 1385 sname = get_sequence_value(sequence, 3, 'KDC-REQ-BODY', 'sname', unpack_principal_name) 1386 postdated_from = get_sequence_value(sequence, 4, 'KDC-REQ-BODY', 'from', unpack_asn1_generalized_time) 1387 postdated_till = get_sequence_value(sequence, 5, 'KDC-REQ-BODY', 'till', unpack_asn1_generalized_time) 1388 rtime = get_sequence_value(sequence, 6, 'KDC-REQ-BODY', 'rtime', unpack_asn1_generalized_time) 1389 nonce = get_sequence_value(sequence, 7, 'KDC-REQ-BODY', 'nonce', unpack_asn1_integer) 1390 etype = get_sequence_value(sequence, 8, 'KDC-REQ-BODY', 'etype', unpack_etype) 1391 addresses = get_sequence_value(sequence, 9, 'KDC-REQ-BODY', 'addresses', unpack_addresses) 1392 enc_auth_data = get_sequence_value(sequence, 10, 'KDC-REQ-BODY', 'enc-authorization-data', 1393 EncryptedData.unpack) 1394 additional_tickets = get_sequence_value(sequence, 11, 'KDC-REQ-BODY', 'additional-tickets', unpack_ticket) 1395 1396 return KdcReqBody(kdc_options, cname, realm, sname, postdated_from, postdated_till, rtime, nonce, etype, 1397 addresses, enc_auth_data, additional_tickets) 1398