]>
Commit | Line | Data |
---|---|---|
fbcc3cb7 MF |
1 | From 2dbba34b2c1289a108f876c78b84889f2a93115d Mon Sep 17 00:00:00 2001 |
2 | From: Simon Kelley <simon@thekelleys.org.uk> | |
3 | Date: Wed, 16 Dec 2015 13:41:58 +0000 | |
4 | Subject: [PATCH] DNSSEC validation tweak. | |
5 | ||
6 | A zone which has at least one key with an algorithm we don't | |
7 | support should be considered as insecure. | |
8 | --- | |
9 | src/dnssec.c | 82 ++++++++++++++++++++++++++++++++++++++-------------------- | |
10 | 1 file changed, 54 insertions(+), 28 deletions(-) | |
11 | ||
12 | diff --git a/src/dnssec.c b/src/dnssec.c | |
13 | index fa3eb81..dc563e0 100644 | |
14 | --- a/src/dnssec.c | |
15 | +++ b/src/dnssec.c | |
16 | @@ -763,10 +763,10 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int | |
17 | STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname) | |
18 | STAT_NEED_DS need DS to complete validation (name is returned in keyname) | |
19 | ||
20 | - if key is non-NULL, use that key, which has the algo and tag given in the params of those names, | |
21 | + If key is non-NULL, use that key, which has the algo and tag given in the params of those names, | |
22 | otherwise find the key in the cache. | |
23 | ||
24 | - name is unchanged on exit. keyname is used as workspace and trashed. | |
25 | + Name is unchanged on exit. keyname is used as workspace and trashed. | |
26 | ||
27 | Call explore_rrset first to find and count RRs and sigs. | |
28 | */ | |
29 | @@ -919,6 +919,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in | |
30 | return STAT_BOGUS; | |
31 | } | |
32 | ||
33 | + | |
34 | /* The DNS packet is expected to contain the answer to a DNSKEY query. | |
35 | Put all DNSKEYs in the answer which are valid into the cache. | |
36 | return codes: | |
37 | @@ -1831,15 +1832,15 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns | |
38 | ||
39 | /* Check signing status of name. | |
40 | returns: | |
41 | - STAT_SECURE zone is signed. | |
42 | - STAT_INSECURE zone proved unsigned. | |
43 | - STAT_NEED_DS require DS record of name returned in keyname. | |
44 | - | |
45 | + STAT_SECURE zone is signed. | |
46 | + STAT_INSECURE zone proved unsigned. | |
47 | + STAT_NEED_DS require DS record of name returned in keyname. | |
48 | + STAT_NEED_DNSKEY require DNSKEY record of name returned in keyname. | |
49 | name returned unaltered. | |
50 | */ | |
51 | static int zone_status(char *name, int class, char *keyname, time_t now) | |
52 | { | |
53 | - int name_start = strlen(name); | |
54 | + int secure_ds, name_start = strlen(name); | |
55 | struct crec *crecp; | |
56 | char *p; | |
57 | ||
58 | @@ -1850,27 +1851,52 @@ static int zone_status(char *name, int class, char *keyname, time_t now) | |
59 | if (!(crecp = cache_find_by_name(NULL, keyname, now, F_DS))) | |
60 | return STAT_NEED_DS; | |
61 | else | |
62 | - do | |
63 | - { | |
64 | - if (crecp->uid == (unsigned int)class) | |
65 | - { | |
66 | - /* F_DNSSECOK misused in DS cache records to non-existance of NS record. | |
67 | - F_NEG && !F_DNSSECOK implies that we've proved there's no DS record here, | |
68 | - but that's because there's no NS record either, ie this isn't the start | |
69 | - of a zone. We only prove that the DNS tree below a node is unsigned when | |
70 | - we prove that we're at a zone cut AND there's no DS record. | |
71 | - */ | |
72 | - if (crecp->flags & F_NEG) | |
73 | - { | |
74 | - if (crecp->flags & F_DNSSECOK) | |
75 | - return STAT_INSECURE; /* proved no DS here */ | |
76 | - } | |
77 | - else if (!ds_digest_name(crecp->addr.ds.digest) || !algo_digest_name(crecp->addr.ds.algo)) | |
78 | - return STAT_INSECURE; /* algo we can't use - insecure */ | |
79 | - } | |
80 | - } | |
81 | - while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS))); | |
82 | - | |
83 | + { | |
84 | + secure_ds = 0; | |
85 | + | |
86 | + do | |
87 | + { | |
88 | + if (crecp->uid == (unsigned int)class) | |
89 | + { | |
90 | + /* F_DNSSECOK misused in DS cache records to non-existance of NS record. | |
91 | + F_NEG && !F_DNSSECOK implies that we've proved there's no DS record here, | |
92 | + but that's because there's no NS record either, ie this isn't the start | |
93 | + of a zone. We only prove that the DNS tree below a node is unsigned when | |
94 | + we prove that we're at a zone cut AND there's no DS record. | |
95 | + */ | |
96 | + if (crecp->flags & F_NEG) | |
97 | + { | |
98 | + if (crecp->flags & F_DNSSECOK) | |
99 | + return STAT_INSECURE; /* proved no DS here */ | |
100 | + } | |
101 | + else if (!ds_digest_name(crecp->addr.ds.digest) || !algo_digest_name(crecp->addr.ds.algo)) | |
102 | + return STAT_INSECURE; /* algo we can't use - insecure */ | |
103 | + else | |
104 | + secure_ds = 1; | |
105 | + } | |
106 | + } | |
107 | + while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS))); | |
108 | + } | |
109 | + | |
110 | + if (secure_ds) | |
111 | + { | |
112 | + /* We've found only DS records that attest to the DNSKEY RRset in the zone, so we believe | |
113 | + that RRset is good. Furthermore the DNSKEY whose hash is proved by the DS record is | |
114 | + one we can use. However the DNSKEY RRset may contain more than one key and | |
115 | + one of the other keys may use an algorithm we don't support. If that's | |
116 | + the case the zone is insecure for us. */ | |
117 | + | |
118 | + if (!(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY))) | |
119 | + return STAT_NEED_KEY; | |
120 | + | |
121 | + do | |
122 | + { | |
123 | + if (crecp->uid == (unsigned int)class && !algo_digest_name(crecp->addr.key.algo)) | |
124 | + return STAT_INSECURE; | |
125 | + } | |
126 | + while ((crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY))); | |
127 | + } | |
128 | + | |
129 | if (name_start == 0) | |
130 | break; | |
131 | ||
132 | -- | |
133 | 1.7.10.4 | |
134 |