Repository: secdev/scapy

Issue:
bug in scapy/layers/radius.py
Environment:
python 2.7
scapy 2.4.0 (same problem here at scapy latest version)

Problem:
data word is not pushed back to the base class.

`class _RadiusAttrHexStringVal(_SpecificRadiusAttr):
       
     __slots__ = ["val"]

    def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None, **fields):  # noqa: E501
        super(_RadiusAttrHexStringVal, self).__init__(
            _pkt,
            post_transform,
            _internal,
            _underlayer
        )`
leading to the following result (show2() used):

> ###[ RADIUS ]### 
        code      = Access-Request
        id        = 156
        len       = 29
        authenticator= 4134384d33394d594f50484452445342
        \attributes\
         |###[ User-Name ]### 
         |  type      = User-Name
         |  len       = 7
         |  value     = 'user1'
         |###[ User-Password ]### 
         |  type      = User-Password
         |  len       = 2
         |  value     = ''


should be:
`class _RadiusAttrHexStringVal(_SpecificRadiusAttr):
     
    __slots__ = ["val"]

    def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None, **fields):  # noqa: E501
        super(_RadiusAttrHexStringVal, self).__init__(
            _pkt,
            post_transform,
            _internal,
            _underlayer,
           **fields
        )`

result:

> ###[ RADIUS ]### 
        code      = Access-Request
        id        = 63
        len       = 45
        authenticator= 42435543394f45375757584538474931
        \attributes\
         |###[ User-Name ]### 
         |  type      = User-Name
         |  len       = 7
         |  value     = 'user1'
         |###[ User-Password ]### 
         |  type      = User-Password
         |  len       = 18
         |  value     = 94fd7b6aecb8aa94a32e1a62cc0f4aa0




Discussion:
Thnaks for sharing. Could you provide an example that triggers the issue?
one of the first things, when connecting a radius server, is sending your own credentials to the server i.e. user_name and password.

Currently scapy does not support these Attr. by classes. Maybe you wanna add the classes as proposed later?

According to RFC 2865 the password is sent as ciphered data. So it's one possibility to use
class _RadiusAttrHexStringVal() as a base class for RadiusAttr_User_Password()
e.g.

```
> class RadiusAttr_User_Password(_RadiusAttrHexStringVal):
>     """RFC 2865"""
>     val = 2
> 
> class RadiusAttr_User_Name(_SpecificRadiusAttr):
>     """RFC 2865"""
>     val = 1
```

Without taking the ciphering into account for the time being, it's finally just a hexstring.

If you add this class to your radius.py, you can create the packet with:

```
> p=Radius(id=random.randint(1, 255), authenticator='0x10x20x30x40x50x60x70x8',
>                                     attributes=[
>                                                 RadiusAttr_User_Password(value='geheim')])
> 
> p.show2()
```

Now you should see, that the value of the RadiusAttr_User_Password was not taken into
account. Adding the '**fields' as proposed should solve the problem.


Are you able to produce a valid packet with the provided code?
what do you mean with 'valid'? Wireshark understands the packet and the router 
understands the packet as well
I mean that your proposed fix does not seem to work:
```
>>> p=Radius(id=random.randint(1, 255), authenticator='0x10x20x30x40x50x60x70x8', attributes=[RadiusAttr_User_Password(value="geheim")]) ; p.show2()
###[ RADIUS ]### 
  code= Access-Request
  id= 55
  len= 22
  authenticator= 30783130783230783330783430783530
  \attributes\
   |###[ User-Password ]### 
   |  type= User-Password
   |  len= 2
   |  value= b''
```

It might be more efficient to submit a working PR =)
@guedou I guess what @lidllohntsich was trying to show was something similar to this:
```Python
from scapy.layers.radius import *
import random
p=Radius(id=random.randint(1, 255), authenticator='0x10x20x30x40x50x60x70x8', attributes=[RadiusAttr_User_Password(value="geheim")])
p.show2()
```
With the output:
```Python
###[ RADIUS ]### 
  code      = Access-Request
  id        = 203
  len       = 28
  authenticator= 30783130783230783330783430783530
  \attributes\
   |###[ User-Password ]### 
   |  type      = User-Password
   |  len       = 8
   |  value     = 67656865696d

```
It was a little confusing to understand what was going on, but I think @lidllohntsich forgot to mention that it's necessary to include `**fields` argument on **both** `_SpecificRadiusAttr` and `_RadiusAttrHexStringVal` classes.
It appears that `**fields` is referenced in both classes constructors, but is not passed to the `super(...).__init__(...)` method, which in turn incorrectly initializes their base `_SpecificRadiusAttr` class.

Resolving patch:
diff --git a/scapy/layers/radius.py b/scapy/layers/radius.py
--- a/scapy/layers/radius.py
+++ b/scapy/layers/radius.py
@@ -294,7 +294,8 @@ def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None,
             _pkt,
             post_transform,
             _internal,
-            _underlayer
+            _underlayer,
+            **fields
         )
         self.fields["type"] = self.val
         name_parts = self.__class__.__name__.split('RadiusAttr_')
@@ -321,6 +322,11 @@ class _RadiusAttrIntValue(_SpecificRadiusAttr):
     ]
 
 
+class RadiusAttr_User_Name(_SpecificRadiusAttr):
+    """RFC 2865"""
+    val = 1
+
+
 class RadiusAttr_NAS_Port(_RadiusAttrIntValue):
     """RFC 2865"""
     val = 5
@@ -503,7 +509,8 @@ def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None,
             _pkt,
             post_transform,
             _internal,
-            _underlayer
+            _underlayer,
+            **fields
         )
         self.fields["type"] = self.val
         name_parts = self.__class__.__name__.split('RadiusAttr_')
@@ -526,6 +533,11 @@ def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None,
     ]
 
 
+class RadiusAttr_User_Password(_RadiusAttrHexStringVal):
+    """RFC 2865"""
+    val = 2
+
+
 class RadiusAttr_State(_RadiusAttrHexStringVal):
     """RFC 2865"""
     val = 24


Test patch:
diff --git a/test/regression.uts b/test/regression.uts
--- a/test/regression.uts
+++ b/test/regression.uts
@@ -10596,7 +10596,7 @@ assert(radius_packet.len == 279)
 assert(radius_packet.authenticator == b'>k\xd4\xc4\x19V\x0b*1\x99\xc8D\xea\xc2\x94Z')
 assert(len(radius_packet.attributes) == 17)
 assert(radius_packet.attributes[0].type == 1)
-assert(type(radius_packet.attributes[0]) == RadiusAttribute)
+assert(type(radius_packet.attributes[0]) == RadiusAttr_User_Name)
 assert(radius_packet.attributes[0].len == 6)
 assert(radius_packet.attributes[0].value == b"leap")
 assert(radius_packet.attributes[1].type == 6)
@@ -10675,7 +10675,7 @@ assert(radius_packet.len == 324)
 assert(radius_packet.authenticator == b'C\xbe!J\x08\xdf\xcf\x9f\x00v~,\xfb\x8e`\xc8')
 assert(len(radius_packet.attributes) == 18)
 assert(radius_packet.attributes[0].type == 1)
-assert(type(radius_packet.attributes[0]) == RadiusAttribute)
+assert(type(radius_packet.attributes[0]) == RadiusAttr_User_Name)
 assert(radius_packet.attributes[0].len == 6)
 assert(radius_packet.attributes[0].value == b"leap")
 assert(radius_packet.attributes[1].type == 6)
@@ -10781,6 +10781,11 @@ r = raw(RadiusAttr_Framed_IP_Address())
 p = RadiusAttr_Framed_IP_Address(p)
 assert p.type == 8
 
+= RadiusAttr_User_Password
+
+r = b'\x01\x00\x00\x1c0x10x20x30x40x50\x02\x08geheim'
+p = Radius(r)
+assert isinstance(p.attributes[0], RadiusAttr_User_Password)
 
 ############
 ############