1## This file is part of Scapy 2## See http://www.secdev.org/projects/scapy for more informations 3## Copyright (C) Philippe Biondi <phil@secdev.org> 4## This program is published under a GPLv2 license 5 6""" 7Classes related to the EAP protocol. 8""" 9 10from __future__ import absolute_import 11from __future__ import print_function 12 13import struct 14 15from scapy.fields import BitField, ByteField, XByteField, ByteEnumField,\ 16ShortField, IntField, XIntField, ByteEnumField, StrLenField, XStrField,\ 17XStrLenField, XStrFixedLenField, LenField, FieldLenField, PacketField,\ 18PacketListField, ConditionalField, PadField 19from scapy.packet import Packet, bind_layers 20from scapy.layers.l2 import SourceMACField, Ether, CookedLinux, GRE, SNAP 21from scapy.config import conf 22from scapy.compat import orb, chb 23 24# 25# EAPOL 26# 27 28#________________________________________________________________________ 29# 30# EAPOL protocol version 31# IEEE Std 802.1X-2010 - Section 11.3.1 32#________________________________________________________________________ 33# 34 35eapol_versions = { 36 0x1: "802.1X-2001", 37 0x2: "802.1X-2004", 38 0x3: "802.1X-2010", 39} 40 41#________________________________________________________________________ 42# 43# EAPOL Packet Types 44# IEEE Std 802.1X-2010 - Table 11.3 45#________________________________________________________________________ 46# 47 48eapol_types = { 49 0x0: "EAP-Packet", # "EAPOL-EAP" in 801.1X-2010 50 0x1: "EAPOL-Start", 51 0x2: "EAPOL-Logoff", 52 0x3: "EAPOL-Key", 53 0x4: "EAPOL-Encapsulated-ASF-Alert", 54 0x5: "EAPOL-MKA", 55 0x6: "EAPOL-Announcement (Generic)", 56 0x7: "EAPOL-Announcement (Specific)", 57 0x8: "EAPOL-Announcement-Req" 58} 59 60 61class EAPOL(Packet): 62 """ 63 EAPOL - IEEE Std 802.1X-2010 64 """ 65 66 name = "EAPOL" 67 fields_desc = [ 68 ByteEnumField("version", 1, eapol_versions), 69 ByteEnumField("type", 0, eapol_types), 70 LenField("len", None, "H") 71 ] 72 73 EAP_PACKET = 0 74 START = 1 75 LOGOFF = 2 76 KEY = 3 77 ASF = 4 78 79 def extract_padding(self, s): 80 l = self.len 81 return s[:l], s[l:] 82 83 def hashret(self): 84 return chb(self.type) + self.payload.hashret() 85 86 def answers(self, other): 87 if isinstance(other, EAPOL): 88 if ((self.type == self.EAP_PACKET) and 89 (other.type == self.EAP_PACKET)): 90 return self.payload.answers(other.payload) 91 return 0 92 93 def mysummary(self): 94 return self.sprintf("EAPOL %EAPOL.type%") 95 96 97# 98# EAP 99# 100 101 102#________________________________________________________________________ 103# 104# EAP methods types 105# http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-4 106#________________________________________________________________________ 107# 108 109eap_types = { 110 0: "Reserved", 111 1: "Identity", 112 2: "Notification", 113 3: "Legacy Nak", 114 4: "MD5-Challenge", 115 5: "One-Time Password (OTP)", 116 6: "Generic Token Card (GTC)", 117 7: "Allocated - RFC3748", 118 8: "Allocated - RFC3748", 119 9: "RSA Public Key Authentication", 120 10: "DSS Unilateral", 121 11: "KEA", 122 12: "KEA-VALIDATE", 123 13: "EAP-TLS", 124 14: "Defender Token (AXENT)", 125 15: "RSA Security SecurID EAP", 126 16: "Arcot Systems EAP", 127 17: "EAP-Cisco Wireless", 128 18: "GSM Subscriber Identity Modules (EAP-SIM)", 129 19: "SRP-SHA1", 130 20: "Unassigned", 131 21: "EAP-TTLS", 132 22: "Remote Access Service", 133 23: "EAP-AKA Authentication", 134 24: "EAP-3Com Wireless", 135 25: "PEAP", 136 26: "MS-EAP-Authentication", 137 27: "Mutual Authentication w/Key Exchange (MAKE)", 138 28: "CRYPTOCard", 139 29: "EAP-MSCHAP-V2", 140 30: "DynamID", 141 31: "Rob EAP", 142 32: "Protected One-Time Password", 143 33: "MS-Authentication-TLV", 144 34: "SentriNET", 145 35: "EAP-Actiontec Wireless", 146 36: "Cogent Systems Biometrics Authentication EAP", 147 37: "AirFortress EAP", 148 38: "EAP-HTTP Digest", 149 39: "SecureSuite EAP", 150 40: "DeviceConnect EAP", 151 41: "EAP-SPEKE", 152 42: "EAP-MOBAC", 153 43: "EAP-FAST", 154 44: "ZoneLabs EAP (ZLXEAP)", 155 45: "EAP-Link", 156 46: "EAP-PAX", 157 47: "EAP-PSK", 158 48: "EAP-SAKE", 159 49: "EAP-IKEv2", 160 50: "EAP-AKA", 161 51: "EAP-GPSK", 162 52: "EAP-pwd", 163 53: "EAP-EKE Version 1", 164 54: "EAP Method Type for PT-EAP", 165 55: "TEAP", 166 254: "Reserved for the Expanded Type", 167 255: "Experimental", 168} 169 170 171#________________________________________________________________________ 172# 173# EAP codes 174# http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-1 175#________________________________________________________________________ 176# 177 178eap_codes = { 179 1: "Request", 180 2: "Response", 181 3: "Success", 182 4: "Failure", 183 5: "Initiate", 184 6: "Finish" 185} 186 187 188class EAP(Packet): 189 """ 190 RFC 3748 - Extensible Authentication Protocol (EAP) 191 """ 192 193 name = "EAP" 194 fields_desc = [ 195 ByteEnumField("code", 4, eap_codes), 196 ByteField("id", 0), 197 ShortField("len", None), 198 ConditionalField(ByteEnumField("type", 0, eap_types), 199 lambda pkt:pkt.code not in [ 200 EAP.SUCCESS, EAP.FAILURE]), 201 ConditionalField(ByteEnumField("desired_auth_type", 0, eap_types), 202 lambda pkt:pkt.code == EAP.RESPONSE and pkt.type == 3), 203 ConditionalField( 204 StrLenField("identity", '', length_from=lambda pkt: pkt.len - 5), 205 lambda pkt: pkt.code == EAP.RESPONSE and hasattr(pkt, 'type') and pkt.type == 1), 206 ConditionalField( 207 StrLenField("message", '', length_from=lambda pkt: pkt.len - 5), 208 lambda pkt: pkt.code == EAP.REQUEST and hasattr(pkt, 'type') and pkt.type == 1) 209 ] 210 211 #________________________________________________________________________ 212 # 213 # EAP codes 214 # http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-1 215 #________________________________________________________________________ 216 # 217 218 REQUEST = 1 219 RESPONSE = 2 220 SUCCESS = 3 221 FAILURE = 4 222 INITIATE = 5 223 FINISH = 6 224 225 registered_methods = {} 226 227 @classmethod 228 def register_variant(cls): 229 cls.registered_methods[cls.type.default] = cls 230 231 @classmethod 232 def dispatch_hook(cls, _pkt=None, *args, **kargs): 233 if _pkt: 234 c = orb(_pkt[0]) 235 if c in [1, 2] and len(_pkt) >= 5: 236 t = orb(_pkt[4]) 237 return cls.registered_methods.get(t, cls) 238 return cls 239 240 def haslayer(self, cls): 241 if cls == "EAP": 242 if isinstance(self, EAP): 243 return True 244 elif issubclass(cls, EAP): 245 if isinstance(self, cls): 246 return True 247 return super(EAP, self).haslayer(cls) 248 249 def getlayer(self, cls, nb=1, _track=None, _subclass=True, **flt): 250 return super(EAP, self).getlayer(cls, nb=nb, _track=_track, 251 _subclass=True, **flt) 252 253 def answers(self, other): 254 if isinstance(other, EAP): 255 if self.code == self.REQUEST: 256 return 0 257 elif self.code == self.RESPONSE: 258 if ((other.code == self.REQUEST) and 259 (other.type == self.type)): 260 return 1 261 elif other.code == self.RESPONSE: 262 return 1 263 return 0 264 265 def mysummary(self): 266 summary_str = "EAP %{eap_class}.code% %{eap_class}.type%".format( 267 eap_class = self.__class__.__name__ 268 ) 269 if self.type == 1 and self.code == EAP.RESPONSE: 270 summary_str += " %{eap_class}.identity%".format( 271 eap_class = self.__class__.__name__ 272 ) 273 return self.sprintf(summary_str) 274 275 def post_build(self, p, pay): 276 if self.len is None: 277 l = len(p) + len(pay) 278 p = p[:2] + chb((l >> 8) & 0xff) + chb(l & 0xff) + p[4:] 279 return p + pay 280 281 282class EAP_MD5(EAP): 283 """ 284 RFC 3748 - "Extensible Authentication Protocol (EAP)" 285 """ 286 287 name = "EAP-MD5" 288 fields_desc = [ 289 ByteEnumField("code", 1, eap_codes), 290 ByteField("id", 0), 291 FieldLenField("len", None, fmt="H", length_of="optional_name", 292 adjust=lambda p, x: x + 6 + (p.value_size or 0)), 293 ByteEnumField("type", 4, eap_types), 294 FieldLenField("value_size", None, fmt="B", length_of="value"), 295 XStrLenField("value", '', length_from=lambda p: p.value_size), 296 XStrLenField("optional_name", '', length_from=lambda p: 0 if p.len is None or p.value_size is None else (p.len - p.value_size - 6)) 297 ] 298 299 300class EAP_TLS(EAP): 301 """ 302 RFC 5216 - "The EAP-TLS Authentication Protocol" 303 """ 304 305 name = "EAP-TLS" 306 fields_desc = [ 307 ByteEnumField("code", 1, eap_codes), 308 ByteField("id", 0), 309 FieldLenField("len", None, fmt="H", length_of="tls_data", 310 adjust=lambda p, x: x + 10 if p.L == 1 else x + 6), 311 ByteEnumField("type", 13, eap_types), 312 BitField('L', 0, 1), 313 BitField('M', 0, 1), 314 BitField('S', 0, 1), 315 BitField('reserved', 0, 5), 316 ConditionalField(IntField('tls_message_len', 0), lambda pkt: pkt.L == 1), 317 XStrLenField('tls_data', '', length_from=lambda pkt: 0 if pkt.len is None else pkt.len - (6 + 4 * pkt.L)) 318 ] 319 320 321class EAP_TTLS(EAP): 322 """ 323 RFC 5281 - "Extensible Authentication Protocol Tunneled Transport Layer 324 Security Authenticated Protocol Version 0 (EAP-TTLSv0)" 325 """ 326 327 name = "EAP-TTLS" 328 fields_desc = [ 329 ByteEnumField("code", 1, eap_codes), 330 ByteField("id", 0), 331 FieldLenField("len", None, fmt="H", length_of="data", 332 adjust=lambda p, x: x + 10 if p.L == 1 else x + 6), 333 ByteEnumField("type", 21, eap_types), 334 BitField("L", 0, 1), 335 BitField("M", 0, 1), 336 BitField("S", 0, 1), 337 BitField("reserved", 0, 2), 338 BitField("version", 0, 3), 339 ConditionalField(IntField("message_len", 0), lambda pkt: pkt.L == 1), 340 XStrLenField("data", "", length_from=lambda pkt: 0 if pkt.len is None else pkt.len - (6 + 4 * pkt.L)) 341 ] 342 343 344class EAP_FAST(EAP): 345 """ 346 RFC 4851 - "The Flexible Authentication via Secure Tunneling 347 Extensible Authentication Protocol Method (EAP-FAST)" 348 """ 349 350 name = "EAP-FAST" 351 fields_desc = [ 352 ByteEnumField("code", 1, eap_codes), 353 ByteField("id", 0), 354 FieldLenField("len", None, fmt="H", length_of="data", 355 adjust=lambda p, x: x + 10 if p.L == 1 else x + 6), 356 ByteEnumField("type", 43, eap_types), 357 BitField('L', 0, 1), 358 BitField('M', 0, 1), 359 BitField('S', 0, 1), 360 BitField('reserved', 0, 2), 361 BitField('version', 0, 3), 362 ConditionalField(IntField('message_len', 0), lambda pkt: pkt.L == 1), 363 XStrLenField('data', '', length_from=lambda pkt: 0 if pkt.len is None else pkt.len - (6 + 4 * pkt.L)) 364 ] 365 366 367class LEAP(EAP): 368 """ 369 Cisco LEAP (Lightweight EAP) 370 https://freeradius.org/rfc/leap.txt 371 """ 372 373 name = "Cisco LEAP" 374 fields_desc = [ 375 ByteEnumField("code", 1, eap_codes), 376 ByteField("id", 0), 377 ShortField("len", None), 378 ByteEnumField("type", 17, eap_types), 379 ByteField('version', 1), 380 XByteField('unused', 0), 381 FieldLenField("count", None, "challenge_response", "B", adjust=lambda p, x: len(p.challenge_response)), 382 XStrLenField("challenge_response", "", length_from=lambda p: 0 or p.count), 383 StrLenField("username", "", length_from=lambda p: p.len - (8 + (0 or p.count))) 384 ] 385 386 387############################################################################# 388##### IEEE 802.1X-2010 - MACsec Key Agreement (MKA) protocol 389############################################################################# 390 391#________________________________________________________________________ 392# 393# IEEE 802.1X-2010 standard 394# Section 11.11.1 395#________________________________________________________________________ 396# 397 398_parameter_set_types = { 399 1: "Live Peer List", 400 2: "Potential Peer List", 401 3: "MACsec SAK Use", 402 4: "Distributed SAK", 403 5: "Distributed CAK", 404 6: "KMD", 405 7: "Announcement", 406 255: "ICV Indicator" 407} 408 409 410# Used by MKAParamSet::dispatch_hook() to instantiate the appropriate class 411_param_set_cls = { 412 1: "MKALivePeerListParamSet", 413 2: "MKAPotentialPeerListParamSet", 414 3: "MKASAKUseParamSet", 415 4: "MKADistributedSAKParamSet", 416 255: "MKAICVSet", 417} 418 419 420class MACsecSCI(Packet): 421 """ 422 Secure Channel Identifier. 423 """ 424 425 #________________________________________________________________________ 426 # 427 # IEEE 802.1AE-2006 standard 428 # Section 9.9 429 #________________________________________________________________________ 430 # 431 432 name = "SCI" 433 fields_desc = [ 434 SourceMACField("system_identifier"), 435 ShortField("port_identifier", 0) 436 ] 437 438 def extract_padding(self, s): 439 return "", s 440 441 442class MKAParamSet(Packet): 443 """ 444 Class from which every parameter set class inherits (except 445 MKABasicParamSet, which has no "Parameter set type" field, and must 446 come first in the list of parameter sets). 447 """ 448 449 MACSEC_DEFAULT_ICV_LEN = 16 450 EAPOL_MKA_DEFAULT_KEY_WRAP_LEN = 24 451 452 @classmethod 453 def dispatch_hook(cls, _pkt=None, *args, **kargs): 454 """ 455 Returns the right parameter set class. 456 """ 457 458 cls = conf.raw_layer 459 if _pkt is not None: 460 ptype = orb(_pkt[0]) 461 return globals().get(_param_set_cls.get(ptype), conf.raw_layer) 462 463 return cls 464 465 466class MKABasicParamSet(Packet): 467 """ 468 Basic Parameter Set (802.1X-2010, section 11.11). 469 """ 470 471 #________________________________________________________________________ 472 # 473 # IEEE 802.1X-2010 standard 474 # Section 11.11 475 #________________________________________________________________________ 476 # 477 478 name = "Basic Parameter Set" 479 fields_desc = [ 480 ByteField("mka_version_id", 0), 481 ByteField("key_server_priority", 0), 482 BitField("key_server", 0, 1), 483 BitField("macsec_desired", 0, 1), 484 BitField("macsec_capability", 0, 2), 485 BitField("param_set_body_len", 0, 12), 486 PacketField("SCI", MACsecSCI(), MACsecSCI), 487 XStrFixedLenField("actor_member_id", "", length=12), 488 XIntField("actor_message_number", 0), 489 XIntField("algorithm_agility", 0), 490 PadField( 491 XStrLenField( 492 "cak_name", 493 "", 494 length_from=lambda pkt: (pkt.param_set_body_len - 28) 495 ), 496 4, 497 padwith=b"\x00" 498 ) 499 ] 500 501 def extract_padding(self, s): 502 return "", s 503 504 505class MKAPeerListTuple(Packet): 506 """ 507 Live / Potential Peer List parameter sets tuples (802.1X-2010, section 11.11). 508 """ 509 510 name = "Peer List Tuple" 511 fields_desc = [ 512 XStrFixedLenField("member_id", "", length=12), 513 XStrFixedLenField("message_number", "", length=4), 514 ] 515 516 517class MKALivePeerListParamSet(MKAParamSet): 518 """ 519 Live Peer List parameter sets (802.1X-2010, section 11.11). 520 """ 521 522 #________________________________________________________________________ 523 # 524 # IEEE 802.1X-2010 standard 525 # Section 11.11 526 #________________________________________________________________________ 527 # 528 529 name = "Live Peer List Parameter Set" 530 fields_desc = [ 531 PadField( 532 ByteEnumField( 533 "param_set_type", 534 1, 535 _parameter_set_types 536 ), 537 2, 538 padwith=b"\x00" 539 ), 540 ShortField("param_set_body_len", 0), 541 PacketListField("member_id_message_num", [], MKAPeerListTuple) 542 ] 543 544 545class MKAPotentialPeerListParamSet(MKAParamSet): 546 """ 547 Potential Peer List parameter sets (802.1X-2010, section 11.11). 548 """ 549 550 #________________________________________________________________________ 551 # 552 # IEEE 802.1X-2010 standard 553 # Section 11.11 554 #________________________________________________________________________ 555 # 556 557 name = "Potential Peer List Parameter Set" 558 fields_desc = [ 559 PadField( 560 ByteEnumField( 561 "param_set_type", 562 2, 563 _parameter_set_types 564 ), 565 2, 566 padwith=b"\x00" 567 ), 568 ShortField("param_set_body_len", 0), 569 PacketListField("member_id_message_num", [], MKAPeerListTuple) 570 ] 571 572 573class MKASAKUseParamSet(MKAParamSet): 574 """ 575 SAK Use Parameter Set (802.1X-2010, section 11.11). 576 """ 577 578 #________________________________________________________________________ 579 # 580 # IEEE 802.1X-2010 standard 581 # Section 11.11 582 #________________________________________________________________________ 583 # 584 585 name = "SAK Use Parameter Set" 586 fields_desc = [ 587 ByteEnumField("param_set_type", 3, _parameter_set_types), 588 BitField("latest_key_an", 0, 2), 589 BitField("latest_key_tx", 0, 1), 590 BitField("latest_key_rx", 0, 1), 591 BitField("old_key_an", 0, 2), 592 BitField("old_key_tx", 0, 1), 593 BitField("old_key_rx", 0, 1), 594 BitField("plain_tx", 0, 1), 595 BitField("plain_rx", 0, 1), 596 BitField("X", 0, 1), 597 BitField("delay_protect", 0, 1), 598 BitField("param_set_body_len", 0, 12), 599 XStrFixedLenField("latest_key_key_server_member_id", "", length=12), 600 XStrFixedLenField("latest_key_key_number", "", length=4), 601 XStrFixedLenField("latest_key_lowest_acceptable_pn", "", length=4), 602 XStrFixedLenField("old_key_key_server_member_id", "", length=12), 603 XStrFixedLenField("old_key_key_number", "", length=4), 604 XStrFixedLenField("old_key_lowest_acceptable_pn", "", length=4) 605 ] 606 607 608class MKADistributedSAKParamSet(MKAParamSet): 609 """ 610 Distributed SAK parameter set (802.1X-2010, section 11.11). 611 """ 612 613 #________________________________________________________________________ 614 # 615 # IEEE 802.1X-2010 standard 616 # Section 11.11 617 #________________________________________________________________________ 618 # 619 620 name = "Distributed SAK parameter set" 621 fields_desc = [ 622 ByteEnumField("param_set_type", 4, _parameter_set_types), 623 BitField("distributed_an", 0, 2), 624 BitField("confidentiality_offset", 0, 2), 625 BitField("unused", 0, 4), 626 ShortField("param_set_body_len", 0), 627 XStrFixedLenField("key_number", "", length=4), 628 ConditionalField( 629 XStrFixedLenField("macsec_cipher_suite", "", length=8), 630 lambda pkt: pkt.param_set_body_len > 28 631 ), 632 XStrFixedLenField( 633 "sak_aes_key_wrap", 634 "", 635 length=MKAParamSet.EAPOL_MKA_DEFAULT_KEY_WRAP_LEN 636 ) 637 ] 638 639 640class MKADistributedCAKParamSet(MKAParamSet): 641 """ 642 Distributed CAK Parameter Set (802.1X-2010, section 11.11). 643 """ 644 645 #________________________________________________________________________ 646 # 647 # IEEE 802.1X-2010 standard 648 # Section 11.11 649 #________________________________________________________________________ 650 # 651 652 name = "Distributed CAK parameter set" 653 fields_desc = [ 654 PadField( 655 ByteEnumField( 656 "param_set_type", 657 5, 658 _parameter_set_types 659 ), 660 2, 661 padwith=b"\x00" 662 ), 663 ShortField("param_set_body_len", 0), 664 XStrFixedLenField( 665 "cak_aes_key_wrap", 666 "", 667 length=MKAParamSet.EAPOL_MKA_DEFAULT_KEY_WRAP_LEN 668 ), 669 XStrField("cak_key_name", "") 670 ] 671 672 673class MKAICVSet(MKAParamSet): 674 """ 675 ICV (802.1X-2010, section 11.11). 676 """ 677 678 #________________________________________________________________________ 679 # 680 # IEEE 802.1X-2010 standard 681 # Section 11.11 682 #________________________________________________________________________ 683 # 684 685 name = "ICV" 686 fields_desc = [ 687 PadField( 688 ByteEnumField( 689 "param_set_type", 690 255, 691 _parameter_set_types 692 ), 693 2, 694 padwith=b"\x00" 695 ), 696 ShortField("param_set_body_len", 0), 697 XStrFixedLenField("icv", "", length=MKAParamSet.MACSEC_DEFAULT_ICV_LEN) 698 ] 699 700 701class MKAParamSetPacketListField(PacketListField): 702 """ 703 PacketListField that handles the parameter sets. 704 """ 705 706 PARAM_SET_LEN_MASK = 0b0000111111111111 707 708 def m2i(self, pkt, m): 709 return MKAParamSet(m) 710 711 def getfield(self, pkt, s): 712 lst = [] 713 remain = s 714 715 while remain: 716 len_bytes = struct.unpack("!H", remain[2:4])[0] 717 param_set_len = self.__class__.PARAM_SET_LEN_MASK & len_bytes 718 current = remain[:4 + param_set_len] 719 remain = remain[4 + param_set_len:] 720 current_packet = self.m2i(pkt, current) 721 lst.append(current_packet) 722 723 return remain, lst 724 725 726class MKAPDU(Packet): 727 """ 728 MACsec Key Agreement Protocol Data Unit. 729 """ 730 731 #________________________________________________________________________ 732 # 733 # IEEE 802.1X-2010 standard 734 # Section 11.11 735 #________________________________________________________________________ 736 # 737 738 name = "MKPDU" 739 fields_desc = [ 740 PacketField("basic_param_set", "", MKABasicParamSet), 741 MKAParamSetPacketListField("parameter_sets", [], MKAParamSet), 742 ] 743 744 def extract_padding(self, s): 745 return "", s 746 747 748bind_layers( Ether, EAPOL, type=34958) 749bind_layers( Ether, EAPOL, dst='01:80:c2:00:00:03', type=34958) 750bind_layers( CookedLinux, EAPOL, proto=34958) 751bind_layers( GRE, EAPOL, proto=34958) 752bind_layers( EAPOL, EAP, type=0) 753bind_layers( SNAP, EAPOL, code=34958) 754bind_layers( EAPOL, MKAPDU, type=5) 755 756