wildcard dnssec test fails

Hello,

I’ve unbound setup on FreeBSD 11.1 and I can’t figure out why “drill www.wilda.nsec.0skar.cz” gives SERVFAIL. The domain is from this (http://0skar.cz/dns/en) test site where it reports three failures (2a, 2b and 4). Any help would be appreciated.

Thanks

Sebastian

$ unbound -h
Version 1.6.7
linked libs: libevent 2.1.8-stable (it uses kqueue), OpenSSL 1.0.2n 7 Dec 2017
linked modules: dns64 respip validator iterator
DNSCrypt feature available
$ uname -a
FreeBSD dns.seby.io 11.1-RELEASE-p4 FreeBSD 11.1-RELEASE-p4 #0: Tue Nov 14 06:12:40 UTC 2017 root@amd64-builder.daemonology.net:/usr/obj/usr/src/sys/GENERIC amd64

(attachments)

unbound.conf (6.36 KB)
unbound.log (9.9 KB)

It does not fail for me:

$ dig www.wilda.nsec.0skar.cz

; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7_4.1 <<>> www.wilda.nsec.0skar.cz
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18098
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.wilda.nsec.0skar.cz. IN A

;; ANSWER SECTION:
www.wilda.nsec.0skar.cz. 300 IN CNAME flexi.oskarcz.net.
flexi.oskarcz.net. 3599 IN A 85.239.227.179

Is your unbound configured to use another DNS as forwarder? There are
some older known bugs that fail in some corner cases with older
forwarders.

Paul

Hi Paul,

Is your unbound configured to use another DNS as forwarder?

Yes, to nsd for opennic TLDs which to my understanding should not impact this query.

Here is the config file:

# This file is managed by Ansible.
#
# template: /Users/seb/git/dns-resolver/required-roles/publicarray.unbound/templates/unbound.conf
# date: 2017-12-04 23:59:52
#
remote-control:
    control-enable: yes
    control-interface: 127.0.0.1
server:
    num-threads: 1
    msg-cache-slabs: 1
    rrset-cache-slabs: 1
    key-cache-slabs: 1
    infra-cache-slabs: 1

    msg-cache-size: 72m
    rrset-cache-size: 144m
    key-cache-size: 72m
    neg-cache-size: 36m

    domain-insecure: "dns.opennic.glue"
    domain-insecure: "bbs"
    domain-insecure: "bit"
    domain-insecure: "chan"
    domain-insecure: "cyb"
    domain-insecure: "dyn"
    domain-insecure: "free"
    domain-insecure: "fur"
    domain-insecure: "geek"
    domain-insecure: "gopher"
    domain-insecure: "indy"
    domain-insecure: "libre"
    domain-insecure: "neo"
    domain-insecure: "null"
    domain-insecure: "o"
    domain-insecure: "opennic.glue"
    domain-insecure: "oss"
    domain-insecure: "oz"
    domain-insecure: "parody"
    domain-insecure: "pirate"
    domain-insecure: "glue"
    domain-insecure: "baza"
    domain-insecure: "coin"
    domain-insecure: "emc"
    domain-insecure: "lib"
    domain-insecure: "ku"
    domain-insecure: "te"
    domain-insecure: "ti"
    domain-insecure: "uu"

    num-queries-per-thread: 2048
    local-zone: example. static
    local-zone: local. static
    local-zone: i2p. static
    local-zone: home. static
    local-zone: zghjccbob3n0. static
    local-zone: dhcp. static
    local-zone: lan. static
    local-zone: localdomain. static
    local-zone: ip. static
    local-zone: internal. static
    local-zone: openstacklocal. static
    local-zone: dlink. static
    local-zone: gw==. static
    local-zone: gateway. static
    local-zone: corp. static
    local-zone: workgroup. static
    local-zone: belkin. static
    local-zone: davolink. static
    local-zone: z. static
    local-zone: domain. static
    local-zone: virtualmin. static
    local-zone: 2.dnscrypt-cert.dns refuse
    outgoing-range: 4096
    statistics-cumulative: no
    auto-trust-anchor-file: root.key
    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
    ratelimit: 200
    ssl-service-pem: /usr/local/etc/unbound/certificate.pem
    minimal-responses: yes
    log-time-ascii: yes
    do-not-query-localhost: no
    hide-identity: yes
    incoming-num-tcp: 200
    infra-host-ttl: 3600
    ssl-service-key: /usr/local/etc/unbound/private.key
    chroot: /usr/local/etc/unbound
    qname-minimisation: yes
    statistics-interval: 0
    port: 56
    val-log-level: 1
    use-syslog: yes
    ssl-port: 853
    hide-trustanchor: yes
    infra-cache-numhosts: 50000
    pidfile: unbound.pid
    ip-ratelimit: 100
    username: unbound
    do-not-query-address: 10.0.0.0/8
    do-not-query-address: 172.16.0.0/12
    do-not-query-address: 192.168.0.0/16
    serve-expired: yes
    access-control: 0.0.0.0/0 allow
    access-control: ::/0 allow
    hide-version: yes
    unwanted-reply-threshold: 10000000
    udp-upstream-without-downstream: yes
    root-hints: root.hints
    interface: 127.0.0.1
    interface: ::1
    interface: 0.0.0.0@853
    interface: ::0@853
    logfile: unbound.log
    prefetch-key: yes
    cache-max-ttl: 86400
    verbosity: 0
    neg-cache-size: 25m
    cache-min-ttl: 300
    prefetch: yes
    directory: /usr/local/etc/unbound
    rrset-roundrobin: yes
    extended-statistics: yes
    jostle-timeout: 325
stub-zone:
    name: "dns.opennic.glue"
    stub-addr: "127.0.0.1@57"   # NSD Authorative Slave DNS server
stub-zone:
    name: "bbs"
    stub-addr: "127.0.0.1@57"
stub-zone:
    name: "bit"
    stub-addr: "127.0.0.1@57"
stub-zone:
    name: "chan"
    stub-addr: "127.0.0.1@57"
stub-zone:
    name: "cyb"
    stub-addr: "127.0.0.1@57"
stub-zone:
    name: "dyn"
    stub-addr: "127.0.0.1@57"
stub-zone:
    name: "free"
    stub-addr: "127.0.0.1@57"
stub-zone:
    name: "fur"
    stub-addr: "127.0.0.1@57"
stub-zone:
    name: "geek"
    stub-addr: "127.0.0.1@57"
stub-zone:
    name: "gopher"
    stub-addr: "127.0.0.1@57"
stub-zone:
    name: "indy"
    stub-addr: "127.0.0.1@57"
stub-zone:
    name: "libre"
    stub-addr: "127.0.0.1@57"
stub-zone:
    name: "neo"
    stub-addr: "127.0.0.1@57"
stub-zone:
    name: "null"
    stub-addr: "127.0.0.1@57"
stub-zone:
    name: "o"
    stub-addr: "127.0.0.1@57"
stub-zone:
    name: "opennic.glue"
    stub-addr: "127.0.0.1@57"
stub-zone:
    name: "oss"
    stub-addr: "127.0.0.1@57"
stub-zone:
    name: "oz"
    stub-addr: "127.0.0.1@57"
stub-zone:
    name: "parody"
    stub-addr: "127.0.0.1@57"
stub-zone:
    name: "pirate"
    stub-addr: "127.0.0.1@57"
# OpenNIC Peers:
stub-zone:
    name: "baza"
    stub-host: "seed1.emercoin.com"
    stub-host: "seed2.emercoin.com"
stub-zone:
    name: "coin"
    stub-host: "seed1.emercoin.com"
    stub-host: "seed2.emercoin.com"
stub-zone:
    name: "emc"
    stub-host: "seed1.emercoin.com"
    stub-host: "seed2.emercoin.com"
stub-zone:
    name: "lib"
    stub-host: "seed1.emercoin.com"
    stub-host: "seed2.emercoin.com"
stub-zone:
    name: "ku"
    stub-addr: "127.0.0.1@57"
    stub-addr: "5.45.96.220"    # ns1.new-nations.ku
    stub-addr: "185.82.22.133"  # ns2.new-nations.ku
stub-zone:
    name: "te"
    stub-addr: "127.0.0.1@57"
    stub-addr: "5.45.96.220"    # ns1.new-nations.te
    stub-addr: "185.82.22.133"  # ns2.new-nations.te
stub-zone:
    name: "ti"
    stub-addr: "127.0.0.1@57"
    stub-addr: "5.45.96.220"    # ns1.new-nations.ti
    stub-addr: "185.82.22.133"  # ns2.new-nations.ti
stub-zone:
    name: "uu"
    stub-addr: "127.0.0.1@57"
    stub-addr: "5.45.96.220"    # ns1.new-nations.uu
    stub-addr: "185.82.22.133"  # ns2.new-nations.uu


Regards

Sebastian

        

The zone's signatures are weird:

    $ unbound-host -f /usr/local/etc/unbound/root.key -v www.wilda.nsec.0skar.cz
    ...
    validation failure <www.wilda.nsec.0skar.cz. A IN>: signature inception after expiration from 2001:1528:132:70::1 for key nsec.0skar.cz. while building chain of trust
    ...

    $ dig +noall +ans +nocl +nottl +nosplit +cd +dnssec -t a www.wilda.nsec.0skar.cz
    www.wilda.nsec.0skar.cz. CNAME flexi.oskarcz.net.
    www.wilda.nsec.0skar.cz. RRSIG CNAME 10 5 300 20800101000000 20140130121330 28887 nsec.0skar.cz. ...
    flexi.oskarcz.net. A 85.239.227.179
    flexi.oskarcz.net. RRSIG A 10 3 3600 20180108024403 20171209024403 31880 oskarcz.net. ...

Note the RRSIG dates for the CNAME:

    Inception: 20140130121330
    Expiration: 20800101000000

Perhaps unbound is comparing these as 32-bit timestamps. Just
under 66 years is an impressive validity range, if intentional.

Hi Sebastian, Viktor,

I�ve unbound setup on FreeBSD 11.1 and I can�t figure out why "drill
www.wilda.nsec.0skar.cz" gives SERVFAIL. The domain is from this
(http://0skar.cz/dns/en) test site where it reports three failures (2a,
2b and 4). Any help would be appreciated.

The zone's signatures are weird:

    $ unbound-host -f /usr/local/etc/unbound/root.key -v www.wilda.nsec.0skar.cz

When I run unbound-host, I get no errors,
./unbound-host www.wilda.nsec.0skar.czwww.wilda.nsec.0skar.cz -f
root.key -v -t A
www.wilda.nsec.0skar.czwww.wilda.nsec.0skar.cz has address
85.239.227.179 (secure)

Unbound performs serial arithmatic on the timestamps in the rrsig,
according to RFC.

(What does that mean? The timestamps are 32bit in the RRSIG, but the
value is interpreted relative to the current date. And what you cannot
do is express something like a point more than some number of years
future or past.)

Best regards, Wouter

Hello Wouter,

Thanks for the insight. Maybe this has something to with the platform?

CentOS 6.9:

$ unbound-host -v -f /etc/unbound/root.key -t A www.wilda.nsec.0skar.cz

www.wilda.nsec.0skar.cz is an alias for flexi.oskarcz.net. (secure)

flexi.oskarcz.net has address 85.239.227.179 (secure)

MacOS 10.13.2 (High Sierra):

$ unbound-host -v -t A -f /usr/local/etc/unbound/root.key www.wilda.nsec.0skar.cz

www.wilda.nsec.0skar.cz is an alias for flexi.oskarcz.net. (BOGUS (security failure))

flexi.oskarcz.net has address 85.239.227.179 (BOGUS (security failure))

validation failure <www.wilda.nsec.0skar.cz. A IN>: signature inception after expiration from 85.239.227.179 for key nsec.0skar.cz. while building chain of trust

FreeBSD 11.1:

$ unbound-host -v -f /usr/local/etc/unbound/root.key -t A www.wilda.nsec.0skar.cz

www.wilda.nsec.0skar.cz is an alias for flexi.oskarcz.net. (BOGUS (security failure))

flexi.oskarcz.net has address 85.239.227.179 (BOGUS (security failure))

validation failure <www.wilda.nsec.0skar.cz. A IN>: signature inception after expiration from 2001:1528:132:70::1 for key nsec.0skar.cz. while building chain of trust

Kind Regards

Sebastian

Hi Sebastian

When I run unbound-host, I get no errors,
./unbound-host www.wilda.nsec.0skar.czwww.wilda.nsec.0skar.cz
<http://www.wilda.nsec.0skar.czwww.wilda.nsec.0skar.cz> -f
root.key -v -t A
www.wilda.nsec.0skar.czwww.wilda.nsec.0skar.cz
<http://www.wilda.nsec.0skar.czwww.wilda.nsec.0skar.cz> has address
85.239.227.179 (secure)

Unbound performs serial arithmatic on the timestamps in the rrsig,
according to RFC.

(What does that mean? The timestamps are 32bit in the RRSIG, but the
value is interpreted relative to the current date. And what you cannot
do is express something like a point more than some number of years
future or past.)

Best regards, Wouter

Hello Wouter,

Thanks for the insight. Maybe this has something to with the platform?

Yes it is the compiler. Clang fails, gcc succeeds. I can make clang
succeed with a small code change together with the removal of -O2
(disabling clang's optimizer).

The code change is instead of if(incep - expi > 0) ..fail.. it now has
var=incep-expi; if(var > 0) ..fail..

Clangs optimizer seems to take the wrong branch in the if statement. If
I printout the value calculated, I get the correct output. Something
like if(!((incep-expi)&0x80000000)) ..fail.. does not trick the
optimizer into taking the right branch.

The code change is in the code repository.
CFLAGS=-g ./configure
This disables -O2 as well, with the current version of unbound.

Or as a workaround, maybe ignore this, perhaps with domain-insecure,
because it seems to only happen for the int32_t values of (1391084010 -
-823674496 > 0). And 2080 is uncommon in RRSIG timestamps.

Best regards, Wouter

Hi,

Wait, no, just CFLAGS=-g ./configure disables -O2, but you also need the
code change. So that won't work as a workaround.

Best regards, Wouter

Hi,

Thanks so much for finding the problem. I've recompiled unbound with GNU gcc for now. It seamed like the simplest solution.
I never would have thought that the access to a internet service (to the end user) could be broken because of a compiler optimisation. Still so many things have to go "wrong" for this to happen.

Will this be reported to the clang developers?

Regards,
Sebastian

The original coded uses non-portable undefined overflow behaviour
for signed integer arithmetic. The compiler is free to replace
"incep - expi > 0" with "incep > expi". The intermediate "var"
may in some cases avoid the problem, but this is still brittle
under optimization. To avoid non-deterministic behaviour unsigned
arithmetic must be used:

    uint32_t incep;
    uint32_t expi;

    /*
     * In serial number arithmetic a > b iff as unsigned integers mod 2^32
     * we have (a - b) < (b - a)
     */
    if ((incep - expi) < (expi - incep)) {
  ... fail ...
    }

The same code should be used for SOA comparisons.

I should perhaps note that in the RFC1982 definition of sequence
space arithmetic, two points that are diametrically opposite on
the circle are not comparable.

Since such ambiguity should be a failure case, a more precisely
correct condition is

    if ((incep - expi) <= (expi - incep)) {
  ... fail ...
    }

Note that this now also includes incep == expi, which should never
be the case for RRSIGs, and so for RRSIG failure makes sense for
both equal and diametrically opposite values. When comparing SOA
serials for AXFR (perhaps not something unbound ever needs to do),
a pair of equal values would of course be treated differently than
a pair or diametrically opposite values.

Hi Viktor,