1161 lines
30 KiB
Python
1161 lines
30 KiB
Python
|
# This file is part of Scapy
|
||
|
# See http://www.secdev.org/projects/scapy for more information
|
||
|
# Copyright (C) Philippe Biondi <phil@secdev.org>
|
||
|
# Vincent Mauge <vmauge.nospam@nospam.gmail.com>
|
||
|
# This program is published under a GPLv2 license
|
||
|
|
||
|
"""
|
||
|
RADIUS (Remote Authentication Dial In User Service)
|
||
|
"""
|
||
|
|
||
|
import struct
|
||
|
import hashlib
|
||
|
import hmac
|
||
|
from scapy.compat import orb, raw
|
||
|
from scapy.packet import Packet, Padding, bind_layers, bind_bottom_up
|
||
|
from scapy.fields import ByteField, ByteEnumField, IntField, StrLenField,\
|
||
|
XStrLenField, XStrFixedLenField, FieldLenField, PacketLenField,\
|
||
|
PacketListField, IPField, MultiEnumField
|
||
|
from scapy.layers.inet import UDP
|
||
|
from scapy.layers.eap import EAP
|
||
|
from scapy.config import conf
|
||
|
from scapy.error import Scapy_Exception
|
||
|
|
||
|
|
||
|
# https://www.iana.org/assignments/radius-types/radius-types.xhtml
|
||
|
_radius_attribute_types = {
|
||
|
1: "User-Name",
|
||
|
2: "User-Password",
|
||
|
3: "CHAP-Password",
|
||
|
4: "NAS-IP-Address",
|
||
|
5: "NAS-Port",
|
||
|
6: "Service-Type",
|
||
|
7: "Framed-Protocol",
|
||
|
8: "Framed-IP-Address",
|
||
|
9: "Framed-IP-Netmask",
|
||
|
10: "Framed-Routing",
|
||
|
11: "Filter-Id",
|
||
|
12: "Framed-MTU",
|
||
|
13: "Framed-Compression",
|
||
|
14: "Login-IP-Host",
|
||
|
15: "Login-Service",
|
||
|
16: "Login-TCP-Port",
|
||
|
17: "Unassigned",
|
||
|
18: "Reply-Message",
|
||
|
19: "Callback-Number",
|
||
|
20: "Callback-Id",
|
||
|
21: "Unassigned",
|
||
|
22: "Framed-Route",
|
||
|
23: "Framed-IPX-Network",
|
||
|
24: "State",
|
||
|
25: "Class",
|
||
|
26: "Vendor-Specific",
|
||
|
27: "Session-Timeout",
|
||
|
28: "Idle-Timeout",
|
||
|
29: "Termination-Action",
|
||
|
30: "Called-Station-Id",
|
||
|
31: "Calling-Station-Id",
|
||
|
32: "NAS-Identifier",
|
||
|
33: "Proxy-State",
|
||
|
34: "Login-LAT-Service",
|
||
|
35: "Login-LAT-Node",
|
||
|
36: "Login-LAT-Group",
|
||
|
37: "Framed-AppleTalk-Link",
|
||
|
38: "Framed-AppleTalk-Network",
|
||
|
39: "Framed-AppleTalk-Zone",
|
||
|
40: "Acct-Status-Type",
|
||
|
41: "Acct-Delay-Time",
|
||
|
42: "Acct-Input-Octets",
|
||
|
43: "Acct-Output-Octets",
|
||
|
44: "Acct-Session-Id",
|
||
|
45: "Acct-Authentic",
|
||
|
46: "Acct-Session-Time",
|
||
|
47: "Acct-Input-Packets",
|
||
|
48: "Acct-Output-Packets",
|
||
|
49: "Acct-Terminate-Cause",
|
||
|
50: "Acct-Multi-Session-Id",
|
||
|
51: "Acct-Link-Count",
|
||
|
52: "Acct-Input-Gigawords",
|
||
|
53: "Acct-Output-Gigawords",
|
||
|
54: "Unassigned",
|
||
|
55: "Event-Timestamp",
|
||
|
56: "Egress-VLANID",
|
||
|
57: "Ingress-Filters",
|
||
|
58: "Egress-VLAN-Name",
|
||
|
59: "User-Priority-Table",
|
||
|
60: "CHAP-Challenge",
|
||
|
61: "NAS-Port-Type",
|
||
|
62: "Port-Limit",
|
||
|
63: "Login-LAT-Port",
|
||
|
64: "Tunnel-Type",
|
||
|
65: "Tunnel-Medium-Type",
|
||
|
66: "Tunnel-Client-Endpoint",
|
||
|
67: "Tunnel-Server-Endpoint",
|
||
|
68: "Acct-Tunnel-Connection",
|
||
|
69: "Tunnel-Password",
|
||
|
70: "ARAP-Password",
|
||
|
71: "ARAP-Features",
|
||
|
72: "ARAP-Zone-Access",
|
||
|
73: "ARAP-Security",
|
||
|
74: "ARAP-Security-Data",
|
||
|
75: "Password-Retry",
|
||
|
76: "Prompt",
|
||
|
77: "Connect-Info",
|
||
|
78: "Configuration-Token",
|
||
|
79: "EAP-Message",
|
||
|
80: "Message-Authenticator",
|
||
|
81: "Tunnel-Private-Group-ID",
|
||
|
82: "Tunnel-Assignment-ID",
|
||
|
83: "Tunnel-Preference",
|
||
|
84: "ARAP-Challenge-Response",
|
||
|
85: "Acct-Interim-Interval",
|
||
|
86: "Acct-Tunnel-Packets-Lost",
|
||
|
87: "NAS-Port-Id",
|
||
|
88: "Framed-Pool",
|
||
|
89: "CUI",
|
||
|
90: "Tunnel-Client-Auth-ID",
|
||
|
91: "Tunnel-Server-Auth-ID",
|
||
|
92: "NAS-Filter-Rule",
|
||
|
93: "Unassigned",
|
||
|
94: "Originating-Line-Info",
|
||
|
95: "NAS-IPv6-Address",
|
||
|
96: "Framed-Interface-Id",
|
||
|
97: "Framed-IPv6-Prefix",
|
||
|
98: "Login-IPv6-Host",
|
||
|
99: "Framed-IPv6-Route",
|
||
|
100: "Framed-IPv6-Pool",
|
||
|
101: "Error-Cause",
|
||
|
102: "EAP-Key-Name",
|
||
|
103: "Digest-Response",
|
||
|
104: "Digest-Realm",
|
||
|
105: "Digest-Nonce",
|
||
|
106: "Digest-Response-Auth",
|
||
|
107: "Digest-Nextnonce",
|
||
|
108: "Digest-Method",
|
||
|
109: "Digest-URI",
|
||
|
110: "Digest-Qop",
|
||
|
111: "Digest-Algorithm",
|
||
|
112: "Digest-Entity-Body-Hash",
|
||
|
113: "Digest-CNonce",
|
||
|
114: "Digest-Nonce-Count",
|
||
|
115: "Digest-Username",
|
||
|
116: "Digest-Opaque",
|
||
|
117: "Digest-Auth-Param",
|
||
|
118: "Digest-AKA-Auts",
|
||
|
119: "Digest-Domain",
|
||
|
120: "Digest-Stale",
|
||
|
121: "Digest-HA1",
|
||
|
122: "SIP-AOR",
|
||
|
123: "Delegated-IPv6-Prefix",
|
||
|
124: "MIP6-Feature-Vector",
|
||
|
125: "MIP6-Home-Link-Prefix",
|
||
|
126: "Operator-Name",
|
||
|
127: "Location-Information",
|
||
|
128: "Location-Data",
|
||
|
129: "Basic-Location-Policy-Rules",
|
||
|
130: "Extended-Location-Policy-Rules",
|
||
|
131: "Location-Capable",
|
||
|
132: "Requested-Location-Info",
|
||
|
133: "Framed-Management-Protocol",
|
||
|
134: "Management-Transport-Protection",
|
||
|
135: "Management-Policy-Id",
|
||
|
136: "Management-Privilege-Level",
|
||
|
137: "PKM-SS-Cert",
|
||
|
138: "PKM-CA-Cert",
|
||
|
139: "PKM-Config-Settings",
|
||
|
140: "PKM-Cryptosuite-List",
|
||
|
141: "PKM-SAID",
|
||
|
142: "PKM-SA-Descriptor",
|
||
|
143: "PKM-Auth-Key",
|
||
|
144: "DS-Lite-Tunnel-Name",
|
||
|
145: "Mobile-Node-Identifier",
|
||
|
146: "Service-Selection",
|
||
|
147: "PMIP6-Home-LMA-IPv6-Address",
|
||
|
148: "PMIP6-Visited-LMA-IPv6-Address",
|
||
|
149: "PMIP6-Home-LMA-IPv4-Address",
|
||
|
150: "PMIP6-Visited-LMA-IPv4-Address",
|
||
|
151: "PMIP6-Home-HN-Prefix",
|
||
|
152: "PMIP6-Visited-HN-Prefix",
|
||
|
153: "PMIP6-Home-Interface-ID",
|
||
|
154: "PMIP6-Visited-Interface-ID",
|
||
|
155: "PMIP6-Home-IPv4-HoA",
|
||
|
156: "PMIP6-Visited-IPv4-HoA",
|
||
|
157: "PMIP6-Home-DHCP4-Server-Address",
|
||
|
158: "PMIP6-Visited-DHCP4-Server-Address",
|
||
|
159: "PMIP6-Home-DHCP6-Server-Address",
|
||
|
160: "PMIP6-Visited-DHCP6-Server-Address",
|
||
|
161: "PMIP6-Home-IPv4-Gateway",
|
||
|
162: "PMIP6-Visited-IPv4-Gateway",
|
||
|
163: "EAP-Lower-Layer",
|
||
|
164: "GSS-Acceptor-Service-Name",
|
||
|
165: "GSS-Acceptor-Host-Name",
|
||
|
166: "GSS-Acceptor-Service-Specifics",
|
||
|
167: "GSS-Acceptor-Realm-Name",
|
||
|
168: "Framed-IPv6-Address",
|
||
|
169: "DNS-Server-IPv6-Address",
|
||
|
170: "Route-IPv6-Information",
|
||
|
171: "Delegated-IPv6-Prefix-Pool",
|
||
|
172: "Stateful-IPv6-Address-Pool",
|
||
|
173: "IPv6-6rd-Configuration",
|
||
|
174: "Allowed-Called-Station-Id",
|
||
|
175: "EAP-Peer-Id",
|
||
|
176: "EAP-Server-Id",
|
||
|
177: "Mobility-Domain-Id",
|
||
|
178: "Preauth-Timeout",
|
||
|
179: "Network-Id-Name",
|
||
|
180: "EAPoL-Announcement",
|
||
|
181: "WLAN-HESSID",
|
||
|
182: "WLAN-Venue-Info",
|
||
|
183: "WLAN-Venue-Language",
|
||
|
184: "WLAN-Venue-Name",
|
||
|
185: "WLAN-Reason-Code",
|
||
|
186: "WLAN-Pairwise-Cipher",
|
||
|
187: "WLAN-Group-Cipher",
|
||
|
188: "WLAN-AKM-Suite",
|
||
|
189: "WLAN-Group-Mgmt-Cipher",
|
||
|
190: "WLAN-RF-Band",
|
||
|
191: "Unassigned",
|
||
|
}
|
||
|
|
||
|
|
||
|
class RadiusAttribute(Packet):
|
||
|
"""
|
||
|
Implements a RADIUS attribute (RFC 2865). Every specific RADIUS attribute
|
||
|
class should inherit from this one.
|
||
|
"""
|
||
|
|
||
|
name = "Radius Attribute"
|
||
|
fields_desc = [
|
||
|
ByteEnumField("type", 1, _radius_attribute_types),
|
||
|
FieldLenField("len", None, "value", "B",
|
||
|
adjust=lambda pkt, x: len(pkt.value) + 2),
|
||
|
StrLenField("value", "", length_from=lambda pkt: pkt.len - 2)
|
||
|
]
|
||
|
|
||
|
registered_attributes = {}
|
||
|
|
||
|
@classmethod
|
||
|
def register_variant(cls):
|
||
|
"""
|
||
|
Registers the RADIUS attributes defined in this module.
|
||
|
"""
|
||
|
|
||
|
if hasattr(cls, "val"):
|
||
|
cls.registered_attributes[cls.val] = cls
|
||
|
else:
|
||
|
cls.registered_attributes[cls.type.default] = cls
|
||
|
|
||
|
@classmethod
|
||
|
def dispatch_hook(cls, _pkt=None, *args, **kargs):
|
||
|
"""
|
||
|
Returns the right RadiusAttribute class for the given data.
|
||
|
"""
|
||
|
|
||
|
if _pkt:
|
||
|
attr_type = orb(_pkt[0])
|
||
|
return cls.registered_attributes.get(attr_type, cls)
|
||
|
return cls
|
||
|
|
||
|
def post_build(self, p, pay):
|
||
|
length = self.len
|
||
|
if length is None:
|
||
|
length = len(p)
|
||
|
p = p[:1] + struct.pack("!B", length) + p[2:]
|
||
|
return p
|
||
|
|
||
|
def guess_payload_class(self, _):
|
||
|
return Padding
|
||
|
|
||
|
|
||
|
class _SpecificRadiusAttr(RadiusAttribute):
|
||
|
"""
|
||
|
Class from which every "specific" RADIUS attribute defined in this module
|
||
|
inherits.
|
||
|
"""
|
||
|
|
||
|
__slots__ = ["val"]
|
||
|
match_subclass = True
|
||
|
|
||
|
def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None, **fields): # noqa: E501
|
||
|
super(_SpecificRadiusAttr, self).__init__(
|
||
|
_pkt,
|
||
|
post_transform,
|
||
|
_internal,
|
||
|
_underlayer,
|
||
|
**fields
|
||
|
)
|
||
|
self.fields["type"] = self.val
|
||
|
name_parts = self.__class__.__name__.split('RadiusAttr_')
|
||
|
if len(name_parts) < 2:
|
||
|
raise Scapy_Exception(
|
||
|
"Invalid class name: {}".format(self.__class__.__name__)
|
||
|
)
|
||
|
self.name = name_parts[1].replace('_', '-')
|
||
|
|
||
|
|
||
|
#
|
||
|
# RADIUS attributes which values are 4 bytes integers
|
||
|
#
|
||
|
|
||
|
class _RadiusAttrIntValue(_SpecificRadiusAttr):
|
||
|
"""
|
||
|
Implements a RADIUS attribute which value field is 4 bytes long integer.
|
||
|
"""
|
||
|
|
||
|
fields_desc = [
|
||
|
ByteEnumField("type", 5, _radius_attribute_types),
|
||
|
ByteField("len", 6),
|
||
|
IntField("value", 0)
|
||
|
]
|
||
|
|
||
|
|
||
|
class RadiusAttr_User_Name(_SpecificRadiusAttr):
|
||
|
"""RFC 2865"""
|
||
|
val = 1
|
||
|
|
||
|
|
||
|
class RadiusAttr_NAS_Port(_RadiusAttrIntValue):
|
||
|
"""RFC 2865"""
|
||
|
val = 5
|
||
|
|
||
|
|
||
|
class RadiusAttr_Framed_MTU(_RadiusAttrIntValue):
|
||
|
"""RFC 2865"""
|
||
|
val = 12
|
||
|
|
||
|
|
||
|
class RadiusAttr_Login_TCP_Port(_RadiusAttrIntValue):
|
||
|
"""RFC 2865"""
|
||
|
val = 16
|
||
|
|
||
|
|
||
|
class RadiusAttr_Session_Timeout(_RadiusAttrIntValue):
|
||
|
"""RFC 2865"""
|
||
|
val = 27
|
||
|
|
||
|
|
||
|
class RadiusAttr_Idle_Timeout(_RadiusAttrIntValue):
|
||
|
"""RFC 2865"""
|
||
|
val = 28
|
||
|
|
||
|
|
||
|
class RadiusAttr_Framed_AppleTalk_Link(_RadiusAttrIntValue):
|
||
|
"""RFC 2865"""
|
||
|
val = 37
|
||
|
|
||
|
|
||
|
class RadiusAttr_Framed_AppleTalk_Network(_RadiusAttrIntValue):
|
||
|
"""RFC 2865"""
|
||
|
val = 38
|
||
|
|
||
|
|
||
|
class RadiusAttr_Acct_Delay_Time(_RadiusAttrIntValue):
|
||
|
"""RFC 2866"""
|
||
|
val = 41
|
||
|
|
||
|
|
||
|
class RadiusAttr_Acct_Input_Octets(_RadiusAttrIntValue):
|
||
|
"""RFC 2866"""
|
||
|
val = 42
|
||
|
|
||
|
|
||
|
class RadiusAttr_Acct_Output_Octets(_RadiusAttrIntValue):
|
||
|
"""RFC 2866"""
|
||
|
val = 43
|
||
|
|
||
|
|
||
|
class RadiusAttr_Acct_Session_Time(_RadiusAttrIntValue):
|
||
|
"""RFC 2866"""
|
||
|
val = 46
|
||
|
|
||
|
|
||
|
class RadiusAttr_Acct_Input_Packets(_RadiusAttrIntValue):
|
||
|
"""RFC 2866"""
|
||
|
val = 47
|
||
|
|
||
|
|
||
|
class RadiusAttr_Acct_Output_Packets(_RadiusAttrIntValue):
|
||
|
"""RFC 2866"""
|
||
|
val = 48
|
||
|
|
||
|
|
||
|
class RadiusAttr_Acct_Link_Count(_RadiusAttrIntValue):
|
||
|
"""RFC 2866"""
|
||
|
val = 51
|
||
|
|
||
|
|
||
|
class RadiusAttr_Acct_Input_Gigawords(_RadiusAttrIntValue):
|
||
|
"""RFC 2869"""
|
||
|
val = 52
|
||
|
|
||
|
|
||
|
class RadiusAttr_Acct_Output_Gigawords(_RadiusAttrIntValue):
|
||
|
"""RFC 2869"""
|
||
|
val = 53
|
||
|
|
||
|
|
||
|
class RadiusAttr_Egress_VLANID(_RadiusAttrIntValue):
|
||
|
"""RFC 4675"""
|
||
|
val = 56
|
||
|
|
||
|
|
||
|
class RadiusAttr_Port_Limit(_RadiusAttrIntValue):
|
||
|
"""RFC 2865"""
|
||
|
val = 62
|
||
|
|
||
|
|
||
|
class RadiusAttr_ARAP_Security(_RadiusAttrIntValue):
|
||
|
"""RFC 2869"""
|
||
|
val = 73
|
||
|
|
||
|
|
||
|
class RadiusAttr_Password_Retry(_RadiusAttrIntValue):
|
||
|
"""RFC 2869"""
|
||
|
val = 75
|
||
|
|
||
|
|
||
|
class RadiusAttr_Tunnel_Preference(_RadiusAttrIntValue):
|
||
|
"""RFC 2868"""
|
||
|
val = 83
|
||
|
|
||
|
|
||
|
class RadiusAttr_Acct_Interim_Interval(_RadiusAttrIntValue):
|
||
|
"""RFC 2869"""
|
||
|
val = 85
|
||
|
|
||
|
|
||
|
class RadiusAttr_Acct_Tunnel_Packets_Lost(_RadiusAttrIntValue):
|
||
|
"""RFC 2867"""
|
||
|
val = 86
|
||
|
|
||
|
|
||
|
class RadiusAttr_Management_Privilege_Level(_RadiusAttrIntValue):
|
||
|
"""RFC 5607"""
|
||
|
val = 136
|
||
|
|
||
|
|
||
|
class RadiusAttr_Mobility_Domain_Id(_RadiusAttrIntValue):
|
||
|
"""RFC 7268"""
|
||
|
val = 177
|
||
|
|
||
|
|
||
|
class RadiusAttr_Preauth_Timeout(_RadiusAttrIntValue):
|
||
|
"""RFC 7268"""
|
||
|
val = 178
|
||
|
|
||
|
|
||
|
class RadiusAttr_WLAN_Venue_Info(_RadiusAttrIntValue):
|
||
|
"""RFC 7268"""
|
||
|
val = 182
|
||
|
|
||
|
|
||
|
class RadiusAttr_WLAN_Reason_Code(_RadiusAttrIntValue):
|
||
|
"""RFC 7268"""
|
||
|
val = 185
|
||
|
|
||
|
|
||
|
class RadiusAttr_WLAN_Pairwise_Cipher(_RadiusAttrIntValue):
|
||
|
"""RFC 7268"""
|
||
|
val = 186
|
||
|
|
||
|
|
||
|
class RadiusAttr_WLAN_Group_Cipher(_RadiusAttrIntValue):
|
||
|
"""RFC 7268"""
|
||
|
val = 187
|
||
|
|
||
|
|
||
|
class RadiusAttr_WLAN_AKM_Suite(_RadiusAttrIntValue):
|
||
|
"""RFC 7268"""
|
||
|
val = 188
|
||
|
|
||
|
|
||
|
class RadiusAttr_WLAN_Group_Mgmt_Cipher(_RadiusAttrIntValue):
|
||
|
"""RFC 7268"""
|
||
|
val = 189
|
||
|
|
||
|
|
||
|
class RadiusAttr_WLAN_RF_Band(_RadiusAttrIntValue):
|
||
|
"""RFC 7268"""
|
||
|
val = 190
|
||
|
|
||
|
|
||
|
#
|
||
|
# RADIUS attributes which values are string (displayed as hex)
|
||
|
#
|
||
|
|
||
|
class _RadiusAttrHexStringVal(_SpecificRadiusAttr):
|
||
|
"""
|
||
|
Implements a RADIUS attribute which value field is a string that will be
|
||
|
as a hex string.
|
||
|
"""
|
||
|
|
||
|
__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
|
||
|
)
|
||
|
self.fields["type"] = self.val
|
||
|
name_parts = self.__class__.__name__.split('RadiusAttr_')
|
||
|
if len(name_parts) < 2:
|
||
|
raise Scapy_Exception(
|
||
|
"Invalid class name: {}".format(self.__class__.__name__)
|
||
|
)
|
||
|
self.name = name_parts[1].replace('_', '-')
|
||
|
|
||
|
fields_desc = [
|
||
|
ByteEnumField("type", 24, _radius_attribute_types),
|
||
|
FieldLenField(
|
||
|
"len",
|
||
|
None,
|
||
|
"value",
|
||
|
"B",
|
||
|
adjust=lambda p, x: len(p.value) + 2
|
||
|
),
|
||
|
XStrLenField("value", "", length_from=lambda p: p.len - 2 if p.len else 0) # noqa: E501
|
||
|
]
|
||
|
|
||
|
|
||
|
class RadiusAttr_User_Password(_RadiusAttrHexStringVal):
|
||
|
"""RFC 2865"""
|
||
|
val = 2
|
||
|
|
||
|
|
||
|
class RadiusAttr_State(_RadiusAttrHexStringVal):
|
||
|
"""RFC 2865"""
|
||
|
val = 24
|
||
|
|
||
|
|
||
|
def prepare_packed_data(radius_packet, packed_req_authenticator):
|
||
|
"""
|
||
|
Pack RADIUS data prior computing the authentication MAC
|
||
|
"""
|
||
|
|
||
|
packed_hdr = struct.pack("!B", radius_packet.code)
|
||
|
packed_hdr += struct.pack("!B", radius_packet.id)
|
||
|
packed_hdr += struct.pack("!H", radius_packet.len)
|
||
|
|
||
|
packed_attrs = b''
|
||
|
for attr in radius_packet.attributes:
|
||
|
packed_attrs += raw(attr)
|
||
|
|
||
|
return packed_hdr + packed_req_authenticator + packed_attrs
|
||
|
|
||
|
|
||
|
class RadiusAttr_Message_Authenticator(_RadiusAttrHexStringVal):
|
||
|
"""RFC 2869"""
|
||
|
val = 80
|
||
|
|
||
|
fields_desc = [
|
||
|
ByteEnumField("type", 24, _radius_attribute_types),
|
||
|
FieldLenField(
|
||
|
"len",
|
||
|
18,
|
||
|
"value",
|
||
|
"B",
|
||
|
),
|
||
|
XStrFixedLenField("value", "\x00" * 16, length=16)
|
||
|
]
|
||
|
|
||
|
@staticmethod
|
||
|
def compute_message_authenticator(radius_packet, packed_req_authenticator,
|
||
|
shared_secret):
|
||
|
"""
|
||
|
Computes the "Message-Authenticator" of a given RADIUS packet.
|
||
|
(RFC 2869 - Page 33)
|
||
|
"""
|
||
|
|
||
|
attr = radius_packet[RadiusAttr_Message_Authenticator]
|
||
|
attr.value = bytearray(attr.len - 2)
|
||
|
data = prepare_packed_data(radius_packet, packed_req_authenticator)
|
||
|
radius_hmac = hmac.new(shared_secret, data, hashlib.md5)
|
||
|
|
||
|
return radius_hmac.digest()
|
||
|
|
||
|
#
|
||
|
# RADIUS attributes which values are IPv4 prefixes
|
||
|
#
|
||
|
|
||
|
|
||
|
class _RadiusAttrIPv4AddrVal(_SpecificRadiusAttr):
|
||
|
"""
|
||
|
Implements a RADIUS attribute which value field is an IPv4 address.
|
||
|
"""
|
||
|
|
||
|
__slots__ = ["val"]
|
||
|
|
||
|
fields_desc = [
|
||
|
ByteEnumField("type", 4, _radius_attribute_types),
|
||
|
ByteField("len", 6),
|
||
|
IPField("value", "0.0.0.0")
|
||
|
]
|
||
|
|
||
|
|
||
|
class RadiusAttr_NAS_IP_Address(_RadiusAttrIPv4AddrVal):
|
||
|
"""RFC 2865"""
|
||
|
val = 4
|
||
|
|
||
|
|
||
|
class RadiusAttr_Framed_IP_Address(_RadiusAttrIPv4AddrVal):
|
||
|
"""RFC 2865"""
|
||
|
val = 8
|
||
|
|
||
|
|
||
|
class RadiusAttr_Framed_IP_Netmask(_RadiusAttrIPv4AddrVal):
|
||
|
"""RFC 2865"""
|
||
|
val = 9
|
||
|
|
||
|
|
||
|
class RadiusAttr_Login_IP_Host(_RadiusAttrIPv4AddrVal):
|
||
|
"""RFC 2865"""
|
||
|
val = 14
|
||
|
|
||
|
|
||
|
class RadiusAttr_Framed_IPX_Network(_RadiusAttrIPv4AddrVal):
|
||
|
"""RFC 2865"""
|
||
|
val = 23
|
||
|
|
||
|
|
||
|
class RadiusAttr_PMIP6_Home_LMA_IPv4_Address(_RadiusAttrIPv4AddrVal):
|
||
|
"""RFC 6572"""
|
||
|
val = 149
|
||
|
|
||
|
|
||
|
class RadiusAttr_PMIP6_Visited_LMA_IPv4_Address(_RadiusAttrIPv4AddrVal):
|
||
|
"""RFC 6572"""
|
||
|
val = 150
|
||
|
|
||
|
|
||
|
class RadiusAttr_PMIP6_Home_DHCP4_Server_Address(_RadiusAttrIPv4AddrVal):
|
||
|
"""RFC 6572"""
|
||
|
val = 157
|
||
|
|
||
|
|
||
|
class RadiusAttr_PMIP6_Visited_DHCP4_Server_Address(_RadiusAttrIPv4AddrVal):
|
||
|
"""RFC 6572"""
|
||
|
val = 158
|
||
|
|
||
|
|
||
|
class RadiusAttr_PMIP6_Home_IPv4_Gateway(_RadiusAttrIPv4AddrVal):
|
||
|
"""RFC 6572"""
|
||
|
val = 161
|
||
|
|
||
|
|
||
|
class RadiusAttr_PMIP6_Visited_IPv4_Gateway(_RadiusAttrIPv4AddrVal):
|
||
|
"""RFC 6572"""
|
||
|
val = 162
|
||
|
|
||
|
|
||
|
# See IANA registry "RADIUS Types"
|
||
|
_radius_attrs_values = {
|
||
|
# Service-Type
|
||
|
6:
|
||
|
{
|
||
|
1: "Login",
|
||
|
2: "Framed",
|
||
|
3: "Callback Login",
|
||
|
4: "Callback Framed",
|
||
|
5: "Outbound",
|
||
|
6: "Administrative",
|
||
|
7: "NAS Prompt",
|
||
|
8: "Authenticate Only",
|
||
|
9: "Callback NAS Prompt",
|
||
|
10: "Call Check",
|
||
|
11: "Callback Administrative",
|
||
|
12: "Voice",
|
||
|
13: "Fax",
|
||
|
14: "Modem Relay",
|
||
|
15: "IAPP-Register",
|
||
|
16: "IAPP-AP-Check",
|
||
|
17: "Authorize Only",
|
||
|
18: "Framed-Management",
|
||
|
19: "Additional-Authorization"
|
||
|
},
|
||
|
|
||
|
# Framed-Protocol
|
||
|
7:
|
||
|
{
|
||
|
1: "PPP",
|
||
|
2: "SLIP",
|
||
|
3: "AppleTalk Remote Access Protocol (ARAP)",
|
||
|
4: "Gandalf proprietary SingleLink/MultiLink protocol",
|
||
|
5: "Xylogics proprietary IPX/SLIP",
|
||
|
6: "X.75 Synchronous",
|
||
|
7: "GPRS PDP Context"
|
||
|
},
|
||
|
|
||
|
# Framed-Routing
|
||
|
10:
|
||
|
{
|
||
|
0: "None",
|
||
|
1: "Send routing packets",
|
||
|
2: "Listen for routing packets",
|
||
|
3: "Send and Listen"
|
||
|
},
|
||
|
|
||
|
# Framed-Compression
|
||
|
13:
|
||
|
{
|
||
|
0: "None",
|
||
|
1: "VJ TCP/IP header compression",
|
||
|
2: "IPX header compression",
|
||
|
3: "Stac-LZS compression"
|
||
|
},
|
||
|
|
||
|
# Login-Service
|
||
|
15:
|
||
|
{
|
||
|
0: "Telnet",
|
||
|
1: "Rlogin",
|
||
|
2: "TCP Clear",
|
||
|
3: "PortMaster (proprietary)",
|
||
|
4: "LAT",
|
||
|
5: "X25-PAD",
|
||
|
6: "X25-T3POS",
|
||
|
7: "Unassigned",
|
||
|
8: "TCP Clear Quiet (suppresses any NAS-generated connect string)"
|
||
|
},
|
||
|
|
||
|
# Termination-Action
|
||
|
29:
|
||
|
{
|
||
|
0: "Default",
|
||
|
1: "RADIUS-Request"
|
||
|
},
|
||
|
|
||
|
# Acct-Status-Type
|
||
|
40:
|
||
|
{
|
||
|
1: "Start",
|
||
|
2: "Stop",
|
||
|
3: "Interim-Update",
|
||
|
4: "Unassigned",
|
||
|
5: "Unassigned",
|
||
|
6: "Unassigned",
|
||
|
7: "Accounting-On",
|
||
|
8: "Accounting-Off",
|
||
|
9: "Tunnel-Start",
|
||
|
10: "Tunnel-Stop",
|
||
|
11: "Tunnel-Reject",
|
||
|
12: "Tunnel-Link-Start",
|
||
|
13: "Tunnel-Link-Stop",
|
||
|
14: "Tunnel-Link-Reject",
|
||
|
15: "Failed"
|
||
|
},
|
||
|
|
||
|
# Acct-Authentic
|
||
|
45:
|
||
|
{
|
||
|
1: "RADIUS",
|
||
|
2: "Local",
|
||
|
3: "Remote",
|
||
|
4: "Diameter"
|
||
|
},
|
||
|
|
||
|
# Acct-Terminate-Cause
|
||
|
49:
|
||
|
{
|
||
|
1: "User Request",
|
||
|
2: "Lost Carrier",
|
||
|
3: "Lost Service",
|
||
|
4: "Idle Timeout",
|
||
|
5: "Session Timeout",
|
||
|
6: "Admin Reset",
|
||
|
7: "Admin Reboot",
|
||
|
8: "Port Error",
|
||
|
9: "NAS Error",
|
||
|
10: "NAS Request",
|
||
|
11: "NAS Reboot",
|
||
|
12: "Port Unneeded",
|
||
|
13: "Port Preempted",
|
||
|
14: "Port Suspended",
|
||
|
15: "Service Unavailable",
|
||
|
16: "Callback",
|
||
|
17: "User Error",
|
||
|
18: "Host Request",
|
||
|
19: "Supplicant Restart",
|
||
|
20: "Reauthentication Failure",
|
||
|
21: "Port Reinitialized",
|
||
|
22: "Port Administratively Disabled",
|
||
|
23: "Lost Power",
|
||
|
},
|
||
|
|
||
|
# NAS-Port-Type
|
||
|
61:
|
||
|
{
|
||
|
0: "Async",
|
||
|
1: "Sync",
|
||
|
2: "ISDN Sync",
|
||
|
3: "ISDN Async V.120",
|
||
|
4: "ISDN Async V.110",
|
||
|
5: "Virtual",
|
||
|
6: "PIAFS",
|
||
|
7: "HDLC Clear Channel",
|
||
|
8: "X.25",
|
||
|
9: "X.75",
|
||
|
10: "G.3 Fax",
|
||
|
11: "SDSL - Symmetric DSL",
|
||
|
12: "ADSL-CAP - Asymmetric DSL, Carrierless Amplitude Phase Modulation", # noqa: E501
|
||
|
13: "ADSL-DMT - Asymmetric DSL, Discrete Multi-Tone",
|
||
|
14: "IDSL - ISDN Digital Subscriber Line",
|
||
|
15: "Ethernet",
|
||
|
16: "xDSL - Digital Subscriber Line of unknown type",
|
||
|
17: "Cable",
|
||
|
18: "Wireles - Other",
|
||
|
19: "Wireless - IEEE 802.11",
|
||
|
20: "Token-Ring",
|
||
|
21: "FDDI",
|
||
|
22: "Wireless - CDMA2000",
|
||
|
23: "Wireless - UMTS",
|
||
|
24: "Wireless - 1X-EV",
|
||
|
25: "IAPP",
|
||
|
26: "FTTP - Fiber to the Premises",
|
||
|
27: "Wireless - IEEE 802.16",
|
||
|
28: "Wireless - IEEE 802.20",
|
||
|
29: "Wireless - IEEE 802.22",
|
||
|
30: "PPPoA - PPP over ATM",
|
||
|
31: "PPPoEoA - PPP over Ethernet over ATM",
|
||
|
32: "PPPoEoE - PPP over Ethernet over Ethernet",
|
||
|
33: "PPPoEoVLAN - PPP over Ethernet over VLAN",
|
||
|
34: "PPPoEoQinQ - PPP over Ethernet over IEEE 802.1QinQ",
|
||
|
35: "xPON - Passive Optical Network",
|
||
|
36: "Wireless - XGP",
|
||
|
37: "WiMAX Pre-Release 8 IWK Function",
|
||
|
38: "WIMAX-WIFI-IWK: WiMAX WIFI Interworking",
|
||
|
39: "WIMAX-SFF: Signaling Forwarding Function for LTE/3GPP2",
|
||
|
40: "WIMAX-HA-LMA: WiMAX HA and or LMA function",
|
||
|
41: "WIMAX-DHCP: WIMAX DHCP service",
|
||
|
42: "WIMAX-LBS: WiMAX location based service",
|
||
|
43: "WIMAX-WVS: WiMAX voice service"
|
||
|
},
|
||
|
|
||
|
# Tunnel-Type
|
||
|
64:
|
||
|
{
|
||
|
1: "Point-to-Point Tunneling Protocol (PPTP)",
|
||
|
2: "Layer Two Forwarding (L2F)",
|
||
|
3: "Layer Two Tunneling Protocol (L2TP)",
|
||
|
4: "Ascend Tunnel Management Protocol (ATMP)",
|
||
|
5: "Virtual Tunneling Protocol (VTP)",
|
||
|
6: "IP Authentication Header in the Tunnel-mode (AH)",
|
||
|
7: "IP-in-IP Encapsulation (IP-IP)",
|
||
|
8: "Minimal IP-in-IP Encapsulation (MIN-IP-IP)",
|
||
|
9: "IP Encapsulating Security Payload in the Tunnel-mode (ESP)",
|
||
|
10: "Generic Route Encapsulation (GRE)",
|
||
|
11: "Bay Dial Virtual Services (DVS)",
|
||
|
12: "IP-in-IP Tunneling",
|
||
|
13: "Virtual LANs (VLAN)"
|
||
|
},
|
||
|
|
||
|
# Tunnel-Medium-Type
|
||
|
65:
|
||
|
{
|
||
|
1: "IPv4 (IP version 4)",
|
||
|
2: "IPv6 (IP version 6)",
|
||
|
3: "NSAP",
|
||
|
4: "HDLC (8-bit multidrop)",
|
||
|
5: "BBN 1822",
|
||
|
6: "802",
|
||
|
7: "E.163 (POTS)",
|
||
|
8: "E.164 (SMDS, Frame Relay, ATM)",
|
||
|
9: "F.69 (Telex)",
|
||
|
10: "X.121 (X.25, Frame Relay)",
|
||
|
11: "IPX",
|
||
|
12: "Appletalk",
|
||
|
13: "Decnet IV",
|
||
|
14: "Banyan Vine",
|
||
|
15: "E.164 with NSAP format subaddress"
|
||
|
},
|
||
|
|
||
|
# ARAP-Zone-Access
|
||
|
72:
|
||
|
{
|
||
|
1: "Only allow access to default zone",
|
||
|
2: "Use zone filter inclusively",
|
||
|
3: "Not used",
|
||
|
4: "Use zone filter exclusively"
|
||
|
},
|
||
|
|
||
|
# Prompt
|
||
|
76:
|
||
|
{
|
||
|
0: "No Echo",
|
||
|
1: "Echo"
|
||
|
},
|
||
|
|
||
|
# Error-Cause Attribute
|
||
|
101:
|
||
|
{
|
||
|
201: "Residual Session Context Removed",
|
||
|
202: "Invalid EAP Packet (Ignored)",
|
||
|
401: "Unsupported Attribute",
|
||
|
402: "Missing Attribute",
|
||
|
403: "NAS Identification Mismatch",
|
||
|
404: "Invalid Request",
|
||
|
405: "Unsupported Service",
|
||
|
406: "Unsupported Extension",
|
||
|
407: "Invalid Attribute Value",
|
||
|
501: "Administratively Prohibited",
|
||
|
502: "Request Not Routable (Proxy)",
|
||
|
503: "Session Context Not Found",
|
||
|
504: "Session Context Not Removable",
|
||
|
505: "Other Proxy Processing Error",
|
||
|
506: "Resources Unavailable",
|
||
|
507: "Request Initiated",
|
||
|
508: "Multiple Session Selection Unsupported",
|
||
|
509: "Location-Info-Required",
|
||
|
601: "Response Too Big"
|
||
|
},
|
||
|
|
||
|
# Operator Namespace Identifier - Attribute 126
|
||
|
126:
|
||
|
{
|
||
|
0x30: "TADIG",
|
||
|
0x31: "REALM",
|
||
|
0x32: "E212",
|
||
|
0x33: "ICC",
|
||
|
0xFF: "Reserved"
|
||
|
},
|
||
|
|
||
|
# Basic-Location-Policy-Rules
|
||
|
129:
|
||
|
{
|
||
|
0: "Retransmission allowed",
|
||
|
},
|
||
|
|
||
|
# Location-Capable
|
||
|
131:
|
||
|
{
|
||
|
1: "CIVIC_LOCATION",
|
||
|
2: "GEO_LOCATION",
|
||
|
4: "USERS_LOCATION",
|
||
|
8: "NAS_LOCATION"
|
||
|
},
|
||
|
|
||
|
# Framed-Management-Protocol
|
||
|
133:
|
||
|
{
|
||
|
1: "SNMP",
|
||
|
2: "Web-based",
|
||
|
3: "NETCONF",
|
||
|
4: "FTP",
|
||
|
5: "TFTP",
|
||
|
6: "SFTP",
|
||
|
7: "RCP",
|
||
|
8: "SCP"
|
||
|
},
|
||
|
|
||
|
# Management-Transport-Protection
|
||
|
134:
|
||
|
{
|
||
|
1: "No-Protection",
|
||
|
2: "Integrity-Protection",
|
||
|
3: "Integrity-Confidentiality-Protection",
|
||
|
},
|
||
|
}
|
||
|
|
||
|
|
||
|
class _RadiusAttrIntEnumVal(_SpecificRadiusAttr):
|
||
|
"""
|
||
|
Implements a RADIUS attribute which value field is 4 bytes long integer.
|
||
|
"""
|
||
|
|
||
|
__slots__ = ["val"]
|
||
|
|
||
|
fields_desc = [
|
||
|
ByteEnumField("type", 6, _radius_attribute_types),
|
||
|
ByteField("len", 6),
|
||
|
MultiEnumField(
|
||
|
"value",
|
||
|
0,
|
||
|
_radius_attrs_values,
|
||
|
depends_on=lambda p: p.type,
|
||
|
fmt="I"
|
||
|
)
|
||
|
]
|
||
|
|
||
|
|
||
|
class RadiusAttr_Service_Type(_RadiusAttrIntEnumVal):
|
||
|
"""RFC 2865"""
|
||
|
val = 6
|
||
|
|
||
|
|
||
|
class RadiusAttr_Framed_Protocol(_RadiusAttrIntEnumVal):
|
||
|
"""RFC 2865"""
|
||
|
val = 7
|
||
|
|
||
|
|
||
|
class RadiusAttr_NAS_Port_Type(_RadiusAttrIntEnumVal):
|
||
|
"""RFC 2865"""
|
||
|
val = 61
|
||
|
|
||
|
|
||
|
class _EAPPacketField(PacketLenField):
|
||
|
|
||
|
"""
|
||
|
Handles EAP-Message attribute value (the actual EAP packet).
|
||
|
"""
|
||
|
|
||
|
def m2i(self, pkt, m):
|
||
|
ret = None
|
||
|
eap_packet_len = struct.unpack("!H", m[2:4])[0]
|
||
|
if eap_packet_len < 254:
|
||
|
# If the EAP packet has not been fragmented, build a Scapy EAP
|
||
|
# packet from the data.
|
||
|
ret = EAP(m)
|
||
|
else:
|
||
|
ret = conf.raw_layer(m)
|
||
|
return ret
|
||
|
|
||
|
|
||
|
class RadiusAttr_EAP_Message(RadiusAttribute):
|
||
|
"""
|
||
|
Implements the "EAP-Message" attribute (RFC 3579).
|
||
|
"""
|
||
|
|
||
|
name = "EAP-Message"
|
||
|
match_subclass = True
|
||
|
fields_desc = [
|
||
|
ByteEnumField("type", 79, _radius_attribute_types),
|
||
|
FieldLenField(
|
||
|
"len",
|
||
|
None,
|
||
|
"value",
|
||
|
"B",
|
||
|
adjust=lambda pkt, x: len(pkt.value) + 2
|
||
|
),
|
||
|
_EAPPacketField("value", "", EAP, length_from=lambda p: p.len - 2)
|
||
|
]
|
||
|
|
||
|
|
||
|
class RadiusAttr_Vendor_Specific(RadiusAttribute):
|
||
|
"""
|
||
|
Implements the "Vendor-Specific" attribute, as described in RFC 2865.
|
||
|
"""
|
||
|
|
||
|
name = "Vendor-Specific"
|
||
|
match_subclass = True
|
||
|
fields_desc = [
|
||
|
ByteEnumField("type", 26, _radius_attribute_types),
|
||
|
FieldLenField(
|
||
|
"len",
|
||
|
None,
|
||
|
"value",
|
||
|
"B",
|
||
|
adjust=lambda pkt, x: len(pkt.value) + 8
|
||
|
),
|
||
|
IntField("vendor_id", 0),
|
||
|
ByteField("vendor_type", 0),
|
||
|
FieldLenField(
|
||
|
"vendor_len",
|
||
|
None,
|
||
|
"value",
|
||
|
"B",
|
||
|
adjust=lambda p, x: len(p.value) + 2
|
||
|
),
|
||
|
StrLenField("value", "", length_from=lambda p: p.vendor_len - 2)
|
||
|
]
|
||
|
|
||
|
|
||
|
# See IANA RADIUS Packet Type Codes registry
|
||
|
_packet_codes = {
|
||
|
1: "Access-Request",
|
||
|
2: "Access-Accept",
|
||
|
3: "Access-Reject",
|
||
|
4: "Accounting-Request",
|
||
|
5: "Accounting-Response",
|
||
|
6: "Accounting-Status (now Interim Accounting)",
|
||
|
7: "Password-Request",
|
||
|
8: "Password-Ack",
|
||
|
9: "Password-Reject",
|
||
|
10: "Accounting-Message",
|
||
|
11: "Access-Challenge",
|
||
|
12: "Status-Server (experimental)",
|
||
|
13: "Status-Client (experimental)",
|
||
|
21: "Resource-Free-Request",
|
||
|
22: "Resource-Free-Response",
|
||
|
23: "Resource-Query-Request",
|
||
|
24: "Resource-Query-Response",
|
||
|
25: "Alternate-Resource-Reclaim-Request",
|
||
|
26: "NAS-Reboot-Request",
|
||
|
27: "NAS-Reboot-Response",
|
||
|
28: "Reserved",
|
||
|
29: "Next-Passcode",
|
||
|
30: "New-Pin",
|
||
|
31: "Terminate-Session",
|
||
|
32: "Password-Expired",
|
||
|
33: "Event-Request",
|
||
|
34: "Event-Response",
|
||
|
40: "Disconnect-Request",
|
||
|
41: "Disconnect-ACK",
|
||
|
42: "Disconnect-NAK",
|
||
|
43: "CoA-Request",
|
||
|
44: "CoA-ACK",
|
||
|
45: "CoA-NAK",
|
||
|
50: "IP-Address-Allocate",
|
||
|
51: "IP-Address-Release",
|
||
|
52: "Protocol-Error",
|
||
|
250: "Experimental Use",
|
||
|
251: "Experimental Use",
|
||
|
252: "Experimental Use",
|
||
|
253: "Experimental Use",
|
||
|
254: "Reserved",
|
||
|
255: "Reserved"
|
||
|
}
|
||
|
|
||
|
|
||
|
class Radius(Packet):
|
||
|
"""
|
||
|
Implements a RADIUS packet (RFC 2865).
|
||
|
"""
|
||
|
|
||
|
name = "RADIUS"
|
||
|
fields_desc = [
|
||
|
ByteEnumField("code", 1, _packet_codes),
|
||
|
ByteField("id", 0),
|
||
|
FieldLenField(
|
||
|
"len",
|
||
|
None,
|
||
|
"attributes",
|
||
|
"H",
|
||
|
adjust=lambda pkt, x: len(pkt.attributes) + 20
|
||
|
),
|
||
|
XStrFixedLenField("authenticator", "", 16),
|
||
|
PacketListField(
|
||
|
"attributes",
|
||
|
[],
|
||
|
RadiusAttribute,
|
||
|
length_from=lambda pkt: pkt.len - 20
|
||
|
)
|
||
|
]
|
||
|
|
||
|
def compute_authenticator(self, packed_request_auth, shared_secret):
|
||
|
"""
|
||
|
Computes the authenticator field (RFC 2865 - Section 3)
|
||
|
"""
|
||
|
|
||
|
data = prepare_packed_data(self, packed_request_auth)
|
||
|
radius_mac = hashlib.md5(data + shared_secret)
|
||
|
return radius_mac.digest()
|
||
|
|
||
|
def post_build(self, p, pay):
|
||
|
p += pay
|
||
|
length = self.len
|
||
|
if length is None:
|
||
|
length = len(p)
|
||
|
p = p[:2] + struct.pack("!H", length) + p[4:]
|
||
|
return p
|
||
|
|
||
|
|
||
|
bind_bottom_up(UDP, Radius, sport=1812)
|
||
|
bind_bottom_up(UDP, Radius, dport=1812)
|
||
|
bind_bottom_up(UDP, Radius, sport=1813)
|
||
|
bind_bottom_up(UDP, Radius, dport=1813)
|
||
|
bind_bottom_up(UDP, Radius, sport=3799)
|
||
|
bind_bottom_up(UDP, Radius, dport=3799)
|
||
|
bind_layers(UDP, Radius, sport=1812, dport=1812)
|