diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index d4e631ecf4..2f2a6c6097 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -2773,6 +2773,24 @@ 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/init-default.bro b/scripts/base/init-default.bro index d87574f4e5..985f38adf2 100644 --- a/scripts/base/init-default.bro +++ b/scripts/base/init-default.bro @@ -47,6 +47,7 @@ @load base/protocols/irc @load base/protocols/modbus @load base/protocols/pop3 +@load base/protocols/radius @load base/protocols/smtp @load base/protocols/socks @load base/protocols/ssh diff --git a/scripts/base/protocols/radius/__load__.bro b/scripts/base/protocols/radius/__load__.bro new file mode 100644 index 0000000000..a10fe855df --- /dev/null +++ b/scripts/base/protocols/radius/__load__.bro @@ -0,0 +1 @@ +@load ./main diff --git a/scripts/base/protocols/radius/consts.bro b/scripts/base/protocols/radius/consts.bro new file mode 100644 index 0000000000..2401998c87 --- /dev/null +++ b/scripts/base/protocols/radius/consts.bro @@ -0,0 +1,231 @@ + +module RADIUS; + +const msg_types: table[count] of string = { + [1] = "Access-Request", + [2] = "Access-Accept", + [3] = "Access-Reject", + [4] = "Accounting-Request", + [5] = "Accounting-Response", + [11] = "Access-Challenge", + [12] = "Status-Server", + [13] = "Status-Client", +} &default=function(i: count): string { return fmt("unknown-%d", i); }; + +const attr_types: table[count] of string = { + [1] = "User-Name", + [2] = "User-Password", + [3] = "CHAP-Password", + [4] = "NAS-IP-Address", + [5] = "NAS-Port", + [6] = "Service-Type", + [7] = "Framed-Protocol", + [8] = "Framed-IP-Address", + [9] = "Framed-IP-Netmask", + [10] = "Framed-Routing", + [11] = "Filter-Id", + [12] = "Framed-MTU", + [13] = "Framed-Compression", + [14] = "Login-IP-Host", + [15] = "Login-Service", + [16] = "Login-TCP-Port", + [18] = "Reply-Message", + [19] = "Callback-Number", + [20] = "Callback-Id", + [22] = "Framed-Route", + [23] = "Framed-IPX-Network", + [24] = "State", + [25] = "Class", + [26] = "Vendor-Specific", + [27] = "Session-Timeout", + [28] = "Idle-Timeout", + [29] = "Termination-Action", + [30] = "Called-Station-Id", + [31] = "Calling-Station-Id", + [32] = "NAS-Identifier", + [33] = "Proxy-State", + [34] = "Login-LAT-Service", + [35] = "Login-LAT-Node", + [36] = "Login-LAT-Group", + [37] = "Framed-AppleTalk-Link", + [38] = "Framed-AppleTalk-Network", + [39] = "Framed-AppleTalk-Zone", + [40] = "Acct-Status-Type", + [41] = "Acct-Delay-Time", + [42] = "Acct-Input-Octets", + [43] = "Acct-Output-Octets", + [44] = "Acct-Session-Id", + [45] = "Acct-Authentic", + [46] = "Acct-Session-Time", + [47] = "Acct-Input-Packets", + [48] = "Acct-Output-Packets", + [49] = "Acct-Terminate-Cause", + [50] = "Acct-Multi-Session-Id", + [51] = "Acct-Link-Count", + [52] = "Acct-Input-Gigawords", + [53] = "Acct-Output-Gigawords", + [55] = "Event-Timestamp", + [56] = "Egress-VLANID", + [57] = "Ingress-Filters", + [58] = "Egress-VLAN-Name", + [59] = "User-Priority-Table", + [60] = "CHAP-Challenge", + [61] = "NAS-Port-Type", + [62] = "Port-Limit", + [63] = "Login-LAT-Port", + [64] = "Tunnel-Type", + [65] = "Tunnel-Medium-Type", + [66] = "Tunnel-Client-EndPoint", + [67] = "Tunnel-Server-EndPoint", + [68] = "Acct-Tunnel-Connection", + [69] = "Tunnel-Password", + [70] = "ARAP-Password", + [71] = "ARAP-Features", + [72] = "ARAP-Zone-Access", + [73] = "ARAP-Security", + [74] = "ARAP-Security-Data", + [75] = "Password-Retry", + [76] = "Prompt", + [77] = "Connect-Info", + [78] = "Configuration-Token", + [79] = "EAP-Message", + [80] = "Message Authenticator", + [81] = "Tunnel-Private-Group-ID", + [82] = "Tunnel-Assignment-ID", + [83] = "Tunnel-Preference", + [84] = "ARAP-Challenge-Response", + [85] = "Acct-Interim-Interval", + [86] = "Acct-Tunnel-Packets-Lost", + [87] = "NAS-Port-Id", + [88] = "Framed-Pool", + [89] = "CUI", + [90] = "Tunnel-Client-Auth-ID", + [91] = "Tunnel-Server-Auth-ID", + [92] = "NAS-Filter-Rule", + [94] = "Originating-Line-Info", + [95] = "NAS-IPv6-Address", + [96] = "Framed-Interface-Id", + [97] = "Framed-IPv6-Prefix", + [98] = "Login-IPv6-Host", + [99] = "Framed-IPv6-Route", + [100] = "Framed-IPv6-Pool", + [101] = "Error-Cause", + [102] = "EAP-Key-Name", + [103] = "Digest-Response", + [104] = "Digest-Realm", + [105] = "Digest-Nonce", + [106] = "Digest-Response-Auth", + [107] = "Digest-Nextnonce", + [108] = "Digest-Method", + [109] = "Digest-URI", + [110] = "Digest-Qop", + [111] = "Digest-Algorithm", + [112] = "Digest-Entity-Body-Hash", + [113] = "Digest-CNonce", + [114] = "Digest-Nonce-Count", + [115] = "Digest-Username", + [116] = "Digest-Opaque", + [117] = "Digest-Auth-Param", + [118] = "Digest-AKA-Auts", + [119] = "Digest-Domain", + [120] = "Digest-Stale", + [121] = "Digest-HA1", + [122] = "SIP-AOR", + [123] = "Delegated-IPv6-Prefix", + [124] = "MIP6-Feature-Vector", + [125] = "MIP6-Home-Link-Prefix", + [126] = "Operator-Name", + [127] = "Location-Information", + [128] = "Location-Data", + [129] = "Basic-Location-Policy-Rules", + [130] = "Extended-Location-Policy-Rules", + [131] = "Location-Capable", + [132] = "Requested-Location-Info", + [133] = "Framed-Management-Protocol", + [134] = "Management-Transport-Protection", + [135] = "Management-Policy-Id", + [136] = "Management-Privilege-Level", + [137] = "PKM-SS-Cert", + [138] = "PKM-CA-Cert", + [139] = "PKM-Config-Settings", + [140] = "PKM-Cryptosuite-List", + [141] = "PKM-SAID", + [142] = "PKM-SA-Descriptor", + [143] = "PKM-Auth-Key", + [144] = "DS-Lite-Tunnel-Name", + [145] = "Mobile-Node-Identifier", + [146] = "Service-Selection", + [147] = "PMIP6-Home-LMA-IPv6-Address", + [148] = "PMIP6-Visited-LMA-IPv6-Address", + [149] = "PMIP6-Home-LMA-IPv4-Address", + [150] = "PMIP6-Visited-LMA-IPv4-Address", + [151] = "PMIP6-Home-HN-Prefix", + [152] = "PMIP6-Visited-HN-Prefix", + [153] = "PMIP6-Home-Interface-ID", + [154] = "PMIP6-Visited-Interface-ID", + [155] = "PMIP6-Home-IPv4-HoA", + [156] = "PMIP6-Visited-IPv4-HoA", + [157] = "PMIP6-Home-DHCP4-Server-Address", + [158] = "PMIP6-Visited-DHCP4-Server-Address", + [159] = "PMIP6-Home-DHCP6-Server-Address", + [160] = "PMIP6-Visited-DHCP6-Server-Address", + [161] = "PMIP6-Home-IPv4-Gateway", + [162] = "PMIP6-Visited-IPv4-Gateway", + [163] = "EAP-Lower-Layer", + [164] = "GSS-Acceptor-Service-Name", + [165] = "GSS-Acceptor-Host-Name", + [166] = "GSS-Acceptor-Service-Specifics", + [167] = "GSS-Acceptor-Realm-Name", + [168] = "Framed-IPv6-Address", + [169] = "DNS-Server-IPv6-Address", + [170] = "Route-IPv6-Information", + [171] = "Delegated-IPv6-Prefix-Pool", + [172] = "Stateful-IPv6-Address-Pool", + [173] = "IPv6-6rd-Configuration" +} &default=function(i: count): string { return fmt("unknown-%d", i); }; + +const nas_port_types: table[count] of string = { + [0] = "Async", + [1] = "Sync", + [2] = "ISDN Sync", + [3] = "ISDN Async V.120", + [4] = "ISDN Async V.110", + [5] = "Virtual", + [6] = "PIAFS", + [7] = "HDLC Clear Channel", + [8] = "X.25", + [9] = "X.75", + [10] = "G.3 Fax", + [11] = "SDSL - Symmetric DSL", + [12] = "ADSL-CAP - Asymmetric DSL, Carrierless Amplitude Phase Modulation", + [13] = "ADSL-DMT - Asymmetric DSL, Discrete Multi-Tone", + [14] = "IDSL - ISDN Digital Subscriber Line", + [15] = "Ethernet", + [16] = "xDSL - Digital Subscriber Line of unknown type", + [17] = "Cable", + [18] = "Wireless - Other", + [19] = "Wireless - IEEE 802.11" +} &default=function(i: count): string { return fmt("unknown-%d", i); }; + +const service_types: table[count] of string = { + [1] = "Login", + [2] = "Framed", + [3] = "Callback Login", + [4] = "Callback Framed", + [5] = "Outbound", + [6] = "Administrative", + [7] = "NAS Prompt", + [8] = "Authenticate Only", + [9] = "Callback NAS Prompt", + [10] = "Call Check", + [11] = "Callback Administrative", +} &default=function(i: count): string { return fmt("unknown-%d", i); }; + +const framed_protocol_types: table[count] of string = { + [1] = "PPP", + [2] = "SLIP", + [3] = "AppleTalk Remote Access Protocol (ARAP)", + [4] = "Gandalf proprietary SingleLink/MultiLink protocol", + [5] = "Xylogics proprietary IPX/SLIP", + [6] = "X.75 Synchronous" +} &default=function(i: count): string { return fmt("unknown-%d", i); }; diff --git a/scripts/base/protocols/radius/main.bro b/scripts/base/protocols/radius/main.bro new file mode 100644 index 0000000000..f8a8a6b798 --- /dev/null +++ b/scripts/base/protocols/radius/main.bro @@ -0,0 +1,127 @@ +##! Implements base functionality for RADIUS analysis. Generates the radius.log file. + +module RADIUS; + +@load ./consts.bro + +export { + redef enum Log::ID += { LOG }; + + type Info: record { + ## Timestamp for when the event happened. + ts : time &log; + ## Unique ID for the connection. + uid : string &log; + ## The connection's 4-tuple of endpoint addresses/ports. + 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 + { + Log::create_stream(RADIUS::LOG, [$columns=Info, $ev=log_radius]); + Analyzer::register_for_ports(Analyzer::ANALYZER_RADIUS, ports); + } + +event radius_message(c: connection, result: RADIUS::Message) + { + local info: Info; + + 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; + } + + 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; + } + + +function expire(t: table[count] of Info, idx: count): interval + { + t[idx]$result = "unknown"; + return 0secs; + } diff --git a/scripts/base/utils/addrs.bro b/scripts/base/utils/addrs.bro index e2031e3efa..9ebd35bbb1 100644 --- a/scripts/base/utils/addrs.bro +++ b/scripts/base/utils/addrs.bro @@ -1,4 +1,4 @@ -##! Functions for parsing and manipulating IP addresses. +##! Functions for parsing and manipulating IP and MAC addresses. # Regular expressions for matching IP addresses in strings. const ipv4_addr_regex = /[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}/; @@ -14,13 +14,13 @@ const ipv6_compressed_hex4dec_regex = /(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?) # ipv6_compressed_hex4dec_regex; #const ip_addr_regex = ipv4_addr_regex | ipv6_addr_regex; -const ipv6_addr_regex = +const ipv6_addr_regex = /([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}/ | /(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)::(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)/ | # IPv6 Compressed Hex /(([0-9A-Fa-f]{1,4}:){6,6})([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/ | # 6Hex4Dec /(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)::(([0-9A-Fa-f]{1,4}:)*)([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/; # CompressedHex4Dec -const ip_addr_regex = +const ip_addr_regex = /[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}/ | /([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}/ | /(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)::(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)/ | # IPv6 Compressed Hex @@ -57,7 +57,7 @@ function is_valid_ip(ip_str: string): bool octets = split(ip_str, /\./); if ( |octets| != 4 ) return F; - + return has_valid_octets(octets); } else if ( ip_str == ipv6_addr_regex ) @@ -119,3 +119,30 @@ function addr_to_uri(a: addr): string else return fmt("[%s]", a); } + +## Given a string, extracts the hex digits and returns a MAC address in the +## format: 00:a0:32:d7:81:8f. If the string doesn't contain 12 or 16 hex digits, +## an empty string is returned. +## +## a: the string to normalize +## +## Returns: a normalized MAC address, or an empty string in the case of an error. +function normalize_mac(a: string): string + { + local result = to_lower(gsub(a, /[^A-Fa-f0-9]/, "")); + local octets: string_vec; + + if ( |result| == 12 ) + { + octets = str_split(result, vector(2, 4, 6, 8, 10)); + return fmt("%s:%s:%s:%s:%s:%s", octets[1], octets[2], octets[3], octets[4], octets[5], octets[6]); + } + + if ( |result| == 16 ) + { + octets = str_split(result, vector(2, 4, 6, 8, 10, 12, 14)); + return fmt("%s:%s:%s:%s:%s:%s:%s:%s", octets[1], octets[2], octets[3], octets[4], octets[5], octets[6], octets[7], octets[8]); + } + + return ""; + } diff --git a/src/analyzer/protocol/CMakeLists.txt b/src/analyzer/protocol/CMakeLists.txt index fc63aa4b66..a7bb43a470 100644 --- a/src/analyzer/protocol/CMakeLists.txt +++ b/src/analyzer/protocol/CMakeLists.txt @@ -19,14 +19,15 @@ add_subdirectory(ident) add_subdirectory(interconn) add_subdirectory(irc) add_subdirectory(login) -add_subdirectory(modbus) add_subdirectory(mime) +add_subdirectory(modbus) add_subdirectory(ncp) -add_subdirectory(netflow) add_subdirectory(netbios) +add_subdirectory(netflow) add_subdirectory(ntp) add_subdirectory(pia) add_subdirectory(pop3) +add_subdirectory(radius) add_subdirectory(rpc) add_subdirectory(smb) add_subdirectory(smtp) diff --git a/src/analyzer/protocol/radius/CMakeLists.txt b/src/analyzer/protocol/radius/CMakeLists.txt new file mode 100644 index 0000000000..022b8a3ac0 --- /dev/null +++ b/src/analyzer/protocol/radius/CMakeLists.txt @@ -0,0 +1,10 @@ + +include(BroPlugin) + +include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) + +bro_plugin_begin(Bro RADIUS) + bro_plugin_cc(RADIUS.cc Plugin.cc) + bro_plugin_bif(events.bif) + bro_plugin_pac(radius.pac radius-analyzer.pac radius-protocol.pac) +bro_plugin_end() diff --git a/src/analyzer/protocol/radius/Plugin.cc b/src/analyzer/protocol/radius/Plugin.cc new file mode 100644 index 0000000000..1c31f82da5 --- /dev/null +++ b/src/analyzer/protocol/radius/Plugin.cc @@ -0,0 +1,10 @@ + +#include "plugin/Plugin.h" + +#include "RADIUS.h" + +BRO_PLUGIN_BEGIN(Bro, RADIUS) + BRO_PLUGIN_DESCRIPTION("RADIUS analyzer"); + BRO_PLUGIN_ANALYZER("RADIUS", RADIUS::RADIUS_Analyzer); + BRO_PLUGIN_BIF_FILE(events); +BRO_PLUGIN_END diff --git a/src/analyzer/protocol/radius/RADIUS.cc b/src/analyzer/protocol/radius/RADIUS.cc new file mode 100644 index 0000000000..9af357c40e --- /dev/null +++ b/src/analyzer/protocol/radius/RADIUS.cc @@ -0,0 +1,40 @@ +// Generated by binpac_quickstart + +#include "RADIUS.h" + +#include "Reporter.h" + +#include "events.bif.h" + +using namespace analyzer::RADIUS; + +RADIUS_Analyzer::RADIUS_Analyzer(Connection* c) + : analyzer::Analyzer("RADIUS", c) + { + interp = new binpac::RADIUS::RADIUS_Conn(this); + } + +RADIUS_Analyzer::~RADIUS_Analyzer() + { + delete interp; + } + +void RADIUS_Analyzer::Done() + { + Analyzer::Done(); + } + +void RADIUS_Analyzer::DeliverPacket(int len, const u_char* data, + bool orig, int seq, const IP_Hdr* ip, int caplen) + { + Analyzer::DeliverPacket(len, data, orig, seq, ip, caplen); + + try + { + interp->NewData(orig, data, data + len); + } + catch ( const binpac::Exception& e ) + { + ProtocolViolation(fmt("Binpac exception: %s", e.c_msg())); + } + } diff --git a/src/analyzer/protocol/radius/RADIUS.h b/src/analyzer/protocol/radius/RADIUS.h new file mode 100644 index 0000000000..44f19395b9 --- /dev/null +++ b/src/analyzer/protocol/radius/RADIUS.h @@ -0,0 +1,34 @@ +// Generated by binpac_quickstart + +#ifndef ANALYZER_PROTOCOL_RADIUS_RADIUS_H +#define ANALYZER_PROTOCOL_RADIUS_RADIUS_H + +#include "events.bif.h" + + +#include "analyzer/protocol/udp/UDP.h" + +#include "radius_pac.h" + +namespace analyzer { namespace RADIUS { + +class RADIUS_Analyzer : public analyzer::Analyzer { +public: + RADIUS_Analyzer(Connection* conn); + virtual ~RADIUS_Analyzer(); + + // Overriden from Analyzer. + virtual void Done(); + virtual void DeliverPacket(int len, const u_char* data, bool orig, + int seq, const IP_Hdr* ip, int caplen); + + static analyzer::Analyzer* InstantiateAnalyzer(Connection* conn) + { return new RADIUS_Analyzer(conn); } + +protected: + binpac::RADIUS::RADIUS_Conn* interp; +}; + +} } // namespace analyzer::* + +#endif diff --git a/src/analyzer/protocol/radius/events.bif b/src/analyzer/protocol/radius/events.bif new file mode 100644 index 0000000000..797d59a0d3 --- /dev/null +++ b/src/analyzer/protocol/radius/events.bif @@ -0,0 +1,26 @@ +## Generated for RADIUS messages. +## +## See `Wikipedia `__ for more +## information about RADIUS. +## +## c: The connection +## msg_type: The value of the code field (1 == Access-Request, 2 == Access-Accept, etc.) +## trans_id: The RADIUS transaction identifier +## authenticator: The value of the authenticator field +## +event radius_message%(c: connection, result: RADIUS::Message%); + +## Generated for each RADIUS attribute. +## +## See `Wikipedia `__ for more +## information about RADIUS. +## +## c: The connection +## attr_type: The value of the code field (1 == User-Name, 2 == User-Password, etc.) +## authenticator: The value of the authenticator field +## +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 new file mode 100644 index 0000000000..119ed7f137 --- /dev/null +++ b/src/analyzer/protocol/radius/radius-analyzer.pac @@ -0,0 +1,63 @@ + +refine flow RADIUS_Flow += { + function proc_radius_message(msg: RADIUS_PDU): bool + %{ + 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})); + + if ( ${msg.attributes}->size() ) + { + TableVal* attributes = new TableVal(BifType::Table::RADIUS::Attributes); + + for ( uint i = 0; i < ${msg.attributes}->size(); ++i ) { + Val* index = new Val(${msg.attributes[i].code}, TYPE_COUNT); + + // Do we already have a vector of attributes for this type? + VectorVal* current = attributes->Lookup(index)->AsVectorVal(); + Val* val = bytestring_to_val(${msg.attributes[i].value}); + + if ( current ) + current->Assign(current->Size(), val); + + else + { + VectorVal* attribute_list = new VectorVal(BifType::Vector::RADIUS::AttributeList); + attribute_list->Assign(0, val); + attributes->Assign(index, attribute_list); + } + + Unref(index); + } + + result->Assign(3, attributes); + } + + BifEvent::generate_radius_message(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), result); + return true; + %} + + function proc_radius_attribute(attr: RADIUS_Attribute): bool + %{ + 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(this); +}; + +refine typeattr RADIUS_Attribute += &let { + proc: bool = $context.flow.proc_radius_attribute(this); +}; diff --git a/src/analyzer/protocol/radius/radius-protocol.pac b/src/analyzer/protocol/radius/radius-protocol.pac new file mode 100644 index 0000000000..9feca621f7 --- /dev/null +++ b/src/analyzer/protocol/radius/radius-protocol.pac @@ -0,0 +1,14 @@ + +type RADIUS_PDU(is_orig: bool) = record { + code: uint8; + trans_id: uint8; + length: uint16; + authenticator: bytestring &length=16; + attributes: RADIUS_Attribute(trans_id)[] &until($input.length() == 0); +} &byteorder=bigendian; + +type RADIUS_Attribute(trans_id: uint8) = record { + code: uint8; + length: uint8; + value: bytestring &length=length-2; +}; diff --git a/src/analyzer/protocol/radius/radius.pac b/src/analyzer/protocol/radius/radius.pac new file mode 100644 index 0000000000..2c3ddca969 --- /dev/null +++ b/src/analyzer/protocol/radius/radius.pac @@ -0,0 +1,30 @@ +# Analyzer for RADIUS +# - radius-protocol.pac: describes the RADIUS protocol messages +# - radius-analyzer.pac: describes the RADIUS analyzer code + +%include binpac.pac +%include bro.pac + +%extern{ + #include "events.bif.h" +%} + +analyzer RADIUS withcontext { + connection: RADIUS_Conn; + flow: RADIUS_Flow; +}; + +# Our connection consists of two flows, one in each direction. +connection RADIUS_Conn(bro_analyzer: BroAnalyzer) { + upflow = RADIUS_Flow(true); + downflow = RADIUS_Flow(false); +}; + +%include radius-protocol.pac + +# Now we define the flow: +flow RADIUS_Flow(is_orig: bool) { + datagram = RADIUS_PDU(is_orig) withcontext(connection, this); +}; + +%include radius-analyzer.pac