From f3c0d17541a56a844f532a111118344882e39a2f Mon Sep 17 00:00:00 2001 From: Vlad Grigorescu Date: Tue, 10 Dec 2013 22:09:16 -0500 Subject: [PATCH] Basic RADIUS functionality implemented. --- scripts/base/init-bare.bro | 17 + scripts/base/protocols/radius/__load__.bro | 1 - scripts/base/protocols/radius/consts.bro | 306 ------------------ scripts/base/protocols/radius/dpd.sig | 7 - scripts/base/protocols/radius/main.bro | 190 +++++------ src/analyzer/protocol/radius/events.bif | 9 +- .../protocol/radius/radius-analyzer.pac | 39 ++- 7 files changed, 140 insertions(+), 429 deletions(-) delete mode 100644 scripts/base/protocols/radius/dpd.sig diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index 9f8c9f42ac..d956cf6a2d 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -2762,6 +2762,23 @@ export { name: string &optional; } &log; } +module RADIUS; + +export { + type RADIUS::AttributeList: vector of string; + type RADIUS::Attributes: table[count] of RADIUS::AttributeList; + + type RADIUS::Message: record { + ## The type of message (Access-Request, Access-Accept, etc.) + code : count; + ## The transaction ID + trans_id : count; + ## The "authenticator" string + authenticator : string; + ## Any attributes + attributes : RADIUS::Attributes &optional; + }; +} module GLOBAL; @load base/bif/event.bif diff --git a/scripts/base/protocols/radius/__load__.bro b/scripts/base/protocols/radius/__load__.bro index 96bf67a57f..fb802a40a5 100644 --- a/scripts/base/protocols/radius/__load__.bro +++ b/scripts/base/protocols/radius/__load__.bro @@ -1,3 +1,2 @@ # Generated by binpac_quickstart @load ./main -#@load-sigs ./dpd.sig \ No newline at end of file diff --git a/scripts/base/protocols/radius/consts.bro b/scripts/base/protocols/radius/consts.bro index 6231a25631..d9c78f58ad 100644 --- a/scripts/base/protocols/radius/consts.bro +++ b/scripts/base/protocols/radius/consts.bro @@ -229,309 +229,3 @@ const framed_protocol_types: table[count] of string = { [6] = "X.75 Synchronous" } &default=function(i: count): string { return fmt("unknown-%d", i); }; -const vendor_9_types: table[count] of string = { - [1] = "Cisco-AVPair", - [2] = "Cisco-NAS-Port", - [3] = "Cisco-Fax-Account-Id-Origin", - [4] = "Cisco-Fax-Msg-Id", - [5] = "Cisco-Fax-Pages", - [6] = "Cisco-Fax-Coverpage-Flag", - [7] = "Cisco-Fax-Modem-Time", - [8] = "Cisco-Fax-Connect-Speed", - [9] = "Cisco-Fax-Recipient-Count", - [10] = "Cisco-Fax-Process-Abort-Flag", - [11] = "Cisco-Fax-Dsn-Address", - [12] = "Cisco-Fax-Dsn-Flag", - [13] = "Cisco-Fax-Mdn-Address", - [14] = "Cisco-Fax-Mdn-Flag", - [15] = "Cisco-Fax-Auth-Status", - [16] = "Cisco-Email-Server-Address", - [17] = "Cisco-Email-Server-Ack-Flag", - [18] = "Cisco-Gateway-Id", - [19] = "Cisco-Call-Type", - [20] = "Cisco-Port-Used", - [21] = "Cisco-Abort-Cause", - [23] = "Cisco-h323-remote-address", - [24] = "Cisco-h323-conf-id", - [25] = "Cisco-h323-setup-time", - [26] = "Cisco-h323-call-origin", - [27] = "Cisco-h323-call-type", - [28] = "Cisco-h323-connect-time", - [29] = "Cisco-h323-disconnect-time", - [30] = "Cisco-h323-disconnect-cause", - [31] = "Cisco-h323-voice-quality", - [33] = "Cisco-h323-gw-id", - [35] = "Cisco-h323-incoming-conn-id", - [37] = "Cisco-Policy-Up", - [38] = "Cisco-Policy-Down", - [100] = "Cisco-sip-conf-id", - [101] = "Cisco-h323-credit-amount", - [102] = "Cisco-h323-credit-time", - [103] = "Cisco-h323-return-code", - [104] = "Cisco-h323-prompt-id", - [105] = "Cisco-h323-day-and-time", - [106] = "Cisco-h323-redirect-number", - [107] = "Cisco-h323-preferred-lang", - [108] = "Cisco-h323-redirect-ip-addr", - [109] = "Cisco-h323-billing-model", - [110] = "Cisco-h323-currency", - [111] = "Cisco-subscriber", - [112] = "Cisco-gw-rxd-cdn", - [113] = "Cisco-gw-final-xlated-cdn", - [114] = "Cisco-remote-media-address", - [115] = "Cisco-release-source", - [116] = "Cisco-gw-rxd-cgn", - [117] = "Cisco-gw-final-xlated-cgn", - [141] = "Cisco-call-id", - [142] = "Cisco-session-protocol", - [143] = "Cisco-method", - [144] = "Cisco-prev-hop-via", - [145] = "Cisco-prev-hop-ip", - [146] = "Cisco-incoming-req-uri", - [147] = "Cisco-outgoing-req-uri", - [148] = "Cisco-next-hop-ip", - [149] = "Cisco-next-hop-dn", - [150] = "Cisco-sip-hdr", - [187] = "Cisco-Multilink-ID", - [188] = "Cisco-Num-In-Multilink", - [190] = "Cisco-Pre-Input-Octets", - [191] = "Cisco-Pre-Output-Octets", - [192] = "Cisco-Pre-Input-Packets", - [193] = "Cisco-Pre-Output-Packets", - [194] = "Cisco-Maximum-Time", - [195] = "Cisco-Disconnect-Cause", - [197] = "Cisco-Data-Rate", - [198] = "Cisco-PreSession-Time", - [208] = "Cisco-PW-Lifetime", - [209] = "Cisco-IP-Direct", - [210] = "Cisco-PPP-VJ-Slot-Comp", - [212] = "Cisco-PPP-Async-Map", - [217] = "Cisco-IP-Pool-Definition", - [218] = "Cisco-Assign-IP-Pool", - [228] = "Cisco-Route-IP", - [233] = "Cisco-Link-Compression", - [234] = "Cisco-Target-Util", - [235] = "Cisco-Maximum-Channels", - [242] = "Cisco-Data-Filter", - [243] = "Cisco-Call-Filter", - [244] = "Cisco-Idle-Limit", - [249] = "Cisco-Subscriber-Password", - [250] = "Cisco-Account-Info", - [251] = "Cisco-Service-Info", - [252] = "Cisco-Command-Code", - [253] = "Cisco-Xmit-Rate" -} &default=function(i: count): string { return fmt("Cisco-unknown-%d", i); }; - -const vendor_255_types: table[count] of string = { - [1] = "CVPN5000-Tunnel-Throughput", - [2] = "CVPN5000-Client-Assigned-IP", - [3] = "CVPN5000-Client-Real-IP", - [4] = "CVPN5000-VPN-GroupInfo", - [5] = "CVPN5000-VPN-Password", - [6] = "CVPN5000-Echo", - [7] = "CVPN5000-Client-Assigned-IPX" -} &default=function(i: count): string { return fmt("CVPN5000-unknown-%d", i); }; - -const vendor_311_types: table[count] of string = { - [1] = "MS-CHAP-Response", - [2] = "MS-CHAP-Error", - [3] = "MS-CHAP-CPW-1", - [4] = "MS-CHAP-CPW-2", - [5] = "MS-CHAP-LM-Enc-PW", - [6] = "MS-CHAP-NT-Enc-PW", - [7] = "MS-MPPE-Encryption-Policy", - [8] = "MS-MPPE-Encryption-Types", - [9] = "MS-RAS-Vendor", - [10] = "MS-CHAP-Domain", - [11] = "MS-CHAP-Challenge", - [12] = "MS-CHAP-MPPE-Keys", - [13] = "MS-BAP-Usage", - [14] = "MS-Link-Utilization-Threshold", - [15] = "MS-Link-Drop-Time-Limit", - [16] = "MS-MPPE-Send-Key", - [17] = "MS-MPPE-Recv-Key", - [18] = "MS-RAS-Version", - [19] = "MS-Old-ARAP-Password", - [20] = "MS-New-ARAP-Password", - [21] = "MS-ARAP-PW-Change-Reason", - [22] = "MS-Filter", - [23] = "MS-Acct-Auth-Type", - [24] = "MS-Acct-EAP-Type", - [25] = "MS-CHAP2-Response", - [26] = "MS-CHAP2-Success", - [27] = "MS-CHAP2-CPW", - [28] = "MS-Primary-DNS-Server", - [29] = "MS-Secondary-DNS-Server", - [30] = "MS-Primary-NBNS-Server", - [31] = "MS-Secondary-NBNS-Server", - [34] = "MS-RAS-Client-Name", - [35] = "MS-RAS-Client-Version", - [36] = "MS-Quarantine-IPFilter", - [37] = "MS-Quarantine-Session-Timeout", - [40] = "MS-User-Security-Identity", - [41] = "MS-Identity-Type", - [42] = "MS-Service-Class", - [44] = "MS-Quarantine-User-Class", - [45] = "MS-Quarantine-State", - [46] = "MS-Quarantine-Grace-Time", - [47] = "MS-Network-Access-Server-Type", - [48] = "MS-AFW-Zone", - [49] = "MS-AFW-Protection-Level", - [50] = "MS-Machine-Name", - [51] = "MS-IPv6-Filter", - [52] = "MS-IPv4-Remediation-Servers", - [53] = "MS-IPv6-Remediation-Servers", - [54] = "MS-RNAP-Not-Quarantine-Capable", - [55] = "MS-Quarantine-SOH", - [56] = "MS-RAS-Correlation", - [57] = "MS-Extended-Quarantine-State", - [58] = "MS-HCAP-User-Groups", - [59] = "MS-HCAP-Location-Group-Name", - [60] = "MS-HCAP-User-Name", - [61] = "MS-User-IPv4-Address", - [62] = "MS-User-IPv6-Address", - [63] = "MS-TSG-Device-Redirection" -} &default=function(i: count): string { return fmt("MS-unknown-%d", i); }; - -const vendor_3076_types: table[count] of string = { - [1] = "CVPN3000-Access-Hours", - [2] = "CVPN3000-Simultaneous-Logins", - [3] = "CVPN3000-Min-Password-Length", - [4] = "CVPN3000-Allow-Alpha-Only-Passwords", - [5] = "CVPN3000-Primary-DNS", - [6] = "CVPN3000-Secondary-DNS", - [7] = "CVPN3000-Primary-WINS", - [8] = "CVPN3000-Secondary-WINS", - [9] = "CVPN3000-SEP-Card-Assignment", - [10] = "CVPN3000-Priority-On-SEP", - [11] = "CVPN3000-Tunneling-Protocols", - [12] = "CVPN3000-IPSec-Sec-Association", - [13] = "CVPN3000-IPSec-Authentication", - [15] = "CVPN3000-IPSec-Banner1", - [16] = "CVPN3000-IPSec-Allow-Passwd-Store", - [17] = "CVPN3000-Use-Client-Address", - [18] = "CVPN3000-PPTP-Min-Auth-Protocol", - [19] = "CVPN3000-L2TP-Min-Auth-Protocol", - [20] = "CVPN3000-PPTP-Encryption", - [21] = "CVPN3000-L2TP-Encryption", - [22] = "CVPN3000-Auth-Server-Type", - [23] = "CVPN3000-Auth-Server-Password", - [24] = "CVPN3000-Request-Auth-Vector", - [25] = "CVPN3000-IPSec-LTL-Keepalives", - [26] = "CVPN3000-IPSec-Group-Name", - [27] = "CVPN3000-IPSec-Split-Tunnel-List", - [28] = "CVPN3000-IPSec-Default-Domain", - [29] = "CVPN3000-IPSec-Split-DNS-Names", - [30] = "CVPN3000-IPSec-Tunnel-Type", - [31] = "CVPN3000-IPSec-Mode-Config", - [32] = "CVPN3000-Auth-Server-Priority", - [33] = "CVPN3000-IPSec-User-Group-Lock", - [34] = "CVPN3000-IPSec-Over-UDP", - [35] = "CVPN3000-IPSec-Over-UDP-Port", - [36] = "CVPN3000-IPSec-Banner2", - [37] = "CVPN3000-PPTP-MPPC-Compression", - [38] = "CVPN3000-L2TP-MPPC-Compression", - [39] = "CVPN3000-IPSec-IP-Compression", - [40] = "CVPN3000-IPSec-IKE-Peer-ID-Check", - [41] = "CVPN3000-IKE-Keep-Alives", - [42] = "CVPN3000-IPSec-Auth-On-Rekey", - [45] = "CVPN3000-Reqrd-Client-Fw-Vendor-Code", - [46] = "CVPN3000-Reqrd-Client-Fw-Product-Code", - [47] = "CVPN3000-Reqrd-Client-Fw-Description", - [48] = "CVPN3000-Require-HW-Client-Auth", - [49] = "CVPN3000-Require-Individual-User-Auth", - [50] = "CVPN3000-Authd-User-Idle-Timeout", - [51] = "CVPN3000-Cisco-IP-Phone-Bypass", - [52] = "CVPN3000-User-Auth-Server-Name", - [53] = "CVPN3000-User-Auth-Server-Port", - [54] = "CVPN3000-User-Auth-Server-Secret", - [55] = "CVPN3000-IPSec-Split-Tunneling-Policy", - [56] = "CVPN3000-IPSec-Reqrd-Client-Fw-Cap", - [57] = "CVPN3000-IPSec-Client-Fw-Filter-Name", - [58] = "CVPN3000-IPSec-Client-Fw-Filter-Opt", - [59] = "CVPN3000-IPSec-Backup-Servers", - [60] = "CVPN3000-IPSec-Backup-Server-List", - [61] = "CVPN3000-DHCP-Network-Scope", - [62] = "CVPN3000-MS-Client-Icpt-DHCP-Conf-Msg", - [63] = "CVPN3000-MS-Client-Subnet-Mask", - [64] = "CVPN3000-Allow-Network-Extension-Mode", - [65] = "CVPN3000-IPSec-Authorization-Type", - [66] = "CVPN3000-IPSec-Authorization-Required", - [67] = "CVPN3000-IPSec-DN-Field", - [68] = "CVPN3000-IPSec-Confidence-Level", - [69] = "CVPN3000-WebVPN-Content-Filter", - [70] = "CVPN3000-WebVPN-Enable-functions", - [74] = "CVPN3000-WebVPN-Exchange-Addr", - [75] = "CVPN3000-LEAP-Bypass", - [78] = "CVPN3000-WebVPN-Exchange-NETBIOS-name", - [79] = "CVPN3000-Port-Forwarding-Name", - [80] = "CVPN3000-IE-Proxy-Server", - [81] = "CVPN3000-IE-Proxy-Server-Policy", - [82] = "CVPN3000-IE-Proxy-Exception-List", - [83] = "CVPN3000-IE-Proxy-Bypass-Local", - [84] = "CVPN3000-IKE-Keepalive-Retry-Interval", - [88] = "CVPN3000-Perfect-Forward-Secrecy-Enable", - [89] = "CVPN3000-NAC-Enable", - [90] = "CVPN3000-NAC-Status-Query-Timer", - [91] = "CVPN3000-NAC-Revalidation-Timer", - [92] = "CVPN3000-NAC-Default-ACL", - [93] = "CVPN3000-WebVPN-URL-Entry-Enable", - [94] = "CVPN3000-WebVPN-File-Access-Enable", - [95] = "CVPN3000-WebVPN-File-Svr-Entry-Enable", - [96] = "CVPN3000-WebVPN-File-Svr-Brwsing-Enable", - [97] = "CVPN3000-WebVPN-Port-Forwarding-Enable", - [98] = "CVPN3000-WebVPN-Outlook-Exch-Proxy-Enb", - [99] = "CVPN3000-WebVPN-Port-Fwding-HTTP-Proxy", - [100] = "CVPN3000-WebVPN-Auto-Applet-Downld-Enb", - [101] = "CVPN3000-WebVPN-Citrix-Metaframe-Enable", - [102] = "CVPN3000-WebVPN-Apply-ACL", - [103] = "CVPN3000-WebVPN-SSL-VPN-Client-Enable", - [104] = "CVPN3000-WebVPN-SSL-VPN-Client-Required", - [105] = "CVPN3000-WebVPN-SSL-VPN-Client-Keep-Ins", - [128] = "CVPN3000-Partition-Primary-DHCP", - [129] = "CVPN3000-Partition-Secondary-DHCP", - [131] = "CVPN3000-Partition-Premise-Router", - [132] = "CVPN3000-Partition-Max-Sessions", - [133] = "CVPN3000-Partition-Mobile-IP-Key", - [134] = "CVPN3000-Partition-Mobile-IP-Address", - [135] = "CVPN3000-Partition-Mobile-IP-SPI", - [136] = "CVPN3000-Strip-Realm", - [137] = "CVPN3000-Group-Name" -} &default=function(i: count): string { return fmt("CPNV3000-unknown-%d", i); }; - -const vendor_14823_types: table[count] of string = { - [1] = "Aruba-User-Role", - [2] = "Aruba-User-Vlan", - [3] = "Aruba-Priv-Admin-User", - [4] = "Aruba-Admin-Role", - [5] = "Aruba-Essid-Name", - [6] = "Aruba-Location-Id", - [7] = "Aruba-Port-Identifier", - [8] = "Aruba-MMS-User-Template", - [9] = "Aruba-Named-User-Vlan", - [10] = "Aruba-AP-Group", - [11] = "Aruba-Framed-IPv6-Address", - [12] = "Aruba-Device-Type", - [13] = "Aruba-AP-Name", - [14] = "Aruba-No-DHCP-Fingerprint", - [15] = "Aruba-Mdps-Device-Udid", - [16] = "Aruba-Mdps-Device-Imei", - [17] = "Aruba-Mdps-Device-Iccid", - [18] = "Aruba-Mdps-Max-Devices", - [19] = "Aruba-Mdps-Device-Name", - [20] = "Aruba-Mdps-Device-Product", - [21] = "Aruba-Mdps-Device-Version", - [22] = "Aruba-Mdps-Device-Serial", - [23] = "Aruba-CPPM-Role", - [24] = "Aruba-AirGroup-User-Name", - [25] = "Aruba-AirGroup-Shared-User", - [26] = "Aruba-AirGroup-Shared-Role", - [27] = "Aruba-AirGroup-Device-Type", - [28] = "Aruba-Auth-Survivability", - [29] = "Aruba-AS-User-Name", - [30] = "Aruba-AS-Credential-Hash", - [31] = "Aruba-WorkSpace-App-Name", - [32] = "Aruba-Mdps-Provisioning-Settings", - [33] = "Aruba-Mdps-Device-Profile" -} &default=function(i: count): string { return fmt("Aruba-unknown-%d", i); }; - diff --git a/scripts/base/protocols/radius/dpd.sig b/scripts/base/protocols/radius/dpd.sig deleted file mode 100644 index d32ba49771..0000000000 --- a/scripts/base/protocols/radius/dpd.sig +++ /dev/null @@ -1,7 +0,0 @@ -# Generated by binpac_quickstart - -signature dpd_radius { - ip-proto == udp - # TODO: payload /^RADIUS/ - enable "radius" -} \ No newline at end of file diff --git a/scripts/base/protocols/radius/main.bro b/scripts/base/protocols/radius/main.bro index 0d239dcb64..0b73ecc257 100644 --- a/scripts/base/protocols/radius/main.bro +++ b/scripts/base/protocols/radius/main.bro @@ -11,19 +11,51 @@ export { type Info: record { ## Timestamp for when the event happened. - ts: time &log; + ts : time &log; ## Unique ID for the connection. - uid: string &log; + uid : string &log; ## The connection's 4-tuple of endpoint addresses/ports. - id: conn_id &log; - msg_type: string &log; + id : conn_id &log; + ## The username, if present + username : string &log &optional; + ## MAC address, if present + mac : string &log &optional; + ## Remote IP address, if present + remote_ip : addr &log &optional; + ## Connect info, if present + connect_info : string &log &optional; + ## Successful or failed authentication + result : string &log &optional; + ## Whether this has already been logged and can be ignored + logged : bool &optional; + }; + ## The amount of time we wait for an authentication response before + ## expiring it. + const expiration_interval = 10secs &redef; + + ## Logs an authentication attempt if we didn't see a response in time + ## + ## t: A table of Info records. + ## + ## idx: The index of the connection$radius table corresponding to the + ## radius authentication about to expire. + ## + ## Returns: 0secs, which when this function is used as an + ## :bro:attr:`&expire_func`, indicates to remove the element at + ## *idx* immediately. + global expire: function(t: table[count] of Info, idx: count): interval; + ## Event that can be handled to access the RADIUS record as it is sent on ## to the loggin framework. global log_radius: event(rec: Info); } +redef record connection += { + radius: table[count] of Info &optional &write_expire=expiration_interval &expire_func=expire; +}; + const ports = { 1812/udp }; event bro_init() &priority=5 @@ -32,111 +64,57 @@ event bro_init() &priority=5 Analyzer::register_for_ports(Analyzer::ANALYZER_RADIUS, ports); } -event radius_message(c: connection, msg_type: count, trans_id: count) +event radius_message(c: connection, result: RADIUS::Message) { local info: Info; - info$ts = network_time(); - info$uid = c$uid; - info$id = c$id; - info$msg_type = msg_types[msg_type]; - - Log::write(RADIUS::LOG, info); - } - -event radius_attribute(c: connection, attr_type: count, trans_id: count, value: string) - { - switch ( attr_types[attr_type] ) { -# case "Calling-Station-Id": -# tmp = normalize_mac(value); -# if ( tmp != "" ) -# print cat(attr_types[attr_type], " ", tmp); -# else -# print cat(attr_types[attr_type], " ", value); -# break; -# case "Called-Station-Id": -# fallthrough; - - ## Strings: - case "Reply-Message": - fallthrough; - case "User-Name": - print cat(attr_types[attr_type], ": ", value); - break; - - ## IPs: - - case "Framed-IP-Address": - fallthrough; - case "Framed-IP-Netmask": - fallthrough; - case "NAS-IP-Address": - print cat(attr_types[attr_type], ": ", count_to_v4_addr(bytestring_to_count(value))); - break; - - ## Counts: - - case "Framed-MTU": - fallthrough; - case "NAS-Port": - fallthrough; - case "Session-Timeout": - print cat(attr_types[attr_type], ": ", bytestring_to_count(value)); - break; - - ## Other: - - case "NAS-Port-Type": - print cat(attr_types[attr_type], ": ", nas_port_types[bytestring_to_count(value)]); - break; - case "Service-Type": - print cat(attr_types[attr_type], ": ", service_types[bytestring_to_count(value)]); - break; - case "Framed-Protocol": - print cat(attr_types[attr_type], ": ", framed_protocol_types[bytestring_to_count(value)]); - break; - case "Vendor-Specific": - switch(bytestring_to_count(sub_bytes(value, 0, 4))) { - case 9: - # Cisco IOS/PIX 6.0 - print cat(vendor_9_types[bytestring_to_count(sub_bytes(value, 5, 1))], ": ", sub_bytes(value, 7, 128)); - break; - case 255: - # Cisco VPN 5000 - print cat(vendor_255_types[bytestring_to_count(sub_bytes(value, 5, 1))], ": ", sub_bytes(value, 7, 128)); - break; - case 311: - # Microsoft - print cat(vendor_311_types[bytestring_to_count(sub_bytes(value, 5, 1))], ": ", sub_bytes(value, 7, 128)); - break; - case 3076: - # Cisco VPN 3000 - print cat(vendor_3076_types[bytestring_to_count(sub_bytes(value, 5, 1))], ": ", sub_bytes(value, 7, 128)); - break; - case 14823: - # Aruba - print cat(vendor_14823_types[bytestring_to_count(sub_bytes(value, 5, 1))], ": ", sub_bytes(value, 7, 128)); - break; - default: - print cat("Unknown vendor: ", bytestring_to_count(sub_bytes(value, 0, 4))); - break; + if ( c?$radius && result$trans_id in c$radius ) + info = c$radius[result$trans_id]; + else + { + c$radius = table(); + info$ts = network_time(); + info$uid = c$uid; + info$id = c$id; } - break; - default: - print cat(attr_types[attr_type], ": ", value); - break; + + switch ( result$code ) { + case 1: + # Acess-Request + if ( result?$attributes ) { + # User-Name + if ( !info?$username && 1 in result$attributes ) + info$username = result$attributes[1][0]; + # Calling-Station-Id (we expect this to be a MAC) + if ( !info?$mac && 31 in result$attributes ) + info$mac = normalize_mac(result$attributes[31][0]); + # Tunnel-Client-EndPoint (useful for VPNs) + if ( !info?$remote_ip && 66 in result$attributes ) + info$remote_ip = to_addr(result$attributes[66][0]); + # Connect-Info + if ( !info?$connect_info && 77 in result$attributes ) + info$connect_info = result$attributes[77][0]; + } + break; + case 2: + # Access-Accept + info$result = "success"; + break; + case 3: + # Access-Reject + info$result = "failed"; + break; } + if ( info?$result && !info?$logged ) + { + info$logged = T; + Log::write(RADIUS::LOG, info); + } + c$radius[result$trans_id] = info; } -# Called-Station-Id: -# Calling-Station-Id: -# Class: -# NAS-Identifier: -# State: -# Vendor-Specific: -# unknown-185: -# unknown-66: -# unknown-77: -# unknown-79: -# unknown-80: -# unknown-87: -# unknown-95: \ No newline at end of file + +function expire(t: table[count] of Info, idx: count): interval + { + t[idx]$result = "unknown"; + return 0secs; + } \ No newline at end of file diff --git a/src/analyzer/protocol/radius/events.bif b/src/analyzer/protocol/radius/events.bif index 4ef93a8ca7..8885c2c6e6 100644 --- a/src/analyzer/protocol/radius/events.bif +++ b/src/analyzer/protocol/radius/events.bif @@ -9,7 +9,7 @@ ## trans_id: The RADIUS transaction identifier ## authenticator: The value of the authenticator field ## -event radius_message%(c: connection, msg_type: count, trans_id: count%); +event radius_message%(c: connection, result: RADIUS::Message%); ## Generated for each RADIUS attribute ## @@ -17,7 +17,10 @@ event radius_message%(c: connection, msg_type: count, trans_id: count%); ## ## c: The connection ## attr_type: The value of the code field (1 == User-Name, 2 == User-Password, etc.) -## trans_id: The RADIUS transaction identifier ## authenticator: The value of the authenticator field ## -event radius_attribute%(c: connection, attr_type: count, trans_id: count, value: string%); \ No newline at end of file +event radius_attribute%(c: connection, attr_type: count, value: string%); + +type RADIUS::AttributeList: vector; +type RADIUS::Attributes: table; +type RADIUS::Message: record; diff --git a/src/analyzer/protocol/radius/radius-analyzer.pac b/src/analyzer/protocol/radius/radius-analyzer.pac index 9739970880..7f0239de1f 100644 --- a/src/analyzer/protocol/radius/radius-analyzer.pac +++ b/src/analyzer/protocol/radius/radius-analyzer.pac @@ -1,23 +1,50 @@ # Generated by binpac_quickstart refine flow RADIUS_Flow += { - function proc_radius_message(code: uint8, trans_id: uint8): bool + function proc_radius_message(msg: RADIUS_PDU): bool %{ - BifEvent::generate_radius_message(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), code, trans_id); + connection()->bro_analyzer()->ProtocolConfirmation(); + + if ( !radius_message ) return false; + + RecordVal* result = new RecordVal(BifType::Record::RADIUS::Message); + result->Assign(0, new Val(${msg.code}, TYPE_COUNT)); + result->Assign(1, new Val(${msg.trans_id}, TYPE_COUNT)); + result->Assign(2, bytestring_to_val(${msg.authenticator})); + + TableVal* Attributes = new TableVal(BifType::Table::RADIUS::Attributes); + + for ( uint i = 0; i < ${msg.attributes}->size(); ++i ) { + // Do we already have a vector of attributes for this type? + VectorVal* current = (VectorVal*) Attributes->Lookup(new Val(${msg.attributes[i].code}, TYPE_COUNT)); + if ( current ) + current->Assign((uint) current->Size(), bytestring_to_val(${msg.attributes[i].value})); + else { + VectorVal* AttributeList = new VectorVal(BifType::Vector::RADIUS::AttributeList); + AttributeList->Assign((uint) 0, bytestring_to_val(${msg.attributes[i].value})); + Attributes->Assign(new Val(${msg.attributes[i].code}, TYPE_COUNT), AttributeList); + } + + } + if ( ${msg.attributes}->size() ) + result->Assign(3, Attributes); + BifEvent::generate_radius_message(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), result); return true; %} - function proc_radius_attribute(code: uint8, trans_id: uint8, value: bytestring): bool + function proc_radius_attribute(attr: RADIUS_Attribute): bool %{ - BifEvent::generate_radius_attribute(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), code, trans_id, bytestring_to_val(value)); + if ( !radius_attribute ) return false; + + BifEvent::generate_radius_attribute(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), ${attr.code}, bytestring_to_val(${attr.value})); return true; %} }; refine typeattr RADIUS_PDU += &let { - proc: bool = $context.flow.proc_radius_message(code, trans_id); + proc: bool = $context.flow.proc_radius_message(this); }; refine typeattr RADIUS_Attribute += &let { - proc: bool = $context.flow.proc_radius_attribute(code, trans_id, value); + proc: bool = $context.flow.proc_radius_attribute(this); }; \ No newline at end of file