1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Acknowledgment: Vincent Mauge <vmauge.nospam@nospam.gmail.com>
5
6"""
7RADIUS (Remote Authentication Dial In User Service)
8
9To disable Radius-EAP defragmentation (True by default), you can use::
10
11 conf.contribs.setdefault("radius", {}).setdefault("auto-defrag", False)
12"""
13
14import collections
15import enum
16import hashlib
17import hmac
18import struct
19
20from scapy.ansmachine import AnsweringMachine
21from scapy.compat import bytes_encode
22from scapy.config import conf, crypto_validator
23from scapy.error import log_runtime, Scapy_Exception
24from scapy.packet import (
25 Packet,
26 Padding,
27 bind_layers,
28 bind_bottom_up,
29)
30from scapy.fields import (
31 ByteEnumField,
32 ByteField,
33 FieldLenField,
34 IPField,
35 IntEnumField,
36 IntField,
37 MultiEnumField,
38 MultipleTypeField,
39 PacketLenField,
40 PacketListField,
41 StrField,
42 StrFixedLenField,
43 StrLenField,
44 XStrFixedLenField,
45 XStrLenField,
46)
47from scapy.sendrecv import send
48from scapy.utils import strxor
49
50from scapy.layers.eap import EAP
51from scapy.layers.inet import UDP, IP
52from scapy.layers.ntlm import MD4le
53
54if conf.crypto_valid:
55 from scapy.layers.tls.crypto.cipher_block import Cipher_DES_ECB
56 from scapy.layers.tls.crypto.hash import (
57 Hash_MD4,
58 Hash_MD5,
59 Hash_SHA,
60 )
61else:
62 Cipher_DES_ECB = None
63 Hash_MD4 = Hash_MD5 = Hash_SHA = None
64
65
66# https://www.iana.org/assignments/radius-types/radius-types.xhtml
67_radius_attribute_types = {
68 1: "User-Name",
69 2: "User-Password",
70 3: "CHAP-Password",
71 4: "NAS-IP-Address",
72 5: "NAS-Port",
73 6: "Service-Type",
74 7: "Framed-Protocol",
75 8: "Framed-IP-Address",
76 9: "Framed-IP-Netmask",
77 10: "Framed-Routing",
78 11: "Filter-Id",
79 12: "Framed-MTU",
80 13: "Framed-Compression",
81 14: "Login-IP-Host",
82 15: "Login-Service",
83 16: "Login-TCP-Port",
84 17: "Unassigned",
85 18: "Reply-Message",
86 19: "Callback-Number",
87 20: "Callback-Id",
88 21: "Unassigned",
89 22: "Framed-Route",
90 23: "Framed-IPX-Network",
91 24: "State",
92 25: "Class",
93 26: "Vendor-Specific",
94 27: "Session-Timeout",
95 28: "Idle-Timeout",
96 29: "Termination-Action",
97 30: "Called-Station-Id",
98 31: "Calling-Station-Id",
99 32: "NAS-Identifier",
100 33: "Proxy-State",
101 34: "Login-LAT-Service",
102 35: "Login-LAT-Node",
103 36: "Login-LAT-Group",
104 37: "Framed-AppleTalk-Link",
105 38: "Framed-AppleTalk-Network",
106 39: "Framed-AppleTalk-Zone",
107 40: "Acct-Status-Type",
108 41: "Acct-Delay-Time",
109 42: "Acct-Input-Octets",
110 43: "Acct-Output-Octets",
111 44: "Acct-Session-Id",
112 45: "Acct-Authentic",
113 46: "Acct-Session-Time",
114 47: "Acct-Input-Packets",
115 48: "Acct-Output-Packets",
116 49: "Acct-Terminate-Cause",
117 50: "Acct-Multi-Session-Id",
118 51: "Acct-Link-Count",
119 52: "Acct-Input-Gigawords",
120 53: "Acct-Output-Gigawords",
121 54: "Unassigned",
122 55: "Event-Timestamp",
123 56: "Egress-VLANID",
124 57: "Ingress-Filters",
125 58: "Egress-VLAN-Name",
126 59: "User-Priority-Table",
127 60: "CHAP-Challenge",
128 61: "NAS-Port-Type",
129 62: "Port-Limit",
130 63: "Login-LAT-Port",
131 64: "Tunnel-Type",
132 65: "Tunnel-Medium-Type",
133 66: "Tunnel-Client-Endpoint",
134 67: "Tunnel-Server-Endpoint",
135 68: "Acct-Tunnel-Connection",
136 69: "Tunnel-Password",
137 70: "ARAP-Password",
138 71: "ARAP-Features",
139 72: "ARAP-Zone-Access",
140 73: "ARAP-Security",
141 74: "ARAP-Security-Data",
142 75: "Password-Retry",
143 76: "Prompt",
144 77: "Connect-Info",
145 78: "Configuration-Token",
146 79: "EAP-Message",
147 80: "Message-Authenticator",
148 81: "Tunnel-Private-Group-ID",
149 82: "Tunnel-Assignment-ID",
150 83: "Tunnel-Preference",
151 84: "ARAP-Challenge-Response",
152 85: "Acct-Interim-Interval",
153 86: "Acct-Tunnel-Packets-Lost",
154 87: "NAS-Port-Id",
155 88: "Framed-Pool",
156 89: "CUI",
157 90: "Tunnel-Client-Auth-ID",
158 91: "Tunnel-Server-Auth-ID",
159 92: "NAS-Filter-Rule",
160 93: "Unassigned",
161 94: "Originating-Line-Info",
162 95: "NAS-IPv6-Address",
163 96: "Framed-Interface-Id",
164 97: "Framed-IPv6-Prefix",
165 98: "Login-IPv6-Host",
166 99: "Framed-IPv6-Route",
167 100: "Framed-IPv6-Pool",
168 101: "Error-Cause",
169 102: "EAP-Key-Name",
170 103: "Digest-Response",
171 104: "Digest-Realm",
172 105: "Digest-Nonce",
173 106: "Digest-Response-Auth",
174 107: "Digest-Nextnonce",
175 108: "Digest-Method",
176 109: "Digest-URI",
177 110: "Digest-Qop",
178 111: "Digest-Algorithm",
179 112: "Digest-Entity-Body-Hash",
180 113: "Digest-CNonce",
181 114: "Digest-Nonce-Count",
182 115: "Digest-Username",
183 116: "Digest-Opaque",
184 117: "Digest-Auth-Param",
185 118: "Digest-AKA-Auts",
186 119: "Digest-Domain",
187 120: "Digest-Stale",
188 121: "Digest-HA1",
189 122: "SIP-AOR",
190 123: "Delegated-IPv6-Prefix",
191 124: "MIP6-Feature-Vector",
192 125: "MIP6-Home-Link-Prefix",
193 126: "Operator-Name",
194 127: "Location-Information",
195 128: "Location-Data",
196 129: "Basic-Location-Policy-Rules",
197 130: "Extended-Location-Policy-Rules",
198 131: "Location-Capable",
199 132: "Requested-Location-Info",
200 133: "Framed-Management-Protocol",
201 134: "Management-Transport-Protection",
202 135: "Management-Policy-Id",
203 136: "Management-Privilege-Level",
204 137: "PKM-SS-Cert",
205 138: "PKM-CA-Cert",
206 139: "PKM-Config-Settings",
207 140: "PKM-Cryptosuite-List",
208 141: "PKM-SAID",
209 142: "PKM-SA-Descriptor",
210 143: "PKM-Auth-Key",
211 144: "DS-Lite-Tunnel-Name",
212 145: "Mobile-Node-Identifier",
213 146: "Service-Selection",
214 147: "PMIP6-Home-LMA-IPv6-Address",
215 148: "PMIP6-Visited-LMA-IPv6-Address",
216 149: "PMIP6-Home-LMA-IPv4-Address",
217 150: "PMIP6-Visited-LMA-IPv4-Address",
218 151: "PMIP6-Home-HN-Prefix",
219 152: "PMIP6-Visited-HN-Prefix",
220 153: "PMIP6-Home-Interface-ID",
221 154: "PMIP6-Visited-Interface-ID",
222 155: "PMIP6-Home-IPv4-HoA",
223 156: "PMIP6-Visited-IPv4-HoA",
224 157: "PMIP6-Home-DHCP4-Server-Address",
225 158: "PMIP6-Visited-DHCP4-Server-Address",
226 159: "PMIP6-Home-DHCP6-Server-Address",
227 160: "PMIP6-Visited-DHCP6-Server-Address",
228 161: "PMIP6-Home-IPv4-Gateway",
229 162: "PMIP6-Visited-IPv4-Gateway",
230 163: "EAP-Lower-Layer",
231 164: "GSS-Acceptor-Service-Name",
232 165: "GSS-Acceptor-Host-Name",
233 166: "GSS-Acceptor-Service-Specifics",
234 167: "GSS-Acceptor-Realm-Name",
235 168: "Framed-IPv6-Address",
236 169: "DNS-Server-IPv6-Address",
237 170: "Route-IPv6-Information",
238 171: "Delegated-IPv6-Prefix-Pool",
239 172: "Stateful-IPv6-Address-Pool",
240 173: "IPv6-6rd-Configuration",
241 174: "Allowed-Called-Station-Id",
242 175: "EAP-Peer-Id",
243 176: "EAP-Server-Id",
244 177: "Mobility-Domain-Id",
245 178: "Preauth-Timeout",
246 179: "Network-Id-Name",
247 180: "EAPoL-Announcement",
248 181: "WLAN-HESSID",
249 182: "WLAN-Venue-Info",
250 183: "WLAN-Venue-Language",
251 184: "WLAN-Venue-Name",
252 185: "WLAN-Reason-Code",
253 186: "WLAN-Pairwise-Cipher",
254 187: "WLAN-Group-Cipher",
255 188: "WLAN-AKM-Suite",
256 189: "WLAN-Group-Mgmt-Cipher",
257 190: "WLAN-RF-Band",
258 191: "Unassigned",
259}
260
261
262class RadiusAttribute(Packet):
263 """
264 Implements a RADIUS attribute (RFC 2865). Every specific RADIUS attribute
265 class should inherit from this one.
266 """
267
268 name = "Radius Attribute"
269 fields_desc = [
270 ByteEnumField("type", 1, _radius_attribute_types),
271 FieldLenField("len", None, "value", "B",
272 adjust=lambda pkt, x: len(pkt.value) + 2),
273 StrLenField("value", "", length_from=lambda pkt: pkt.len - 2)
274 ]
275
276 registered_attributes = {}
277
278 @classmethod
279 def register_variant(cls):
280 """
281 Registers the RADIUS attributes defined in this module.
282 """
283
284 if hasattr(cls, "val"):
285 cls.registered_attributes[cls.val] = cls
286 else:
287 cls.registered_attributes[cls.type.default] = cls
288
289 @classmethod
290 def dispatch_hook(cls, _pkt=None, *args, **kargs):
291 """
292 Returns the right RadiusAttribute class for the given data.
293 """
294
295 if _pkt:
296 attr_type = _pkt[0]
297 return cls.registered_attributes.get(attr_type, cls)
298 return cls
299
300 def post_build(self, p, pay):
301 length = self.len
302 if length is None:
303 length = len(p)
304 p = p[:1] + struct.pack("!B", length) + p[2:]
305 return p
306
307 def guess_payload_class(self, _):
308 return Padding
309
310
311class _SpecificRadiusAttr(RadiusAttribute):
312 """
313 Class from which every "specific" RADIUS attribute defined in this module
314 inherits.
315 """
316
317 __slots__ = ["val"]
318 match_subclass = True
319
320 def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None, **fields): # noqa: E501
321 super(_SpecificRadiusAttr, self).__init__(
322 _pkt,
323 post_transform,
324 _internal,
325 _underlayer,
326 **fields
327 )
328 self.fields["type"] = self.val
329 name_parts = self.__class__.__name__.split('RadiusAttr_')
330 if len(name_parts) < 2:
331 raise Scapy_Exception(
332 "Invalid class name: {}".format(self.__class__.__name__)
333 )
334 self.name = name_parts[1].replace('_', '-')
335
336
337#
338# RADIUS attributes which values are 4 bytes integers
339#
340
341class _RadiusAttrIntValue(_SpecificRadiusAttr):
342 """
343 Implements a RADIUS attribute which value field is 4 bytes long integer.
344 """
345
346 fields_desc = [
347 ByteEnumField("type", 5, _radius_attribute_types),
348 ByteField("len", 6),
349 IntField("value", 0)
350 ]
351
352
353class RadiusAttr_User_Name(_SpecificRadiusAttr):
354 """RFC 2865"""
355 val = 1
356
357
358class RadiusAttr_NAS_Port(_RadiusAttrIntValue):
359 """RFC 2865"""
360 val = 5
361
362
363class RadiusAttr_Framed_MTU(_RadiusAttrIntValue):
364 """RFC 2865"""
365 val = 12
366
367
368class RadiusAttr_Login_TCP_Port(_RadiusAttrIntValue):
369 """RFC 2865"""
370 val = 16
371
372
373class RadiusAttr_Session_Timeout(_RadiusAttrIntValue):
374 """RFC 2865"""
375 val = 27
376
377
378class RadiusAttr_Idle_Timeout(_RadiusAttrIntValue):
379 """RFC 2865"""
380 val = 28
381
382
383class RadiusAttr_Framed_AppleTalk_Link(_RadiusAttrIntValue):
384 """RFC 2865"""
385 val = 37
386
387
388class RadiusAttr_Framed_AppleTalk_Network(_RadiusAttrIntValue):
389 """RFC 2865"""
390 val = 38
391
392
393class RadiusAttr_Acct_Delay_Time(_RadiusAttrIntValue):
394 """RFC 2866"""
395 val = 41
396
397
398class RadiusAttr_Acct_Input_Octets(_RadiusAttrIntValue):
399 """RFC 2866"""
400 val = 42
401
402
403class RadiusAttr_Acct_Output_Octets(_RadiusAttrIntValue):
404 """RFC 2866"""
405 val = 43
406
407
408class RadiusAttr_Acct_Session_Time(_RadiusAttrIntValue):
409 """RFC 2866"""
410 val = 46
411
412
413class RadiusAttr_Acct_Input_Packets(_RadiusAttrIntValue):
414 """RFC 2866"""
415 val = 47
416
417
418class RadiusAttr_Acct_Output_Packets(_RadiusAttrIntValue):
419 """RFC 2866"""
420 val = 48
421
422
423class RadiusAttr_Acct_Link_Count(_RadiusAttrIntValue):
424 """RFC 2866"""
425 val = 51
426
427
428class RadiusAttr_Acct_Input_Gigawords(_RadiusAttrIntValue):
429 """RFC 2869"""
430 val = 52
431
432
433class RadiusAttr_Acct_Output_Gigawords(_RadiusAttrIntValue):
434 """RFC 2869"""
435 val = 53
436
437
438class RadiusAttr_Egress_VLANID(_RadiusAttrIntValue):
439 """RFC 4675"""
440 val = 56
441
442
443class RadiusAttr_Port_Limit(_RadiusAttrIntValue):
444 """RFC 2865"""
445 val = 62
446
447
448class RadiusAttr_ARAP_Security(_RadiusAttrIntValue):
449 """RFC 2869"""
450 val = 73
451
452
453class RadiusAttr_Password_Retry(_RadiusAttrIntValue):
454 """RFC 2869"""
455 val = 75
456
457
458class RadiusAttr_Tunnel_Preference(_RadiusAttrIntValue):
459 """RFC 2868"""
460 val = 83
461
462
463class RadiusAttr_Acct_Interim_Interval(_RadiusAttrIntValue):
464 """RFC 2869"""
465 val = 85
466
467
468class RadiusAttr_Acct_Tunnel_Packets_Lost(_RadiusAttrIntValue):
469 """RFC 2867"""
470 val = 86
471
472
473class RadiusAttr_Management_Privilege_Level(_RadiusAttrIntValue):
474 """RFC 5607"""
475 val = 136
476
477
478class RadiusAttr_Mobility_Domain_Id(_RadiusAttrIntValue):
479 """RFC 7268"""
480 val = 177
481
482
483class RadiusAttr_Preauth_Timeout(_RadiusAttrIntValue):
484 """RFC 7268"""
485 val = 178
486
487
488class RadiusAttr_WLAN_Venue_Info(_RadiusAttrIntValue):
489 """RFC 7268"""
490 val = 182
491
492
493class RadiusAttr_WLAN_Reason_Code(_RadiusAttrIntValue):
494 """RFC 7268"""
495 val = 185
496
497
498class RadiusAttr_WLAN_Pairwise_Cipher(_RadiusAttrIntValue):
499 """RFC 7268"""
500 val = 186
501
502
503class RadiusAttr_WLAN_Group_Cipher(_RadiusAttrIntValue):
504 """RFC 7268"""
505 val = 187
506
507
508class RadiusAttr_WLAN_AKM_Suite(_RadiusAttrIntValue):
509 """RFC 7268"""
510 val = 188
511
512
513class RadiusAttr_WLAN_Group_Mgmt_Cipher(_RadiusAttrIntValue):
514 """RFC 7268"""
515 val = 189
516
517
518class RadiusAttr_WLAN_RF_Band(_RadiusAttrIntValue):
519 """RFC 7268"""
520 val = 190
521
522
523#
524# RADIUS attributes which values are string (displayed as hex)
525#
526
527class _RadiusAttrHexStringVal(_SpecificRadiusAttr):
528 """
529 Implements a RADIUS attribute which value field is a string that will be
530 as a hex string.
531 """
532
533 __slots__ = ["val"]
534
535 def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None, **fields): # noqa: E501
536 super(_RadiusAttrHexStringVal, self).__init__(
537 _pkt,
538 post_transform,
539 _internal,
540 _underlayer,
541 **fields
542 )
543 self.fields["type"] = self.val
544 name_parts = self.__class__.__name__.split('RadiusAttr_')
545 if len(name_parts) < 2:
546 raise Scapy_Exception(
547 "Invalid class name: {}".format(self.__class__.__name__)
548 )
549 self.name = name_parts[1].replace('_', '-')
550
551 fields_desc = [
552 ByteEnumField("type", 24, _radius_attribute_types),
553 FieldLenField(
554 "len",
555 None,
556 "value",
557 "B",
558 adjust=lambda p, x: len(p.value) + 2
559 ),
560 XStrLenField("value", "", length_from=lambda p: p.len - 2 if p.len else 0) # noqa: E501
561 ]
562
563
564class RadiusAttr_User_Password(_RadiusAttrHexStringVal):
565 """RFC 2865"""
566 val = 2
567
568 def decrypt(self, radius_packet, secret):
569 """
570 Return the decrypted value of the User-Password field
571 RFC2865 sect 5.2
572 """
573 password = b""
574
575 encrypted = self.value
576 ci = radius_packet.authenticator
577 while encrypted:
578 bi = Hash_MD5().digest(secret + ci)
579 ci, encrypted = encrypted[:16], encrypted[16:]
580 password += strxor(ci, bi)
581
582 return password.rstrip(b"\x00")
583
584 @staticmethod
585 def encrypt(radius_packet, password, secret):
586 """
587 Create a User-Password attribute from a secret
588 RFC2865 sect 5.2
589 """
590 password = bytes_encode(password)
591
592 # Pad to 16 octets boundary
593 password += (-len(password) % 16) * b"\x00"
594
595 encrypted = b""
596 ci = radius_packet.authenticator
597 while password:
598 bi = Hash_MD5().digest(secret + ci)
599 ci = strxor(password[:16], bi)
600 password = password[16:]
601
602 return RadiusAttr_User_Password(value=encrypted)
603
604
605class RadiusAttr_State(_RadiusAttrHexStringVal):
606 """RFC 2865"""
607 val = 24
608
609
610def prepare_packed_data(radius_packet, RequestAuth):
611 """
612 Pack RADIUS data prior computing the authentication MAC
613 """
614 s = bytes(radius_packet)
615 Code_Id_Length = s[:4]
616 Attributes = s[4 + 16:]
617 return Code_Id_Length + RequestAuth + Attributes
618
619
620class RadiusAttr_Message_Authenticator(_RadiusAttrHexStringVal):
621 """RFC 2869"""
622 val = 80
623
624 fields_desc = [
625 ByteEnumField("type", 24, _radius_attribute_types),
626 FieldLenField(
627 "len",
628 18,
629 "value",
630 "B",
631 ),
632 XStrFixedLenField("value", "\x00" * 16, length=16)
633 ]
634
635 @staticmethod
636 def compute_message_authenticator(radius_packet, packed_req_authenticator,
637 shared_secret):
638 """
639 Computes the "Message-Authenticator" of a given RADIUS packet.
640 (RFC 2869 - Page 33)
641 """
642
643 # Make sure the current auth is empty
644 attr = radius_packet[RadiusAttr_Message_Authenticator]
645 attr.value = b"\x00" * (attr.len - 2)
646
647 data = prepare_packed_data(radius_packet, packed_req_authenticator)
648 radius_hmac = hmac.new(shared_secret, data, hashlib.md5)
649
650 return radius_hmac.digest()
651
652#
653# RADIUS attributes which values are IPv4 prefixes
654#
655
656
657class _RadiusAttrIPv4AddrVal(_SpecificRadiusAttr):
658 """
659 Implements a RADIUS attribute which value field is an IPv4 address.
660 """
661
662 __slots__ = ["val"]
663
664 fields_desc = [
665 ByteEnumField("type", 4, _radius_attribute_types),
666 ByteField("len", 6),
667 IPField("value", "0.0.0.0")
668 ]
669
670
671class RadiusAttr_NAS_IP_Address(_RadiusAttrIPv4AddrVal):
672 """RFC 2865"""
673 val = 4
674
675
676class RadiusAttr_Framed_IP_Address(_RadiusAttrIPv4AddrVal):
677 """RFC 2865"""
678 val = 8
679
680
681class RadiusAttr_Framed_IP_Netmask(_RadiusAttrIPv4AddrVal):
682 """RFC 2865"""
683 val = 9
684
685
686class RadiusAttr_Login_IP_Host(_RadiusAttrIPv4AddrVal):
687 """RFC 2865"""
688 val = 14
689
690
691class RadiusAttr_Framed_IPX_Network(_RadiusAttrIPv4AddrVal):
692 """RFC 2865"""
693 val = 23
694
695
696class RadiusAttr_PMIP6_Home_LMA_IPv4_Address(_RadiusAttrIPv4AddrVal):
697 """RFC 6572"""
698 val = 149
699
700
701class RadiusAttr_PMIP6_Visited_LMA_IPv4_Address(_RadiusAttrIPv4AddrVal):
702 """RFC 6572"""
703 val = 150
704
705
706class RadiusAttr_PMIP6_Home_DHCP4_Server_Address(_RadiusAttrIPv4AddrVal):
707 """RFC 6572"""
708 val = 157
709
710
711class RadiusAttr_PMIP6_Visited_DHCP4_Server_Address(_RadiusAttrIPv4AddrVal):
712 """RFC 6572"""
713 val = 158
714
715
716class RadiusAttr_PMIP6_Home_IPv4_Gateway(_RadiusAttrIPv4AddrVal):
717 """RFC 6572"""
718 val = 161
719
720
721class RadiusAttr_PMIP6_Visited_IPv4_Gateway(_RadiusAttrIPv4AddrVal):
722 """RFC 6572"""
723 val = 162
724
725
726# See IANA registry "RADIUS Types"
727_radius_attrs_values = {
728 # Service-Type
729 6:
730 {
731 1: "Login",
732 2: "Framed",
733 3: "Callback Login",
734 4: "Callback Framed",
735 5: "Outbound",
736 6: "Administrative",
737 7: "NAS Prompt",
738 8: "Authenticate Only",
739 9: "Callback NAS Prompt",
740 10: "Call Check",
741 11: "Callback Administrative",
742 12: "Voice",
743 13: "Fax",
744 14: "Modem Relay",
745 15: "IAPP-Register",
746 16: "IAPP-AP-Check",
747 17: "Authorize Only",
748 18: "Framed-Management",
749 19: "Additional-Authorization"
750 },
751
752 # Framed-Protocol
753 7:
754 {
755 1: "PPP",
756 2: "SLIP",
757 3: "AppleTalk Remote Access Protocol (ARAP)",
758 4: "Gandalf proprietary SingleLink/MultiLink protocol",
759 5: "Xylogics proprietary IPX/SLIP",
760 6: "X.75 Synchronous",
761 7: "GPRS PDP Context"
762 },
763
764 # Framed-Routing
765 10:
766 {
767 0: "None",
768 1: "Send routing packets",
769 2: "Listen for routing packets",
770 3: "Send and Listen"
771 },
772
773 # Framed-Compression
774 13:
775 {
776 0: "None",
777 1: "VJ TCP/IP header compression",
778 2: "IPX header compression",
779 3: "Stac-LZS compression"
780 },
781
782 # Login-Service
783 15:
784 {
785 0: "Telnet",
786 1: "Rlogin",
787 2: "TCP Clear",
788 3: "PortMaster (proprietary)",
789 4: "LAT",
790 5: "X25-PAD",
791 6: "X25-T3POS",
792 7: "Unassigned",
793 8: "TCP Clear Quiet (suppresses any NAS-generated connect string)"
794 },
795
796 # Termination-Action
797 29:
798 {
799 0: "Default",
800 1: "RADIUS-Request"
801 },
802
803 # Acct-Status-Type
804 40:
805 {
806 1: "Start",
807 2: "Stop",
808 3: "Interim-Update",
809 4: "Unassigned",
810 5: "Unassigned",
811 6: "Unassigned",
812 7: "Accounting-On",
813 8: "Accounting-Off",
814 9: "Tunnel-Start",
815 10: "Tunnel-Stop",
816 11: "Tunnel-Reject",
817 12: "Tunnel-Link-Start",
818 13: "Tunnel-Link-Stop",
819 14: "Tunnel-Link-Reject",
820 15: "Failed"
821 },
822
823 # Acct-Authentic
824 45:
825 {
826 1: "RADIUS",
827 2: "Local",
828 3: "Remote",
829 4: "Diameter"
830 },
831
832 # Acct-Terminate-Cause
833 49:
834 {
835 1: "User Request",
836 2: "Lost Carrier",
837 3: "Lost Service",
838 4: "Idle Timeout",
839 5: "Session Timeout",
840 6: "Admin Reset",
841 7: "Admin Reboot",
842 8: "Port Error",
843 9: "NAS Error",
844 10: "NAS Request",
845 11: "NAS Reboot",
846 12: "Port Unneeded",
847 13: "Port Preempted",
848 14: "Port Suspended",
849 15: "Service Unavailable",
850 16: "Callback",
851 17: "User Error",
852 18: "Host Request",
853 19: "Supplicant Restart",
854 20: "Reauthentication Failure",
855 21: "Port Reinitialized",
856 22: "Port Administratively Disabled",
857 23: "Lost Power",
858 },
859
860 # NAS-Port-Type
861 61:
862 {
863 0: "Async",
864 1: "Sync",
865 2: "ISDN Sync",
866 3: "ISDN Async V.120",
867 4: "ISDN Async V.110",
868 5: "Virtual",
869 6: "PIAFS",
870 7: "HDLC Clear Channel",
871 8: "X.25",
872 9: "X.75",
873 10: "G.3 Fax",
874 11: "SDSL - Symmetric DSL",
875 12: "ADSL-CAP - Asymmetric DSL, Carrierless Amplitude Phase Modulation", # noqa: E501
876 13: "ADSL-DMT - Asymmetric DSL, Discrete Multi-Tone",
877 14: "IDSL - ISDN Digital Subscriber Line",
878 15: "Ethernet",
879 16: "xDSL - Digital Subscriber Line of unknown type",
880 17: "Cable",
881 18: "Wireles - Other",
882 19: "Wireless - IEEE 802.11",
883 20: "Token-Ring",
884 21: "FDDI",
885 22: "Wireless - CDMA2000",
886 23: "Wireless - UMTS",
887 24: "Wireless - 1X-EV",
888 25: "IAPP",
889 26: "FTTP - Fiber to the Premises",
890 27: "Wireless - IEEE 802.16",
891 28: "Wireless - IEEE 802.20",
892 29: "Wireless - IEEE 802.22",
893 30: "PPPoA - PPP over ATM",
894 31: "PPPoEoA - PPP over Ethernet over ATM",
895 32: "PPPoEoE - PPP over Ethernet over Ethernet",
896 33: "PPPoEoVLAN - PPP over Ethernet over VLAN",
897 34: "PPPoEoQinQ - PPP over Ethernet over IEEE 802.1QinQ",
898 35: "xPON - Passive Optical Network",
899 36: "Wireless - XGP",
900 37: "WiMAX Pre-Release 8 IWK Function",
901 38: "WIMAX-WIFI-IWK: WiMAX WIFI Interworking",
902 39: "WIMAX-SFF: Signaling Forwarding Function for LTE/3GPP2",
903 40: "WIMAX-HA-LMA: WiMAX HA and or LMA function",
904 41: "WIMAX-DHCP: WIMAX DHCP service",
905 42: "WIMAX-LBS: WiMAX location based service",
906 43: "WIMAX-WVS: WiMAX voice service"
907 },
908
909 # Tunnel-Type
910 64:
911 {
912 1: "Point-to-Point Tunneling Protocol (PPTP)",
913 2: "Layer Two Forwarding (L2F)",
914 3: "Layer Two Tunneling Protocol (L2TP)",
915 4: "Ascend Tunnel Management Protocol (ATMP)",
916 5: "Virtual Tunneling Protocol (VTP)",
917 6: "IP Authentication Header in the Tunnel-mode (AH)",
918 7: "IP-in-IP Encapsulation (IP-IP)",
919 8: "Minimal IP-in-IP Encapsulation (MIN-IP-IP)",
920 9: "IP Encapsulating Security Payload in the Tunnel-mode (ESP)",
921 10: "Generic Route Encapsulation (GRE)",
922 11: "Bay Dial Virtual Services (DVS)",
923 12: "IP-in-IP Tunneling",
924 13: "Virtual LANs (VLAN)"
925 },
926
927 # Tunnel-Medium-Type
928 65:
929 {
930 1: "IPv4 (IP version 4)",
931 2: "IPv6 (IP version 6)",
932 3: "NSAP",
933 4: "HDLC (8-bit multidrop)",
934 5: "BBN 1822",
935 6: "802",
936 7: "E.163 (POTS)",
937 8: "E.164 (SMDS, Frame Relay, ATM)",
938 9: "F.69 (Telex)",
939 10: "X.121 (X.25, Frame Relay)",
940 11: "IPX",
941 12: "Appletalk",
942 13: "Decnet IV",
943 14: "Banyan Vine",
944 15: "E.164 with NSAP format subaddress"
945 },
946
947 # ARAP-Zone-Access
948 72:
949 {
950 1: "Only allow access to default zone",
951 2: "Use zone filter inclusively",
952 3: "Not used",
953 4: "Use zone filter exclusively"
954 },
955
956 # Prompt
957 76:
958 {
959 0: "No Echo",
960 1: "Echo"
961 },
962
963 # Error-Cause Attribute
964 101:
965 {
966 201: "Residual Session Context Removed",
967 202: "Invalid EAP Packet (Ignored)",
968 401: "Unsupported Attribute",
969 402: "Missing Attribute",
970 403: "NAS Identification Mismatch",
971 404: "Invalid Request",
972 405: "Unsupported Service",
973 406: "Unsupported Extension",
974 407: "Invalid Attribute Value",
975 501: "Administratively Prohibited",
976 502: "Request Not Routable (Proxy)",
977 503: "Session Context Not Found",
978 504: "Session Context Not Removable",
979 505: "Other Proxy Processing Error",
980 506: "Resources Unavailable",
981 507: "Request Initiated",
982 508: "Multiple Session Selection Unsupported",
983 509: "Location-Info-Required",
984 601: "Response Too Big"
985 },
986
987 # Operator Namespace Identifier - Attribute 126
988 126:
989 {
990 0x30: "TADIG",
991 0x31: "REALM",
992 0x32: "E212",
993 0x33: "ICC",
994 0xFF: "Reserved"
995 },
996
997 # Basic-Location-Policy-Rules
998 129:
999 {
1000 0: "Retransmission allowed",
1001 },
1002
1003 # Location-Capable
1004 131:
1005 {
1006 1: "CIVIC_LOCATION",
1007 2: "GEO_LOCATION",
1008 4: "USERS_LOCATION",
1009 8: "NAS_LOCATION"
1010 },
1011
1012 # Framed-Management-Protocol
1013 133:
1014 {
1015 1: "SNMP",
1016 2: "Web-based",
1017 3: "NETCONF",
1018 4: "FTP",
1019 5: "TFTP",
1020 6: "SFTP",
1021 7: "RCP",
1022 8: "SCP"
1023 },
1024
1025 # Management-Transport-Protection
1026 134:
1027 {
1028 1: "No-Protection",
1029 2: "Integrity-Protection",
1030 3: "Integrity-Confidentiality-Protection",
1031 },
1032}
1033
1034
1035class _RadiusAttrIntEnumVal(_SpecificRadiusAttr):
1036 """
1037 Implements a RADIUS attribute which value field is 4 bytes long integer.
1038 """
1039
1040 __slots__ = ["val"]
1041
1042 fields_desc = [
1043 ByteEnumField("type", 6, _radius_attribute_types),
1044 ByteField("len", 6),
1045 MultiEnumField(
1046 "value",
1047 0,
1048 _radius_attrs_values,
1049 depends_on=lambda p: p.type,
1050 fmt="I"
1051 )
1052 ]
1053
1054
1055class RadiusAttr_Service_Type(_RadiusAttrIntEnumVal):
1056 """RFC 2865"""
1057 val = 6
1058
1059
1060class RadiusAttr_Framed_Protocol(_RadiusAttrIntEnumVal):
1061 """RFC 2865"""
1062 val = 7
1063
1064
1065class RadiusAttr_Acct_Status_Type(_RadiusAttrIntEnumVal):
1066 """RFC 2866"""
1067 val = 40
1068
1069
1070class RadiusAttr_Acct_Authentic(_RadiusAttrIntEnumVal):
1071 """RFC 2866"""
1072 val = 45
1073
1074
1075class RadiusAttr_Acct_Terminate_Cause(_RadiusAttrIntEnumVal):
1076 """RFC 2866"""
1077 val = 49
1078
1079
1080class RadiusAttr_NAS_Port_Type(_RadiusAttrIntEnumVal):
1081 """RFC 2865"""
1082 val = 61
1083
1084#
1085# RADIUS attributes that are complex structures
1086#
1087
1088
1089class _EAPPacketField(PacketLenField):
1090 """
1091 Handles EAP-Message attribute value (the actual EAP packet).
1092 """
1093
1094 def m2i(self, pkt, m):
1095 ret = None
1096 eap_packet_len = struct.unpack("!H", m[2:4])[0]
1097 if eap_packet_len < 254:
1098 # If the EAP packet has not been fragmented, build a Scapy EAP
1099 # packet from the data.
1100 ret = EAP(m)
1101 else:
1102 ret = conf.raw_layer(m)
1103 return ret
1104
1105
1106class RadiusAttr_EAP_Message(RadiusAttribute):
1107 """
1108 Implements the "EAP-Message" attribute (RFC 3579).
1109 """
1110 name = "EAP-Message"
1111 match_subclass = True
1112 fields_desc = [
1113 ByteEnumField("type", 79, _radius_attribute_types),
1114 FieldLenField(
1115 "len",
1116 None,
1117 "value",
1118 "B",
1119 adjust=lambda pkt, x: x + 2
1120 ),
1121 _EAPPacketField("value", "", EAP, length_from=lambda p: p.len - 2)
1122 ]
1123
1124 def post_dissect(self, s):
1125 if not conf.contribs.get("radius", {}).get("auto-defrag", True):
1126 return s
1127 if isinstance(self.value, conf.raw_layer):
1128 # Defragment
1129 x = s
1130 buf = self.value.load
1131 while x and struct.unpack("!B", x[:1])[0] == 79:
1132 # Let's carefully avoid the infinite loop
1133 length = struct.unpack("!B", x[1:2])[0]
1134 if not length:
1135 return s
1136 buf, x = buf + x[2:length], x[length:]
1137 if length < 254:
1138 self.value = EAP(buf)
1139 return x
1140 return s
1141
1142
1143_radius_vendor_types = {
1144 # Microsoft (RFC 2548)
1145 311: {
1146 1: "MS-CHAP-Response",
1147 2: "MS-CHAP-Error",
1148 3: "MS-CHAP-CPW-1",
1149 4: "MS-CHAP-CPW-2",
1150 5: "MS-CHAP-LM-Enc-PW",
1151 6: "MS-CHAP-NT-Enc-PW",
1152 7: "MS-MPPE-Encryption-Policy",
1153 8: "MS-MPPE-Encryption-Type",
1154 9: "MS-RAS-Vendor",
1155 10: "MS-CHAP-Domain",
1156 11: "MS-CHAP-Challenge",
1157 12: "MS-CHAP-MPPE-Keys",
1158 13: "MS-BAP-Usage",
1159 14: "MS-Link-Utilization-Threshold",
1160 15: "MS-Link-Drop-Time-Limit",
1161 16: "MS-MPPE-Send-Key",
1162 17: "MS-MPPE-Recv-Key",
1163 18: "MS-RAS-Version",
1164 19: "MS-Old-ARAP-Password",
1165 20: "MS-New-ARAP-Password",
1166 21: "MS-ARAP-PW-Change-Reason",
1167 22: "MS-Filter",
1168 23: "MS-Acct-Auth-Type",
1169 24: "MS-Acct-EAP-Type",
1170 25: "MS-CHAP2-Response",
1171 26: "MS-CHAP2-Success",
1172 27: "MS-CHAP2-CPW",
1173 28: "MS-Primary-DNS-Server",
1174 29: "MS-Secondary-DNS-Server",
1175 30: "MS-Primary-NBNS-Server",
1176 31: "MS-Secondary-NBNS-Server",
1177 33: "MS-ARAP-Challenge",
1178 }
1179}
1180
1181
1182class _RadiusAttrVendorValue(Packet):
1183 """
1184 Used to register a 'value' vendor-specific
1185 """
1186 registered_vendor_value = collections.defaultdict(dict)
1187 VENDOR_ID = 0
1188 VENDOR_TYPE = 0
1189
1190 @classmethod
1191 def register_variant(cls):
1192 cls.registered_vendor_value[cls.VENDOR_ID][cls.VENDOR_TYPE] = cls
1193
1194
1195def _radius_vendor_cls(pkt):
1196 """
1197 Return the class that makes for a 'value' in the vendor attribute, or None.
1198 """
1199 if pkt.vendor_id not in _RadiusAttrVendorValue.registered_vendor_value:
1200 return None
1201 return _RadiusAttrVendorValue.registered_vendor_value[pkt.vendor_id].get(
1202 pkt.vendor_type,
1203 None,
1204 )
1205
1206
1207class _RadiusVendorValueField(PacketLenField):
1208 def m2i(self, pkt, s):
1209 return _radius_vendor_cls(pkt)(s, _parent=pkt)
1210
1211
1212class RadiusAttr_Vendor_Specific(RadiusAttribute):
1213 """
1214 Implements the "Vendor-Specific" attribute, as described in RFC 2865.
1215 """
1216
1217 name = "Vendor-Specific"
1218 match_subclass = True
1219 fields_desc = [
1220 ByteEnumField("type", 26, _radius_attribute_types),
1221 FieldLenField(
1222 "len",
1223 None,
1224 "value",
1225 "B",
1226 adjust=lambda pkt, x: len(pkt.value) + 8
1227 ),
1228 IntEnumField("vendor_id", 0, {
1229 311: "Microsoft",
1230 }),
1231 MultiEnumField(
1232 "vendor_type",
1233 0,
1234 _radius_vendor_types,
1235 depends_on=lambda p: p.vendor_id,
1236 fmt="B"
1237 ),
1238 FieldLenField(
1239 "vendor_len",
1240 None,
1241 "value",
1242 "B",
1243 adjust=lambda p, x: len(p.value) + 2
1244 ),
1245 MultipleTypeField(
1246 [
1247 (
1248 _RadiusVendorValueField("value", None, None,
1249 length_from=lambda p: p.vendor_len - 2),
1250 lambda pkt: _radius_vendor_cls(pkt) is not None
1251 )
1252 ],
1253 StrLenField("value", "", length_from=lambda p: p.vendor_len - 2),
1254 )
1255 ]
1256
1257
1258# See IANA RADIUS Packet Type Codes registry
1259_packet_codes = {
1260 1: "Access-Request",
1261 2: "Access-Accept",
1262 3: "Access-Reject",
1263 4: "Accounting-Request",
1264 5: "Accounting-Response",
1265 6: "Accounting-Status (now Interim Accounting)",
1266 7: "Password-Request",
1267 8: "Password-Ack",
1268 9: "Password-Reject",
1269 10: "Accounting-Message",
1270 11: "Access-Challenge",
1271 12: "Status-Server (experimental)",
1272 13: "Status-Client (experimental)",
1273 21: "Resource-Free-Request",
1274 22: "Resource-Free-Response",
1275 23: "Resource-Query-Request",
1276 24: "Resource-Query-Response",
1277 25: "Alternate-Resource-Reclaim-Request",
1278 26: "NAS-Reboot-Request",
1279 27: "NAS-Reboot-Response",
1280 28: "Reserved",
1281 29: "Next-Passcode",
1282 30: "New-Pin",
1283 31: "Terminate-Session",
1284 32: "Password-Expired",
1285 33: "Event-Request",
1286 34: "Event-Response",
1287 40: "Disconnect-Request",
1288 41: "Disconnect-ACK",
1289 42: "Disconnect-NAK",
1290 43: "CoA-Request",
1291 44: "CoA-ACK",
1292 45: "CoA-NAK",
1293 50: "IP-Address-Allocate",
1294 51: "IP-Address-Release",
1295 52: "Protocol-Error",
1296 250: "Experimental Use",
1297 251: "Experimental Use",
1298 252: "Experimental Use",
1299 253: "Experimental Use",
1300 254: "Reserved",
1301 255: "Reserved"
1302}
1303
1304
1305class Radius(Packet):
1306 """
1307 Implements a RADIUS packet (RFC 2865).
1308 """
1309
1310 name = "RADIUS"
1311 fields_desc = [
1312 ByteEnumField("code", 1, _packet_codes),
1313 ByteField("id", 0),
1314 FieldLenField(
1315 "len",
1316 None,
1317 "attributes",
1318 "H",
1319 adjust=lambda pkt, x: len(pkt.attributes) + 20
1320 ),
1321 XStrFixedLenField("authenticator", "", 16),
1322 PacketListField(
1323 "attributes",
1324 [],
1325 RadiusAttribute,
1326 length_from=lambda pkt: pkt.len - 20
1327 )
1328 ]
1329
1330 def compute_authenticator(self, packed_request_auth, shared_secret):
1331 """
1332 Computes the authenticator field (RFC 2865 - Section 3)
1333 """
1334
1335 data = prepare_packed_data(self, packed_request_auth)
1336 radius_mac = hashlib.md5(data + shared_secret)
1337 return radius_mac.digest()
1338
1339 def post_build(self, p, pay):
1340 p += pay
1341 length = self.len
1342 if length is None:
1343 length = len(p)
1344 p = p[:2] + struct.pack("!H", length) + p[4:]
1345 return p
1346
1347 def mysummary(self):
1348 extra = ""
1349 if self.code == 1:
1350 # Access-Request
1351 attrs = {
1352 (
1353 (x.vendor_id, x.vendor_type)
1354 if RadiusAttr_Vendor_Specific in x else
1355 x.type
1356 ): x
1357 for x in self.attributes
1358 if isinstance(x, RadiusAttribute)
1359 }
1360 # Log additional attributes
1361 if 1 in attrs:
1362 extra += "User:'%s' " % attrs[1].value.decode(errors="ignore")
1363 # Try to detect the logon algo
1364 if 2 in attrs:
1365 extra += "PAP"
1366 elif 3 in attrs:
1367 extra += "CHAP"
1368 elif 79 in attrs:
1369 extra += "EAP"
1370 elif (311, 1) in attrs:
1371 extra += "MS-CHAP"
1372 elif (311, 25) in attrs:
1373 extra += "MS-CHAP2"
1374 if extra:
1375 extra = " (%s)" % extra.strip()
1376 return self.sprintf("RADIUS %code%") + extra
1377
1378
1379bind_bottom_up(UDP, Radius, sport=1812)
1380bind_bottom_up(UDP, Radius, dport=1812)
1381bind_bottom_up(UDP, Radius, sport=1813)
1382bind_bottom_up(UDP, Radius, dport=1813)
1383bind_bottom_up(UDP, Radius, sport=3799)
1384bind_bottom_up(UDP, Radius, dport=3799)
1385bind_layers(UDP, Radius, sport=1812, dport=1812)
1386
1387
1388# MS-CHAP2
1389
1390# RFC 2548 sect 2.3.2
1391
1392class MS_CHAP2_Response(_RadiusAttrVendorValue):
1393 VENDOR_ID = 311
1394 VENDOR_TYPE = 25
1395 fields_desc = [
1396 ByteField("Ident", 0),
1397 ByteField("Flags", 0),
1398 XStrFixedLenField("PeerChallenge", b"", length=16),
1399 XStrFixedLenField("Reserved", b"", length=8),
1400 XStrFixedLenField("Response", b"", length=24),
1401 ]
1402
1403
1404# RFC 2548 sect 2.3.3
1405
1406class MS_CHAP2_Success(_RadiusAttrVendorValue):
1407 VENDOR_ID = 311
1408 VENDOR_TYPE = 26
1409 fields_desc = [
1410 ByteField("Ident", 0),
1411 StrFixedLenField("String", b"", length=42),
1412 ]
1413
1414
1415# RFC 2548 sect 2.1.5
1416
1417class MS_CHAP_Error(_RadiusAttrVendorValue):
1418 VENDOR_ID = 311
1419 VENDOR_TYPE = 2
1420 fields_desc = [
1421 ByteField("Ident", 0),
1422 StrField("String", b""),
1423 ]
1424
1425
1426# RFC 2548 sect 2.1.4
1427
1428class MS_CHAP_Domain(_RadiusAttrVendorValue):
1429 VENDOR_ID = 311
1430 VENDOR_TYPE = 10
1431 fields_desc = [
1432 ByteField("Ident", 0),
1433 StrField("String", b""),
1434 ]
1435
1436
1437def MS_CHAP2_GenerateNTResponse(AuthenticatorChallenge, PeerChallenge,
1438 UserName, HashNT):
1439 """
1440 RFC2759 sect 8.1
1441 """
1442 Challenge = MS_CHAP2_ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName)
1443 PasswordHash = HashNT
1444 return MS_CHAP2_ChallengeResponse(Challenge, PasswordHash)
1445
1446
1447def MS_CHAP2_ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName):
1448 """
1449 rfc 2759 sect 8.2
1450 """
1451 UserName = UserName.split(b"\\")[-1] # Strip domain if present
1452 return Hash_SHA().digest(PeerChallenge + AuthenticatorChallenge + UserName)[:8]
1453
1454
1455def MS_CHAP2_ChallengeResponse(Challenge, PasswordHash):
1456 """
1457 rfc 2759 sect 8.5
1458 """
1459 ZPasswordHash = int.from_bytes(
1460 PasswordHash + b"\x00" * (-len(PasswordHash) % 21),
1461 "big",
1462 )
1463
1464 # Add !FAKE! DES parity bits because cryptography requires them (then drops them)
1465 ZPasswordHashParity = b""
1466 for _ in range(24):
1467 val, ZPasswordHash = (ZPasswordHash & 0x7F), (ZPasswordHash >> 7)
1468 ZPasswordHashParity = struct.pack("B", val << 1) + ZPasswordHashParity
1469
1470 return (
1471 Cipher_DES_ECB(ZPasswordHashParity[0:8]).encrypt(Challenge) +
1472 Cipher_DES_ECB(ZPasswordHashParity[8:16]).encrypt(Challenge) +
1473 Cipher_DES_ECB(ZPasswordHashParity[16:24]).encrypt(Challenge)
1474 )
1475
1476
1477def MS_CHAP2_GenerateAuthenticatorResponse(HashNT,
1478 NTResponse,
1479 PeerChallenge,
1480 AuthenticatorChallenge,
1481 UserName):
1482 """
1483 rfc 2759 sect 8.7
1484 """
1485 Magic1 = bytes(bytearray([
1486 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
1487 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
1488 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
1489 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74,
1490 ]))
1491 Magic2 = bytes(bytearray([
1492 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
1493 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
1494 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
1495 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
1496 0x6E
1497 ]))
1498 PasswordHash = HashNT
1499 PasswordHashHash = Hash_MD4().digest(PasswordHash)
1500
1501 Digest = Hash_SHA().digest(PasswordHashHash + NTResponse + Magic1)
1502
1503 Challenge = MS_CHAP2_ChallengeHash(
1504 PeerChallenge,
1505 AuthenticatorChallenge,
1506 UserName,
1507 )
1508
1509 return Hash_SHA().digest(Digest + Challenge + Magic2)
1510
1511
1512# Answering machine
1513
1514class RadiusAuthType(enum.Enum):
1515 MS_CHAP_V2 = enum.auto()
1516 EAP = enum.auto()
1517
1518
1519@crypto_validator
1520class Radius_am(AnsweringMachine):
1521 function_name = "radiusd"
1522 filter = "udp and port 1812"
1523 send_function = staticmethod(send)
1524 send_options_list = ["inter", "verbose"]
1525
1526 def parse_options(self,
1527 secret,
1528 IDENTITIES=None,
1529 IDENTITIES_MSCHAPv2=None,
1530 servicetype=None,
1531 mschapdomain=None,
1532 extra_attributes=[]):
1533 """
1534 This provides a tiny RADIUS daemon that answers Access-Request messages.
1535 This can be used while setting up a Cisco switch for instance.
1536
1537 Demo::
1538
1539 >>> radiusd(secret="SECRET", iface="lo", IDENTITIES={"user": "password"})
1540 $ echo "Message-Authenticator=0x00,User-Name=user,\\
1541 User-Password=password" | radclient -P udp 127.0.0.1 auth -F SECRET
1542
1543 :param secret: the server's secret
1544 :param IDENTITIES: the identities in format {"username": b"password"}
1545 :param IDENTITIES_MSCHAPv2: the MsCHAPv2 identities in format
1546 {"username": b"HashNT"}. The HashNT can be obtained
1547 using MD4le(). If IDENTITIES is provided, this will be calculated.
1548 :param servicetype: the Service-Type to answer.
1549 :param mschapdomain: the MS-CHAP-DOMAIN to answer if MS-CHAP* is used.
1550 :param extra_attributes: a list of extra Radius attributes
1551 """
1552 self.secret = bytes_encode(secret)
1553 self.servicetype = servicetype
1554 self.mschapdomain = mschapdomain
1555 self.extra_attributes = extra_attributes
1556 if not IDENTITIES:
1557 IDENTITIES = {}
1558 if IDENTITIES_MSCHAPv2 is None and IDENTITIES:
1559 IDENTITIES_MSCHAPv2 = {
1560 user: MD4le(pwd)
1561 for user, pwd in IDENTITIES.items()
1562 }
1563 self.IDENTITIES = {
1564 user: bytes_encode(pwd)
1565 for user, pwd in IDENTITIES.items()
1566 }
1567 self.IDENTITIES_MSCHAPv2 = IDENTITIES_MSCHAPv2
1568
1569 def is_request(self, req):
1570 # Only match Access-Request
1571 return Radius in req and req[Radius].code == 1
1572
1573 def print_reply(self, req, reply):
1574 print("%s / %s -> %s" % (
1575 reply[IP].dst,
1576 req[Radius].summary(),
1577 (
1578 conf.color_theme.fail
1579 if reply.code != 2 else
1580 conf.color_theme.success
1581 )(reply.sprintf("%Radius.code%")),
1582 ))
1583
1584 def make_reply(self, req):
1585 resp = req
1586
1587 # Basic response
1588 resp = (
1589 IP(src=req[IP].dst, dst=req[IP].src) /
1590 UDP(sport=req[UDP].dport, dport=req[UDP].sport)
1591 )
1592
1593 # Sort attributes for quick access
1594 attrs = {
1595 (
1596 (x.vendor_id, x.vendor_type)
1597 if RadiusAttr_Vendor_Specific in x else
1598 x.type
1599 ): x
1600 for x in req.attributes
1601 }
1602
1603 # Build Radius response
1604 rad = Radius(code=2, id=req[Radius].id)
1605
1606 # Process various authentication methods
1607 try:
1608 if 2 in attrs:
1609 # PAP
1610 if not self.IDENTITIES:
1611 raise Scapy_Exception(
1612 "Missing IDENTITIES for User-Password auth ! Assuming OK."
1613 )
1614
1615 UserName = attrs[1].value
1616 KnownPassword = self.IDENTITIES.get(UserName.decode(), None)
1617 UserPassword = attrs[2].decrypt(
1618 req,
1619 self.secret,
1620 )
1621
1622 if KnownPassword is None:
1623 log_runtime.warning("Couldn't find user '%s'" % UserName.decode())
1624 rad.code = 3
1625 elif UserPassword != KnownPassword:
1626 log_runtime.warning(
1627 "Bad password for user '%s'" % UserName.decode()
1628 )
1629 rad.code = 3
1630 elif 79 in attrs:
1631 # EAP-Message is used
1632 raise Scapy_Exception(
1633 "EAP as a Radius auth method is not implemented !"
1634 )
1635 elif (311, 25) in attrs:
1636 # MS-CHAP2
1637 if not self.IDENTITIES_MSCHAPv2:
1638 raise Scapy_Exception("Missing IDENTITIES_MSCHAPv2 for MsChapV2 !")
1639
1640 response = attrs[(311, 25)].value
1641 try:
1642 AuthenticatorChallenge = attrs[(311, 11)].value # CHAP-Challenge
1643 except KeyError:
1644 raise Scapy_Exception("Missing CHAP-Challenge !")
1645
1646 UserName = attrs[1].value
1647 HashNT = self.IDENTITIES_MSCHAPv2.get(UserName.decode(), None)
1648
1649 # 1. Check the client-provided NTResponse
1650 if HashNT is None:
1651 log_runtime.warning("Couldn't find user '%s'" % UserName.decode())
1652 rad.code = 3
1653 elif MS_CHAP2_GenerateNTResponse(
1654 AuthenticatorChallenge,
1655 response.PeerChallenge,
1656 UserName,
1657 HashNT) != response.Response:
1658 log_runtime.warning(
1659 "Bad MS-CHAP2-NTResponse for user '%s' !" % UserName.decode()
1660 )
1661 rad.code = 3
1662
1663 # Did the auth failed?
1664 if rad.code == 3:
1665 rad.attributes.append(
1666 RadiusAttr_Vendor_Specific(
1667 vendor_id="Microsoft",
1668 vendor_type=2,
1669 value=MS_CHAP_Error(
1670 Ident=response.Ident,
1671 String="E=691 R=0 V=3",
1672 ),
1673 )
1674 )
1675 else:
1676 # 2. Build the response 'success' response
1677 auth_string = MS_CHAP2_GenerateAuthenticatorResponse(
1678 HashNT,
1679 response.Response,
1680 response.PeerChallenge,
1681 AuthenticatorChallenge,
1682 UserName,
1683 )
1684 rad.attributes.append(
1685 RadiusAttr_Vendor_Specific(
1686 vendor_id=311,
1687 vendor_type="MS-CHAP2-Success",
1688 value=MS_CHAP2_Success(
1689 Ident=response.Ident,
1690 String="S=%s" % auth_string.hex().upper()
1691 )
1692 )
1693 )
1694 if self.mschapdomain is not None:
1695 rad.attributes.append(
1696 RadiusAttr_Vendor_Specific(
1697 vendor_id=311,
1698 vendor_type="MS-CHAP-Domain",
1699 value=MS_CHAP_Domain(
1700 Ident=response.Ident,
1701 String=self.mschapdomain,
1702 )
1703 )
1704 )
1705 else:
1706 raise Scapy_Exception(
1707 "Authentication method not provided or unsupported !"
1708 )
1709 except Scapy_Exception as ex:
1710 # display a warning
1711 log_runtime.warning(str(ex))
1712
1713 # Add additional records if it's an accept
1714 if rad.code == 2:
1715 if self.servicetype is not None:
1716 rad.attributes.append(
1717 RadiusAttr_Service_Type(value=self.servicetype)
1718 )
1719
1720 rad.attributes.extend(self.extra_attributes)
1721
1722 # Add and compute message authenticator
1723 mauth = RadiusAttr_Message_Authenticator()
1724 rad.attributes.insert(0, mauth)
1725 mauth.value = mauth.compute_message_authenticator(
1726 rad,
1727 req.authenticator,
1728 self.secret,
1729 )
1730
1731 # Add global authenticator
1732 rad.authenticator = rad.compute_authenticator(req.authenticator, self.secret)
1733
1734 # Final packet
1735 return resp / rad