]>
Commit | Line | Data |
---|---|---|
c6ce1e7e MT |
1 | From fe3992f9fa69fa975ea31919c53933b5f6a63527 Mon Sep 17 00:00:00 2001 |
2 | From: Simon Kelley <simon@thekelleys.org.uk> | |
3 | Date: Fri, 3 Apr 2015 21:25:05 +0100 | |
697b4f04 | 4 | Subject: [PATCH 070/113] Return INSECURE, rather than BOGUS when DS proved not |
c6ce1e7e MT |
5 | to exist. |
6 | ||
7 | Return INSECURE when validating DNS replies which have RRSIGs, but | |
8 | when a needed DS record in the trust chain is proved not to exist. | |
9 | It's allowed for a zone to set up DNSKEY and RRSIG records first, then | |
10 | add a DS later, completing the chain of trust. | |
11 | ||
12 | Also, since we don't have the infrastructure to track that these | |
13 | non-validated replies have RRSIGS, don't cache them, so we don't | |
14 | provide answers with missing RRSIGS from the cache. | |
15 | --- | |
16 | src/dnsmasq.h | 1 + | |
17 | src/dnssec.c | 2 +- | |
18 | src/forward.c | 87 +++++++++++++++++++++++++++++++++++++++++++++-------------- | |
19 | 3 files changed, 69 insertions(+), 21 deletions(-) | |
20 | ||
21 | diff --git a/src/dnsmasq.h b/src/dnsmasq.h | |
22 | index 42952fc76c7a..6fe4a4189188 100644 | |
23 | --- a/src/dnsmasq.h | |
24 | +++ b/src/dnsmasq.h | |
25 | @@ -583,6 +583,7 @@ struct hostsfile { | |
26 | #define STAT_NO_NS 10 | |
27 | #define STAT_NEED_DS_NEG 11 | |
28 | #define STAT_CHASE_CNAME 12 | |
29 | +#define STAT_INSECURE_DS 13 | |
30 | ||
31 | #define FREC_NOREBIND 1 | |
32 | #define FREC_CHECKING_DISABLED 2 | |
33 | diff --git a/src/dnssec.c b/src/dnssec.c | |
34 | index 14bae7e9bf75..05e0983cb251 100644 | |
35 | --- a/src/dnssec.c | |
36 | +++ b/src/dnssec.c | |
37 | @@ -981,7 +981,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch | |
38 | ||
39 | /* If we've cached that DS provably doesn't exist, result must be INSECURE */ | |
40 | if (crecp->flags & F_NEG) | |
41 | - return STAT_INSECURE; | |
42 | + return STAT_INSECURE_DS; | |
43 | ||
44 | /* NOTE, we need to find ONE DNSKEY which matches the DS */ | |
45 | for (valid = 0, j = ntohs(header->ancount); j != 0 && !valid; j--) | |
46 | diff --git a/src/forward.c b/src/forward.c | |
47 | index 985814c3aec5..e8cf615aa939 100644 | |
48 | --- a/src/forward.c | |
49 | +++ b/src/forward.c | |
50 | @@ -521,7 +521,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, | |
51 | } | |
52 | ||
53 | static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind, | |
54 | - int no_cache, int cache_secure, int ad_reqd, int do_bit, int added_pheader, int check_subnet, union mysockaddr *query_source) | |
55 | + int no_cache, int cache_secure, int bogusanswer, int ad_reqd, int do_bit, int added_pheader, | |
56 | + int check_subnet, union mysockaddr *query_source) | |
57 | { | |
58 | unsigned char *pheader, *sizep; | |
59 | char **sets = 0; | |
60 | @@ -634,7 +635,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server | |
61 | } | |
62 | ||
63 | #ifdef HAVE_DNSSEC | |
64 | - if (no_cache && !(header->hb4 & HB4_CD)) | |
65 | + if (bogusanswer && !(header->hb4 & HB4_CD)) | |
66 | { | |
67 | if (!option_bool(OPT_DNSSEC_DEBUG)) | |
68 | { | |
69 | @@ -786,7 +787,7 @@ void reply_query(int fd, int family, time_t now) | |
70 | everything is broken */ | |
71 | if (forward->forwardall == 0 || --forward->forwardall == 1 || RCODE(header) != SERVFAIL) | |
72 | { | |
73 | - int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0; | |
74 | + int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0; | |
75 | ||
76 | if (option_bool(OPT_NO_REBIND)) | |
77 | check_rebind = !(forward->flags & FREC_NOREBIND); | |
78 | @@ -819,7 +820,13 @@ void reply_query(int fd, int family, time_t now) | |
79 | else if (forward->flags & FREC_DS_QUERY) | |
80 | { | |
81 | status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); | |
82 | - if (status == STAT_NO_DS || status == STAT_NO_NS) | |
83 | + /* Provably no DS, everything below is insecure, even if signatures are offered */ | |
84 | + if (status == STAT_NO_DS) | |
85 | + /* We only cache sigs when we've validated a reply. | |
86 | + Avoid caching a reply with sigs if there's a vaildated break in the | |
87 | + DS chain, so we don't return replies from cache missing sigs. */ | |
88 | + status = STAT_INSECURE_DS; | |
89 | + else if (status == STAT_NO_NS) | |
90 | status = STAT_BOGUS; | |
91 | } | |
92 | else if (forward->flags & FREC_CHECK_NOSIGN) | |
93 | @@ -959,8 +966,14 @@ void reply_query(int fd, int family, time_t now) | |
94 | else if (forward->flags & FREC_DS_QUERY) | |
95 | { | |
96 | status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); | |
97 | - if (status == STAT_NO_DS || status == STAT_NO_NS) | |
98 | - status = STAT_BOGUS; | |
99 | + /* Provably no DS, everything below is insecure, even if signatures are offered */ | |
100 | + if (status == STAT_NO_DS) | |
101 | + /* We only cache sigs when we've validated a reply. | |
102 | + Avoid caching a reply with sigs if there's a vaildated break in the | |
103 | + DS chain, so we don't return replies from cache missing sigs. */ | |
104 | + status = STAT_INSECURE_DS; | |
105 | + else if (status == STAT_NO_NS) | |
106 | + status = STAT_BOGUS; | |
107 | } | |
108 | else if (forward->flags & FREC_CHECK_NOSIGN) | |
109 | { | |
110 | @@ -985,6 +998,17 @@ void reply_query(int fd, int family, time_t now) | |
111 | } | |
112 | } | |
113 | ||
114 | + no_cache_dnssec = 0; | |
115 | + | |
116 | + if (status == STAT_INSECURE_DS) | |
117 | + { | |
118 | + /* We only cache sigs when we've validated a reply. | |
119 | + Avoid caching a reply with sigs if there's a vaildated break in the | |
120 | + DS chain, so we don't return replies from cache missing sigs. */ | |
121 | + status = STAT_INSECURE; | |
122 | + no_cache_dnssec = 1; | |
123 | + } | |
124 | + | |
125 | if (status == STAT_TRUNCATED) | |
126 | header->hb3 |= HB3_TC; | |
127 | else | |
128 | @@ -1002,12 +1026,13 @@ void reply_query(int fd, int family, time_t now) | |
129 | log_query(F_KEYTAG | F_SECSTAT, "result", NULL, result); | |
130 | } | |
131 | ||
132 | - no_cache_dnssec = 0; | |
133 | - | |
134 | if (status == STAT_SECURE) | |
135 | cache_secure = 1; | |
136 | else if (status == STAT_BOGUS) | |
137 | - no_cache_dnssec = 1; | |
138 | + { | |
139 | + no_cache_dnssec = 1; | |
140 | + bogusanswer = 1; | |
141 | + } | |
142 | } | |
143 | #endif | |
144 | ||
145 | @@ -1017,7 +1042,7 @@ void reply_query(int fd, int family, time_t now) | |
146 | else | |
147 | header->hb4 &= ~HB4_CD; | |
148 | ||
149 | - if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, | |
150 | + if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer, | |
151 | forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, | |
152 | forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->source))) | |
153 | { | |
154 | @@ -1420,7 +1445,7 @@ static int do_check_sign(struct frec *forward, int status, time_t now, char *nam | |
155 | } | |
156 | } | |
157 | ||
158 | -/* Move toward the root, until we find a signed non-existance of a DS, in which case | |
159 | +/* Move down from the root, until we find a signed non-existance of a DS, in which case | |
160 | an unsigned answer is OK, or we find a signed DS, in which case there should be | |
161 | a signature, and the answer is BOGUS */ | |
162 | static int tcp_check_for_unsigned_zone(time_t now, struct dns_header *header, size_t plen, int class, char *name, | |
163 | @@ -1570,8 +1595,13 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si | |
164 | else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG) | |
165 | { | |
166 | new_status = dnssec_validate_ds(now, header, n, name, keyname, class); | |
167 | - if (status == STAT_NEED_DS && (new_status == STAT_NO_DS || new_status == STAT_NO_NS)) | |
168 | - new_status = STAT_BOGUS; | |
169 | + if (status == STAT_NEED_DS) | |
170 | + { | |
171 | + if (new_status == STAT_NO_DS) | |
172 | + new_status = STAT_INSECURE_DS; | |
173 | + else if (new_status == STAT_NO_NS) | |
174 | + new_status = STAT_BOGUS; | |
175 | + } | |
176 | } | |
177 | else if (status == STAT_CHASE_CNAME) | |
178 | new_status = dnssec_chase_cname(now, header, n, name, keyname); | |
179 | @@ -1630,8 +1660,13 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si | |
180 | else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG) | |
181 | { | |
182 | new_status = dnssec_validate_ds(now, header, n, name, keyname, class); | |
183 | - if (status == STAT_NEED_DS && (new_status == STAT_NO_DS || new_status == STAT_NO_NS)) | |
184 | - new_status = STAT_BOGUS; /* Validated no DS */ | |
185 | + if (status == STAT_NEED_DS) | |
186 | + { | |
187 | + if (new_status == STAT_NO_DS) | |
188 | + new_status = STAT_INSECURE_DS; | |
189 | + else if (new_status == STAT_NO_NS) | |
190 | + new_status = STAT_BOGUS; /* Validated no DS */ | |
191 | + } | |
192 | } | |
193 | else if (status == STAT_CHASE_CNAME) | |
194 | new_status = dnssec_chase_cname(now, header, n, name, keyname); | |
195 | @@ -1652,7 +1687,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si | |
196 | goto another_tcp_key; | |
197 | } | |
198 | } | |
199 | - | |
200 | + | |
201 | free(packet); | |
202 | } | |
203 | return new_status; | |
204 | @@ -1673,7 +1708,7 @@ unsigned char *tcp_request(int confd, time_t now, | |
205 | int local_auth = 0; | |
206 | #endif | |
207 | int checking_disabled, ad_question, do_bit, added_pheader = 0; | |
208 | - int check_subnet, no_cache_dnssec = 0, cache_secure = 0; | |
209 | + int check_subnet, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0; | |
210 | size_t m; | |
211 | unsigned short qtype; | |
212 | unsigned int gotname; | |
213 | @@ -1941,6 +1976,15 @@ unsigned char *tcp_request(int confd, time_t now, | |
214 | int status = tcp_key_recurse(now, STAT_TRUNCATED, header, m, 0, daemon->namebuff, daemon->keyname, last_server, &keycount); | |
215 | char *result; | |
216 | ||
217 | + if (status == STAT_INSECURE_DS) | |
218 | + { | |
219 | + /* We only cache sigs when we've validated a reply. | |
220 | + Avoid caching a reply with sigs if there's a vaildated break in the | |
221 | + DS chain, so we don't return replies from cache missing sigs. */ | |
222 | + status = STAT_INSECURE; | |
223 | + no_cache_dnssec = 1; | |
224 | + } | |
225 | + | |
226 | if (keycount == 0) | |
227 | { | |
228 | result = "ABANDONED"; | |
229 | @@ -1952,8 +1996,11 @@ unsigned char *tcp_request(int confd, time_t now, | |
230 | log_query(F_KEYTAG | F_SECSTAT, "result", NULL, result); | |
231 | ||
232 | if (status == STAT_BOGUS) | |
233 | - no_cache_dnssec = 1; | |
234 | - | |
235 | + { | |
236 | + no_cache_dnssec = 1; | |
237 | + bogusanswer = 1; | |
238 | + } | |
239 | + | |
240 | if (status == STAT_SECURE) | |
241 | cache_secure = 1; | |
242 | } | |
243 | @@ -1987,7 +2034,7 @@ unsigned char *tcp_request(int confd, time_t now, | |
244 | #endif | |
245 | ||
246 | m = process_reply(header, now, last_server, (unsigned int)m, | |
247 | - option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, | |
248 | + option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, bogusanswer, | |
249 | cache_secure, ad_question, do_bit, added_pheader, check_subnet, &peer_addr); | |
250 | ||
251 | break; | |
252 | -- | |
253 | 2.1.0 | |
254 |