Je suis Charlie

Autres trucs

Accueil

Seulement les RFC

Seulement les fiches de lecture

Ève

Une bogue amusante de BIND avec les enregistements NSEC3

Première rédaction de cet article le 8 septembre 2010


Pratiquement chaque TLD qui a décidé de déployer DNSSEC a trouvé une nouvelle bogue de BIND, car ce TLD utilisait une combinaison d'options non encore testée. Lors des essais préalables à la signature de .FR, le cas d'une zone NSEC3 ne comportant qu'un seul nom signé a révélé une nouvelle bogue.

Faites l'expérience vous-même avec toutes les versions de BIND jusqu'à la 9.6.2 ou 9.7.2 incluses. Créez une zone où un seul nom contient des enregistrements faisant autorité, par exemple :

example.       IN      SOA     ns1 root (
                2010083009              ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                          86400 )       ; Negative Cache TTL

        IN      NS      ns1.nic.fr.
        IN      NS      ns2.nic.fr.

        IN      TXT "Test"

toto    IN   NS ns1.bortzmeyer.org.
        IN   NS ns2.bortzmeyer.org.

foobar    IN   NS ns1.bortzmeyer.org.
        IN   NS ns2.bortzmeyer.org.

baz    IN   NS ns1.example.net.

$INCLUDE "Kexample.+008+31414.key"

Cette zone contient plusieurs noms (example, toto.example, etc). Signée avec NSEC ou même avec le NSEC3 du RFC 5155, elle ne pose pas de problème. Mais si on ajoute à NSEC3 l'option opt-out (section 6 du RFC 5155, option -A de dnssec-signzone par exemple dnssec-signzone -3 0BADDCAF -H 1 -A -P -z example), il n'y a qu'un seul nom signé, l'apex, example. En effet, les autres noms comme foobar.example ne contiennent pas d'enregistrement faisant autorité, seulement des délégations. La chaîne NSEC3 boucle alors sur elle-même, il n'y a qu'un seul condensat cryptographique, pour l'apex :

P9UQ7AKGQS75E10PPBQ2FRKUSSNEKID6.example. 86400 IN NSEC3 1 1 1 \
             0BADDCAF P9UQ7AKGQS75E10PPBQ2FRKUSSNEKID6 NS SOA TXT 

Comme le savent tous les programmeurs, les bogues surviennent en général aux limites, pour des tailles de 0 ou de 1 ou de 2^32... Ici, lorsque la chaîne des NSEC3 est de longueur 1, BIND n'est plus capable de servir la zone correctement. Lançons-le avec ce fichier de configuration :

options {
        directory "/tmp/bind";
        recursion no;
        dnssec-enable yes;
        listen-on port 9053 {any;};
};

zone "example" {
        type master;
        file "example.signed";
};

puis, named -g -c /mon/fichier.conf. Si on interroge ce BIND avec dig sur l'apex, tout va bien :


% dig +dnssec @localhost -p 9053 A example

; <<>> DiG 9.6-ESV-R1 <<>> +dnssec @localhost -p 9053 A example
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 33585
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 1
...
;; AUTHORITY SECTION:
example.                600     IN      SOA ns1.example. root.example. 2010083009 604800 86400 2419200 86400
example.                600     IN      RRSIG   SOA 8 1 600 20100929110548 2010083011054831414 example. pdY7ja1es9hjmXKuWcsxi7WL/rWl40Ox5AEGEFJjb/E4PXqDbUXCsgMx...
P9UQ7AKGQS75E10PPBQ2FRKUSSNEKID6.example. 86400 IN NSEC3 1 1 1 0BADDCAF P9UQ7AKGQS75E10PPBQ2FRKUSSNEKID6 NS SOA TXT RRSIG DNSKEY NSEC3PARAM
...

Si on interroge sur un nom qui n'existe pas, tout va également bien, le NSEC3 est bien renvoyé pour prouver la non-existence :


% dig +dnssec @localhost -p 9053 A doesnotexistatall.example
                                                            
; <<>> DiG 9.6-ESV-R1 <<>> +dnssec @localhost -p 9053 A doesnotexistatall.example
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 8774
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 1
...
;; AUTHORITY SECTION:
example.                600     IN      SOA ns1.example. root.example. 2010083009 604800 86400 2419200 86400
example.                600     IN      RRSIG   SOA 8 1 600 20100929110548 2010083011054831414 example. pdY7ja1es9h...
P9UQ7AKGQS75E10PPBQ2FRKUSSNEKID6.example. 86400 IN NSEC3 1 1 1 0BADDCAF P9UQ7AKGQS75E10PPBQ2FRKUSSNEKID6 NS SOA TXT RRSIG DNSKEY NSEC3PARAM
...

Mais, si on interroge le serveur sur un nom qui existe, patatras :


% dig +dnssec @localhost -p 9053 A foobar.example

; <<>> DiG 9.6-ESV-R1 <<>> +dnssec @localhost -p 9053 A foobar.example
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 63947
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 1
...
;; AUTHORITY SECTION:
foobar.example.         600     IN      NS      ns1.bortzmeyer.org.
foobar.example.         600     IN      NS      ns2.bortzmeyer.org.
...

Pas de NSEC3 pour prouver l'absence d'enregistrements (ANSWER: 0) ! Un tel comportement est tout à fait contraire aux normes et conduit les résolveurs validants à refuser de transmettre la réponse (SERVFAIL, pour Server Failure).

Si vous voulez voir le comportement normal d'un serveur de noms DNSSEC, regardez des zones signées avec NSEC3 et opt-out comme .ORG ou .PM. Un test comme dig +dnssec @a2.org.afilias-nst.info A bortzmeyer.org (nom existant mais non signé) ou dig +dnssec @d.nic.fr A nic.pm (même chose) doit renvoyer des NSEC3.

Alors, pourquoi BIND n'en renvoyait-il pas, alors que NSD, par exemple, le faisait bien ? C'est à cause de ce cas limite, un seul nom signé et donc une chaîne NSEC3 bouclant immédiatement, cas qui n'était pas prévu. En ajoutant un nom bidon (le _ est là pour éviter une collision avec un nom délégué) :

; Adding this name workarounds the bug
_test    IN TXT "Test again"

Alors, tout marche :


% dig +dnssec @localhost -p 9053 A foobar.example

; <<>> DiG 9.6-ESV-R1 <<>> +dnssec @localhost -p 9053 A foobar.example
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41292
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 6, ADDITIONAL: 1
...
;; AUTHORITY SECTION:
foobar.example.         600     IN      NS      ns2.bortzmeyer.org.
foobar.example.         600     IN      NS      ns1.bortzmeyer.org.
P9UQ7AKGQS75E10PPBQ2FRKUSSNEKID6.example. 86400 IN NSEC3 1 1 1 0BADDCAF 50P7FK09A20A8BATD9DPV7NQPP1QM4OB NS SOA TXT RRSIG DNSKEY NSEC3PARAM
...

Notez bien que le NSEC3 pointe désormais vers un second condensat cryptographique puisqu'il y a deux noms signés.

La bogue a été signalée à l'ISC et corrigée quelques jours après. Vous ne pouvez pas encore la voir en ligne car BIND 9 (contrairement à son successeur) a un mode de développement assez fermé, où les rapports de bogue et le VCS ne sont pas publics. Mais voici ce qui apparaitra dans la liste des changements :

2951.   [bug]           named failed to generate a correct signed response
                        in a optout, delegation only zone with no secure
                        delegations. [RT #22007]

Et voici le patch complet. Notez que la correction elle-même ne fait que quelques lignes de C mais que le patch a nécessité le développement d'un test de non-régression, destiné à empêcher cette bogue de réapparaître dans le futur. La livraison d'une nouvelle version de BIND est en effet précédée de tests automatiques du serveur ainsi compilé.

Version PDF de cette page (mais vous pouvez aussi imprimer depuis votre navigateur, il y a une feuille de style prévue pour cela)

Source XML de cette page (cette page est distribuée sous les termes de la licence GFDL)