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##               Vincent Mauge   <vmauge.nospam@nospam.gmail.com>
5## This program is published under a GPLv2 license
6
7"""
8RADIUS (Remote Authentication Dial In User Service)
9"""
10
11import struct
12import logging
13import hashlib
14import hmac
15from scapy.compat import *
16from scapy.packet import Packet, bind_layers
17from scapy.fields import ByteField, ByteEnumField, IntField, StrLenField,\
18    XStrLenField, XStrFixedLenField, FieldLenField, PacketField,\
19    PacketListField, IPField, MultiEnumField
20from scapy.layers.inet import UDP
21from scapy.layers.eap import EAP
22from scapy.config import conf
23from scapy.error import Scapy_Exception
24
25
26# https://www.iana.org/assignments/radius-types/radius-types.xhtml
27_radius_attribute_types = {
28    1: "User-Name",
29    2: "User-Password",
30    3: "CHAP-Password",
31    4: "NAS-IP-Address",
32    5: "NAS-Port",
33    6: "Service-Type",
34    7: "Framed-Protocol",
35    8: "Framed-IP-Address",
36    9: "Framed-IP-Netmask",
37    10: "Framed-Routing",
38    11: "Filter-Id",
39    12: "Framed-MTU",
40    13: "Framed-Compression",
41    14: "Login-IP-Host",
42    15: "Login-Service",
43    16: "Login-TCP-Port",
44    17: "Unassigned",
45    18: "Reply-Message",
46    19: "Callback-Number",
47    20: "Callback-Id",
48    21: "Unassigned",
49    22: "Framed-Route",
50    23: "Framed-IPX-Network",
51    24: "State",
52    25: "Class",
53    26: "Vendor-Specific",
54    27: "Session-Timeout",
55    28: "Idle-Timeout",
56    29: "Termination-Action",
57    30: "Called-Station-Id",
58    31: "Calling-Station-Id",
59    32: "NAS-Identifier",
60    33: "Proxy-State",
61    34: "Login-LAT-Service",
62    35: "Login-LAT-Node",
63    36: "Login-LAT-Group",
64    37: "Framed-AppleTalk-Link",
65    38: "Framed-AppleTalk-Network",
66    39: "Framed-AppleTalk-Zone",
67    40: "Acct-Status-Type",
68    41: "Acct-Delay-Time",
69    42: "Acct-Input-Octets",
70    43: "Acct-Output-Octets",
71    44: "Acct-Session-Id",
72    45: "Acct-Authentic",
73    46: "Acct-Session-Time",
74    47: "Acct-Input-Packets",
75    48: "Acct-Output-Packets",
76    49: "Acct-Terminate-Cause",
77    50: "Acct-Multi-Session-Id",
78    51: "Acct-Link-Count",
79    52: "Acct-Input-Gigawords",
80    53: "Acct-Output-Gigawords",
81    54: "Unassigned",
82    55: "Event-Timestamp",
83    56: "Egress-VLANID",
84    57: "Ingress-Filters",
85    58: "Egress-VLAN-Name",
86    59: "User-Priority-Table",
87    60: "CHAP-Challenge",
88    61: "NAS-Port-Type",
89    62: "Port-Limit",
90    63: "Login-LAT-Port",
91    64: "Tunnel-Type",
92    65: "Tunnel-Medium-Type",
93    66: "Tunnel-Client-Endpoint",
94    67: "Tunnel-Server-Endpoint",
95    68: "Acct-Tunnel-Connection",
96    69: "Tunnel-Password",
97    70: "ARAP-Password",
98    71: "ARAP-Features",
99    72: "ARAP-Zone-Access",
100    73: "ARAP-Security",
101    74: "ARAP-Security-Data",
102    75: "Password-Retry",
103    76: "Prompt",
104    77: "Connect-Info",
105    78: "Configuration-Token",
106    79: "EAP-Message",
107    80: "Message-Authenticator",
108    81: "Tunnel-Private-Group-ID",
109    82: "Tunnel-Assignment-ID",
110    83: "Tunnel-Preference",
111    84: "ARAP-Challenge-Response",
112    85: "Acct-Interim-Interval",
113    86: "Acct-Tunnel-Packets-Lost",
114    87: "NAS-Port-Id",
115    88: "Framed-Pool",
116    89: "CUI",
117    90: "Tunnel-Client-Auth-ID",
118    91: "Tunnel-Server-Auth-ID",
119    92: "NAS-Filter-Rule",
120    93: "Unassigned",
121    94: "Originating-Line-Info",
122    95: "NAS-IPv6-Address",
123    96: "Framed-Interface-Id",
124    97: "Framed-IPv6-Prefix",
125    98: "Login-IPv6-Host",
126    99: "Framed-IPv6-Route",
127    100: "Framed-IPv6-Pool",
128    101: "Error-Cause",
129    102: "EAP-Key-Name",
130    103: "Digest-Response",
131    104: "Digest-Realm",
132    105: "Digest-Nonce",
133    106: "Digest-Response-Auth",
134    107: "Digest-Nextnonce",
135    108: "Digest-Method",
136    109: "Digest-URI",
137    110: "Digest-Qop",
138    111: "Digest-Algorithm",
139    112: "Digest-Entity-Body-Hash",
140    113: "Digest-CNonce",
141    114: "Digest-Nonce-Count",
142    115: "Digest-Username",
143    116: "Digest-Opaque",
144    117: "Digest-Auth-Param",
145    118: "Digest-AKA-Auts",
146    119: "Digest-Domain",
147    120: "Digest-Stale",
148    121: "Digest-HA1",
149    122: "SIP-AOR",
150    123: "Delegated-IPv6-Prefix",
151    124: "MIP6-Feature-Vector",
152    125: "MIP6-Home-Link-Prefix",
153    126: "Operator-Name",
154    127: "Location-Information",
155    128: "Location-Data",
156    129: "Basic-Location-Policy-Rules",
157    130: "Extended-Location-Policy-Rules",
158    131: "Location-Capable",
159    132: "Requested-Location-Info",
160    133: "Framed-Management-Protocol",
161    134: "Management-Transport-Protection",
162    135: "Management-Policy-Id",
163    136: "Management-Privilege-Level",
164    137: "PKM-SS-Cert",
165    138: "PKM-CA-Cert",
166    139: "PKM-Config-Settings",
167    140: "PKM-Cryptosuite-List",
168    141: "PKM-SAID",
169    142: "PKM-SA-Descriptor",
170    143: "PKM-Auth-Key",
171    144: "DS-Lite-Tunnel-Name",
172    145: "Mobile-Node-Identifier",
173    146: "Service-Selection",
174    147: "PMIP6-Home-LMA-IPv6-Address",
175    148: "PMIP6-Visited-LMA-IPv6-Address",
176    149: "PMIP6-Home-LMA-IPv4-Address",
177    150: "PMIP6-Visited-LMA-IPv4-Address",
178    151: "PMIP6-Home-HN-Prefix",
179    152: "PMIP6-Visited-HN-Prefix",
180    153: "PMIP6-Home-Interface-ID",
181    154: "PMIP6-Visited-Interface-ID",
182    155: "PMIP6-Home-IPv4-HoA",
183    156: "PMIP6-Visited-IPv4-HoA",
184    157: "PMIP6-Home-DHCP4-Server-Address",
185    158: "PMIP6-Visited-DHCP4-Server-Address",
186    159: "PMIP6-Home-DHCP6-Server-Address",
187    160: "PMIP6-Visited-DHCP6-Server-Address",
188    161: "PMIP6-Home-IPv4-Gateway",
189    162: "PMIP6-Visited-IPv4-Gateway",
190    163: "EAP-Lower-Layer",
191    164: "GSS-Acceptor-Service-Name",
192    165: "GSS-Acceptor-Host-Name",
193    166: "GSS-Acceptor-Service-Specifics",
194    167: "GSS-Acceptor-Realm-Name",
195    168: "Framed-IPv6-Address",
196    169: "DNS-Server-IPv6-Address",
197    170: "Route-IPv6-Information",
198    171: "Delegated-IPv6-Prefix-Pool",
199    172: "Stateful-IPv6-Address-Pool",
200    173: "IPv6-6rd-Configuration",
201    174: "Allowed-Called-Station-Id",
202    175: "EAP-Peer-Id",
203    176: "EAP-Server-Id",
204    177: "Mobility-Domain-Id",
205    178: "Preauth-Timeout",
206    179: "Network-Id-Name",
207    180: "EAPoL-Announcement",
208    181: "WLAN-HESSID",
209    182: "WLAN-Venue-Info",
210    183: "WLAN-Venue-Language",
211    184: "WLAN-Venue-Name",
212    185: "WLAN-Reason-Code",
213    186: "WLAN-Pairwise-Cipher",
214    187: "WLAN-Group-Cipher",
215    188: "WLAN-AKM-Suite",
216    189: "WLAN-Group-Mgmt-Cipher",
217    190: "WLAN-RF-Band",
218    191: "Unassigned",
219}
220
221
222class RadiusAttribute(Packet):
223    """
224    Implements a RADIUS attribute (RFC 2865). Every specific RADIUS attribute
225    class should inherit from this one.
226    """
227
228    name = "Radius Attribute"
229    fields_desc = [
230        ByteEnumField("type", 1, _radius_attribute_types),
231        FieldLenField("len", None, "value", "B",
232                      adjust=lambda pkt, x: len(pkt.value) + 2),
233        StrLenField("value", "", length_from=lambda pkt: pkt.len - 2)
234    ]
235
236    registered_attributes = {}
237
238    @classmethod
239    def register_variant(cls):
240        """
241        Registers the RADIUS attributes defined in this module.
242        """
243
244        if hasattr(cls, "val"):
245            cls.registered_attributes[cls.val] = cls
246        else:
247            cls.registered_attributes[cls.type.default] = cls
248
249    @classmethod
250    def dispatch_hook(cls, _pkt=None, *args, **kargs):
251        """
252        Returns the right RadiusAttribute class for the given data.
253        """
254
255        if _pkt:
256            attr_type = orb(_pkt[0])
257            return cls.registered_attributes.get(attr_type, cls)
258        return cls
259
260    def haslayer(self, cls):
261        if cls == "RadiusAttribute":
262            if isinstance(self, RadiusAttribute):
263                return True
264        elif issubclass(cls, RadiusAttribute):
265            if isinstance(self, cls):
266                return True
267        return super(RadiusAttribute, self).haslayer(cls)
268
269    def getlayer(self, cls, nb=1, _track=None, _subclass=True, **flt):
270        return super(RadiusAttribute, self).getlayer(cls, nb=nb, _track=_track,
271                                                     _subclass=True, **flt)
272
273    def post_build(self, p, pay):
274        length = self.len
275        if length is None:
276            length = len(p)
277            p = p[:1] + struct.pack("!B", length) + p[2:]
278        return p
279
280
281class _SpecificRadiusAttr(RadiusAttribute):
282    """
283    Class from which every "specific" RADIUS attribute defined in this module
284    inherits.
285    """
286
287    __slots__ = ["val"]
288
289    def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None, **fields):
290        super(_SpecificRadiusAttr, self).__init__(
291            _pkt,
292            post_transform,
293            _internal,
294            _underlayer
295        )
296        self.fields["type"] = self.val
297        name_parts = self.__class__.__name__.split('RadiusAttr_')
298        if len(name_parts) < 2:
299            raise Scapy_Exception(
300                "Invalid class name: {}".format(self.__class__.__name__)
301            )
302        self.name = name_parts[1].replace('_', '-')
303
304
305#
306# RADIUS attributes which values are 4 bytes integers
307#
308
309class _RadiusAttrIntValue(_SpecificRadiusAttr):
310    """
311    Implements a RADIUS attribute which value field is 4 bytes long integer.
312    """
313
314    fields_desc = [
315        ByteEnumField("type", 5, _radius_attribute_types),
316        ByteField("len", 6),
317        IntField("value", 0)
318    ]
319
320
321class RadiusAttr_NAS_Port(_RadiusAttrIntValue):
322    """RFC 2865"""
323    val = 5
324
325
326class RadiusAttr_Framed_MTU(_RadiusAttrIntValue):
327    """RFC 2865"""
328    val = 12
329
330
331class RadiusAttr_Login_TCP_Port(_RadiusAttrIntValue):
332    """RFC 2865"""
333    val = 16
334
335
336class RadiusAttr_Session_Timeout(_RadiusAttrIntValue):
337    """RFC 2865"""
338    val = 27
339
340
341class RadiusAttr_Idle_Timeout(_RadiusAttrIntValue):
342    """RFC 2865"""
343    val = 28
344
345
346class RadiusAttr_Framed_AppleTalk_Link(_RadiusAttrIntValue):
347    """RFC 2865"""
348    val = 37
349
350
351class RadiusAttr_Framed_AppleTalk_Network(_RadiusAttrIntValue):
352    """RFC 2865"""
353    val = 38
354
355
356class RadiusAttr_Acct_Delay_Time(_RadiusAttrIntValue):
357    """RFC 2866"""
358    val = 41
359
360
361class RadiusAttr_Acct_Input_Octets(_RadiusAttrIntValue):
362    """RFC 2866"""
363    val = 42
364
365
366class RadiusAttr_Acct_Output_Octets(_RadiusAttrIntValue):
367    """RFC 2866"""
368    val = 43
369
370
371class RadiusAttr_Acct_Session_Time(_RadiusAttrIntValue):
372    """RFC 2866"""
373    val = 46
374
375
376class RadiusAttr_Acct_Input_Packets(_RadiusAttrIntValue):
377    """RFC 2866"""
378    val = 47
379
380
381class RadiusAttr_Acct_Output_Packets(_RadiusAttrIntValue):
382    """RFC 2866"""
383    val = 48
384
385
386class RadiusAttr_Acct_Link_Count(_RadiusAttrIntValue):
387    """RFC 2866"""
388    val = 51
389
390
391class RadiusAttr_Acct_Input_Gigawords(_RadiusAttrIntValue):
392    """RFC 2869"""
393    val = 52
394
395
396class RadiusAttr_Acct_Output_Gigawords(_RadiusAttrIntValue):
397    """RFC 2869"""
398    val = 53
399
400
401class RadiusAttr_Egress_VLANID(_RadiusAttrIntValue):
402    """RFC 4675"""
403    val = 56
404
405
406class RadiusAttr_Port_Limit(_RadiusAttrIntValue):
407    """RFC 2865"""
408    val = 62
409
410
411class RadiusAttr_ARAP_Security(_RadiusAttrIntValue):
412    """RFC 2869"""
413    val = 73
414
415
416class RadiusAttr_Password_Retry(_RadiusAttrIntValue):
417    """RFC 2869"""
418    val = 75
419
420
421class RadiusAttr_Tunnel_Preference(_RadiusAttrIntValue):
422    """RFC 2868"""
423    val = 83
424
425
426class RadiusAttr_Acct_Interim_Interval(_RadiusAttrIntValue):
427    """RFC 2869"""
428    val = 85
429
430
431class RadiusAttr_Acct_Tunnel_Packets_Lost(_RadiusAttrIntValue):
432    """RFC 2867"""
433    val = 86
434
435
436class RadiusAttr_Management_Privilege_Level(_RadiusAttrIntValue):
437    """RFC 5607"""
438    val = 136
439
440
441class RadiusAttr_Mobility_Domain_Id(_RadiusAttrIntValue):
442    """RFC 7268"""
443    val = 177
444
445
446class RadiusAttr_Preauth_Timeout(_RadiusAttrIntValue):
447    """RFC 7268"""
448    val = 178
449
450
451class RadiusAttr_WLAN_Venue_Info(_RadiusAttrIntValue):
452    """RFC 7268"""
453    val = 182
454
455
456class RadiusAttr_WLAN_Reason_Code(_RadiusAttrIntValue):
457    """RFC 7268"""
458    val = 185
459
460
461class RadiusAttr_WLAN_Pairwise_Cipher(_RadiusAttrIntValue):
462    """RFC 7268"""
463    val = 186
464
465
466class RadiusAttr_WLAN_Group_Cipher(_RadiusAttrIntValue):
467    """RFC 7268"""
468    val = 187
469
470
471class RadiusAttr_WLAN_AKM_Suite(_RadiusAttrIntValue):
472    """RFC 7268"""
473    val = 188
474
475
476class RadiusAttr_WLAN_Group_Mgmt_Cipher(_RadiusAttrIntValue):
477    """RFC 7268"""
478    val = 189
479
480
481class RadiusAttr_WLAN_RF_Band(_RadiusAttrIntValue):
482    """RFC 7268"""
483    val = 190
484
485
486#
487# RADIUS attributes which values are string (displayed as hex)
488#
489
490class _RadiusAttrHexStringVal(_SpecificRadiusAttr):
491    """
492    Implements a RADIUS attribute which value field is a string that will be
493    as a hex string.
494    """
495
496    __slots__ = ["val"]
497
498    def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None, **fields):
499        super(_RadiusAttrHexStringVal, self).__init__(
500            _pkt,
501            post_transform,
502            _internal,
503            _underlayer
504        )
505        self.fields["type"] = self.val
506        name_parts = self.__class__.__name__.split('RadiusAttr_')
507        if len(name_parts) < 2:
508            raise Scapy_Exception(
509                "Invalid class name: {}".format(self.__class__.__name__)
510            )
511        self.name = name_parts[1].replace('_', '-')
512
513    fields_desc = [
514        ByteEnumField("type", 24, _radius_attribute_types),
515        FieldLenField(
516            "len",
517            None,
518            "value",
519            "B",
520            adjust=lambda p, x: len(p.value) + 2
521        ),
522        XStrLenField("value", "", length_from=lambda p: p.len - 2 if p.len else 0)
523    ]
524
525
526class RadiusAttr_State(_RadiusAttrHexStringVal):
527    """RFC 2865"""
528    val = 24
529
530
531
532def prepare_packed_data(radius_packet, packed_req_authenticator):
533    """
534    Pack RADIUS data prior computing the authentication MAC
535    """
536
537    packed_hdr = struct.pack("!B", radius_packet.code)
538    packed_hdr += struct.pack("!B", radius_packet.id)
539    packed_hdr += struct.pack("!H", radius_packet.len)
540
541    packed_attrs = b''
542    for attr in radius_packet.attributes:
543        packed_attrs += raw(attr)
544
545    return packed_hdr + packed_req_authenticator + packed_attrs
546
547
548class RadiusAttr_Message_Authenticator(_RadiusAttrHexStringVal):
549    """RFC 2869"""
550    val = 80
551
552    fields_desc = [
553        ByteEnumField("type", 24, _radius_attribute_types),
554        FieldLenField(
555            "len",
556            18,
557            "value",
558            "B",
559        ),
560        XStrFixedLenField("value", "\x00" * 16, length=16)
561    ]
562
563    @staticmethod
564    def compute_message_authenticator(radius_packet, packed_req_authenticator,
565                                      shared_secret):
566        """
567        Computes the "Message-Authenticator" of a given RADIUS packet.
568        """
569
570        data = prepare_packed_data(radius_packet, packed_req_authenticator)
571        radius_hmac = hmac.new(shared_secret, data, hashlib.md5)
572
573        return radius_hmac.digest()
574
575#
576# RADIUS attributes which values are IPv4 prefixes
577#
578
579class _RadiusAttrIPv4AddrVal(RadiusAttribute):
580    """
581    Implements a RADIUS attribute which value field is an IPv4 address.
582    """
583
584    __slots__ = ["val"]
585
586    fields_desc = [
587        ByteEnumField("type", 4, _radius_attribute_types),
588        ByteField("len", 6),
589        IPField("value", "0.0.0.0")
590    ]
591
592
593class RadiusAttr_NAS_IP_Address(_RadiusAttrIPv4AddrVal):
594    """RFC 2865"""
595    val = 4
596
597
598class RadiusAttr_Framed_IP_Address(_RadiusAttrIPv4AddrVal):
599    """RFC 2865"""
600    val = 8
601
602
603class RadiusAttr_Framed_IP_Netmask(_RadiusAttrIPv4AddrVal):
604    """RFC 2865"""
605    val = 9
606
607
608class RadiusAttr_Login_IP_Host(_RadiusAttrIPv4AddrVal):
609    """RFC 2865"""
610    val = 14
611
612
613class RadiusAttr_Framed_IPX_Network(_RadiusAttrIPv4AddrVal):
614    """RFC 2865"""
615    val = 23
616
617
618class RadiusAttr_PMIP6_Home_LMA_IPv4_Address(_RadiusAttrIPv4AddrVal):
619    """RFC 6572"""
620    val = 149
621
622
623class RadiusAttr_PMIP6_Visited_LMA_IPv4_Address(_RadiusAttrIPv4AddrVal):
624    """RFC 6572"""
625    val = 150
626
627
628class RadiusAttr_PMIP6_Home_DHCP4_Server_Address(_RadiusAttrIPv4AddrVal):
629    """RFC 6572"""
630    val = 157
631
632
633class RadiusAttr_PMIP6_Visited_DHCP4_Server_Address(_RadiusAttrIPv4AddrVal):
634    """RFC 6572"""
635    val = 158
636
637
638class RadiusAttr_PMIP6_Home_IPv4_Gateway(_RadiusAttrIPv4AddrVal):
639    """RFC 6572"""
640    val = 161
641
642
643class RadiusAttr_PMIP6_Visited_IPv4_Gateway(_RadiusAttrIPv4AddrVal):
644    """RFC 6572"""
645    val = 162
646
647
648# See IANA registry "RADIUS Types"
649_radius_attrs_values = {
650    # Service-Type
651    6:
652    {
653        1: "Login",
654        2: "Framed",
655        3: "Callback Login",
656        4: "Callback Framed",
657        5: "Outbound",
658        6: "Administrative",
659        7: "NAS Prompt",
660        8: "Authenticate Only",
661        9: "Callback NAS Prompt",
662        10: "Call Check",
663        11: "Callback Administrative",
664        12: "Voice",
665        13: "Fax",
666        14: "Modem Relay",
667        15: "IAPP-Register",
668        16: "IAPP-AP-Check",
669        17: "Authorize Only",
670        18: "Framed-Management",
671        19: "Additional-Authorization"
672    },
673
674    # Framed-Protocol
675    7:
676    {
677        1: "PPP",
678        2: "SLIP",
679        3: "AppleTalk Remote Access Protocol (ARAP)",
680        4: "Gandalf proprietary SingleLink/MultiLink protocol",
681        5: "Xylogics proprietary IPX/SLIP",
682        6: "X.75 Synchronous",
683        7: "GPRS PDP Context"
684    },
685
686    # Framed-Routing
687    10:
688    {
689        0: "None",
690        1: "Send routing packets",
691        2: "Listen for routing packets",
692        3: "Send and Listen"
693    },
694
695    # Framed-Compression
696    13:
697    {
698        0: "None",
699        1: "VJ TCP/IP header compression",
700        2: "IPX header compression",
701        3: "Stac-LZS compression"
702    },
703
704    # Login-Service
705    15:
706    {
707        0: "Telnet",
708        1: "Rlogin",
709        2: "TCP Clear",
710        3: "PortMaster (proprietary)",
711        4: "LAT",
712        5: "X25-PAD",
713        6: "X25-T3POS",
714        7: "Unassigned",
715        8: "TCP Clear Quiet (suppresses any NAS-generated connect string)"
716    },
717
718    # Termination-Action
719    29:
720    {
721        0: "Default",
722        1: "RADIUS-Request"
723    },
724
725    # Acct-Status-Type
726    40:
727    {
728        1: "Start",
729        2: "Stop",
730        3: "Interim-Update",
731        4: "Unassigned",
732        5: "Unassigned",
733        6: "Unassigned",
734        7: "Accounting-On",
735        8: "Accounting-Off",
736        9: "Tunnel-Start",
737        10: "Tunnel-Stop",
738        11: "Tunnel-Reject",
739        12: "Tunnel-Link-Start",
740        13: "Tunnel-Link-Stop",
741        14: "Tunnel-Link-Reject",
742        15: "Failed"
743    },
744
745    # Acct-Authentic
746    45:
747    {
748        1: "RADIUS",
749        2: "Local",
750        3: "Remote",
751        4: "Diameter"
752    },
753
754    # Acct-Terminate-Cause
755    49:
756    {
757        1: "User Request",
758        2: "Lost Carrier",
759        3: "Lost Service",
760        4: "Idle Timeout",
761        5: "Session Timeout",
762        6: "Admin Reset",
763        7: "Admin Reboot",
764        8: "Port Error",
765        9: "NAS Error",
766        10: "NAS Request",
767        11: "NAS Reboot",
768        12: "Port Unneeded",
769        13: "Port Preempted",
770        14: "Port Suspended",
771        15: "Service Unavailable",
772        16: "Callback",
773        17: "User Error",
774        18: "Host Request",
775        19: "Supplicant Restart",
776        20: "Reauthentication Failure",
777        21: "Port Reinitialized",
778        22: "Port Administratively Disabled",
779        23: "Lost Power",
780    },
781
782    # NAS-Port-Type
783    61:
784    {
785        0: "Async",
786        1: "Sync",
787        2: "ISDN Sync",
788        3: "ISDN Async V.120",
789        4: "ISDN Async V.110",
790        5: "Virtual",
791        6: "PIAFS",
792        7: "HDLC Clear Channel",
793        8: "X.25",
794        9: "X.75",
795        10: "G.3 Fax",
796        11: "SDSL - Symmetric DSL",
797        12: "ADSL-CAP - Asymmetric DSL, Carrierless Amplitude Phase Modulation",
798        13: "ADSL-DMT - Asymmetric DSL, Discrete Multi-Tone",
799        14: "IDSL - ISDN Digital Subscriber Line",
800        15: "Ethernet",
801        16: "xDSL - Digital Subscriber Line of unknown type",
802        17: "Cable",
803        18: "Wireles - Other",
804        19: "Wireless - IEEE 802.11",
805        20: "Token-Ring",
806        21: "FDDI",
807        22: "Wireless - CDMA2000",
808        23: "Wireless - UMTS",
809        24: "Wireless - 1X-EV",
810        25: "IAPP",
811        26: "FTTP - Fiber to the Premises",
812        27: "Wireless - IEEE 802.16",
813        28: "Wireless - IEEE 802.20",
814        29: "Wireless - IEEE 802.22",
815        30: "PPPoA - PPP over ATM",
816        31: "PPPoEoA - PPP over Ethernet over ATM",
817        32: "PPPoEoE - PPP over Ethernet over Ethernet",
818        33: "PPPoEoVLAN - PPP over Ethernet over VLAN",
819        34: "PPPoEoQinQ - PPP over Ethernet over IEEE 802.1QinQ",
820        35: "xPON - Passive Optical Network",
821        36: "Wireless - XGP",
822        37: "WiMAX Pre-Release 8 IWK Function",
823        38: "WIMAX-WIFI-IWK: WiMAX WIFI Interworking",
824        39: "WIMAX-SFF: Signaling Forwarding Function for LTE/3GPP2",
825        40: "WIMAX-HA-LMA: WiMAX HA and or LMA function",
826        41: "WIMAX-DHCP: WIMAX DCHP service",
827        42: "WIMAX-LBS: WiMAX location based service",
828        43: "WIMAX-WVS: WiMAX voice service"
829    },
830
831    # Tunnel-Type
832    64:
833    {
834        1: "Point-to-Point Tunneling Protocol (PPTP)",
835        2: "Layer Two Forwarding (L2F)",
836        3: "Layer Two Tunneling Protocol (L2TP)",
837        4: "Ascend Tunnel Management Protocol (ATMP)",
838        5: "Virtual Tunneling Protocol (VTP)",
839        6: "IP Authentication Header in the Tunnel-mode (AH)",
840        7: "IP-in-IP Encapsulation (IP-IP)",
841        8: "Minimal IP-in-IP Encapsulation (MIN-IP-IP)",
842        9: "IP Encapsulating Security Payload in the Tunnel-mode (ESP)",
843        10: "Generic Route Encapsulation (GRE)",
844        11: "Bay Dial Virtual Services (DVS)",
845        12: "IP-in-IP Tunneling",
846        13: "Virtual LANs (VLAN)"
847    },
848
849    # Tunnel-Medium-Type
850    65:
851    {
852        1: "IPv4 (IP version 4)",
853        2: "IPv6 (IP version 6)",
854        3: "NSAP",
855        4: "HDLC (8-bit multidrop)",
856        5: "BBN 1822",
857        6: "802",
858        7: "E.163 (POTS)",
859        8: "E.164 (SMDS, Frame Relay, ATM)",
860        9: "F.69 (Telex)",
861        10: "X.121 (X.25, Frame Relay)",
862        11: "IPX",
863        12: "Appletalk",
864        13: "Decnet IV",
865        14: "Banyan Vine",
866        15: "E.164 with NSAP format subaddress"
867    },
868
869    # ARAP-Zone-Access
870    72:
871    {
872        1: "Only allow access to default zone",
873        2: "Use zone filter inclusively",
874        3: "Not used",
875        4: "Use zone filter exclusively"
876    },
877
878    # Prompt
879    76:
880    {
881        0: "No Echo",
882        1: "Echo"
883    },
884
885    # Error-Cause Attribute
886    101:
887    {
888        201: "Residual Session Context Removed",
889        202: "Invalid EAP Packet (Ignored)",
890        401: "Unsupported Attribute",
891        402: "Missing Attribute",
892        403: "NAS Identification Mismatch",
893        404: "Invalid Request",
894        405: "Unsupported Service",
895        406: "Unsupported Extension",
896        407: "Invalid Attribute Value",
897        501: "Administratively Prohibited",
898        502: "Request Not Routable (Proxy)",
899        503: "Session Context Not Found",
900        504: "Session Context Not Removable",
901        505: "Other Proxy Processing Error",
902        506: "Resources Unavailable",
903        507: "Request Initiated",
904        508: "Multiple Session Selection Unsupported",
905        509: "Location-Info-Required",
906        601: "Response Too Big"
907    },
908
909    # Operator Namespace Identifier - Attribute 126
910    126:
911    {
912        0x30: "TADIG",
913        0x31: "REALM",
914        0x32: "E212",
915        0x33: "ICC",
916        0xFF: "Reserved"
917    },
918
919    # Basic-Location-Policy-Rules
920    129:
921    {
922        0: "Retransmission allowed",
923    },
924
925    # Location-Capable
926    131:
927    {
928        1: "CIVIC_LOCATION",
929        2: "GEO_LOCATION",
930        4: "USERS_LOCATION",
931        8: "NAS_LOCATION"
932    },
933
934    # Framed-Management-Protocol
935    133:
936    {
937        1: "SNMP",
938        2: "Web-based",
939        3: "NETCONF",
940        4: "FTP",
941        5: "TFTP",
942        6: "SFTP",
943        7: "RCP",
944        8: "SCP"
945    },
946
947    # Management-Transport-Protection
948    134:
949    {
950        1: "No-Protection",
951        2: "Integrity-Protection",
952        3: "Integrity-Confidentiality-Protection",
953    },
954}
955
956
957class _RadiusAttrIntEnumVal(_SpecificRadiusAttr):
958    """
959    Implements a RADIUS attribute which value field is 4 bytes long integer.
960    """
961
962    __slots__ = ["val"]
963
964    fields_desc = [
965        ByteEnumField("type", 6, _radius_attribute_types),
966        ByteField("len", 6),
967        MultiEnumField(
968            "value",
969            0,
970            _radius_attrs_values,
971            depends_on=lambda p: p.type,
972            fmt="I"
973        )
974    ]
975
976
977class RadiusAttr_Service_Type(_RadiusAttrIntEnumVal):
978    """RFC 2865"""
979    val = 6
980
981
982class RadiusAttr_Framed_Protocol(_RadiusAttrIntEnumVal):
983    """RFC 2865"""
984    val = 7
985
986
987class RadiusAttr_NAS_Port_Type(_RadiusAttrIntEnumVal):
988    """RFC 2865"""
989    val = 61
990
991
992class _EAPPacketField(PacketField):
993
994    """
995    Handles EAP-Message attribute value (the actual EAP packet).
996    """
997
998    def m2i(self, pkt, m):
999        ret = None
1000        eap_packet_len = struct.unpack("!H", m[2:4])[0]
1001        if eap_packet_len < 254:
1002            # If the EAP packet has not been fragmented, build a Scapy EAP
1003            # packet from the data.
1004            ret = EAP(m)
1005        else:
1006            ret = conf.raw_layer(m)
1007        return ret
1008
1009
1010class RadiusAttr_EAP_Message(RadiusAttribute):
1011    """
1012    Implements the "EAP-Message" attribute (RFC 3579).
1013    """
1014
1015    name = "EAP-Message"
1016    fields_desc = [
1017        ByteEnumField("type", 79, _radius_attribute_types),
1018        FieldLenField(
1019            "len",
1020            None,
1021            "value",
1022            "B",
1023            adjust=lambda pkt, x: len(pkt.value) + 2
1024        ),
1025        _EAPPacketField("value", "", EAP)
1026    ]
1027
1028
1029class RadiusAttr_Vendor_Specific(RadiusAttribute):
1030    """
1031    Implements the "Vendor-Specific" attribute, as described in RFC 2865.
1032    """
1033
1034    name = "Vendor-Specific"
1035    fields_desc = [
1036        ByteEnumField("type", 26, _radius_attribute_types),
1037        FieldLenField(
1038            "len",
1039            None,
1040            "value",
1041            "B",
1042            adjust=lambda pkt, x: len(pkt.value) + 8
1043        ),
1044        IntField("vendor_id", 0),
1045        ByteField("vendor_type", 0),
1046        FieldLenField(
1047            "vendor_len",
1048            None,
1049            "value",
1050            "B",
1051            adjust=lambda p, x: len(p.value) + 2
1052        ),
1053        StrLenField("value", "", length_from=lambda p: p.vendor_len - 2)
1054    ]
1055
1056
1057class _RADIUSAttrPacketListField(PacketListField):
1058    """
1059    PacketListField handling a list of RADIUS attributes.
1060    """
1061
1062    def getfield(self, pkt, s):
1063        lst = []
1064        length = None
1065        ret = ""
1066
1067        if self.length_from is not None:
1068            length = self.length_from(pkt)
1069
1070        if length is not None:
1071            remain, ret = s[:length], s[length:]
1072
1073        while remain:
1074            attr_len = orb(remain[1])
1075            current = remain[:attr_len]
1076            remain = remain[attr_len:]
1077            packet = self.m2i(pkt, current)
1078            lst.append(packet)
1079
1080        return remain + ret, lst
1081
1082
1083# See IANA RADIUS Packet Type Codes registry
1084_packet_codes = {
1085    1: "Access-Request",
1086    2: "Access-Accept",
1087    3: "Access-Reject",
1088    4: "Accounting-Request",
1089    5: "Accounting-Response",
1090    6: "Accounting-Status (now Interim Accounting)",
1091    7: "Password-Request",
1092    8: "Password-Ack",
1093    9: "Password-Reject",
1094    10: "Accounting-Message",
1095    11: "Access-Challenge",
1096    12: "Status-Server (experimental)",
1097    13: "Status-Client (experimental)",
1098    21: "Resource-Free-Request",
1099    22: "Resource-Free-Response",
1100    23: "Resource-Query-Request",
1101    24: "Resource-Query-Response",
1102    25: "Alternate-Resource-Reclaim-Request",
1103    26: "NAS-Reboot-Request",
1104    27: "NAS-Reboot-Response",
1105    28: "Reserved",
1106    29: "Next-Passcode",
1107    30: "New-Pin",
1108    31: "Terminate-Session",
1109    32: "Password-Expired",
1110    33: "Event-Request",
1111    34: "Event-Response",
1112    40: "Disconnect-Request",
1113    41: "Disconnect-ACK",
1114    42: "Disconnect-NAK",
1115    43: "CoA-Request",
1116    44: "CoA-ACK",
1117    45: "CoA-NAK",
1118    50: "IP-Address-Allocate",
1119    51: "IP-Address-Release",
1120    52: "Protocol-Error",
1121    250: "Experimental Use",
1122    251: "Experimental Use",
1123    252: "Experimental Use",
1124    253: "Experimental Use",
1125    254: "Reserved",
1126    255: "Reserved"
1127}
1128
1129
1130class Radius(Packet):
1131    """
1132    Implements a RADIUS packet (RFC 2865).
1133    """
1134
1135    name = "RADIUS"
1136    fields_desc = [
1137        ByteEnumField("code", 1, _packet_codes),
1138        ByteField("id", 0),
1139        FieldLenField(
1140            "len",
1141            None,
1142            "attributes",
1143            "H",
1144            adjust=lambda pkt, x: len(pkt.attributes) + 20
1145        ),
1146        XStrFixedLenField("authenticator", "", 16),
1147        _RADIUSAttrPacketListField(
1148            "attributes",
1149            [],
1150            RadiusAttribute,
1151            length_from=lambda pkt: pkt.len - 20
1152        )
1153    ]
1154
1155    def compute_authenticator(self, packed_request_auth, shared_secret):
1156        """
1157        Computes the authenticator field (RFC 2865 - Section 3)
1158        """
1159
1160        data = prepare_packed_data(self, packed_request_auth)
1161        radius_mac = hashlib.md5(data + shared_secret)
1162        return radius_mac.digest()
1163
1164    def post_build(self, p, pay):
1165        p += pay
1166        length = self.len
1167        if length is None:
1168            length = len(p)
1169            p = p[:2] + struct.pack("!H", length) + p[4:]
1170        return p
1171
1172
1173bind_layers(UDP, Radius, sport=1812)
1174bind_layers(UDP, Radius, dport=1812)
1175bind_layers(UDP, Radius, sport=1813)
1176bind_layers(UDP, Radius, dport=1813)
1177