diff --git a/src/IP.cc b/src/IP.cc index 149be6a41c..3ee9134304 100644 --- a/src/IP.cc +++ b/src/IP.cc @@ -619,6 +619,12 @@ void IPv6_Hdr_Chain::ProcessRoutingHeader(const struct ip6_rthdr* r, uint16_t le #ifdef ENABLE_MOBILE_IPV6 void IPv6_Hdr_Chain::ProcessDstOpts(const struct ip6_dest* d, uint16_t len) { + // Skip two bytes to get the beginning of the first option structure. These + // two bytes are the protocol for the next header and extension header length, + // already known to exist before calling this method. See header format: + // https://datatracker.ietf.org/doc/html/rfc8200#section-4.6 + assert(len >= 2); + const u_char* data = (const u_char*) d; len -= 2 * sizeof(uint8_t); data += 2* sizeof(uint8_t); @@ -627,34 +633,45 @@ void IPv6_Hdr_Chain::ProcessDstOpts(const struct ip6_dest* d, uint16_t len) { const struct ip6_opt* opt = (const struct ip6_opt*) data; switch ( opt->ip6o_type ) { - case 201: // Home Address Option, Mobile IPv6 RFC 6275 section 6.3 - { - if ( opt->ip6o_len == 16 ) - if ( homeAddr ) - reporter->Weird(SrcAddr(), DstAddr(), "multiple_home_addr_opts"); - else - homeAddr = new IPAddr(*((const in6_addr*)(data + 2))); - else - reporter->Weird(SrcAddr(), DstAddr(), "bad_home_addr_len"); - } - break; - - default: - break; - } - - if ( opt->ip6o_type == 0 ) - { + case 0: + // If option type is zero, it's a Pad0 and can be just a single + // byte in width. Skip over it. data += sizeof(uint8_t); len -= sizeof(uint8_t); - } - else + break; + default: { - data += 2 * sizeof(uint8_t) + opt->ip6o_len; - len -= 2 * sizeof(uint8_t) + opt->ip6o_len; + // Double-check that the len can hold the whole option structure. + // Otherwise we get a buffer-overflow when we check the option_len. + // Also check that it holds everything for the option itself. + if ( len < sizeof(struct ip6_opt) || + len < sizeof(struct ip6_opt) + opt->ip6o_len ) + { + reporter->Weird(SrcAddr(), DstAddr(), "bad_ipv6_dest_opt_len"); + len = 0; + break; + } + + if ( opt->ip6o_type == 201 ) // Home Address Option, Mobile IPv6 RFC 6275 section 6.3 + { + if ( opt->ip6o_len == sizeof(struct in6_addr) ) + { + if ( homeAddr ) + reporter->Weird(SrcAddr(), DstAddr(), "multiple_home_addr_opts"); + else + homeAddr = new IPAddr(*((const in6_addr*)(data + sizeof(struct ip6_opt)))); + } + else + reporter->Weird(SrcAddr(), DstAddr(), "bad_home_addr_len"); + } + + data += sizeof(struct ip6_opt) + opt->ip6o_len; + len -= sizeof(struct ip6_opt) + opt->ip6o_len; } + break; } } + } #endif VectorValPtr IPv6_Hdr_Chain::ToVal() const diff --git a/testing/btest/Baseline/core.mobile-ipv6-dst-opts/weird.log b/testing/btest/Baseline/core.mobile-ipv6-dst-opts/weird.log new file mode 100644 index 0000000000..ffb7b57b55 --- /dev/null +++ b/testing/btest/Baseline/core.mobile-ipv6-dst-opts/weird.log @@ -0,0 +1,14 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path weird +#open XXXX-XX-XX-XX-XX-XX +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p name addl notice peer source +#types time string addr port addr port string string bool string string +XXXXXXXXXX.XXXXXX - 2001:4f8:4:7:2e0:81ff:fe52:ffff 0 2001:4f8:4:7:2e0:81ff:fe52:9a6b 0 bad_ipv6_dest_opt_len - F zeek - +XXXXXXXXXX.XXXXXX - 2001:78:1:32::1 0 2001:4f8:4:7:2e0:81ff:fe52:9a6b 0 multiple_home_addr_opts - F zeek - +XXXXXXXXXX.XXXXXX - 2001:78:1:32::1 0 2001:4f8:4:7:2e0:81ff:fe52:9a6b 0 invalid_IP_header_size - F zeek IP +XXXXXXXXXX.XXXXXX - 2001:4f8:4:7:2e0:81ff:fe52:ffff 0 2001:4f8:4:7:2e0:81ff:fe52:9a6b 0 bad_home_addr_len - F zeek - +#close XXXX-XX-XX-XX-XX-XX diff --git a/testing/btest/Traces/ipv6-mobility-dst-opts.trace b/testing/btest/Traces/ipv6-mobility-dst-opts.trace new file mode 100755 index 0000000000..b660885ff8 Binary files /dev/null and b/testing/btest/Traces/ipv6-mobility-dst-opts.trace differ diff --git a/testing/btest/core/mobile-ipv6-dst-opts.test b/testing/btest/core/mobile-ipv6-dst-opts.test new file mode 100644 index 0000000000..be6581f7b3 --- /dev/null +++ b/testing/btest/core/mobile-ipv6-dst-opts.test @@ -0,0 +1,4 @@ +# @TEST-REQUIRES: grep -q "#define ENABLE_MOBILE_IPV6" $BUILD/zeek-config.h +# +# @TEST-EXEC: zeek -C -r $TRACES/ipv6-mobility-dst-opts.trace +# @TEST-EXEC: btest-diff weird.log