diff --git a/scripts/base/init-bare.zeek b/scripts/base/init-bare.zeek index 36aa4f6f4f..ff013721e7 100644 --- a/scripts/base/init-bare.zeek +++ b/scripts/base/init-bare.zeek @@ -3708,6 +3708,14 @@ type dns_edns_tcp_keepalive: record { keepalive_timeout: count; ##< Timeout value, in 100ms }; +## An DNS EDNS COOKIE (COOKIE) record. +## +## .. zeek:see:: dns_EDNS_tcp_keepalive +type dns_edns_cookie: record { + client_cookie: string; ##< Cookie from the client (fixed 8 bytes) + server_cookie: string &default=""; ##< Cookie from the server (8 to 32 bytes) +}; + ## An additional DNS TSIG record. ## ## .. zeek:see:: dns_TSIG_addl diff --git a/scripts/base/protocols/dns/consts.zeek b/scripts/base/protocols/dns/consts.zeek index 38dd077ec5..2d39eefcb6 100644 --- a/scripts/base/protocols/dns/consts.zeek +++ b/scripts/base/protocols/dns/consts.zeek @@ -110,6 +110,7 @@ export { [20] = "BADNAME", # Duplicate key name [21] = "BADALG", # Algorithm not supported [22] = "BADTRUNC", # draft-ietf-dnsext-tsig-sha-05.txt + [23] = "BADCOOKIE", # Bad EDNS cookie value [3842] = "BADSIG", # 16 <= number collision with EDNS(16); # this is a translation from TSIG(16) } &default = function(n: count): string { return fmt("rcode-%d", n); }; diff --git a/src/NetVar.cc b/src/NetVar.cc index 6b7099d570..9244109b73 100644 --- a/src/NetVar.cc +++ b/src/NetVar.cc @@ -109,6 +109,7 @@ zeek::RecordType* dns_soa; zeek::RecordType* dns_edns_additional; zeek::RecordType* dns_edns_ecs; zeek::RecordType* dns_edns_tcp_keepalive; +zeek::RecordType* dns_edns_cookie; zeek::RecordType* dns_tsig_additional; zeek::RecordType* dns_rrsig_rr; zeek::RecordType* dns_dnskey_rr; diff --git a/src/NetVar.h b/src/NetVar.h index 6d3e22c2ff..37934629ce 100644 --- a/src/NetVar.h +++ b/src/NetVar.h @@ -153,6 +153,8 @@ extern zeek::RecordType* dns_edns_ecs; [[deprecated("Remove in v4.1. Perform your own lookup.")]] extern zeek::RecordType* dns_edns_tcp_keepalive; [[deprecated("Remove in v4.1. Perform your own lookup.")]] +extern zeek::RecordType* dns_edns_cookie; +[[deprecated("Remove in v4.1. Perform your own lookup.")]] extern zeek::RecordType* dns_tsig_additional; [[deprecated("Remove in v4.1. Perform your own lookup.")]] extern zeek::RecordType* dns_rrsig_rr; diff --git a/src/analyzer/protocol/dns/DNS.cc b/src/analyzer/protocol/dns/DNS.cc index d53abf8c9e..85733ac287 100644 --- a/src/analyzer/protocol/dns/DNS.cc +++ b/src/analyzer/protocol/dns/DNS.cc @@ -810,14 +810,50 @@ bool DNS_Interpreter::ParseRR_EDNS(DNS_MsgInfo* msg, msg->BuildHdrVal(), msg->BuildEDNS_TCP_KA_Val(&edns_tcp_keepalive) ); - break; + } else { - break; // error. MUST BE 0 or 2 bytes + // error. MUST BE 0 or 2 bytes } + data += option_len; + break; } // END EDNS TCP KEEPALIVE + case TYPE_COOKIE: + { + EDNS_COOKIE cookie{}; + if (option_len != 8 && !(option_len >= 16 && option_len <= 40)) { + /* + * option length for DNS Cookie must be 8 bytes (with client cookie only) + * OR + * between 16 bytes to 40 bytes (with an 8 bytes client and an 8 to 32 bytes + * server cookie) + */ + break; + } + int remaining_cookie_len = option_len; + int client_cookie_len = 8; + + cookie.client_cookie = ExtractStream(data, client_cookie_len, client_cookie_len); + cookie.server_cookie = nullptr; + + remaining_cookie_len -= 8; + + if (remaining_cookie_len >= 8) { + cookie.server_cookie = ExtractStream(data, remaining_cookie_len, remaining_cookie_len); + } + + analyzer->EnqueueConnEvent(dns_EDNS_cookie, + analyzer->ConnVal(), + msg->BuildHdrVal(), + msg->BuildEDNS_COOKIE_Val(&cookie) + ); + + data += option_len; + break; + } // END EDNS COOKIE + default: { data += option_len; @@ -1649,6 +1685,19 @@ zeek::RecordValPtr DNS_MsgInfo::BuildEDNS_TCP_KA_Val(struct EDNS_TCP_KEEPALIVE* return r; } +zeek::RecordValPtr DNS_MsgInfo::BuildEDNS_COOKIE_Val(struct EDNS_COOKIE* opt) + { + static auto dns_edns_cookie = zeek::id::find_type("dns_edns_cookie"); + auto r = zeek::make_intrusive(dns_edns_cookie); + + r->Assign(0, zeek::make_intrusive(opt->client_cookie)); + if (opt->server_cookie != nullptr) { + r->Assign(1, zeek::make_intrusive(opt->server_cookie)); + } + + return r; + } + zeek::RecordValPtr DNS_MsgInfo::BuildTSIG_Val(struct TSIG_DATA* tsig) { static auto dns_tsig_additional = zeek::id::find_type("dns_tsig_additional"); diff --git a/src/analyzer/protocol/dns/DNS.h b/src/analyzer/protocol/dns/DNS.h index b0d6521a89..c97d58db1f 100644 --- a/src/analyzer/protocol/dns/DNS.h +++ b/src/analyzer/protocol/dns/DNS.h @@ -29,6 +29,7 @@ typedef enum { DNS_CODE_NAME_ERR = 3, ///< no such domain DNS_CODE_NOT_IMPL = 4, ///< not implemented DNS_CODE_REFUSED = 5, ///< refused + DNS_CODE_BADCOOKIE = 23, ///< Bad cookie value (RFC 7873, IANA early allocation) } DNS_Code; typedef enum { @@ -92,6 +93,7 @@ typedef enum { TYPE_N3U = 7, ///< RFC6975 TYPE_ECS = 8, ///< RFC7871 TYPE_EXPIRE = 9, ///< RFC7314 + TYPE_COOKIE = 10, ///< RFC7873 TYPE_TCP_KA = 11, ///< RFC7828 TYPE_PAD = 12, ///< RFC7830 TYPE_CHAIN = 13, ///< RFC7901 @@ -161,6 +163,11 @@ struct EDNS_TCP_KEEPALIVE { uint16_t keepalive_timeout; // the timeout value (in 100ms) sent by the client/server }; +struct EDNS_COOKIE { + zeek::String* client_cookie; + zeek::String* server_cookie; +}; + struct TSIG_DATA { zeek::String* alg_name; unsigned long time_s; @@ -217,6 +224,7 @@ public: zeek::RecordValPtr BuildEDNS_Val(); zeek::RecordValPtr BuildEDNS_ECS_Val(struct EDNS_ECS*); zeek::RecordValPtr BuildEDNS_TCP_KA_Val(struct EDNS_TCP_KEEPALIVE*); + zeek::RecordValPtr BuildEDNS_COOKIE_Val(struct EDNS_COOKIE*); zeek::RecordValPtr BuildTSIG_Val(struct TSIG_DATA*); zeek::RecordValPtr BuildRRSIG_Val(struct RRSIG_DATA*); zeek::RecordValPtr BuildDNSKEY_Val(struct DNSKEY_DATA*); diff --git a/src/analyzer/protocol/dns/events.bif b/src/analyzer/protocol/dns/events.bif index 0468eb919b..2567a9de9f 100644 --- a/src/analyzer/protocol/dns/events.bif +++ b/src/analyzer/protocol/dns/events.bif @@ -551,6 +551,29 @@ event dns_EDNS_ecs%(c: connection, msg: dns_msg, opt: dns_edns_ecs%); ## dns_skip_all_addl dns_skip_all_auth dns_skip_auth event dns_EDNS_tcp_keepalive%(c: connection, msg: dns_msg, opt: dns_edns_tcp_keepalive%); +## Generated for DNS replies of type *EDNS*. For replies with multiple options, +## an individual event is raised for each. +## +## See `Wikipedia `__ for more +## information about the DNS protocol. Zeek analyzes both UDP and TCP DNS +## sessions. +## +## c: The connection, which may be UDP or TCP depending on the type of the +## transport-layer session being analyzed. +## +## msg: The parsed DNS message header. +## +## opt: The parsed EDNS option. +## +## .. zeek:see:: dns_AAAA_reply dns_A_reply dns_CNAME_reply dns_HINFO_reply dns_MX_reply +## dns_NS_reply dns_PTR_reply dns_SOA_reply dns_SRV_reply dns_TSIG_addl +## dns_TXT_reply dns_SPF_reply dns_WKS_reply dns_end dns_mapping_altered +## dns_mapping_lost_name dns_mapping_new_name dns_mapping_unverified +## dns_mapping_valid dns_message dns_query_reply dns_rejected dns_request +## dns_max_queries dns_session_timeout dns_skip_addl +## dns_skip_all_addl dns_skip_all_auth dns_skip_auth +event dns_EDNS_cookie%(c: connection, msg: dns_msg, opt: dns_edns_cookie%); + ## Generated for DNS replies of type *TSIG*. For replies with multiple answers, ## an individual event of the corresponding type is raised for each. ## diff --git a/src/legacy-netvar-init.cc b/src/legacy-netvar-init.cc index eaa6b231ad..572fe950bc 100644 --- a/src/legacy-netvar-init.cc +++ b/src/legacy-netvar-init.cc @@ -44,6 +44,7 @@ void zeek_legacy_netvar_init() ::dns_edns_additional = zeek::id::find_type("dns_edns_additional")->AsRecordType(); ::dns_edns_ecs = zeek::id::find_type("dns_edns_ecs")->AsRecordType(); ::dns_edns_tcp_keepalive = zeek::id::find_type("dns_edns_tcp_keepalive")->AsRecordType(); + ::dns_edns_cookie = zeek::id::find_type("dns_edns_cookie")->AsRecordType(); ::dns_tsig_additional = zeek::id::find_type("dns_tsig_additional")->AsRecordType(); ::dns_rrsig_rr = zeek::id::find_type("dns_rrsig_rr")->AsRecordType(); ::dns_dnskey_rr = zeek::id::find_type("dns_dnskey_rr")->AsRecordType();