]> git.ipfire.org Git - people/ms/dnsmasq.git/blame - src/dnssec.c
DSA-NSEC3-SHA1 is an alias of DSA for signature verification.
[people/ms/dnsmasq.git] / src / dnssec.c
CommitLineData
e292e93d
GB
1
2#include "dnsmasq.h"
d322de06 3#include "dnssec-crypto.h"
e292e93d
GB
4#include <assert.h>
5
6299ffbe
GB
6/* Maximum length in octects of a domain name, in wire format */
7#define MAXCDNAME 256
8
e292e93d
GB
9#define SERIAL_UNDEF -100
10#define SERIAL_EQ 0
11#define SERIAL_LT -1
12#define SERIAL_GT 1
13
e292e93d
GB
14/* Implement RFC1982 wrapped compare for 32-bit numbers */
15static int serial_compare_32(unsigned long s1, unsigned long s2)
16{
17 if (s1 == s2)
18 return SERIAL_EQ;
19
20 if ((s1 < s2 && (s2 - s1) < (1UL<<31)) ||
21 (s1 > s2 && (s1 - s2) > (1UL<<31)))
22 return SERIAL_LT;
23 if ((s1 < s2 && (s2 - s1) > (1UL<<31)) ||
24 (s1 > s2 && (s1 - s2) < (1UL<<31)))
25 return SERIAL_GT;
26 return SERIAL_UNDEF;
27}
28
29/* Extract a DNS name from wire format, without handling compression. This is
30 faster than extract_name() and does not require access to the full dns
31 packet. */
32static int extract_name_no_compression(unsigned char *rr, int maxlen, char *buf)
33{
34 unsigned char *start=rr, *end = rr+maxlen;
35 int count;
36
37 while (rr < end && *rr != 0)
38 {
39 count = *rr++;
6445c8ed 40 while (count-- > 0 && rr < end)
e292e93d
GB
41 {
42 *buf = *rr++;
b98f7715
GB
43 if (!isascii(*buf) || iscntrl(*buf) || *buf == '.')
44 return 0;
e292e93d
GB
45 if (*buf >= 'A' && *buf <= 'Z')
46 *buf += 'a' - 'A';
47 buf++;
79333a24 48 }
e292e93d
GB
49 *buf++ = '.';
50 }
dd090561 51 /* Remove trailing dot (if any) */
2ef843dd
GB
52 if (rr != start)
53 *(--buf) = 0;
e292e93d
GB
54 if (rr == end)
55 return 0;
dd090561 56 /* Trailing \0 in source data must be consumed */
79333a24 57 return rr-start+1;
e292e93d
GB
58}
59
6299ffbe
GB
60/* process_domain_name() - do operations with domain names in canonicalized wire format.
61 *
62 * Handling domain names in wire format can be done with buffers as large as MAXCDNAME (256),
63 * while the representation format (as created by, eg., extract_name) requires MAXDNAME (1024).
64 *
65 * With "canonicalized wire format", we mean the standard DNS wire format, eg:
66 *
67 * <3>www<7>example<3>org<0>
68 *
69 * with all ÅSCII letters converted to lowercase, and no wire-level compression.
70 *
71 * The function works with two different buffers:
72 * - Input buffer: 'rdata' is a pointer to the actual wire data, and 'rdlen' is
73 * the total length till the end of the rdata or DNS packet section. Both
74 * variables are updated after processing the domain name, so that rdata points
75 * after it, and rdlen is decreased by the amount of the processed octects.
76 * - Output buffer: 'out' points to it. In some cases, this buffer can be prefilled
77 * and used as additional input (see below).
78 *
79 * The argument "action" decides what to do with the submitted domain name:
80 *
81 * PDN_EXTRACT:
82 * Extract the domain name from input buffer into the output buffer, possibly uncompressing it.
83 * Return the length of the domain name in the output buffer in octects, or zero if error.
84 *
85 * PDN_COMPARE:
86 * Compare the domain name in the input buffer and the one in the output buffer (ignoring
87 * differences in compression). Returns 0 in case of error, a positive number
88 * if they are equal, or a negative number if they are different. This function always
89 * consumes the whole name in the input buffer (there is no early exit).
90 *
91 * PDN_ORDER:
92 * Order between the domain name in the input buffer and the domain name in the output buffer.
93 * Returns 0 if the names are equal, 1 if input > output, or -1 if input < output. This
94 * function early-exits when it finds a difference, so rdata might not be fully updated.
95 *
96 * Notice: because of compression, rdata/rdlen might be updated with a different quantity than
97 * the returned number of octects. For instance, if we extract a compressed domain name, rdata/rdlen
98 * might be updated only by 2 bytes (that is, rdata is incresed by 2, and rdlen decreased by 2),
99 * because it then reuses existing data elsewhere in the DNS packet, while the return value might be
100 * larger, reflecting the total number of octects composing the domain name.
101 *
102 */
ec2962ea
GB
103#define PWN_EXTRACT 0
104#define PWN_COMPARE 1
105#define PWN_ORDER 2
6299ffbe
GB
106static int process_domain_name(struct dns_header *header, size_t pktlen,
107 unsigned char** rdata, size_t* rdlen,
108 unsigned char *out, int action)
109{
110 int hops = 0, total = 0, i;
111 unsigned char label_type;
112 unsigned char *end = (unsigned char *)header + pktlen;
113 unsigned char count; unsigned char *p = *rdata;
114 int nonequal = 0;
115
116#define PROCESS(ch) \
117 do { \
118 if (action == PWN_EXTRACT) \
119 *out++ = ch; \
120 else if (action == PWN_COMPARE) \
121 { \
122 if (*out++ != ch) \
123 nonequal = 1; \
124 } \
125 else if (action == PWN_ORDER) \
126 { \
127 char _ch = *out++; \
128 if (ch < _ch) \
129 return -1; \
130 else if (_ch > ch) \
131 return 1; \
132 } \
133 } while (0)
134
135 while (1)
136 {
137 if (p >= end)
138 return 0;
139 if (!(count = *p++))
140 break;
141 label_type = count & 0xC0;
142 if (label_type == 0xC0)
143 {
0db0e0c2 144 int l2;
6299ffbe
GB
145 if (p >= end)
146 return 0;
0db0e0c2 147 l2 = *p++;
6299ffbe
GB
148 if (hops == 0)
149 {
150 if (p - *rdata > *rdlen)
151 return 0;
152 *rdlen -= p - *rdata;
153 *rdata = p;
154 }
155 if (++hops == 256)
156 return 0;
0db0e0c2 157 p = (unsigned char*)header + (count & 0x3F) * 256 + l2;
6299ffbe
GB
158 }
159 else if (label_type == 0x00)
160 {
161 if (p+count-1 >= end)
162 return 0;
163 total += count+1;
164 if (total >= MAXCDNAME)
165 return 0;
166 PROCESS(count);
167 for (i = 0; i < count; ++i)
168 {
169 unsigned char ch = *p++;
170 if (ch >= 'A' && ch <= 'Z')
171 ch += 'a' - 'A';
172 PROCESS(ch);
173 }
174 }
175 else
176 return 0; /* unsupported label_type */
177 }
178
179 if (hops == 0)
180 {
181 if (p - *rdata > *rdlen)
182 return 0;
183 *rdlen -= p - *rdata;
184 *rdata = p;
185 }
186 ++total;
187 if (total >= MAXCDNAME)
188 return 0;
189 PROCESS(0);
190
191 /* If we arrived here without early-exit, they're equal */
192 if (action == PWN_ORDER)
193 return 0;
194 return nonequal ? -total : total;
195
196 #undef PROCESS
197}
198
199
200/* RDATA meta-description.
201 *
202 * RFC4034 §6.2 introduces the concept of a "canonical form of a RR". This canonical
203 * form is used in two important points within the DNSSEC protocol/algorithm:
204 *
205 * 1) When computing the hash for verifying the RRSIG signature, we need to do it on
206 * the canonical form.
207 * 2) When ordering a RRset in canonical order (§6.3), we need to lexicographically sort
208 * the RRs in canonical form.
209 *
210 * The canonical form of a RR is specifically tricky because it also affects the RDATA,
211 * which is different for each RR type; in fact, RFC4034 says that "domain names in
212 * RDATA must be canonicalized" (= uncompressed and lower-cased).
213 *
214 * To handle this correctly, we then need a way to describe how the RDATA section is
215 * composed for each RR type; we don't need to describe every field, but just to specify
216 * where domain names are. The following array contains this description, and it is
217 * used by rrset_canonical_order() and verifyalg_add_rdata(), to adjust their behaviour
218 * for each RR type.
219 *
220 * The format of the description is very easy, for instance:
221 *
222 * { 12, RDESC_DOMAIN, RDESC_DOMAIN, 4, RDESC_DOMAIN, RDESC_END }
223 *
224 * This means that this (ficticious) RR type has a RDATA section containing 12 octects
225 * (we don't care what they contain), followed by 2 domain names, followed by 4 octects,
226 * followed by 1 domain name, and then followed by an unspecificied number of octects (0
227 * or more).
228 */
229
230#define RDESC_DOMAIN -1
231#define RDESC_END 0
232static const int rdata_description[][8] =
233{
234 /**/ { RDESC_END },
235 /* 1: A */ { RDESC_END },
236 /* 2: NS */ { RDESC_DOMAIN, RDESC_END },
237 /* 3: .. */ { RDESC_END },
238 /* 4: .. */ { RDESC_END },
239 /* 5: CNAME */ { RDESC_DOMAIN, RDESC_END },
785ee80b 240 /* 6: SOA */ { RDESC_DOMAIN, RDESC_DOMAIN, RDESC_END },
0937692d
GB
241 /* 7: */ { RDESC_END },
242 /* 8: */ { RDESC_END },
243 /* 9: */ { RDESC_END },
244 /* 10: */ { RDESC_END },
245 /* 11: */ { RDESC_END },
246 /* 12: */ { RDESC_END },
247 /* 13: */ { RDESC_END },
248 /* 14: */ { RDESC_END },
249 /* 15: MX */ { 2, RDESC_DOMAIN, RDESC_END },
6299ffbe
GB
250};
251
252
4885d57c
GB
253/* On-the-fly rdata canonicalization
254 *
255 * This set of functions allow the user to iterate over the rdata section of a RR
256 * while canonicalizing it on-the-fly. This is a great memory saving since the user
257 * doesn't need to allocate memory for a copy of the whole rdata section.
258 *
259 * Sample usage:
260 *
261 * RDataCFrom cf;
262 * rdata_cfrom_init(
263 * &cf,
264 * header, pktlen, // dns_header
265 * rdata, // pointer to rdata section
266 * rrtype, // RR tyep
267 * tmpbuf); // temporary buf (MAXCDNAME)
268 *
269 * while ((p = rdata_cfrom_next(&cf, &len))
270 * {
271 * // Process p[0..len]
272 * }
273 *
274 * if (rdata_cfrom_error(&cf))
275 * // error occurred while parsing
276 *
277 */
278typedef struct
279{
280 struct dns_header *header;
281 size_t pktlen;
282 unsigned char *rdata;
283 unsigned char *tmpbuf;
284 size_t rdlen;
285 int rrtype;
286 int cnt;
287} RDataCForm;
288
289static void rdata_cform_init(RDataCForm *ctx, struct dns_header *header, size_t pktlen,
290 unsigned char *rdata, int rrtype, unsigned char *tmpbuf)
291{
292 if (rrtype >= countof(rdata_description))
293 rrtype = 0;
294 ctx->header = header;
295 ctx->pktlen = pktlen;
296 ctx->rdata = rdata;
297 ctx->rrtype = rrtype;
298 ctx->tmpbuf = tmpbuf;
299 ctx->cnt = -1;
300 GETSHORT(ctx->rdlen, ctx->rdata);
301}
302
303static int rdata_cform_error(RDataCForm *ctx)
304{
305 return ctx->cnt == -2;
306}
307
308static unsigned char *rdata_cform_next(RDataCForm *ctx, size_t *len)
309{
310 if (ctx->cnt != -1 && rdata_description[ctx->rrtype][ctx->cnt] == RDESC_END)
311 return NULL;
312
313 int d = rdata_description[ctx->rrtype][++ctx->cnt];
314 if (d == RDESC_DOMAIN)
315 {
316 *len = process_domain_name(ctx->header, ctx->pktlen, &ctx->rdata, &ctx->rdlen, ctx->tmpbuf, PWN_EXTRACT);
317 if (!*len)
318 {
319 ctx->cnt = -2;
320 return NULL;
321 }
322 return ctx->tmpbuf;
323 }
324 else if (d == RDESC_END)
325 {
326 *len = ctx->rdlen;
327 return ctx->rdata;
328 }
329 else
330 {
331 unsigned char *ret = ctx->rdata;
332 ctx->rdlen -= d;
333 ctx->rdata += d;
334 *len = d;
335 return ret;
336 }
337}
338
339
e292e93d
GB
340/* Check whether today/now is between date_start and date_end */
341static int check_date_range(unsigned long date_start, unsigned long date_end)
342{
343 /* TODO: double-check that time(0) is the correct time we are looking for */
344 /* TODO: dnssec requires correct timing; implement SNTP in dnsmasq? */
345 unsigned long curtime = time(0);
346
347 /* We must explicitly check against wanted values, because of SERIAL_UNDEF */
41de7442
GB
348 return serial_compare_32(curtime, date_start) == SERIAL_GT
349 && serial_compare_32(curtime, date_end) == SERIAL_LT;
e292e93d
GB
350}
351
0ca895f5 352
e292e93d
GB
353/* Sort RRs within a RRset in canonical order, according to RFC4034, §6.3
354 Notice that the RRDATA sections have been already normalized, so a memcpy
355 is sufficient.
356 NOTE: r1/r2 point immediately after the owner name. */
0ca895f5
GB
357
358struct {
359 struct dns_header *header;
360 size_t pktlen;
361} rrset_canonical_order_ctx;
362
e292e93d
GB
363static int rrset_canonical_order(const void *r1, const void *r2)
364{
0ca895f5 365 size_t r1len, r2len;
da23c4f9 366 int rrtype;
0ca895f5
GB
367 unsigned char *pr1=*(unsigned char**)r1, *pr2=*(unsigned char**)r2;
368 unsigned char tmp1[MAXCDNAME], tmp2[MAXCDNAME]; /* TODO: use part of daemon->namebuff */
e292e93d 369
0ca895f5
GB
370 GETSHORT(rrtype, pr1);
371 pr1 += 6; pr2 += 8;
da23c4f9
GB
372
373 RDataCForm cf1, cf2;
374 rdata_cform_init(&cf1, rrset_canonical_order_ctx.header, rrset_canonical_order_ctx.pktlen,
375 pr1, rrtype, tmp1);
376 rdata_cform_init(&cf2, rrset_canonical_order_ctx.header, rrset_canonical_order_ctx.pktlen,
377 pr2, rrtype, tmp2);
378 while ((pr1 = rdata_cform_next(&cf1, &r1len)) &&
379 (pr2 = rdata_cform_next(&cf2, &r2len)))
380 {
381 int res = memcmp(pr1, pr2, MIN(r1len,r2len));
382 if (res != 0)
383 return res;
384 if (r1len < r2len)
385 return -1;
386 if (r2len > r1len)
387 return 1;
388 }
389
390 /* If we reached this point, the two RRs are identical (or an error occurred).
0ca895f5
GB
391 RFC2181 says that an RRset is not allowed to contain duplicate
392 records. If it happens, it is a protocol error and anything goes. */
393 return 1;
e292e93d
GB
394}
395
adca3e9c
GB
396typedef struct PendingRRSIGValidation
397{
398 VerifyAlgCtx *alg;
399 char *signer_name;
400 int keytag;
401} PendingRRSIGValidation;
402
7f0485cf
GB
403/* strchrnul - like strchr, but when character is not found, returns a pointer to the terminating \0.
404
405 This is an existing C GNU extension, but it's easier to reimplement it,
406 rather than tweaking with configure. */
4f9aefc7 407static char *my_strchrnul(char *str, char ch)
7f0485cf
GB
408{
409 while (*str && *str != ch)
410 str++;
411 return str;
412}
413
32b826e2
GB
414/* Convert a domain name to wire format */
415static int convert_domain_to_wire(char *name, unsigned char* out)
13e435eb 416{
32b826e2
GB
417 unsigned char len;
418 unsigned char *start = out;
419 char *p;
13e435eb 420
7f0485cf 421 do
13e435eb 422 {
4f9aefc7 423 p = my_strchrnul(name, '.');
7f0485cf
GB
424 if ((len = p-name))
425 {
32b826e2 426 *out++ = len;
b58fb39f
GB
427 while (len--)
428 {
429 char ch = *name++;
430 /* TODO: this will not be required anymore once we
431 remove all usages of extract_name() from DNSSEC code */
432 if (ch >= 'A' && ch <= 'Z')
433 ch = ch - 'A' + 'a';
434 *out++ = ch;
435 }
7f0485cf 436 }
13e435eb
GB
437 name = p+1;
438 }
7f0485cf
GB
439 while (*p);
440
32b826e2
GB
441 *out++ = '\0';
442 return out-start;
13e435eb
GB
443}
444
0ca895f5 445
ed1fc985 446/* Pass a resource record's rdata field through the currently-initailized digest algorithm.
0852d76b
GB
447
448 We must pass the record in DNS wire format, but if the record contains domain names,
449 they must be uncompressed. This makes things very tricky, because */
ed1fc985 450static int digestalg_add_rdata(int sigtype, struct dns_header *header, size_t pktlen,
0852d76b
GB
451 unsigned char *rdata)
452{
f119ed38 453 size_t len;
0852d76b 454 unsigned char *p;
f119ed38
GB
455 unsigned short total;
456 unsigned char tmpbuf[MAXDNAME]; /* TODO: reuse part of daemon->namebuff */
457 RDataCForm cf1, cf2;
0852d76b 458
f119ed38
GB
459 /* Initialize two iterations over the canonical form*/
460 rdata_cform_init(&cf1, header, pktlen, rdata, sigtype, tmpbuf);
461 cf2 = cf1;
462
463 /* Iteration 1: go through the canonical record and count the total octects.
464 This number might be different from the non-canonical rdata length
465 because of domain names compression. */
466 total = 0;
467 while ((p = rdata_cform_next(&cf1, &len)))
468 total += len;
469 if (rdata_cform_error(&cf1))
470 return 0;
471
472 /* Iteration 2: process the canonical record through the hash function */
473 total = htons(total);
ed1fc985 474 digestalg_add_data(&total, 2);
f119ed38
GB
475
476 while ((p = rdata_cform_next(&cf2, &len)))
ed1fc985 477 digestalg_add_data(p, len);
0852d76b 478
0852d76b
GB
479 return 1;
480}
481
482
adca3e9c
GB
483static int begin_rrsig_validation(struct dns_header *header, size_t pktlen,
484 unsigned char *reply, int count, char *owner,
485 int sigclass, int sigrdlen, unsigned char *sig,
486 PendingRRSIGValidation *out)
e292e93d
GB
487{
488 int i, res;
489 int sigtype, sigalg, siglbl;
490 unsigned char *sigrdata = sig;
491 unsigned long sigttl, date_end, date_start;
492 unsigned char* p = reply;
493 char* signer_name = daemon->namebuff;
13e435eb 494 int signer_name_rdlen;
e292e93d
GB
495 int keytag;
496 void *rrset[16]; /* TODO: max RRset size? */
497 int rrsetidx = 0;
498
499 if (sigrdlen < 18)
500 return 0;
501 GETSHORT(sigtype, sig);
502 sigalg = *sig++;
503 siglbl = *sig++;
504 GETLONG(sigttl, sig);
505 GETLONG(date_end, sig);
506 GETLONG(date_start, sig);
507 GETSHORT(keytag, sig);
508 sigrdlen -= 18;
509
adca3e9c 510 if (!verifyalg_supported(sigalg))
e292e93d 511 {
4b528700 512 printf("ERROR: RRSIG algorithm not supported: %d\n", sigalg);
e292e93d
GB
513 return 0;
514 }
515
d31d057a 516 if (!check_date_range(date_start, date_end))
e292e93d 517 {
4b528700 518 printf("ERROR: RRSIG outside date range\n");
e292e93d
GB
519 return 0;
520 }
521
522 /* Iterate within the answer and find the RRsets matching the current RRsig */
523 for (i = 0; i < count; ++i)
524 {
525 int qtype, qclass, rdlen;
526 if (!(res = extract_name(header, pktlen, &p, owner, 0, 10)))
527 return 0;
528 rrset[rrsetidx] = p;
529 GETSHORT(qtype, p);
530 GETSHORT(qclass, p);
531 p += 4; /* skip ttl */
532 GETSHORT(rdlen, p);
533 if (res == 1 && qtype == sigtype && qclass == sigclass)
534 {
535 ++rrsetidx;
382e38f4
GB
536 if (rrsetidx == countof(rrset))
537 {
538 /* Internal buffer too small */
539 printf("internal buffer too small for this RRset\n");
540 return 0;
541 }
e292e93d
GB
542 }
543 p += rdlen;
544 }
545
546 /* Sort RRset records in canonical order. */
0ca895f5
GB
547 rrset_canonical_order_ctx.header = header;
548 rrset_canonical_order_ctx.pktlen = pktlen;
e292e93d
GB
549 qsort(rrset, rrsetidx, sizeof(void*), rrset_canonical_order);
550
50a96b62
GB
551 /* Skip through the signer name; we don't extract it right now because
552 we don't want to overwrite the single daemon->namebuff which contains
553 the owner name. We'll get to this later. */
554 if (!(p = skip_name(sig, header, pktlen, 0)))
e292e93d 555 return 0;
50a96b62
GB
556 signer_name_rdlen = p - sig;
557 sig = p; sigrdlen -= signer_name_rdlen;
13e435eb 558
e292e93d
GB
559 /* Now initialize the signature verification algorithm and process the whole
560 RRset */
adca3e9c
GB
561 VerifyAlgCtx *alg = verifyalg_alloc(sigalg);
562 if (!alg)
563 return 0;
3af1ea8c
GB
564 alg->sig = sig;
565 alg->siglen = sigrdlen;
e292e93d 566
4b0eecbb
GB
567 sigtype = htons(sigtype);
568 sigclass = htons(sigclass);
569 sigttl = htonl(sigttl);
570
32b826e2
GB
571 /* TODO: we shouldn't need to convert this to wire here. Best solution would be:
572 - Use process_name() instead of extract_name() everywhere in dnssec code
573 - Convert from wire format to representation format only for querying/storing cache
574 */
575 unsigned char owner_wire[MAXCDNAME];
576 int owner_wire_len = convert_domain_to_wire(owner, owner_wire);
577
3af1ea8c 578 digestalg_begin(alg->vtbl->digest_algo);
ed1fc985 579 digestalg_add_data(sigrdata, 18+signer_name_rdlen);
e292e93d
GB
580 for (i = 0; i < rrsetidx; ++i)
581 {
13e435eb
GB
582 p = (unsigned char*)(rrset[i]);
583
ed1fc985
GB
584 digestalg_add_data(owner_wire, owner_wire_len);
585 digestalg_add_data(&sigtype, 2);
586 digestalg_add_data(&sigclass, 2);
587 digestalg_add_data(&sigttl, 4);
e292e93d 588
e292e93d 589 p += 8;
ed1fc985 590 if (!digestalg_add_rdata(ntohs(sigtype), header, pktlen, p))
0852d76b 591 return 0;
e292e93d 592 }
3af1ea8c
GB
593 int digest_len = digestalg_len();
594 memcpy(alg->digest, digestalg_final(), digest_len);
e292e93d 595
50a96b62
GB
596 /* We don't need the owner name anymore; now extract the signer name */
597 if (!extract_name_no_compression(sigrdata+18, signer_name_rdlen, signer_name))
598 return 0;
599
adca3e9c
GB
600 out->alg = alg;
601 out->keytag = keytag;
602 out->signer_name = signer_name;
603 return 1;
604}
605
606static int end_rrsig_validation(PendingRRSIGValidation *val, struct crec *crec_dnskey)
607{
a7338645 608 /* FIXME: keydata is non-contiguous */
708bcd2d 609 return val->alg->vtbl->verify(val->alg, crec_dnskey->addr.key.keydata, crec_dnskey->uid);
adca3e9c
GB
610}
611
612static void dnssec_parserrsig(struct dns_header *header, size_t pktlen,
613 unsigned char *reply, int count, char *owner,
614 int sigclass, int sigrdlen, unsigned char *sig)
615{
616 PendingRRSIGValidation val;
617
618 /* Initiate the RRSIG validation process. The pending state is returned into val. */
619 if (!begin_rrsig_validation(header, pktlen, reply, count, owner, sigclass, sigrdlen, sig, &val))
620 return;
621
622 printf("RRSIG: querying cache for DNSKEY %s (keytag: %d)\n", val.signer_name, val.keytag);
20bccd49
GB
623
624 /* Look in the cache for *all* the DNSKEYs with matching signer_name and keytag */
adca3e9c
GB
625 char onekey = 0;
626 struct crec *crecp = NULL;
a55ce08c 627 while ((crecp = cache_find_by_name(crecp, val.signer_name, time(0), F_DNSKEY))) /* TODO: time(0) */
adca3e9c
GB
628 {
629 onekey = 1;
630
20bccd49
GB
631 if (crecp->addr.key.keytag == val.keytag
632 && crecp->addr.key.algo == verifyalg_algonum(val.alg))
633 {
634 printf("RRSIG: found DNSKEY %d in cache, attempting validation\n", val.keytag);
adca3e9c 635
20bccd49
GB
636 if (end_rrsig_validation(&val, crecp))
637 printf("Validation OK\n");
638 else
ccd1d32c 639 printf("ERROR: Validation FAILED (%s, keytag:%d, algo:%d)\n", owner, val.keytag, verifyalg_algonum(val.alg));
20bccd49 640 }
adca3e9c
GB
641 }
642
643 if (!onekey)
644 {
ccca70cb 645 printf("DNSKEY not found, need to fetch it\n");
adca3e9c
GB
646 /* TODO: store PendingRRSIGValidation in routing table,
647 fetch key (and make it go through dnssec_parskey), then complete validation. */
648 }
e292e93d
GB
649}
650
3471f181 651/* Compute keytag (checksum to quickly index a key). See RFC4034 */
75ffc9bf 652static int dnskey_keytag(int alg, unsigned char *rdata, int rdlen)
3471f181 653{
75ffc9bf
GB
654 if (alg == 1)
655 {
656 /* Algorithm 1 (RSAMD5) has a different (older) keytag calculation algorithm.
657 See RFC4034, Appendix B.1 */
658 return rdata[rdlen-3] * 256 + rdata[rdlen-2];
659 }
660 else
661 {
662 unsigned long ac;
663 int i;
664
665 ac = 0;
666 for (i = 0; i < rdlen; ++i)
667 ac += (i & 1) ? rdata[i] : rdata[i] << 8;
668 ac += (ac >> 16) & 0xFFFF;
669 return ac & 0xFFFF;
670 }
3471f181
GB
671}
672
0304d28f
GB
673/* Check if the DS record (from cache) points to the DNSKEY record (from cache) */
674static int dnskey_ds_match(struct crec *dnskey, struct crec *ds)
675{
676 if (dnskey->addr.key.keytag != ds->addr.key.keytag)
677 return 0;
678 if (dnskey->addr.key.algo != ds->addr.key.algo)
679 return 0;
680
681 unsigned char owner[MAXCDNAME]; /* TODO: user part of daemon->namebuff */
682 int owner_len = convert_domain_to_wire(cache_get_name(ds), owner);
683 size_t keylen = dnskey->uid;
684 int dig = ds->uid;
ed1fc985 685 int digsize;
0304d28f
GB
686
687 if (!digestalg_begin(dig))
688 return 0;
ed1fc985 689 digsize = digestalg_len();
0304d28f
GB
690 digestalg_add_data(owner, owner_len);
691 digestalg_add_data("\x01\x01\x03", 3);
692 digestalg_add_data(&ds->addr.key.algo, 1);
693 digestalg_add_keydata(dnskey->addr.key.keydata, keylen);
ed1fc985 694 return (memcmp(digestalg_final(), ds->addr.key.keydata->key, digsize) == 0);
0304d28f
GB
695}
696
3471f181
GB
697int dnssec_parsekey(struct dns_header *header, size_t pktlen, char *owner, unsigned long ttl,
698 int rdlen, unsigned char *rdata)
699{
700 int flags, proto, alg;
701 struct keydata *key; struct crec *crecp;
3471f181
GB
702 unsigned char *ordata = rdata; int ordlen = rdlen;
703
704 CHECKED_GETSHORT(flags, rdata, rdlen);
705 CHECKED_GETCHAR(proto, rdata, rdlen);
706 CHECKED_GETCHAR(alg, rdata, rdlen);
707
708 if (proto != 3)
709 return 0;
0d829ebc
GB
710 /* Skip non-signing keys (as specified in RFC4034 */
711 if (!(flags & 0x100))
712 return 0;
3471f181 713
a55ce08c 714 key = keydata_alloc((char*)rdata, rdlen);
3471f181 715
3471f181
GB
716 /* TODO: time(0) is correct here? */
717 crecp = cache_insert(owner, NULL, time(0), ttl, F_FORWARD | F_DNSKEY);
718 if (crecp)
719 {
720 /* TODO: improve union not to name "uid" this field */
721 crecp->uid = rdlen;
722 crecp->addr.key.keydata = key;
723 crecp->addr.key.algo = alg;
75ffc9bf 724 crecp->addr.key.keytag = dnskey_keytag(alg, ordata, ordlen);
3471f181
GB
725 printf("DNSKEY: storing key for %s (keytag: %d)\n", owner, crecp->addr.key.keytag);
726 }
727 else
728 {
729 keydata_free(key);
730 /* TODO: if insertion really might fail, verify we don't depend on cache
731 insertion success for validation workflow correctness */
732 printf("DNSKEY: cache insertion failure\n");
733 return 0;
734 }
3471f181
GB
735 return 1;
736}
737
738int dnssec_parseds(struct dns_header *header, size_t pktlen, char *owner, unsigned long ttl,
739 int rdlen, unsigned char *rdata)
740{
0304d28f
GB
741 int keytag, algo, dig;
742 struct keydata *key; struct crec *crec_ds, *crec_key;
743
744 CHECKED_GETSHORT(keytag, rdata, rdlen);
745 CHECKED_GETCHAR(algo, rdata, rdlen);
746 CHECKED_GETCHAR(dig, rdata, rdlen);
747
748 if (!digestalg_supported(dig))
749 return 0;
750
751 key = keydata_alloc((char*)rdata, rdlen);
752
753 /* TODO: time(0) is correct here? */
754 crec_ds = cache_insert(owner, NULL, time(0), ttl, F_FORWARD | F_DS);
755 if (!crec_ds)
756 {
757 keydata_free(key);
758 /* TODO: if insertion really might fail, verify we don't depend on cache
759 insertion success for validation workflow correctness */
760 printf("DS: cache insertion failure\n");
761 return 0;
762 }
763
764 /* TODO: improve union not to name "uid" this field */
765 crec_ds->uid = dig;
766 crec_ds->addr.key.keydata = key;
767 crec_ds->addr.key.algo = algo;
768 crec_ds->addr.key.keytag = keytag;
769 printf("DS: storing key for %s (digest: %d)\n", owner, dig);
770
771 /* Now try to find a DNSKEY which matches this DS digest. */
772 printf("Looking for a DNSKEY matching DS %d...\n", keytag);
773 crec_key = NULL;
774 while ((crec_key = cache_find_by_name(crec_key, owner, time(0), F_DNSKEY))) /* TODO: time(0) */
775 {
776 if (dnskey_ds_match(crec_key, crec_ds))
777 {
778 /* TODO: create a link within the cache: ds => dnskey */
779 printf("MATCH FOUND for keytag %d\n", keytag);
ccd1d32c 780 return 1;
0304d28f
GB
781 }
782 }
783
ccd1d32c 784 printf("ERROR: match not found for DS %d (owner: %s)\n", keytag, owner);
3471f181
GB
785 return 0;
786}
e292e93d
GB
787
788int dnssec_validate(struct dns_header *header, size_t pktlen)
789{
790 unsigned char *p, *reply;
791 char *owner = daemon->namebuff;
23c21766 792 int i, s, qtype, qclass, rdlen;
e292e93d 793 unsigned long ttl;
23c21766 794 int slen[3] = { ntohs(header->ancount), ntohs(header->nscount), ntohs(header->arcount) };
e292e93d 795
23c21766 796 if (slen[0] + slen[1] + slen[2] == 0)
e292e93d
GB
797 return 0;
798 if (!(reply = p = skip_questions(header, pktlen)))
799 return 0;
d0edff7d
GB
800
801 /* First, process DNSKEY/DS records and add them to the cache. */
802 cache_start_insert();
23c21766 803 for (i = 0; i < slen[0]; i++)
e292e93d
GB
804 {
805 if (!extract_name(header, pktlen, &p, owner, 1, 10))
806 return 0;
807 GETSHORT(qtype, p);
808 GETSHORT(qclass, p);
809 GETLONG(ttl, p);
810 GETSHORT(rdlen, p);
3471f181
GB
811 if (qtype == T_DS)
812 {
813 printf("DS found\n");
814 dnssec_parseds(header, pktlen, owner, ttl, rdlen, p);
815 }
816 else if (qtype == T_DNSKEY)
817 {
818 printf("DNSKEY found\n");
47f99dd2 819 dnssec_parsekey(header, pktlen, owner, ttl, rdlen, p);
3471f181 820 }
4137b84e
GB
821 p += rdlen;
822 }
d0edff7d 823 cache_end_insert();
4137b84e 824
d0edff7d 825 /* After we have cached DNSKEY/DS records, start looking for RRSIGs.
4137b84e
GB
826 We want to do this in a separate step because we want the cache
827 to be already populated with DNSKEYs before parsing signatures. */
828 p = reply;
23c21766 829 for (s = 0; s < 3; ++s)
4137b84e 830 {
23c21766
GB
831 reply = p;
832 for (i = 0; i < slen[s]; i++)
e292e93d 833 {
23c21766
GB
834 if (!extract_name(header, pktlen, &p, owner, 1, 10))
835 return 0;
836 GETSHORT(qtype, p);
837 GETSHORT(qclass, p);
838 GETLONG(ttl, p);
839 GETSHORT(rdlen, p);
840 if (qtype == T_RRSIG)
841 {
842 printf("RRSIG found (owner: %s)\n", owner);
843 /* TODO: missing logic. We should only validate RRSIGs for which we
844 have a valid DNSKEY that is referenced by a DS record upstream.
845 There is a memory vs CPU conflict here; should we validate everything
846 to save memory and thus waste CPU, or better first acquire all information
847 (wasting memory) and then doing the minimum CPU computations required? */
848 dnssec_parserrsig(header, pktlen, reply, slen[s], owner, qclass, rdlen, p);
849 }
850 p += rdlen;
4137b84e 851 }
e292e93d 852 }
4137b84e 853
e292e93d
GB
854 return 1;
855}