return sizeof(*priv) + regional_get_mem(priv->region);
}
+/**
+ * Check if svcparam ipv4hint contains a private address.
+ * @param priv: private address lookup struct.
+ * @param d: the data bytes.
+ * @param data_len: number of data bytes in the svcparam.
+ * @param addr: address to return the private address to log in to.
+ * It has space for IPv4 and IPv6 addresses.
+ * @param addrlen: length of the addr. Returns the correct size for the addr.
+ * @return true if the rdata contains a private address.
+ */
+static int svcb_ipv4hint_contains_priv_addr(struct iter_priv* priv,
+ uint8_t* d, uint16_t data_len, struct sockaddr_storage* addr,
+ socklen_t* addrlen)
+{
+ struct sockaddr_in sa;
+ *addrlen = (socklen_t)sizeof(struct sockaddr_in);
+ memset(&sa, 0, sizeof(struct sockaddr_in));
+ sa.sin_family = AF_INET;
+ sa.sin_port = (in_port_t)htons(UNBOUND_DNS_PORT);
+
+ while(data_len >= LDNS_IP4ADDRLEN) {
+ memmove(&sa.sin_addr, d, LDNS_IP4ADDRLEN);
+ memmove(addr, &sa, *addrlen);
+ if(priv_lookup_addr(priv, addr, *addrlen))
+ return 1;
+
+ d += LDNS_IP4ADDRLEN;
+ data_len -= LDNS_IP4ADDRLEN;
+ }
+ /* if data_len != 0 here, then the svcparam is malformed. */
+ return 0;
+}
+
+/**
+ * Check if svcparam ipv6hint contains a private address.
+ * @param priv: private address lookup struct.
+ * @param d: the data bytes.
+ * @param data_len: number of data bytes in the svcparam.
+ * @param addr: address to return the private address to log in to.
+ * It has space for IPv4 and IPv6 addresses.
+ * @param addrlen: length of the addr. Returns the correct size for the addr.
+ * @return true if the rdata contains a private address.
+ */
+static int svcb_ipv6hint_contains_priv_addr(struct iter_priv* priv,
+ uint8_t* d, uint16_t data_len, struct sockaddr_storage* addr,
+ socklen_t* addrlen)
+{
+ struct sockaddr_in6 sa;
+ *addrlen = (socklen_t)sizeof(struct sockaddr_in6);
+ memset(&sa, 0, sizeof(struct sockaddr_in6));
+ sa.sin6_family = AF_INET6;
+ sa.sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT);
+
+ while(data_len >= LDNS_IP6ADDRLEN) {
+ memmove(&sa.sin6_addr, d, LDNS_IP6ADDRLEN);
+ memmove(addr, &sa, *addrlen);
+ if(priv_lookup_addr(priv, addr, *addrlen))
+ return 1;
+
+ d += LDNS_IP6ADDRLEN;
+ data_len -= LDNS_IP6ADDRLEN;
+ }
+ /* if data_len != 0 here, then the svcparam is malformed. */
+ return 0;
+}
+
+/**
+ * Check if type SVCB and HTTPS rdata contains a private address.
+ * @param priv: private address lookup struct.
+ * @param pkt: the packet.
+ * @param rr: the rr with rdata to check.
+ * @param addr: address to return the private address to log in to.
+ * @param addrlen: length of the addr. Initially the total size, on
+ * return the correct size for the addr.
+ * @return true if the rdata contains a private address.
+ */
+static int svcb_rr_contains_priv_addr(struct iter_priv* priv,
+ sldns_buffer* pkt, struct rr_parse* rr, struct sockaddr_storage* addr,
+ socklen_t* addrlen)
+{
+ uint8_t* d = rr->ttl_data;
+ uint16_t svcparamkey, data_len, rdatalen;
+ size_t oldpos, dname_len, dname_start, dname_compr_len;
+ d += 4; /* skip TTL */
+ rdatalen = sldns_read_uint16(d); /* read rdata length */
+ d += 2;
+
+ if(rdatalen < 2 /* priority */ + 1 /* 1 length target */)
+ return 0; /* malformed, too short */
+ d += 2; /* skip priority */
+ rdatalen -= 2;
+ oldpos = sldns_buffer_position(pkt);
+ sldns_buffer_set_position(pkt, (size_t)(d - sldns_buffer_begin(pkt)));
+ dname_start = sldns_buffer_position(pkt);
+ dname_len = pkt_dname_len(pkt);
+ dname_compr_len = sldns_buffer_position(pkt) - dname_start;
+ sldns_buffer_set_position(pkt, oldpos);
+ if(dname_len == 0)
+ return 0; /* dname malformed */
+ if(dname_compr_len > rdatalen)
+ return 0; /* malformed */
+ d += dname_compr_len; /* skip target */
+ rdatalen -= dname_compr_len;
+
+ while(rdatalen >= 4) {
+ svcparamkey = sldns_read_uint16(d);
+ data_len = sldns_read_uint16(d+2);
+ d += 4;
+ rdatalen -= 4;
+
+ /* verify that we have data_len data */
+ if(data_len > rdatalen) {
+ /* It is malformed, but if there are addresses
+ * in there it can be rejected. */
+ data_len = rdatalen;
+ }
+
+ if(!data_len)
+ continue; /* no data for the svcparamkey */
+
+ if(svcparamkey == SVCB_KEY_IPV4HINT) {
+ if(svcb_ipv4hint_contains_priv_addr(priv, d, data_len,
+ addr, addrlen))
+ return 1;
+ } else if(svcparamkey == SVCB_KEY_IPV6HINT) {
+ if(svcb_ipv6hint_contains_priv_addr(priv, d, data_len,
+ addr, addrlen))
+ return 1;
+ }
+ d += data_len;
+ rdatalen -= data_len;
+ }
+ /* If rdatalen != 0 here, then the svcb rdata is malformed. */
+ return 0;
+}
+
+/**
+ * Check if the SVCB and HTTPS rrset is bad.
+ * @param priv: private address lookup struct.
+ * @param pkt: the packet.
+ * @param rrset: the rrset to check.
+ * @return 1 if the entire rrset has to be removed. 0 if not.
+ * It removes RRs if they have private addresses, and log that.
+ */
+static int priv_svcb_rrset_bad(struct iter_priv* priv, sldns_buffer* pkt,
+ struct rrset_parse* rrset)
+{
+ struct rr_parse* rr, *prev = NULL;
+ struct sockaddr_storage addr;
+ socklen_t addrlen = (socklen_t)sizeof(addr);
+ for(rr = rrset->rr_first; rr; rr = rr->next) {
+ if(svcb_rr_contains_priv_addr(priv, pkt, rr, &addr,
+ &addrlen)) {
+ if(msgparse_rrset_remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, rr, &addr, addrlen))
+ return 1;
+ continue;
+ }
+ prev = rr;
+ }
+ return 0;
+}
+
int priv_rrset_bad(struct iter_priv* priv, sldns_buffer* pkt,
struct rrset_parse* rrset)
{
}
prev = rr;
}
- }
+ } else if(rrset->type == LDNS_RR_TYPE_SVCB ||
+ rrset->type == LDNS_RR_TYPE_HTTPS) {
+ if(priv_svcb_rrset_bad(priv, pkt, rrset))
+ return 1;
+ }
}
return 0;
}
--- /dev/null
+; config options
+server:
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: no
+ minimal-responses: yes
+ iter-scrub-promiscuous: yes
+
+ private-address: 10.0.0.0/8
+ private-address: 172.16.0.0/12
+ private-address: 192.168.0.0/16
+ private-address: 169.254.0.0/16
+ private-address: fd00::/8
+ private-address: fe80::/10
+
+ private-domain: "example.net"
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+
+CONFIG_END
+
+SCENARIO_BEGIN Test iterator scrubber with private addresses in SVCB.
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN A
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+; root server authoritative for example.net too.
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+mail.example.net. IN SVCB
+SECTION ANSWER
+mail.example.net. IN SVCB 1 foo.example.net. ipv4hint=10.20.30.40
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN A
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+ns.example.com. IN A
+SECTION ANSWER
+ns.example.com. IN A 1.2.3.4
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+ns.example.com. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+example.com. IN SOA ns.example.com. root.example.com. 4 14400 3600 604800 3600
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.example.com. IN SVCB
+SECTION ANSWER
+www.example.com. IN SVCB 1 foo.example.com. ipv4hint=192.20.30.40
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+mail.example.com. IN SVCB
+SECTION ANSWER
+mail.example.com. IN SVCB 1 foo.example.com. ipv6hint=fe80::15
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+foo.example.com. IN SVCB
+SECTION ANSWER
+foo.example.com. IN SVCB 1 foo.example.com. ipv4hint=10.20.30.40
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+toss.example.com. IN SVCB
+SECTION ANSWER
+toss.example.com. IN SVCB 1 foo.example.com. ipv4hint=10.20.30.40
+toss.example.com. IN SVCB 1 foo.example.com. ipv4hint=10.20.30.40
+toss.example.com. IN SVCB 1 foo.example.com. ipv4hint=1.2.3.4
+toss.example.com. IN SVCB 1 foo.example.com. ipv6hint=fe80::15
+toss.example.com. IN SVCB 1 foo.example.com. ipv4hint=10.20.30.41
+toss.example.com. IN SVCB 1 foo.example.com. ipv4hint=192.0.2.1,10.20.30.42,192.0.2.2
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+; public address is not scrubbed
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN SVCB
+ENTRY_END
+
+; recursion happens here.
+STEP 2 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN SVCB
+SECTION ANSWER
+www.example.com. IN SVCB 1 foo.example.com. ipv4hint=192.20.30.40
+ENTRY_END
+
+; IPv4 address is scrubbed
+STEP 3 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+foo.example.com. IN SVCB
+ENTRY_END
+
+; recursion happens here.
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+foo.example.com. IN SVCB
+SECTION ANSWER
+; scrubbed away
+ENTRY_END
+
+; IPv6 address is scrubbed
+STEP 20 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+mail.example.com. IN SVCB
+ENTRY_END
+
+STEP 30 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+mail.example.com. IN SVCB
+SECTION ANSWER
+ENTRY_END
+
+; allowed domain is not scrubbed.
+STEP 40 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+mail.example.net. IN SVCB
+ENTRY_END
+
+STEP 50 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+mail.example.net. IN SVCB
+SECTION ANSWER
+mail.example.net. IN SVCB 1 foo.example.net. ipv4hint=10.20.30.40
+ENTRY_END
+
+; rest of RRset intact, only 10/8 tossed away.
+STEP 60 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+toss.example.com. IN SVCB
+ENTRY_END
+
+STEP 70 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+toss.example.com. IN SVCB
+SECTION ANSWER
+toss.example.com. IN SVCB 1 foo.example.com. ipv4hint=1.2.3.4
+ENTRY_END
+
+SCENARIO_END