From c2333696ccc842c56658dc85a24f786f9e9e4466 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A1n=20Sebechlebsk=C3=BD?= <sebechlebskyjan@gmail.com>
Date: Thu, 23 Mar 2017 11:27:49 +0100
Subject: [PATCH] Fix EAP (#557)

* Add message field in EAP-Indentity request

EAP-Identity request may contain message as stated in RFC3748.

* Fix fragmented EAP-TLS

Current implementation fails to dissect fragmented EAP-TLS.
Fragmented EAP-TLS messages are quite common, in this case only
first EAP-TLS will contain tls_message_len field (indicated by L bit),
which will be total length of reassembled tls message.

Length of tls payload in single EAP-TLS message should therefore
be determined by EAP.len field (see RFC5216-Fragmentation).

To implement this I find reasonable to make EAP-TLS subclass of EAP.

* Fix fragmented EAP-FAST

Implementation of EAP-FAST suffers from the same issue as EAP-TLS,
see previous commit message.

* Fix EAP-MD5 Dissection

Implementation of EAP-MD5 was not following RFC3748 (which is
referencing RFC1994) properly.

Field value_size is determining only size of value_field.
It might have length different than 16B in EAP-MD5 request,
because in that case it is not used to transmit MD5 hash,
but random challenge value.
Size of optional_name(extra data) is determined as "rest of" EAP message
(len field of EAP).
---
 scapy/layers/l2.py | 59 +++++++++++++++++++++++++++++++++-------------
 1 file changed, 43 insertions(+), 16 deletions(-)

diff --git a/scapy/layers/l2.py b/scapy/layers/l2.py
index 584deda1..1bda6318 100644
--- a/scapy/layers/l2.py
+++ b/scapy/layers/l2.py
@@ -455,7 +455,10 @@ class EAP(Packet):
                          lambda pkt:pkt.code == EAP.RESPONSE and pkt.type == 3),
         ConditionalField(
             StrLenField("identity", '', length_from=lambda pkt: pkt.len - 5),
-                         lambda pkt: pkt.code == EAP.RESPONSE and hasattr(pkt, 'type') and pkt.type == 1)
+                         lambda pkt: pkt.code == EAP.RESPONSE and hasattr(pkt, 'type') and pkt.type == 1),
+        ConditionalField(
+            StrLenField("message", '', length_from=lambda pkt: pkt.len - 5),
+                         lambda pkt: pkt.code == EAP.REQUEST and hasattr(pkt, 'type') and pkt.type == 1)
     ]
 
     #________________________________________________________________________
@@ -472,6 +475,21 @@ class EAP(Packet):
     INITIATE = 5
     FINISH = 6
 
+    registered_options = {}
+
+    @classmethod
+    def register_variant(cls):
+        cls.registered_options[cls.type.default] = cls
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt:
+            c = ord(_pkt[0])
+            if c in [1, 2] and len(_pkt) >= 5:
+                t = ord(_pkt[4])
+                return cls.registered_options.get(t, cls)
+        return cls
+
     def answers(self, other):
         if isinstance(other, EAP):
             if self.code == self.REQUEST:
@@ -491,7 +509,7 @@ class EAP(Packet):
         return p + pay
 
 
-class EAP_MD5(Packet):
+class EAP_MD5(EAP):
 
     """
     RFC 3748 - "Extensible Authentication Protocol (EAP)"
@@ -499,13 +517,18 @@ class EAP_MD5(Packet):
 
     name = "EAP-MD5"
     fields_desc = [
-        ByteField("value_size", 0),
-        StrFixedLenField("value", 0, length=16), # MD5 hash length
-        StrLenField("optional_name", "", length_from=lambda p: p.value_size - 16)
+        ByteEnumField("code", 1, eap_codes),
+        ByteField("id", 0),
+        FieldLenField("len", None, fmt="H", length_of="optional_name",
+                      adjust=lambda p, x: x + p.value_size + 6),
+        ByteEnumField("type", 4, eap_types),
+        FieldLenField("value_size", None, fmt="B", length_of="value"),
+        StrLenField("value", '', length_from=lambda p: p.value_size),
+        StrLenField("optional_name", '', length_from=lambda p: p.len - p.value_size - 6)
     ]
 
 
-class EAP_TLS(Packet):
+class EAP_TLS(EAP):
 
     """
     RFC 5216 - "The EAP-TLS Authentication Protocol"
@@ -513,18 +536,21 @@ class EAP_TLS(Packet):
 
     name = "EAP-TLS"
     fields_desc = [
+        ByteEnumField("code", 1, eap_codes),
+        ByteField("id", 0),
+        FieldLenField("len", None, fmt="H", length_of="tls_data",
+                      adjust=lambda p, x: x + 10 if p.L == 1 else x + 6),
+        ByteEnumField("type", 13, eap_types),
         BitField('L', 0, 1),
         BitField('M', 0, 1),
         BitField('S', 0, 1),
         BitField('reserved', 0, 5),
-        ConditionalField(
-            IntField('tls_message_len', 0), lambda pkt: pkt.L == 1),
-        ConditionalField(
-            StrLenField('tls_data', '', length_from=lambda pkt: pkt.tls_message_len), lambda pkt: pkt.L == 1)
+        ConditionalField(IntField('tls_message_len', 0), lambda pkt: pkt.L == 1),
+        StrLenField('tls_data', '', length_from=lambda pkt: pkt.len - 10 if pkt.L == 1 else pkt.len - 6)
     ]
 
 
-class EAP_FAST(Packet):
+class EAP_FAST(EAP):
 
     """
     RFC 4851 - "The Flexible Authentication via Secure Tunneling
@@ -533,14 +559,18 @@ class EAP_FAST(Packet):
 
     name = "EAP-FAST"
     fields_desc = [
+        ByteEnumField("code", 1, eap_codes),
+        ByteField("id", 0),
+        FieldLenField("len", None, fmt="H", length_of="data",
+                      adjust=lambda p, x: x + 10 if p.L == 1 else x + 6),
+        ByteEnumField("type", 43, eap_types),
         BitField('L', 0, 1),
         BitField('M', 0, 1),
         BitField('S', 0, 1),
         BitField('reserved', 0, 2),
         BitField('version', 0, 3),
         ConditionalField(IntField('message_len', 0), lambda pkt: pkt.L == 1),
-        ConditionalField(
-            StrLenField('data', '', length_from=lambda pkt: pkt.message_len), lambda pkt: pkt.L == 1)
+        StrLenField('data', '', length_from=lambda pkt: pkt.len - 10 if pkt.L == 1 else pkt.len - 6)
     ]
 
 
@@ -1068,9 +1098,6 @@ bind_layers( GRErouting,    conf.raw_layer,{ "address_family" : 0, "SRE_len" : 0
 bind_layers( GRErouting,    GRErouting,    { } )
 bind_layers( EAPOL,         EAP,           type=0)
 bind_layers( EAPOL,         MKAPDU,        type=5)
-bind_layers(EAP,           EAP_TLS,       type=13)
-bind_layers(EAP,           EAP_FAST,      type=43)
-bind_layers( EAP,           EAP_MD5,       type=4)
 bind_layers( LLC,           STP,           dsap=66, ssap=66, ctrl=3)
 bind_layers( LLC,           SNAP,          dsap=170, ssap=170, ctrl=3)
 bind_layers( SNAP,          Dot1Q,         code=33024)
-- 
GitLab