blob: ed2f53ff176304886013ffc1023194893de7c1ce [file] [log] [blame]
Giovanni Bajo8d41ebd2012-05-05 00:48:12 +02001/* dnssec.c is Copyright (c) 2012 Giovanni Bajo <rasky@develer.com>
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07002 and Copyright (c) 2012-2023 Simon Kelley
Giovanni Bajo8d41ebd2012-05-05 00:48:12 +02003
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 dated June, 1991, or
7 (at your option) version 3 dated 29 June, 2007.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
Giovanni Bajoe292e932012-04-22 14:32:02 +020017
18#include "dnsmasq.h"
Simon Kelley0fc2f312014-01-08 10:26:58 +000019
20#ifdef HAVE_DNSSEC
21
Giovanni Bajoe292e932012-04-22 14:32:02 +020022#define SERIAL_UNDEF -100
23#define SERIAL_EQ 0
24#define SERIAL_LT -1
25#define SERIAL_GT 1
26
Simon Kelley5ada8882014-01-09 22:25:03 +000027/* Input in presentation format */
28static int count_labels(char *name)
29{
30 int i;
Simon Kelley4fe67442018-01-19 12:26:08 +000031 char *p;
32
Simon Kelley5ada8882014-01-09 22:25:03 +000033 if (*name == 0)
34 return 0;
35
Simon Kelley4fe67442018-01-19 12:26:08 +000036 for (p = name, i = 0; *p; p++)
37 if (*p == '.')
Simon Kelley5ada8882014-01-09 22:25:03 +000038 i++;
39
Simon Kelley4fe67442018-01-19 12:26:08 +000040 /* Don't count empty first label. */
41 return *name == '.' ? i : i+1;
Simon Kelley5ada8882014-01-09 22:25:03 +000042}
43
Simon Kelley5f8e58f2014-01-09 17:31:19 +000044/* Implement RFC1982 wrapped compare for 32-bit numbers */
Simon Kelleycc7cb0b2016-01-04 16:04:51 +000045static int serial_compare_32(u32 s1, u32 s2)
Giovanni Bajo0852d762012-04-28 03:49:24 +020046{
Simon Kelley5f8e58f2014-01-09 17:31:19 +000047 if (s1 == s2)
48 return SERIAL_EQ;
Giovanni Bajo0852d762012-04-28 03:49:24 +020049
Simon Kelley5f8e58f2014-01-09 17:31:19 +000050 if ((s1 < s2 && (s2 - s1) < (1UL<<31)) ||
51 (s1 > s2 && (s1 - s2) > (1UL<<31)))
52 return SERIAL_LT;
53 if ((s1 < s2 && (s2 - s1) > (1UL<<31)) ||
54 (s1 > s2 && (s1 - s2) < (1UL<<31)))
55 return SERIAL_GT;
56 return SERIAL_UNDEF;
57}
Giovanni Bajo0852d762012-04-28 03:49:24 +020058
Simon Kelleyf6e62e22015-03-01 18:17:54 +000059/* Called at startup. If the timestamp file is configured and exists, put its mtime on
60 timestamp_time. If it doesn't exist, create it, and set the mtime to 1-1-2015.
Simon Kelley360f2512015-03-07 18:28:06 +000061 return -1 -> Cannot create file.
62 0 -> not using timestamp, or timestamp exists and is in past.
63 1 -> timestamp exists and is in future.
Simon Kelleyf6e62e22015-03-01 18:17:54 +000064*/
Simon Kelley360f2512015-03-07 18:28:06 +000065
Simon Kelleyf6e62e22015-03-01 18:17:54 +000066static time_t timestamp_time;
Simon Kelleyf6e62e22015-03-01 18:17:54 +000067
Simon Kelley360f2512015-03-07 18:28:06 +000068int setup_timestamp(void)
Simon Kelleyf6e62e22015-03-01 18:17:54 +000069{
70 struct stat statbuf;
71
Kevin Darbyshire-Bryant34b5d192015-07-27 19:34:23 +010072 daemon->back_to_the_future = 0;
Simon Kelleyf6e62e22015-03-01 18:17:54 +000073
Simon Kelley360f2512015-03-07 18:28:06 +000074 if (!daemon->timestamp_file)
Simon Kelleyf6e62e22015-03-01 18:17:54 +000075 return 0;
76
77 if (stat(daemon->timestamp_file, &statbuf) != -1)
78 {
79 timestamp_time = statbuf.st_mtime;
80 check_and_exit:
81 if (difftime(timestamp_time, time(0)) <= 0)
82 {
83 /* time already OK, update timestamp, and do key checking from the start. */
Vladislav Grishenko4583dd92017-05-03 23:16:51 +010084 if (utimes(daemon->timestamp_file, NULL) == -1)
Simon Kelleyf6e62e22015-03-01 18:17:54 +000085 my_syslog(LOG_ERR, _("failed to update mtime on %s: %s"), daemon->timestamp_file, strerror(errno));
Kevin Darbyshire-Bryant34b5d192015-07-27 19:34:23 +010086 daemon->back_to_the_future = 1;
Simon Kelleyf6e62e22015-03-01 18:17:54 +000087 return 0;
88 }
89 return 1;
90 }
91
92 if (errno == ENOENT)
93 {
Simon Kelley360f2512015-03-07 18:28:06 +000094 /* NB. for explanation of O_EXCL flag, see comment on pidfile in dnsmasq.c */
95 int fd = open(daemon->timestamp_file, O_WRONLY | O_CREAT | O_NONBLOCK | O_EXCL, 0666);
Simon Kelleyf6e62e22015-03-01 18:17:54 +000096 if (fd != -1)
97 {
Vladislav Grishenko4583dd92017-05-03 23:16:51 +010098 struct timeval tv[2];
Simon Kelleyf6e62e22015-03-01 18:17:54 +000099
100 close(fd);
101
Vladislav Grishenko4583dd92017-05-03 23:16:51 +0100102 timestamp_time = 1420070400; /* 1-1-2015 */
103 tv[0].tv_sec = tv[1].tv_sec = timestamp_time;
104 tv[0].tv_usec = tv[1].tv_usec = 0;
105 if (utimes(daemon->timestamp_file, tv) == 0)
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000106 goto check_and_exit;
107 }
108 }
109
Simon Kelley360f2512015-03-07 18:28:06 +0000110 return -1;
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000111}
112
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000113/* Check whether today/now is between date_start and date_end */
Simon Kelleyae7a3b92019-09-03 14:40:47 +0100114static int is_check_date(unsigned long curtime)
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000115{
Simon Kelleye98bd522014-03-28 20:41:23 +0000116 /* Checking timestamps may be temporarily disabled */
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000117
118 /* If the current time if _before_ the timestamp
119 on our persistent timestamp file, then assume the
120 time if not yet correct, and don't check the
121 key timestamps. As soon as the current time is
122 later then the timestamp, update the timestamp
123 and start checking keys */
124 if (daemon->timestamp_file)
125 {
Kevin Darbyshire-Bryant34b5d192015-07-27 19:34:23 +0100126 if (daemon->back_to_the_future == 0 && difftime(timestamp_time, curtime) <= 0)
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000127 {
Vladislav Grishenko4583dd92017-05-03 23:16:51 +0100128 if (utimes(daemon->timestamp_file, NULL) != 0)
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000129 my_syslog(LOG_ERR, _("failed to update mtime on %s: %s"), daemon->timestamp_file, strerror(errno));
130
Kevin Darbyshire-Bryant06093a92016-07-11 21:03:27 +0100131 my_syslog(LOG_INFO, _("system time considered valid, now checking DNSSEC signature timestamps."));
Kevin Darbyshire-Bryant34b5d192015-07-27 19:34:23 +0100132 daemon->back_to_the_future = 1;
Kevin Darbyshire-Bryant06093a92016-07-11 21:03:27 +0100133 daemon->dnssec_no_time_check = 0;
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000134 queue_event(EVENT_RELOAD); /* purge cache */
135 }
136
Simon Kelleyae7a3b92019-09-03 14:40:47 +0100137 return daemon->back_to_the_future;
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000138 }
Simon Kelleyae7a3b92019-09-03 14:40:47 +0100139 else
140 return !daemon->dnssec_no_time_check;
141}
142
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000143/* Return bytes of canonicalised rrdata one by one.
144 Init state->ip with the RR, and state->end with the end of same.
145 Init state->op to NULL.
146 Init state->desc to RR descriptor.
147 Init state->buff with a MAXDNAME * 2 buffer.
148
149 After each call which returns 1, state->op points to the next byte of data.
150 On returning 0, the end has been reached.
151*/
152struct rdata_state {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700153 short *desc;
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000154 size_t c;
155 unsigned char *end, *ip, *op;
156 char *buff;
157};
158
159static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state *state)
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000160{
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000161 int d;
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000162
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000163 if (state->op && state->c != 1)
Simon Kelley094b5c32014-12-21 16:11:52 +0000164 {
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000165 state->op++;
166 state->c--;
167 return 1;
Simon Kelley094b5c32014-12-21 16:11:52 +0000168 }
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000169
170 while (1)
171 {
172 d = *(state->desc);
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000173
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700174 if (d == -1)
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000175 {
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000176 /* all the bytes to the end. */
177 if ((state->c = state->end - state->ip) != 0)
178 {
179 state->op = state->ip;
180 state->ip = state->end;;
181 }
182 else
183 return 0;
184 }
185 else
186 {
187 state->desc++;
188
189 if (d == (u16)0)
190 {
191 /* domain-name, canonicalise */
192 int len;
193
194 if (!extract_name(header, plen, &state->ip, state->buff, 1, 0) ||
195 (len = to_wire(state->buff)) == 0)
196 continue;
197
198 state->c = len;
199 state->op = (unsigned char *)state->buff;
200 }
201 else
202 {
203 /* plain data preceding a domain-name, don't run off the end of the data */
204 if ((state->end - state->ip) < d)
205 d = state->end - state->ip;
206
207 if (d == 0)
208 continue;
209
210 state->op = state->ip;
211 state->c = d;
212 state->ip += d;
213 }
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000214 }
215
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000216 return 1;
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000217 }
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000218}
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000219
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000220/* Bubble sort the RRset into the canonical order. */
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000221
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700222static int sort_rrset(struct dns_header *header, size_t plen, short *rr_desc, int rrsetidx,
Simon Kelleye5412452018-01-06 22:16:31 +0000223 unsigned char **rrset, char *buff1, char *buff2)
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000224{
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000225 int swap, i, j;
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000226
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000227 do
228 {
229 for (swap = 0, i = 0; i < rrsetidx-1; i++)
230 {
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000231 int rdlen1, rdlen2;
232 struct rdata_state state1, state2;
233
Simon Kelley5107ace2014-02-23 10:48:32 +0000234 /* Note that these have been determined to be OK previously,
235 so we don't need to check for NULL return here. */
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000236 state1.ip = skip_name(rrset[i], header, plen, 10);
237 state2.ip = skip_name(rrset[i+1], header, plen, 10);
238 state1.op = state2.op = NULL;
239 state1.buff = buff1;
240 state2.buff = buff2;
241 state1.desc = state2.desc = rr_desc;
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000242
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000243 state1.ip += 8; /* skip class, type, ttl */
244 GETSHORT(rdlen1, state1.ip);
245 if (!CHECK_LEN(header, state1.ip, plen, rdlen1))
246 return rrsetidx; /* short packet */
247 state1.end = state1.ip + rdlen1;
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000248
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000249 state2.ip += 8; /* skip class, type, ttl */
250 GETSHORT(rdlen2, state2.ip);
251 if (!CHECK_LEN(header, state2.ip, plen, rdlen2))
252 return rrsetidx; /* short packet */
253 state2.end = state2.ip + rdlen2;
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000254
Simon Kelley8ebdc362021-01-22 18:50:43 +0000255 /* If the RR has no names in it then canonicalisation
256 is the identity function and we can compare
257 the RRs directly. If not we compare the
258 canonicalised RRs one byte at a time. */
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700259 if (*rr_desc == -1)
Simon Kelley8ebdc362021-01-22 18:50:43 +0000260 {
261 int rdmin = rdlen1 > rdlen2 ? rdlen2 : rdlen1;
262 int cmp = memcmp(state1.ip, state2.ip, rdmin);
263
264 if (cmp > 0 || (cmp == 0 && rdlen1 > rdmin))
265 {
266 unsigned char *tmp = rrset[i+1];
267 rrset[i+1] = rrset[i];
268 rrset[i] = tmp;
269 swap = 1;
270 }
271 else if (cmp == 0 && (rdlen1 == rdlen2))
Simon Kelleye5412452018-01-06 22:16:31 +0000272 {
273 /* Two RRs are equal, remove one copy. RFC 4034, para 6.3 */
274 for (j = i+1; j < rrsetidx-1; j++)
275 rrset[j] = rrset[j+1];
276 rrsetidx--;
277 i--;
278 }
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000279 }
Simon Kelley8ebdc362021-01-22 18:50:43 +0000280 else
281 /* Comparing canonicalised RRs, byte-at-a-time. */
282 while (1)
283 {
284 int ok1, ok2;
285
286 ok1 = get_rdata(header, plen, &state1);
287 ok2 = get_rdata(header, plen, &state2);
288
289 if (!ok1 && !ok2)
290 {
291 /* Two RRs are equal, remove one copy. RFC 4034, para 6.3 */
292 for (j = i+1; j < rrsetidx-1; j++)
293 rrset[j] = rrset[j+1];
294 rrsetidx--;
295 i--;
296 break;
297 }
298 else if (ok1 && (!ok2 || *state1.op > *state2.op))
299 {
300 unsigned char *tmp = rrset[i+1];
301 rrset[i+1] = rrset[i];
302 rrset[i] = tmp;
303 swap = 1;
304 break;
305 }
306 else if (ok2 && (!ok1 || *state2.op > *state1.op))
307 break;
308
309 /* arrive here when bytes are equal, go round the loop again
310 and compare the next ones. */
311 }
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000312 }
313 } while (swap);
Simon Kelleye5412452018-01-06 22:16:31 +0000314
315 return rrsetidx;
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000316}
317
Simon Kelley9a31b682015-12-15 10:20:39 +0000318static unsigned char **rrset = NULL, **sigs = NULL;
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000319
Josh Soref730c6742017-02-06 16:14:04 +0000320/* Get pointers to RRset members and signature(s) for same.
Simon Kelley9a31b682015-12-15 10:20:39 +0000321 Check signatures, and return keyname associated in keyname. */
322static int explore_rrset(struct dns_header *header, size_t plen, int class, int type,
323 char *name, char *keyname, int *sigcnt, int *rrcnt)
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000324{
Simon Kelley9a31b682015-12-15 10:20:39 +0000325 static int rrset_sz = 0, sig_sz = 0;
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000326 unsigned char *p;
Simon Kelley9a31b682015-12-15 10:20:39 +0000327 int rrsetidx, sigidx, j, rdlen, res;
Simon Kelley9a31b682015-12-15 10:20:39 +0000328 int gotkey = 0;
329
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000330 if (!(p = skip_questions(header, plen)))
Simon Kelley05299fd2019-07-15 22:04:20 +0100331 return 0;
Simon Kelley5ada8882014-01-09 22:25:03 +0000332
Simon Kelley9a31b682015-12-15 10:20:39 +0000333 /* look for RRSIGs for this RRset and get pointers to each RR in the set. */
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000334 for (rrsetidx = 0, sigidx = 0, j = ntohs(header->ancount) + ntohs(header->nscount);
335 j != 0; j--)
336 {
337 unsigned char *pstart, *pdata;
Simon Kelleyd67ecac2015-12-20 20:44:23 +0000338 int stype, sclass, type_covered;
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000339
340 pstart = p;
341
342 if (!(res = extract_name(header, plen, &p, name, 0, 10)))
Simon Kelley05299fd2019-07-15 22:04:20 +0100343 return 0; /* bad packet */
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000344
345 GETSHORT(stype, p);
346 GETSHORT(sclass, p);
Simon Kelleyae7a3b92019-09-03 14:40:47 +0100347
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000348 pdata = p;
349
Simon Kelleyae7a3b92019-09-03 14:40:47 +0100350 p += 4; /* TTL */
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000351 GETSHORT(rdlen, p);
352
Simon Kelleye7829ae2014-01-22 22:21:51 +0000353 if (!CHECK_LEN(header, p, plen, rdlen))
Simon Kelley9a31b682015-12-15 10:20:39 +0000354 return 0;
Simon Kelleye7829ae2014-01-22 22:21:51 +0000355
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000356 if (res == 1 && sclass == class)
357 {
358 if (stype == type)
359 {
Simon Kelley613ad152014-02-25 23:02:28 +0000360 if (!expand_workspace(&rrset, &rrset_sz, rrsetidx))
Simon Kelley9a31b682015-12-15 10:20:39 +0000361 return 0;
Simon Kelley613ad152014-02-25 23:02:28 +0000362
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000363 rrset[rrsetidx++] = pstart;
364 }
365
366 if (stype == T_RRSIG)
367 {
Simon Kelley613ad152014-02-25 23:02:28 +0000368 if (rdlen < 18)
Simon Kelley9a31b682015-12-15 10:20:39 +0000369 return 0; /* bad packet */
Simon Kelley613ad152014-02-25 23:02:28 +0000370
371 GETSHORT(type_covered, p);
Simon Kelleyd67ecac2015-12-20 20:44:23 +0000372 p += 16; /* algo, labels, orig_ttl, sig_expiration, sig_inception, key_tag */
Simon Kelley613ad152014-02-25 23:02:28 +0000373
Simon Kelley9a31b682015-12-15 10:20:39 +0000374 if (gotkey)
375 {
376 /* If there's more than one SIG, ensure they all have same keyname */
377 if (extract_name(header, plen, &p, keyname, 0, 0) != 1)
378 return 0;
379 }
380 else
381 {
382 gotkey = 1;
383
384 if (!extract_name(header, plen, &p, keyname, 1, 0))
385 return 0;
386
387 /* RFC 4035 5.3.1 says that the Signer's Name field MUST equal
388 the name of the zone containing the RRset. We can't tell that
389 for certain, but we can check that the RRset name is equal to
390 or encloses the signers name, which should be enough to stop
391 an attacker using signatures made with the key of an unrelated
392 zone he controls. Note that the root key is always allowed. */
393 if (*keyname != 0)
394 {
395 char *name_start;
396 for (name_start = name; !hostname_isequal(name_start, keyname); )
397 if ((name_start = strchr(name_start, '.')))
398 name_start++; /* chop a label off and try again */
399 else
400 return 0;
401 }
402 }
403
Simon Kelleyd67ecac2015-12-20 20:44:23 +0000404
405 if (type_covered == type)
Simon Kelley613ad152014-02-25 23:02:28 +0000406 {
407 if (!expand_workspace(&sigs, &sig_sz, sigidx))
Simon Kelley9a31b682015-12-15 10:20:39 +0000408 return 0;
Simon Kelley613ad152014-02-25 23:02:28 +0000409
410 sigs[sigidx++] = pdata;
411 }
412
Simon Kelleyae7a3b92019-09-03 14:40:47 +0100413 p = pdata + 6; /* restore for ADD_RDLEN */
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000414 }
415 }
Simon Kelley613ad152014-02-25 23:02:28 +0000416
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000417 if (!ADD_RDLEN(header, p, plen, rdlen))
Simon Kelley9a31b682015-12-15 10:20:39 +0000418 return 0;
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000419 }
420
Simon Kelley9a31b682015-12-15 10:20:39 +0000421 *sigcnt = sigidx;
422 *rrcnt = rrsetidx;
Simon Kelley00a5b5d2014-02-28 18:10:55 +0000423
Simon Kelley9a31b682015-12-15 10:20:39 +0000424 return 1;
425}
426
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700427int dec_counter(int *counter, char *message)
428{
429 if ((*counter)-- == 0)
430 {
431 my_syslog(LOG_WARNING, "limit exceeded: %s", message ? message : _("per-query crypto work"));
432 return 1;
433 }
434
435 return 0;
436}
437
Simon Kelley9a31b682015-12-15 10:20:39 +0000438/* Validate a single RRset (class, type, name) in the supplied DNS reply
439 Return code:
440 STAT_SECURE if it validates.
441 STAT_SECURE_WILDCARD if it validates and is the result of wildcard expansion.
442 (In this case *wildcard_out points to the "body" of the wildcard within name.)
443 STAT_BOGUS signature is wrong, bad packet.
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700444 STAT_ABANDONED validation abandoned do to excess resource usage.
Simon Kelley9a31b682015-12-15 10:20:39 +0000445 STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname)
446 STAT_NEED_DS need DS to complete validation (name is returned in keyname)
447
Simon Kelley2dbba342015-12-16 13:41:58 +0000448 If key is non-NULL, use that key, which has the algo and tag given in the params of those names,
Simon Kelley9a31b682015-12-15 10:20:39 +0000449 otherwise find the key in the cache.
450
Simon Kelley2dbba342015-12-16 13:41:58 +0000451 Name is unchanged on exit. keyname is used as workspace and trashed.
Simon Kelley9a31b682015-12-15 10:20:39 +0000452
453 Call explore_rrset first to find and count RRs and sigs.
Simon Kelleyae7a3b92019-09-03 14:40:47 +0100454
455 ttl_out is the floor on TTL, based on TTL and orig_ttl and expiration of sig used to validate.
Simon Kelley9a31b682015-12-15 10:20:39 +0000456*/
457static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class, int type, int sigidx, int rrsetidx,
Simon Kelleyae7a3b92019-09-03 14:40:47 +0100458 char *name, char *keyname, char **wildcard_out, struct blockdata *key, int keylen,
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700459 int algo_in, int keytag_in, unsigned long *ttl_out, int *validate_counter)
Simon Kelley9a31b682015-12-15 10:20:39 +0000460{
461 unsigned char *p;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700462 int rdlen, j, name_labels, algo, labels, key_tag, sig_fail_cnt;
Simon Kelley9a31b682015-12-15 10:20:39 +0000463 struct crec *crecp = NULL;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700464 short *rr_desc = rrfilter_desc(type);
Simon Kelleyae7a3b92019-09-03 14:40:47 +0100465 u32 sig_expiration, sig_inception;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700466 int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP | DNSSEC_FAIL_NOKEYSUP;
467
Simon Kelleyae7a3b92019-09-03 14:40:47 +0100468 unsigned long curtime = time(0);
469 int time_check = is_check_date(curtime);
470
Simon Kelley9a31b682015-12-15 10:20:39 +0000471 if (wildcard_out)
472 *wildcard_out = NULL;
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000473
Simon Kelley9a31b682015-12-15 10:20:39 +0000474 name_labels = count_labels(name); /* For 4035 5.3.2 check */
475
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000476 /* Sort RRset records into canonical order.
Simon Kelleyd3873802014-02-23 16:20:46 +0000477 Note that at this point keyname and daemon->workspacename buffs are
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000478 unused, and used as workspace by the sort. */
Simon Kelleye5412452018-01-06 22:16:31 +0000479 rrsetidx = sort_rrset(header, plen, rr_desc, rrsetidx, rrset, daemon->workspacename, keyname);
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000480
481 /* Now try all the sigs to try and find one which validates */
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700482 for (sig_fail_cnt = daemon->limit[LIMIT_SIG_FAIL], j = 0; j <sigidx; j++)
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000483 {
Simon Kelleyd3873802014-02-23 16:20:46 +0000484 unsigned char *psav, *sig, *digest;
Simon Kelley86bec2d2014-01-13 21:31:20 +0000485 int i, wire_len, sig_len;
486 const struct nettle_hash *hash;
487 void *ctx;
Simon Kelleyd3873802014-02-23 16:20:46 +0000488 char *name_start;
Simon Kelleyae7a3b92019-09-03 14:40:47 +0100489 u32 nsigttl, ttl, orig_ttl;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700490
491 failflags &= ~DNSSEC_FAIL_NOSIG;
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000492
493 p = sigs[j];
Simon Kelleyae7a3b92019-09-03 14:40:47 +0100494 GETLONG(ttl, p);
Simon Kelley5ada8882014-01-09 22:25:03 +0000495 GETSHORT(rdlen, p); /* rdlen >= 18 checked previously */
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000496 psav = p;
497
Simon Kelley5ada8882014-01-09 22:25:03 +0000498 p += 2; /* type_covered - already checked */
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000499 algo = *p++;
500 labels = *p++;
501 GETLONG(orig_ttl, p);
Simon Kelleyd67ecac2015-12-20 20:44:23 +0000502 GETLONG(sig_expiration, p);
503 GETLONG(sig_inception, p);
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000504 GETSHORT(key_tag, p);
505
Simon Kelley394ff492015-03-29 22:17:14 +0100506 if (!extract_name(header, plen, &p, keyname, 1, 0))
Simon Kelley87070192014-03-01 20:48:24 +0000507 return STAT_BOGUS;
Simon Kelleyd3873802014-02-23 16:20:46 +0000508
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700509 if (!time_check)
510 failflags &= ~(DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP);
511 else
512 {
513 /* We must explicitly check against wanted values, because of SERIAL_UNDEF */
514 if (serial_compare_32(curtime, sig_inception) == SERIAL_LT)
515 continue;
516 else
517 failflags &= ~DNSSEC_FAIL_NYV;
518
519 if (serial_compare_32(curtime, sig_expiration) == SERIAL_GT)
520 continue;
521 else
522 failflags &= ~DNSSEC_FAIL_EXP;
523 }
524
525 if (!(hash = hash_find(algo_digest_name(algo))))
526 continue;
527 else
528 failflags &= ~DNSSEC_FAIL_NOKEYSUP;
529
530 if (labels > name_labels ||
Simon Kelleye7829ae2014-01-22 22:21:51 +0000531 !hash_init(hash, &ctx, &digest))
532 continue;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700533
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000534 /* OK, we have the signature record, see if the relevant DNSKEY is in the cache. */
535 if (!key && !(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY)))
536 return STAT_NEED_KEY;
Simon Kelleyae7a3b92019-09-03 14:40:47 +0100537
538 if (ttl_out)
539 {
540 /* 4035 5.3.3 rules on TTLs */
541 if (orig_ttl < ttl)
542 ttl = orig_ttl;
543
544 if (time_check && difftime(sig_expiration, curtime) < ttl)
545 ttl = difftime(sig_expiration, curtime);
546
547 *ttl_out = ttl;
548 }
549
Simon Kelley86bec2d2014-01-13 21:31:20 +0000550 sig = p;
551 sig_len = rdlen - (p - psav);
Simon Kelleye7829ae2014-01-22 22:21:51 +0000552
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000553 nsigttl = htonl(orig_ttl);
554
Simon Kelley86bec2d2014-01-13 21:31:20 +0000555 hash->update(ctx, 18, psav);
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000556 wire_len = to_wire(keyname);
Simon Kelley86bec2d2014-01-13 21:31:20 +0000557 hash->update(ctx, (unsigned int)wire_len, (unsigned char*)keyname);
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000558 from_wire(keyname);
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000559
Simon Kelley059aded2020-11-12 23:09:15 +0000560#define RRBUFLEN 128 /* Most RRs are smaller than this. */
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000561
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000562 for (i = 0; i < rrsetidx; ++i)
563 {
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000564 int j;
565 struct rdata_state state;
566 u16 len;
567 unsigned char rrbuf[RRBUFLEN];
Simon Kelleyd3873802014-02-23 16:20:46 +0000568
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000569 p = rrset[i];
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000570
Simon Kelley394ff492015-03-29 22:17:14 +0100571 if (!extract_name(header, plen, &p, name, 1, 10))
Simon Kelley87070192014-03-01 20:48:24 +0000572 return STAT_BOGUS;
Simon Kelley5ada8882014-01-09 22:25:03 +0000573
Simon Kelleyd3873802014-02-23 16:20:46 +0000574 name_start = name;
575
Simon Kelley5ada8882014-01-09 22:25:03 +0000576 /* if more labels than in RRsig name, hash *.<no labels in rrsig labels field> 4035 5.3.2 */
577 if (labels < name_labels)
578 {
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000579 for (j = name_labels - labels; j != 0; j--)
Simon Kelleyfbc52052014-12-23 15:46:08 +0000580 {
581 while (*name_start != '.' && *name_start != 0)
582 name_start++;
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000583 if (j != 1 && *name_start == '.')
Simon Kelleyfbc52052014-12-23 15:46:08 +0000584 name_start++;
585 }
586
587 if (wildcard_out)
588 *wildcard_out = name_start+1;
589
Simon Kelley5ada8882014-01-09 22:25:03 +0000590 name_start--;
591 *name_start = '*';
592 }
593
594 wire_len = to_wire(name_start);
Simon Kelley86bec2d2014-01-13 21:31:20 +0000595 hash->update(ctx, (unsigned int)wire_len, (unsigned char *)name_start);
596 hash->update(ctx, 4, p); /* class and type */
597 hash->update(ctx, 4, (unsigned char *)&nsigttl);
Simon Kelley059aded2020-11-12 23:09:15 +0000598
599 p += 8; /* skip type, class, ttl */
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000600 GETSHORT(rdlen, p);
Simon Kelley5ada8882014-01-09 22:25:03 +0000601 if (!CHECK_LEN(header, p, plen, rdlen))
Simon Kelley87070192014-03-01 20:48:24 +0000602 return STAT_BOGUS;
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000603
Simon Kelley059aded2020-11-12 23:09:15 +0000604 /* Optimisation for RR types which need no cannonicalisation.
605 This includes DNSKEY DS NSEC and NSEC3, which are also long, so
606 it saves lots of calls to get_rdata, and avoids the pessimal
607 segmented insertion, even with a small rrbuf[].
608
609 If canonicalisation is not needed, a simple insertion into the hash works.
610 */
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700611 if (*rr_desc == -1)
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000612 {
Simon Kelley059aded2020-11-12 23:09:15 +0000613 len = htons(rdlen);
614 hash->update(ctx, 2, (unsigned char *)&len);
615 hash->update(ctx, rdlen, p);
616 }
617 else
618 {
619 /* canonicalise rdata and calculate length of same, use
620 name buffer as workspace for get_rdata. */
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000621 state.ip = p;
622 state.op = NULL;
623 state.desc = rr_desc;
Simon Kelley059aded2020-11-12 23:09:15 +0000624 state.buff = name;
625 state.end = p + rdlen;
626
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000627 for (j = 0; get_rdata(header, plen, &state); j++)
Simon Kelley059aded2020-11-12 23:09:15 +0000628 if (j < RRBUFLEN)
629 rrbuf[j] = *state.op;
630
631 len = htons((u16)j);
632 hash->update(ctx, 2, (unsigned char *)&len);
633
634 /* If the RR is shorter than RRBUFLEN (most of them, in practice)
635 then we can just digest it now. If it exceeds RRBUFLEN we have to
636 go back to the start and do it in chunks. */
637 if (j >= RRBUFLEN)
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000638 {
Simon Kelley059aded2020-11-12 23:09:15 +0000639 state.ip = p;
640 state.op = NULL;
641 state.desc = rr_desc;
642
643 for (j = 0; get_rdata(header, plen, &state); j++)
644 {
645 rrbuf[j] = *state.op;
646
647 if (j == RRBUFLEN - 1)
648 {
649 hash->update(ctx, RRBUFLEN, rrbuf);
650 j = -1;
651 }
652 }
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000653 }
Simon Kelley059aded2020-11-12 23:09:15 +0000654
655 if (j != 0)
656 hash->update(ctx, j, rrbuf);
Simon Kelley4e96a4b2020-11-11 23:25:04 +0000657 }
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000658 }
Simon Kelley86bec2d2014-01-13 21:31:20 +0000659
660 hash->digest(ctx, hash->digest_size, digest);
661
Simon Kelley5ada8882014-01-09 22:25:03 +0000662 /* namebuff used for workspace above, restore to leave unchanged on exit */
663 p = (unsigned char*)(rrset[0]);
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700664 if (!extract_name(header, plen, &p, name, 1, 0))
665 return STAT_BOGUS;
Simon Kelley5ada8882014-01-09 22:25:03 +0000666
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000667 if (key)
668 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700669 if (algo_in == algo && keytag_in == key_tag)
670 {
671 if (dec_counter(validate_counter, NULL))
672 return STAT_ABANDONED;
673
674 if (verify(key, keylen, sig, sig_len, digest, hash->digest_size, algo))
675 return STAT_SECURE;
676 }
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000677 }
678 else
679 {
680 /* iterate through all possible keys 4035 5.3.1 */
681 for (; crecp; crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY))
Simon Kelleycc921df2019-01-02 22:48:59 +0000682 if (crecp->addr.key.algo == algo &&
683 crecp->addr.key.keytag == key_tag &&
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700684 crecp->uid == (unsigned int)class)
685 {
686 if (dec_counter(validate_counter, NULL))
687 return STAT_ABANDONED;
688
689 if (verify(crecp->addr.key.keydata, crecp->addr.key.keylen, sig, sig_len, digest, hash->digest_size, algo))
690 return (labels < name_labels) ? STAT_SECURE_WILDCARD : STAT_SECURE;
691
692 /* An attacker can waste a lot of our CPU by setting up a giant DNSKEY RRSET full of failing
693 keys, all of which we have to try. Since many failing keys is not likely for
694 a legitimate domain, set a limit on how many can fail. */
695 if ((daemon->limit[LIMIT_SIG_FAIL] - (sig_fail_cnt + 1)) > (int)daemon->metrics[METRIC_SIG_FAIL_HWM])
696 daemon->metrics[METRIC_SIG_FAIL_HWM] = daemon->limit[LIMIT_SIG_FAIL] - (sig_fail_cnt + 1);
697 if (dec_counter(&sig_fail_cnt, _("per-RRSet signature fails")))
698 return STAT_ABANDONED;
699 }
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000700 }
701 }
702
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700703 /* If we reach this point, no verifying key was found */
704 return STAT_BOGUS | failflags | DNSSEC_FAIL_NOKEY;
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000705}
706
Simon Kelley2dbba342015-12-16 13:41:58 +0000707
Simon Kelley0fc2f312014-01-08 10:26:58 +0000708/* The DNS packet is expected to contain the answer to a DNSKEY query.
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000709 Put all DNSKEYs in the answer which are valid into the cache.
710 return codes:
Simon Kelley3b799c82015-12-17 16:58:04 +0000711 STAT_OK Done, key(s) in cache.
712 STAT_BOGUS No DNSKEYs found, which can be validated with DS,
713 or self-sign for DNSKEY RRset is not valid, bad packet.
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700714 STAT_ABANDONED resource exhaustion.
Simon Kelley3b799c82015-12-17 16:58:04 +0000715 STAT_NEED_DS DS records to validate a key not found, name in keyname
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000716*/
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700717int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name,
718 char *keyname, int class, int *validate_counter)
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000719{
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700720 unsigned char *psave, *p = (unsigned char *)(header+1), *keyaddr;
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000721 struct crec *crecp, *recp1;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700722 int rc, j, qtype, qclass, rdlen, flags, algo, keytag, sigcnt, rrcnt;
Simon Kelleyae7a3b92019-09-03 14:40:47 +0100723 unsigned long ttl, sig_ttl;
Simon Kelleycc921df2019-01-02 22:48:59 +0000724 union all_addr a;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700725 int failflags = DNSSEC_FAIL_NODSSUP | DNSSEC_FAIL_NOZONE;
726 char valid_digest[255];
727 static unsigned char **cached_digest;
728 static size_t cached_digest_size = 0;
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000729
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700730 if (ntohs(header->qdcount) != 1 || RCODE(header) != NOERROR || !extract_name(header, plen, &p, name, 1, 4))
731 return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000732
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000733 GETSHORT(qtype, p);
734 GETSHORT(qclass, p);
735
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700736 if (qtype != T_DNSKEY || qclass != class ||
737 !explore_rrset(header, plen, class, T_DNSKEY, name, keyname, &sigcnt, &rrcnt) ||
738 rrcnt == 0)
739 return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000740
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700741 if (sigcnt == 0)
742 return STAT_BOGUS | DNSSEC_FAIL_NOSIG;
743
Simon Kelleyb8eac192014-02-27 14:30:03 +0000744 /* See if we have cached a DS record which validates this key */
Simon Kelley0fc2f312014-01-08 10:26:58 +0000745 if (!(crecp = cache_find_by_name(NULL, name, now, F_DS)))
746 {
747 strcpy(keyname, name);
748 return STAT_NEED_DS;
749 }
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700750
Simon Kelley0fc2f312014-01-08 10:26:58 +0000751 /* NOTE, we need to find ONE DNSKEY which matches the DS */
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700752 for (j = ntohs(header->ancount); j != 0; j--)
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000753 {
754 /* Ensure we have type, class TTL and length */
Simon Kelley0fc2f312014-01-08 10:26:58 +0000755 if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
Simon Kelley87070192014-03-01 20:48:24 +0000756 return STAT_BOGUS; /* bad packet */
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000757
758 GETSHORT(qtype, p);
759 GETSHORT(qclass, p);
760 GETLONG(ttl, p);
761 GETSHORT(rdlen, p);
Simon Kelley6f468102014-01-26 23:39:17 +0000762
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700763 if (!CHECK_LEN(header, p, plen, rdlen))
Simon Kelley87070192014-03-01 20:48:24 +0000764 return STAT_BOGUS; /* bad packet */
Simon Kelley0fc2f312014-01-08 10:26:58 +0000765
Simon Kelley6f468102014-01-26 23:39:17 +0000766 if (qclass != class || qtype != T_DNSKEY || rc == 2)
767 {
768 p += rdlen;
769 continue;
770 }
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700771
772 if (rdlen < 5)
773 return STAT_BOGUS; /* min 1 byte key! */
774
Simon Kelley0fc2f312014-01-08 10:26:58 +0000775 psave = p;
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000776
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000777 GETSHORT(flags, p);
Simon Kelley0fc2f312014-01-08 10:26:58 +0000778 if (*p++ != 3)
Simon Kelley8d718cb2014-02-03 16:27:37 +0000779 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700780 p = psave + rdlen;
781 continue;
Simon Kelley8d718cb2014-02-03 16:27:37 +0000782 }
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700783 algo = *p++;
784 keyaddr = p;
785 keytag = dnskey_keytag(algo, flags, keyaddr, rdlen - 4);
786
787 p = psave + rdlen;
Simon Kelley8d718cb2014-02-03 16:27:37 +0000788
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700789 /* key must have zone key flag set */
790 if (!(flags & 0x100))
Simon Kelley0fc2f312014-01-08 10:26:58 +0000791 continue;
792
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700793 failflags &= ~DNSSEC_FAIL_NOZONE;
794
795 /* clear digest cache. */
796 memset(valid_digest, 0, sizeof(valid_digest));
797
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000798 for (recp1 = crecp; recp1; recp1 = cache_find_by_name(recp1, name, now, F_DS))
Simon Kelley86bec2d2014-01-13 21:31:20 +0000799 {
800 void *ctx;
801 unsigned char *digest, *ds_digest;
802 const struct nettle_hash *hash;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700803 int wire_len;
804
805 if ((recp1->flags & F_NEG) ||
806 recp1->addr.ds.algo != algo ||
807 recp1->addr.ds.keytag != keytag ||
808 recp1->uid != (unsigned int)class)
809 continue;
810
811 if (!(hash = hash_find(ds_digest_name(recp1->addr.ds.digest))))
812 continue;
813
814 failflags &= ~DNSSEC_FAIL_NODSSUP;
815
816 if (recp1->addr.ds.keylen != (int)hash->digest_size ||
817 !(ds_digest = blockdata_retrieve(recp1->addr.ds.keydata, recp1->addr.ds.keylen, NULL)))
818 continue;
Simon Kelley9a31b682015-12-15 10:20:39 +0000819
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700820 if (valid_digest[recp1->addr.ds.digest])
821 digest = cached_digest[recp1->addr.ds.digest];
822 else
Simon Kelley86bec2d2014-01-13 21:31:20 +0000823 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700824 /* computing a hash is a unit of crypto work. */
825 if (dec_counter(validate_counter, NULL))
826 return STAT_ABANDONED;
827
828 if (!hash_init(hash, &ctx, &digest))
829 continue;
830
831 wire_len = to_wire(name);
Simon Kelley86bec2d2014-01-13 21:31:20 +0000832
833 /* Note that digest may be different between DSs, so
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700834 we can't move this outside the loop. We keep
835 copies of each digest we make for this key,
836 so maximum digest work is O(keys x digests_types)
837 rather then O(keys x DSs) */
Simon Kelley86bec2d2014-01-13 21:31:20 +0000838 hash->update(ctx, (unsigned int)wire_len, (unsigned char *)name);
839 hash->update(ctx, (unsigned int)rdlen, psave);
840 hash->digest(ctx, hash->digest_size, digest);
841
842 from_wire(name);
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700843
844 if (recp1->addr.ds.digest >= cached_digest_size)
Simon Kelley86bec2d2014-01-13 21:31:20 +0000845 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700846 unsigned char **new;
847
848 /* whine_malloc zeros memory */
849 if ((new = whine_malloc((recp1->addr.ds.digest + 5) * sizeof(unsigned char *))))
Simon Kelley8d718cb2014-02-03 16:27:37 +0000850 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700851 if (cached_digest_size != 0)
852 {
853 memcpy(new, cached_digest, cached_digest_size * sizeof(unsigned char *));
854 free(cached_digest);
855 }
Simon Kelleyab194ed2019-01-01 01:35:30 +0000856
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700857 cached_digest_size = recp1->addr.ds.digest + 5;
858 cached_digest = new;
Simon Kelley8d718cb2014-02-03 16:27:37 +0000859 }
860 }
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700861
862 if (recp1->addr.ds.digest < cached_digest_size)
863 {
864 if (!cached_digest[recp1->addr.ds.digest])
865 cached_digest[recp1->addr.ds.digest] = whine_malloc(recp1->addr.ds.keylen);
866
867 if (cached_digest[recp1->addr.ds.digest])
868 {
869 memcpy(cached_digest[recp1->addr.ds.digest], digest, recp1->addr.ds.keylen);
870 valid_digest[recp1->addr.ds.digest] = 1;
871 }
872 }
Simon Kelleye7829ae2014-01-22 22:21:51 +0000873 }
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700874
875 if (memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0)
876 {
877 /* Found the key validated by a DS record.
878 Now check the self-sig for the entire key RRset using that key.
879 Note that validate_rrset() will never return STAT_NEED_KEY here,
880 since we supply the key it will use as an argument. */
881 struct blockdata *key;
882
883 if (!(key = blockdata_alloc((char *)keyaddr, rdlen - 4)))
884 break;
885
886 rc = validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname,
887 NULL, key, rdlen - 4, algo, keytag, &sig_ttl, validate_counter);
888
889 blockdata_free(key);
890
891 if (STAT_ISEQUAL(rc, STAT_ABANDONED))
892 return rc;
893
894 /* can't validate KEY RRset with this key, see if there's another that
895 will, which is validated by another DS. */
896 if (!STAT_ISEQUAL(rc, STAT_SECURE))
897 break;
898
899 /* DNSKEY RRset determined to be OK, now cache it. */
900 cache_start_insert();
901
902 p = skip_questions(header, plen);
903
904 for (j = ntohs(header->ancount); j != 0; j--)
905 {
906 /* Ensure we have type, class TTL and length */
907 if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
908 return STAT_BOGUS; /* bad packet */
909
910 GETSHORT(qtype, p);
911 GETSHORT(qclass, p);
912 GETLONG(ttl, p);
913 GETSHORT(rdlen, p);
914
915 /* TTL may be limited by sig. */
916 if (sig_ttl < ttl)
917 ttl = sig_ttl;
918
919 if (!CHECK_LEN(header, p, plen, rdlen))
920 return STAT_BOGUS; /* bad packet */
921
922 psave = p;
Simon Kelley8d718cb2014-02-03 16:27:37 +0000923
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700924 if (qclass == class && rc == 1 && qtype == T_DNSKEY)
925 {
926 if (rdlen < 4)
927 return STAT_BOGUS; /* min 1 byte key! */
928
929 GETSHORT(flags, p);
930 if (*p++ == 3)
931 {
932 algo = *p++;
933 keytag = dnskey_keytag(algo, flags, p, rdlen - 4);
934
935 if (!(key = blockdata_alloc((char*)p, rdlen - 4)))
936 return STAT_BOGUS;
937
938 a.key.keylen = rdlen - 4;
939 a.key.keydata = key;
940 a.key.algo = algo;
941 a.key.keytag = keytag;
942 a.key.flags = flags;
943
944 if (!cache_insert(name, &a, class, now, ttl, F_FORWARD | F_DNSKEY | F_DNSSECOK))
945 {
946 blockdata_free(key);
947 return STAT_BOGUS;
948 }
949
950 a.log.keytag = keytag;
951 a.log.algo = algo;
952 if (algo_digest_name(algo))
953 log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu", 0);
954 else
955 log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu (not supported)", 0);
956 }
957 }
958
959 p = psave + rdlen;
960 }
961
962 /* commit cache insert. */
963 cache_end_insert();
964 return STAT_OK;
965 }
Simon Kelleye7829ae2014-01-22 22:21:51 +0000966 }
Simon Kelley0fc2f312014-01-08 10:26:58 +0000967 }
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700968
969 log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY", 0);
970 return STAT_BOGUS | failflags;
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000971}
Simon Kelley0fc2f312014-01-08 10:26:58 +0000972
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000973/* The DNS packet is expected to contain the answer to a DS query
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700974 Put all DSs in the answer which are valid and have hash and signature algos
975 we support into the cache.
Simon Kelley9a31b682015-12-15 10:20:39 +0000976 Also handles replies which prove that there's no DS at this location,
977 either because the zone is unsigned or this isn't a zone cut. These are
978 cached too.
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700979 If none of the DS's are for supported algos, treat the answer as if
980 it's a proof of no DS at this location. RFC4035 para 5.2.
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000981 return codes:
Simon Kelley9a31b682015-12-15 10:20:39 +0000982 STAT_OK At least one valid DS found and in cache.
Simon Kelley97e618a2015-01-07 21:55:43 +0000983 STAT_BOGUS no DS in reply or not signed, fails validation, bad packet.
Simon Kelley0b8a5a32015-03-27 11:44:55 +0000984 STAT_NEED_KEY DNSKEY records to validate a DS not found, name in keyname
Simon Kelley9a31b682015-12-15 10:20:39 +0000985 STAT_NEED_DS DS record needed.
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700986 STAT_ABANDONED resource exhaustion.
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000987*/
988
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700989int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name,
990 char *keyname, int class, int *validate_counter)
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000991{
Simon Kelley51ea3ca2014-01-22 19:31:38 +0000992 unsigned char *p = (unsigned char *)(header+1);
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700993 int qtype, qclass, rc, i, neganswer = 0, nons = 0, servfail = 0, neg_ttl = 0, found_supported = 0;
994 int aclass, atype, rdlen, flags;
Simon Kelleyd64c81f2015-12-15 16:11:06 +0000995 unsigned long ttl;
Simon Kelleycc921df2019-01-02 22:48:59 +0000996 union all_addr a;
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +0000997
Simon Kelley5f8e58f2014-01-09 17:31:19 +0000998 if (ntohs(header->qdcount) != 1 ||
Simon Kelleyb8eac192014-02-27 14:30:03 +0000999 !(p = skip_name(p, header, plen, 4)))
Simon Kelley87070192014-03-01 20:48:24 +00001000 return STAT_BOGUS;
Simon Kelley8d718cb2014-02-03 16:27:37 +00001001
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +00001002 GETSHORT(qtype, p);
1003 GETSHORT(qclass, p);
1004
Simon Kelleyb47b04c2014-02-25 23:13:28 +00001005 if (qtype != T_DS || qclass != class)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001006 return STAT_BOGUS;
1007
1008 /* A SERVFAIL answer has been seen to a DS query not at start of authority,
1009 so treat it as such and continue to search for a DS or proof of no existence
1010 further down the tree. */
1011 if (RCODE(header) == SERVFAIL)
1012 servfail = neganswer = nons = 1;
Simon Kelleyb47b04c2014-02-25 23:13:28 +00001013 else
Simon Kelley7f008432018-04-15 20:01:49 +01001014 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001015 rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl, validate_counter);
1016
1017 if (STAT_ISEQUAL(rc, STAT_INSECURE))
1018 {
1019 my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
1020 log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure", 0);
1021 return STAT_BOGUS | DNSSEC_FAIL_INDET;
1022 }
1023
1024 p = (unsigned char *)(header+1);
1025 if (!extract_name(header, plen, &p, name, 1, 4))
1026 return STAT_BOGUS;
1027
1028 p += 4; /* qtype, qclass */
1029
1030 /* If the key needed to validate the DS is on the same domain as the DS, we'll
1031 loop getting nowhere. Stop that now. This can happen of the DS answer comes
1032 from the DS's zone, and not the parent zone. */
1033 if (STAT_ISEQUAL(rc, STAT_NEED_KEY) && hostname_isequal(name, keyname))
1034 {
1035 log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS", 0);
1036 return STAT_BOGUS;
1037 }
1038
1039 if (!STAT_ISEQUAL(rc, STAT_SECURE))
1040 return rc;
Simon Kelley7f008432018-04-15 20:01:49 +01001041 }
1042
Simon Kelleyd64c81f2015-12-15 16:11:06 +00001043 if (!neganswer)
Simon Kelley97e618a2015-01-07 21:55:43 +00001044 {
Simon Kelleyd64c81f2015-12-15 16:11:06 +00001045 cache_start_insert();
1046
1047 for (i = 0; i < ntohs(header->ancount); i++)
1048 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001049 unsigned char *psave;
1050
Simon Kelleyd64c81f2015-12-15 16:11:06 +00001051 if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
1052 return STAT_BOGUS; /* bad packet */
1053
1054 GETSHORT(atype, p);
1055 GETSHORT(aclass, p);
1056 GETLONG(ttl, p);
1057 GETSHORT(rdlen, p);
1058
1059 if (!CHECK_LEN(header, p, plen, rdlen))
1060 return STAT_BOGUS; /* bad packet */
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001061
1062 psave = p;
Simon Kelleyd64c81f2015-12-15 16:11:06 +00001063
1064 if (aclass == class && atype == T_DS && rc == 1)
1065 {
1066 int algo, digest, keytag;
Simon Kelleyd64c81f2015-12-15 16:11:06 +00001067 struct blockdata *key;
Simon Kelleyab194ed2019-01-01 01:35:30 +00001068
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001069 if (rdlen < 5)
1070 return STAT_BOGUS; /* min 1 byte digest! */
Simon Kelleyd64c81f2015-12-15 16:11:06 +00001071
1072 GETSHORT(keytag, p);
1073 algo = *p++;
1074 digest = *p++;
1075
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001076 if (!ds_digest_name(digest) || !algo_digest_name(algo))
1077 {
1078 a.log.keytag = keytag;
1079 a.log.algo = algo;
1080 a.log.digest = digest;
1081 log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS for keytag %hu, algo %hu, digest %hu (not supported)", 0);
1082 neg_ttl = ttl;
1083 }
1084 else if ((key = blockdata_alloc((char*)p, rdlen - 4)))
Simon Kelleyd64c81f2015-12-15 16:11:06 +00001085 {
Simon Kelleycc921df2019-01-02 22:48:59 +00001086 a.ds.digest = digest;
1087 a.ds.keydata = key;
1088 a.ds.algo = algo;
1089 a.ds.keytag = keytag;
1090 a.ds.keylen = rdlen - 4;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001091
Simon Kelleyab194ed2019-01-01 01:35:30 +00001092 if (!cache_insert(name, &a, class, now, ttl, F_FORWARD | F_DS | F_DNSSECOK))
Simon Kelleyd64c81f2015-12-15 16:11:06 +00001093 {
1094 blockdata_free(key);
1095 return STAT_BOGUS;
1096 }
1097 else
1098 {
Simon Kelleycc921df2019-01-02 22:48:59 +00001099 a.log.keytag = keytag;
1100 a.log.algo = algo;
1101 a.log.digest = digest;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001102 log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS for keytag %hu, algo %hu, digest %hu", 0);
1103 found_supported = 1;
Simon Kelleyd64c81f2015-12-15 16:11:06 +00001104 }
1105 }
Simon Kelleyd64c81f2015-12-15 16:11:06 +00001106 }
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001107
1108 p = psave + rdlen;
Simon Kelleyd64c81f2015-12-15 16:11:06 +00001109 }
Simon Kelley3b799c82015-12-17 16:58:04 +00001110
1111 cache_end_insert();
1112
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001113 /* Fall through if no supported algo DS found. */
1114 if (found_supported)
1115 return STAT_OK;
Simon Kelleyd64c81f2015-12-15 16:11:06 +00001116 }
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001117
1118 flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
1119
1120 if (neganswer)
Simon Kelleyd64c81f2015-12-15 16:11:06 +00001121 {
Simon Kelley00a5b5d2014-02-28 18:10:55 +00001122 if (RCODE(header) == NXDOMAIN)
1123 flags |= F_NXDOMAIN;
1124
Simon Kelley97e618a2015-01-07 21:55:43 +00001125 /* We only cache validated DS records, DNSSECOK flag hijacked
1126 to store presence/absence of NS. */
1127 if (nons)
1128 flags &= ~F_DNSSECOK;
Simon Kelleyb8eac192014-02-27 14:30:03 +00001129 }
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001130
1131 cache_start_insert();
1132
1133 /* Use TTL from NSEC for negative cache entries */
1134 if (!cache_insert(name, NULL, class, now, neg_ttl, flags))
1135 return STAT_BOGUS;
1136
1137 cache_end_insert();
1138
1139 if (neganswer)
1140 log_query(F_NOEXTRA | F_UPSTREAM, name, NULL,
1141 servfail ? "SERVFAIL" : (nons ? "no DS/cut" : "no DS"), 0);
Simon Kelleyd64c81f2015-12-15 16:11:06 +00001142
Simon Kelley9a31b682015-12-15 10:20:39 +00001143 return STAT_OK;
Simon Kelleyc3e0b9b2013-12-31 13:50:39 +00001144}
1145
Simon Kelley9a31b682015-12-15 10:20:39 +00001146
Simon Kelleyc5f4ec72014-01-20 22:37:55 +00001147/* 4034 6.1 */
1148static int hostname_cmp(const char *a, const char *b)
1149{
Simon Kelleydbf72122014-01-21 14:28:02 +00001150 char *sa, *ea, *ca, *sb, *eb, *cb;
1151 unsigned char ac, bc;
1152
1153 sa = ea = (char *)a + strlen(a);
1154 sb = eb = (char *)b + strlen(b);
1155
Simon Kelleyc5f4ec72014-01-20 22:37:55 +00001156 while (1)
1157 {
Simon Kelleydbf72122014-01-21 14:28:02 +00001158 while (sa != a && *(sa-1) != '.')
1159 sa--;
Simon Kelleyc5f4ec72014-01-20 22:37:55 +00001160
Simon Kelleydbf72122014-01-21 14:28:02 +00001161 while (sb != b && *(sb-1) != '.')
1162 sb--;
Simon Kelleyc5f4ec72014-01-20 22:37:55 +00001163
Simon Kelleydbf72122014-01-21 14:28:02 +00001164 ca = sa;
1165 cb = sb;
Simon Kelleyc5f4ec72014-01-20 22:37:55 +00001166
Simon Kelleydbf72122014-01-21 14:28:02 +00001167 while (1)
Simon Kelleyc5f4ec72014-01-20 22:37:55 +00001168 {
Simon Kelleydbf72122014-01-21 14:28:02 +00001169 if (ca == ea)
1170 {
1171 if (cb == eb)
1172 break;
1173
1174 return -1;
1175 }
Simon Kelleyc5f4ec72014-01-20 22:37:55 +00001176
Simon Kelleydbf72122014-01-21 14:28:02 +00001177 if (cb == eb)
1178 return 1;
1179
1180 ac = (unsigned char) *ca++;
1181 bc = (unsigned char) *cb++;
1182
1183 if (ac >= 'A' && ac <= 'Z')
1184 ac += 'a' - 'A';
1185 if (bc >= 'A' && bc <= 'Z')
1186 bc += 'a' - 'A';
1187
Simon Kelley979cdf92014-01-21 16:26:41 +00001188 if (ac < bc)
Simon Kelleydbf72122014-01-21 14:28:02 +00001189 return -1;
1190 else if (ac != bc)
1191 return 1;
1192 }
1193
1194
1195 if (sa == a)
1196 {
1197 if (sb == b)
1198 return 0;
1199
1200 return -1;
Simon Kelleyc5f4ec72014-01-20 22:37:55 +00001201 }
1202
Simon Kelleydbf72122014-01-21 14:28:02 +00001203 if (sb == b)
1204 return 1;
Simon Kelleyc5f4ec72014-01-20 22:37:55 +00001205
Simon Kelley3e86d312015-12-20 20:50:05 +00001206 ea = --sa;
1207 eb = --sb;
Simon Kelleyc5f4ec72014-01-20 22:37:55 +00001208 }
1209}
1210
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001211/* returns 0 on success, or DNSSEC_FAIL_* value on failure. */
Simon Kelley4fe67442018-01-19 12:26:08 +00001212static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, unsigned char **labels, int nsec_count,
1213 char *workspace1_in, char *workspace2, char *name, int type, int *nons)
Simon Kelley5107ace2014-02-23 10:48:32 +00001214{
1215 int i, rc, rdlen;
1216 unsigned char *p, *psave;
1217 int offset = (type & 0xff) >> 3;
1218 int mask = 0x80 >> (type & 0x07);
Simon Kelley97e618a2015-01-07 21:55:43 +00001219
1220 if (nons)
Simon Kelley9a31b682015-12-15 10:20:39 +00001221 *nons = 1;
Simon Kelley5107ace2014-02-23 10:48:32 +00001222
1223 /* Find NSEC record that proves name doesn't exist */
1224 for (i = 0; i < nsec_count; i++)
1225 {
Simon Kelley4fe67442018-01-19 12:26:08 +00001226 char *workspace1 = workspace1_in;
1227 int sig_labels, name_labels;
1228
Simon Kelley5107ace2014-02-23 10:48:32 +00001229 p = nsecs[i];
Simon Kelley394ff492015-03-29 22:17:14 +01001230 if (!extract_name(header, plen, &p, workspace1, 1, 10))
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001231 return DNSSEC_FAIL_BADPACKET;
Simon Kelley5107ace2014-02-23 10:48:32 +00001232 p += 8; /* class, type, TTL */
1233 GETSHORT(rdlen, p);
1234 psave = p;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001235 if (!extract_name(header, plen, &p, workspace2, 1, 0))
1236 return DNSSEC_FAIL_BADPACKET;
Simon Kelley4fe67442018-01-19 12:26:08 +00001237
1238 /* If NSEC comes from wildcard expansion, use original wildcard
1239 as name for computation. */
1240 sig_labels = *labels[i];
1241 name_labels = count_labels(workspace1);
1242
1243 if (sig_labels < name_labels)
1244 {
1245 int k;
1246 for (k = name_labels - sig_labels; k != 0; k--)
1247 {
1248 while (*workspace1 != '.' && *workspace1 != 0)
1249 workspace1++;
1250 if (k != 1 && *workspace1 == '.')
1251 workspace1++;
1252 }
1253
1254 workspace1--;
1255 *workspace1 = '*';
1256 }
1257
Simon Kelley5107ace2014-02-23 10:48:32 +00001258 rc = hostname_cmp(workspace1, name);
1259
1260 if (rc == 0)
1261 {
Simon Kelleyf01d7be2014-02-24 20:20:00 +00001262 /* 4035 para 5.4. Last sentence */
1263 if (type == T_NSEC || type == T_RRSIG)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001264 return 0;
Simon Kelleyf01d7be2014-02-24 20:20:00 +00001265
Simon Kelley5107ace2014-02-23 10:48:32 +00001266 /* NSEC with the same name as the RR we're testing, check
1267 that the type in question doesn't appear in the type map */
1268 rdlen -= p - psave;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001269 /* rdlen is now length of type map, and p points to it
1270 packet checked to be as long as rdlen implies in prove_non_existence() */
Simon Kelley5107ace2014-02-23 10:48:32 +00001271
Simon Kelley97e618a2015-01-07 21:55:43 +00001272 /* If we can prove that there's no NS record, return that information. */
Simon Kelley9a31b682015-12-15 10:20:39 +00001273 if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) != 0)
1274 *nons = 0;
Simon Kelley97e618a2015-01-07 21:55:43 +00001275
Simon Kelley9a31b682015-12-15 10:20:39 +00001276 if (rdlen >= 2 && p[0] == 0)
1277 {
1278 /* A CNAME answer would also be valid, so if there's a CNAME is should
1279 have been returned. */
1280 if ((p[2] & (0x80 >> T_CNAME)) != 0)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001281 return DNSSEC_FAIL_NONSEC;
Simon Kelley9a31b682015-12-15 10:20:39 +00001282
1283 /* If the SOA bit is set for a DS record, then we have the
Simon Kelleya969ba62018-01-20 23:08:38 +00001284 DS from the wrong side of the delegation. For the root DS,
1285 this is expected. */
1286 if (name_labels != 0 && type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001287 return DNSSEC_FAIL_NONSEC;
Simon Kelley9a31b682015-12-15 10:20:39 +00001288 }
1289
Simon Kelley5107ace2014-02-23 10:48:32 +00001290 while (rdlen >= 2)
1291 {
1292 if (!CHECK_LEN(header, p, plen, rdlen))
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001293 return DNSSEC_FAIL_BADPACKET;
Simon Kelley5107ace2014-02-23 10:48:32 +00001294
1295 if (p[0] == type >> 8)
1296 {
1297 /* Does the NSEC say our type exists? */
Simon Kelleya857daa2014-02-24 21:01:09 +00001298 if (offset < p[1] && (p[offset+2] & mask) != 0)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001299 return DNSSEC_FAIL_NONSEC;
Simon Kelley5107ace2014-02-23 10:48:32 +00001300
Josh Soref730c6742017-02-06 16:14:04 +00001301 break; /* finished checking */
Simon Kelley5107ace2014-02-23 10:48:32 +00001302 }
1303
1304 rdlen -= p[1];
1305 p += p[1];
1306 }
1307
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001308 return 0;
Simon Kelley5107ace2014-02-23 10:48:32 +00001309 }
1310 else if (rc == -1)
1311 {
1312 /* Normal case, name falls between NSEC name and next domain name,
1313 wrap around case, name falls between NSEC name (rc == -1) and end */
Simon Kelley4d25cf82015-06-06 23:13:57 +01001314 if (hostname_cmp(workspace2, name) >= 0 || hostname_cmp(workspace1, workspace2) >= 0)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001315 return 0;
Simon Kelley5107ace2014-02-23 10:48:32 +00001316 }
1317 else
1318 {
1319 /* wrap around case, name falls between start and next domain name */
Simon Kelley4d25cf82015-06-06 23:13:57 +01001320 if (hostname_cmp(workspace1, workspace2) >= 0 && hostname_cmp(workspace2, name) >=0 )
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001321 return 0;
Simon Kelley5107ace2014-02-23 10:48:32 +00001322 }
1323 }
1324
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001325 return DNSSEC_FAIL_NONSEC;
Simon Kelley5107ace2014-02-23 10:48:32 +00001326}
1327
1328/* return digest length, or zero on error */
1329static int hash_name(char *in, unsigned char **out, struct nettle_hash const *hash,
1330 unsigned char *salt, int salt_len, int iterations)
1331{
1332 void *ctx;
1333 unsigned char *digest;
1334 int i;
1335
1336 if (!hash_init(hash, &ctx, &digest))
1337 return 0;
1338
1339 hash->update(ctx, to_wire(in), (unsigned char *)in);
1340 hash->update(ctx, salt_len, salt);
1341 hash->digest(ctx, hash->digest_size, digest);
1342
1343 for(i = 0; i < iterations; i++)
1344 {
1345 hash->update(ctx, hash->digest_size, digest);
1346 hash->update(ctx, salt_len, salt);
1347 hash->digest(ctx, hash->digest_size, digest);
1348 }
1349
1350 from_wire(in);
1351
1352 *out = digest;
1353 return hash->digest_size;
1354}
1355
1356/* Decode base32 to first "." or end of string */
1357static int base32_decode(char *in, unsigned char *out)
1358{
Simon Kelleya857daa2014-02-24 21:01:09 +00001359 int oc, on, c, mask, i;
Simon Kelley5107ace2014-02-23 10:48:32 +00001360 unsigned char *p = out;
1361
Simon Kelleya857daa2014-02-24 21:01:09 +00001362 for (c = *in, oc = 0, on = 0; c != 0 && c != '.'; c = *++in)
Simon Kelley5107ace2014-02-23 10:48:32 +00001363 {
Simon Kelley5107ace2014-02-23 10:48:32 +00001364 if (c >= '0' && c <= '9')
1365 c -= '0';
1366 else if (c >= 'a' && c <= 'v')
1367 c -= 'a', c += 10;
1368 else if (c >= 'A' && c <= 'V')
1369 c -= 'A', c += 10;
1370 else
1371 return 0;
1372
1373 for (mask = 0x10, i = 0; i < 5; i++)
1374 {
Simon Kelleya857daa2014-02-24 21:01:09 +00001375 if (c & mask)
1376 oc |= 1;
1377 mask = mask >> 1;
1378 if (((++on) & 7) == 0)
1379 *p++ = oc;
1380 oc = oc << 1;
Simon Kelley5107ace2014-02-23 10:48:32 +00001381 }
1382 }
1383
1384 if ((on & 7) != 0)
1385 return 0;
1386
1387 return p - out;
1388}
1389
Simon Kelleyfbc52052014-12-23 15:46:08 +00001390static int check_nsec3_coverage(struct dns_header *header, size_t plen, int digest_len, unsigned char *digest, int type,
Simon Kelleya969ba62018-01-20 23:08:38 +00001391 char *workspace1, char *workspace2, unsigned char **nsecs, int nsec_count, int *nons, int name_labels)
Simon Kelleyfbc52052014-12-23 15:46:08 +00001392{
Simon Kelley9a31b682015-12-15 10:20:39 +00001393 int i, hash_len, salt_len, base32_len, rdlen, flags;
Simon Kelleyfbc52052014-12-23 15:46:08 +00001394 unsigned char *p, *psave;
1395
1396 for (i = 0; i < nsec_count; i++)
1397 if ((p = nsecs[i]))
1398 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001399 if (!extract_name(header, plen, &p, workspace1, 1, 10) ||
Simon Kelleyfbc52052014-12-23 15:46:08 +00001400 !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
1401 return 0;
1402
1403 p += 8; /* class, type, TTL */
1404 GETSHORT(rdlen, p);
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001405
Simon Kelleyfbc52052014-12-23 15:46:08 +00001406 psave = p;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001407
1408 /* packet checked to be as long as implied by rdlen, salt_len and hash_len in prove_non_existence() */
Simon Kelley9a31b682015-12-15 10:20:39 +00001409 p++; /* algo */
1410 flags = *p++; /* flags */
1411 p += 2; /* iterations */
Simon Kelleyfbc52052014-12-23 15:46:08 +00001412 salt_len = *p++; /* salt_len */
1413 p += salt_len; /* salt */
1414 hash_len = *p++; /* p now points to next hashed name */
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001415
Simon Kelleyfbc52052014-12-23 15:46:08 +00001416 if (digest_len == base32_len && hash_len == base32_len)
1417 {
1418 int rc = memcmp(workspace2, digest, digest_len);
1419
1420 if (rc == 0)
1421 {
1422 /* We found an NSEC3 whose hashed name exactly matches the query, so
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001423 we just need to check the type map. p points to the RR data for the record.
1424 Note we have packet length up to rdlen bytes checked. */
Simon Kelleyfbc52052014-12-23 15:46:08 +00001425
1426 int offset = (type & 0xff) >> 3;
1427 int mask = 0x80 >> (type & 0x07);
1428
1429 p += hash_len; /* skip next-domain hash */
1430 rdlen -= p - psave;
1431
Simon Kelley9a31b682015-12-15 10:20:39 +00001432 if (rdlen >= 2 && p[0] == 0)
1433 {
Simon Kelleyec0628c2015-12-31 20:55:39 +00001434 /* If we can prove that there's no NS record, return that information. */
1435 if (nons && (p[2] & (0x80 >> T_NS)) != 0)
1436 *nons = 0;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001437
Simon Kelley9a31b682015-12-15 10:20:39 +00001438 /* A CNAME answer would also be valid, so if there's a CNAME is should
1439 have been returned. */
1440 if ((p[2] & (0x80 >> T_CNAME)) != 0)
1441 return 0;
1442
1443 /* If the SOA bit is set for a DS record, then we have the
Simon Kelleya969ba62018-01-20 23:08:38 +00001444 DS from the wrong side of the delegation. For the root DS,
1445 this is expected. */
1446 if (name_labels != 0 && type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
Simon Kelley9a31b682015-12-15 10:20:39 +00001447 return 0;
1448 }
1449
Simon Kelleyfbc52052014-12-23 15:46:08 +00001450 while (rdlen >= 2)
1451 {
1452 if (p[0] == type >> 8)
1453 {
1454 /* Does the NSEC3 say our type exists? */
1455 if (offset < p[1] && (p[offset+2] & mask) != 0)
Simon Kelley9a31b682015-12-15 10:20:39 +00001456 return 0;
Simon Kelleyfbc52052014-12-23 15:46:08 +00001457
Josh Soref730c6742017-02-06 16:14:04 +00001458 break; /* finished checking */
Simon Kelleyfbc52052014-12-23 15:46:08 +00001459 }
1460
1461 rdlen -= p[1];
1462 p += p[1];
1463 }
Simon Kelley9a31b682015-12-15 10:20:39 +00001464
Simon Kelleyfbc52052014-12-23 15:46:08 +00001465 return 1;
1466 }
Simon Kelley4d25cf82015-06-06 23:13:57 +01001467 else if (rc < 0)
Simon Kelleyfbc52052014-12-23 15:46:08 +00001468 {
1469 /* Normal case, hash falls between NSEC3 name-hash and next domain name-hash,
1470 wrap around case, name-hash falls between NSEC3 name-hash and end */
Simon Kelley4d25cf82015-06-06 23:13:57 +01001471 if (memcmp(p, digest, digest_len) >= 0 || memcmp(workspace2, p, digest_len) >= 0)
Simon Kelley9a31b682015-12-15 10:20:39 +00001472 {
1473 if ((flags & 0x01) && nons) /* opt out */
1474 *nons = 0;
1475
1476 return 1;
1477 }
Simon Kelleyfbc52052014-12-23 15:46:08 +00001478 }
1479 else
1480 {
1481 /* wrap around case, name falls between start and next domain name */
Simon Kelley4d25cf82015-06-06 23:13:57 +01001482 if (memcmp(workspace2, p, digest_len) >= 0 && memcmp(p, digest, digest_len) >= 0)
Simon Kelley9a31b682015-12-15 10:20:39 +00001483 {
1484 if ((flags & 0x01) && nons) /* opt out */
1485 *nons = 0;
1486
1487 return 1;
1488 }
Simon Kelleyfbc52052014-12-23 15:46:08 +00001489 }
1490 }
1491 }
Simon Kelley9a31b682015-12-15 10:20:39 +00001492
Simon Kelleyfbc52052014-12-23 15:46:08 +00001493 return 0;
1494}
1495
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001496/* returns 0 on success, or DNSSEC_FAIL_* value on failure. */
1497static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count, char *workspace1,
1498 char *workspace2, char *name, int type, char *wildname, int *nons, int *validate_counter)
Simon Kelley5107ace2014-02-23 10:48:32 +00001499{
Simon Kelleya857daa2014-02-24 21:01:09 +00001500 unsigned char *salt, *p, *digest;
Simon Kelleyfbc52052014-12-23 15:46:08 +00001501 int digest_len, i, iterations, salt_len, base32_len, algo = 0;
Simon Kelley5107ace2014-02-23 10:48:32 +00001502 struct nettle_hash const *hash;
1503 char *closest_encloser, *next_closest, *wildcard;
Simon Kelley97e618a2015-01-07 21:55:43 +00001504
1505 if (nons)
Simon Kelley9a31b682015-12-15 10:20:39 +00001506 *nons = 1;
Simon Kelley97e618a2015-01-07 21:55:43 +00001507
Simon Kelley5107ace2014-02-23 10:48:32 +00001508 /* Look though the NSEC3 records to find the first one with
Simon Kelleyd67ecac2015-12-20 20:44:23 +00001509 an algorithm we support.
Simon Kelley5107ace2014-02-23 10:48:32 +00001510
1511 Take the algo, iterations, and salt of that record
1512 as the ones we're going to use, and prune any
1513 that don't match. */
1514
1515 for (i = 0; i < nsec_count; i++)
1516 {
1517 if (!(p = skip_name(nsecs[i], header, plen, 15)))
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001518 return DNSSEC_FAIL_BADPACKET; /* bad packet */
Simon Kelley5107ace2014-02-23 10:48:32 +00001519
1520 p += 10; /* type, class, TTL, rdlen */
1521 algo = *p++;
1522
Simon Kelleyd67ecac2015-12-20 20:44:23 +00001523 if ((hash = hash_find(nsec3_digest_name(algo))))
Simon Kelley5107ace2014-02-23 10:48:32 +00001524 break; /* known algo */
1525 }
1526
1527 /* No usable NSEC3s */
1528 if (i == nsec_count)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001529 return DNSSEC_FAIL_NONSEC;
Simon Kelley5107ace2014-02-23 10:48:32 +00001530
1531 p++; /* flags */
Simon Kelley40205a02016-03-14 21:24:00 +00001532
Simon Kelley5107ace2014-02-23 10:48:32 +00001533 GETSHORT (iterations, p);
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001534 /* Upper-bound iterations, to avoid DoS. RFC 9276 refers. */
1535 if (iterations > daemon->limit[LIMIT_NSEC3_ITERS])
1536 return DNSSEC_FAIL_NSEC3_ITERS;
Simon Kelley40205a02016-03-14 21:24:00 +00001537
Simon Kelley5107ace2014-02-23 10:48:32 +00001538 salt_len = *p++;
1539 salt = p;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001540
Simon Kelley5107ace2014-02-23 10:48:32 +00001541 /* Now prune so we only have NSEC3 records with same iterations, salt and algo */
1542 for (i = 0; i < nsec_count; i++)
1543 {
1544 unsigned char *nsec3p = nsecs[i];
Simon Kelleyce5732e2015-12-20 21:39:19 +00001545 int this_iter, flags;
Simon Kelley5107ace2014-02-23 10:48:32 +00001546
1547 nsecs[i] = NULL; /* Speculative, will be restored if OK. */
1548
1549 if (!(p = skip_name(nsec3p, header, plen, 15)))
Simon Kelleyb40f26c2015-12-17 11:57:26 +00001550 return 0; /* bad packet */
Simon Kelley5107ace2014-02-23 10:48:32 +00001551
1552 p += 10; /* type, class, TTL, rdlen */
1553
1554 if (*p++ != algo)
1555 continue;
1556
Simon Kelleyce5732e2015-12-20 21:39:19 +00001557 flags = *p++; /* flags */
Simon Kelley5107ace2014-02-23 10:48:32 +00001558
Simon Kelleyce5732e2015-12-20 21:39:19 +00001559 /* 5155 8.2 */
1560 if (flags != 0 && flags != 1)
1561 continue;
1562
Simon Kelleya857daa2014-02-24 21:01:09 +00001563 GETSHORT(this_iter, p);
Simon Kelley5107ace2014-02-23 10:48:32 +00001564 if (this_iter != iterations)
1565 continue;
1566
1567 if (salt_len != *p++)
1568 continue;
1569
Simon Kelley5107ace2014-02-23 10:48:32 +00001570 if (memcmp(p, salt, salt_len) != 0)
1571 continue;
1572
1573 /* All match, put the pointer back */
1574 nsecs[i] = nsec3p;
1575 }
1576
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001577 if (dec_counter(validate_counter, NULL))
1578 return DNSSEC_FAIL_WORK;
1579
Simon Kelleyfbc52052014-12-23 15:46:08 +00001580 if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001581 return DNSSEC_FAIL_NONSEC;
Simon Kelleyfbc52052014-12-23 15:46:08 +00001582
Simon Kelleya969ba62018-01-20 23:08:38 +00001583 if (check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, nons, count_labels(name)))
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001584 return 0;
Simon Kelleyfbc52052014-12-23 15:46:08 +00001585
1586 /* Can't find an NSEC3 which covers the name directly, we need the "closest encloser NSEC3"
1587 or an answer inferred from a wildcard record. */
Simon Kelley5107ace2014-02-23 10:48:32 +00001588 closest_encloser = name;
1589 next_closest = NULL;
1590
1591 do
1592 {
1593 if (*closest_encloser == '.')
1594 closest_encloser++;
1595
Simon Kelleyfbc52052014-12-23 15:46:08 +00001596 if (wildname && hostname_isequal(closest_encloser, wildname))
1597 break;
1598
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001599 if (dec_counter(validate_counter, NULL))
1600 return DNSSEC_FAIL_WORK;
1601
Simon Kelleya857daa2014-02-24 21:01:09 +00001602 if ((digest_len = hash_name(closest_encloser, &digest, hash, salt, salt_len, iterations)) == 0)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001603 return DNSSEC_FAIL_NONSEC;
Simon Kelley5107ace2014-02-23 10:48:32 +00001604
1605 for (i = 0; i < nsec_count; i++)
1606 if ((p = nsecs[i]))
1607 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001608 if (!extract_name(header, plen, &p, workspace1, 1, 0))
1609 return DNSSEC_FAIL_BADPACKET;
1610
1611 if (!(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
1612 return DNSSEC_FAIL_NONSEC;
Simon Kelley5107ace2014-02-23 10:48:32 +00001613
Simon Kelleya857daa2014-02-24 21:01:09 +00001614 if (digest_len == base32_len &&
1615 memcmp(digest, workspace2, digest_len) == 0)
Simon Kelley5107ace2014-02-23 10:48:32 +00001616 break; /* Gotit */
1617 }
1618
1619 if (i != nsec_count)
1620 break;
1621
1622 next_closest = closest_encloser;
1623 }
1624 while ((closest_encloser = strchr(closest_encloser, '.')));
1625
Simon Kelleya7b27e82016-03-16 19:11:52 +00001626 if (!closest_encloser || !next_closest)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001627 return DNSSEC_FAIL_NONSEC;
Simon Kelley5107ace2014-02-23 10:48:32 +00001628
Simon Kelley24187532014-02-24 21:46:44 +00001629 /* Look for NSEC3 that proves the non-existence of the next-closest encloser */
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001630 if (dec_counter(validate_counter, NULL))
1631 return DNSSEC_FAIL_WORK;
1632
Simon Kelleya857daa2014-02-24 21:01:09 +00001633 if ((digest_len = hash_name(next_closest, &digest, hash, salt, salt_len, iterations)) == 0)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001634 return DNSSEC_FAIL_NONSEC;
Simon Kelley5107ace2014-02-23 10:48:32 +00001635
Simon Kelleya969ba62018-01-20 23:08:38 +00001636 if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL, 1))
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001637 return DNSSEC_FAIL_NONSEC;
Simon Kelley5107ace2014-02-23 10:48:32 +00001638
1639 /* Finally, check that there's no seat of wildcard synthesis */
Simon Kelleyfbc52052014-12-23 15:46:08 +00001640 if (!wildname)
1641 {
1642 if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest)
Simon Kelleyb40f26c2015-12-17 11:57:26 +00001643 return 0;
Simon Kelleyfbc52052014-12-23 15:46:08 +00001644
1645 wildcard--;
1646 *wildcard = '*';
1647
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001648 if (dec_counter(validate_counter, NULL))
1649 return DNSSEC_FAIL_WORK;
1650
Simon Kelleyfbc52052014-12-23 15:46:08 +00001651 if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001652 return DNSSEC_FAIL_NONSEC;
Simon Kelleyfbc52052014-12-23 15:46:08 +00001653
Simon Kelleya969ba62018-01-20 23:08:38 +00001654 if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL, 1))
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001655 return DNSSEC_FAIL_NONSEC;
Simon Kelleyfbc52052014-12-23 15:46:08 +00001656 }
Simon Kelley5107ace2014-02-23 10:48:32 +00001657
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001658 return 0;
Simon Kelleyb40f26c2015-12-17 11:57:26 +00001659}
1660
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001661/* returns 0 on success, or DNSSEC_FAIL_* value on failure. */
1662static int prove_non_existence(struct dns_header *header, size_t plen, char *keyname, char *name, int qtype, int qclass,
1663 char *wildname, int *nons, int *nsec_ttl, int *validate_counter)
Simon Kelleyb40f26c2015-12-17 11:57:26 +00001664{
Simon Kelley4fe67442018-01-19 12:26:08 +00001665 static unsigned char **nsecset = NULL, **rrsig_labels = NULL;
1666 static int nsecset_sz = 0, rrsig_labels_sz = 0;
Simon Kelleyb40f26c2015-12-17 11:57:26 +00001667
1668 int type_found = 0;
Simon Kelley4fe67442018-01-19 12:26:08 +00001669 unsigned char *auth_start, *p = skip_questions(header, plen);
Simon Kelleyae7a3b92019-09-03 14:40:47 +01001670 int type, class, rdlen, i, nsecs_found;
1671 unsigned long ttl;
Simon Kelleyb40f26c2015-12-17 11:57:26 +00001672
1673 /* Move to NS section */
1674 if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen)))
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001675 return DNSSEC_FAIL_BADPACKET;
Simon Kelley4fe67442018-01-19 12:26:08 +00001676
1677 auth_start = p;
Simon Kelleyb40f26c2015-12-17 11:57:26 +00001678
Simon Kelleye24abf22019-09-03 22:48:39 +01001679 for (nsecs_found = 0, i = 0; i < ntohs(header->nscount); i++)
Simon Kelleyb40f26c2015-12-17 11:57:26 +00001680 {
1681 unsigned char *pstart = p;
1682
Simon Kelley4fe67442018-01-19 12:26:08 +00001683 if (!extract_name(header, plen, &p, daemon->workspacename, 1, 10))
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001684 return DNSSEC_FAIL_BADPACKET;
Simon Kelley4fe67442018-01-19 12:26:08 +00001685
Simon Kelleyb40f26c2015-12-17 11:57:26 +00001686 GETSHORT(type, p);
1687 GETSHORT(class, p);
Simon Kelleyfef2f1c2019-08-29 21:59:00 +01001688 GETLONG(ttl, p);
Simon Kelleyb40f26c2015-12-17 11:57:26 +00001689 GETSHORT(rdlen, p);
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001690
1691 if (!CHECK_LEN(header, p, plen, rdlen))
1692 return DNSSEC_FAIL_BADPACKET;
1693
Simon Kelleyb40f26c2015-12-17 11:57:26 +00001694 if (class == qclass && (type == T_NSEC || type == T_NSEC3))
1695 {
Simon Kelleyfef2f1c2019-08-29 21:59:00 +01001696 if (nsec_ttl)
Simon Kelleyae7a3b92019-09-03 14:40:47 +01001697 {
1698 /* Limit TTL with sig TTL */
1699 if (daemon->rr_status[ntohs(header->ancount) + i] < ttl)
1700 ttl = daemon->rr_status[ntohs(header->ancount) + i];
1701 *nsec_ttl = ttl;
1702 }
1703
Simon Kelleyb40f26c2015-12-17 11:57:26 +00001704 /* No mixed NSECing 'round here, thankyouverymuch */
1705 if (type_found != 0 && type_found != type)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001706 return DNSSEC_FAIL_NONSEC;
Simon Kelleyb40f26c2015-12-17 11:57:26 +00001707
1708 type_found = type;
1709
1710 if (!expand_workspace(&nsecset, &nsecset_sz, nsecs_found))
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001711 return DNSSEC_FAIL_BADPACKET;
Simon Kelleyb40f26c2015-12-17 11:57:26 +00001712
Simon Kelley4fe67442018-01-19 12:26:08 +00001713 if (type == T_NSEC)
1714 {
1715 /* If we're looking for NSECs, find the corresponding SIGs, to
1716 extract the labels value, which we need in case the NSECs
1717 are the result of wildcard expansion.
1718 Note that the NSEC may not have been validated yet
1719 so if there are multiple SIGs, make sure the label value
1720 is the same in all, to avoid be duped by a rogue one.
1721 If there are no SIGs, that's an error */
1722 unsigned char *p1 = auth_start;
1723 int res, j, rdlen1, type1, class1;
1724
1725 if (!expand_workspace(&rrsig_labels, &rrsig_labels_sz, nsecs_found))
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001726 return DNSSEC_FAIL_BADPACKET;
Simon Kelley4fe67442018-01-19 12:26:08 +00001727
1728 rrsig_labels[nsecs_found] = NULL;
1729
1730 for (j = ntohs(header->nscount); j != 0; j--)
1731 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001732 unsigned char *psav;
Simon Kelley4fe67442018-01-19 12:26:08 +00001733
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001734 if (!(res = extract_name(header, plen, &p1, daemon->workspacename, 0, 10)))
1735 return DNSSEC_FAIL_BADPACKET;
1736
Simon Kelley4fe67442018-01-19 12:26:08 +00001737 GETSHORT(type1, p1);
1738 GETSHORT(class1, p1);
1739 p1 += 4; /* TTL */
1740 GETSHORT(rdlen1, p1);
1741
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001742 psav = p1;
1743
Simon Kelley4fe67442018-01-19 12:26:08 +00001744 if (!CHECK_LEN(header, p1, plen, rdlen1))
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001745 return DNSSEC_FAIL_BADPACKET;
Simon Kelley4fe67442018-01-19 12:26:08 +00001746
1747 if (res == 1 && class1 == qclass && type1 == T_RRSIG)
1748 {
1749 int type_covered;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001750
Simon Kelleycd7df612018-01-20 00:10:55 +00001751 if (rdlen1 < 18)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001752 return DNSSEC_FAIL_BADPACKET; /* bad packet */
Simon Kelley4fe67442018-01-19 12:26:08 +00001753
1754 GETSHORT(type_covered, p1);
1755
1756 if (type_covered == T_NSEC)
1757 {
1758 p1++; /* algo */
1759
1760 /* labels field must be the same in every SIG we find. */
1761 if (!rrsig_labels[nsecs_found])
1762 rrsig_labels[nsecs_found] = p1;
1763 else if (*rrsig_labels[nsecs_found] != *p1) /* algo */
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001764 return DNSSEC_FAIL_NONSEC;
1765 }
Simon Kelley4fe67442018-01-19 12:26:08 +00001766 }
1767
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001768 p1 = psav + rdlen1;
Simon Kelley4fe67442018-01-19 12:26:08 +00001769 }
1770
1771 /* Must have found at least one sig. */
1772 if (!rrsig_labels[nsecs_found])
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001773 return DNSSEC_FAIL_NONSEC;
1774 }
1775 else if (type == T_NSEC3)
1776 {
1777 /* Decode the packet structure enough to check that rdlen is big enough
1778 to contain everything other than the type bitmap.
1779 (packet checked to be long enough to contain rdlen above)
1780 We don't need to do any further length checks in check_nes3_coverage()
1781 or prove_non_existence_nsec3() */
1782
1783 int salt_len, hash_len;
1784 unsigned char *psav = p;
1785
1786 if (rdlen < 5)
1787 return DNSSEC_FAIL_BADPACKET;
1788
1789 p += 4; /* algo, flags, iterations */
1790 salt_len = *p++; /* salt_len */
1791 if (rdlen < (6 + salt_len))
1792 return DNSSEC_FAIL_BADPACKET; /* check up to hash_length */
1793
1794 p += salt_len; /* salt */
1795 hash_len = *p++;
1796 if (rdlen < (6 + salt_len + hash_len))
1797 return DNSSEC_FAIL_BADPACKET; /* check to end of next hashed name */
1798
1799 p = psav;
Simon Kelley4fe67442018-01-19 12:26:08 +00001800 }
1801
1802 nsecset[nsecs_found++] = pstart;
Simon Kelleyb40f26c2015-12-17 11:57:26 +00001803 }
1804
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001805 p += rdlen;
Simon Kelleyb40f26c2015-12-17 11:57:26 +00001806 }
1807
1808 if (type_found == T_NSEC)
Simon Kelley4fe67442018-01-19 12:26:08 +00001809 return prove_non_existence_nsec(header, plen, nsecset, rrsig_labels, nsecs_found, daemon->workspacename, keyname, name, qtype, nons);
Simon Kelleyd67ecac2015-12-20 20:44:23 +00001810 else if (type_found == T_NSEC3)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001811 return prove_non_existence_nsec3(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, wildname, nons, validate_counter);
Simon Kelleyd67ecac2015-12-20 20:44:23 +00001812 else
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001813 return DNSSEC_FAIL_NONSEC;
Simon Kelley5107ace2014-02-23 10:48:32 +00001814}
Simon Kelley9a31b682015-12-15 10:20:39 +00001815
1816/* Check signing status of name.
1817 returns:
Simon Kelley3b799c82015-12-17 16:58:04 +00001818 STAT_SECURE zone is signed.
1819 STAT_INSECURE zone proved unsigned.
1820 STAT_NEED_DS require DS record of name returned in keyname.
1821 STAT_NEED_KEY require DNSKEY record of name returned in keyname.
Simon Kelley9a31b682015-12-15 10:20:39 +00001822 name returned unaltered.
1823*/
1824static int zone_status(char *name, int class, char *keyname, time_t now)
Giovanni Bajoe292e932012-04-22 14:32:02 +02001825{
Simon Kelleya63b8b82016-01-12 11:28:58 +00001826 int name_start = strlen(name); /* for when TA is root */
Simon Kelley9a31b682015-12-15 10:20:39 +00001827 struct crec *crecp;
1828 char *p;
Simon Kelleya63b8b82016-01-12 11:28:58 +00001829
1830 /* First, work towards the root, looking for a trust anchor.
1831 This can either be one configured, or one previously cached.
1832 We can assume, if we don't find one first, that there is
1833 a trust anchor at the root. */
1834 for (p = name; p; p = strchr(p, '.'))
1835 {
1836 if (*p == '.')
1837 p++;
1838
1839 if (cache_find_by_name(NULL, p, now, F_DS))
1840 {
1841 name_start = p - name;
1842 break;
1843 }
1844 }
Simon Kelley367341f2016-01-12 15:58:23 +00001845
Simon Kelleya63b8b82016-01-12 11:28:58 +00001846 /* Now work away from the trust anchor */
Simon Kelley9a31b682015-12-15 10:20:39 +00001847 while (1)
1848 {
1849 strcpy(keyname, &name[name_start]);
1850
1851 if (!(crecp = cache_find_by_name(NULL, keyname, now, F_DS)))
1852 return STAT_NEED_DS;
Simon Kelleyd67ecac2015-12-20 20:44:23 +00001853
Josh Soref730c6742017-02-06 16:14:04 +00001854 /* F_DNSSECOK misused in DS cache records to non-existence of NS record.
Simon Kelleyd67ecac2015-12-20 20:44:23 +00001855 F_NEG && !F_DNSSECOK implies that we've proved there's no DS record here,
1856 but that's because there's no NS record either, ie this isn't the start
1857 of a zone. We only prove that the DNS tree below a node is unsigned when
1858 we prove that we're at a zone cut AND there's no DS record. */
1859 if (crecp->flags & F_NEG)
1860 {
1861 if (crecp->flags & F_DNSSECOK)
1862 return STAT_INSECURE; /* proved no DS here */
1863 }
Simon Kelley9a31b682015-12-15 10:20:39 +00001864 else
Simon Kelley2dbba342015-12-16 13:41:58 +00001865 {
Simon Kelleyd67ecac2015-12-20 20:44:23 +00001866 /* If all the DS records have digest and/or sig algos we don't support,
1867 then the zone is insecure. Note that if an algo
1868 appears in the DS, then RRSIGs for that algo MUST
1869 exist for each RRset: 4035 para 2.2 So if we find
1870 a DS here with digest and sig we can do, we're entitled
1871 to assume we can validate the zone and if we can't later,
1872 because an RRSIG is missing we return BOGUS.
1873 */
Simon Kelley2dbba342015-12-16 13:41:58 +00001874 do
1875 {
Simon Kelleyd67ecac2015-12-20 20:44:23 +00001876 if (crecp->uid == (unsigned int)class &&
Simon Kelleycc921df2019-01-02 22:48:59 +00001877 ds_digest_name(crecp->addr.ds.digest) &&
1878 algo_digest_name(crecp->addr.ds.algo))
Simon Kelleya86fdf42015-12-20 21:19:20 +00001879 break;
Simon Kelley2dbba342015-12-16 13:41:58 +00001880 }
1881 while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS)));
Simon Kelley2dbba342015-12-16 13:41:58 +00001882
Simon Kelleya86fdf42015-12-20 21:19:20 +00001883 if (!crecp)
Simon Kelleyd67ecac2015-12-20 20:44:23 +00001884 return STAT_INSECURE;
Simon Kelley2dbba342015-12-16 13:41:58 +00001885 }
1886
Simon Kelley9a31b682015-12-15 10:20:39 +00001887 if (name_start == 0)
1888 break;
1889
1890 for (p = &name[name_start-2]; (*p != '.') && (p != name); p--);
1891
1892 if (p != name)
1893 p++;
1894
1895 name_start = p - name;
1896 }
1897
1898 return STAT_SECURE;
1899}
1900
1901/* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3)
1902 Return code:
1903 STAT_SECURE if it validates.
1904 STAT_INSECURE at least one RRset not validated, because in unsigned zone.
1905 STAT_BOGUS signature is wrong, bad packet, no validation where there should be.
1906 STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname, class in *class)
Simon Kelleya6004d72017-10-25 17:48:19 +01001907 STAT_NEED_DS need DS to complete validation (name is returned in keyname)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001908 STAT_ABANDONED resource exhaustion.
Simon Kelleya6004d72017-10-25 17:48:19 +01001909
Simon Kelley373e9172017-12-01 22:40:56 +00001910 daemon->rr_status points to a char array which corressponds to the RRs in the
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001911 answer and auth sections. This is set to >1 for each RR which is validated, and 0 for any which aren't.
Simon Kelleyfef2f1c2019-08-29 21:59:00 +01001912
1913 When validating replies to DS records, we're only interested in the NSEC{3} RRs in the auth section.
1914 Other RRs in that section missing sigs will not cause am INSECURE reply. We determine this mode
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001915 if the nons argument is non-NULL.
Simon Kelley9a31b682015-12-15 10:20:39 +00001916*/
1917int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname,
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001918 int *class, int check_unsigned, int *neganswer, int *nons, int *nsec_ttl, int *validate_counter)
Simon Kelley9a31b682015-12-15 10:20:39 +00001919{
1920 static unsigned char **targets = NULL;
1921 static int target_sz = 0;
1922
Simon Kelleyb40f26c2015-12-17 11:57:26 +00001923 unsigned char *ans_start, *p1, *p2;
Simon Kelleya6004d72017-10-25 17:48:19 +01001924 int type1, class1, rdlen1 = 0, type2, class2, rdlen2, qclass, qtype, targetidx;
Simon Kelley91421cb2018-10-18 19:21:55 +01001925 int i, j, rc = STAT_INSECURE;
Simon Kelleya6004d72017-10-25 17:48:19 +01001926 int secure = STAT_SECURE;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001927 int rc_nsec;
Simon Kelley373e9172017-12-01 22:40:56 +00001928 /* extend rr_status if necessary */
Simon Kelleyae7a3b92019-09-03 14:40:47 +01001929 if (daemon->rr_status_sz < ntohs(header->ancount) + ntohs(header->nscount))
Simon Kelley373e9172017-12-01 22:40:56 +00001930 {
Simon Kelleyae7a3b92019-09-03 14:40:47 +01001931 unsigned long *new = whine_malloc(sizeof(*daemon->rr_status) * (ntohs(header->ancount) + ntohs(header->nscount) + 64));
Simon Kelley373e9172017-12-01 22:40:56 +00001932
1933 if (!new)
1934 return STAT_BOGUS;
1935
1936 free(daemon->rr_status);
1937 daemon->rr_status = new;
Simon Kelleyae7a3b92019-09-03 14:40:47 +01001938 daemon->rr_status_sz = ntohs(header->ancount) + ntohs(header->nscount) + 64;
Simon Kelley373e9172017-12-01 22:40:56 +00001939 }
1940
Simon Kelleyae7a3b92019-09-03 14:40:47 +01001941 memset(daemon->rr_status, 0, sizeof(*daemon->rr_status) * daemon->rr_status_sz);
Simon Kelleya6004d72017-10-25 17:48:19 +01001942
Simon Kelley00a5b5d2014-02-28 18:10:55 +00001943 if (neganswer)
1944 *neganswer = 0;
1945
Simon Kelley87070192014-03-01 20:48:24 +00001946 if (RCODE(header) == SERVFAIL || ntohs(header->qdcount) != 1)
Simon Kelleye3ec15a2014-02-13 16:56:30 +00001947 return STAT_BOGUS;
1948
Simon Kelley87070192014-03-01 20:48:24 +00001949 if (RCODE(header) != NXDOMAIN && RCODE(header) != NOERROR)
Simon Kelley72ae2f32014-01-19 09:54:16 +00001950 return STAT_INSECURE;
Simon Kelley00a5b5d2014-02-28 18:10:55 +00001951
Simon Kelley9a31b682015-12-15 10:20:39 +00001952 p1 = (unsigned char *)(header+1);
Simon Kelleyc5f4ec72014-01-20 22:37:55 +00001953
Simon Kelley9a31b682015-12-15 10:20:39 +00001954 /* Find all the targets we're looking for answers to.
1955 The zeroth array element is for the query, subsequent ones
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001956 for CNAME targets, unless the query is for a CNAME or ANY. */
Simon Kelley9a31b682015-12-15 10:20:39 +00001957
1958 if (!expand_workspace(&targets, &target_sz, 0))
1959 return STAT_BOGUS;
1960
1961 targets[0] = p1;
1962 targetidx = 1;
1963
Simon Kelley394ff492015-03-29 22:17:14 +01001964 if (!extract_name(header, plen, &p1, name, 1, 4))
Simon Kelley87070192014-03-01 20:48:24 +00001965 return STAT_BOGUS;
Simon Kelley9a31b682015-12-15 10:20:39 +00001966
Simon Kelley00a5b5d2014-02-28 18:10:55 +00001967 GETSHORT(qtype, p1);
1968 GETSHORT(qclass, p1);
1969 ans_start = p1;
1970
Simon Kelley9a31b682015-12-15 10:20:39 +00001971 /* Can't validate an RRSIG query */
Simon Kelley00a5b5d2014-02-28 18:10:55 +00001972 if (qtype == T_RRSIG)
1973 return STAT_INSECURE;
Simon Kelleye3ec6f02015-06-12 21:39:11 +01001974
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001975 if (qtype != T_CNAME && qtype != T_ANY)
Simon Kelley9a31b682015-12-15 10:20:39 +00001976 for (j = ntohs(header->ancount); j != 0; j--)
1977 {
1978 if (!(p1 = skip_name(p1, header, plen, 10)))
1979 return STAT_BOGUS; /* bad packet */
1980
1981 GETSHORT(type2, p1);
1982 p1 += 6; /* class, TTL */
1983 GETSHORT(rdlen2, p1);
1984
1985 if (type2 == T_CNAME)
1986 {
1987 if (!expand_workspace(&targets, &target_sz, targetidx))
1988 return STAT_BOGUS;
1989
1990 targets[targetidx++] = p1; /* pointer to target name */
1991 }
1992
1993 if (!ADD_RDLEN(header, p1, plen, rdlen2))
1994 return STAT_BOGUS;
1995 }
1996
Simon Kelley0fc2f312014-01-08 10:26:58 +00001997 for (p1 = ans_start, i = 0; i < ntohs(header->ancount) + ntohs(header->nscount); i++)
Giovanni Bajoe292e932012-04-22 14:32:02 +02001998 {
Simon Kelley91421cb2018-10-18 19:21:55 +01001999 if (i != 0 && !ADD_RDLEN(header, p1, plen, rdlen1))
2000 return STAT_BOGUS;
2001
2002 if (!extract_name(header, plen, &p1, name, 1, 10))
Simon Kelley87070192014-03-01 20:48:24 +00002003 return STAT_BOGUS; /* bad packet */
Simon Kelley0fc2f312014-01-08 10:26:58 +00002004
2005 GETSHORT(type1, p1);
2006 GETSHORT(class1, p1);
2007 p1 += 4; /* TTL */
2008 GETSHORT(rdlen1, p1);
2009
2010 /* Don't try and validate RRSIGs! */
Simon Kelleya6004d72017-10-25 17:48:19 +01002011 if (type1 == T_RRSIG)
2012 continue;
2013
2014 /* Check if we've done this RRset already */
2015 for (p2 = ans_start, j = 0; j < i; j++)
Simon Kelley0fc2f312014-01-08 10:26:58 +00002016 {
Simon Kelleya6004d72017-10-25 17:48:19 +01002017 if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
2018 return STAT_BOGUS; /* bad packet */
Simon Kelley0fc2f312014-01-08 10:26:58 +00002019
Simon Kelleya6004d72017-10-25 17:48:19 +01002020 GETSHORT(type2, p2);
2021 GETSHORT(class2, p2);
2022 p2 += 4; /* TTL */
2023 GETSHORT(rdlen2, p2);
2024
2025 if (type2 == type1 && class2 == class1 && rc == 1)
2026 break; /* Done it before: name, type, class all match. */
2027
2028 if (!ADD_RDLEN(header, p2, plen, rdlen2))
2029 return STAT_BOGUS;
2030 }
2031
Simon Kelleyae7a3b92019-09-03 14:40:47 +01002032 /* Done already: copy the validation status */
Simon Kelleya6004d72017-10-25 17:48:19 +01002033 if (j != i)
Simon Kelleyae7a3b92019-09-03 14:40:47 +01002034 daemon->rr_status[i] = daemon->rr_status[j];
Simon Kelleya6004d72017-10-25 17:48:19 +01002035 else
2036 {
Simon Kelley0fc2f312014-01-08 10:26:58 +00002037 /* Not done, validate now */
Simon Kelleya6004d72017-10-25 17:48:19 +01002038 int sigcnt, rrcnt;
2039 char *wildname;
2040
2041 if (!explore_rrset(header, plen, class1, type1, name, keyname, &sigcnt, &rrcnt))
2042 return STAT_BOGUS;
2043
Ville Skyttäfaaf3062018-01-14 17:32:52 +00002044 /* No signatures for RRset. We can be configured to assume this is OK and return an INSECURE result. */
Simon Kelleya6004d72017-10-25 17:48:19 +01002045 if (sigcnt == 0)
Simon Kelley0fc2f312014-01-08 10:26:58 +00002046 {
Simon Kelley69a04772019-09-03 16:49:02 +01002047 /* NSEC and NSEC3 records must be signed. We make this assumption elsewhere. */
2048 if (type1 == T_NSEC || type1 == T_NSEC3)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07002049 return STAT_BOGUS | DNSSEC_FAIL_NOSIG;
Simon Kelley69a04772019-09-03 16:49:02 +01002050 else if (nons && i >= ntohs(header->ancount))
2051 /* If we're validating a DS reply, rather than looking for the value of AD bit,
2052 we only care that NSEC and NSEC3 RRs in the auth section are signed.
2053 Return SECURE even if others (SOA....) are not. */
Simon Kelleyfef2f1c2019-08-29 21:59:00 +01002054 rc = STAT_SECURE;
2055 else
Simon Kelley51ea3ca2014-01-22 19:31:38 +00002056 {
Simon Kelley69a04772019-09-03 16:49:02 +01002057 /* unsigned RRsets in auth section are not BOGUS, but do make reply insecure. */
2058 if (check_unsigned && i < ntohs(header->ancount))
Simon Kelleyfef2f1c2019-08-29 21:59:00 +01002059 {
2060 rc = zone_status(name, class1, keyname, now);
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07002061 if (STAT_ISEQUAL(rc, STAT_SECURE))
2062 rc = STAT_BOGUS | DNSSEC_FAIL_NOSIG;
2063
Simon Kelleyfef2f1c2019-08-29 21:59:00 +01002064 if (class)
2065 *class = class1; /* Class for NEED_DS or NEED_KEY */
2066 }
2067 else
2068 rc = STAT_INSECURE;
2069
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07002070 if (!STAT_ISEQUAL(rc, STAT_INSECURE))
Simon Kelleyfef2f1c2019-08-29 21:59:00 +01002071 return rc;
Simon Kelley51ea3ca2014-01-22 19:31:38 +00002072 }
Simon Kelleya6004d72017-10-25 17:48:19 +01002073 }
2074 else
2075 {
Simon Kelley9a31b682015-12-15 10:20:39 +00002076 /* explore_rrset() gives us key name from sigs in keyname.
2077 Can't overwrite name here. */
2078 strcpy(daemon->workspacename, keyname);
2079 rc = zone_status(daemon->workspacename, class1, keyname, now);
Simon Kelleya6004d72017-10-25 17:48:19 +01002080
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07002081 if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS))
Simon Kelley51ea3ca2014-01-22 19:31:38 +00002082 {
Simon Kelley9a31b682015-12-15 10:20:39 +00002083 if (class)
Simon Kelley3b799c82015-12-17 16:58:04 +00002084 *class = class1; /* Class for NEED_DS or NEED_KEY */
Simon Kelley9a31b682015-12-15 10:20:39 +00002085 return rc;
Simon Kelleya6004d72017-10-25 17:48:19 +01002086 }
Simon Kelley9a31b682015-12-15 10:20:39 +00002087
Simon Kelleya6004d72017-10-25 17:48:19 +01002088 /* Zone is insecure, don't need to validate RRset */
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07002089 if (STAT_ISEQUAL(rc, STAT_SECURE))
Simon Kelley9a31b682015-12-15 10:20:39 +00002090 {
Simon Kelleyae7a3b92019-09-03 14:40:47 +01002091 unsigned long sig_ttl;
Simon Kelleya6004d72017-10-25 17:48:19 +01002092 rc = validate_rrset(now, header, plen, class1, type1, sigcnt,
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07002093 rrcnt, name, keyname, &wildname, NULL, 0, 0, 0, &sig_ttl, validate_counter);
Simon Kelleya6004d72017-10-25 17:48:19 +01002094
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07002095 if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS) || STAT_ISEQUAL(rc, STAT_ABANDONED))
Simon Kelleya6004d72017-10-25 17:48:19 +01002096 {
2097 if (class)
2098 *class = class1; /* Class for DS or DNSKEY */
2099 return rc;
2100 }
2101
Simon Kelley9a31b682015-12-15 10:20:39 +00002102 /* rc is now STAT_SECURE or STAT_SECURE_WILDCARD */
Simon Kelleya6004d72017-10-25 17:48:19 +01002103
2104 /* Note that RR is validated */
Simon Kelleyae7a3b92019-09-03 14:40:47 +01002105 daemon->rr_status[i] = sig_ttl;
Simon Kelleya6004d72017-10-25 17:48:19 +01002106
Simon Kelley9a31b682015-12-15 10:20:39 +00002107 /* Note if we've validated either the answer to the question
2108 or the target of a CNAME. Any not noted will need NSEC or
2109 to be in unsigned space. */
Simon Kelley9a31b682015-12-15 10:20:39 +00002110 for (j = 0; j <targetidx; j++)
2111 if ((p2 = targets[j]))
2112 {
Simon Kelleya6004d72017-10-25 17:48:19 +01002113 int rc1;
2114 if (!(rc1 = extract_name(header, plen, &p2, name, 0, 10)))
Simon Kelley9a31b682015-12-15 10:20:39 +00002115 return STAT_BOGUS; /* bad packet */
2116
Simon Kelleya6004d72017-10-25 17:48:19 +01002117 if (class1 == qclass && rc1 == 1 && (type1 == T_CNAME || type1 == qtype || qtype == T_ANY ))
Simon Kelley9a31b682015-12-15 10:20:39 +00002118 targets[j] = NULL;
2119 }
Simon Kelleya6004d72017-10-25 17:48:19 +01002120
2121 /* An attacker replay a wildcard answer with a different
2122 answer and overlay a genuine RR. To prove this
2123 hasn't happened, the answer must prove that
2124 the genuine record doesn't exist. Check that here.
2125 Note that we may not yet have validated the NSEC/NSEC3 RRsets.
2126 That's not a problem since if the RRsets later fail
2127 we'll return BOGUS then. */
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07002128 if (STAT_ISEQUAL(rc, STAT_SECURE_WILDCARD) &&
2129 ((rc_nsec = prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL, NULL, validate_counter))) != 0)
2130 return (rc_nsec & DNSSEC_FAIL_WORK) ? STAT_ABANDONED : (STAT_BOGUS | rc_nsec);
Simon Kelleya6004d72017-10-25 17:48:19 +01002131
2132 rc = STAT_SECURE;
Simon Kelley51ea3ca2014-01-22 19:31:38 +00002133 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00002134 }
2135 }
2136
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07002137 if (STAT_ISEQUAL(rc, STAT_INSECURE))
Simon Kelleya6004d72017-10-25 17:48:19 +01002138 secure = STAT_INSECURE;
Giovanni Bajoe292e932012-04-22 14:32:02 +02002139 }
2140
Simon Kelley9a31b682015-12-15 10:20:39 +00002141 /* OK, all the RRsets validate, now see if we have a missing answer or CNAME target. */
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07002142 for (j = 0; j <targetidx; j++)
2143 if ((p2 = targets[j]))
2144 {
2145 if (neganswer)
2146 *neganswer = 1;
2147
2148 if (!extract_name(header, plen, &p2, name, 1, 10))
2149 return STAT_BOGUS; /* bad packet */
2150
2151 /* NXDOMAIN or NODATA reply, unanswered question is (name, qclass, qtype) */
2152
2153 /* For anything other than a DS record, this situation is OK if either
2154 the answer is in an unsigned zone, or there's a NSEC records. */
2155 if ((rc_nsec = prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons, nsec_ttl, validate_counter)) != 0)
2156 {
2157 if (rc_nsec & DNSSEC_FAIL_WORK)
2158 return STAT_ABANDONED;
2159
2160 /* Empty DS without NSECS */
2161 if (qtype == T_DS)
2162 return STAT_BOGUS | rc_nsec;
2163
2164 if ((rc_nsec & (DNSSEC_FAIL_NONSEC | DNSSEC_FAIL_NSEC3_ITERS)) &&
2165 !STAT_ISEQUAL((rc = zone_status(name, qclass, keyname, now)), STAT_SECURE))
2166 {
2167 if (class)
2168 *class = qclass; /* Class for NEED_DS or NEED_KEY */
2169 return rc;
2170 }
2171
2172 return STAT_BOGUS | rc_nsec; /* signed zone, no NSECs */
2173 }
2174 }
Simon Kelley9a31b682015-12-15 10:20:39 +00002175
Simon Kelleya6004d72017-10-25 17:48:19 +01002176 return secure;
Simon Kelley00a5b5d2014-02-28 18:10:55 +00002177}
2178
2179
Giovanni Bajo3471f182012-04-25 17:49:16 +02002180/* Compute keytag (checksum to quickly index a key). See RFC4034 */
Simon Kelley0fc2f312014-01-08 10:26:58 +00002181int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen)
Giovanni Bajo3471f182012-04-25 17:49:16 +02002182{
Giovanni Bajo75ffc9b2012-05-02 19:58:06 +02002183 if (alg == 1)
2184 {
2185 /* Algorithm 1 (RSAMD5) has a different (older) keytag calculation algorithm.
2186 See RFC4034, Appendix B.1 */
Simon Kelley0fc2f312014-01-08 10:26:58 +00002187 return key[keylen-4] * 256 + key[keylen-3];
Giovanni Bajo75ffc9b2012-05-02 19:58:06 +02002188 }
2189 else
2190 {
Simon Kelley1633e302014-02-10 16:42:46 +00002191 unsigned long ac = flags + 0x300 + alg;
Giovanni Bajo75ffc9b2012-05-02 19:58:06 +02002192 int i;
Giovanni Bajo3471f182012-04-25 17:49:16 +02002193
Simon Kelley0fc2f312014-01-08 10:26:58 +00002194 for (i = 0; i < keylen; ++i)
2195 ac += (i & 1) ? key[i] : key[i] << 8;
Simon Kelley1633e302014-02-10 16:42:46 +00002196
Simon Kelley0fc2f312014-01-08 10:26:58 +00002197 ac += (ac >> 16) & 0xffff;
2198 return ac & 0xffff;
Giovanni Bajo75ffc9b2012-05-02 19:58:06 +02002199 }
Giovanni Bajo3471f182012-04-25 17:49:16 +02002200}
2201
Simon Kelley33702ab2015-12-28 23:17:15 +00002202size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class,
Simon Kelleye1791f32018-10-06 23:23:23 +01002203 int type, int edns_pktsz)
Simon Kelley5f8e58f2014-01-09 17:31:19 +00002204{
2205 unsigned char *p;
Simon Kelleya77cec82015-05-08 16:25:38 +01002206 size_t ret;
Giovanni Bajo0304d282012-05-02 03:29:52 +02002207
Simon Kelley5f8e58f2014-01-09 17:31:19 +00002208 header->qdcount = htons(1);
2209 header->ancount = htons(0);
2210 header->nscount = htons(0);
2211 header->arcount = htons(0);
2212
2213 header->hb3 = HB3_RD;
2214 SET_OPCODE(header, QUERY);
Simon Kelley5b3bf922014-01-25 17:03:07 +00002215 /* For debugging, set Checking Disabled, otherwise, have the upstream check too,
2216 this allows it to select auth servers when one is returning bad data. */
2217 header->hb4 = option_bool(OPT_DNSSEC_DEBUG) ? HB4_CD : 0;
Simon Kelley5f8e58f2014-01-09 17:31:19 +00002218
2219 /* ID filled in later */
2220
2221 p = (unsigned char *)(header+1);
2222
Simon Kelley0549c732017-09-25 18:17:11 +01002223 p = do_rfc1035_name(p, name, NULL);
Simon Kelley5f8e58f2014-01-09 17:31:19 +00002224 *p++ = 0;
2225 PUTSHORT(type, p);
2226 PUTSHORT(class, p);
2227
Simon Kelleya77cec82015-05-08 16:25:38 +01002228 ret = add_do_bit(header, p - (unsigned char *)header, end);
2229
Simon Kelley5bb88f02015-12-21 16:23:47 +00002230 if (find_pseudoheader(header, ret, NULL, &p, NULL, NULL))
Simon Kelleya77cec82015-05-08 16:25:38 +01002231 PUTSHORT(edns_pktsz, p);
2232
2233 return ret;
Simon Kelley5f8e58f2014-01-09 17:31:19 +00002234}
Simon Kelley8a9be9e2014-01-25 23:17:21 +00002235
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07002236int errflags_to_ede(int status)
2237{
2238 /* We can end up with more than one flag set for some errors,
2239 so this encodes a rough priority so the (eg) No sig is reported
2240 before no-unexpired-sig. */
2241
2242 if (status & DNSSEC_FAIL_NYV)
2243 return EDE_SIG_NYV;
2244 else if (status & DNSSEC_FAIL_EXP)
2245 return EDE_SIG_EXP;
2246 else if (status & DNSSEC_FAIL_NOKEYSUP)
2247 return EDE_USUPDNSKEY;
2248 else if (status & DNSSEC_FAIL_NOZONE)
2249 return EDE_NO_ZONEKEY;
2250 else if (status & DNSSEC_FAIL_NOKEY)
2251 return EDE_NO_DNSKEY;
2252 else if (status & DNSSEC_FAIL_NODSSUP)
2253 return EDE_USUPDS;
2254 else if (status & DNSSEC_FAIL_NSEC3_ITERS)
2255 return EDE_UNS_NS3_ITER;
2256 else if (status & DNSSEC_FAIL_NONSEC)
2257 return EDE_NO_NSEC;
2258 else if (status & DNSSEC_FAIL_INDET)
2259 return EDE_DNSSEC_IND;
2260 else if (status & DNSSEC_FAIL_NOSIG)
2261 return EDE_NO_RRSIG;
2262 else
2263 return EDE_UNSET;
2264}
Simon Kelley0fc2f312014-01-08 10:26:58 +00002265#endif /* HAVE_DNSSEC */