diff --git a/.gitignore b/.gitignore index 8dacf57dc7..d962c792a4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,7 @@ tmp # Configuration and build directories for CLion .idea -cmake-build-debug \ No newline at end of file +cmake-build-debug + +# skip DS Store for MacOS +.DS_Store diff --git a/CHANGES b/CHANGES index 267d360cb0..4333335d76 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,25 @@ +3.3.0-dev.135 | 2020-08-20 10:23:29 -0700 + + * remove variables in netvar, use tabs in DNS.h and polish comments in dns events. (FlyingWithJerome) + + * add new line at the end of the test baseline (FlyingWithJerome) + + * add data+=option_len to skip unknown bytes (FlyingWithJerome) + + * better explanation to server cookie (FlyingWithJerome) + + * add edns-cookie testcase (FlyingWithJerome) + + * remove data+=option_len error in cookie and keepalive (FlyingWithJerome) + + * add EDNS cookie parsing (FlyingWithJerome) + + * add testcases (FlyingWithJerome) + + * add units (100ms) to comments (FlyingWithJerome) + + * initial adding EDNS TCP keepalive (FlyingWithJerome) + 3.3.0-dev.119 | 2020-08-19 14:35:51 -0700 * Misc strings.bif adjustments diff --git a/NEWS b/NEWS index 9439c757c6..1367b16894 100644 --- a/NEWS +++ b/NEWS @@ -9,7 +9,7 @@ Zeek 4.0.0 New Functionality ----------------- -TODO: nothing notable yet +- Added support for EDNS0 Cookie and Keep-Alive options. Changed Functionality --------------------- diff --git a/VERSION b/VERSION index 0c4f3b7266..f715f54dda 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.3.0-dev.119 +3.3.0-dev.135 diff --git a/doc b/doc index 5abdcc5614..db50308120 160000 --- a/doc +++ b/doc @@ -1 +1 @@ -Subproject commit 5abdcc5614cbd87d9edb3d41ffb9abf3674ea822 +Subproject commit db50308120cb39206aa0ab91c9789f87d92c7faf diff --git a/scripts/base/init-bare.zeek b/scripts/base/init-bare.zeek index d99dbcc346..81b0f85532 100644 --- a/scripts/base/init-bare.zeek +++ b/scripts/base/init-bare.zeek @@ -3700,6 +3700,22 @@ type dns_edns_ecs: record { address: string; ##< Client Subnet Address. }; +## An DNS EDNS TCP KEEPALIVE (TCP KEEPALIVE) record. +## +## .. zeek:see:: dns_EDNS_tcp_keepalive +type dns_edns_tcp_keepalive: record { + keepalive_timeout_omitted: bool; ##< Whether timeout value is omitted. + keepalive_timeout: count; ##< Timeout value, in 100ms. +}; + +## An DNS EDNS COOKIE (COOKIE) record. +## +## .. zeek:see:: dns_EDNS_cookie +type dns_edns_cookie: record { + client_cookie: string; ##< Cookie from the client (fixed 8 bytes). + server_cookie: string &default=""; ##< Cookie from the server (0 bytes if missing, or 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/analyzer/protocol/dns/DNS.cc b/src/analyzer/protocol/dns/DNS.cc index fb4c33954a..3d8d8cc0ab 100644 --- a/src/analyzer/protocol/dns/DNS.cc +++ b/src/analyzer/protocol/dns/DNS.cc @@ -700,7 +700,6 @@ bool DNS_Interpreter::ParseRR_EDNS(DNS_MsgInfo* msg, const u_char*& data, int& len, int rdlength, const u_char* msg_start) { - if ( dns_EDNS_addl && ! msg->skip_event ) analyzer->EnqueueConnEvent(dns_EDNS_addl, analyzer->ConnVal(), @@ -714,7 +713,7 @@ bool DNS_Interpreter::ParseRR_EDNS(DNS_MsgInfo* msg, uint16_t option_code = ExtractShort(data, len); int option_len = ExtractShort(data, len); // check for invalid option length - if ( (option_len > len) || (0 == option_len) ) { + if ( (option_len > len) ) { break; } len -= option_len; @@ -783,7 +782,82 @@ bool DNS_Interpreter::ParseRR_EDNS(DNS_MsgInfo* msg, msg->BuildEDNS_ECS_Val(&opt) ); break; - } + } // END EDNS ECS + + case TYPE_TCP_KA: + { + EDNS_TCP_KEEPALIVE edns_tcp_keepalive{ + .keepalive_timeout_omitted = true, + .keepalive_timeout = 0 + }; + if ( option_len == 0 || option_len == 2) + { + // 0 bytes is permitted by RFC 7828, showing that the timeout value is omitted. + if (option_len == 2) + { + edns_tcp_keepalive.keepalive_timeout = ExtractShort(data, option_len); + edns_tcp_keepalive.keepalive_timeout_omitted = false; + } + + if (analyzer->Conn()->ConnTransport() == TRANSPORT_UDP) + { + /* + * Based on RFC 7828 (3.2.1/3.2.2), clients and servers MUST NOT + * negotiate TCP Keepalive timeout in DNS-over-UDP. + */ + analyzer->Weird("EDNS_TCP_Keepalive_In_UDP"); + } + + analyzer->EnqueueConnEvent(dns_EDNS_tcp_keepalive, + analyzer->ConnVal(), + msg->BuildHdrVal(), + msg->BuildEDNS_TCP_KA_Val(&edns_tcp_keepalive) + ); + + } + else + { + // error. MUST BE 0 or 2 bytes. skip + 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) + */ + data += option_len; + break; + } + + int client_cookie_len = 8; + int server_cookie_len = option_len - client_cookie_len; + + cookie.client_cookie = ExtractStream(data, client_cookie_len, client_cookie_len); + cookie.server_cookie = nullptr; + + if (server_cookie_len >= 8) + { + cookie.server_cookie = ExtractStream(data, server_cookie_len, server_cookie_len); + } + + analyzer->EnqueueConnEvent(dns_EDNS_cookie, + analyzer->ConnVal(), + msg->BuildHdrVal(), + msg->BuildEDNS_COOKIE_Val(&cookie) + ); + + break; + } // END EDNS COOKIE + default: { data += option_len; @@ -1604,6 +1678,30 @@ zeek::RecordValPtr DNS_MsgInfo::BuildEDNS_ECS_Val(struct EDNS_ECS* opt) return r; } +zeek::RecordValPtr DNS_MsgInfo::BuildEDNS_TCP_KA_Val(struct EDNS_TCP_KEEPALIVE* opt) + { + static auto dns_edns_tcp_keepalive = zeek::id::find_type("dns_edns_tcp_keepalive"); + auto r = zeek::make_intrusive(dns_edns_tcp_keepalive); + + r->Assign(0, zeek::val_mgr->Bool(opt->keepalive_timeout_omitted)); + r->Assign(1, zeek::val_mgr->Count(opt->keepalive_timeout)); + + 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 e74e1191b2..57b14e1e48 100644 --- a/src/analyzer/protocol/dns/DNS.h +++ b/src/analyzer/protocol/dns/DNS.h @@ -92,6 +92,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 @@ -156,6 +157,16 @@ struct EDNS_ECS { zeek::IntrusivePtr ecs_addr; ///< EDNS client subnet address }; +struct EDNS_TCP_KEEPALIVE { + bool keepalive_timeout_omitted; ///< whether the keepalive timeout is omitted + uint16_t keepalive_timeout; ///< the timeout value (in 100ms) sent by the client/server +}; + +struct EDNS_COOKIE { + zeek::String* client_cookie; ///< cookie value sent by the client (8 bytes) + zeek::String* server_cookie; ///< cookie value sent by the server (0 or 8-32 bytes) +}; + struct TSIG_DATA { zeek::String* alg_name; unsigned long time_s; @@ -211,6 +222,8 @@ public: zeek::RecordValPtr BuildAnswerVal(); 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 31e9a11625..a32a6966ba 100644 --- a/src/analyzer/protocol/dns/events.bif +++ b/src/analyzer/protocol/dns/events.bif @@ -528,6 +528,56 @@ event dns_EDNS_addl%(c: connection, msg: dns_msg, ans: dns_edns_additional%); ## dns_skip_all_addl dns_skip_all_auth dns_skip_auth event dns_EDNS_ecs%(c: connection, msg: dns_msg, opt: dns_edns_ecs%); +## Generated for DNS replies of type *EDNS*, and an option field in this *EDNS* record has +## an opt-type of 11. For replies with multiple option fields, an individual event is +## raised for each. +## +## See `Wikipedia `__ for more +## information about the DNS protocol. See `RFC7828 `__ for +## more information about EDNS0 TCP keepalive. 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 Keepalive 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_tcp_keepalive%(c: connection, msg: dns_msg, opt: dns_edns_tcp_keepalive%); + +## Generated for DNS replies of type *EDNS*, and an option field in this *EDNS* record has +## an opt-type of 10. For replies with multiple options fields, an individual event +## is raised for each. +## +## See `Wikipedia `__ for more +## information about the DNS protocol. See `RFC7873 `__ for +## more information about EDNS0 cookie. 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 Cookie 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/testing/btest/Baseline/scripts.base.protocols.dns.dns-edns-cookie/output b/testing/btest/Baseline/scripts.base.protocols.dns.dns-edns-cookie/output new file mode 100644 index 0000000000..96d28efa58 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dns.dns-edns-cookie/output @@ -0,0 +1,4 @@ +[client_cookie=\xc8\x14\x98Z\x92\x8acB, server_cookie=] +[client_cookie=\xc8\x14\x98Z\x92\x8acB, server_cookie==\xcd>O{\xa9$z] +[client_cookie=1;Pl\x0b\xdd\x04s, server_cookie=] +[client_cookie=1;Pl\x0b\xdd\x04s, server_cookie=\xb0\xb1.E\xbahYl] diff --git a/testing/btest/Baseline/scripts.base.protocols.dns.dns-edns-tcp-keepalive/output b/testing/btest/Baseline/scripts.base.protocols.dns.dns-edns-tcp-keepalive/output new file mode 100644 index 0000000000..6b4149fa12 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.dns.dns-edns-tcp-keepalive/output @@ -0,0 +1,4 @@ +[keepalive_timeout_omitted=F, keepalive_timeout=10] +[keepalive_timeout_omitted=F, keepalive_timeout=370] +[keepalive_timeout_omitted=T, keepalive_timeout=0] +[keepalive_timeout_omitted=F, keepalive_timeout=370] diff --git a/testing/btest/Traces/dns-edns-cookie.pcap b/testing/btest/Traces/dns-edns-cookie.pcap new file mode 100644 index 0000000000..07b5ab1581 Binary files /dev/null and b/testing/btest/Traces/dns-edns-cookie.pcap differ diff --git a/testing/btest/Traces/dns-edns-tcp-keepalive.pcap b/testing/btest/Traces/dns-edns-tcp-keepalive.pcap new file mode 100644 index 0000000000..07b5ab1581 Binary files /dev/null and b/testing/btest/Traces/dns-edns-tcp-keepalive.pcap differ diff --git a/testing/btest/scripts/base/protocols/dns/dns-edns-cookie.zeek b/testing/btest/scripts/base/protocols/dns/dns-edns-cookie.zeek new file mode 100644 index 0000000000..6875099ec9 --- /dev/null +++ b/testing/btest/scripts/base/protocols/dns/dns-edns-cookie.zeek @@ -0,0 +1,8 @@ +# @TEST-EXEC: zeek -C -r $TRACES/dns-edns-cookie.pcap %INPUT > output +# @TEST-EXEC: btest-diff output +@load policy/protocols/dns/auth-addl + +event dns_EDNS_cookie(c: connection, msg: dns_msg, opt: dns_edns_cookie) + { + print opt; + } \ No newline at end of file diff --git a/testing/btest/scripts/base/protocols/dns/dns-edns-tcp-keepalive.zeek b/testing/btest/scripts/base/protocols/dns/dns-edns-tcp-keepalive.zeek new file mode 100644 index 0000000000..2291bc68fe --- /dev/null +++ b/testing/btest/scripts/base/protocols/dns/dns-edns-tcp-keepalive.zeek @@ -0,0 +1,8 @@ +# @TEST-EXEC: zeek -C -r $TRACES/dns-edns-tcp-keepalive.pcap %INPUT > output +# @TEST-EXEC: btest-diff output +@load policy/protocols/dns/auth-addl + +event dns_EDNS_tcp_keepalive(c: connection, msg: dns_msg, opt: dns_edns_tcp_keepalive) + { + print opt; + } \ No newline at end of file