diff --git a/CHANGES b/CHANGES index 3d817368a6..632465b4a0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,22 @@ +2.5-544 | 2018-05-01 17:57:15 -0500 + + * Rewrite the DHCP analyzer and accompanying script-layer API. + (Valerio G, Corelight) + + * Reduced all DHCP events into a single dhcp_message event. + (removed legacy events since they weren't widely used anyway) + - Support many more DHCP options. + + - DHCP log is completely reworked and now represents DHCP sessions + based on the transaction ID (and works on clusters). + + - Removed the known-devices-and-hostnames.bro and known-devices.bro + scripts since it's generally less relevant now with the updated log. + + * Change include directory search order to better support --with-openssl. + (Johanna Amann) + 2.5-535 | 2018-04-30 16:22:30 -0500 * Improve how coverage unit tests handle name of build dir (Corelight) diff --git a/NEWS b/NEWS index 09c87e8ac7..b6f003a5cd 100644 --- a/NEWS +++ b/NEWS @@ -149,6 +149,34 @@ New Functionality Changed Functionality --------------------- +- The DHCP analyzer and its script-layer interface have been rewritten. + + - Supports more DHCP options than before. + + - The DHCP log now represents DHCP sessions based on transaction ID + and works on Bro cluster deployments. + + - Removed the policy/protocols/dhcp/known-devices-and-hostnames.bro + script since it's generally less relevant now with the updated log. + + - Removed policy/misc/known-devices.bro script and thus + known_devices.log will no longer be created. + + - Removed the base/protocols/dhcp/utils.bro script and thus the + 'reverse_ip' function. + + - Replaced all DHCP events with the single 'dhcp_message' event. + The list of removed events includes: + + - dhcp_discover + - dhcp_offer + - dhcp_request + - dhcp_decline + - dhcp_ack + - dhcp_nak + - dhcp_release + - dhcp_inform + - The --with-binpac= configure option has changed to mean "path to the binpac executable" instead of "path to binpac installation root". diff --git a/VERSION b/VERSION index 0cf391dccc..75a080b8dc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.5-535 +2.5-544 diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index 1506d51bef..bd2206ceab 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -3285,24 +3285,151 @@ export { module GLOBAL; -## A list of router addresses offered by a DHCP server. -## -## .. bro:see:: dhcp_ack dhcp_offer -type dhcp_router_list: table[count] of addr; +module DHCP; -## A DHCP message. -## -## .. bro:see:: dhcp_ack dhcp_decline dhcp_discover dhcp_inform dhcp_nak -## dhcp_offer dhcp_release dhcp_request -type dhcp_msg: record { - op: count; ##< Message OP code. 1 = BOOTREQUEST, 2 = BOOTREPLY - m_type: count; ##< The type of DHCP message. - xid: count; ##< Transaction ID of a DHCP session. - h_addr: string; ##< Hardware address of the client. - ciaddr: addr; ##< Original IP address of the client. - yiaddr: addr; ##< IP address assigned to the client. -}; +export { + ## A list of addresses offered by a DHCP server. Could be routers, + ## DNS servers, or other. + ## + ## .. bro:see:: dhcp_message + type DHCP::Addrs: vector of addr; + ## A DHCP message. + ## .. bro:see:: dhcp_message + type DHCP::Msg: record { + op: count; ##< Message OP code. 1 = BOOTREQUEST, 2 = BOOTREPLY + m_type: count; ##< The type of DHCP message. + xid: count; ##< Transaction ID of a DHCP session. + ## Number of seconds since client began address acquisition + ## or renewal process + secs: interval; + flags: count; + ciaddr: addr; ##< Original IP address of the client. + yiaddr: addr; ##< IP address assigned to the client. + siaddr: addr; ##< IP address of the server. + giaddr: addr; ##< IP address of the relaying gateway. + chaddr: string; ##< Client hardware address. + sname: string &default=""; ##< Server host name. + file_n: string &default=""; ##< Boot file name. + }; + + ## DHCP Client Identifier (Option 61) + ## .. bro:see:: dhcp_message + type DHCP::ClientID: record { + hwtype: count; + hwaddr: string; + }; + + ## DHCP Client FQDN Option information (Option 81) + type DHCP::ClientFQDN: record { + ## An unparsed bitfield of flags (refer to RFC 4702). + flags: count; + ## This field is deprecated in the standard. + rcode1: count; + ## This field is deprecated in the standard. + rcode2: count; + ## The Domain Name part of the option carries all or part of the FQDN + ## of a DHCP client. + domain_name: string; + }; + + ## DHCP Relay Agent Information Option (Option 82) + ## .. bro:see:: dhcp_message + type DHCP::SubOpt: record { + code: count; + value: string; + }; + + type DHCP::SubOpts: vector of DHCP::SubOpt; + + type DHCP::Options: record { + ## The ordered list of all DHCP option numbers. + options: index_vec &optional; + + ## Subnet Mask Value (option 1) + subnet_mask: addr &optional; + + ## Router addresses (option 3) + routers: DHCP::Addrs &optional; + + ## DNS Server addresses (option 6) + dns_servers: DHCP::Addrs &optional; + + ## The Hostname of the client (option 12) + host_name: string &optional; + + ## The DNS domain name of the client (option 15) + domain_name: string &optional; + + ## Enable/Disable IP Forwarding (option 19) + forwarding: bool &optional; + + ## Broadcast Address (option 28) + broadcast: addr &optional; + + ## Vendor specific data. This can frequently + ## be unparsed binary data. (option 43) + vendor: string &optional; + + ## NETBIOS name server list (option 44) + nbns: DHCP::Addrs &optional; + + ## Address requested by the client (option 50) + addr_request: addr &optional; + + ## Lease time offered by the server. (option 51) + lease: interval &optional; + + ## Server address to allow clients to distinguish + ## between lease offers. (option 54) + serv_addr: addr &optional; + + ## DHCP Parameter Request list (option 55) + param_list: index_vec &optional; + + ## Textual error message (option 56) + message: string &optional; + + ## Maximum Message Size (option 57) + max_msg_size: count &optional; + + ## This option specifies the time interval from address + ## assignment until the client transitions to the + ## RENEWING state. (option 58) + renewal_time: interval &optional; + + ## This option specifies the time interval from address + ## assignment until the client transitions to the + ## REBINDING state. (option 59) + rebinding_time: interval &optional; + + ## This option is used by DHCP clients to optionally + ## identify the vendor type and configuration of a DHCP + ## client. (option 60) + vendor_class: string &optional; + + ## DHCP Client Identifier (Option 61) + client_id: DHCP::ClientID &optional; + + ## User Class opaque value (Option 77) + user_class: string &optional; + + ## DHCP Client FQDN (Option 81) + client_fqdn: DHCP::ClientFQDN &optional; + + ## DHCP Relay Agent Information Option (Option 82) + sub_opt: DHCP::SubOpts &optional; + + ## Auto Config option to let host know if it's allowed to + ## auto assign an IP address. (Option 116) + auto_config: bool &optional; + + ## URL to find a proxy.pac for auto proxy config (Option 252) + auto_proxy_config: string &optional; + }; +} + +module GLOBAL; ## A DNS message. ## ## .. bro:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_EDNS_addl diff --git a/scripts/base/protocols/dhcp/consts.bro b/scripts/base/protocols/dhcp/consts.bro index 5afdfc9415..0bf16ded96 100644 --- a/scripts/base/protocols/dhcp/consts.bro +++ b/scripts/base/protocols/dhcp/consts.bro @@ -4,17 +4,186 @@ module DHCP; export { - - ## Types of DHCP messages. See :rfc:`1533`. + ## Types of DHCP messages. See :rfc:`1533`, :rfc:`3203`, + ## :rfc:`4388`, :rfc:`6926`, and :rfc:`7724`. 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", + [1] = "DISCOVER", + [2] = "OFFER", + [3] = "REQUEST", + [4] = "DECLINE", + [5] = "ACK", + [6] = "NAK", + [7] = "RELEASE", + [8] = "INFORM", + [9] = "FORCERENEW", # RFC3203 + [10] = "LEASEQUERY", # RFC4388 + [11] = "LEASEUNASSIGNED", # RFC4388 + [12] = "LEASEUNKNOWN", # RFC4388 + [13] = "LEASEACTIVE", # RFC4388 + [14] = "BULKLEASEQUERY", # RFC6926 + [15] = "LEASEQUERYDONE", # RFC6926 + [16] = "ACTIVELEASEQUERY", # RFC7724 + [17] = "LEASEQUERYSTATUS", # RFC7724 + [18] = "TLS", # RFC7724 } &default = function(n: count): string { return fmt("unknown-message-type-%d", n); }; + ## Option types mapped to their names. + const option_types: table[int] of string = { + [0] = "Pad", + [1] = "Subnet Mask", + [2] = "Time Offset", + [3] = "Router", + [4] = "Time Server", + [5] = "Name Server", + [6] = "Domain Server", + [7] = "Log Server", + [8] = "Quotes Server", + [9] = "LPR Server", + [10] = "Impress Server", + [11] = "RLP Server", + [12] = "Hostname", + [13] = "Boot File Size", + [14] = "Merit Dump File", + [15] = "Domain Name", + [16] = "Swap Server", + [17] = "Root Path", + [18] = "Extension File", + [19] = "Forward On/Off", + [20] = "SrcRte On/Off", + [21] = "Policy Filter", + [22] = "Max DG Assembly", + [23] = "Default IP TTL", + [24] = "MTU Timeout", + [25] = "MTU Plateau", + [26] = "MTU Interface", + [27] = "MTU Subnet", + [28] = "Broadcast Address", + [29] = "Mask Discovery", + [30] = "Mask Supplier", + [31] = "Router Discovery", + [32] = "Router Request", + [33] = "Static Route", + [34] = "Trailers", + [35] = "ARP Timeout", + [36] = "Ethernet", + [37] = "Default TCP TTL", + [38] = "Keepalive Time", + [39] = "Keepalive Data", + [40] = "NIS Domain", + [41] = "NIS Servers", + [42] = "NTP Servers", + [43] = "Vendor Specific", + [44] = "NETBIOS Name Srv", + [45] = "NETBIOS Dist Srv", + [46] = "NETBIOS Node Type", + [47] = "NETBIOS Scope", + [48] = "X Window Font", + [49] = "X Window Manager", + [50] = "Address Request", + [51] = "Address Time", + [52] = "Overload", + [53] = "DHCP Msg Type", + [54] = "DHCP Server Id", + [55] = "Parameter List", + [56] = "DHCP Message", + [57] = "DHCP Max Msg Size", + [58] = "Renewal Time", + [59] = "Rebinding Time", + [60] = "Class Id", + [61] = "Client Id", + [62] = "NetWare/IP Domain", + [63] = "NetWare/IP Option", + [64] = "NIS-Domain-Name", + [65] = "NIS-Server-Addr", + [66] = "Server-Name", + [67] = "Bootfile-Name", + [68] = "Home-Agent-Addrs", + [69] = "SMTP-Server", + [70] = "POP3-Server", + [71] = "NNTP-Server", + [72] = "WWW-Server", + [73] = "Finger-Server", + [74] = "IRC-Server", + [75] = "StreetTalk-Server", + [76] = "STDA-Server", + [77] = "User-Class", + [78] = "Directory Agent", + [79] = "Service Scope", + [80] = "Rapid Commit", + [81] = "Client FQDN", + [82] = "Relay Agent Information", + [83] = "iSNS", + [85] = "NDS Servers", + [86] = "NDS Tree Name", + [87] = "NDS Context", + [88] = "BCMCS Controller Domain Name list", + [89] = "BCMCS Controller IPv4 address option", + [90] = "Authentication", + [91] = "client-last-transaction-time option", + [92] = "associated-ip option", + [93] = "Client System", + [94] = "Client NDI", + [95] = "LDAP", + [97] = "UUID/GUID", + [98] = "User-Auth", + [99] = "GEOCONF_CIVIC", + [100] = "PCode", + [101] = "TCode", + [112] = "Netinfo Address", + [113] = "Netinfo Tag", + [114] = "URL", + [116] = "Auto-Config", + [117] = "Name Service Search", + [118] = "Subnet Selection Option", + [119] = "Domain Search", + [120] = "SIP Servers DHCP Option", + [121] = "Classless Static Route Option", + [122] = "CCC", + [123] = "GeoConf Option", + [124] = "V-I Vendor Class", + [125] = "V-I Vendor-Specific Information", + [128] = "PXE - undefined (vendor specific)", + [129] = "PXE - undefined (vendor specific)", + [130] = "PXE - undefined (vendor specific)", + [131] = "PXE - undefined (vendor specific)", + [132] = "IEEE 802.1Q VLAN ID", + [133] = "IEEE 802.1D/p Layer 2 Priority", + [134] = "Diffserv Code Point (DSCP) for VoIP signalling and media streams", + [135] = "HTTP Proxy for phone-specific applications", + [136] = "OPTION_PANA_AGENT", + [137] = "OPTION_V4_LOST", + [138] = "OPTION_CAPWAP_AC_V4", + [139] = "OPTION-IPv4_Address-MoS", + [140] = "OPTION-IPv4_FQDN-MoS", + [141] = "SIP UA Configuration Service Domains", + [142] = "OPTION-IPv4_Address-ANDSF", + [144] = "GeoLoc", + [145] = "FORCERENEW_NONCE_CAPABLE", + [146] = "RDNSS Selection", + [150] = "TFTP server address", + [151] = "status-code", + [152] = "base-time", + [153] = "start-time-of-state", + [154] = "query-start-time", + [155] = "query-end-time", + [156] = "dhcp-state", + [157] = "data-source", + [158] = "OPTION_V4_PCP_SERVER", + [159] = "OPTION_V4_PORTPARAMS", + [160] = "DHCP Captive-Portal", + [161] = "OPTION_MUD_URL_V4 (TEMPORARY - registered 2016-11-17)", + [175] = "Etherboot (Tentatively Assigned - 2005-06-23)", + [176] = "IP Telephone (Tentatively Assigned - 2005-06-23)", + [177] = "PacketCable and CableHome (replaced by 122)", + [208] = "PXELINUX Magic", + [209] = "Configuration File", + [210] = "Path Prefix", + [211] = "Reboot Time", + [212] = "OPTION_6RD", + [213] = "OPTION_V4_ACCESS_DOMAIN", + [220] = "Subnet Allocation Option", + [221] = "Virtual Subnet Selection (VSS) Option", + [252] = "auto-proxy-config", + [255] = "End", + } &default = function(n: int): string { return fmt("unknown-option-type-%d", n); }; } diff --git a/scripts/base/protocols/dhcp/dpd.sig b/scripts/base/protocols/dhcp/dpd.sig index 010920e2d8..85aa23ea16 100644 --- a/scripts/base/protocols/dhcp/dpd.sig +++ b/scripts/base/protocols/dhcp/dpd.sig @@ -1,5 +1,5 @@ signature dhcp_cookie { ip-proto == udp - payload /^.*\x63\x82\x53\x63/ + payload /^.{236}\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 index bfc3d98117..8458b22a4a 100644 --- a/scripts/base/protocols/dhcp/main.bro +++ b/scripts/base/protocols/dhcp/main.bro @@ -1,12 +1,11 @@ -##! 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 -##! :doc:`/scripts/policy/protocols/dhcp/known-devices-and-hostnames.bro`. +##! Analyze DHCP traffic and provide a log that is organized around +##! the idea of a DHCP "conversation" defined by messages exchanged within +##! a relatively short period of time using the same transaction ID. +##! The log will have information from clients and servers to give a more +##! complete picture of what happened. -@load ./utils.bro +@load base/frameworks/cluster +@load ./consts module DHCP; @@ -17,22 +16,81 @@ export { 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 - ## occurring. - uid: string &log; - ## The connection's 4-tuple of endpoint addresses/ports. - id: conn_id &log; + ts: time &log; + + ## A series of unique identifiers of the connections over which + ## DHCP is occurring. This behavior with multiple connections is + ## unique to DHCP because of the way it uses broadcast packets + ## on local networks. + uids: set[string] &log; + + ## IP address of the client. If a transaction + ## is only a client sending INFORM messages then + ## there is no lease information exchanged so this + ## is helpful to know who sent the messages. + ## Getting an address in this field does require + ## that the client sources at least one DHCP message + ## using a non-broadcast address. + client_addr: addr &log &optional; + ## IP address of the server involved in actually + ## handing out the lease. There could be other + ## servers replying with OFFER messages which won't + ## be represented here. Getting an address in this + ## field also requires that the server handing out + ## the lease also sources packets from a non-broadcast + ## IP address. + server_addr: addr &log &optional; + ## Client's hardware address. - mac: string &log &optional; - ## Client's actual assigned IP address. - assigned_ip: addr &log &optional; + mac: string &log &optional; + + ## Name given by client in Hostname option 12. + host_name: string &log &optional; + ## FQDN given by client in Client FQDN option 81. + client_fqdn: string &log &optional; + ## Domain given by the server in option 15. + domain: string &log &optional; + + ## IP address requested by the client. + requested_addr: addr &log &optional; + ## IP address assigned by the server. + assigned_addr: addr &log &optional; ## IP address lease interval. - lease_time: interval &log &optional; - ## A random number chosen by the client for this transaction. - trans_id: count &log; + lease_time: interval &log &optional; + + ## Message typically accompanied with a DHCP_DECLINE + ## so the client can tell the server why it rejected + ## an address. + client_message: string &log &optional; + ## Message typically accompanied with a DHCP_NAK to let + ## the client know why it rejected the request. + server_message: string &log &optional; + + ## The DHCP message types seen by this DHCP transaction + msg_types: vector of string &log &default=string_vec(); + + ## Duration of the DHCP "session" representing the + ## time from the first message to the last. + duration: interval &log &default=0secs; }; + ## The maximum amount of time that a transation ID will be watched + ## for to try and tie messages together into a single DHCP + ## transaction narrative. + const DHCP::max_txid_watch_time = 30secs &redef; + + ## This event is used internally to distribute data around clusters + ## since DHCP doesn't follow the normal "connection" model used by + ## most protocols. It can also be handled to extend the DHCP log. + ## bro:see::`DHCP::log_info`. + global DHCP::aggregate_msgs: event(ts: time, id: conn_id, uid: string, is_orig: bool, msg: DHCP::Msg, options: DHCP::Options); + + ## This is a global variable that is only to be used in the + ## :bro::see::`DHCP::aggregate_msgs` event. It can be used to avoid + ## looking up the info record for a transaction ID in every event handler + ## for :bro:see::`DHCP::aggregate_msgs`. + global DHCP::log_info: Info; + ## 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); @@ -43,8 +101,13 @@ redef record connection += { dhcp: Info &optional; }; +redef record Info += { + last_message_ts: time &optional; +}; + # 67/udp is the server's port, 68/udp the client. -const ports = { 67/udp, 68/udp }; +# 4011/udp seems to be some proxyDHCP thing. +const ports = { 67/udp, 68/udp, 4011/udp }; redef likely_server_ports += { 67/udp }; event bro_init() &priority=5 @@ -53,27 +116,144 @@ event bro_init() &priority=5 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 +# Setup the clusterized config that is needed to tie messages together on a cluster. +redef Cluster::worker2manager_events += /DHCP::aggregate_msgs/; + +function join_data_expiration(t: table[count] of Info, idx: count): interval { - local info: Info; - info$ts = network_time(); - info$id = c$id; - info$uid = c$uid; - info$lease_time = lease; - info$trans_id = msg$xid; + local info = t[idx]; - if ( msg$h_addr != "" ) - info$mac = msg$h_addr; + local now = network_time(); + # If a message hasn't been seen in the past 5 seconds or the + # total time watching has been more than the maximum time + # allowed by the configuration then log this data and expire it. + # Also, if Bro is shutting down. + if ( (now - info$last_message_ts) > 5sec || + (now - info$ts) > max_txid_watch_time || + bro_is_terminating() ) + { + Log::write(LOG, info); - if ( reverse_ip(msg$yiaddr) != 0.0.0.0 ) - info$assigned_ip = reverse_ip(msg$yiaddr); + # Go ahead and expire the data now that the log + # entry has been written. + return 0secs; + } else - info$assigned_ip = c$id$orig_h; - - c$dhcp = info; + { + return 5secs; + } } -event dhcp_ack(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) &priority=-5 +# This is where the data is stored as it's centralized. All data for a log must +# arrive within the expiration interval if it's to be logged fully. On a cluster, +# this data is only maintained on the manager. +global join_data: table[count] of Info = table() + &create_expire=10secs &expire_func=join_data_expiration; + + + +@if ( ! Cluster::is_enabled() || Cluster::local_node_type() == Cluster::MANAGER ) +# We are handling this event at priority 1000 because we really want +# the DHCP::log_info global to be set correctly before a user might try +# to access it. +event DHCP::aggregate_msgs(ts: time, id: conn_id, uid: string, is_orig: bool, msg: DHCP::Msg, options: DHCP::Options) &priority=1000 { - Log::write(DHCP::LOG, c$dhcp); + if ( msg$xid !in join_data ) + { + join_data[msg$xid] = Info($ts=ts, + $uids=set(uid)); + } + + log_info = join_data[msg$xid]; + } + +event DHCP::aggregate_msgs(ts: time, id: conn_id, uid: string, is_orig: bool, msg: DHCP::Msg, options: DHCP::Options) &priority=5 + { + log_info$duration = ts - log_info$ts; + + if ( uid !in log_info$uids ) + add log_info$uids[uid]; + + log_info$msg_types[|log_info$msg_types|] = DHCP::message_types[msg$m_type]; + + # Let's watch for messages in any DHCP message type + # and split them out based on client and server. + if ( options?$message ) + { + if ( is_orig ) + log_info$client_message = options$message; + else + log_info$server_message = options$message; + } + + # Update the last message time so that we can do some data + # expiration handling. + log_info$last_message_ts = ts; + + if ( is_orig ) # client requests + { + # Assign the client addr in case this is a session + # of only INFORM messages (no lease handed out). + # This also works if a normal lease handout uses + # unicast. + if ( id$orig_h != 0.0.0.0 && id$orig_h != 255.255.255.255 ) + log_info$client_addr = id$orig_h; + + if ( options?$host_name ) + log_info$host_name = options$host_name; + + if ( options?$client_fqdn ) + log_info$client_fqdn = options$client_fqdn$domain_name; + + if ( options?$client_id && + options$client_id$hwtype == 1 ) # ETHERNET + log_info$mac = options$client_id$hwaddr; + + if ( options?$addr_request ) + log_info$requested_addr = options$addr_request; + } + else # server reply messages + { + # Only log the address of the server if it handed out + # an IP address. + if ( msg$yiaddr != 0.0.0.0 && + id$resp_h != 255.255.255.255 ) + { + log_info$server_addr = id$resp_h; + } + + # Only use the client hardware address from the server + # if we didn't already pick one up from the client. + if ( msg$chaddr != "" && !log_info?$mac ) + log_info$mac = msg$chaddr; + + if ( msg$yiaddr != 0.0.0.0 ) + log_info$assigned_addr = msg$yiaddr; + + # If no client address has been seen yet, let's use the assigned addr. + if ( ! log_info?$client_addr && log_info?$assigned_addr ) + log_info$client_addr = log_info$assigned_addr; + + if ( options?$domain_name ) + log_info$domain = options$domain_name; + + if ( options?$lease ) + log_info$lease_time = options$lease; + } + } +@endif + + + +# Aggregate DHCP messages to the manager. +event dhcp_message(c: connection, is_orig: bool, msg: DHCP::Msg, options: DHCP::Options) &priority=-5 + { + event DHCP::aggregate_msgs(network_time(), c$id, c$uid, is_orig, msg, options); + } + +event bro_done() &priority=-5 + { + # Log any remaining data that hasn't already been logged! + for ( i in DHCP::join_data ) + join_data_expiration(DHCP::join_data, i); } diff --git a/scripts/base/protocols/dhcp/utils.bro b/scripts/base/protocols/dhcp/utils.bro deleted file mode 100644 index 9d5a422128..0000000000 --- a/scripts/base/protocols/dhcp/utils.bro +++ /dev/null @@ -1,19 +0,0 @@ -##! Utilities specific for DHCP processing. - -module DHCP; - -export { - ## Reverse the octets of an IPv4 address. - ## - ## ip: An IPv4 address. - ## - ## Returns: A reversed IPv4 address. - global reverse_ip: function(ip: addr): addr; -} - -function reverse_ip(ip: addr): addr - { - local octets = split_string(cat(ip), /\./); - return to_addr(cat(octets[3], ".", octets[2], ".", octets[1], ".", octets[0])); - } - diff --git a/scripts/policy/misc/known-devices.bro b/scripts/policy/misc/known-devices.bro deleted file mode 100644 index 2f1f81524f..0000000000 --- a/scripts/policy/misc/known-devices.bro +++ /dev/null @@ -1,42 +0,0 @@ -##! 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:`/scripts/policy/protocols/dhcp/known-devices-and-hostnames.bro`. - -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 - ## scripts to add, and check for, addresses seen in use. - ## - ## We maintain each entry for 24 hours by default so that the existence - ## of individual addresses 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, $path="known_devices"]); - } diff --git a/scripts/policy/protocols/dhcp/known-devices-and-hostnames.bro b/scripts/policy/protocols/dhcp/known-devices-and-hostnames.bro deleted file mode 100644 index 63b794cb9f..0000000000 --- a/scripts/policy/protocols/dhcp/known-devices-and-hostnames.bro +++ /dev/null @@ -1,37 +0,0 @@ -##! 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/scripts/policy/protocols/dhcp/msg-orig.bro b/scripts/policy/protocols/dhcp/msg-orig.bro new file mode 100644 index 0000000000..beb7c7d78b --- /dev/null +++ b/scripts/policy/protocols/dhcp/msg-orig.bro @@ -0,0 +1,21 @@ +##! Add a field that logs the order of hosts sending messages +##! using the same DHCP transaction ID. This information is +##! occasionally needed on some networks to fully explain the +##! DHCP sequence. + +@load base/protocols/dhcp + +module DHCP; + +export { + redef record DHCP::Info += { + ## The address that originated each message from the + ## `msg_types` field. + msg_orig: vector of addr &log &default=addr_vec(); + }; +} + +event DHCP::aggregate_msgs(ts: time, id: conn_id, uid: string, is_orig: bool, msg: DHCP::Msg, options: DHCP::Options) &priority=3 + { + log_info$msg_orig[|log_info$msg_orig|] = is_orig ? id$orig_h : id$resp_h; + } diff --git a/scripts/policy/protocols/dhcp/software.bro b/scripts/policy/protocols/dhcp/software.bro new file mode 100644 index 0000000000..6509550622 --- /dev/null +++ b/scripts/policy/protocols/dhcp/software.bro @@ -0,0 +1,63 @@ +##! Software identification and extraction for DHCP traffic. + +@load base/protocols/dhcp +@load base/frameworks/software + +module DHCP; + +export { + redef enum Software::Type += { + ## Identifier for web servers in the software framework. + DHCP::SERVER, + ## Identifier for web browsers in the software framework. + DHCP::CLIENT, + }; + + redef record DHCP::Info += { + ## Software reported by the client in the `vendor_class` option. + client_software: string &log &optional; + ## Software reported by the server in the `vendor_class` option. + server_software: string &log &optional; + }; +} + +event DHCP::aggregate_msgs(ts: time, id: conn_id, uid: string, is_orig: bool, msg: DHCP::Msg, options: DHCP::Options) &priority=5 + { + if ( options?$vendor_class ) + { + if ( is_orig ) + log_info$client_software = options$vendor_class; + else + { + log_info$server_software = options$vendor_class; + Software::found(id, [$unparsed_version=options$vendor_class, + $host=id$resp_h, + $software_type=DHCP::SERVER]); + } + } + } + +event DHCP::log_dhcp(rec: DHCP::Info) + { + if ( rec?$assigned_addr && rec?$server_addr && + (rec?$client_software || rec?$server_software) ) + { + # Not quite right to just blindly use 67 and 68 as the ports + local id: conn_id = [$orig_h=rec$assigned_addr, $orig_p=68/udp, + $resp_h=rec$server_addr, $resp_p=67/udp]; + + if ( rec?$client_software && rec$assigned_addr != 255.255.255.255 ) + { + Software::found(id, [$unparsed_version=rec$client_software, + $host=rec$assigned_addr, + $software_type=DHCP::CLIENT]); + } + + if ( rec?$server_software ) + { + Software::found(id, [$unparsed_version=rec$server_software, + $host=rec$server_addr, + $software_type=DHCP::SERVER]); + } + } + } diff --git a/scripts/policy/protocols/dhcp/sub-opts.bro b/scripts/policy/protocols/dhcp/sub-opts.bro new file mode 100644 index 0000000000..43b6942fab --- /dev/null +++ b/scripts/policy/protocols/dhcp/sub-opts.bro @@ -0,0 +1,45 @@ + +@load base/protocols/dhcp + +module DHCP; + +export { + redef record DHCP::Info += { + ## Added by DHCP relay agents which terminate switched or + ## permanent circuits. It encodes an agent-local identifier + ## of the circuit from which a DHCP client-to-server packet was + ## received. Typically it should represent a router or switch + ## interface number. + circuit_id: string &log &optional; + + ## A globally unique identifier added by relay agents to identify + ## the remote host end of the circuit. + agent_remote_id: string &log &optional; + + ## The subscriber ID is a value independent of the physical + ## network configuration so that a customer's DHCP configuration + ## can be given to them correctly no matter where they are + ## physically connected. + subscriber_id: string &log &optional; + }; +} + +event DHCP::aggregate_msgs(ts: time, id: conn_id, uid: string, is_orig: bool, msg: DHCP::Msg, options: DHCP::Options) + { + if ( options?$sub_opt ) + { + for ( i in options$sub_opt ) + { + local sub_opt = options$sub_opt[i]; + + if ( sub_opt$code == 1 ) + DHCP::log_info$circuit_id = sub_opt$value; + + else if ( sub_opt$code == 2 ) + DHCP::log_info$agent_remote_id = sub_opt$value; + + else if ( sub_opt$code == 6 ) + DHCP::log_info$subscriber_id = sub_opt$value; + } + } + } diff --git a/scripts/test-all-policy.bro b/scripts/test-all-policy.bro index 7c828241d0..adaafaea7f 100644 --- a/scripts/test-all-policy.bro +++ b/scripts/test-all-policy.bro @@ -48,7 +48,6 @@ @load misc/detect-traceroute/__load__.bro @load misc/detect-traceroute/main.bro # @load misc/dump-events.bro -@load misc/known-devices.bro @load misc/load-balancing.bro @load misc/loaded-scripts.bro @load misc/profiling.bro @@ -60,7 +59,9 @@ @load protocols/conn/mac-logging.bro @load protocols/conn/vlan-logging.bro @load protocols/conn/weirds.bro -@load protocols/dhcp/known-devices-and-hostnames.bro +@load protocols/dhcp/msg-orig.bro +@load protocols/dhcp/software.bro +@load protocols/dhcp/sub-opts.bro @load protocols/dns/auth-addl.bro @load protocols/dns/detect-external-names.bro @load protocols/ftp/detect-bruteforcing.bro diff --git a/src/NetVar.cc b/src/NetVar.cc index 75613364e2..93533b9627 100644 --- a/src/NetVar.cc +++ b/src/NetVar.cc @@ -110,9 +110,6 @@ RecordType* geo_location; RecordType* entropy_test_result; -TableType* dhcp_router_list; -RecordType* dhcp_msg; - RecordType* dns_msg; RecordType* dns_answer; RecordType* dns_soa; @@ -426,9 +423,6 @@ void init_net_var() entropy_test_result = internal_type("entropy_test_result")->AsRecordType(); - dhcp_router_list = internal_type("dhcp_router_list")->AsTableType(); - dhcp_msg = internal_type("dhcp_msg")->AsRecordType(); - dns_msg = internal_type("dns_msg")->AsRecordType(); dns_answer = internal_type("dns_answer")->AsRecordType(); dns_soa = internal_type("dns_soa")->AsRecordType(); diff --git a/src/NetVar.h b/src/NetVar.h index 2b8ebd69c2..023be18867 100644 --- a/src/NetVar.h +++ b/src/NetVar.h @@ -113,9 +113,6 @@ extern RecordType* geo_location; extern RecordType* entropy_test_result; -extern TableType* dhcp_router_list; -extern RecordType* dhcp_msg; - extern RecordType* dns_msg; extern RecordType* dns_answer; extern RecordType* dns_soa; diff --git a/src/analyzer/protocol/dhcp/CMakeLists.txt b/src/analyzer/protocol/dhcp/CMakeLists.txt index 646a11f9ab..6077adfeb6 100644 --- a/src/analyzer/protocol/dhcp/CMakeLists.txt +++ b/src/analyzer/protocol/dhcp/CMakeLists.txt @@ -6,5 +6,6 @@ include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DI bro_plugin_begin(Bro DHCP) bro_plugin_cc(DHCP.cc Plugin.cc) bro_plugin_bif(events.bif) -bro_plugin_pac(dhcp.pac dhcp-protocol.pac dhcp-analyzer.pac) +bro_plugin_bif(types.bif) +bro_plugin_pac(dhcp.pac dhcp-protocol.pac dhcp-analyzer.pac dhcp-options.pac) bro_plugin_end() diff --git a/src/analyzer/protocol/dhcp/DHCP.cc b/src/analyzer/protocol/dhcp/DHCP.cc index 78b1c6be69..11ecb91107 100644 --- a/src/analyzer/protocol/dhcp/DHCP.cc +++ b/src/analyzer/protocol/dhcp/DHCP.cc @@ -1,6 +1,7 @@ #include "DHCP.h" #include "events.bif.h" +#include "types.bif.h" using namespace analyzer::dhcp; @@ -24,5 +25,14 @@ void DHCP_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, uint64 seq, const IP_Hdr* ip, int caplen) { Analyzer::DeliverPacket(len, data, orig, seq, ip, caplen); - interp->NewData(orig, data, data + len); + + 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/dhcp/DHCP.h b/src/analyzer/protocol/dhcp/DHCP.h index 95510ddf97..462538db45 100644 --- a/src/analyzer/protocol/dhcp/DHCP.h +++ b/src/analyzer/protocol/dhcp/DHCP.h @@ -14,7 +14,7 @@ public: void Done() override; void DeliverPacket(int len, const u_char* data, bool orig, - uint64 seq, const IP_Hdr* ip, int caplen) override; + uint64 seq, const IP_Hdr* ip, int caplen) override; static analyzer::Analyzer* Instantiate(Connection* conn) { return new DHCP_Analyzer(conn); } diff --git a/src/analyzer/protocol/dhcp/dhcp-analyzer.pac b/src/analyzer/protocol/dhcp/dhcp-analyzer.pac index a11412ce96..93966fabeb 100644 --- a/src/analyzer/protocol/dhcp/dhcp-analyzer.pac +++ b/src/analyzer/protocol/dhcp/dhcp-analyzer.pac @@ -1,227 +1,44 @@ -connection DHCP_Conn(bro_analyzer: BroAnalyzer) { - upflow = DHCP_Flow(true); - downflow = DHCP_Flow(false); -}; - -flow DHCP_Flow(is_orig: bool) { - datagram = DHCP_Message withcontext(connection, this); +refine flow DHCP_Flow += { %member{ - BroVal dhcp_msg_val_; + RecordVal* options; + VectorVal* all_options; %} %init{ - dhcp_msg_val_ = 0; + options = nullptr; + all_options = nullptr; %} %cleanup{ - Unref(dhcp_msg_val_); - dhcp_msg_val_ = 0; + Unref(options); + options = nullptr; + + Unref(all_options); + all_options = nullptr; %} - function get_dhcp_msgtype(options: DHCP_Option[]): uint8 + function init_options(): bool %{ - vector::const_iterator ptr; - uint8 type = 0; - - // Leave the for loop if the message type is found. - bool parsed = false; - - for ( ptr = options->begin(); - ptr != options->end() && ! (*ptr)->last(); ++ptr ) + if ( ! options ) { - // We use a switch for future expandability. - switch ( (*ptr)->code() ) { - case MSG_TYPE_OPTION: - type = (*ptr)->info()->msg_type(); - parsed = true; - break; - } - - if ( parsed ) - break; - } - - if ( type == 0 ) - connection()->bro_analyzer()->ProtocolViolation("no DHCP message type option"); - - return type; - %} - - function parse_request(options: DHCP_Option[], type: uint8): bool - %{ - vector::const_iterator ptr; - - // 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 ) - { - 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 HOST_NAME_OPTION: - Unref(host_name); - 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), 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), host_name); - 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(), host_name); - break; - - case DHCPINFORM: - BifEvent::generate_dhcp_inform(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - dhcp_msg_val_->Ref(), host_name); - break; - - default: - Unref(host_name); - break; + options = new RecordVal(BifType::Record::DHCP::Options); + all_options = new VectorVal(index_vec); + options->Assign(0, all_options->Ref()); } return true; %} - function parse_reply(options: DHCP_Option[], type: uint8): bool + function create_options(code: uint8): bool %{ - vector::const_iterator ptr; + init_options(); - // RFC 1533 allows a list of router addresses. - TableVal* router_list = 0; - - ::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); - - { - 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: - Unref(host_name); - 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 DHCPOFFER: - if ( ! router_list ) - router_list = new TableVal(dhcp_router_list); - - 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 DHCPACK: - if ( ! router_list ) - router_list = new TableVal(dhcp_router_list); - - 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: - Unref(router_list); - BifEvent::generate_dhcp_nak(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - dhcp_msg_val_->Ref(), host_name); - break; - - default: - Unref(router_list); - Unref(host_name); - break; - } + if ( code != 255 ) + all_options->Assign(all_options->Size(), + new Val(code, TYPE_COUNT)); return true; - %} function process_dhcp_message(msg: DHCP_Message): bool @@ -235,52 +52,67 @@ flow DHCP_Flow(is_orig: bool) { return false; } - Unref(dhcp_msg_val_); - - std::string mac_str = fmt_mac(${msg.chaddr}.data(), ${msg.chaddr}.length()); - - RecordVal* r = new RecordVal(dhcp_msg); - 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)); - r->Assign(3, new StringVal(mac_str)); - r->Assign(4, new AddrVal(${msg.ciaddr})); - r->Assign(5, new AddrVal(${msg.yiaddr})); - - dhcp_msg_val_ = r; - - switch ( ${msg.op} ) + if ( dhcp_message ) { - 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; + std::string mac_str = fmt_mac(${msg.chaddr}.data(), ${msg.chaddr}.length()); + double secs = static_cast(${msg.secs}); - 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})); + auto dhcp_msg_val = new RecordVal(BifType::Record::DHCP::Msg); + dhcp_msg_val->Assign(0, new Val(${msg.op}, TYPE_COUNT)); + dhcp_msg_val->Assign(1, new Val(${msg.type}, TYPE_COUNT)); + dhcp_msg_val->Assign(2, new Val(${msg.xid}, TYPE_COUNT)); + dhcp_msg_val->Assign(3, new Val(secs, TYPE_INTERVAL)); + dhcp_msg_val->Assign(4, new Val(${msg.flags}, TYPE_COUNT)); + dhcp_msg_val->Assign(5, new AddrVal(htonl(${msg.ciaddr}))); + dhcp_msg_val->Assign(6, new AddrVal(htonl(${msg.yiaddr}))); + dhcp_msg_val->Assign(7, new AddrVal(htonl(${msg.siaddr}))); + dhcp_msg_val->Assign(8, new AddrVal(htonl(${msg.giaddr}))); + dhcp_msg_val->Assign(9, new StringVal(mac_str)); - break; + int last_non_null = 0; - default: - connection()->bro_analyzer()->ProtocolViolation(fmt("unknown DHCP message op code (%d). Known codes: 1=BOOTREQUEST, 2=BOOTREPLY", - ${msg.op})); - break; + for ( int i = 0; i < ${msg.sname}.length(); ++i ) + { + if ( *(${msg.sname}.begin() + i) != 0 ) + last_non_null = i; + } + + if ( last_non_null > 0 ) + dhcp_msg_val->Assign(10, new StringVal(last_non_null + 1, + reinterpret_cast(${msg.sname}.begin()))); + + last_non_null = 0; + + for ( int i = 0; i < ${msg.file_n}.length(); ++i ) + { + if ( *(${msg.file_n}.begin() + i) != 0 ) + last_non_null = i; + } + + if ( last_non_null > 0 ) + dhcp_msg_val->Assign(11, new StringVal(last_non_null + 1, + reinterpret_cast(${msg.file_n}.begin()))); + + init_options(); + + BifEvent::generate_dhcp_message(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + ${msg.is_orig}, + dhcp_msg_val, + options); + + options = nullptr; + Unref(all_options); + all_options = nullptr; } + // A single message reaching this point is enough to confirm the protocol + // because it's not uncommon to see a single DHCP message + // on a "connection". + // The binpac analyzer would have thrown an error before this point + // if there was a problem too (and subsequently called ProtocolViolation). connection()->bro_analyzer()->ProtocolConfirmation(); + return true; %} }; @@ -288,3 +120,8 @@ flow DHCP_Flow(is_orig: bool) { refine typeattr DHCP_Message += &let { proc_dhcp_message = $context.flow.process_dhcp_message(this); }; + +refine typeattr Option += &let { + proc_create_options = $context.flow.create_options(code); +}; + diff --git a/src/analyzer/protocol/dhcp/dhcp-options.pac b/src/analyzer/protocol/dhcp/dhcp-options.pac new file mode 100644 index 0000000000..c154498dbf --- /dev/null +++ b/src/analyzer/protocol/dhcp/dhcp-options.pac @@ -0,0 +1,711 @@ +############################## +# SUBNET OPTION +############################## +let SUBNET_OPTION = 1; + +# Parse the option +refine casetype OptionValue += { + SUBNET_OPTION -> subnet : uint32; +}; + +refine flow DHCP_Flow += { + function process_subnet_option(v: OptionValue): bool + %{ + ${context.flow}->options->Assign(1, new AddrVal(htonl(${v.subnet}))); + return true; + %} +}; + +refine typeattr Option += &let { + proc_subnet_option = $context.flow.process_subnet_option(info.value) &if(code==SUBNET_OPTION); +}; + + +############################## +# ROUTER OPTION +############################## +let ROUTER_OPTION = 3; + +# Parse the option +refine casetype OptionValue += { + ROUTER_OPTION -> router_list : uint32[length/4]; +}; + +refine flow DHCP_Flow += { + function process_router_option(v: OptionValue): bool + %{ + VectorVal* router_list = new VectorVal(BifType::Vector::DHCP::Addrs); + int num_routers = ${v.router_list}->size(); + vector* rlist = ${v.router_list}; + + for ( int i = 0; i < num_routers; ++i ) + { + uint32 raddr = (*rlist)[i]; + router_list->Assign(i, new AddrVal(htonl(raddr))); + } + + ${context.flow}->options->Assign(2, router_list); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_router_option = $context.flow.process_router_option(info.value) &if(code==ROUTER_OPTION); +}; + + +############################## +# DNS SERVER OPTION +############################## +let DNS_SERVER_OPTION = 6; + +# Parse the option +refine casetype OptionValue += { + DNS_SERVER_OPTION -> dns_server_list : uint32[length/4]; +}; + +refine flow DHCP_Flow += { + function process_dns_server_option(v: OptionValue): bool + %{ + VectorVal* server_list = new VectorVal(BifType::Vector::DHCP::Addrs); + int num_servers = ${v.dns_server_list}->size(); + vector* rlist = ${v.dns_server_list}; + + for ( int i = 0; i < num_servers; ++i ) + { + uint32 raddr = (*rlist)[i]; + server_list->Assign(i, new AddrVal(htonl(raddr))); + } + + ${context.flow}->options->Assign(3, server_list); + return true; + %} +}; + +refine typeattr Option += &let { + proc_dns_server_option = $context.flow.process_dns_server_option(info.value) &if(code==DNS_SERVER_OPTION); +}; + + +############################## +# HOST NAME OPTION +############################## +let HOST_NAME_OPTION = 12; + +# Parse the option +refine casetype OptionValue += { + HOST_NAME_OPTION -> host_name : bytestring &length=length; +}; + +refine flow DHCP_Flow += { + function process_host_name_option(v: OptionValue): bool + %{ + ${context.flow}->options->Assign(4, new StringVal(${v.host_name}.length(), + reinterpret_cast(${v.host_name}.begin()))); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_host_name_option = $context.flow.process_host_name_option(info.value) &if(code==HOST_NAME_OPTION); +}; + + +############################## +# DOMAIN NAME OPTION +############################## +let DOMAIN_NAME_OPTION = 15; + +# Parse the option +refine casetype OptionValue += { + DOMAIN_NAME_OPTION -> domain_name : bytestring &length=length; +}; + +refine flow DHCP_Flow += { + function process_domain_name_option(v: OptionValue): bool + %{ + int last_non_null = 0; + + for ( int i = 0; i < ${v.domain_name}.length(); ++i ) + { + if ( *(${v.domain_name}.begin() + i ) != 0 ) + last_non_null = i; + } + + ${context.flow}->options->Assign(5, new StringVal(last_non_null == 0 ? 0 : last_non_null + 1, + reinterpret_cast(${v.domain_name}.begin()))); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_domain_name_option = $context.flow.process_domain_name_option(info.value) &if(code==DOMAIN_NAME_OPTION); +}; + + +############################## +# FORWARDING OPTION +############################## +let FORWARDING_OPTION = 19; + +# Parse the option +refine casetype OptionValue += { + FORWARDING_OPTION -> forwarding : uint8; +}; + +refine flow DHCP_Flow += { + function process_forwarding_option(v: OptionValue): bool + %{ + ${context.flow}->options->Assign(6, new Val(${v.forwarding} == 0 ? false : true, TYPE_BOOL)); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_forwarding_option = $context.flow.process_forwarding_option(info.value) &if(code==FORWARDING_OPTION); +}; + + +############################## +# BROADCAST ADDRESS OPTION +############################## +let BROADCAST_ADDRESS_OPTION = 28; + +# Parse the option +refine casetype OptionValue += { + BROADCAST_ADDRESS_OPTION -> broadcast_address : uint32; +}; + +refine flow DHCP_Flow += { + function process_broadcast_address_option(v: OptionValue): bool + %{ + ${context.flow}->options->Assign(7, new AddrVal(htonl(${v.broadcast_address}))); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_broadcast_address_option = $context.flow.process_broadcast_address_option(info.value) &if(code==BROADCAST_ADDRESS_OPTION); +}; + + +############################## +# VENDOR SPECIFIC OPTION +############################## +let VENDOR_SPECIFIC_OPTION = 43; + +# Parse the option +refine casetype OptionValue += { + VENDOR_SPECIFIC_OPTION -> vendor_specific : bytestring &length=length; +}; + +refine flow DHCP_Flow += { + function process_vendor_specific_option(v: OptionValue): bool + %{ + ${context.flow}->options->Assign(8, new StringVal(${v.vendor_specific}.length(), + reinterpret_cast(${v.vendor_specific}.begin()))); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_vendor_specific_option = $context.flow.process_vendor_specific_option(info.value) &if(code==VENDOR_SPECIFIC_OPTION); +}; + + +############################## +# NETBIOS NAME SERVER OPTION +############################## +let NBNS_OPTION = 44; + +# Parse the option +refine casetype OptionValue += { + NBNS_OPTION -> nbns : uint32[length/4]; +}; + +refine flow DHCP_Flow += { + function process_nbns_option(v: OptionValue): bool + %{ + VectorVal* server_list = new VectorVal(BifType::Vector::DHCP::Addrs); + int num_servers = ${v.nbns}->size(); + vector* rlist = ${v.nbns}; + + for ( int i = 0; i < num_servers; ++i ) + { + uint32 raddr = (*rlist)[i]; + server_list->Assign(i, new AddrVal(htonl(raddr))); + } + + ${context.flow}->options->Assign(9, server_list); + return true; + %} +}; + +refine typeattr Option += &let { + proc_nbns_option = $context.flow.process_nbns_option(info.value) &if(code==NBNS_OPTION); +}; + + +############################## +# ADDR REQUEST OPTION +############################## +let ADDR_REQUEST_OPTION = 50; + +# Parse the option +refine casetype OptionValue += { + ADDR_REQUEST_OPTION -> addr_request : uint32; +}; + +refine flow DHCP_Flow += { + function process_addr_request_option(v: OptionValue): bool + %{ + ${context.flow}->options->Assign(10, new AddrVal(htonl(${v.addr_request}))); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_addr_request_option = $context.flow.process_addr_request_option(info.value) &if(code==ADDR_REQUEST_OPTION); +}; + + +############################## +# LEASE_OPTION OPTION +############################## +let LEASE_OPTION = 51; + +# Parse the option +refine casetype OptionValue += { + LEASE_OPTION -> lease : uint32; +}; + +refine flow DHCP_Flow += { + function process_lease_option(v: OptionValue): bool + %{ + double lease = static_cast(${v.lease}); + ${context.flow}->options->Assign(11, new Val(lease, TYPE_INTERVAL)); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_lease_option = $context.flow.process_lease_option(info.value) &if(code==LEASE_OPTION); +}; + + +############################## +# SERV_ID_OPTION OPTION +############################## +let SERV_ID_OPTION = 54; + +# Parse the option +refine casetype OptionValue += { + SERV_ID_OPTION -> serv_addr : uint32; +}; + +refine flow DHCP_Flow += { + function process_serv_id_option(v: OptionValue): bool + %{ + ${context.flow}->options->Assign(12, new AddrVal(htonl(${v.serv_addr}))); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_serv_id_option = $context.flow.process_serv_id_option(info.value) &if(code==SERV_ID_OPTION); +}; + + +############################## +# PAR_REQ_LIST OPTION +############################## +let PAR_REQ_LIST_OPTION = 55; + +# Parse the option +refine casetype OptionValue += { + PAR_REQ_LIST_OPTION -> par_req_list : uint8[length]; +}; + +refine flow DHCP_Flow += { + function process_par_req_list_option(v: OptionValue): bool + %{ + VectorVal* params = new VectorVal(index_vec); + int num_parms = ${v.par_req_list}->size(); + vector* plist = ${v.par_req_list}; + + for ( int i = 0; i < num_parms; ++i ) + { + uint8 param = (*plist)[i]; + params->Assign(i, new Val(param, TYPE_COUNT)); + } + + ${context.flow}->options->Assign(13, params); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_par_req_list_option = $context.flow.process_par_req_list_option(info.value) &if(code==PAR_REQ_LIST_OPTION); +}; + + +############################## +# MESSAGE OPTION +############################## +let MESSAGE_OPTION = 56; + +# Parse the option +refine casetype OptionValue += { + MESSAGE_OPTION -> message : bytestring &length=length; +}; + +refine flow DHCP_Flow += { + function process_message_option(v: OptionValue): bool + %{ + ${context.flow}->options->Assign(14, new StringVal(${v.message}.length(), + reinterpret_cast(${v.message}.begin()))); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_message_option = $context.flow.process_message_option(info.value) &if(code==MESSAGE_OPTION); +}; + + +############################## +# MAX MESSAGE SIZE OPTION +############################## +let MAX_MESSAGE_SIZE_OPTION = 57; + +# Parse the option +refine casetype OptionValue += { + MAX_MESSAGE_SIZE_OPTION -> max_msg_size : uint16; +}; + +refine flow DHCP_Flow += { + function process_max_message_size_option(v: OptionValue): bool + %{ + ${context.flow}->options->Assign(15, new Val(${v.max_msg_size}, TYPE_COUNT)); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_max_message_size_option = $context.flow.process_max_message_size_option(info.value) &if(code==MAX_MESSAGE_SIZE_OPTION); +}; + + +############################## +# RENEWAL_TIME_OPTION OPTION +############################## +let RENEWAL_TIME_OPTION = 58; + +# Parse the option +refine casetype OptionValue += { + RENEWAL_TIME_OPTION -> renewal_time : uint32; +}; + +refine flow DHCP_Flow += { + function process_renewal_time_option(v: OptionValue): bool + %{ + double renewal_time = static_cast(${v.renewal_time}); + ${context.flow}->options->Assign(16, new Val(renewal_time, TYPE_INTERVAL)); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_renewal_time_option = $context.flow.process_renewal_time_option(info.value) &if(code==RENEWAL_TIME_OPTION); +}; + + +############################## +# REBINDING_TIME_OPTION OPTION +############################## +let REBINDING_TIME_OPTION = 59; + +# Parse the option +refine casetype OptionValue += { + REBINDING_TIME_OPTION -> rebinding_time : uint32; +}; + +refine flow DHCP_Flow += { + function process_rebinding_time_option(v: OptionValue): bool + %{ + double rebinding_time = static_cast(${v.rebinding_time}); + ${context.flow}->options->Assign(17, new Val(rebinding_time, TYPE_INTERVAL)); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_rebinding_time_option = $context.flow.process_rebinding_time_option(info.value) &if(code==REBINDING_TIME_OPTION); +}; + + +############################## +# VENDOR CLASS OPTION +############################## +let VENDOR_CLASS_OPTION = 60; + +# Parse the option +refine casetype OptionValue += { + VENDOR_CLASS_OPTION -> vendor_class : bytestring &length=length; +}; + +refine flow DHCP_Flow += { + function process_vendor_class_option(v: OptionValue): bool + %{ + ${context.flow}->options->Assign(18, new StringVal(${v.vendor_class}.length(), + reinterpret_cast(${v.vendor_class}.begin()))); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_vendor_class_option = $context.flow.process_vendor_class_option(info.value) &if(code==VENDOR_CLASS_OPTION); +}; + + +############################## +# CLIENT_ID_OPTION OPTION +############################## +let CLIENT_ID_OPTION = 61; + +type Client_Identifier(length: uint8) = record { + hwtype : uint8; + hwaddr : bytestring &length = length - 1; +}; + +# Parse the option +refine casetype OptionValue += { + CLIENT_ID_OPTION -> client_id : Client_Identifier(length); +}; + +refine flow DHCP_Flow += { + function process_client_id_option(v: OptionValue): bool + %{ + RecordVal* client_id = new RecordVal(BifType::Record::DHCP::ClientID); + client_id->Assign(0, new Val(${v.client_id.hwtype}, TYPE_COUNT)); + client_id->Assign(1, new StringVal(fmt_mac(${v.client_id.hwaddr}.begin(), ${v.client_id.hwaddr}.length()))); + + ${context.flow}->options->Assign(19, client_id); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_client_id_option = $context.flow.process_client_id_option(info.value) &if(code==CLIENT_ID_OPTION); +}; + + +############################## +# USER CLASS OPTION +############################## +let USER_CLASS_OPTION = 77; + +# Parse the option +refine casetype OptionValue += { + USER_CLASS_OPTION -> user_class : bytestring &length=length; +}; + +refine flow DHCP_Flow += { + function process_user_class_option(v: OptionValue): bool + %{ + ${context.flow}->options->Assign(20, new StringVal(${v.user_class}.length(), + reinterpret_cast(${v.user_class}.begin()))); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_user_class_option = $context.flow.process_user_class_option(info.value) &if(code==USER_CLASS_OPTION); +}; + + +############################## +# CLIENT FQDN OPTION +############################## +let CLIENT_FQDN_OPTION = 81; + +type Client_FQDN(length: uint8) = record { + flags : uint8; + rcode1 : uint8; + rcode2 : uint8; + domain_name : bytestring &length=length-3; +}; + +# Parse the option +refine casetype OptionValue += { + CLIENT_FQDN_OPTION -> client_fqdn : Client_FQDN(length); +}; + +refine flow DHCP_Flow += { + function process_client_fqdn_option(v: OptionValue): bool + %{ + RecordVal* client_fqdn = new RecordVal(BifType::Record::DHCP::ClientFQDN); + client_fqdn->Assign(0, new Val(${v.client_fqdn.flags}, TYPE_COUNT)); + client_fqdn->Assign(1, new Val(${v.client_fqdn.rcode1}, TYPE_COUNT)); + client_fqdn->Assign(2, new Val(${v.client_fqdn.rcode2}, TYPE_COUNT)); + const char* domain_name = reinterpret_cast(${v.client_fqdn.domain_name}.begin()); + client_fqdn->Assign(3, new StringVal(${v.client_fqdn.domain_name}.length(), domain_name)); + + ${context.flow}->options->Assign(21, client_fqdn); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_client_fqdn_option = $context.flow.process_client_fqdn_option(info.value) &if(code==CLIENT_FQDN_OPTION); +}; + + +############################## +# RELAY_AGENT_INF OPTION +############################## +let RELAY_AGENT_INF_OPTION = 82; + +type Relay_Agent_SubOption(tot_len: uint8) = record { + code : uint8; + length : uint8; + value : bytestring &length = length; +} &let { + sum_len: uint8 = $context.flow.get_dhcp_sumlen(length + 2); + last: bool = (sum_len == tot_len); +}; + +# Parse the option +refine casetype OptionValue += { + RELAY_AGENT_INF_OPTION -> relay_agent_inf : Relay_Agent_SubOption(length)[] &until($element.last); +}; + +refine flow DHCP_Flow += { + %member{ + uint8 sum_len; + %} + + %init{ + sum_len = 0; + %} + + %cleanup{ + sum_len = 0; + %} + + function get_dhcp_sumlen(len: uint8): uint8 + %{ + sum_len = len + sum_len; + return sum_len; + %} + + function process_relay_agent_inf_option(v: OptionValue): bool + %{ + VectorVal* relay_agent_sub_opt = new VectorVal(BifType::Vector::DHCP::SubOpts); + + uint16 i = 0; + + for ( auto ptrsubopt = ${v.relay_agent_inf}->begin(); + ptrsubopt != ${v.relay_agent_inf}->end(); ++ptrsubopt ) + { + auto r = new RecordVal(BifType::Record::DHCP::SubOpt); + r->Assign(0, new Val((*ptrsubopt)->code(), TYPE_COUNT)); + r->Assign(1, bytestring_to_val((*ptrsubopt)->value())); + + relay_agent_sub_opt->Assign(i, r); + ++i; + } + + ${context.flow}->options->Assign(22, relay_agent_sub_opt); + return true; + %} +}; + +refine typeattr Option += &let { + proc_relay_agent_info_option = $context.flow.process_relay_agent_inf_option(info.value) &if(code==RELAY_AGENT_INF_OPTION); +}; + + +############################## +# AUTO_CONFIG OPTION +############################## +let AUTO_CONFIG_OPTION = 116; + +# Parse the option +refine casetype OptionValue += { + AUTO_CONFIG_OPTION -> auto_config : uint8; +}; + +refine flow DHCP_Flow += { + function process_auto_config_option(v: OptionValue): bool + %{ + ${context.flow}->options->Assign(23, new Val(${v.auto_config} == 0 ? false : true, TYPE_BOOL)); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_auto_config_option = $context.flow.process_auto_config_option(info.value) &if(code==AUTO_CONFIG_OPTION); +}; + + +############################## +# AUTO PROXY CONFIG OPTION +############################## +let AUTO_PROXY_CONFIG_OPTION = 252; + +# Parse the option +refine casetype OptionValue += { + AUTO_PROXY_CONFIG_OPTION -> auto_proxy_config : bytestring &length=length; +}; + +refine flow DHCP_Flow += { + function process_auto_proxy_config_option(v: OptionValue): bool + %{ + int string_len = ${v.auto_proxy_config}.length(); + + if ( string_len == 0 ) + { + ${context.flow}->options->Assign(24, new StringVal(0, "")); + return true; + } + + + const char* last_char = reinterpret_cast(${v.auto_proxy_config}.begin() + string_len - 1); + + bool has_newline = *last_char == '\x0a'; + + if ( has_newline ) + --string_len; + + ${context.flow}->options->Assign(24, new StringVal(string_len, + reinterpret_cast(${v.auto_proxy_config}.begin()))); + + return true; + %} +}; + +refine typeattr Option += &let { + proc_auto_proxy_config_option = $context.flow.process_auto_proxy_config_option(info.value) &if(code==AUTO_PROXY_CONFIG_OPTION); +}; + + diff --git a/src/analyzer/protocol/dhcp/dhcp-protocol.pac b/src/analyzer/protocol/dhcp/dhcp-protocol.pac index cf8cf69b26..af48a416c4 100644 --- a/src/analyzer/protocol/dhcp/dhcp-protocol.pac +++ b/src/analyzer/protocol/dhcp/dhcp-protocol.pac @@ -3,116 +3,73 @@ # Refer to RFC 2131 for op types. enum OP_type { BOOTREQUEST = 1, - BOOTREPLY = 2, + BOOTREPLY = 2 }; -# Refer to RFC 1533 for option types. -# 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, - 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, +let MSG_TYPE_OPTION = 53; + +type OptionValue(code: uint8, length: uint8) = case code of { + # This is extended in dhcp-options.pac + MSG_TYPE_OPTION -> msg_type : uint8; + default -> other : bytestring &length = length; }; -# Refer to RFC 1533 for message types (with option = 53). -enum DHCP_message_type { - DHCPDISCOVER = 1, - DHCPOFFER = 2, - DHCPREQUEST = 3, - DHCPDECLINE = 4, - DHCPACK = 5, - DHCPNAK = 6, - DHCPRELEASE = 7, - DHCPINFORM = 8, +type OptionValueWrapper(code: uint8) = record { + length : uint8; + value : OptionValue(code, length); }; -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; - 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); +type Option = record { + code : uint8; + data : case code of { + 0, 255 -> none : empty; + default -> info : OptionValueWrapper(code); }; } &let { - last: bool = (code == 255); # Mark the end of a list of options + last = (code == 255); # Mark the end of a list of options }; -# Message format according to RFC 2131 -# -# 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 -# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 -# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -# | op (1) | htype (1) | hlen (1) | hops (1) | -# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -# | xid (4) | -# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -# | secs (2) | flags (2) | -# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -# | ciaddr (4) | -# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -# | yiaddr (4) | -# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -# | siaddr (4) | -# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -# | giaddr (4) | -# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -# | | -# | chaddr (16) | -# / / -# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -# | | -# | sname (64) | -# / / -# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -# | | -# | file (128) | -# / / -# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -# | | -# | options (variable) | -# / / -# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - -type DHCP_Message = record { - op : uint8; - htype : uint8; - hlen : uint8; - hops : uint8; - xid : uint32; - secs : uint16; - flags : uint16; - ciaddr : uint32; - yiaddr : uint32; - siaddr : uint32; - giaddr : uint32; +type DHCP_Message(is_orig: bool) = record { + op : uint8; + htype : uint8; + hlen : uint8; + hops : uint8; + xid : uint32; + secs : uint16; + flags : uint16; + ciaddr : uint32; + yiaddr : uint32; + siaddr : uint32; + giaddr : uint32; chaddr : bytestring &length = 16; - sname : bytestring &length = 64; - file : bytestring &length = 128; - + sname : bytestring &length = 64; + file_n : bytestring &length = 128; # Cookie belongs to options in RFC 2131, but we separate # them here for easy parsing. cookie : uint32; - - options : DHCP_Option[] &until($element.last); + options : Option[] &until($element.last); } &let { - type : uint8 = $context.flow.get_dhcp_msgtype(options); + type = $context.flow.get_dhcp_msgtype(options); } &byteorder = bigendian; + +refine flow DHCP_Flow += { + function get_dhcp_msgtype(options: Option[]): uint8 + %{ + uint8 type = 0; + for ( auto ptr = options->begin(); + ptr != options->end() && ! (*ptr)->last(); ++ptr ) + { + if ( (*ptr)->code() == MSG_TYPE_OPTION ) + { + type = (*ptr)->info()->value()->msg_type(); + break; + } + } + + if ( type == 0 ) + connection()->bro_analyzer()->ProtocolViolation("no DHCP message type option"); + + return type; + %} +}; + diff --git a/src/analyzer/protocol/dhcp/dhcp.pac b/src/analyzer/protocol/dhcp/dhcp.pac index 706be31e10..ac88726b3c 100644 --- a/src/analyzer/protocol/dhcp/dhcp.pac +++ b/src/analyzer/protocol/dhcp/dhcp.pac @@ -2,6 +2,7 @@ %include bro.pac %extern{ +#include "types.bif.h" #include "events.bif.h" %} @@ -10,5 +11,15 @@ analyzer DHCP withcontext { flow: DHCP_Flow; }; +connection DHCP_Conn(bro_analyzer: BroAnalyzer) { + upflow = DHCP_Flow(true); + downflow = DHCP_Flow(false); +}; + +flow DHCP_Flow(is_orig: bool) { + datagram = DHCP_Message(is_orig) withcontext(connection, this); +}; + %include dhcp-protocol.pac %include dhcp-analyzer.pac +%include dhcp-options.pac diff --git a/src/analyzer/protocol/dhcp/events.bif b/src/analyzer/protocol/dhcp/events.bif index bbd27c71f7..1d2065d75e 100644 --- a/src/analyzer/protocol/dhcp/events.bif +++ b/src/analyzer/protocol/dhcp/events.bif @@ -1,164 +1,12 @@ -## Generated for DHCP messages of type *DHCPDISCOVER* (client broadcast to locate -## available servers). +## Generated for all DHCP messages. ## ## c: The connection record describing the underlying UDP flow. ## -## msg: The parsed type-independent part of the DHCP message. +## is_orig: Indicate if the message came in a packet from the +## originator/client of the udp flow or the responder/server. ## -## req_addr: The specific address requested by the client. +## msg: The parsed type-independent part of the DHCP message. The message +## type is indicated in this record. ## -## 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. -## -event dhcp_discover%(c: connection, msg: dhcp_msg, req_addr: addr, host_name: string%); - -## 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: The parsed type-independent part of the DHCP message. -## -## mask: The subnet mask specified by the message. -## -## router: The list of routers specified by the message. -## -## lease: The least interval specified by the message. -## -## serv_addr: The server address specified by the message. -## -## host_name: Optional host name value. May differ from the host name requested -## from 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. -## -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 *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. -## -## msg: The parsed type-independent part of the DHCP message. -## -## req_addr: The client address specified by the message. -## -## serv_addr: The server address specified by the message. -## -## 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. -## -event dhcp_request%(c: connection, msg: dhcp_msg, req_addr: addr, serv_addr: addr, host_name: string%); - -## 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. -## -## host_name: Optional host name value. -## -## .. 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. -## -event dhcp_decline%(c: connection, msg: dhcp_msg, host_name: string%); - -## 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. -## -## msg: The parsed type-independent part of the DHCP message. -## -## mask: The subnet mask specified by the message. -## -## router: The list of routers specified by the message. -## -## lease: The least interval specified by the message. -## -## serv_addr: The server address specified by the message. -## -## host_name: Optional host name value. May differ from the host name requested -## from the client. -## -## .. bro:see:: dhcp_discover dhcp_offer dhcp_request dhcp_decline dhcp_nak -## dhcp_release dhcp_inform -## -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 *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. -## -## host_name: Optional host name value. -## -## .. 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. -## -event dhcp_nak%(c: connection, msg: dhcp_msg, host_name: string%); - -## 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. -## -## 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_inform -## -event dhcp_release%(c: connection, msg: dhcp_msg, host_name: string%); - -## 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. -## -## 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. -## -event dhcp_inform%(c: connection, msg: dhcp_msg, host_name: string%); - +## options: The full set of supported and parsed DHCP options. +event dhcp_message%(c: connection, is_orig: bool, msg: DHCP::Msg, options: DHCP::Options%); diff --git a/src/analyzer/protocol/dhcp/types.bif b/src/analyzer/protocol/dhcp/types.bif new file mode 100644 index 0000000000..d0062e312c --- /dev/null +++ b/src/analyzer/protocol/dhcp/types.bif @@ -0,0 +1,9 @@ +module DHCP; + +type Msg: record; +type Addrs: vector; +type SubOpt: record; +type SubOpts: vector; +type ClientFQDN: record; +type ClientID: record; +type Options: record; diff --git a/testing/btest/Baseline/core.print-bpf-filters/output2 b/testing/btest/Baseline/core.print-bpf-filters/output2 index 53d4189bf7..af9ccda1ae 100644 --- a/testing/btest/Baseline/core.print-bpf-filters/output2 +++ b/testing/btest/Baseline/core.print-bpf-filters/output2 @@ -17,6 +17,7 @@ 1 3306 1 3389 1 3544 +1 4011 2 443 1 502 1 5060 @@ -51,8 +52,8 @@ 1 992 1 993 1 995 -58 and -57 or -58 port +59 and +58 or +59 port 40 tcp -18 udp +19 udp diff --git a/testing/btest/Baseline/coverage.bare-load-baseline/canonified_loaded_scripts.log b/testing/btest/Baseline/coverage.bare-load-baseline/canonified_loaded_scripts.log index 6a9666dab1..36e8934e74 100644 --- a/testing/btest/Baseline/coverage.bare-load-baseline/canonified_loaded_scripts.log +++ b/testing/btest/Baseline/coverage.bare-load-baseline/canonified_loaded_scripts.log @@ -3,7 +3,7 @@ #empty_field (empty) #unset_field - #path loaded_scripts -#open 2018-02-05-22-27-42 +#open 2018-03-01-16-07-03 #fields name #types string scripts/base/init-bare.bro @@ -68,6 +68,7 @@ scripts/base/init-bare.bro build/scripts/base/bif/plugins/Bro_DCE_RPC.types.bif.bro build/scripts/base/bif/plugins/Bro_DCE_RPC.events.bif.bro build/scripts/base/bif/plugins/Bro_DHCP.events.bif.bro + build/scripts/base/bif/plugins/Bro_DHCP.types.bif.bro build/scripts/base/bif/plugins/Bro_DNP3.events.bif.bro build/scripts/base/bif/plugins/Bro_DNS.events.bif.bro build/scripts/base/bif/plugins/Bro_File.events.bif.bro @@ -173,4 +174,4 @@ scripts/base/init-bare.bro build/scripts/base/bif/plugins/Bro_SQLiteWriter.sqlite.bif.bro scripts/policy/misc/loaded-scripts.bro scripts/base/utils/paths.bro -#close 2018-02-05-22-27-42 +#close 2018-03-01-16-07-03 diff --git a/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log b/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log index 8a56fcfbac..10985d24f7 100644 --- a/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log +++ b/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log @@ -3,7 +3,7 @@ #empty_field (empty) #unset_field - #path loaded_scripts -#open 2018-02-05-22-27-48 +#open 2018-03-01-16-09-46 #fields name #types string scripts/base/init-bare.bro @@ -68,6 +68,7 @@ scripts/base/init-bare.bro build/scripts/base/bif/plugins/Bro_DCE_RPC.types.bif.bro build/scripts/base/bif/plugins/Bro_DCE_RPC.events.bif.bro build/scripts/base/bif/plugins/Bro_DHCP.events.bif.bro + build/scripts/base/bif/plugins/Bro_DHCP.types.bif.bro build/scripts/base/bif/plugins/Bro_DNP3.events.bif.bro build/scripts/base/bif/plugins/Bro_DNS.events.bif.bro build/scripts/base/bif/plugins/Bro_File.events.bif.bro @@ -275,7 +276,6 @@ scripts/base/init-default.bro scripts/base/protocols/dhcp/__load__.bro scripts/base/protocols/dhcp/consts.bro scripts/base/protocols/dhcp/main.bro - scripts/base/protocols/dhcp/utils.bro scripts/base/protocols/dnp3/__load__.bro scripts/base/protocols/dnp3/main.bro scripts/base/protocols/dnp3/consts.bro @@ -365,4 +365,4 @@ scripts/base/init-default.bro scripts/base/misc/find-filtered-trace.bro scripts/base/misc/version.bro scripts/policy/misc/loaded-scripts.bro -#close 2018-02-05-22-27-48 +#close 2018-03-01-16-09-46 diff --git a/testing/btest/Baseline/coverage.find-bro-logs/out b/testing/btest/Baseline/coverage.find-bro-logs/out index 1433646a9f..03bdb1b0e3 100644 --- a/testing/btest/Baseline/coverage.find-bro-logs/out +++ b/testing/btest/Baseline/coverage.find-bro-logs/out @@ -15,7 +15,6 @@ http intel irc known_certs -known_devices known_hosts known_modbus known_services diff --git a/testing/btest/Baseline/plugins.hooks/output b/testing/btest/Baseline/plugins.hooks/output index c10c82f6d1..9d53995f7c 100644 --- a/testing/btest/Baseline/plugins.hooks/output +++ b/testing/btest/Baseline/plugins.hooks/output @@ -4,6 +4,7 @@ 0.000000 MetaHookPost CallFunction(Analyzer::__disable_analyzer, , (Analyzer::ANALYZER_TCPSTATS)) -> 0.000000 MetaHookPost CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_AYIYA, 5072/udp)) -> 0.000000 MetaHookPost CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_DCE_RPC, 135/tcp)) -> +0.000000 MetaHookPost CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_DHCP, 4011/udp)) -> 0.000000 MetaHookPost CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_DHCP, 67/udp)) -> 0.000000 MetaHookPost CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_DHCP, 68/udp)) -> 0.000000 MetaHookPost CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_DNP3_TCP, 20000/tcp)) -> @@ -66,6 +67,7 @@ 0.000000 MetaHookPost CallFunction(Analyzer::disable_analyzer, , (Analyzer::ANALYZER_TCPSTATS)) -> 0.000000 MetaHookPost CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_AYIYA, 5072/udp)) -> 0.000000 MetaHookPost CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_DCE_RPC, 135/tcp)) -> +0.000000 MetaHookPost CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_DHCP, 4011/udp)) -> 0.000000 MetaHookPost CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_DHCP, 67/udp)) -> 0.000000 MetaHookPost CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_DHCP, 68/udp)) -> 0.000000 MetaHookPost CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_DNP3_TCP, 20000/tcp)) -> @@ -258,7 +260,7 @@ 0.000000 MetaHookPost CallFunction(Log::__create_stream, , (Weird::LOG, [columns=, ev=Weird::log_weird, path=weird])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, , (X509::LOG, [columns=, ev=X509::log_x509, path=x509])) -> 0.000000 MetaHookPost CallFunction(Log::__create_stream, , (mysql::LOG, [columns=, ev=MySQL::log_mysql, path=mysql])) -> -0.000000 MetaHookPost CallFunction(Log::__write, , (PacketFilter::LOG, [ts=1517966765.39294, node=bro, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::__write, , (PacketFilter::LOG, [ts=1519920860.516463, node=bro, filter=ip or not ip, init=T, success=T])) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, , (Cluster::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, , (Communication::LOG)) -> 0.000000 MetaHookPost CallFunction(Log::add_default_filter, , (Config::LOG)) -> @@ -435,7 +437,7 @@ 0.000000 MetaHookPost CallFunction(Log::create_stream, , (Weird::LOG, [columns=, ev=Weird::log_weird, path=weird])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, , (X509::LOG, [columns=, ev=X509::log_x509, path=x509])) -> 0.000000 MetaHookPost CallFunction(Log::create_stream, , (mysql::LOG, [columns=, ev=MySQL::log_mysql, path=mysql])) -> -0.000000 MetaHookPost CallFunction(Log::write, , (PacketFilter::LOG, [ts=1517966765.39294, node=bro, filter=ip or not ip, init=T, success=T])) -> +0.000000 MetaHookPost CallFunction(Log::write, , (PacketFilter::LOG, [ts=1519920860.516463, node=bro, filter=ip or not ip, init=T, success=T])) -> 0.000000 MetaHookPost CallFunction(NetControl::check_plugins, , ()) -> 0.000000 MetaHookPost CallFunction(NetControl::init, , ()) -> 0.000000 MetaHookPost CallFunction(Notice::want_pp, , ()) -> @@ -488,6 +490,7 @@ 0.000000 MetaHookPost LoadFile(0, .<...>/Bro_DCE_RPC.events.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/Bro_DCE_RPC.types.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/Bro_DHCP.events.bif.bro) -> -1 +0.000000 MetaHookPost LoadFile(0, .<...>/Bro_DHCP.types.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/Bro_DNP3.events.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/Bro_DNS.events.bif.bro) -> -1 0.000000 MetaHookPost LoadFile(0, .<...>/Bro_FTP.events.bif.bro) -> -1 @@ -795,6 +798,7 @@ 0.000000 MetaHookPre CallFunction(Analyzer::__disable_analyzer, , (Analyzer::ANALYZER_TCPSTATS)) 0.000000 MetaHookPre CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_AYIYA, 5072/udp)) 0.000000 MetaHookPre CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_DCE_RPC, 135/tcp)) +0.000000 MetaHookPre CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_DHCP, 4011/udp)) 0.000000 MetaHookPre CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_DHCP, 67/udp)) 0.000000 MetaHookPre CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_DHCP, 68/udp)) 0.000000 MetaHookPre CallFunction(Analyzer::__register_for_port, , (Analyzer::ANALYZER_DNP3_TCP, 20000/tcp)) @@ -857,6 +861,7 @@ 0.000000 MetaHookPre CallFunction(Analyzer::disable_analyzer, , (Analyzer::ANALYZER_TCPSTATS)) 0.000000 MetaHookPre CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_AYIYA, 5072/udp)) 0.000000 MetaHookPre CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_DCE_RPC, 135/tcp)) +0.000000 MetaHookPre CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_DHCP, 4011/udp)) 0.000000 MetaHookPre CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_DHCP, 67/udp)) 0.000000 MetaHookPre CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_DHCP, 68/udp)) 0.000000 MetaHookPre CallFunction(Analyzer::register_for_port, , (Analyzer::ANALYZER_DNP3_TCP, 20000/tcp)) @@ -1049,7 +1054,7 @@ 0.000000 MetaHookPre CallFunction(Log::__create_stream, , (Weird::LOG, [columns=, ev=Weird::log_weird, path=weird])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, , (X509::LOG, [columns=, ev=X509::log_x509, path=x509])) 0.000000 MetaHookPre CallFunction(Log::__create_stream, , (mysql::LOG, [columns=, ev=MySQL::log_mysql, path=mysql])) -0.000000 MetaHookPre CallFunction(Log::__write, , (PacketFilter::LOG, [ts=1517966765.39294, node=bro, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::__write, , (PacketFilter::LOG, [ts=1519920860.516463, node=bro, filter=ip or not ip, init=T, success=T])) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, , (Cluster::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, , (Communication::LOG)) 0.000000 MetaHookPre CallFunction(Log::add_default_filter, , (Config::LOG)) @@ -1226,7 +1231,7 @@ 0.000000 MetaHookPre CallFunction(Log::create_stream, , (Weird::LOG, [columns=, ev=Weird::log_weird, path=weird])) 0.000000 MetaHookPre CallFunction(Log::create_stream, , (X509::LOG, [columns=, ev=X509::log_x509, path=x509])) 0.000000 MetaHookPre CallFunction(Log::create_stream, , (mysql::LOG, [columns=, ev=MySQL::log_mysql, path=mysql])) -0.000000 MetaHookPre CallFunction(Log::write, , (PacketFilter::LOG, [ts=1517966765.39294, node=bro, filter=ip or not ip, init=T, success=T])) +0.000000 MetaHookPre CallFunction(Log::write, , (PacketFilter::LOG, [ts=1519920860.516463, node=bro, filter=ip or not ip, init=T, success=T])) 0.000000 MetaHookPre CallFunction(NetControl::check_plugins, , ()) 0.000000 MetaHookPre CallFunction(NetControl::init, , ()) 0.000000 MetaHookPre CallFunction(Notice::want_pp, , ()) @@ -1279,6 +1284,7 @@ 0.000000 MetaHookPre LoadFile(0, .<...>/Bro_DCE_RPC.events.bif.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/Bro_DCE_RPC.types.bif.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/Bro_DHCP.events.bif.bro) +0.000000 MetaHookPre LoadFile(0, .<...>/Bro_DHCP.types.bif.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/Bro_DNP3.events.bif.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/Bro_DNS.events.bif.bro) 0.000000 MetaHookPre LoadFile(0, .<...>/Bro_FTP.events.bif.bro) @@ -1586,6 +1592,7 @@ 0.000000 | HookCallFunction Analyzer::__disable_analyzer(Analyzer::ANALYZER_TCPSTATS) 0.000000 | HookCallFunction Analyzer::__register_for_port(Analyzer::ANALYZER_AYIYA, 5072/udp) 0.000000 | HookCallFunction Analyzer::__register_for_port(Analyzer::ANALYZER_DCE_RPC, 135/tcp) +0.000000 | HookCallFunction Analyzer::__register_for_port(Analyzer::ANALYZER_DHCP, 4011/udp) 0.000000 | HookCallFunction Analyzer::__register_for_port(Analyzer::ANALYZER_DHCP, 67/udp) 0.000000 | HookCallFunction Analyzer::__register_for_port(Analyzer::ANALYZER_DHCP, 68/udp) 0.000000 | HookCallFunction Analyzer::__register_for_port(Analyzer::ANALYZER_DNP3_TCP, 20000/tcp) @@ -1648,6 +1655,7 @@ 0.000000 | HookCallFunction Analyzer::disable_analyzer(Analyzer::ANALYZER_TCPSTATS) 0.000000 | HookCallFunction Analyzer::register_for_port(Analyzer::ANALYZER_AYIYA, 5072/udp) 0.000000 | HookCallFunction Analyzer::register_for_port(Analyzer::ANALYZER_DCE_RPC, 135/tcp) +0.000000 | HookCallFunction Analyzer::register_for_port(Analyzer::ANALYZER_DHCP, 4011/udp) 0.000000 | HookCallFunction Analyzer::register_for_port(Analyzer::ANALYZER_DHCP, 67/udp) 0.000000 | HookCallFunction Analyzer::register_for_port(Analyzer::ANALYZER_DHCP, 68/udp) 0.000000 | HookCallFunction Analyzer::register_for_port(Analyzer::ANALYZER_DNP3_TCP, 20000/tcp) @@ -1839,7 +1847,7 @@ 0.000000 | HookCallFunction Log::__create_stream(Weird::LOG, [columns=, ev=Weird::log_weird, path=weird]) 0.000000 | HookCallFunction Log::__create_stream(X509::LOG, [columns=, ev=X509::log_x509, path=x509]) 0.000000 | HookCallFunction Log::__create_stream(mysql::LOG, [columns=, ev=MySQL::log_mysql, path=mysql]) -0.000000 | HookCallFunction Log::__write(PacketFilter::LOG, [ts=1517966765.39294, node=bro, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::__write(PacketFilter::LOG, [ts=1519920860.516463, node=bro, filter=ip or not ip, init=T, success=T]) 0.000000 | HookCallFunction Log::add_default_filter(Cluster::LOG) 0.000000 | HookCallFunction Log::add_default_filter(Communication::LOG) 0.000000 | HookCallFunction Log::add_default_filter(Config::LOG) @@ -2016,7 +2024,7 @@ 0.000000 | HookCallFunction Log::create_stream(Weird::LOG, [columns=, ev=Weird::log_weird, path=weird]) 0.000000 | HookCallFunction Log::create_stream(X509::LOG, [columns=, ev=X509::log_x509, path=x509]) 0.000000 | HookCallFunction Log::create_stream(mysql::LOG, [columns=, ev=MySQL::log_mysql, path=mysql]) -0.000000 | HookCallFunction Log::write(PacketFilter::LOG, [ts=1517966765.39294, node=bro, filter=ip or not ip, init=T, success=T]) +0.000000 | HookCallFunction Log::write(PacketFilter::LOG, [ts=1519920860.516463, node=bro, filter=ip or not ip, init=T, success=T]) 0.000000 | HookCallFunction NetControl::check_plugins() 0.000000 | HookCallFunction NetControl::init() 0.000000 | HookCallFunction Notice::want_pp() @@ -2069,6 +2077,7 @@ 0.000000 | HookLoadFile .<...>/Bro_DCE_RPC.events.bif.bro 0.000000 | HookLoadFile .<...>/Bro_DCE_RPC.types.bif.bro 0.000000 | HookLoadFile .<...>/Bro_DHCP.events.bif.bro +0.000000 | HookLoadFile .<...>/Bro_DHCP.types.bif.bro 0.000000 | HookLoadFile .<...>/Bro_DNP3.events.bif.bro 0.000000 | HookLoadFile .<...>/Bro_DNS.events.bif.bro 0.000000 | HookLoadFile .<...>/Bro_FTP.events.bif.bro @@ -2366,7 +2375,7 @@ 0.000000 | HookLoadFile base<...>/x509 0.000000 | HookLoadFile base<...>/xmpp 0.000000 | HookLogInit packet_filter 1/1 {ts (time), node (string), filter (string), init (bool), success (bool)} -0.000000 | HookLogWrite packet_filter [ts=1517966765.392940, node=bro, filter=ip or not ip, init=T, success=T] +0.000000 | HookLogWrite packet_filter [ts=1519920860.516463, node=bro, filter=ip or not ip, init=T, success=T] 0.000000 | HookQueueEvent NetControl::init() 0.000000 | HookQueueEvent bro_init() 0.000000 | HookQueueEvent filter_change_tracking() diff --git a/testing/btest/Baseline/scripts.base.protocols.dhcp.dhcp-ack-msg-types/dhcp.log b/testing/btest/Baseline/scripts.base.protocols.dhcp.dhcp-ack-msg-types/dhcp.log new file mode 100644 index 0000000000..7131010acb --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dhcp.dhcp-ack-msg-types/dhcp.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path dhcp +#open 2018-03-01-15-18-30 +#fields ts uids client_addr server_addr mac host_name client_fqdn domain requested_addr assigned_addr lease_time client_message server_message msg_types duration +#types time set[string] addr addr string string string string addr addr interval string string vector[string] interval +1102274184.387798 CHhAvVGS1DHFjwGM9 192.168.0.10 10.10.0.1 00:0a:28:00:fa:42 - - - - 192.168.0.10 3600.000000 - - ACK 0.000000 +#close 2018-03-01-15-18-30 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 index 5f7aeee659..cf36777c6d 100644 --- 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 @@ -3,8 +3,10 @@ #empty_field (empty) #unset_field - #path dhcp -#open 2016-07-13-16-15-58 -#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 CtPZjS20MLrsMUOJi2 128.2.6.189 68 128.2.6.152 67 90:b1:1c:99:49:29 128.2.6.189 900.000000 1984 -#close 2016-07-13-16-15-58 +#open 2018-03-01-15-19-24 +#fields ts uids client_addr server_addr mac host_name client_fqdn domain requested_addr assigned_addr lease_time client_message server_message msg_types duration +#types time set[string] addr addr string string string string addr addr interval string string vector[string] interval +1370200447.422207 CHhAvVGS1DHFjwGM9 - - - btest.is.cool - - 128.2.6.189 - - - - INFORM 0.000000 +1370200442.323173 CtPZjS20MLrsMUOJi2,CHhAvVGS1DHFjwGM9,C4J4Th3PJpwUYZZ6gc,ClEkJM2Vm5giqnMf4h 128.2.6.97 128.2.6.152 90:b1:1c:99:49:29 btest.is.cool - cmu.edu 128.2.6.189 128.2.6.189 900.000000 - requested address not available DISCOVER,OFFER,REQUEST,NAK,REQUEST,ACK,DECLINE 3.058797 +1370200446.402928 CHhAvVGS1DHFjwGM9 - - - - - - - - - - - RELEASE 0.000000 +#close 2018-03-01-15-19-24 diff --git a/testing/btest/Baseline/scripts.base.protocols.dhcp.dhcp-discover-msg-types/dhcp.log b/testing/btest/Baseline/scripts.base.protocols.dhcp.dhcp-discover-msg-types/dhcp.log new file mode 100644 index 0000000000..48c6f36cf1 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dhcp.dhcp-discover-msg-types/dhcp.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path dhcp +#open 2018-03-01-15-30-31 +#fields ts uids client_addr server_addr mac host_name client_fqdn domain requested_addr assigned_addr lease_time client_message server_message msg_types duration +#types time set[string] addr addr string string string string addr addr interval string string vector[string] interval +1102274184.317453 CHhAvVGS1DHFjwGM9 - - 00:0b:82:01:fc:42 test0000 - - 208.67.222.222 - - - - DISCOVER 0.000000 +#close 2018-03-01-15-30-31 diff --git a/testing/btest/Baseline/scripts.base.protocols.dhcp.dhcp-sub-opts/dhcp.log b/testing/btest/Baseline/scripts.base.protocols.dhcp.dhcp-sub-opts/dhcp.log new file mode 100644 index 0000000000..5534459dd2 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dhcp.dhcp-sub-opts/dhcp.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path dhcp +#open 2018-03-01-15-30-58 +#fields ts uids client_addr server_addr mac host_name client_fqdn domain requested_addr assigned_addr lease_time client_message server_message msg_types duration circuit_id agent_remote_id subscriber_id +#types time set[string] addr addr string string string string addr addr interval string string vector[string] interval string string string +1102274184.387798 CHhAvVGS1DHFjwGM9 192.168.0.10 10.10.0.1 00:0a:28:00:fa:42 - - - - 192.168.0.10 3600.000000 - - ACK 0.000000 this is only a test... \x13 -subID- +#close 2018-03-01-15-30-58 diff --git a/testing/btest/Baseline/scripts.base.protocols.dhcp.inform/dhcp.log b/testing/btest/Baseline/scripts.base.protocols.dhcp.inform/dhcp.log index 0a92c6c32d..ef1f5483ce 100644 --- a/testing/btest/Baseline/scripts.base.protocols.dhcp.inform/dhcp.log +++ b/testing/btest/Baseline/scripts.base.protocols.dhcp.inform/dhcp.log @@ -3,8 +3,8 @@ #empty_field (empty) #unset_field - #path dhcp -#open 2016-07-13-16-15-59 -#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 CHhAvVGS1DHFjwGM9 128.2.6.122 68 128.2.6.152 67 90:b1:1c:99:49:29 128.2.6.122 0.000000 2754407505 -#close 2016-07-13-16-15-59 +#open 2018-03-01-15-32-52 +#fields ts uids client_addr server_addr mac host_name client_fqdn domain requested_addr assigned_addr lease_time client_message server_message msg_types duration +#types time set[string] addr addr string string string string addr addr interval string string vector[string] interval +1374432420.186878 CHhAvVGS1DHFjwGM9 128.2.6.122 - 90:b1:1c:99:49:29 - - - - - - - - INFORM,ACK 0.004327 +#close 2018-03-01-15-32-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 deleted file mode 100644 index 91d37f8950..0000000000 --- a/testing/btest/Baseline/scripts.policy.protocols.dhcp.known-devices-and-hostnames.basic/known_devices.log +++ /dev/null @@ -1,11 +0,0 @@ -#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_ack_subscriber_id_and_agent_remote_id.trace b/testing/btest/Traces/dhcp/dhcp_ack_subscriber_id_and_agent_remote_id.trace new file mode 100644 index 0000000000..b5e72d4735 Binary files /dev/null and b/testing/btest/Traces/dhcp/dhcp_ack_subscriber_id_and_agent_remote_id.trace differ diff --git a/testing/btest/Traces/dhcp/dhcp_discover_param_req_and_client_id.trace b/testing/btest/Traces/dhcp/dhcp_discover_param_req_and_client_id.trace new file mode 100644 index 0000000000..bfdbb54c01 Binary files /dev/null and b/testing/btest/Traces/dhcp/dhcp_discover_param_req_and_client_id.trace differ diff --git a/testing/btest/scripts/base/protocols/dhcp/dhcp-ack-msg-types.btest b/testing/btest/scripts/base/protocols/dhcp/dhcp-ack-msg-types.btest new file mode 100644 index 0000000000..8f192b7aa4 --- /dev/null +++ b/testing/btest/scripts/base/protocols/dhcp/dhcp-ack-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_ack_subscriber_id_and_agent_remote_id.trace %INPUT +# @TEST-EXEC: btest-diff dhcp.log diff --git a/testing/btest/scripts/base/protocols/dhcp/dhcp-discover-msg-types.btest b/testing/btest/scripts/base/protocols/dhcp/dhcp-discover-msg-types.btest new file mode 100644 index 0000000000..1952682e61 --- /dev/null +++ b/testing/btest/scripts/base/protocols/dhcp/dhcp-discover-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_discover_param_req_and_client_id.trace %INPUT +# @TEST-EXEC: btest-diff dhcp.log diff --git a/testing/btest/scripts/base/protocols/dhcp/dhcp-sub-opts.btest b/testing/btest/scripts/base/protocols/dhcp/dhcp-sub-opts.btest new file mode 100644 index 0000000000..3bd37a996b --- /dev/null +++ b/testing/btest/scripts/base/protocols/dhcp/dhcp-sub-opts.btest @@ -0,0 +1,2 @@ +# @TEST-EXEC: bro -r $TRACES/dhcp/dhcp_ack_subscriber_id_and_agent_remote_id.trace %INPUT protocols/dhcp/sub-opts +# @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 deleted file mode 100644 index 1144ae1377..0000000000 --- a/testing/btest/scripts/policy/protocols/dhcp/known-devices-and-hostnames/basic.test +++ /dev/null @@ -1,8 +0,0 @@ -# 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