• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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