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