From 2cd6d3347faca1bd705460fcd1bd55fa8aa7be47 Mon Sep 17 00:00:00 2001
From: Pierre Lorinquer <pierre.lorinquer@ssi.gouv.fr>
Date: Tue, 28 Mar 2017 11:21:35 +0200
Subject: [PATCH] Fix EAP_MD5 "len" field computation.

The default values of the "value_size" field is None. This can trigger
an issue
when computing the "len" field.

Test: str(EAP_MD5())

In order to fix this issue, the default value of the "value_size" field
has been set to 0.

Tests have been added in "regression.uts".

Added regression tests for EAP_TLS and EAP_FAST.

Use XStrLenField in EAP_MD5, EAP_TLS and EAP_FAST packets instead of StrLenField.

Delete blank lines before the class description (EAPOL, EAP, EAP_MD5, EAP_TLS and EAP_FAST packet classes).

"registered_options" dictionary was renamed "registered_methods"
(EAP-MD5, EAP-TLS etc are not options, but authentication methods).

EAP getlayer() and haslayer() methods have been overloaded in order to
allow access to a given "EAP layer" (such as EAP_TLS, for instance) by
providing the parent class name ("EAP"). For example, this is now
possible:

>>> eap_tls = EAP_TLS()
>>> EAP_TLS in eap_tls
True
>>> EAP in eap_tls
True
>>> eap_tls[EAP_TLS]
<EAP_TLS  |>
>>> eap_tls[EAP]
<EAP_TLS  |>

Regression tests have been added.
---
 scapy/layers/l2.py  | 43 ++++++++++++++++++++++++++++++-------------
 test/regression.uts | 36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 66 insertions(+), 13 deletions(-)

diff --git a/scapy/layers/l2.py b/scapy/layers/l2.py
index 1bda6318..252d8a22 100644
--- a/scapy/layers/l2.py
+++ b/scapy/layers/l2.py
@@ -310,7 +310,6 @@ eapol_types = {
 
 
 class EAPOL(Packet):
-
     """
     EAPOL - IEEE Std 802.1X-2010
     """
@@ -438,7 +437,6 @@ eap_codes = {
 
 
 class EAP(Packet):
-
     """
     RFC 3748 - Extensible Authentication Protocol (EAP)
     """
@@ -475,11 +473,11 @@ class EAP(Packet):
     INITIATE = 5
     FINISH = 6
 
-    registered_options = {}
+    registered_methods = {}
 
     @classmethod
     def register_variant(cls):
-        cls.registered_options[cls.type.default] = cls
+        cls.registered_methods[cls.type.default] = cls
 
     @classmethod
     def dispatch_hook(cls, _pkt=None, *args, **kargs):
@@ -487,9 +485,31 @@ class EAP(Packet):
             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.registered_methods.get(t, cls)
         return cls
 
+    def haslayer(self, cls):
+        ret = 0
+        if cls == EAP:
+            for eap_class in EAP.registered_methods.values():
+                if isinstance(self, eap_class):
+                    ret = 1
+                    break
+        elif cls in EAP.registered_methods.values() and isinstance(self, cls):
+            ret = 1
+        return ret
+
+    def getlayer(self, cls, nb=1, _track=None):
+        layer = None
+        if cls == EAP:
+            for eap_class in EAP.registered_methods.values():
+                if isinstance(self, eap_class):
+                    layer = self
+                    break
+        else:
+            layer = Packet.getlayer(self, cls, nb, _track)
+        return layer
+
     def answers(self, other):
         if isinstance(other, EAP):
             if self.code == self.REQUEST:
@@ -510,7 +530,6 @@ class EAP(Packet):
 
 
 class EAP_MD5(EAP):
-
     """
     RFC 3748 - "Extensible Authentication Protocol (EAP)"
     """
@@ -522,14 +541,13 @@ class EAP_MD5(EAP):
         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)
+        FieldLenField("value_size", 0, fmt="B", length_of="value"),
+        XStrLenField("value", '', length_from=lambda p: p.value_size),
+        XStrLenField("optional_name", '', length_from=lambda p: p.len - p.value_size - 6)
     ]
 
 
 class EAP_TLS(EAP):
-
     """
     RFC 5216 - "The EAP-TLS Authentication Protocol"
     """
@@ -546,12 +564,11 @@ class EAP_TLS(EAP):
         BitField('S', 0, 1),
         BitField('reserved', 0, 5),
         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)
+        XStrLenField('tls_data', '', length_from=lambda pkt: pkt.len - 10 if pkt.L == 1 else pkt.len - 6)
     ]
 
 
 class EAP_FAST(EAP):
-
     """
     RFC 4851 - "The Flexible Authentication via Secure Tunneling
     Extensible Authentication Protocol Method (EAP-FAST)"
@@ -570,7 +587,7 @@ class EAP_FAST(EAP):
         BitField('reserved', 0, 2),
         BitField('version', 0, 3),
         ConditionalField(IntField('message_len', 0), lambda pkt: pkt.L == 1),
-        StrLenField('data', '', length_from=lambda pkt: pkt.len - 10 if pkt.L == 1 else pkt.len - 6)
+        XStrLenField('data', '', length_from=lambda pkt: pkt.len - 10 if pkt.L == 1 else pkt.len - 6)
     ]
 
 
diff --git a/test/regression.uts b/test/regression.uts
index be34b65d..a4205431 100644
--- a/test/regression.uts
+++ b/test/regression.uts
@@ -5805,6 +5805,15 @@ assert(eap.type == 3)
 assert(hasattr(eap, 'desired_auth_type'))
 assert(eap.desired_auth_type == 43)
 
+= EAP - EAP_TLS - Basic Instantiation
+str(EAP_TLS()) == b'\x01\x00\x00\x06\r\x00'
+
+= EAP - EAP_FAST - Basic Instantiation
+str(EAP_FAST()) == b'\x01\x00\x00\x06+\x00'
+
+= EAP - EAP_MD5 - Basic Instantiation
+str(EAP_MD5()) == b'\x01\x00\x00\x06\x04\x00'
+
 = EAP - EAP_MD5 - Request - Dissection (8)
 s = b'\x01\x02\x00\x16\x04\x10\x86\xf9\x89\x94\x81\x01\xb3 nHh\x1b\x8d\xe7^\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
 eap = EAP(s)
@@ -5829,6 +5838,33 @@ assert(eap[EAP_MD5].value_size == 16)
 assert(eap[EAP_MD5].value == b'\xfd\x1e\xffe\xf5\x80y\xa8\xe3\xc8\xf1\xbd\xc2\x85\xae\xcf')
 assert(eap[EAP_MD5].optional_name == '')
 
+= EAP - Layers (1)
+eap = EAP_MD5()
+assert(EAP_MD5 in eap)
+assert(not EAP_TLS in eap)
+assert(not EAP_FAST in eap)
+assert(EAP in eap)
+eap = EAP_TLS()
+assert(EAP_TLS in eap)
+assert(not EAP_MD5 in eap)
+assert(not EAP_FAST in eap)
+assert(EAP in eap)
+eap = EAP_FAST()
+assert(EAP_FAST in eap)
+assert(not EAP_MD5 in eap)
+assert(not EAP_TLS in eap)
+assert(EAP in eap)
+
+
+= EAP - Layers (2)
+eap = EAP_MD5()
+assert(type(eap[EAP]) == EAP_MD5)
+eap = EAP_TLS()
+assert(type(eap[EAP]) == EAP_TLS)
+eap = EAP_FAST()
+assert(type(eap[EAP]) == EAP_FAST)
+
+
 ############
 ############
 + NTP module tests
-- 
GitLab