From deeb5ec38e2f0c3dadcb7138d672cf1ff905d577 Mon Sep 17 00:00:00 2001 From: Vlad Grigorescu Date: Thu, 18 Jul 2013 21:17:06 -0400 Subject: [PATCH] Rework the DHCP analyzer to make it compatible again. --- scripts/base/init-default.bro | 1 + scripts/base/protocols/dhcp/__load__.bro | 2 + scripts/base/protocols/dhcp/consts.bro | 20 ++ scripts/base/protocols/dhcp/main.bro | 74 +++++ scripts/base/protocols/dhcp/utils.bro | 21 ++ .../protocols/dhcp/handle_extra_msg_types.bro | 256 +++++++++++++++++ src/analyzer/protocol/dhcp/DHCP.cc | 1 - src/analyzer/protocol/dhcp/DHCP.h | 3 + src/analyzer/protocol/dhcp/dhcp-analyzer.pac | 269 ++++++++++-------- src/analyzer/protocol/dhcp/dhcp-protocol.pac | 34 +-- src/analyzer/protocol/dhcp/dhcp.pac | 1 + src/analyzer/protocol/dhcp/events.bif | 195 ++++--------- src/util.cc | 20 ++ src/util.h | 1 + 14 files changed, 623 insertions(+), 275 deletions(-) create mode 100644 scripts/base/protocols/dhcp/__load__.bro create mode 100644 scripts/base/protocols/dhcp/consts.bro create mode 100644 scripts/base/protocols/dhcp/main.bro create mode 100644 scripts/base/protocols/dhcp/utils.bro create mode 100644 scripts/policy/protocols/dhcp/handle_extra_msg_types.bro diff --git a/scripts/base/init-default.bro b/scripts/base/init-default.bro index 72ba0bf115..6aaf7446b9 100644 --- a/scripts/base/init-default.bro +++ b/scripts/base/init-default.bro @@ -36,6 +36,7 @@ @load base/frameworks/tunnels @load base/protocols/conn +@load base/protocols/dhcp @load base/protocols/dns @load base/protocols/ftp @load base/protocols/http diff --git a/scripts/base/protocols/dhcp/__load__.bro b/scripts/base/protocols/dhcp/__load__.bro new file mode 100644 index 0000000000..0098b81a7a --- /dev/null +++ b/scripts/base/protocols/dhcp/__load__.bro @@ -0,0 +1,2 @@ +@load ./consts +@load ./main \ No newline at end of file diff --git a/scripts/base/protocols/dhcp/consts.bro b/scripts/base/protocols/dhcp/consts.bro new file mode 100644 index 0000000000..81914a98c3 --- /dev/null +++ b/scripts/base/protocols/dhcp/consts.bro @@ -0,0 +1,20 @@ +##! Types, errors, and fields for analyzing DHCP data. A helper file +##! for DHCP analysis scripts. + +module DHCP; + +export { + + ## Types of DHCP messages. See RFC 1533. + const message_types = { + [1] = "DHCP_DISCOVER", + [2] = "DHCP_OFFER", + [3] = "DHCP_REQUEST", + [4] = "DHCP_DECLINE", + [5] = "DHCP_ACK", + [6] = "DHCP_NAK", + [7] = "DHCP_RELEASE", + [8] = "DHCP_INFORM", + } &default = function(n: count): string { return fmt("unknown-message-type-%d", n); }; + +} \ No newline at end of file diff --git a/scripts/base/protocols/dhcp/main.bro b/scripts/base/protocols/dhcp/main.bro new file mode 100644 index 0000000000..9fc63df152 --- /dev/null +++ b/scripts/base/protocols/dhcp/main.bro @@ -0,0 +1,74 @@ +##! Analyzes DHCP traffic in order to log DHCP leases given to clients. +##! This script ignores large swaths of the protocol, since it is rather +##! noisy on most networks, and focuses on the end-result: assigned leases. +##! +##! To enable further analysis and log output for DHCP, see the optional +##! scripts in the policy/protocols/dhcp directory. + +@load ./utils.bro + +module DHCP; + +export { + redef enum Log::ID += { LOG }; + + ## The record type which contains the column fields of the DHCP log. + type Info: record { + ## The earliest time at which a DHCP message over the + ## associated connection is observed. + ts: time &log; + ## A unique identifier of the connection over which DHCP is + ## occuring. + uid: string &log; + ## The connection's 4-tuple of endpoint addresses/ports. + id: conn_id &log; + ## Client's hardware address. + mac: string &log &optional; + ## Client's actual assigned IP address. + assigned_ip: addr &log &optional; + ## IP address lease interval. + lease_time: interval &log &optional; + ## A random number choosen by the client for this transaction. + trans_id: count &log; + }; + + ## Event that can be handled to access the DHCP + ## record as it is sent on to the logging framework. + global log_dhcp: event(rec: Info); +} + +# Add the dhcp info to the connection record +redef record connection += { + dhcp: Info &optional; +}; + +const ports = { 67/udp, 68/udp }; +redef likely_server_ports += { 67/udp }; + +event bro_init() &priority=5 + { + Log::create_stream(DHCP::LOG, [$columns=Info, $ev=log_dhcp]); + Analyzer::register_for_ports(Analyzer::ANALYZER_DHCP, ports); + } + +event dhcp_ack(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) &priority=5 + { + local info: Info; + info$ts = network_time(); + info$id = c$id; + info$uid = c$uid; + info$assigned_ip = reverse_ip(msg$yiaddr); + info$lease_time = lease; + info$trans_id = msg$xid; + + if ( msg$h_addr != "" ) + info$mac = msg$h_addr; + + c$dhcp = info; + } + +# We let policy scripts add stuff too, so we run this at a lower priority +event dhcp_ack(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) &priority=1 + { + Log::write(DHCP::LOG, c$dhcp); + } diff --git a/scripts/base/protocols/dhcp/utils.bro b/scripts/base/protocols/dhcp/utils.bro new file mode 100644 index 0000000000..cb06450088 --- /dev/null +++ b/scripts/base/protocols/dhcp/utils.bro @@ -0,0 +1,21 @@ +##! Utilities specific for DHCP processing. + +@load ./main + +module DHCP; + +export { + ## Reverse the octets of an IPv4 IP. + ## + ## ip: An :bro:type:`addr` IPv4 address. + ## + ## Returns: A reversed addr. + global reverse_ip: function(ip: addr): addr; +} + +function reverse_ip(ip: addr): addr + { + local octets = split(cat(ip), /\./); + return to_addr(cat(octets[4], ".", octets[3], ".", octets[2], ".", octets[1])); + } + diff --git a/scripts/policy/protocols/dhcp/handle_extra_msg_types.bro b/scripts/policy/protocols/dhcp/handle_extra_msg_types.bro new file mode 100644 index 0000000000..e5fa713da4 --- /dev/null +++ b/scripts/policy/protocols/dhcp/handle_extra_msg_types.bro @@ -0,0 +1,256 @@ +##! Handlers for DHCP message types other than DHCPACK, which is handled in base/protocols/dhcp. +##! For networks that wish to get more details from their DHCP logs, at the expense +##! of a significantly higher log rate. + +@load base/protocols/dhcp + +module DHCP; + +export { + redef record Info += { + ## The value of the host name option, if seen + host_name: string &log &optional; + ## The IP requested by the client, if any + requested_ip: addr &log &optional; + ## The type of the DHCP message (DHCPOFFER, DHCPRELEASE, etc.) + msg_type: string &log &optional; + }; + + #### Enabled by default + + ## A boolean value to determine if DHCPREQUEST messages are logged. + ## Often useful to see client activity, and because host_name is often available. + const log_dhcprequest = T &redef; + + ## A boolean value to determine if DHCPDECLINE messages are logged. + ## A client declines a lease if it detects that the IP is already in use (usually via ARP). + const log_dhcpdecline = T &redef; + + ## A boolean value to determine if DHCPNAK messages are logged. + ## A server issues a DHCPNAK if a client DHCPREQUEST is invalid. + const log_dhcpnak = T &redef; + + ## A boolean value to determine if DHCPRELEASE messages are logged. + ## A client issues a DHCPRELEASE when it no longer needs the lease (e.g. it's shutting down). + const log_dhcprelease = T &redef; + + #### Not enabled by default + + ## A boolean value to determine if DHCPOFFER messages are logged. + ## Used to profile server -> client communication. + const log_dhcpoffer = F &redef; + + ## A boolean value to determine if DHCPDISCOVER messages are logged. + ## Used to profile broadcast client discovery requests. + const log_dhcpdiscover = F &redef; + + ## A boolean value to determine if DHCPINFORM messages are logged. + ## Used to profile clients attempting to request/renew specific IPs. + const log_dhcpinform = F &redef; + +} + +event dhcp_offer(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) &priority=5 + { + if ( ! log_dhcpoffer ) + return; + + local info: Info; + info$ts = network_time(); + info$id = c$id; + info$uid = c$uid; + info$assigned_ip = reverse_ip(msg$yiaddr); + info$lease_time = lease; + info$trans_id = msg$xid; + info$msg_type = "DHCPOFFER"; + + if ( msg$h_addr != "" ) + info$mac = msg$h_addr; + + if ( host_name != "" ) + info$host_name = host_name; + + c$dhcp = info; + } + +event dhcp_discover(c: connection, msg: dhcp_msg, req_addr: addr, host_name: string) &priority=5 + { + if ( ! log_dhcpdiscover ) + return; + + local info: Info; + info$ts = network_time(); + info$id = c$id; + info$uid = c$uid; + info$requested_ip = req_addr; + info$trans_id = msg$xid; + info$msg_type = "DHCPDISCOVER"; + + if ( msg$h_addr != "" ) + info$mac = msg$h_addr; + + if ( host_name != "" ) + info$host_name = host_name; + + c$dhcp = info; + } + +event dhcp_request(c: connection, msg: dhcp_msg, req_addr: addr, serv_addr: addr, host_name: string) &priority=5 + { + if ( ! log_dhcprequest ) + return; + + local info: Info; + info$ts = network_time(); + info$id = c$id; + info$uid = c$uid; + info$requested_ip = req_addr; + info$trans_id = msg$xid; + info$msg_type = "DHCPREQUEST"; + + if ( msg$h_addr != "" ) + info$mac = msg$h_addr; + + if ( host_name != "" ) + info$host_name = host_name; + + c$dhcp = info; + } + +event dhcp_decline(c: connection, msg: dhcp_msg, host_name: string) &priority=5 + { + if ( ! log_dhcpdecline ) + return; + + local info: Info; + info$ts = network_time(); + info$id = c$id; + info$uid = c$uid; + info$trans_id = msg$xid; + info$msg_type = "DHCPDECLINE"; + + if ( msg$h_addr != "" ) + info$mac = msg$h_addr; + + if ( host_name != "" ) + info$host_name = host_name; + + c$dhcp = info; + } + +event dhcp_nak(c: connection, msg: dhcp_msg, host_name: string) &priority=5 + { + if ( ! log_dhcpnak ) + return; + + local info: Info; + info$ts = network_time(); + info$id = c$id; + info$uid = c$uid; + info$trans_id = msg$xid; + info$msg_type = "DHCPNAK"; + + if ( msg$h_addr != "" ) + info$mac = msg$h_addr; + + if ( host_name != "" ) + info$host_name = host_name; + + c$dhcp = info; + } + +event dhcp_release(c: connection, msg: dhcp_msg, host_name: string) &priority=5 + { + if ( ! log_dhcprelease ) + return; + + local info: Info; + info$ts = network_time(); + info$id = c$id; + info$uid = c$uid; + info$trans_id = msg$xid; + info$msg_type = "DHCPRELEASE"; + + if ( msg$h_addr != "" ) + info$mac = msg$h_addr; + + if ( host_name != "" ) + info$host_name = host_name; + + c$dhcp = info; + } + +event dhcp_inform(c: connection, msg: dhcp_msg, host_name: string) &priority=5 + { + if ( ! log_dhcpinform ) + return; + + local info: Info; + info$ts = network_time(); + info$id = c$id; + info$uid = c$uid; + info$trans_id = msg$xid; + info$msg_type = "DHCPINFORM"; + + if ( msg$h_addr != "" ) + info$mac = msg$h_addr; + + if ( host_name != "" ) + info$host_name = host_name; + + c$dhcp = info; + } + +event dhcp_ack(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) &priority=4 + { + ## For the sake of consistency, let's add msg_type to DHCPACK as well. + c$dhcp$msg_type = "DHCPACK"; + ## host_name is generally not in ACKs, but let's check anyway. + if ( host_name != "" ) + c$dhcp$host_name = host_name; + } + +#### We log stuff at a lower priority, in case any other scripts would like to extend the Info record first. + +event dhcp_offer(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) &priority=1 + { + if ( log_dhcpoffer ) + Log::write(DHCP::LOG, c$dhcp); + } + +event dhcp_discover(c: connection, msg: dhcp_msg, req_addr: addr, host_name: string) &priority=1 + { + if ( log_dhcpdiscover ) + Log::write(DHCP::LOG, c$dhcp); + } + +event dhcp_request(c: connection, msg: dhcp_msg, req_addr: addr, serv_addr: addr, host_name: string) &priority=1 + { + if ( log_dhcprequest ) + Log::write(DHCP::LOG, c$dhcp); + } + +event dhcp_decline(c: connection, msg: dhcp_msg, host_name: string) &priority=1 + { + if ( log_dhcpdecline ) + Log::write(DHCP::LOG, c$dhcp); + } + +event dhcp_nak(c: connection, msg: dhcp_msg, host_name: string) &priority=1 + { + if ( log_dhcpnak ) + Log::write(DHCP::LOG, c$dhcp); + } + +event dhcp_release(c: connection, msg: dhcp_msg, host_name: string) &priority=1 + { + if ( log_dhcprelease ) + Log::write(DHCP::LOG, c$dhcp); + } + +event dhcp_inform(c: connection, msg: dhcp_msg, host_name: string) &priority=1 + { + if ( log_dhcpinform ) + Log::write(DHCP::LOG, c$dhcp); + } + diff --git a/src/analyzer/protocol/dhcp/DHCP.cc b/src/analyzer/protocol/dhcp/DHCP.cc index 8d05aef37d..1fa8759fbf 100644 --- a/src/analyzer/protocol/dhcp/DHCP.cc +++ b/src/analyzer/protocol/dhcp/DHCP.cc @@ -1,4 +1,3 @@ - #include "DHCP.h" #include "events.bif.h" diff --git a/src/analyzer/protocol/dhcp/DHCP.h b/src/analyzer/protocol/dhcp/DHCP.h index a1c06e8b85..b9784d34f1 100644 --- a/src/analyzer/protocol/dhcp/DHCP.h +++ b/src/analyzer/protocol/dhcp/DHCP.h @@ -19,6 +19,9 @@ public: static analyzer::Analyzer* InstantiateAnalyzer(Connection* conn) { return new DHCP_Analyzer(conn); } + static bool Available() + { return dhcp_discover || dhcp_offer || dhcp_request || dhcp_decline || dhcp_ack || dhcp_nak || dhcp_release || dhcp_inform; } + protected: binpac::DHCP::DHCP_Conn* interp; }; diff --git a/src/analyzer/protocol/dhcp/dhcp-analyzer.pac b/src/analyzer/protocol/dhcp/dhcp-analyzer.pac index 5267075445..df928b4be4 100644 --- a/src/analyzer/protocol/dhcp/dhcp-analyzer.pac +++ b/src/analyzer/protocol/dhcp/dhcp-analyzer.pac @@ -8,12 +8,10 @@ flow DHCP_Flow(is_orig: bool) { %member{ BroVal dhcp_msg_val_; - BroAnalyzer interp; %} %init{ dhcp_msg_val_ = 0; - interp = connection->bro_analyzer(); %} %cleanup{ @@ -45,7 +43,7 @@ flow DHCP_Flow(is_orig: bool) { } if ( type == 0 ) - interp->Weird("DHCP_no_type_option"); + connection()->bro_analyzer()->ProtocolViolation("no DHCP message type option"); return type; %} @@ -56,54 +54,63 @@ flow DHCP_Flow(is_orig: bool) { // Requested IP address to the server. ::uint32 req_addr = 0, serv_addr = 0; + StringVal* host_name = 0; - for ( ptr = options->begin(); - ptr != options->end() && ! (*ptr)->last(); ++ptr ) + for ( ptr = options->begin(); ptr != options->end() && ! (*ptr)->last(); ++ptr ) { - switch ( (*ptr)->code() ) { - case REQ_IP_OPTION: - req_addr = htonl((*ptr)->info()->req_addr()); - break; + switch ( (*ptr)->code() ) + { + case REQ_IP_OPTION: + req_addr = htonl((*ptr)->info()->req_addr()); + break; - case SERV_ID_OPTION: - serv_addr = htonl((*ptr)->info()->serv_addr()); - break; - } + case SERV_ID_OPTION: + serv_addr = htonl((*ptr)->info()->serv_addr()); + break; + + case HOST_NAME_OPTION: + host_name = new StringVal((*ptr)->info()->host_name().length(), + (const char*) (*ptr)->info()->host_name().begin()); + break; + } } + if ( host_name == 0 ) + host_name = new StringVal(""); + switch ( type ) - { - case DHCPDISCOVER: - BifEvent::generate_dhcp_discover(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - dhcp_msg_val_->Ref(), new AddrVal(req_addr)); - break; + { + case DHCPDISCOVER: + BifEvent::generate_dhcp_discover(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + dhcp_msg_val_->Ref(), new AddrVal(req_addr), host_name); + break; - case DHCPREQUEST: - BifEvent::generate_dhcp_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - dhcp_msg_val_->Ref(), new AddrVal(req_addr), - new AddrVal(serv_addr)); - break; + case DHCPREQUEST: + BifEvent::generate_dhcp_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + dhcp_msg_val_->Ref(), new AddrVal(req_addr), + new AddrVal(serv_addr), host_name); + break; - case DHCPDECLINE: - BifEvent::generate_dhcp_decline(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - dhcp_msg_val_->Ref()); - break; + case DHCPDECLINE: + BifEvent::generate_dhcp_decline(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + dhcp_msg_val_->Ref(), host_name); + break; - case DHCPRELEASE: - BifEvent::generate_dhcp_release(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - dhcp_msg_val_->Ref()); - break; + case DHCPRELEASE: + BifEvent::generate_dhcp_release(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + dhcp_msg_val_->Ref(), host_name); + break; - case DHCPINFORM: - BifEvent::generate_dhcp_inform(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - dhcp_msg_val_->Ref()); - break; - } + case DHCPINFORM: + BifEvent::generate_dhcp_inform(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + dhcp_msg_val_->Ref(), host_name); + break; + } return true; %} @@ -118,72 +125,83 @@ flow DHCP_Flow(is_orig: bool) { ::uint32 subnet_mask = 0, serv_addr = 0; uint32 lease = 0; + StringVal* host_name = 0; for ( ptr = options->begin(); ptr != options->end() && ! (*ptr)->last(); ++ptr ) { - switch ( (*ptr)->code() ) { - case SUBNET_OPTION: - subnet_mask = htonl((*ptr)->info()->mask()); - break; - - case ROUTER_OPTION: - // Let's hope there aren't multiple - // such options. - Unref(router_list); - router_list = new TableVal(dhcp_router_list); - + switch ( (*ptr)->code() ) { - int num_routers = - (*ptr)->info()->router_list()->size(); + case SUBNET_OPTION: + subnet_mask = htonl((*ptr)->info()->mask()); + break; - for ( int i = 0; i < num_routers; ++i ) - { - vector* rlist = - (*ptr)->info()->router_list(); - uint32 raddr = (*rlist)[i]; - ::uint32 tmp_addr; - tmp_addr = htonl(raddr); - // index starting from 1 - Val* index = new Val(i + 1, TYPE_COUNT); - router_list->Assign(index, new AddrVal(tmp_addr)); - Unref(index); - } + case ROUTER_OPTION: + // Let's hope there aren't multiple + // such options. + Unref(router_list); + router_list = new TableVal(dhcp_router_list); + + { + int num_routers = (*ptr)->info()->router_list()->size(); + + for ( int i = 0; i < num_routers; ++i ) + { + vector* rlist = (*ptr)->info()->router_list(); + + uint32 raddr = (*rlist)[i]; + ::uint32 tmp_addr; + tmp_addr = htonl(raddr); + + // index starting from 1 + Val* index = new Val(i + 1, TYPE_COUNT); + router_list->Assign(index, new AddrVal(tmp_addr)); + Unref(index); + } + } + break; + + case LEASE_OPTION: + lease = (*ptr)->info()->lease(); + break; + + case SERV_ID_OPTION: + serv_addr = htonl((*ptr)->info()->serv_addr()); + break; + + case HOST_NAME_OPTION: + host_name = new StringVal((*ptr)->info()->host_name().length(), + (const char*) (*ptr)->info()->host_name().begin()); + break; } - break; - - case LEASE_OPTION: - lease = (*ptr)->info()->lease(); - break; - - case SERV_ID_OPTION: - serv_addr = htonl((*ptr)->info()->serv_addr()); - break; - } } - switch ( type ) { - case DHCPOFFER: - BifEvent::generate_dhcp_offer(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - dhcp_msg_val_->Ref(), new AddrVal(subnet_mask), - router_list, lease, new AddrVal(serv_addr)); - break; + if ( host_name == 0 ) + host_name = new StringVal(""); - case DHCPACK: - BifEvent::generate_dhcp_ack(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - dhcp_msg_val_->Ref(), new AddrVal(subnet_mask), - router_list, lease, new AddrVal(serv_addr)); - break; + switch ( type ) + { + case DHCPOFFER: + BifEvent::generate_dhcp_offer(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + dhcp_msg_val_->Ref(), new AddrVal(subnet_mask), + router_list, lease, new AddrVal(serv_addr), host_name); + break; - case DHCPNAK: - BifEvent::generate_dhcp_nak(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - dhcp_msg_val_->Ref()); - break; + case DHCPACK: + BifEvent::generate_dhcp_ack(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + dhcp_msg_val_->Ref(), new AddrVal(subnet_mask), + router_list, lease, new AddrVal(serv_addr), host_name); + break; - } + case DHCPNAK: + BifEvent::generate_dhcp_nak(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + dhcp_msg_val_->Ref(), host_name); + break; + + } return true; @@ -195,7 +213,10 @@ flow DHCP_Flow(is_orig: bool) { // DHCP or BOOTP. If not, we are unable to interpret // the message options. if ( ${msg.cookie} != 0x63825363 ) + { + connection()->bro_analyzer()->ProtocolViolation(fmt("bad cookie (%d)", ${msg.cookie})); return false; + } Unref(dhcp_msg_val_); RecordVal* r = new RecordVal(dhcp_msg); @@ -203,40 +224,44 @@ flow DHCP_Flow(is_orig: bool) { r->Assign(0, new Val(${msg.op}, TYPE_COUNT)); r->Assign(1, new Val(${msg.type}, TYPE_COUNT)); r->Assign(2, new Val(${msg.xid}, TYPE_COUNT)); - - // We want only 6 bytes for Ethernet address. - r->Assign(3, new StringVal(6, (const char*) ${msg.chaddr}.begin())); - + r->Assign(3, new StringVal(format_mac(${msg.chaddr}.data()))); r->Assign(4, new AddrVal(${msg.ciaddr})); r->Assign(5, new AddrVal(${msg.yiaddr})); dhcp_msg_val_ = r; - switch ( ${msg.op} ) { - case BOOTREQUEST: // presumablye from client to server - if ( ${msg.type} == DHCPDISCOVER || - ${msg.type} == DHCPREQUEST || - ${msg.type} == DHCPDECLINE || - ${msg.type} == DHCPRELEASE || - ${msg.type} == DHCPINFORM ) - parse_request(${msg.options}, ${msg.type}); - else - interp->Weird("DHCP_wrong_msg_type"); - break; + switch ( ${msg.op} ) + { + case BOOTREQUEST: // presumably from client to server + if ( ${msg.type} == DHCPDISCOVER || + ${msg.type} == DHCPREQUEST || + ${msg.type} == DHCPDECLINE || + ${msg.type} == DHCPRELEASE || + ${msg.type} == DHCPINFORM ) + parse_request(${msg.options}, ${msg.type}); + else + connection()->bro_analyzer()->ProtocolViolation(fmt("unknown DHCP message type option for BOOTREQUEST (%d)", + ${msg.type})); + break; - case BOOTREPLY: // presumably from server to client - if ( ${msg.type} == DHCPOFFER || - ${msg.type} == DHCPACK || ${msg.type} == DHCPNAK ) - parse_reply(${msg.options}, ${msg.type}); - else - interp->Weird("DHCP_wrong_msg_type"); - break; - - default: - interp->Weird("DHCP_wrong_op_type"); - break; - } + case BOOTREPLY: // presumably from server to client + if ( ${msg.type} == DHCPOFFER || + ${msg.type} == DHCPACK || + ${msg.type} == DHCPNAK ) + parse_reply(${msg.options}, ${msg.type}); + else + connection()->bro_analyzer()->ProtocolViolation(fmt("unknown DHCP message type option for BOOTREPLY (%d)", + ${msg.type})); + + break; + default: + connection()->bro_analyzer()->ProtocolViolation(fmt("unknown DHCP message op code (%d). Known codes: 1=BOOTREQUEST, 2=BOOTREPLY", + ${msg.op})); + break; + } + + connection()->bro_analyzer()->ProtocolConfirmation(); return true; %} }; diff --git a/src/analyzer/protocol/dhcp/dhcp-protocol.pac b/src/analyzer/protocol/dhcp/dhcp-protocol.pac index d77780b1b3..1eb5b399e5 100644 --- a/src/analyzer/protocol/dhcp/dhcp-protocol.pac +++ b/src/analyzer/protocol/dhcp/dhcp-protocol.pac @@ -10,13 +10,14 @@ enum OP_type { # The option types are by no means complete. # Anyone can add a new option type in RFC 1533 to be parsed here. enum OPTION_type { - SUBNET_OPTION = 1, - ROUTER_OPTION = 3, - REQ_IP_OPTION = 50, - LEASE_OPTION = 51, - MSG_TYPE_OPTION = 53, - SERV_ID_OPTION = 54, # Server address, actually :) - END_OPTION = 255, + SUBNET_OPTION = 1, + ROUTER_OPTION = 3, + HOST_NAME_OPTION = 12, + REQ_IP_OPTION = 50, + LEASE_OPTION = 51, + MSG_TYPE_OPTION = 53, + SERV_ID_OPTION = 54, # Server address, actually :) + END_OPTION = 255, }; # Refer to RFC 1533 for message types (with option = 53). @@ -34,21 +35,22 @@ enum DHCP_message_type { type Option_Info(code: uint8) = record { length : uint8; value : case code of { - SUBNET_OPTION -> mask : uint32; - ROUTER_OPTION -> router_list: uint32[length/4]; - REQ_IP_OPTION -> req_addr : uint32; - LEASE_OPTION -> lease : uint32; - MSG_TYPE_OPTION -> msg_type : uint8; - SERV_ID_OPTION -> serv_addr: uint32; - default -> other: bytestring &length = length; + SUBNET_OPTION -> mask : uint32; + ROUTER_OPTION -> router_list : uint32[length/4]; + REQ_IP_OPTION -> req_addr : uint32; + LEASE_OPTION -> lease : uint32; + MSG_TYPE_OPTION -> msg_type : uint8; + SERV_ID_OPTION -> serv_addr : uint32; + HOST_NAME_OPTION -> host_name : bytestring &length = length; + default -> other : bytestring &length = length; }; }; type DHCP_Option = record { code : uint8; data : case code of { - 0, 255 -> none : empty; - default -> info : Option_Info(code); + 0, 255 -> none : empty; + default -> info : Option_Info(code); }; } &let { last: bool = (code == 255); # Mark the end of a list of options diff --git a/src/analyzer/protocol/dhcp/dhcp.pac b/src/analyzer/protocol/dhcp/dhcp.pac index c4a684badc..706be31e10 100644 --- a/src/analyzer/protocol/dhcp/dhcp.pac +++ b/src/analyzer/protocol/dhcp/dhcp.pac @@ -1,3 +1,4 @@ +%include binpac.pac %include bro.pac %extern{ diff --git a/src/analyzer/protocol/dhcp/events.bif b/src/analyzer/protocol/dhcp/events.bif index 741504185e..987213dad3 100644 --- a/src/analyzer/protocol/dhcp/events.bif +++ b/src/analyzer/protocol/dhcp/events.bif @@ -1,8 +1,5 @@ -## Generated for DHCP messages of type *discover*. -## -## See `Wikipedia -## `__ for -## more information about the DHCP protocol. +## Generated for DHCP messages of type *DHCPDISCOVER* (client broadcast to locate +## available servers). ## ## c: The connection record describing the underlying UDP flow. ## @@ -10,33 +7,23 @@ ## ## req_addr: The specific address requested by the client. ## -## .. bro:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl -## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply -## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end -## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name -## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply -## dns_rejected dns_request non_dns_request dns_max_queries dns_session_timeout -## dns_skip_addl dns_skip_all_addl dns_skip_all_auth dns_skip_auth +## host_name: The value of the host name option, if specified by the client. +## +## .. bro:see:: dhcp_discover dhcp_offer dhcp_request dhcp_decline dhcp_ack dhcp_nak +## dhcp_release dhcp_inform ## ## .. note:: Bro does not support broadcast packets (as used by the DHCP ## protocol). It treats broadcast addresses just like any other and ## associates packets into transport-level flows in the same way as usual. ## -## .. todo:: Bro's current default configuration does not activate the protocol -## analyzer that generates this event; the corresponding script has not yet -## been ported to Bro 2.x. To still enable this event, one needs to -## register a port for it or add a DPD payload signature. -event dhcp_discover%(c: connection, msg: dhcp_msg, req_addr: addr%); +event dhcp_discover%(c: connection, msg: dhcp_msg, req_addr: addr, host_name: string%); -## Generated for DHCP messages of type *offer*. -## -## See `Wikipedia -## `__ for -## more information about the DHCP protocol. +## Generated for DHCP messages of type *DHCPOFFER* (server to client in response to +## DHCPDISCOVER with offer of configuration parameters). ## ## c: The connection record describing the underlying UDP flow. ## -## msg: TODO. +## msg: The parsed type-independent part of the DHCP message. ## ## mask: The subnet mask specified by the message. ## @@ -46,28 +33,21 @@ event dhcp_discover%(c: connection, msg: dhcp_msg, req_addr: addr%); ## ## serv_addr: The server address specified by the message. ## -## .. bro:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl -## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply -## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end -## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name -## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply -## dns_rejected dns_request non_dns_request +## host_name: The value of the host name option, if specified by the client. +## +## .. bro:see:: dhcp_discover dhcp_request dhcp_decline dhcp_ack dhcp_nak +## dhcp_release dhcp_inform ## ## .. note:: Bro does not support broadcast packets (as used by the DHCP ## protocol). It treats broadcast addresses just like any other and ## associates packets into transport-level flows in the same way as usual. ## -## .. todo:: Bro's current default configuration does not activate the protocol -## analyzer that generates this event; the corresponding script has not yet -## been ported to Bro 2.x. To still enable this event, one needs to -## register a port for it or add a DPD payload signature. -event dhcp_offer%(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr%); +event dhcp_offer%(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string%); -## Generated for DHCP messages of type *request*. -## -## See `Wikipedia -## `__ for -## more information about the DHCP protocol. +## Generated for DHCP messages of type *DHCPREQUEST* (Client message to servers either +## (a) requesting offered parameters from one server and implicitly declining offers +## from all others, (b) confirming correctness of previously allocated address after, +## e.g., system reboot, or (c) extending the lease on a particular network address.) ## ## c: The connection record describing the underlying UDP flow. ## @@ -77,55 +57,37 @@ event dhcp_offer%(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_ ## ## serv_addr: The server address specified by the message. ## -## .. bro:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl -## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply -## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end -## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name -## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply -## dns_rejected dns_request non_dns_request +## host_name: The value of the host name option, if specified by the client. +## +## .. bro:see:: dhcp_discover dhcp_offer dhcp_decline dhcp_ack dhcp_nak +## dhcp_release dhcp_inform ## ## .. note:: Bro does not support broadcast packets (as used by the DHCP ## protocol). It treats broadcast addresses just like any other and ## associates packets into transport-level flows in the same way as usual. ## -## .. todo:: Bro's current default configuration does not activate the protocol -## analyzer that generates this event; the corresponding script has not yet -## been ported to Bro 2.x. To still enable this event, one needs to -## register a port for it or add a DPD payload signature. -event dhcp_request%(c: connection, msg: dhcp_msg, req_addr: addr, serv_addr: addr%); +event dhcp_request%(c: connection, msg: dhcp_msg, req_addr: addr, serv_addr: addr, host_name: string%); -## Generated for DHCP messages of type *decline*. -## -## See `Wikipedia -## `__ for -## more information about the DHCP protocol. +## Generated for DHCP messages of type *DHCPDECLINE* (Client to server indicating +## network address is already in use). ## ## c: The connection record describing the underlying UDP flow. ## ## msg: The parsed type-independent part of the DHCP message. ## -## .. bro:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl -## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply -## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end -## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name -## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply -## dns_rejected dns_request non_dns_request +## host_name: The value of the host name option, if specified by the client. +## +## .. bro:see:: dhcp_discover dhcp_offer dhcp_request dhcp_ack dhcp_nak +## dhcp_release dhcp_inform ## ## .. note:: Bro does not support broadcast packets (as used by the DHCP ## protocol). It treats broadcast addresses just like any other and ## associates packets into transport-level flows in the same way as usual. ## -## .. todo:: Bro's current default configuration does not activate the protocol -## analyzer that generates this event; the corresponding script has not yet -## been ported to Bro 2.x. To still enable this event, one needs to -## register a port for it or add a DPD payload signature. -event dhcp_decline%(c: connection, msg: dhcp_msg%); +event dhcp_decline%(c: connection, msg: dhcp_msg, host_name: string%); -## Generated for DHCP messages of type *acknowledgment*. -## -## See `Wikipedia -## `__ for -## more information about the DHCP protocol. +## Generated for DHCP messages of type *DHCPACK* (Server to client with configuration +## parameters, including committed network address). ## ## c: The connection record describing the underlying UDP flow. ## @@ -139,101 +101,62 @@ event dhcp_decline%(c: connection, msg: dhcp_msg%); ## ## serv_addr: The server address specified by the message. ## -## .. bro:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl -## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply -## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end -## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name -## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply -## dns_rejected dns_request non_dns_request +## host_name: The value of the host name option, if specified by the client. ## -## .. note:: Bro does not support broadcast packets (as used by the DHCP -## protocol). It treats broadcast addresses just like any other and -## associates packets into transport-level flows in the same way as usual. +## .. bro:see:: dhcp_discover dhcp_offer dhcp_request dhcp_decline dhcp_nak +## dhcp_release dhcp_inform ## -## .. todo:: Bro's current default configuration does not activate the protocol -## analyzer that generates this event; the corresponding script has not yet -## been ported to Bro 2.x. To still enable this event, one needs to -## register a port for it or add a DPD payload signature. -event dhcp_ack%(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr%); +event dhcp_ack%(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string%); -## Generated for DHCP messages of type *negative acknowledgment*. -## -## See `Wikipedia -## `__ for -## more information about the DHCP protocol. +## Generated for DHCP messages of type *DHCPNAK* (Server to client indicating client's +## notion of network address is incorrect (e.g., client has moved to new subnet) or +## client's lease has expired). ## ## c: The connection record describing the underlying UDP flow. ## ## msg: The parsed type-independent part of the DHCP message. ## -## .. bro:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl -## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply -## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end -## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name -## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply -## dns_rejected dns_request non_dns_request +## host_name: The value of the host name option, if specified by the client. +## +## .. bro:see:: dhcp_discover dhcp_offer dhcp_request dhcp_decline dhcp_ack dhcp_release +## dhcp_inform ## ## .. note:: Bro does not support broadcast packets (as used by the DHCP ## protocol). It treats broadcast addresses just like any other and ## associates packets into transport-level flows in the same way as usual. ## -## .. todo:: Bro's current default configuration does not activate the protocol -## analyzer that generates this event; the corresponding script has not yet -## been ported to Bro 2.x. To still enable this event, one needs to -## register a port for it or add a DPD payload signature. -event dhcp_nak%(c: connection, msg: dhcp_msg%); +event dhcp_nak%(c: connection, msg: dhcp_msg, host_name: string%); -## Generated for DHCP messages of type *release*. -## -## See `Wikipedia -## `__ for -## more information about the DHCP protocol. +## Generated for DHCP messages of type *DHCPRELEASE* (Client to server relinquishing +## network address and cancelling remaining lease). ## ## c: The connection record describing the underlying UDP flow. ## ## msg: The parsed type-independent part of the DHCP message. ## -## .. bro:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl -## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply -## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end -## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name -## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply -## dns_rejected dns_request non_dns_request +## host_name: The value of the host name option, if specified by the client. ## -## .. note:: Bro does not support broadcast packets (as used by the DHCP -## protocol). It treats broadcast addresses just like any other and -## associates packets into transport-level flows in the same way as usual. +## .. bro:see:: dhcp_discover dhcp_offer dhcp_request dhcp_decline dhcp_ack dhcp_nak +## dhcp_inform ## -## .. todo:: Bro's current default configuration does not activate the protocol -## analyzer that generates this event; the corresponding script has not yet -## been ported to Bro 2.x. To still enable this event, one needs to -## register a port for it or add a DPD payload signature. -event dhcp_release%(c: connection, msg: dhcp_msg%); +event dhcp_release%(c: connection, msg: dhcp_msg, host_name: string%); -## Generated for DHCP messages of type *inform*. -## -## See `Wikipedia -## `__ for -## more information about the DHCP protocol. +## Generated for DHCP messages of type *DHCPINFORM* (Client to server, asking only for +## local configuration parameters; client already has externally configured network +## address). ## ## c: The connection record describing the underlying UDP flow. ## ## msg: The parsed type-independent part of the DHCP message. ## -## .. bro:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl -## dns_HINFO_reply dns_MX_reply dns_NS_reply dns_PTR_reply dns_SOA_reply -## dns_SRV_reply dns_TSIG_addl dns_TXT_reply dns_WKS_reply dns_end -## dns_full_request dns_mapping_altered dns_mapping_lost_name dns_mapping_new_name -## dns_mapping_unverified dns_mapping_valid dns_message dns_query_reply -## dns_rejected dns_request non_dns_request +## host_name: The value of the host name option, if specified by the client. +## +## .. bro:see:: dhcp_discover dhcp_offer dhcp_request dhcp_decline dhcp_ack dhcp_nak +## dhcp_release ## ## .. note:: Bro does not support broadcast packets (as used by the DHCP ## protocol). It treats broadcast addresses just like any other and ## associates packets into transport-level flows in the same way as usual. ## -## .. todo:: Bro's current default configuration does not activate the protocol -## analyzer that generates this event; the corresponding script has not yet -## been ported to Bro 2.x. To still enable this event, one needs to -## register a port for it or add a DPD payload signature. -event dhcp_inform%(c: connection, msg: dhcp_msg%); +event dhcp_inform%(c: connection, msg: dhcp_msg, host_name: string%); diff --git a/src/util.cc b/src/util.cc index 5a63be22cb..0a349ccce2 100644 --- a/src/util.cc +++ b/src/util.cc @@ -78,6 +78,26 @@ std::string extract_ip_and_len(const std::string& i, int* len) return extract_ip(i.substr(0, pos)); } +/** +* Given a MAC address, formats it as 00:de:ad:be:ef +* Supports both EUI-48 and EUI-64. If it's neither, returns +* an empty string. +* +* @param m EUI-48 or EUI-64 MAC address to format, as a char array +* @return A string of the formatted MAC +*/ +char* format_mac(const unsigned char* m) +{ + char* buf = new char[24]; + if (m[6] == 0 && m[7] == 0) // EUI-48 + snprintf(buf, 18, "%02x:%02x:%02x:%02x:%02x:%02x", + m[0], m[1], m[2], m[3], m[4], m[5]); + else + snprintf(buf, 24, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7]); + return buf; +} + /** * Takes a string, unescapes all characters that are escaped as hex codes * (\x##) and turns them into the equivalent ascii-codes. Returns a string diff --git a/src/util.h b/src/util.h index 91ed8f2888..8201456b45 100644 --- a/src/util.h +++ b/src/util.h @@ -106,6 +106,7 @@ std::string get_escaped_string(const std::string& str, bool escape_all); extern char* copy_string(const char* s); extern int streq(const char* s1, const char* s2); +extern char* format_mac(const unsigned char* m); // Returns the character corresponding to the given escape sequence (s points // just past the '\'), and updates s to point just beyond the last character