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