]>
Commit | Line | Data |
---|---|---|
fbcc3cb7 MF |
1 | From 9a31b68b59adcac01016d4026d906b69c4216c01 Mon Sep 17 00:00:00 2001 |
2 | From: Simon Kelley <simon@thekelleys.org.uk> | |
3 | Date: Tue, 15 Dec 2015 10:20:39 +0000 | |
4 | Subject: [PATCH] Major rationalisation of DNSSEC validation. | |
5 | ||
6 | Much gnarly special-case code removed and replaced with correct | |
7 | general implementaion. Checking of zone-status moved to DNSSEC code, | |
8 | where it should be, vastly simplifying query-forwarding code. | |
9 | --- | |
10 | src/dnsmasq.h | 19 +- | |
11 | src/dnssec.c | 926 ++++++++++++++++++++++++++++++--------------------------- | |
12 | src/forward.c | 741 ++++++++++----------------------------------- | |
13 | 3 files changed, 653 insertions(+), 1033 deletions(-) | |
14 | ||
15 | diff --git a/src/dnsmasq.h b/src/dnsmasq.h | |
16 | index f42acdb..023a1cf 100644 | |
17 | --- a/src/dnsmasq.h | |
18 | +++ b/src/dnsmasq.h | |
19 | @@ -586,12 +586,8 @@ struct hostsfile { | |
20 | #define STAT_NEED_KEY 5 | |
21 | #define STAT_TRUNCATED 6 | |
22 | #define STAT_SECURE_WILDCARD 7 | |
23 | -#define STAT_NO_SIG 8 | |
24 | -#define STAT_NO_DS 9 | |
25 | -#define STAT_NO_NS 10 | |
26 | -#define STAT_NEED_DS_NEG 11 | |
27 | -#define STAT_CHASE_CNAME 12 | |
28 | -#define STAT_INSECURE_DS 13 | |
29 | +#define STAT_OK 8 | |
30 | +#define STAT_ABANDONED 9 | |
31 | ||
32 | #define FREC_NOREBIND 1 | |
33 | #define FREC_CHECKING_DISABLED 2 | |
34 | @@ -601,8 +597,7 @@ struct hostsfile { | |
35 | #define FREC_AD_QUESTION 32 | |
36 | #define FREC_DO_QUESTION 64 | |
37 | #define FREC_ADDED_PHEADER 128 | |
38 | -#define FREC_CHECK_NOSIGN 256 | |
39 | -#define FREC_TEST_PKTSZ 512 | |
40 | +#define FREC_TEST_PKTSZ 256 | |
41 | ||
42 | #ifdef HAVE_DNSSEC | |
43 | #define HASH_SIZE 20 /* SHA-1 digest size */ | |
44 | @@ -626,9 +621,7 @@ struct frec { | |
45 | #ifdef HAVE_DNSSEC | |
46 | int class, work_counter; | |
47 | struct blockdata *stash; /* Saved reply, whilst we validate */ | |
48 | - struct blockdata *orig_domain; /* domain of original query, whilst | |
49 | - we're seeing is if in unsigned domain */ | |
50 | - size_t stash_len, name_start, name_len; | |
51 | + size_t stash_len; | |
52 | struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */ | |
53 | struct frec *blocking_query; /* Query which is blocking us. */ | |
54 | #endif | |
55 | @@ -1162,8 +1155,8 @@ int in_zone(struct auth_zone *zone, char *name, char **cut); | |
56 | size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr, int edns_pktsz); | |
57 | int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class); | |
58 | int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class); | |
59 | -int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int *neganswer, int *nons); | |
60 | -int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname); | |
61 | +int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, | |
62 | + int check_unsigned, int *neganswer, int *nons); | |
63 | int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen); | |
64 | size_t filter_rrsigs(struct dns_header *header, size_t plen); | |
65 | unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name); | |
66 | diff --git a/src/dnssec.c b/src/dnssec.c | |
67 | index b4dc14e..de7b335 100644 | |
68 | --- a/src/dnssec.c | |
69 | +++ b/src/dnssec.c | |
70 | @@ -65,8 +65,10 @@ static char *algo_digest_name(int algo) | |
71 | case 8: return "sha256"; | |
72 | case 10: return "sha512"; | |
73 | case 12: return "gosthash94"; | |
74 | +#ifndef NO_NETTLE_ECC | |
75 | case 13: return "sha256"; | |
76 | case 14: return "sha384"; | |
77 | +#endif | |
78 | default: return NULL; | |
79 | } | |
80 | } | |
81 | @@ -592,30 +594,30 @@ static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end, | |
82 | } | |
83 | } | |
84 | ||
85 | -static int expand_workspace(unsigned char ***wkspc, int *sz, int new) | |
86 | +static int expand_workspace(unsigned char ***wkspc, int *szp, int new) | |
87 | { | |
88 | unsigned char **p; | |
89 | - int new_sz = *sz; | |
90 | - | |
91 | - if (new_sz > new) | |
92 | + int old = *szp; | |
93 | + | |
94 | + if (old >= new+1) | |
95 | return 1; | |
96 | ||
97 | if (new >= 100) | |
98 | return 0; | |
99 | ||
100 | - new_sz += 5; | |
101 | + new += 5; | |
102 | ||
103 | - if (!(p = whine_malloc((new_sz) * sizeof(unsigned char **)))) | |
104 | + if (!(p = whine_malloc(new * sizeof(unsigned char **)))) | |
105 | return 0; | |
106 | ||
107 | - if (*wkspc) | |
108 | + if (old != 0 && *wkspc) | |
109 | { | |
110 | - memcpy(p, *wkspc, *sz * sizeof(unsigned char **)); | |
111 | + memcpy(p, *wkspc, old * sizeof(unsigned char **)); | |
112 | free(*wkspc); | |
113 | } | |
114 | ||
115 | *wkspc = p; | |
116 | - *sz = new_sz; | |
117 | + *szp = new; | |
118 | ||
119 | return 1; | |
120 | } | |
121 | @@ -706,47 +708,28 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int | |
122 | } while (swap); | |
123 | } | |
124 | ||
125 | -/* Validate a single RRset (class, type, name) in the supplied DNS reply | |
126 | - Return code: | |
127 | - STAT_SECURE if it validates. | |
128 | - STAT_SECURE_WILDCARD if it validates and is the result of wildcard expansion. | |
129 | - (In this case *wildcard_out points to the "body" of the wildcard within name.) | |
130 | - STAT_NO_SIG no RRsigs found. | |
131 | - STAT_INSECURE RRset empty. | |
132 | - STAT_BOGUS signature is wrong, bad packet. | |
133 | - STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname) | |
134 | - | |
135 | - if key is non-NULL, use that key, which has the algo and tag given in the params of those names, | |
136 | - otherwise find the key in the cache. | |
137 | +static unsigned char **rrset = NULL, **sigs = NULL; | |
138 | ||
139 | - name is unchanged on exit. keyname is used as workspace and trashed. | |
140 | -*/ | |
141 | -static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class, int type, | |
142 | - char *name, char *keyname, char **wildcard_out, struct blockdata *key, int keylen, int algo_in, int keytag_in) | |
143 | +/* Get pointers to RRset menbers and signature(s) for same. | |
144 | + Check signatures, and return keyname associated in keyname. */ | |
145 | +static int explore_rrset(struct dns_header *header, size_t plen, int class, int type, | |
146 | + char *name, char *keyname, int *sigcnt, int *rrcnt) | |
147 | { | |
148 | - static unsigned char **rrset = NULL, **sigs = NULL; | |
149 | - static int rrset_sz = 0, sig_sz = 0; | |
150 | - | |
151 | + static int rrset_sz = 0, sig_sz = 0; | |
152 | unsigned char *p; | |
153 | - int rrsetidx, sigidx, res, rdlen, j, name_labels; | |
154 | - struct crec *crecp = NULL; | |
155 | - int type_covered, algo, labels, orig_ttl, sig_expiration, sig_inception, key_tag; | |
156 | - u16 *rr_desc = get_desc(type); | |
157 | - | |
158 | - if (wildcard_out) | |
159 | - *wildcard_out = NULL; | |
160 | - | |
161 | + int rrsetidx, sigidx, j, rdlen, res; | |
162 | + int name_labels = count_labels(name); /* For 4035 5.3.2 check */ | |
163 | + int gotkey = 0; | |
164 | + | |
165 | if (!(p = skip_questions(header, plen))) | |
166 | return STAT_BOGUS; | |
167 | - | |
168 | - name_labels = count_labels(name); /* For 4035 5.3.2 check */ | |
169 | ||
170 | - /* look for RRSIGs for this RRset and get pointers to each RR in the set. */ | |
171 | + /* look for RRSIGs for this RRset and get pointers to each RR in the set. */ | |
172 | for (rrsetidx = 0, sigidx = 0, j = ntohs(header->ancount) + ntohs(header->nscount); | |
173 | j != 0; j--) | |
174 | { | |
175 | unsigned char *pstart, *pdata; | |
176 | - int stype, sclass; | |
177 | + int stype, sclass, algo, type_covered, labels, sig_expiration, sig_inception; | |
178 | ||
179 | pstart = p; | |
180 | ||
181 | @@ -762,14 +745,14 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in | |
182 | GETSHORT(rdlen, p); | |
183 | ||
184 | if (!CHECK_LEN(header, p, plen, rdlen)) | |
185 | - return STAT_BOGUS; | |
186 | + return 0; | |
187 | ||
188 | if (res == 1 && sclass == class) | |
189 | { | |
190 | if (stype == type) | |
191 | { | |
192 | if (!expand_workspace(&rrset, &rrset_sz, rrsetidx)) | |
193 | - return STAT_BOGUS; | |
194 | + return 0; | |
195 | ||
196 | rrset[rrsetidx++] = pstart; | |
197 | } | |
198 | @@ -777,14 +760,54 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in | |
199 | if (stype == T_RRSIG) | |
200 | { | |
201 | if (rdlen < 18) | |
202 | - return STAT_BOGUS; /* bad packet */ | |
203 | + return 0; /* bad packet */ | |
204 | ||
205 | GETSHORT(type_covered, p); | |
206 | + algo = *p++; | |
207 | + labels = *p++; | |
208 | + p += 4; /* orig_ttl */ | |
209 | + GETLONG(sig_expiration, p); | |
210 | + GETLONG(sig_inception, p); | |
211 | + p += 2; /* key_tag */ | |
212 | ||
213 | - if (type_covered == type) | |
214 | + if (gotkey) | |
215 | + { | |
216 | + /* If there's more than one SIG, ensure they all have same keyname */ | |
217 | + if (extract_name(header, plen, &p, keyname, 0, 0) != 1) | |
218 | + return 0; | |
219 | + } | |
220 | + else | |
221 | + { | |
222 | + gotkey = 1; | |
223 | + | |
224 | + if (!extract_name(header, plen, &p, keyname, 1, 0)) | |
225 | + return 0; | |
226 | + | |
227 | + /* RFC 4035 5.3.1 says that the Signer's Name field MUST equal | |
228 | + the name of the zone containing the RRset. We can't tell that | |
229 | + for certain, but we can check that the RRset name is equal to | |
230 | + or encloses the signers name, which should be enough to stop | |
231 | + an attacker using signatures made with the key of an unrelated | |
232 | + zone he controls. Note that the root key is always allowed. */ | |
233 | + if (*keyname != 0) | |
234 | + { | |
235 | + char *name_start; | |
236 | + for (name_start = name; !hostname_isequal(name_start, keyname); ) | |
237 | + if ((name_start = strchr(name_start, '.'))) | |
238 | + name_start++; /* chop a label off and try again */ | |
239 | + else | |
240 | + return 0; | |
241 | + } | |
242 | + } | |
243 | + | |
244 | + /* Don't count signatures for algos we don't support */ | |
245 | + if (check_date_range(sig_inception, sig_expiration) && | |
246 | + labels <= name_labels && | |
247 | + type_covered == type && | |
248 | + algo_digest_name(algo)) | |
249 | { | |
250 | if (!expand_workspace(&sigs, &sig_sz, sigidx)) | |
251 | - return STAT_BOGUS; | |
252 | + return 0; | |
253 | ||
254 | sigs[sigidx++] = pdata; | |
255 | } | |
256 | @@ -794,17 +817,45 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in | |
257 | } | |
258 | ||
259 | if (!ADD_RDLEN(header, p, plen, rdlen)) | |
260 | - return STAT_BOGUS; | |
261 | + return 0; | |
262 | } | |
263 | ||
264 | - /* RRset empty */ | |
265 | - if (rrsetidx == 0) | |
266 | - return STAT_INSECURE; | |
267 | + *sigcnt = sigidx; | |
268 | + *rrcnt = rrsetidx; | |
269 | + | |
270 | + return 1; | |
271 | +} | |
272 | + | |
273 | +/* Validate a single RRset (class, type, name) in the supplied DNS reply | |
274 | + Return code: | |
275 | + STAT_SECURE if it validates. | |
276 | + STAT_SECURE_WILDCARD if it validates and is the result of wildcard expansion. | |
277 | + (In this case *wildcard_out points to the "body" of the wildcard within name.) | |
278 | + STAT_BOGUS signature is wrong, bad packet. | |
279 | + STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname) | |
280 | + STAT_NEED_DS need DS to complete validation (name is returned in keyname) | |
281 | + | |
282 | + if key is non-NULL, use that key, which has the algo and tag given in the params of those names, | |
283 | + otherwise find the key in the cache. | |
284 | ||
285 | - /* no RRSIGs */ | |
286 | - if (sigidx == 0) | |
287 | - return STAT_NO_SIG; | |
288 | + name is unchanged on exit. keyname is used as workspace and trashed. | |
289 | + | |
290 | + Call explore_rrset first to find and count RRs and sigs. | |
291 | +*/ | |
292 | +static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class, int type, int sigidx, int rrsetidx, | |
293 | + char *name, char *keyname, char **wildcard_out, struct blockdata *key, int keylen, int algo_in, int keytag_in) | |
294 | +{ | |
295 | + unsigned char *p; | |
296 | + int rdlen, j, name_labels; | |
297 | + struct crec *crecp = NULL; | |
298 | + int algo, labels, orig_ttl, key_tag; | |
299 | + u16 *rr_desc = get_desc(type); | |
300 | + | |
301 | + if (wildcard_out) | |
302 | + *wildcard_out = NULL; | |
303 | ||
304 | + name_labels = count_labels(name); /* For 4035 5.3.2 check */ | |
305 | + | |
306 | /* Sort RRset records into canonical order. | |
307 | Note that at this point keyname and daemon->workspacename buffs are | |
308 | unused, and used as workspace by the sort. */ | |
309 | @@ -828,44 +879,16 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in | |
310 | algo = *p++; | |
311 | labels = *p++; | |
312 | GETLONG(orig_ttl, p); | |
313 | - GETLONG(sig_expiration, p); | |
314 | - GETLONG(sig_inception, p); | |
315 | + p += 8; /* sig_expiration, sig_inception already checked */ | |
316 | GETSHORT(key_tag, p); | |
317 | ||
318 | if (!extract_name(header, plen, &p, keyname, 1, 0)) | |
319 | return STAT_BOGUS; | |
320 | ||
321 | - /* RFC 4035 5.3.1 says that the Signer's Name field MUST equal | |
322 | - the name of the zone containing the RRset. We can't tell that | |
323 | - for certain, but we can check that the RRset name is equal to | |
324 | - or encloses the signers name, which should be enough to stop | |
325 | - an attacker using signatures made with the key of an unrelated | |
326 | - zone he controls. Note that the root key is always allowed. */ | |
327 | - if (*keyname != 0) | |
328 | - { | |
329 | - int failed = 0; | |
330 | - | |
331 | - for (name_start = name; !hostname_isequal(name_start, keyname); ) | |
332 | - if ((name_start = strchr(name_start, '.'))) | |
333 | - name_start++; /* chop a label off and try again */ | |
334 | - else | |
335 | - { | |
336 | - failed = 1; | |
337 | - break; | |
338 | - } | |
339 | - | |
340 | - /* Bad sig, try another */ | |
341 | - if (failed) | |
342 | - continue; | |
343 | - } | |
344 | - | |
345 | - /* Other 5.3.1 checks */ | |
346 | - if (!check_date_range(sig_inception, sig_expiration) || | |
347 | - labels > name_labels || | |
348 | - !(hash = hash_find(algo_digest_name(algo))) || | |
349 | + if (!(hash = hash_find(algo_digest_name(algo))) || | |
350 | !hash_init(hash, &ctx, &digest)) | |
351 | continue; | |
352 | - | |
353 | + | |
354 | /* OK, we have the signature record, see if the relevant DNSKEY is in the cache. */ | |
355 | if (!key && !(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY))) | |
356 | return STAT_NEED_KEY; | |
357 | @@ -971,10 +994,11 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in | |
358 | /* The DNS packet is expected to contain the answer to a DNSKEY query. | |
359 | Put all DNSKEYs in the answer which are valid into the cache. | |
360 | return codes: | |
361 | - STAT_SECURE At least one valid DNSKEY found and in cache. | |
362 | - STAT_BOGUS No DNSKEYs found, which can be validated with DS, | |
363 | - or self-sign for DNSKEY RRset is not valid, bad packet. | |
364 | - STAT_NEED_DS DS records to validate a key not found, name in keyname | |
365 | + STAT_OK Done, key(s) in cache. | |
366 | + STAT_BOGUS No DNSKEYs found, which can be validated with DS, | |
367 | + or self-sign for DNSKEY RRset is not valid, bad packet. | |
368 | + STAT_NEED_DS DS records to validate a key not found, name in keyname | |
369 | + STAT_NEED_DNSKEY DNSKEY records to validate a key not found, name in keyname | |
370 | */ | |
371 | int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class) | |
372 | { | |
373 | @@ -1001,23 +1025,6 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch | |
374 | return STAT_NEED_DS; | |
375 | } | |
376 | ||
377 | - /* If we've cached that DS provably doesn't exist, result must be INSECURE */ | |
378 | - if (crecp->flags & F_NEG) | |
379 | - return STAT_INSECURE_DS; | |
380 | - | |
381 | - /* 4035 5.2 | |
382 | - If the validator does not support any of the algorithms listed in an | |
383 | - authenticated DS RRset, then the resolver has no supported | |
384 | - authentication path leading from the parent to the child. The | |
385 | - resolver should treat this case as it would the case of an | |
386 | - authenticated NSEC RRset proving that no DS RRset exists, */ | |
387 | - for (recp1 = crecp; recp1; recp1 = cache_find_by_name(recp1, name, now, F_DS)) | |
388 | - if (hash_find(ds_digest_name(recp1->addr.ds.digest))) | |
389 | - break; | |
390 | - | |
391 | - if (!recp1) | |
392 | - return STAT_INSECURE_DS; | |
393 | - | |
394 | /* NOTE, we need to find ONE DNSKEY which matches the DS */ | |
395 | for (valid = 0, j = ntohs(header->ancount); j != 0 && !valid; j--) | |
396 | { | |
397 | @@ -1070,7 +1077,8 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch | |
398 | void *ctx; | |
399 | unsigned char *digest, *ds_digest; | |
400 | const struct nettle_hash *hash; | |
401 | - | |
402 | + int sigcnt, rrcnt; | |
403 | + | |
404 | if (recp1->addr.ds.algo == algo && | |
405 | recp1->addr.ds.keytag == keytag && | |
406 | recp1->uid == (unsigned int)class && | |
407 | @@ -1088,10 +1096,14 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch | |
408 | ||
409 | from_wire(name); | |
410 | ||
411 | - if (recp1->addr.ds.keylen == (int)hash->digest_size && | |
412 | + if (!(recp1->flags & F_NEG) && | |
413 | + recp1->addr.ds.keylen == (int)hash->digest_size && | |
414 | (ds_digest = blockdata_retrieve(recp1->addr.key.keydata, recp1->addr.ds.keylen, NULL)) && | |
415 | memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0 && | |
416 | - validate_rrset(now, header, plen, class, T_DNSKEY, name, keyname, NULL, key, rdlen - 4, algo, keytag) == STAT_SECURE) | |
417 | + explore_rrset(header, plen, class, T_DNSKEY, name, keyname, &sigcnt, &rrcnt) && | |
418 | + sigcnt != 0 && rrcnt != 0 && | |
419 | + validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname, | |
420 | + NULL, key, rdlen - 4, algo, keytag) == STAT_SECURE) | |
421 | { | |
422 | valid = 1; | |
423 | break; | |
424 | @@ -1112,7 +1124,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch | |
425 | { | |
426 | /* Ensure we have type, class TTL and length */ | |
427 | if (!(rc = extract_name(header, plen, &p, name, 0, 10))) | |
428 | - return STAT_INSECURE; /* bad packet */ | |
429 | + return STAT_BOGUS; /* bad packet */ | |
430 | ||
431 | GETSHORT(qtype, p); | |
432 | GETSHORT(qclass, p); | |
433 | @@ -1198,7 +1210,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch | |
434 | ||
435 | /* commit cache insert. */ | |
436 | cache_end_insert(); | |
437 | - return STAT_SECURE; | |
438 | + return STAT_OK; | |
439 | } | |
440 | ||
441 | log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY"); | |
442 | @@ -1207,12 +1219,14 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch | |
443 | ||
444 | /* The DNS packet is expected to contain the answer to a DS query | |
445 | Put all DSs in the answer which are valid into the cache. | |
446 | + Also handles replies which prove that there's no DS at this location, | |
447 | + either because the zone is unsigned or this isn't a zone cut. These are | |
448 | + cached too. | |
449 | return codes: | |
450 | - STAT_SECURE At least one valid DS found and in cache. | |
451 | - STAT_NO_DS It's proved there's no DS here. | |
452 | - STAT_NO_NS It's proved there's no DS _or_ NS here. | |
453 | + STAT_OK At least one valid DS found and in cache. | |
454 | STAT_BOGUS no DS in reply or not signed, fails validation, bad packet. | |
455 | STAT_NEED_KEY DNSKEY records to validate a DS not found, name in keyname | |
456 | + STAT_NEED_DS DS record needed. | |
457 | */ | |
458 | ||
459 | int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class) | |
460 | @@ -1230,7 +1244,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char | |
461 | if (qtype != T_DS || qclass != class) | |
462 | val = STAT_BOGUS; | |
463 | else | |
464 | - val = dnssec_validate_reply(now, header, plen, name, keyname, NULL, &neganswer, &nons); | |
465 | + val = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons); | |
466 | /* Note dnssec_validate_reply() will have cached positive answers */ | |
467 | ||
468 | if (val == STAT_INSECURE) | |
469 | @@ -1242,22 +1256,21 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char | |
470 | ||
471 | if (!(p = skip_section(p, ntohs(header->ancount), header, plen))) | |
472 | val = STAT_BOGUS; | |
473 | - | |
474 | - /* If we return STAT_NO_SIG, name contains the name of the DS query */ | |
475 | - if (val == STAT_NO_SIG) | |
476 | - return val; | |
477 | ||
478 | /* If the key needed to validate the DS is on the same domain as the DS, we'll | |
479 | loop getting nowhere. Stop that now. This can happen of the DS answer comes | |
480 | from the DS's zone, and not the parent zone. */ | |
481 | - if (val == STAT_BOGUS || (val == STAT_NEED_KEY && hostname_isequal(name, keyname))) | |
482 | + if (val == STAT_BOGUS || (val == STAT_NEED_KEY && hostname_isequal(name, keyname))) | |
483 | { | |
484 | log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS"); | |
485 | return STAT_BOGUS; | |
486 | } | |
487 | + | |
488 | + if (val != STAT_SECURE) | |
489 | + return val; | |
490 | ||
491 | /* By here, the answer is proved secure, and a positive answer has been cached. */ | |
492 | - if (val == STAT_SECURE && neganswer) | |
493 | + if (neganswer) | |
494 | { | |
495 | int rdlen, flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK; | |
496 | unsigned long ttl, minttl = ULONG_MAX; | |
497 | @@ -1317,15 +1330,14 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char | |
498 | ||
499 | cache_end_insert(); | |
500 | ||
501 | - log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no delegation" : "no DS"); | |
502 | + log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "no DS"); | |
503 | } | |
504 | - | |
505 | - return nons ? STAT_NO_NS : STAT_NO_DS; | |
506 | } | |
507 | ||
508 | - return val; | |
509 | + return STAT_OK; | |
510 | } | |
511 | ||
512 | + | |
513 | /* 4034 6.1 */ | |
514 | static int hostname_cmp(const char *a, const char *b) | |
515 | { | |
516 | @@ -1452,7 +1464,7 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi | |
517 | int mask = 0x80 >> (type & 0x07); | |
518 | ||
519 | if (nons) | |
520 | - *nons = 0; | |
521 | + *nons = 1; | |
522 | ||
523 | /* Find NSEC record that proves name doesn't exist */ | |
524 | for (i = 0; i < nsec_count; i++) | |
525 | @@ -1480,9 +1492,22 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi | |
526 | /* rdlen is now length of type map, and p points to it */ | |
527 | ||
528 | /* If we can prove that there's no NS record, return that information. */ | |
529 | - if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) == 0) | |
530 | - *nons = 1; | |
531 | + if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) != 0) | |
532 | + *nons = 0; | |
533 | ||
534 | + if (rdlen >= 2 && p[0] == 0) | |
535 | + { | |
536 | + /* A CNAME answer would also be valid, so if there's a CNAME is should | |
537 | + have been returned. */ | |
538 | + if ((p[2] & (0x80 >> T_CNAME)) != 0) | |
539 | + return STAT_BOGUS; | |
540 | + | |
541 | + /* If the SOA bit is set for a DS record, then we have the | |
542 | + DS from the wrong side of the delegation. */ | |
543 | + if (type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0) | |
544 | + return STAT_BOGUS; | |
545 | + } | |
546 | + | |
547 | while (rdlen >= 2) | |
548 | { | |
549 | if (!CHECK_LEN(header, p, plen, rdlen)) | |
550 | @@ -1586,7 +1611,7 @@ static int base32_decode(char *in, unsigned char *out) | |
551 | static int check_nsec3_coverage(struct dns_header *header, size_t plen, int digest_len, unsigned char *digest, int type, | |
552 | char *workspace1, char *workspace2, unsigned char **nsecs, int nsec_count, int *nons) | |
553 | { | |
554 | - int i, hash_len, salt_len, base32_len, rdlen; | |
555 | + int i, hash_len, salt_len, base32_len, rdlen, flags; | |
556 | unsigned char *p, *psave; | |
557 | ||
558 | for (i = 0; i < nsec_count; i++) | |
559 | @@ -1599,7 +1624,9 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige | |
560 | p += 8; /* class, type, TTL */ | |
561 | GETSHORT(rdlen, p); | |
562 | psave = p; | |
563 | - p += 4; /* algo, flags, iterations */ | |
564 | + p++; /* algo */ | |
565 | + flags = *p++; /* flags */ | |
566 | + p += 2; /* iterations */ | |
567 | salt_len = *p++; /* salt_len */ | |
568 | p += salt_len; /* salt */ | |
569 | hash_len = *p++; /* p now points to next hashed name */ | |
570 | @@ -1626,16 +1653,29 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige | |
571 | return 0; | |
572 | ||
573 | /* If we can prove that there's no NS record, return that information. */ | |
574 | - if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) == 0) | |
575 | - *nons = 1; | |
576 | + if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) != 0) | |
577 | + *nons = 0; | |
578 | ||
579 | + if (rdlen >= 2 && p[0] == 0) | |
580 | + { | |
581 | + /* A CNAME answer would also be valid, so if there's a CNAME is should | |
582 | + have been returned. */ | |
583 | + if ((p[2] & (0x80 >> T_CNAME)) != 0) | |
584 | + return 0; | |
585 | + | |
586 | + /* If the SOA bit is set for a DS record, then we have the | |
587 | + DS from the wrong side of the delegation. */ | |
588 | + if (type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0) | |
589 | + return 0; | |
590 | + } | |
591 | + | |
592 | while (rdlen >= 2) | |
593 | { | |
594 | if (p[0] == type >> 8) | |
595 | { | |
596 | /* Does the NSEC3 say our type exists? */ | |
597 | if (offset < p[1] && (p[offset+2] & mask) != 0) | |
598 | - return STAT_BOGUS; | |
599 | + return 0; | |
600 | ||
601 | break; /* finshed checking */ | |
602 | } | |
603 | @@ -1643,7 +1683,7 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige | |
604 | rdlen -= p[1]; | |
605 | p += p[1]; | |
606 | } | |
607 | - | |
608 | + | |
609 | return 1; | |
610 | } | |
611 | else if (rc < 0) | |
612 | @@ -1651,16 +1691,27 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige | |
613 | /* Normal case, hash falls between NSEC3 name-hash and next domain name-hash, | |
614 | wrap around case, name-hash falls between NSEC3 name-hash and end */ | |
615 | if (memcmp(p, digest, digest_len) >= 0 || memcmp(workspace2, p, digest_len) >= 0) | |
616 | - return 1; | |
617 | + { | |
618 | + if ((flags & 0x01) && nons) /* opt out */ | |
619 | + *nons = 0; | |
620 | + | |
621 | + return 1; | |
622 | + } | |
623 | } | |
624 | else | |
625 | { | |
626 | /* wrap around case, name falls between start and next domain name */ | |
627 | if (memcmp(workspace2, p, digest_len) >= 0 && memcmp(p, digest, digest_len) >= 0) | |
628 | - return 1; | |
629 | + { | |
630 | + if ((flags & 0x01) && nons) /* opt out */ | |
631 | + *nons = 0; | |
632 | + | |
633 | + return 1; | |
634 | + } | |
635 | } | |
636 | } | |
637 | } | |
638 | + | |
639 | return 0; | |
640 | } | |
641 | ||
642 | @@ -1673,7 +1724,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns | |
643 | char *closest_encloser, *next_closest, *wildcard; | |
644 | ||
645 | if (nons) | |
646 | - *nons = 0; | |
647 | + *nons = 1; | |
648 | ||
649 | /* Look though the NSEC3 records to find the first one with | |
650 | an algorithm we support (currently only algo == 1). | |
651 | @@ -1813,16 +1864,81 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns | |
652 | ||
653 | return STAT_SECURE; | |
654 | } | |
655 | - | |
656 | -/* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3) */ | |
657 | -/* Returns are the same as validate_rrset, plus the class if the missing key is in *class */ | |
658 | + | |
659 | +/* Check signing status of name. | |
660 | + returns: | |
661 | + STAT_SECURE zone is signed. | |
662 | + STAT_INSECURE zone proved unsigned. | |
663 | + STAT_NEED_DS require DS record of name returned in keyname. | |
664 | + | |
665 | + name returned unaltered. | |
666 | +*/ | |
667 | +static int zone_status(char *name, int class, char *keyname, time_t now) | |
668 | +{ | |
669 | + int name_start = strlen(name); | |
670 | + struct crec *crecp; | |
671 | + char *p; | |
672 | + | |
673 | + while (1) | |
674 | + { | |
675 | + strcpy(keyname, &name[name_start]); | |
676 | + | |
677 | + if (!(crecp = cache_find_by_name(NULL, keyname, now, F_DS))) | |
678 | + return STAT_NEED_DS; | |
679 | + else | |
680 | + do | |
681 | + { | |
682 | + if (crecp->uid == (unsigned int)class) | |
683 | + { | |
684 | + /* F_DNSSECOK misused in DS cache records to non-existance of NS record. | |
685 | + F_NEG && !F_DNSSECOK implies that we've proved there's no DS record here, | |
686 | + but that's because there's no NS record either, ie this isn't the start | |
687 | + of a zone. We only prove that the DNS tree below a node is unsigned when | |
688 | + we prove that we're at a zone cut AND there's no DS record. | |
689 | + */ | |
690 | + if (crecp->flags & F_NEG) | |
691 | + { | |
692 | + if (crecp->flags & F_DNSSECOK) | |
693 | + return STAT_INSECURE; /* proved no DS here */ | |
694 | + } | |
695 | + else if (!ds_digest_name(crecp->addr.ds.digest) || !algo_digest_name(crecp->addr.ds.algo)) | |
696 | + return STAT_INSECURE; /* algo we can't use - insecure */ | |
697 | + } | |
698 | + } | |
699 | + while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS))); | |
700 | + | |
701 | + if (name_start == 0) | |
702 | + break; | |
703 | + | |
704 | + for (p = &name[name_start-2]; (*p != '.') && (p != name); p--); | |
705 | + | |
706 | + if (p != name) | |
707 | + p++; | |
708 | + | |
709 | + name_start = p - name; | |
710 | + } | |
711 | + | |
712 | + return STAT_SECURE; | |
713 | +} | |
714 | + | |
715 | +/* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3) | |
716 | + Return code: | |
717 | + STAT_SECURE if it validates. | |
718 | + STAT_INSECURE at least one RRset not validated, because in unsigned zone. | |
719 | + STAT_BOGUS signature is wrong, bad packet, no validation where there should be. | |
720 | + STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname, class in *class) | |
721 | + STAT_NEED_DS need DS to complete validation (name is returned in keyname) | |
722 | +*/ | |
723 | int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, | |
724 | - int *class, int *neganswer, int *nons) | |
725 | + int *class, int check_unsigned, int *neganswer, int *nons) | |
726 | { | |
727 | - unsigned char *ans_start, *qname, *p1, *p2, **nsecs; | |
728 | - int type1, class1, rdlen1, type2, class2, rdlen2, qclass, qtype; | |
729 | - int i, j, rc, nsec_count, cname_count = CNAME_CHAIN; | |
730 | - int nsec_type = 0, have_answer = 0; | |
731 | + static unsigned char **targets = NULL; | |
732 | + static int target_sz = 0; | |
733 | + | |
734 | + unsigned char *ans_start, *p1, *p2, **nsecs; | |
735 | + int type1, class1, rdlen1, type2, class2, rdlen2, qclass, qtype, targetidx; | |
736 | + int i, j, rc, nsec_count; | |
737 | + int nsec_type; | |
738 | ||
739 | if (neganswer) | |
740 | *neganswer = 0; | |
741 | @@ -1833,70 +1949,51 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch | |
742 | if (RCODE(header) != NXDOMAIN && RCODE(header) != NOERROR) | |
743 | return STAT_INSECURE; | |
744 | ||
745 | - qname = p1 = (unsigned char *)(header+1); | |
746 | + p1 = (unsigned char *)(header+1); | |
747 | ||
748 | + /* Find all the targets we're looking for answers to. | |
749 | + The zeroth array element is for the query, subsequent ones | |
750 | + for CNAME targets, unless the query is for a CNAME. */ | |
751 | + | |
752 | + if (!expand_workspace(&targets, &target_sz, 0)) | |
753 | + return STAT_BOGUS; | |
754 | + | |
755 | + targets[0] = p1; | |
756 | + targetidx = 1; | |
757 | + | |
758 | if (!extract_name(header, plen, &p1, name, 1, 4)) | |
759 | return STAT_BOGUS; | |
760 | - | |
761 | + | |
762 | GETSHORT(qtype, p1); | |
763 | GETSHORT(qclass, p1); | |
764 | ans_start = p1; | |
765 | - | |
766 | - if (qtype == T_ANY) | |
767 | - have_answer = 1; | |
768 | ||
769 | - /* Can't validate an RRISG query */ | |
770 | + /* Can't validate an RRSIG query */ | |
771 | if (qtype == T_RRSIG) | |
772 | return STAT_INSECURE; | |
773 | - | |
774 | - cname_loop: | |
775 | - for (j = ntohs(header->ancount); j != 0; j--) | |
776 | - { | |
777 | - /* leave pointer to missing name in qname */ | |
778 | - | |
779 | - if (!(rc = extract_name(header, plen, &p1, name, 0, 10))) | |
780 | - return STAT_BOGUS; /* bad packet */ | |
781 | - | |
782 | - GETSHORT(type2, p1); | |
783 | - GETSHORT(class2, p1); | |
784 | - p1 += 4; /* TTL */ | |
785 | - GETSHORT(rdlen2, p1); | |
786 | - | |
787 | - if (rc == 1 && qclass == class2) | |
788 | - { | |
789 | - /* Do we have an answer for the question? */ | |
790 | - if (type2 == qtype) | |
791 | - { | |
792 | - have_answer = 1; | |
793 | - break; | |
794 | - } | |
795 | - else if (type2 == T_CNAME) | |
796 | - { | |
797 | - qname = p1; | |
798 | - | |
799 | - /* looped CNAMES */ | |
800 | - if (!cname_count-- || !extract_name(header, plen, &p1, name, 1, 0)) | |
801 | - return STAT_BOGUS; | |
802 | - | |
803 | - p1 = ans_start; | |
804 | - goto cname_loop; | |
805 | - } | |
806 | - } | |
807 | - | |
808 | - if (!ADD_RDLEN(header, p1, plen, rdlen2)) | |
809 | - return STAT_BOGUS; | |
810 | - } | |
811 | - | |
812 | - if (neganswer && !have_answer) | |
813 | - *neganswer = 1; | |
814 | ||
815 | - /* No data, therefore no sigs */ | |
816 | - if (ntohs(header->ancount) + ntohs(header->nscount) == 0) | |
817 | - { | |
818 | - *keyname = 0; | |
819 | - return STAT_NO_SIG; | |
820 | - } | |
821 | - | |
822 | + if (qtype != T_CNAME) | |
823 | + for (j = ntohs(header->ancount); j != 0; j--) | |
824 | + { | |
825 | + if (!(p1 = skip_name(p1, header, plen, 10))) | |
826 | + return STAT_BOGUS; /* bad packet */ | |
827 | + | |
828 | + GETSHORT(type2, p1); | |
829 | + p1 += 6; /* class, TTL */ | |
830 | + GETSHORT(rdlen2, p1); | |
831 | + | |
832 | + if (type2 == T_CNAME) | |
833 | + { | |
834 | + if (!expand_workspace(&targets, &target_sz, targetidx)) | |
835 | + return STAT_BOGUS; | |
836 | + | |
837 | + targets[targetidx++] = p1; /* pointer to target name */ | |
838 | + } | |
839 | + | |
840 | + if (!ADD_RDLEN(header, p1, plen, rdlen2)) | |
841 | + return STAT_BOGUS; | |
842 | + } | |
843 | + | |
844 | for (p1 = ans_start, i = 0; i < ntohs(header->ancount) + ntohs(header->nscount); i++) | |
845 | { | |
846 | if (!extract_name(header, plen, &p1, name, 1, 10)) | |
847 | @@ -1931,7 +2028,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch | |
848 | /* Not done, validate now */ | |
849 | if (j == i) | |
850 | { | |
851 | - int ttl, keytag, algo, digest, type_covered; | |
852 | + int ttl, keytag, algo, digest, type_covered, sigcnt, rrcnt; | |
853 | unsigned char *psave; | |
854 | struct all_addr a; | |
855 | struct blockdata *key; | |
856 | @@ -1939,143 +2036,186 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch | |
857 | char *wildname; | |
858 | int have_wildcard = 0; | |
859 | ||
860 | - rc = validate_rrset(now, header, plen, class1, type1, name, keyname, &wildname, NULL, 0, 0, 0); | |
861 | - | |
862 | - if (rc == STAT_SECURE_WILDCARD) | |
863 | - { | |
864 | - have_wildcard = 1; | |
865 | - | |
866 | - /* An attacker replay a wildcard answer with a different | |
867 | - answer and overlay a genuine RR. To prove this | |
868 | - hasn't happened, the answer must prove that | |
869 | - the gennuine record doesn't exist. Check that here. */ | |
870 | - if (!nsec_type && !(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class1))) | |
871 | - return STAT_BOGUS; /* No NSECs or bad packet */ | |
872 | - | |
873 | - if (nsec_type == T_NSEC) | |
874 | - rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1, NULL); | |
875 | - else | |
876 | - rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, | |
877 | - keyname, name, type1, wildname, NULL); | |
878 | - | |
879 | - if (rc != STAT_SECURE) | |
880 | - return rc; | |
881 | - } | |
882 | - else if (rc != STAT_SECURE) | |
883 | - { | |
884 | - if (class) | |
885 | - *class = class1; /* Class for DS or DNSKEY */ | |
886 | + if (!explore_rrset(header, plen, class1, type1, name, keyname, &sigcnt, &rrcnt)) | |
887 | + return STAT_BOGUS; | |
888 | ||
889 | - if (rc == STAT_NO_SIG) | |
890 | + /* No signatures for RRset. We can be configured to assume this is OK and return a INSECURE result. */ | |
891 | + if (sigcnt == 0) | |
892 | + { | |
893 | + if (check_unsigned) | |
894 | { | |
895 | - /* If we dropped off the end of a CNAME chain, return | |
896 | - STAT_NO_SIG and the last name is keyname. This is used for proving non-existence | |
897 | - if DS records in CNAME chains. */ | |
898 | - if (cname_count == CNAME_CHAIN || i < ntohs(header->ancount)) | |
899 | - /* No CNAME chain, or no sig in answer section, return empty name. */ | |
900 | - *keyname = 0; | |
901 | - else if (!extract_name(header, plen, &qname, keyname, 1, 0)) | |
902 | - return STAT_BOGUS; | |
903 | + rc = zone_status(name, class1, keyname, now); | |
904 | + if (rc == STAT_SECURE) | |
905 | + rc = STAT_BOGUS; | |
906 | + if (class) | |
907 | + *class = class1; /* Class for NEED_DS or NEED_DNSKEY */ | |
908 | } | |
909 | - | |
910 | + else | |
911 | + rc = STAT_INSECURE; | |
912 | + | |
913 | return rc; | |
914 | } | |
915 | ||
916 | - /* Cache RRsigs in answer section, and if we just validated a DS RRset, cache it */ | |
917 | - cache_start_insert(); | |
918 | + /* explore_rrset() gives us key name from sigs in keyname. | |
919 | + Can't overwrite name here. */ | |
920 | + strcpy(daemon->workspacename, keyname); | |
921 | + rc = zone_status(daemon->workspacename, class1, keyname, now); | |
922 | + if (rc != STAT_SECURE) | |
923 | + { | |
924 | + /* Zone is insecure, don't need to validate RRset */ | |
925 | + if (class) | |
926 | + *class = class1; /* Class for NEED_DS or NEED_DNSKEY */ | |
927 | + return rc; | |
928 | + } | |
929 | + | |
930 | + rc = validate_rrset(now, header, plen, class1, type1, sigcnt, rrcnt, name, keyname, &wildname, NULL, 0, 0, 0); | |
931 | ||
932 | - for (p2 = ans_start, j = 0; j < ntohs(header->ancount); j++) | |
933 | + if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS) | |
934 | { | |
935 | - if (!(rc = extract_name(header, plen, &p2, name, 0, 10))) | |
936 | - return STAT_BOGUS; /* bad packet */ | |
937 | + if (class) | |
938 | + *class = class1; /* Class for DS or DNSKEY */ | |
939 | + return rc; | |
940 | + } | |
941 | + else | |
942 | + { | |
943 | + /* rc is now STAT_SECURE or STAT_SECURE_WILDCARD */ | |
944 | + | |
945 | + /* Note if we've validated either the answer to the question | |
946 | + or the target of a CNAME. Any not noted will need NSEC or | |
947 | + to be in unsigned space. */ | |
948 | + | |
949 | + for (j = 0; j <targetidx; j++) | |
950 | + if ((p2 = targets[j])) | |
951 | + { | |
952 | + if (!(rc = extract_name(header, plen, &p2, name, 0, 10))) | |
953 | + return STAT_BOGUS; /* bad packet */ | |
954 | + | |
955 | + if (class1 == qclass && rc == 1 && (type1 == T_CNAME || type1 == qtype || qtype == T_ANY )) | |
956 | + targets[j] = NULL; | |
957 | + } | |
958 | + | |
959 | + if (rc == STAT_SECURE_WILDCARD) | |
960 | + { | |
961 | + have_wildcard = 1; | |
962 | ||
963 | - GETSHORT(type2, p2); | |
964 | - GETSHORT(class2, p2); | |
965 | - GETLONG(ttl, p2); | |
966 | - GETSHORT(rdlen2, p2); | |
967 | - | |
968 | - if (!CHECK_LEN(header, p2, plen, rdlen2)) | |
969 | - return STAT_BOGUS; /* bad packet */ | |
970 | - | |
971 | - if (class2 == class1 && rc == 1) | |
972 | - { | |
973 | - psave = p2; | |
974 | + /* An attacker replay a wildcard answer with a different | |
975 | + answer and overlay a genuine RR. To prove this | |
976 | + hasn't happened, the answer must prove that | |
977 | + the gennuine record doesn't exist. Check that here. */ | |
978 | + if (!(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class1))) | |
979 | + return STAT_BOGUS; /* No NSECs or bad packet */ | |
980 | + | |
981 | + /* Note that we may not yet have validated the NSEC/NSEC3 RRsets. Since the check | |
982 | + below returns either SECURE or BOGUS, that's not a problem. If the RRsets later fail | |
983 | + we'll return BOGUS then. */ | |
984 | ||
985 | - if (type1 == T_DS && type2 == T_DS) | |
986 | - { | |
987 | - if (rdlen2 < 4) | |
988 | - return STAT_BOGUS; /* bad packet */ | |
989 | - | |
990 | - GETSHORT(keytag, p2); | |
991 | - algo = *p2++; | |
992 | - digest = *p2++; | |
993 | - | |
994 | - /* Cache needs to known class for DNSSEC stuff */ | |
995 | - a.addr.dnssec.class = class2; | |
996 | - | |
997 | - if ((key = blockdata_alloc((char*)p2, rdlen2 - 4))) | |
998 | - { | |
999 | - if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DS | F_DNSSECOK))) | |
1000 | - blockdata_free(key); | |
1001 | - else | |
1002 | - { | |
1003 | - a.addr.keytag = keytag; | |
1004 | - log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u"); | |
1005 | - crecp->addr.ds.digest = digest; | |
1006 | - crecp->addr.ds.keydata = key; | |
1007 | - crecp->addr.ds.algo = algo; | |
1008 | - crecp->addr.ds.keytag = keytag; | |
1009 | - crecp->addr.ds.keylen = rdlen2 - 4; | |
1010 | - } | |
1011 | - } | |
1012 | - } | |
1013 | - else if (type2 == T_RRSIG) | |
1014 | - { | |
1015 | - if (rdlen2 < 18) | |
1016 | - return STAT_BOGUS; /* bad packet */ | |
1017 | + if (nsec_type == T_NSEC) | |
1018 | + rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1, NULL); | |
1019 | + else | |
1020 | + rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, | |
1021 | + keyname, name, type1, wildname, NULL); | |
1022 | + | |
1023 | + if (rc == STAT_BOGUS) | |
1024 | + return rc; | |
1025 | + } | |
1026 | + | |
1027 | + /* Cache RRsigs in answer section, and if we just validated a DS RRset, cache it */ | |
1028 | + /* Also note if the RRset is the answer to the question, or the target of a CNAME */ | |
1029 | + cache_start_insert(); | |
1030 | + | |
1031 | + for (p2 = ans_start, j = 0; j < ntohs(header->ancount); j++) | |
1032 | + { | |
1033 | + if (!(rc = extract_name(header, plen, &p2, name, 0, 10))) | |
1034 | + return STAT_BOGUS; /* bad packet */ | |
1035 | + | |
1036 | + GETSHORT(type2, p2); | |
1037 | + GETSHORT(class2, p2); | |
1038 | + GETLONG(ttl, p2); | |
1039 | + GETSHORT(rdlen2, p2); | |
1040 | + | |
1041 | + if (!CHECK_LEN(header, p2, plen, rdlen2)) | |
1042 | + return STAT_BOGUS; /* bad packet */ | |
1043 | + | |
1044 | + if (class2 == class1 && rc == 1) | |
1045 | + { | |
1046 | + psave = p2; | |
1047 | ||
1048 | - GETSHORT(type_covered, p2); | |
1049 | - | |
1050 | - if (type_covered == type1 && | |
1051 | - (type_covered == T_A || type_covered == T_AAAA || | |
1052 | - type_covered == T_CNAME || type_covered == T_DS || | |
1053 | - type_covered == T_DNSKEY || type_covered == T_PTR)) | |
1054 | + if (type1 == T_DS && type2 == T_DS) | |
1055 | { | |
1056 | - a.addr.dnssec.type = type_covered; | |
1057 | - a.addr.dnssec.class = class1; | |
1058 | + if (rdlen2 < 4) | |
1059 | + return STAT_BOGUS; /* bad packet */ | |
1060 | ||
1061 | - algo = *p2++; | |
1062 | - p2 += 13; /* labels, orig_ttl, expiration, inception */ | |
1063 | GETSHORT(keytag, p2); | |
1064 | + algo = *p2++; | |
1065 | + digest = *p2++; | |
1066 | + | |
1067 | + /* Cache needs to known class for DNSSEC stuff */ | |
1068 | + a.addr.dnssec.class = class2; | |
1069 | ||
1070 | - /* We don't cache sigs for wildcard answers, because to reproduce the | |
1071 | - answer from the cache will require one or more NSEC/NSEC3 records | |
1072 | - which we don't cache. The lack of the RRSIG ensures that a query for | |
1073 | - this RRset asking for a secure answer will always be forwarded. */ | |
1074 | - if (!have_wildcard && (key = blockdata_alloc((char*)psave, rdlen2))) | |
1075 | + if ((key = blockdata_alloc((char*)p2, rdlen2 - 4))) | |
1076 | { | |
1077 | - if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DS))) | |
1078 | + if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DS | F_DNSSECOK))) | |
1079 | blockdata_free(key); | |
1080 | else | |
1081 | { | |
1082 | - crecp->addr.sig.keydata = key; | |
1083 | - crecp->addr.sig.keylen = rdlen2; | |
1084 | - crecp->addr.sig.keytag = keytag; | |
1085 | - crecp->addr.sig.type_covered = type_covered; | |
1086 | - crecp->addr.sig.algo = algo; | |
1087 | + a.addr.keytag = keytag; | |
1088 | + log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u"); | |
1089 | + crecp->addr.ds.digest = digest; | |
1090 | + crecp->addr.ds.keydata = key; | |
1091 | + crecp->addr.ds.algo = algo; | |
1092 | + crecp->addr.ds.keytag = keytag; | |
1093 | + crecp->addr.ds.keylen = rdlen2 - 4; | |
1094 | + } | |
1095 | + } | |
1096 | + } | |
1097 | + else if (type2 == T_RRSIG) | |
1098 | + { | |
1099 | + if (rdlen2 < 18) | |
1100 | + return STAT_BOGUS; /* bad packet */ | |
1101 | + | |
1102 | + GETSHORT(type_covered, p2); | |
1103 | + | |
1104 | + if (type_covered == type1 && | |
1105 | + (type_covered == T_A || type_covered == T_AAAA || | |
1106 | + type_covered == T_CNAME || type_covered == T_DS || | |
1107 | + type_covered == T_DNSKEY || type_covered == T_PTR)) | |
1108 | + { | |
1109 | + a.addr.dnssec.type = type_covered; | |
1110 | + a.addr.dnssec.class = class1; | |
1111 | + | |
1112 | + algo = *p2++; | |
1113 | + p2 += 13; /* labels, orig_ttl, expiration, inception */ | |
1114 | + GETSHORT(keytag, p2); | |
1115 | + | |
1116 | + /* We don't cache sigs for wildcard answers, because to reproduce the | |
1117 | + answer from the cache will require one or more NSEC/NSEC3 records | |
1118 | + which we don't cache. The lack of the RRSIG ensures that a query for | |
1119 | + this RRset asking for a secure answer will always be forwarded. */ | |
1120 | + if (!have_wildcard && (key = blockdata_alloc((char*)psave, rdlen2))) | |
1121 | + { | |
1122 | + if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DS))) | |
1123 | + blockdata_free(key); | |
1124 | + else | |
1125 | + { | |
1126 | + crecp->addr.sig.keydata = key; | |
1127 | + crecp->addr.sig.keylen = rdlen2; | |
1128 | + crecp->addr.sig.keytag = keytag; | |
1129 | + crecp->addr.sig.type_covered = type_covered; | |
1130 | + crecp->addr.sig.algo = algo; | |
1131 | + } | |
1132 | } | |
1133 | } | |
1134 | } | |
1135 | + | |
1136 | + p2 = psave; | |
1137 | } | |
1138 | ||
1139 | - p2 = psave; | |
1140 | + if (!ADD_RDLEN(header, p2, plen, rdlen2)) | |
1141 | + return STAT_BOGUS; /* bad packet */ | |
1142 | } | |
1143 | ||
1144 | - if (!ADD_RDLEN(header, p2, plen, rdlen2)) | |
1145 | - return STAT_BOGUS; /* bad packet */ | |
1146 | + cache_end_insert(); | |
1147 | } | |
1148 | - | |
1149 | - cache_end_insert(); | |
1150 | } | |
1151 | } | |
1152 | ||
1153 | @@ -2083,143 +2223,49 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch | |
1154 | return STAT_BOGUS; | |
1155 | } | |
1156 | ||
1157 | - /* OK, all the RRsets validate, now see if we have a NODATA or NXDOMAIN reply */ | |
1158 | - if (have_answer) | |
1159 | - return STAT_SECURE; | |
1160 | - | |
1161 | - /* NXDOMAIN or NODATA reply, prove that (name, class1, type1) can't exist */ | |
1162 | - /* First marshall the NSEC records, if we've not done it previously */ | |
1163 | - if (!nsec_type && !(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, qclass))) | |
1164 | - { | |
1165 | - /* No NSEC records. If we dropped off the end of a CNAME chain, return | |
1166 | - STAT_NO_SIG and the last name is keyname. This is used for proving non-existence | |
1167 | - if DS records in CNAME chains. */ | |
1168 | - if (cname_count == CNAME_CHAIN) /* No CNAME chain, return empty name. */ | |
1169 | - *keyname = 0; | |
1170 | - else if (!extract_name(header, plen, &qname, keyname, 1, 0)) | |
1171 | - return STAT_BOGUS; | |
1172 | - return STAT_NO_SIG; /* No NSECs, this is probably a dangling CNAME pointing into | |
1173 | - an unsigned zone. Return STAT_NO_SIG to cause this to be proved. */ | |
1174 | - } | |
1175 | - | |
1176 | - /* Get name of missing answer */ | |
1177 | - if (!extract_name(header, plen, &qname, name, 1, 0)) | |
1178 | - return STAT_BOGUS; | |
1179 | - | |
1180 | - if (nsec_type == T_NSEC) | |
1181 | - return prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, nons); | |
1182 | - else | |
1183 | - return prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, NULL, nons); | |
1184 | -} | |
1185 | - | |
1186 | -/* Chase the CNAME chain in the packet until the first record which _doesn't validate. | |
1187 | - Needed for proving answer in unsigned space. | |
1188 | - Return STAT_NEED_* | |
1189 | - STAT_BOGUS - error | |
1190 | - STAT_INSECURE - name of first non-secure record in name | |
1191 | -*/ | |
1192 | -int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname) | |
1193 | -{ | |
1194 | - unsigned char *p = (unsigned char *)(header+1); | |
1195 | - int type, class, qclass, rdlen, j, rc; | |
1196 | - int cname_count = CNAME_CHAIN; | |
1197 | - char *wildname; | |
1198 | - | |
1199 | - /* Get question */ | |
1200 | - if (!extract_name(header, plen, &p, name, 1, 4)) | |
1201 | - return STAT_BOGUS; | |
1202 | - | |
1203 | - p +=2; /* type */ | |
1204 | - GETSHORT(qclass, p); | |
1205 | - | |
1206 | - while (1) | |
1207 | - { | |
1208 | - for (j = ntohs(header->ancount); j != 0; j--) | |
1209 | - { | |
1210 | - if (!(rc = extract_name(header, plen, &p, name, 0, 10))) | |
1211 | - return STAT_BOGUS; /* bad packet */ | |
1212 | - | |
1213 | - GETSHORT(type, p); | |
1214 | - GETSHORT(class, p); | |
1215 | - p += 4; /* TTL */ | |
1216 | - GETSHORT(rdlen, p); | |
1217 | - | |
1218 | - /* Not target, loop */ | |
1219 | - if (rc == 2 || qclass != class) | |
1220 | - { | |
1221 | - if (!ADD_RDLEN(header, p, plen, rdlen)) | |
1222 | - return STAT_BOGUS; | |
1223 | - continue; | |
1224 | - } | |
1225 | - | |
1226 | - /* Got to end of CNAME chain. */ | |
1227 | - if (type != T_CNAME) | |
1228 | - return STAT_INSECURE; | |
1229 | - | |
1230 | - /* validate CNAME chain, return if insecure or need more data */ | |
1231 | - rc = validate_rrset(now, header, plen, class, type, name, keyname, &wildname, NULL, 0, 0, 0); | |
1232 | - | |
1233 | - if (rc == STAT_SECURE_WILDCARD) | |
1234 | - { | |
1235 | - int nsec_type, nsec_count, i; | |
1236 | - unsigned char **nsecs; | |
1237 | - | |
1238 | - /* An attacker can replay a wildcard answer with a different | |
1239 | - answer and overlay a genuine RR. To prove this | |
1240 | - hasn't happened, the answer must prove that | |
1241 | - the genuine record doesn't exist. Check that here. */ | |
1242 | - if (!(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class))) | |
1243 | - return STAT_BOGUS; /* No NSECs or bad packet */ | |
1244 | - | |
1245 | - /* Note that we're called here because something didn't validate in validate_reply, | |
1246 | - so we can't assume that any NSEC records have been validated. We do them by steam here */ | |
1247 | - | |
1248 | - for (i = 0; i < nsec_count; i++) | |
1249 | - { | |
1250 | - unsigned char *p1 = nsecs[i]; | |
1251 | - | |
1252 | - if (!extract_name(header, plen, &p1, daemon->workspacename, 1, 0)) | |
1253 | - return STAT_BOGUS; | |
1254 | - | |
1255 | - rc = validate_rrset(now, header, plen, class, nsec_type, daemon->workspacename, keyname, NULL, NULL, 0, 0, 0); | |
1256 | + /* OK, all the RRsets validate, now see if we have a missing answer or CNAME target. */ | |
1257 | + for (j = 0; j <targetidx; j++) | |
1258 | + if ((p2 = targets[j])) | |
1259 | + { | |
1260 | + if (neganswer) | |
1261 | + *neganswer = 1; | |
1262 | ||
1263 | - /* NSECs can't be wildcards. */ | |
1264 | - if (rc == STAT_SECURE_WILDCARD) | |
1265 | - rc = STAT_BOGUS; | |
1266 | + if (!extract_name(header, plen, &p2, name, 1, 10)) | |
1267 | + return STAT_BOGUS; /* bad packet */ | |
1268 | + | |
1269 | + /* NXDOMAIN or NODATA reply, unanswered question is (name, qclass, qtype) */ | |
1270 | ||
1271 | - if (rc != STAT_SECURE) | |
1272 | + /* For anything other than a DS record, this situation is OK if either | |
1273 | + the answer is in an unsigned zone, or there's a NSEC records. */ | |
1274 | + if (!(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, qclass))) | |
1275 | + { | |
1276 | + /* Empty DS without NSECS */ | |
1277 | + if (qtype == T_DS) | |
1278 | + return STAT_BOGUS; | |
1279 | + else | |
1280 | + { | |
1281 | + rc = zone_status(name, qclass, keyname, now); | |
1282 | + if (rc != STAT_SECURE) | |
1283 | + { | |
1284 | + if (class) | |
1285 | + *class = qclass; /* Class for NEED_DS or NEED_DNSKEY */ | |
1286 | return rc; | |
1287 | - } | |
1288 | - | |
1289 | - if (nsec_type == T_NSEC) | |
1290 | - rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type, NULL); | |
1291 | - else | |
1292 | - rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, | |
1293 | - keyname, name, type, wildname, NULL); | |
1294 | - | |
1295 | - if (rc != STAT_SECURE) | |
1296 | - return rc; | |
1297 | - } | |
1298 | - | |
1299 | - if (rc != STAT_SECURE) | |
1300 | - { | |
1301 | - if (rc == STAT_NO_SIG) | |
1302 | - rc = STAT_INSECURE; | |
1303 | - return rc; | |
1304 | - } | |
1305 | + } | |
1306 | + | |
1307 | + return STAT_BOGUS; /* signed zone, no NSECs */ | |
1308 | + } | |
1309 | + } | |
1310 | ||
1311 | - /* Loop down CNAME chain/ */ | |
1312 | - if (!cname_count-- || | |
1313 | - !extract_name(header, plen, &p, name, 1, 0) || | |
1314 | - !(p = skip_questions(header, plen))) | |
1315 | - return STAT_BOGUS; | |
1316 | - | |
1317 | - break; | |
1318 | - } | |
1319 | + if (nsec_type == T_NSEC) | |
1320 | + rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, nons); | |
1321 | + else | |
1322 | + rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, NULL, nons); | |
1323 | ||
1324 | - /* End of CNAME chain */ | |
1325 | - return STAT_INSECURE; | |
1326 | - } | |
1327 | + if (rc != STAT_SECURE) | |
1328 | + return rc; | |
1329 | + } | |
1330 | + | |
1331 | + return STAT_SECURE; | |
1332 | } | |
1333 | ||
1334 | ||
1335 | diff --git a/src/forward.c b/src/forward.c | |
1336 | index b76a974..dd22a62 100644 | |
1337 | --- a/src/forward.c | |
1338 | +++ b/src/forward.c | |
1339 | @@ -23,15 +23,6 @@ static struct frec *lookup_frec_by_sender(unsigned short id, | |
1340 | static unsigned short get_id(void); | |
1341 | static void free_frec(struct frec *f); | |
1342 | ||
1343 | -#ifdef HAVE_DNSSEC | |
1344 | -static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n, | |
1345 | - int class, char *name, char *keyname, struct server *server, int *keycount); | |
1346 | -static int do_check_sign(struct frec *forward, int status, time_t now, char *name, char *keyname); | |
1347 | -static int send_check_sign(struct frec *forward, time_t now, struct dns_header *header, size_t plen, | |
1348 | - char *name, char *keyname); | |
1349 | -#endif | |
1350 | - | |
1351 | - | |
1352 | /* Send a UDP packet with its source address set as "source" | |
1353 | unless nowild is true, when we just send it with the kernel default */ | |
1354 | int send_from(int fd, int nowild, char *packet, size_t len, | |
1355 | @@ -825,236 +816,142 @@ void reply_query(int fd, int family, time_t now) | |
1356 | #ifdef HAVE_DNSSEC | |
1357 | if (server && option_bool(OPT_DNSSEC_VALID) && !(forward->flags & FREC_CHECKING_DISABLED)) | |
1358 | { | |
1359 | - int status; | |
1360 | + int status = 0; | |
1361 | ||
1362 | /* We've had a reply already, which we're validating. Ignore this duplicate */ | |
1363 | if (forward->blocking_query) | |
1364 | return; | |
1365 | - | |
1366 | - if (header->hb3 & HB3_TC) | |
1367 | - { | |
1368 | - /* Truncated answer can't be validated. | |
1369 | + | |
1370 | + /* Truncated answer can't be validated. | |
1371 | If this is an answer to a DNSSEC-generated query, we still | |
1372 | need to get the client to retry over TCP, so return | |
1373 | an answer with the TC bit set, even if the actual answer fits. | |
1374 | */ | |
1375 | - status = STAT_TRUNCATED; | |
1376 | - } | |
1377 | - else if (forward->flags & FREC_DNSKEY_QUERY) | |
1378 | - status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); | |
1379 | - else if (forward->flags & FREC_DS_QUERY) | |
1380 | - { | |
1381 | - status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); | |
1382 | - /* Provably no DS, everything below is insecure, even if signatures are offered */ | |
1383 | - if (status == STAT_NO_DS) | |
1384 | - /* We only cache sigs when we've validated a reply. | |
1385 | - Avoid caching a reply with sigs if there's a vaildated break in the | |
1386 | - DS chain, so we don't return replies from cache missing sigs. */ | |
1387 | - status = STAT_INSECURE_DS; | |
1388 | - else if (status == STAT_NO_SIG) | |
1389 | - { | |
1390 | - if (option_bool(OPT_DNSSEC_NO_SIGN)) | |
1391 | - { | |
1392 | - status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname); | |
1393 | - if (status == STAT_INSECURE) | |
1394 | - status = STAT_INSECURE_DS; | |
1395 | - } | |
1396 | - else | |
1397 | - status = STAT_INSECURE_DS; | |
1398 | - } | |
1399 | - else if (status == STAT_NO_NS) | |
1400 | - status = STAT_BOGUS; | |
1401 | - } | |
1402 | - else if (forward->flags & FREC_CHECK_NOSIGN) | |
1403 | - { | |
1404 | - status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); | |
1405 | - if (status != STAT_NEED_KEY) | |
1406 | - status = do_check_sign(forward, status, now, daemon->namebuff, daemon->keyname); | |
1407 | - } | |
1408 | - else | |
1409 | + if (header->hb3 & HB3_TC) | |
1410 | + status = STAT_TRUNCATED; | |
1411 | + | |
1412 | + while (1) | |
1413 | { | |
1414 | - status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL, NULL); | |
1415 | - if (status == STAT_NO_SIG) | |
1416 | + /* As soon as anything returns BOGUS, we stop and unwind, to do otherwise | |
1417 | + would invite infinite loops, since the answers to DNSKEY and DS queries | |
1418 | + will not be cached, so they'll be repeated. */ | |
1419 | + if (status != STAT_BOGUS && status != STAT_TRUNCATED && status != STAT_ABANDONED) | |
1420 | { | |
1421 | - if (option_bool(OPT_DNSSEC_NO_SIGN)) | |
1422 | - status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname); | |
1423 | + if (forward->flags & FREC_DNSKEY_QUERY) | |
1424 | + status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); | |
1425 | + else if (forward->flags & FREC_DS_QUERY) | |
1426 | + status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); | |
1427 | else | |
1428 | - status = STAT_INSECURE; | |
1429 | + status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, | |
1430 | + option_bool(OPT_DNSSEC_NO_SIGN), NULL, NULL); | |
1431 | } | |
1432 | - } | |
1433 | - /* Can't validate, as we're missing key data. Put this | |
1434 | - answer aside, whilst we get that. */ | |
1435 | - if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG || status == STAT_NEED_KEY) | |
1436 | - { | |
1437 | - struct frec *new, *orig; | |
1438 | - | |
1439 | - /* Free any saved query */ | |
1440 | - if (forward->stash) | |
1441 | - blockdata_free(forward->stash); | |
1442 | - | |
1443 | - /* Now save reply pending receipt of key data */ | |
1444 | - if (!(forward->stash = blockdata_alloc((char *)header, n))) | |
1445 | - return; | |
1446 | - forward->stash_len = n; | |
1447 | ||
1448 | - anotherkey: | |
1449 | - /* Find the original query that started it all.... */ | |
1450 | - for (orig = forward; orig->dependent; orig = orig->dependent); | |
1451 | - | |
1452 | - if (--orig->work_counter == 0 || !(new = get_new_frec(now, NULL, 1))) | |
1453 | - status = STAT_INSECURE; | |
1454 | - else | |
1455 | + /* Can't validate, as we're missing key data. Put this | |
1456 | + answer aside, whilst we get that. */ | |
1457 | + if (status == STAT_NEED_DS || status == STAT_NEED_KEY) | |
1458 | { | |
1459 | - int fd; | |
1460 | - struct frec *next = new->next; | |
1461 | - *new = *forward; /* copy everything, then overwrite */ | |
1462 | - new->next = next; | |
1463 | - new->blocking_query = NULL; | |
1464 | - new->sentto = server; | |
1465 | - new->rfd4 = NULL; | |
1466 | - new->orig_domain = NULL; | |
1467 | -#ifdef HAVE_IPV6 | |
1468 | - new->rfd6 = NULL; | |
1469 | -#endif | |
1470 | - new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_CHECK_NOSIGN); | |
1471 | + struct frec *new, *orig; | |
1472 | ||
1473 | - new->dependent = forward; /* to find query awaiting new one. */ | |
1474 | - forward->blocking_query = new; /* for garbage cleaning */ | |
1475 | - /* validate routines leave name of required record in daemon->keyname */ | |
1476 | - if (status == STAT_NEED_KEY) | |
1477 | - { | |
1478 | - new->flags |= FREC_DNSKEY_QUERY; | |
1479 | - nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz, | |
1480 | - daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz); | |
1481 | - } | |
1482 | - else | |
1483 | - { | |
1484 | - if (status == STAT_NEED_DS_NEG) | |
1485 | - new->flags |= FREC_CHECK_NOSIGN; | |
1486 | - else | |
1487 | - new->flags |= FREC_DS_QUERY; | |
1488 | - nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz, | |
1489 | - daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz); | |
1490 | - } | |
1491 | - if ((hash = hash_questions(header, nn, daemon->namebuff))) | |
1492 | - memcpy(new->hash, hash, HASH_SIZE); | |
1493 | - new->new_id = get_id(); | |
1494 | - header->id = htons(new->new_id); | |
1495 | - /* Save query for retransmission */ | |
1496 | - if (!(new->stash = blockdata_alloc((char *)header, nn))) | |
1497 | + /* Free any saved query */ | |
1498 | + if (forward->stash) | |
1499 | + blockdata_free(forward->stash); | |
1500 | + | |
1501 | + /* Now save reply pending receipt of key data */ | |
1502 | + if (!(forward->stash = blockdata_alloc((char *)header, n))) | |
1503 | return; | |
1504 | - | |
1505 | - new->stash_len = nn; | |
1506 | + forward->stash_len = n; | |
1507 | ||
1508 | - /* Don't resend this. */ | |
1509 | - daemon->srv_save = NULL; | |
1510 | + /* Find the original query that started it all.... */ | |
1511 | + for (orig = forward; orig->dependent; orig = orig->dependent); | |
1512 | ||
1513 | - if (server->sfd) | |
1514 | - fd = server->sfd->fd; | |
1515 | + if (--orig->work_counter == 0 || !(new = get_new_frec(now, NULL, 1))) | |
1516 | + status = STAT_ABANDONED; | |
1517 | else | |
1518 | { | |
1519 | - fd = -1; | |
1520 | + int fd; | |
1521 | + struct frec *next = new->next; | |
1522 | + *new = *forward; /* copy everything, then overwrite */ | |
1523 | + new->next = next; | |
1524 | + new->blocking_query = NULL; | |
1525 | + new->sentto = server; | |
1526 | + new->rfd4 = NULL; | |
1527 | #ifdef HAVE_IPV6 | |
1528 | - if (server->addr.sa.sa_family == AF_INET6) | |
1529 | + new->rfd6 = NULL; | |
1530 | +#endif | |
1531 | + new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY); | |
1532 | + | |
1533 | + new->dependent = forward; /* to find query awaiting new one. */ | |
1534 | + forward->blocking_query = new; /* for garbage cleaning */ | |
1535 | + /* validate routines leave name of required record in daemon->keyname */ | |
1536 | + if (status == STAT_NEED_KEY) | |
1537 | + { | |
1538 | + new->flags |= FREC_DNSKEY_QUERY; | |
1539 | + nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz, | |
1540 | + daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz); | |
1541 | + } | |
1542 | + else | |
1543 | { | |
1544 | - if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6))) | |
1545 | - fd = new->rfd6->fd; | |
1546 | + new->flags |= FREC_DS_QUERY; | |
1547 | + nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz, | |
1548 | + daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz); | |
1549 | } | |
1550 | + if ((hash = hash_questions(header, nn, daemon->namebuff))) | |
1551 | + memcpy(new->hash, hash, HASH_SIZE); | |
1552 | + new->new_id = get_id(); | |
1553 | + header->id = htons(new->new_id); | |
1554 | + /* Save query for retransmission */ | |
1555 | + new->stash = blockdata_alloc((char *)header, nn); | |
1556 | + new->stash_len = nn; | |
1557 | + | |
1558 | + /* Don't resend this. */ | |
1559 | + daemon->srv_save = NULL; | |
1560 | + | |
1561 | + if (server->sfd) | |
1562 | + fd = server->sfd->fd; | |
1563 | else | |
1564 | + { | |
1565 | + fd = -1; | |
1566 | +#ifdef HAVE_IPV6 | |
1567 | + if (server->addr.sa.sa_family == AF_INET6) | |
1568 | + { | |
1569 | + if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6))) | |
1570 | + fd = new->rfd6->fd; | |
1571 | + } | |
1572 | + else | |
1573 | #endif | |
1574 | + { | |
1575 | + if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET))) | |
1576 | + fd = new->rfd4->fd; | |
1577 | + } | |
1578 | + } | |
1579 | + | |
1580 | + if (fd != -1) | |
1581 | { | |
1582 | - if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET))) | |
1583 | - fd = new->rfd4->fd; | |
1584 | + while (retry_send(sendto(fd, (char *)header, nn, 0, | |
1585 | + &server->addr.sa, | |
1586 | + sa_len(&server->addr)))); | |
1587 | + server->queries++; | |
1588 | } | |
1589 | - } | |
1590 | - | |
1591 | - if (fd != -1) | |
1592 | - { | |
1593 | - while (retry_send(sendto(fd, (char *)header, nn, 0, | |
1594 | - &server->addr.sa, | |
1595 | - sa_len(&server->addr)))); | |
1596 | - server->queries++; | |
1597 | - } | |
1598 | - | |
1599 | + } | |
1600 | return; | |
1601 | } | |
1602 | - } | |
1603 | ||
1604 | - /* Ok, we reached far enough up the chain-of-trust that we can validate something. | |
1605 | - Now wind back down, pulling back answers which wouldn't previously validate | |
1606 | - and validate them with the new data. Note that if an answer needs multiple | |
1607 | - keys to validate, we may find another key is needed, in which case we set off | |
1608 | - down another branch of the tree. Once we get to the original answer | |
1609 | - (FREC_DNSSEC_QUERY not set) and it validates, return it to the original requestor. */ | |
1610 | - while (forward->dependent) | |
1611 | - { | |
1612 | + /* Validated original answer, all done. */ | |
1613 | + if (!forward->dependent) | |
1614 | + break; | |
1615 | + | |
1616 | + /* validated subsdiary query, (and cached result) | |
1617 | + pop that and return to the previous query we were working on. */ | |
1618 | struct frec *prev = forward->dependent; | |
1619 | free_frec(forward); | |
1620 | forward = prev; | |
1621 | forward->blocking_query = NULL; /* already gone */ | |
1622 | blockdata_retrieve(forward->stash, forward->stash_len, (void *)header); | |
1623 | n = forward->stash_len; | |
1624 | - | |
1625 | - if (status == STAT_SECURE) | |
1626 | - { | |
1627 | - if (forward->flags & FREC_DNSKEY_QUERY) | |
1628 | - status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); | |
1629 | - else if (forward->flags & FREC_DS_QUERY) | |
1630 | - { | |
1631 | - status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); | |
1632 | - /* Provably no DS, everything below is insecure, even if signatures are offered */ | |
1633 | - if (status == STAT_NO_DS) | |
1634 | - /* We only cache sigs when we've validated a reply. | |
1635 | - Avoid caching a reply with sigs if there's a vaildated break in the | |
1636 | - DS chain, so we don't return replies from cache missing sigs. */ | |
1637 | - status = STAT_INSECURE_DS; | |
1638 | - else if (status == STAT_NO_SIG) | |
1639 | - { | |
1640 | - if (option_bool(OPT_DNSSEC_NO_SIGN)) | |
1641 | - { | |
1642 | - status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname); | |
1643 | - if (status == STAT_INSECURE) | |
1644 | - status = STAT_INSECURE_DS; | |
1645 | - } | |
1646 | - else | |
1647 | - status = STAT_INSECURE_DS; | |
1648 | - } | |
1649 | - else if (status == STAT_NO_NS) | |
1650 | - status = STAT_BOGUS; | |
1651 | - } | |
1652 | - else if (forward->flags & FREC_CHECK_NOSIGN) | |
1653 | - { | |
1654 | - status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); | |
1655 | - if (status != STAT_NEED_KEY) | |
1656 | - status = do_check_sign(forward, status, now, daemon->namebuff, daemon->keyname); | |
1657 | - } | |
1658 | - else | |
1659 | - { | |
1660 | - status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL, NULL); | |
1661 | - if (status == STAT_NO_SIG) | |
1662 | - { | |
1663 | - if (option_bool(OPT_DNSSEC_NO_SIGN)) | |
1664 | - status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname); | |
1665 | - else | |
1666 | - status = STAT_INSECURE; | |
1667 | - } | |
1668 | - } | |
1669 | - | |
1670 | - if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG || status == STAT_NEED_KEY) | |
1671 | - goto anotherkey; | |
1672 | - } | |
1673 | } | |
1674 | + | |
1675 | ||
1676 | no_cache_dnssec = 0; | |
1677 | - | |
1678 | - if (status == STAT_INSECURE_DS) | |
1679 | - { | |
1680 | - /* We only cache sigs when we've validated a reply. | |
1681 | - Avoid caching a reply with sigs if there's a vaildated break in the | |
1682 | - DS chain, so we don't return replies from cache missing sigs. */ | |
1683 | - status = STAT_INSECURE; | |
1684 | - no_cache_dnssec = 1; | |
1685 | - } | |
1686 | ||
1687 | if (status == STAT_TRUNCATED) | |
1688 | header->hb3 |= HB3_TC; | |
1689 | @@ -1062,7 +959,7 @@ void reply_query(int fd, int family, time_t now) | |
1690 | { | |
1691 | char *result, *domain = "result"; | |
1692 | ||
1693 | - if (forward->work_counter == 0) | |
1694 | + if (status == STAT_ABANDONED) | |
1695 | { | |
1696 | result = "ABANDONED"; | |
1697 | status = STAT_BOGUS; | |
1698 | @@ -1072,7 +969,7 @@ void reply_query(int fd, int family, time_t now) | |
1699 | ||
1700 | if (status == STAT_BOGUS && extract_request(header, n, daemon->namebuff, NULL)) | |
1701 | domain = daemon->namebuff; | |
1702 | - | |
1703 | + | |
1704 | log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result); | |
1705 | } | |
1706 | ||
1707 | @@ -1415,315 +1312,49 @@ void receive_query(struct listener *listen, time_t now) | |
1708 | } | |
1709 | ||
1710 | #ifdef HAVE_DNSSEC | |
1711 | - | |
1712 | -/* UDP: we've got an unsigned answer, return STAT_INSECURE if we can prove there's no DS | |
1713 | - and therefore the answer shouldn't be signed, or STAT_BOGUS if it should be, or | |
1714 | - STAT_NEED_DS_NEG and keyname if we need to do the query. */ | |
1715 | -static int send_check_sign(struct frec *forward, time_t now, struct dns_header *header, size_t plen, | |
1716 | - char *name, char *keyname) | |
1717 | -{ | |
1718 | - int status = dnssec_chase_cname(now, header, plen, name, keyname); | |
1719 | - | |
1720 | - if (status != STAT_INSECURE) | |
1721 | - return status; | |
1722 | - | |
1723 | - /* Store the domain we're trying to check. */ | |
1724 | - forward->name_start = strlen(name); | |
1725 | - forward->name_len = forward->name_start + 1; | |
1726 | - if (!(forward->orig_domain = blockdata_alloc(name, forward->name_len))) | |
1727 | - return STAT_BOGUS; | |
1728 | - | |
1729 | - return do_check_sign(forward, 0, now, name, keyname); | |
1730 | -} | |
1731 | - | |
1732 | -/* We either have a a reply (header non-NULL, or we need to start by looking in the cache */ | |
1733 | -static int do_check_sign(struct frec *forward, int status, time_t now, char *name, char *keyname) | |
1734 | -{ | |
1735 | - /* get domain we're checking back from blockdata store, it's stored on the original query. */ | |
1736 | - while (forward->dependent && !forward->orig_domain) | |
1737 | - forward = forward->dependent; | |
1738 | - | |
1739 | - blockdata_retrieve(forward->orig_domain, forward->name_len, name); | |
1740 | - | |
1741 | - while (1) | |
1742 | - { | |
1743 | - char *p; | |
1744 | - | |
1745 | - if (status == 0) | |
1746 | - { | |
1747 | - struct crec *crecp; | |
1748 | - | |
1749 | - /* Haven't received answer, see if in cache */ | |
1750 | - if (!(crecp = cache_find_by_name(NULL, &name[forward->name_start], now, F_DS))) | |
1751 | - { | |
1752 | - /* put name of DS record we're missing into keyname */ | |
1753 | - strcpy(keyname, &name[forward->name_start]); | |
1754 | - /* and wait for reply to arrive */ | |
1755 | - return STAT_NEED_DS_NEG; | |
1756 | - } | |
1757 | - | |
1758 | - /* F_DNSSECOK misused in DS cache records to non-existance of NS record */ | |
1759 | - if (!(crecp->flags & F_NEG)) | |
1760 | - status = STAT_SECURE; | |
1761 | - else if (crecp->flags & F_DNSSECOK) | |
1762 | - status = STAT_NO_DS; | |
1763 | - else | |
1764 | - status = STAT_NO_NS; | |
1765 | - } | |
1766 | - | |
1767 | - /* Have entered non-signed part of DNS tree. */ | |
1768 | - if (status == STAT_NO_DS) | |
1769 | - return forward->dependent ? STAT_INSECURE_DS : STAT_INSECURE; | |
1770 | - | |
1771 | - if (status == STAT_BOGUS) | |
1772 | - return STAT_BOGUS; | |
1773 | - | |
1774 | - if (status == STAT_NO_SIG && *keyname != 0) | |
1775 | - { | |
1776 | - /* There is a validated CNAME chain that doesn't end in a DS record. Start | |
1777 | - the search again in that domain. */ | |
1778 | - blockdata_free(forward->orig_domain); | |
1779 | - forward->name_start = strlen(keyname); | |
1780 | - forward->name_len = forward->name_start + 1; | |
1781 | - if (!(forward->orig_domain = blockdata_alloc(keyname, forward->name_len))) | |
1782 | - return STAT_BOGUS; | |
1783 | - | |
1784 | - strcpy(name, keyname); | |
1785 | - status = 0; /* force to cache when we iterate. */ | |
1786 | - continue; | |
1787 | - } | |
1788 | - | |
1789 | - /* There's a proven DS record, or we're within a zone, where there doesn't need | |
1790 | - to be a DS record. Add a name and try again. | |
1791 | - If we've already tried the whole name, then fail */ | |
1792 | - | |
1793 | - if (forward->name_start == 0) | |
1794 | - return STAT_BOGUS; | |
1795 | - | |
1796 | - for (p = &name[forward->name_start-2]; (*p != '.') && (p != name); p--); | |
1797 | - | |
1798 | - if (p != name) | |
1799 | - p++; | |
1800 | - | |
1801 | - forward->name_start = p - name; | |
1802 | - status = 0; /* force to cache when we iterate. */ | |
1803 | - } | |
1804 | -} | |
1805 | - | |
1806 | -/* Move down from the root, until we find a signed non-existance of a DS, in which case | |
1807 | - an unsigned answer is OK, or we find a signed DS, in which case there should be | |
1808 | - a signature, and the answer is BOGUS */ | |
1809 | -static int tcp_check_for_unsigned_zone(time_t now, struct dns_header *header, size_t plen, int class, char *name, | |
1810 | - char *keyname, struct server *server, int *keycount) | |
1811 | -{ | |
1812 | - size_t m; | |
1813 | - unsigned char *packet, *payload; | |
1814 | - u16 *length; | |
1815 | - int status, name_len; | |
1816 | - struct blockdata *block; | |
1817 | - | |
1818 | - char *name_start; | |
1819 | - | |
1820 | - /* Get first insecure entry in CNAME chain */ | |
1821 | - status = tcp_key_recurse(now, STAT_CHASE_CNAME, header, plen, class, name, keyname, server, keycount); | |
1822 | - if (status == STAT_BOGUS) | |
1823 | - return STAT_BOGUS; | |
1824 | - | |
1825 | - if (!(packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16)))) | |
1826 | - return STAT_BOGUS; | |
1827 | - | |
1828 | - payload = &packet[2]; | |
1829 | - header = (struct dns_header *)payload; | |
1830 | - length = (u16 *)packet; | |
1831 | - | |
1832 | - /* Stash the name away, since the buffer will be trashed when we recurse */ | |
1833 | - name_len = strlen(name) + 1; | |
1834 | - name_start = name + name_len - 1; | |
1835 | - | |
1836 | - if (!(block = blockdata_alloc(name, name_len))) | |
1837 | - { | |
1838 | - free(packet); | |
1839 | - return STAT_BOGUS; | |
1840 | - } | |
1841 | - | |
1842 | - while (1) | |
1843 | - { | |
1844 | - unsigned char c1, c2; | |
1845 | - struct crec *crecp; | |
1846 | - | |
1847 | - if (--(*keycount) == 0) | |
1848 | - { | |
1849 | - free(packet); | |
1850 | - blockdata_free(block); | |
1851 | - return STAT_BOGUS; | |
1852 | - } | |
1853 | - | |
1854 | - while ((crecp = cache_find_by_name(NULL, name_start, now, F_DS))) | |
1855 | - { | |
1856 | - if ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)) | |
1857 | - { | |
1858 | - /* Found a secure denial of DS - delegation is indeed insecure */ | |
1859 | - free(packet); | |
1860 | - blockdata_free(block); | |
1861 | - return STAT_INSECURE; | |
1862 | - } | |
1863 | - | |
1864 | - /* Here, either there's a secure DS, or no NS and no DS, and therefore no delegation. | |
1865 | - Add another label and continue. */ | |
1866 | - | |
1867 | - if (name_start == name) | |
1868 | - { | |
1869 | - free(packet); | |
1870 | - blockdata_free(block); | |
1871 | - return STAT_BOGUS; /* run out of labels */ | |
1872 | - } | |
1873 | - | |
1874 | - name_start -= 2; | |
1875 | - while (*name_start != '.' && name_start != name) | |
1876 | - name_start--; | |
1877 | - if (name_start != name) | |
1878 | - name_start++; | |
1879 | - } | |
1880 | - | |
1881 | - /* Can't find it in the cache, have to send a query */ | |
1882 | - | |
1883 | - m = dnssec_generate_query(header, ((char *) header) + 65536, name_start, class, T_DS, &server->addr, server->edns_pktsz); | |
1884 | - | |
1885 | - *length = htons(m); | |
1886 | - | |
1887 | - if (read_write(server->tcpfd, packet, m + sizeof(u16), 0) && | |
1888 | - read_write(server->tcpfd, &c1, 1, 1) && | |
1889 | - read_write(server->tcpfd, &c2, 1, 1) && | |
1890 | - read_write(server->tcpfd, payload, (c1 << 8) | c2, 1)) | |
1891 | - { | |
1892 | - m = (c1 << 8) | c2; | |
1893 | - | |
1894 | - /* Note this trashes all three name workspaces */ | |
1895 | - status = tcp_key_recurse(now, STAT_NEED_DS_NEG, header, m, class, name, keyname, server, keycount); | |
1896 | - | |
1897 | - if (status == STAT_NO_DS) | |
1898 | - { | |
1899 | - /* Found a secure denial of DS - delegation is indeed insecure */ | |
1900 | - free(packet); | |
1901 | - blockdata_free(block); | |
1902 | - return STAT_INSECURE; | |
1903 | - } | |
1904 | - | |
1905 | - if (status == STAT_NO_SIG && *keyname != 0) | |
1906 | - { | |
1907 | - /* There is a validated CNAME chain that doesn't end in a DS record. Start | |
1908 | - the search again in that domain. */ | |
1909 | - blockdata_free(block); | |
1910 | - name_len = strlen(keyname) + 1; | |
1911 | - name_start = name + name_len - 1; | |
1912 | - | |
1913 | - if (!(block = blockdata_alloc(keyname, name_len))) | |
1914 | - return STAT_BOGUS; | |
1915 | - | |
1916 | - strcpy(name, keyname); | |
1917 | - continue; | |
1918 | - } | |
1919 | - | |
1920 | - if (status == STAT_BOGUS) | |
1921 | - { | |
1922 | - free(packet); | |
1923 | - blockdata_free(block); | |
1924 | - return STAT_BOGUS; | |
1925 | - } | |
1926 | - | |
1927 | - /* Here, either there's a secure DS, or no NS and no DS, and therefore no delegation. | |
1928 | - Add another label and continue. */ | |
1929 | - | |
1930 | - /* Get name we're checking back. */ | |
1931 | - blockdata_retrieve(block, name_len, name); | |
1932 | - | |
1933 | - if (name_start == name) | |
1934 | - { | |
1935 | - free(packet); | |
1936 | - blockdata_free(block); | |
1937 | - return STAT_BOGUS; /* run out of labels */ | |
1938 | - } | |
1939 | - | |
1940 | - name_start -= 2; | |
1941 | - while (*name_start != '.' && name_start != name) | |
1942 | - name_start--; | |
1943 | - if (name_start != name) | |
1944 | - name_start++; | |
1945 | - } | |
1946 | - else | |
1947 | - { | |
1948 | - /* IO failure */ | |
1949 | - free(packet); | |
1950 | - blockdata_free(block); | |
1951 | - return STAT_BOGUS; /* run out of labels */ | |
1952 | - } | |
1953 | - } | |
1954 | -} | |
1955 | - | |
1956 | static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n, | |
1957 | int class, char *name, char *keyname, struct server *server, int *keycount) | |
1958 | { | |
1959 | /* Recurse up the key heirarchy */ | |
1960 | int new_status; | |
1961 | + unsigned char *packet = NULL; | |
1962 | + size_t m; | |
1963 | + unsigned char *payload = NULL; | |
1964 | + struct dns_header *new_header = NULL; | |
1965 | + u16 *length = NULL; | |
1966 | + unsigned char c1, c2; | |
1967 | ||
1968 | - /* limit the amount of work we do, to avoid cycling forever on loops in the DNS */ | |
1969 | - if (--(*keycount) == 0) | |
1970 | - return STAT_INSECURE; | |
1971 | - | |
1972 | - if (status == STAT_NEED_KEY) | |
1973 | - new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class); | |
1974 | - else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG) | |
1975 | + while (1) | |
1976 | { | |
1977 | - new_status = dnssec_validate_ds(now, header, n, name, keyname, class); | |
1978 | - if (status == STAT_NEED_DS) | |
1979 | + /* limit the amount of work we do, to avoid cycling forever on loops in the DNS */ | |
1980 | + if (--(*keycount) == 0) | |
1981 | + new_status = STAT_ABANDONED; | |
1982 | + else if (status == STAT_NEED_KEY) | |
1983 | + new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class); | |
1984 | + else if (status == STAT_NEED_DS) | |
1985 | + new_status = dnssec_validate_ds(now, header, n, name, keyname, class); | |
1986 | + else | |
1987 | + new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, option_bool(OPT_DNSSEC_NO_SIGN), NULL, NULL); | |
1988 | + | |
1989 | + if (new_status != STAT_NEED_DS && new_status != STAT_NEED_KEY) | |
1990 | + break; | |
1991 | + | |
1992 | + /* Can't validate because we need a key/DS whose name now in keyname. | |
1993 | + Make query for same, and recurse to validate */ | |
1994 | + if (!packet) | |
1995 | { | |
1996 | - if (new_status == STAT_NO_DS) | |
1997 | - new_status = STAT_INSECURE_DS; | |
1998 | - if (new_status == STAT_NO_SIG) | |
1999 | - { | |
2000 | - if (option_bool(OPT_DNSSEC_NO_SIGN)) | |
2001 | - { | |
2002 | - new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount); | |
2003 | - if (new_status == STAT_INSECURE) | |
2004 | - new_status = STAT_INSECURE_DS; | |
2005 | - } | |
2006 | - else | |
2007 | - new_status = STAT_INSECURE_DS; | |
2008 | - } | |
2009 | - else if (new_status == STAT_NO_NS) | |
2010 | - new_status = STAT_BOGUS; | |
2011 | + packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16)); | |
2012 | + payload = &packet[2]; | |
2013 | + new_header = (struct dns_header *)payload; | |
2014 | + length = (u16 *)packet; | |
2015 | } | |
2016 | - } | |
2017 | - else if (status == STAT_CHASE_CNAME) | |
2018 | - new_status = dnssec_chase_cname(now, header, n, name, keyname); | |
2019 | - else | |
2020 | - { | |
2021 | - new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL, NULL); | |
2022 | ||
2023 | - if (new_status == STAT_NO_SIG) | |
2024 | + if (!packet) | |
2025 | { | |
2026 | - if (option_bool(OPT_DNSSEC_NO_SIGN)) | |
2027 | - new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount); | |
2028 | - else | |
2029 | - new_status = STAT_INSECURE; | |
2030 | + new_status = STAT_ABANDONED; | |
2031 | + break; | |
2032 | } | |
2033 | - } | |
2034 | - | |
2035 | - /* Can't validate because we need a key/DS whose name now in keyname. | |
2036 | - Make query for same, and recurse to validate */ | |
2037 | - if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY) | |
2038 | - { | |
2039 | - size_t m; | |
2040 | - unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16)); | |
2041 | - unsigned char *payload = &packet[2]; | |
2042 | - struct dns_header *new_header = (struct dns_header *)payload; | |
2043 | - u16 *length = (u16 *)packet; | |
2044 | - unsigned char c1, c2; | |
2045 | - | |
2046 | - if (!packet) | |
2047 | - return STAT_INSECURE; | |
2048 | - | |
2049 | - another_tcp_key: | |
2050 | + | |
2051 | m = dnssec_generate_query(new_header, ((char *) new_header) + 65536, keyname, class, | |
2052 | new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr, server->edns_pktsz); | |
2053 | ||
2054 | @@ -1733,65 +1364,22 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si | |
2055 | !read_write(server->tcpfd, &c1, 1, 1) || | |
2056 | !read_write(server->tcpfd, &c2, 1, 1) || | |
2057 | !read_write(server->tcpfd, payload, (c1 << 8) | c2, 1)) | |
2058 | - new_status = STAT_INSECURE; | |
2059 | - else | |
2060 | { | |
2061 | - m = (c1 << 8) | c2; | |
2062 | - | |
2063 | - new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, keycount); | |
2064 | - | |
2065 | - if (new_status == STAT_SECURE) | |
2066 | - { | |
2067 | - /* Reached a validated record, now try again at this level. | |
2068 | - Note that we may get ANOTHER NEED_* if an answer needs more than one key. | |
2069 | - If so, go round again. */ | |
2070 | - | |
2071 | - if (status == STAT_NEED_KEY) | |
2072 | - new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class); | |
2073 | - else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG) | |
2074 | - { | |
2075 | - new_status = dnssec_validate_ds(now, header, n, name, keyname, class); | |
2076 | - if (status == STAT_NEED_DS) | |
2077 | - { | |
2078 | - if (new_status == STAT_NO_DS) | |
2079 | - new_status = STAT_INSECURE_DS; | |
2080 | - else if (new_status == STAT_NO_SIG) | |
2081 | - { | |
2082 | - if (option_bool(OPT_DNSSEC_NO_SIGN)) | |
2083 | - { | |
2084 | - new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount); | |
2085 | - if (new_status == STAT_INSECURE) | |
2086 | - new_status = STAT_INSECURE_DS; | |
2087 | - } | |
2088 | - else | |
2089 | - new_status = STAT_INSECURE_DS; | |
2090 | - } | |
2091 | - else if (new_status == STAT_NO_NS) | |
2092 | - new_status = STAT_BOGUS; | |
2093 | - } | |
2094 | - } | |
2095 | - else if (status == STAT_CHASE_CNAME) | |
2096 | - new_status = dnssec_chase_cname(now, header, n, name, keyname); | |
2097 | - else | |
2098 | - { | |
2099 | - new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL, NULL); | |
2100 | - | |
2101 | - if (new_status == STAT_NO_SIG) | |
2102 | - { | |
2103 | - if (option_bool(OPT_DNSSEC_NO_SIGN)) | |
2104 | - new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount); | |
2105 | - else | |
2106 | - new_status = STAT_INSECURE; | |
2107 | - } | |
2108 | - } | |
2109 | - | |
2110 | - if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY) | |
2111 | - goto another_tcp_key; | |
2112 | - } | |
2113 | + new_status = STAT_ABANDONED; | |
2114 | + break; | |
2115 | } | |
2116 | + | |
2117 | + m = (c1 << 8) | c2; | |
2118 | ||
2119 | - free(packet); | |
2120 | + new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, keycount); | |
2121 | + | |
2122 | + if (new_status != STAT_OK) | |
2123 | + break; | |
2124 | } | |
2125 | + | |
2126 | + if (packet) | |
2127 | + free(packet); | |
2128 | + | |
2129 | return new_status; | |
2130 | } | |
2131 | #endif | |
2132 | @@ -2075,19 +1663,10 @@ unsigned char *tcp_request(int confd, time_t now, | |
2133 | if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled) | |
2134 | { | |
2135 | int keycount = DNSSEC_WORK; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */ | |
2136 | - int status = tcp_key_recurse(now, STAT_TRUNCATED, header, m, 0, daemon->namebuff, daemon->keyname, last_server, &keycount); | |
2137 | + int status = tcp_key_recurse(now, STAT_OK, header, m, 0, daemon->namebuff, daemon->keyname, last_server, &keycount); | |
2138 | char *result, *domain = "result"; | |
2139 | - | |
2140 | - if (status == STAT_INSECURE_DS) | |
2141 | - { | |
2142 | - /* We only cache sigs when we've validated a reply. | |
2143 | - Avoid caching a reply with sigs if there's a vaildated break in the | |
2144 | - DS chain, so we don't return replies from cache missing sigs. */ | |
2145 | - status = STAT_INSECURE; | |
2146 | - no_cache_dnssec = 1; | |
2147 | - } | |
2148 | ||
2149 | - if (keycount == 0) | |
2150 | + if (status == STAT_ABANDONED) | |
2151 | { | |
2152 | result = "ABANDONED"; | |
2153 | status = STAT_BOGUS; | |
2154 | @@ -2179,7 +1758,6 @@ static struct frec *allocate_frec(time_t now) | |
2155 | f->dependent = NULL; | |
2156 | f->blocking_query = NULL; | |
2157 | f->stash = NULL; | |
2158 | - f->orig_domain = NULL; | |
2159 | #endif | |
2160 | daemon->frec_list = f; | |
2161 | } | |
2162 | @@ -2248,12 +1826,6 @@ static void free_frec(struct frec *f) | |
2163 | f->stash = NULL; | |
2164 | } | |
2165 | ||
2166 | - if (f->orig_domain) | |
2167 | - { | |
2168 | - blockdata_free(f->orig_domain); | |
2169 | - f->orig_domain = NULL; | |
2170 | - } | |
2171 | - | |
2172 | /* Anything we're waiting on is pointless now, too */ | |
2173 | if (f->blocking_query) | |
2174 | free_frec(f->blocking_query); | |
2175 | @@ -2281,14 +1853,23 @@ struct frec *get_new_frec(time_t now, int *wait, int force) | |
2176 | target = f; | |
2177 | else | |
2178 | { | |
2179 | - if (difftime(now, f->time) >= 4*TIMEOUT) | |
2180 | - { | |
2181 | - free_frec(f); | |
2182 | - target = f; | |
2183 | - } | |
2184 | - | |
2185 | - if (!oldest || difftime(f->time, oldest->time) <= 0) | |
2186 | - oldest = f; | |
2187 | +#ifdef HAVE_DNSSEC | |
2188 | + /* Don't free DNSSEC sub-queries here, as we may end up with | |
2189 | + dangling references to them. They'll go when their "real" query | |
2190 | + is freed. */ | |
2191 | + if (!f->dependent) | |
2192 | +#endif | |
2193 | + { | |
2194 | + if (difftime(now, f->time) >= 4*TIMEOUT) | |
2195 | + { | |
2196 | + free_frec(f); | |
2197 | + target = f; | |
2198 | + } | |
2199 | + | |
2200 | + | |
2201 | + if (!oldest || difftime(f->time, oldest->time) <= 0) | |
2202 | + oldest = f; | |
2203 | + } | |
2204 | } | |
2205 | ||
2206 | if (target) | |
2207 | -- | |
2208 | 1.7.10.4 | |
2209 |