diff --git a/scripts/base/init-default.bro b/scripts/base/init-default.bro index 6e348cfffd..61376c7de4 100644 --- a/scripts/base/init-default.bro +++ b/scripts/base/init-default.bro @@ -39,6 +39,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..c04423a855 --- /dev/null +++ b/scripts/base/protocols/dhcp/__load__.bro @@ -0,0 +1,4 @@ +@load ./consts +@load ./main + +@load-sigs ./dpd.sig diff --git a/scripts/base/protocols/dhcp/consts.bro b/scripts/base/protocols/dhcp/consts.bro new file mode 100644 index 0000000000..1b2a271563 --- /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); }; + +} diff --git a/scripts/base/protocols/dhcp/dpd.sig b/scripts/base/protocols/dhcp/dpd.sig new file mode 100644 index 0000000000..010920e2d8 --- /dev/null +++ b/scripts/base/protocols/dhcp/dpd.sig @@ -0,0 +1,5 @@ +signature dhcp_cookie { + ip-proto == udp + payload /^.*\x63\x82\x53\x63/ + enable "dhcp" +} \ 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..144e5a53e7 --- /dev/null +++ b/scripts/base/protocols/dhcp/main.bro @@ -0,0 +1,75 @@ +##! 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. +##! +##! If you'd like to track known DHCP devices and to log the hostname +##! supplied by the client, see policy/protocols/dhcp/known-devices.bro + +@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; +}; + +# 67/udp is the server's port, 68/udp the client. +const ports = { 67/udp, 68/udp }; +redef likely_server_ports += { 67/udp }; + +event bro_init() + { + 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) + { + local info: Info; + info$ts = network_time(); + info$id = c$id; + info$uid = c$uid; + info$lease_time = lease; + info$trans_id = msg$xid; + + if ( msg$h_addr != "" ) + info$mac = msg$h_addr; + + if ( reverse_ip(msg$yiaddr) != 0.0.0.0 ) + info$assigned_ip = reverse_ip(msg$yiaddr); + else + info$assigned_ip = c$id$orig_h; + + c$dhcp = info; + + 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/misc/known-devices.bro b/scripts/policy/misc/known-devices.bro new file mode 100644 index 0000000000..f4776a990b --- /dev/null +++ b/scripts/policy/misc/known-devices.bro @@ -0,0 +1,41 @@ +##! This script provides infrastructure for logging devices for which Bro has been +##! able to determine the MAC address, and it logs them once per day (by default). +##! The log that is output provides an easy way to determine a count of the devices +##! in use on a network per day. +##! +##! ..note:: +##! +##! This script will not generate any logs on its own, it needs to be +##! supplied with information from elsewhere, such as +##! :doc:`policy/protocols/dhcp/known-devices-and-hostnames/scripts/. + +module Known; + +export { + ## The known-hosts logging stream identifier. + redef enum Log::ID += { DEVICES_LOG }; + + ## The record type which contains the column fields of the known-devices log. + type DevicesInfo: record { + ## The timestamp at which the host was detected. + ts: time &log; + ## The MAC address that was detected. + mac: string &log; + }; + + ## The set of all known MAC addresses. It can accessed from other + ## to add, and check for, addresses seen in use. + ## + ## We maintain each entry for 24 hours by default so that the existence of + ## individual addressed is logged each day. + global known_devices: set[string] &create_expire=1day &synchronized &redef; + + ## An event that can be handled to access the :bro:type:`Known::DevicesInfo` + ## record as it is sent on to the logging framework. + global log_known_devices: event(rec: DevicesInfo); +} + +event bro_init() + { + Log::create_stream(Known::DEVICES_LOG, [$columns=DevicesInfo, $ev=log_known_devices]); + } diff --git a/scripts/policy/protocols/dhcp/known-devices-and-hostnames.bro b/scripts/policy/protocols/dhcp/known-devices-and-hostnames.bro new file mode 100644 index 0000000000..519429981c --- /dev/null +++ b/scripts/policy/protocols/dhcp/known-devices-and-hostnames.bro @@ -0,0 +1,37 @@ +##! Tracks MAC address with hostnames seen in DHCP traffic. They are logged into +##! ``devices.log``. + +@load policy/misc/known-devices + +module Known; + +export { + redef record DevicesInfo += { + ## The value of the DHCP host name option, if seen + dhcp_host_name: string &log &optional; + }; +} + +event dhcp_request(c: connection, msg: dhcp_msg, req_addr: addr, serv_addr: addr, host_name: string) + { + if ( msg$h_addr == "" ) + return; + + if ( msg$h_addr !in known_devices ) + { + add known_devices[msg$h_addr]; + Log::write(Known::DEVICES_LOG, [$ts=network_time(), $mac=msg$h_addr, $dhcp_host_name=host_name]); + } + } + +event dhcp_inform(c: connection, msg: dhcp_msg, host_name: string) + { + if ( msg$h_addr == "" ) + return; + + if ( msg$h_addr !in known_devices ) + { + add known_devices[msg$h_addr]; + Log::write(Known::DEVICES_LOG, [$ts=network_time(), $mac=msg$h_addr, $dhcp_host_name=host_name]); + } + } 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-analyzer.pac b/src/analyzer/protocol/dhcp/dhcp-analyzer.pac index 5267075445..f58a2d9b5e 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(fmt_mac(${msg.chaddr}.data(), ${msg.chaddr}.length()))); 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; + 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})); - default: - interp->Weird("DHCP_wrong_op_type"); - break; - } + 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..cf8cf69b26 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..1f1e84ef0c 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/net_util.cc b/src/net_util.cc index d91cf02de9..aa88903a8a 100644 --- a/src/net_util.cc +++ b/src/net_util.cc @@ -148,6 +148,26 @@ const char* fmt_conn_id(const uint32* src_addr, uint32 src_port, return fmt_conn_id(src, src_port, dst, dst_port); } +char* fmt_mac(const unsigned char* m, int len) + { + char* buf = new char[25]; + + if ( len < 8 ) + { + *buf = '\0'; + return buf; + } + + if ( m[6] == 0 && m[7] == 0 ) // EUI-48 + snprintf(buf, 19, "%02x:%02x:%02x:%02x:%02x:%02x", + m[0], m[1], m[2], m[3], m[4], m[5]); + else + snprintf(buf, 25, "%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; + } + uint32 extract_uint32(const u_char* data) { uint32 val; diff --git a/src/net_util.h b/src/net_util.h index 733d946564..e5dbbcdae2 100644 --- a/src/net_util.h +++ b/src/net_util.h @@ -156,6 +156,18 @@ extern const char* fmt_conn_id(const IPAddr& src_addr, uint32 src_port, extern const char* fmt_conn_id(const uint32* src_addr, uint32 src_port, const uint32* dst_addr, uint32 dst_port); +/** +* Given a MAC address, formats it in hex 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 +* @param len Number of bytes valid starting at *n*. This must be at +* least 8 for a valid address. +* @return A string of the formatted MAC. Passes ownership to caller. +*/ +extern char* fmt_mac(const unsigned char* m, int len); + // Read 4 bytes from data and return in network order. extern uint32 extract_uint32(const u_char* data); diff --git a/testing/btest/Baseline/scripts.base.protocols.dhcp.dhcp-all-msg-types/dhcp.log b/testing/btest/Baseline/scripts.base.protocols.dhcp.dhcp-all-msg-types/dhcp.log new file mode 100644 index 0000000000..b52d455a4a --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dhcp.dhcp-all-msg-types/dhcp.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path dhcp +#open 2013-07-31-21-00-49 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p mac assigned_ip lease_time trans_id +#types time string addr port addr port string addr interval count +1370200444.371332 nQcgTWjvg4c 128.2.6.189 68 128.2.6.152 67 90:b1:1c:99:49:29 128.2.6.189 900.000000 1984 +#close 2013-07-31-21-00-50 diff --git a/testing/btest/Baseline/scripts.base.protocols.dhcp.inform/dhcp.log b/testing/btest/Baseline/scripts.base.protocols.dhcp.inform/dhcp.log new file mode 100644 index 0000000000..51b4a28f9c --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dhcp.inform/dhcp.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path dhcp +#open 2013-08-03-01-18-52 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p mac assigned_ip lease_time trans_id +#types time string addr port addr port string addr interval count +1374432420.191205 UWkUyAuUGXf 128.2.6.122 68 128.2.6.152 67 90:b1:1c:99:49:29 128.2.6.122 0.000000 2754407505 +#close 2013-08-03-01-18-52 diff --git a/testing/btest/Baseline/scripts.policy.protocols.dhcp.known-devices-and-hostnames.basic/known_devices.log b/testing/btest/Baseline/scripts.policy.protocols.dhcp.known-devices-and-hostnames.basic/known_devices.log new file mode 100644 index 0000000000..91d37f8950 --- /dev/null +++ b/testing/btest/Baseline/scripts.policy.protocols.dhcp.known-devices-and-hostnames.basic/known_devices.log @@ -0,0 +1,11 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path known_devices +#open 2013-07-31-21-27-41 +#fields ts mac dhcp_host_name +#types time string string +1370200443.344965 90:b1:1c:99:49:29 btest.is.cool +1374432420.186878 90:b1:1c:99:49:29 (empty) +#close 2013-07-31-21-27-41 diff --git a/testing/btest/Traces/dhcp/dhcp.trace b/testing/btest/Traces/dhcp/dhcp.trace new file mode 100644 index 0000000000..aeb00a133f Binary files /dev/null and b/testing/btest/Traces/dhcp/dhcp.trace differ diff --git a/testing/btest/Traces/dhcp/dhcp_inform.trace b/testing/btest/Traces/dhcp/dhcp_inform.trace new file mode 100644 index 0000000000..798ca84149 Binary files /dev/null and b/testing/btest/Traces/dhcp/dhcp_inform.trace differ diff --git a/testing/btest/scripts/base/protocols/dhcp/dhcp-all-msg-types.btest b/testing/btest/scripts/base/protocols/dhcp/dhcp-all-msg-types.btest new file mode 100644 index 0000000000..752ab91780 --- /dev/null +++ b/testing/btest/scripts/base/protocols/dhcp/dhcp-all-msg-types.btest @@ -0,0 +1,6 @@ +# This tests that DHCP leases are logged in dhcp.log +# The trace has a message of each DHCP message type, +# but only one lease should show up in the logs. + +# @TEST-EXEC: bro -r $TRACES/dhcp/dhcp.trace %INPUT +# @TEST-EXEC: btest-diff dhcp.log diff --git a/testing/btest/scripts/base/protocols/dhcp/inform.test b/testing/btest/scripts/base/protocols/dhcp/inform.test new file mode 100644 index 0000000000..652fd1ae45 --- /dev/null +++ b/testing/btest/scripts/base/protocols/dhcp/inform.test @@ -0,0 +1,5 @@ +# DHCPINFORM leases are special-cased in the code. +# This tests that those leases are correctly logged. + +# @TEST-EXEC: bro -r $TRACES/dhcp/dhcp_inform.trace %INPUT +# @TEST-EXEC: btest-diff dhcp.log diff --git a/testing/btest/scripts/policy/protocols/dhcp/known-devices-and-hostnames/basic.test b/testing/btest/scripts/policy/protocols/dhcp/known-devices-and-hostnames/basic.test new file mode 100644 index 0000000000..1144ae1377 --- /dev/null +++ b/testing/btest/scripts/policy/protocols/dhcp/known-devices-and-hostnames/basic.test @@ -0,0 +1,8 @@ +# This tests that the known_devices log is created, +# that devices are logged by MAC address, and that +# the DHCP hostname is added, if available. + +# @TEST-EXEC: bro -r $TRACES/dhcp/dhcp.trace -r $TRACES/dhcp/dhcp_inform.trace %INPUT +# @TEST-EXEC: btest-diff known_devices.log + +@load policy/protocols/dhcp/known-devices-and-hostnames