]> git.ipfire.org Git - people/ms/dnsmasq.git/blame - src/dnssec.c
Implement digest algorithm support.
[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. */
407static char *strchrnul(char *str, char ch)
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 {
7f0485cf
GB
423 p = strchrnul(name, '.');
424 if ((len = p-name))
425 {
32b826e2
GB
426 *out++ = len;
427 memcpy(out, name, len);
428 out += len;
7f0485cf 429 }
13e435eb
GB
430 name = p+1;
431 }
7f0485cf
GB
432 while (*p);
433
32b826e2
GB
434 *out++ = '\0';
435 return out-start;
13e435eb
GB
436}
437
0ca895f5 438
0852d76b
GB
439/* Pass a resource record's rdata field through a verification hash function.
440
441 We must pass the record in DNS wire format, but if the record contains domain names,
442 they must be uncompressed. This makes things very tricky, because */
443static int verifyalg_add_rdata(VerifyAlgCtx *alg, int sigtype, struct dns_header *header, size_t pktlen,
444 unsigned char *rdata)
445{
f119ed38 446 size_t len;
0852d76b 447 unsigned char *p;
f119ed38
GB
448 unsigned short total;
449 unsigned char tmpbuf[MAXDNAME]; /* TODO: reuse part of daemon->namebuff */
450 RDataCForm cf1, cf2;
0852d76b 451
f119ed38
GB
452 /* Initialize two iterations over the canonical form*/
453 rdata_cform_init(&cf1, header, pktlen, rdata, sigtype, tmpbuf);
454 cf2 = cf1;
455
456 /* Iteration 1: go through the canonical record and count the total octects.
457 This number might be different from the non-canonical rdata length
458 because of domain names compression. */
459 total = 0;
460 while ((p = rdata_cform_next(&cf1, &len)))
461 total += len;
462 if (rdata_cform_error(&cf1))
463 return 0;
464
465 /* Iteration 2: process the canonical record through the hash function */
466 total = htons(total);
467 alg->vtbl->add_data(alg, &total, 2);
468
469 while ((p = rdata_cform_next(&cf2, &len)))
470 alg->vtbl->add_data(alg, p, len);
0852d76b 471
0852d76b
GB
472 return 1;
473}
474
475
adca3e9c
GB
476static int begin_rrsig_validation(struct dns_header *header, size_t pktlen,
477 unsigned char *reply, int count, char *owner,
478 int sigclass, int sigrdlen, unsigned char *sig,
479 PendingRRSIGValidation *out)
e292e93d
GB
480{
481 int i, res;
482 int sigtype, sigalg, siglbl;
483 unsigned char *sigrdata = sig;
484 unsigned long sigttl, date_end, date_start;
485 unsigned char* p = reply;
486 char* signer_name = daemon->namebuff;
13e435eb 487 int signer_name_rdlen;
e292e93d
GB
488 int keytag;
489 void *rrset[16]; /* TODO: max RRset size? */
490 int rrsetidx = 0;
491
492 if (sigrdlen < 18)
493 return 0;
494 GETSHORT(sigtype, sig);
495 sigalg = *sig++;
496 siglbl = *sig++;
497 GETLONG(sigttl, sig);
498 GETLONG(date_end, sig);
499 GETLONG(date_start, sig);
500 GETSHORT(keytag, sig);
501 sigrdlen -= 18;
502
adca3e9c 503 if (!verifyalg_supported(sigalg))
e292e93d
GB
504 {
505 printf("RRSIG algorithm not supported: %d\n", sigalg);
506 return 0;
507 }
508
d31d057a 509 if (!check_date_range(date_start, date_end))
e292e93d
GB
510 {
511 printf("RRSIG outside date range\n");
512 return 0;
513 }
514
515 /* Iterate within the answer and find the RRsets matching the current RRsig */
516 for (i = 0; i < count; ++i)
517 {
518 int qtype, qclass, rdlen;
519 if (!(res = extract_name(header, pktlen, &p, owner, 0, 10)))
520 return 0;
521 rrset[rrsetidx] = p;
522 GETSHORT(qtype, p);
523 GETSHORT(qclass, p);
524 p += 4; /* skip ttl */
525 GETSHORT(rdlen, p);
526 if (res == 1 && qtype == sigtype && qclass == sigclass)
527 {
528 ++rrsetidx;
382e38f4
GB
529 if (rrsetidx == countof(rrset))
530 {
531 /* Internal buffer too small */
532 printf("internal buffer too small for this RRset\n");
533 return 0;
534 }
e292e93d
GB
535 }
536 p += rdlen;
537 }
538
539 /* Sort RRset records in canonical order. */
0ca895f5
GB
540 rrset_canonical_order_ctx.header = header;
541 rrset_canonical_order_ctx.pktlen = pktlen;
e292e93d
GB
542 qsort(rrset, rrsetidx, sizeof(void*), rrset_canonical_order);
543
50a96b62
GB
544 /* Skip through the signer name; we don't extract it right now because
545 we don't want to overwrite the single daemon->namebuff which contains
546 the owner name. We'll get to this later. */
547 if (!(p = skip_name(sig, header, pktlen, 0)))
e292e93d 548 return 0;
50a96b62
GB
549 signer_name_rdlen = p - sig;
550 sig = p; sigrdlen -= signer_name_rdlen;
13e435eb 551
e292e93d
GB
552 /* Now initialize the signature verification algorithm and process the whole
553 RRset */
adca3e9c
GB
554 VerifyAlgCtx *alg = verifyalg_alloc(sigalg);
555 if (!alg)
556 return 0;
557 if (!alg->vtbl->set_signature(alg, sig, sigrdlen))
e292e93d
GB
558 return 0;
559
4b0eecbb
GB
560 sigtype = htons(sigtype);
561 sigclass = htons(sigclass);
562 sigttl = htonl(sigttl);
563
32b826e2
GB
564 /* TODO: we shouldn't need to convert this to wire here. Best solution would be:
565 - Use process_name() instead of extract_name() everywhere in dnssec code
566 - Convert from wire format to representation format only for querying/storing cache
567 */
568 unsigned char owner_wire[MAXCDNAME];
569 int owner_wire_len = convert_domain_to_wire(owner, owner_wire);
570
adca3e9c 571 alg->vtbl->begin_data(alg);
13e435eb 572 alg->vtbl->add_data(alg, sigrdata, 18+signer_name_rdlen);
e292e93d
GB
573 for (i = 0; i < rrsetidx; ++i)
574 {
13e435eb
GB
575 p = (unsigned char*)(rrset[i]);
576
32b826e2 577 alg->vtbl->add_data(alg, owner_wire, owner_wire_len);
adca3e9c
GB
578 alg->vtbl->add_data(alg, &sigtype, 2);
579 alg->vtbl->add_data(alg, &sigclass, 2);
580 alg->vtbl->add_data(alg, &sigttl, 4);
e292e93d 581
e292e93d 582 p += 8;
0852d76b
GB
583 if (!verifyalg_add_rdata(alg, ntohs(sigtype), header, pktlen, p))
584 return 0;
e292e93d 585 }
adca3e9c 586 alg->vtbl->end_data(alg);
e292e93d 587
50a96b62
GB
588 /* We don't need the owner name anymore; now extract the signer name */
589 if (!extract_name_no_compression(sigrdata+18, signer_name_rdlen, signer_name))
590 return 0;
591
adca3e9c
GB
592 out->alg = alg;
593 out->keytag = keytag;
594 out->signer_name = signer_name;
595 return 1;
596}
597
598static int end_rrsig_validation(PendingRRSIGValidation *val, struct crec *crec_dnskey)
599{
a7338645 600 /* FIXME: keydata is non-contiguous */
708bcd2d 601 return val->alg->vtbl->verify(val->alg, crec_dnskey->addr.key.keydata, crec_dnskey->uid);
adca3e9c
GB
602}
603
604static void dnssec_parserrsig(struct dns_header *header, size_t pktlen,
605 unsigned char *reply, int count, char *owner,
606 int sigclass, int sigrdlen, unsigned char *sig)
607{
608 PendingRRSIGValidation val;
609
610 /* Initiate the RRSIG validation process. The pending state is returned into val. */
611 if (!begin_rrsig_validation(header, pktlen, reply, count, owner, sigclass, sigrdlen, sig, &val))
612 return;
613
614 printf("RRSIG: querying cache for DNSKEY %s (keytag: %d)\n", val.signer_name, val.keytag);
20bccd49
GB
615
616 /* Look in the cache for *all* the DNSKEYs with matching signer_name and keytag */
adca3e9c
GB
617 char onekey = 0;
618 struct crec *crecp = NULL;
a55ce08c 619 while ((crecp = cache_find_by_name(crecp, val.signer_name, time(0), F_DNSKEY))) /* TODO: time(0) */
adca3e9c
GB
620 {
621 onekey = 1;
622
20bccd49
GB
623 if (crecp->addr.key.keytag == val.keytag
624 && crecp->addr.key.algo == verifyalg_algonum(val.alg))
625 {
626 printf("RRSIG: found DNSKEY %d in cache, attempting validation\n", val.keytag);
adca3e9c 627
20bccd49
GB
628 if (end_rrsig_validation(&val, crecp))
629 printf("Validation OK\n");
630 else
631 printf("Validation FAILED\n");
632 }
adca3e9c
GB
633 }
634
635 if (!onekey)
636 {
ccca70cb 637 printf("DNSKEY not found, need to fetch it\n");
adca3e9c
GB
638 /* TODO: store PendingRRSIGValidation in routing table,
639 fetch key (and make it go through dnssec_parskey), then complete validation. */
640 }
e292e93d
GB
641}
642
3471f181
GB
643/* Compute keytag (checksum to quickly index a key). See RFC4034 */
644static int dnskey_keytag(unsigned char *rdata, int rdlen)
645{
646 unsigned long ac;
647 int i;
648
649 ac = 0;
650 for (i = 0; i < rdlen; ++i)
651 ac += (i & 1) ? rdata[i] : rdata[i] << 8;
652 ac += (ac >> 16) & 0xFFFF;
653 return ac & 0xFFFF;
654}
655
656int dnssec_parsekey(struct dns_header *header, size_t pktlen, char *owner, unsigned long ttl,
657 int rdlen, unsigned char *rdata)
658{
659 int flags, proto, alg;
660 struct keydata *key; struct crec *crecp;
3471f181
GB
661 unsigned char *ordata = rdata; int ordlen = rdlen;
662
663 CHECKED_GETSHORT(flags, rdata, rdlen);
664 CHECKED_GETCHAR(proto, rdata, rdlen);
665 CHECKED_GETCHAR(alg, rdata, rdlen);
666
667 if (proto != 3)
668 return 0;
0d829ebc
GB
669 /* Skip non-signing keys (as specified in RFC4034 */
670 if (!(flags & 0x100))
671 return 0;
3471f181 672
a55ce08c 673 key = keydata_alloc((char*)rdata, rdlen);
3471f181 674
3471f181
GB
675 /* TODO: time(0) is correct here? */
676 crecp = cache_insert(owner, NULL, time(0), ttl, F_FORWARD | F_DNSKEY);
677 if (crecp)
678 {
679 /* TODO: improve union not to name "uid" this field */
680 crecp->uid = rdlen;
681 crecp->addr.key.keydata = key;
682 crecp->addr.key.algo = alg;
683 crecp->addr.key.keytag = dnskey_keytag(ordata, ordlen);
684 printf("DNSKEY: storing key for %s (keytag: %d)\n", owner, crecp->addr.key.keytag);
685 }
686 else
687 {
688 keydata_free(key);
689 /* TODO: if insertion really might fail, verify we don't depend on cache
690 insertion success for validation workflow correctness */
691 printf("DNSKEY: cache insertion failure\n");
692 return 0;
693 }
3471f181
GB
694 return 1;
695}
696
697int dnssec_parseds(struct dns_header *header, size_t pktlen, char *owner, unsigned long ttl,
698 int rdlen, unsigned char *rdata)
699{
700 return 0;
701}
e292e93d
GB
702
703int dnssec_validate(struct dns_header *header, size_t pktlen)
704{
705 unsigned char *p, *reply;
706 char *owner = daemon->namebuff;
23c21766 707 int i, s, qtype, qclass, rdlen;
e292e93d 708 unsigned long ttl;
23c21766 709 int slen[3] = { ntohs(header->ancount), ntohs(header->nscount), ntohs(header->arcount) };
e292e93d 710
23c21766 711 if (slen[0] + slen[1] + slen[2] == 0)
e292e93d
GB
712 return 0;
713 if (!(reply = p = skip_questions(header, pktlen)))
714 return 0;
d0edff7d
GB
715
716 /* First, process DNSKEY/DS records and add them to the cache. */
717 cache_start_insert();
23c21766 718 for (i = 0; i < slen[0]; i++)
e292e93d
GB
719 {
720 if (!extract_name(header, pktlen, &p, owner, 1, 10))
721 return 0;
722 GETSHORT(qtype, p);
723 GETSHORT(qclass, p);
724 GETLONG(ttl, p);
725 GETSHORT(rdlen, p);
3471f181
GB
726 if (qtype == T_DS)
727 {
728 printf("DS found\n");
729 dnssec_parseds(header, pktlen, owner, ttl, rdlen, p);
730 }
731 else if (qtype == T_DNSKEY)
732 {
733 printf("DNSKEY found\n");
47f99dd2 734 dnssec_parsekey(header, pktlen, owner, ttl, rdlen, p);
3471f181 735 }
4137b84e
GB
736 p += rdlen;
737 }
d0edff7d 738 cache_end_insert();
4137b84e 739
d0edff7d 740 /* After we have cached DNSKEY/DS records, start looking for RRSIGs.
4137b84e
GB
741 We want to do this in a separate step because we want the cache
742 to be already populated with DNSKEYs before parsing signatures. */
743 p = reply;
23c21766 744 for (s = 0; s < 3; ++s)
4137b84e 745 {
23c21766
GB
746 reply = p;
747 for (i = 0; i < slen[s]; i++)
e292e93d 748 {
23c21766
GB
749 if (!extract_name(header, pktlen, &p, owner, 1, 10))
750 return 0;
751 GETSHORT(qtype, p);
752 GETSHORT(qclass, p);
753 GETLONG(ttl, p);
754 GETSHORT(rdlen, p);
755 if (qtype == T_RRSIG)
756 {
757 printf("RRSIG found (owner: %s)\n", owner);
758 /* TODO: missing logic. We should only validate RRSIGs for which we
759 have a valid DNSKEY that is referenced by a DS record upstream.
760 There is a memory vs CPU conflict here; should we validate everything
761 to save memory and thus waste CPU, or better first acquire all information
762 (wasting memory) and then doing the minimum CPU computations required? */
763 dnssec_parserrsig(header, pktlen, reply, slen[s], owner, qclass, rdlen, p);
764 }
765 p += rdlen;
4137b84e 766 }
e292e93d 767 }
4137b84e 768
e292e93d
GB
769 return 1;
770}