blob: b5a94f23646d45f8ce07264f36a5ca51e4c155af [file] [log] [blame]
Giovanni Bajoe292e932012-04-22 14:32:02 +02001
2#include "dnsmasq.h"
Giovanni Bajod322de02012-04-23 00:30:00 +02003#include "dnssec-crypto.h"
Giovanni Bajoe292e932012-04-22 14:32:02 +02004#include <assert.h>
5
Giovanni Bajo6299ffb2012-05-01 18:27:52 +02006/* Maximum length in octects of a domain name, in wire format */
7#define MAXCDNAME 256
8
Giovanni Bajoe292e932012-04-22 14:32:02 +02009#define SERIAL_UNDEF -100
10#define SERIAL_EQ 0
11#define SERIAL_LT -1
12#define SERIAL_GT 1
13
Giovanni Bajoe292e932012-04-22 14:32:02 +020014/* 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++;
Giovanni Bajo6445c8e2012-04-24 02:02:29 +020040 while (count-- > 0 && rr < end)
Giovanni Bajoe292e932012-04-22 14:32:02 +020041 {
42 *buf = *rr++;
Giovanni Bajob98f7712012-04-22 15:59:27 +020043 if (!isascii(*buf) || iscntrl(*buf) || *buf == '.')
44 return 0;
Giovanni Bajoe292e932012-04-22 14:32:02 +020045 if (*buf >= 'A' && *buf <= 'Z')
46 *buf += 'a' - 'A';
47 buf++;
Giovanni Bajo79333a22012-04-28 01:03:10 +020048 }
Giovanni Bajoe292e932012-04-22 14:32:02 +020049 *buf++ = '.';
50 }
Giovanni Bajodd090562012-04-28 03:47:10 +020051 /* Remove trailing dot (if any) */
Giovanni Bajo2ef843d2012-04-25 17:48:40 +020052 if (rr != start)
53 *(--buf) = 0;
Giovanni Bajoe292e932012-04-22 14:32:02 +020054 if (rr == end)
55 return 0;
Giovanni Bajodd090562012-04-28 03:47:10 +020056 /* Trailing \0 in source data must be consumed */
Giovanni Bajo79333a22012-04-28 01:03:10 +020057 return rr-start+1;
Giovanni Bajoe292e932012-04-22 14:32:02 +020058}
59
Giovanni Bajo6299ffb2012-05-01 18:27:52 +020060/* 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 */
Giovanni Bajoec2962e2012-05-02 00:15:26 +0200103#define PWN_EXTRACT 0
104#define PWN_COMPARE 1
105#define PWN_ORDER 2
Giovanni Bajo6299ffb2012-05-01 18:27:52 +0200106static 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 {
Giovanni Bajo0db0e0c2012-05-02 00:16:08 +0200144 int l2;
Giovanni Bajo6299ffb2012-05-01 18:27:52 +0200145 if (p >= end)
146 return 0;
Giovanni Bajo0db0e0c2012-05-02 00:16:08 +0200147 l2 = *p++;
Giovanni Bajo6299ffb2012-05-01 18:27:52 +0200148 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;
Giovanni Bajo0db0e0c2012-05-02 00:16:08 +0200157 p = (unsigned char*)header + (count & 0x3F) * 256 + l2;
Giovanni Bajo6299ffb2012-05-01 18:27:52 +0200158 }
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 },
240};
241
242
Giovanni Bajo4885d572012-05-02 00:30:56 +0200243/* On-the-fly rdata canonicalization
244 *
245 * This set of functions allow the user to iterate over the rdata section of a RR
246 * while canonicalizing it on-the-fly. This is a great memory saving since the user
247 * doesn't need to allocate memory for a copy of the whole rdata section.
248 *
249 * Sample usage:
250 *
251 * RDataCFrom cf;
252 * rdata_cfrom_init(
253 * &cf,
254 * header, pktlen, // dns_header
255 * rdata, // pointer to rdata section
256 * rrtype, // RR tyep
257 * tmpbuf); // temporary buf (MAXCDNAME)
258 *
259 * while ((p = rdata_cfrom_next(&cf, &len))
260 * {
261 * // Process p[0..len]
262 * }
263 *
264 * if (rdata_cfrom_error(&cf))
265 * // error occurred while parsing
266 *
267 */
268typedef struct
269{
270 struct dns_header *header;
271 size_t pktlen;
272 unsigned char *rdata;
273 unsigned char *tmpbuf;
274 size_t rdlen;
275 int rrtype;
276 int cnt;
277} RDataCForm;
278
279static void rdata_cform_init(RDataCForm *ctx, struct dns_header *header, size_t pktlen,
280 unsigned char *rdata, int rrtype, unsigned char *tmpbuf)
281{
282 if (rrtype >= countof(rdata_description))
283 rrtype = 0;
284 ctx->header = header;
285 ctx->pktlen = pktlen;
286 ctx->rdata = rdata;
287 ctx->rrtype = rrtype;
288 ctx->tmpbuf = tmpbuf;
289 ctx->cnt = -1;
290 GETSHORT(ctx->rdlen, ctx->rdata);
291}
292
293static int rdata_cform_error(RDataCForm *ctx)
294{
295 return ctx->cnt == -2;
296}
297
298static unsigned char *rdata_cform_next(RDataCForm *ctx, size_t *len)
299{
300 if (ctx->cnt != -1 && rdata_description[ctx->rrtype][ctx->cnt] == RDESC_END)
301 return NULL;
302
303 int d = rdata_description[ctx->rrtype][++ctx->cnt];
304 if (d == RDESC_DOMAIN)
305 {
306 *len = process_domain_name(ctx->header, ctx->pktlen, &ctx->rdata, &ctx->rdlen, ctx->tmpbuf, PWN_EXTRACT);
307 if (!*len)
308 {
309 ctx->cnt = -2;
310 return NULL;
311 }
312 return ctx->tmpbuf;
313 }
314 else if (d == RDESC_END)
315 {
316 *len = ctx->rdlen;
317 return ctx->rdata;
318 }
319 else
320 {
321 unsigned char *ret = ctx->rdata;
322 ctx->rdlen -= d;
323 ctx->rdata += d;
324 *len = d;
325 return ret;
326 }
327}
328
329
Giovanni Bajoe292e932012-04-22 14:32:02 +0200330/* Check whether today/now is between date_start and date_end */
331static int check_date_range(unsigned long date_start, unsigned long date_end)
332{
333 /* TODO: double-check that time(0) is the correct time we are looking for */
334 /* TODO: dnssec requires correct timing; implement SNTP in dnsmasq? */
335 unsigned long curtime = time(0);
336
337 /* We must explicitly check against wanted values, because of SERIAL_UNDEF */
Giovanni Bajo41de7442012-04-28 03:59:49 +0200338 return serial_compare_32(curtime, date_start) == SERIAL_GT
339 && serial_compare_32(curtime, date_end) == SERIAL_LT;
Giovanni Bajoe292e932012-04-22 14:32:02 +0200340}
341
Giovanni Bajo0ca895f2012-05-01 18:28:43 +0200342
Giovanni Bajoe292e932012-04-22 14:32:02 +0200343/* Sort RRs within a RRset in canonical order, according to RFC4034, §6.3
344 Notice that the RRDATA sections have been already normalized, so a memcpy
345 is sufficient.
346 NOTE: r1/r2 point immediately after the owner name. */
Giovanni Bajo0ca895f2012-05-01 18:28:43 +0200347
348struct {
349 struct dns_header *header;
350 size_t pktlen;
351} rrset_canonical_order_ctx;
352
Giovanni Bajoe292e932012-04-22 14:32:02 +0200353static int rrset_canonical_order(const void *r1, const void *r2)
354{
Giovanni Bajo0ca895f2012-05-01 18:28:43 +0200355 size_t r1len, r2len;
356 int rrtype, i;
357 unsigned char *pr1=*(unsigned char**)r1, *pr2=*(unsigned char**)r2;
358 unsigned char tmp1[MAXCDNAME], tmp2[MAXCDNAME]; /* TODO: use part of daemon->namebuff */
Giovanni Bajoe292e932012-04-22 14:32:02 +0200359
Giovanni Bajo0ca895f2012-05-01 18:28:43 +0200360 #define ORDER(buf1,len1, buf2,len2) \
361 do { \
362 int res = memcmp(buf1, buf2, MIN(len1,len2)); \
363 if (res != 0) return res; \
364 if (len1 < len2) return -1; \
365 if (len1 > len2) return 1; \
366 } while (0)
367
368 GETSHORT(rrtype, pr1);
369 pr1 += 6; pr2 += 8;
Giovanni Bajoe292e932012-04-22 14:32:02 +0200370 GETSHORT(r1len, pr1); GETSHORT(r2len, pr2);
371
Giovanni Bajo0ca895f2012-05-01 18:28:43 +0200372 if (rrtype < countof(rdata_description))
373 for (i = 0; rdata_description[rrtype][i] != RDESC_END; ++i)
374 {
375 int d = rdata_description[rrtype][i];
376 if (d == RDESC_DOMAIN)
377 {
378 int dl1 = process_domain_name(rrset_canonical_order_ctx.header, rrset_canonical_order_ctx.pktlen,
379 &pr1, &r1len, tmp1, PWN_EXTRACT);
380 int dl2 = process_domain_name(rrset_canonical_order_ctx.header, rrset_canonical_order_ctx.pktlen,
381 &pr2, &r2len, tmp2, PWN_EXTRACT);
382 /* TODO: how do we handle errors, that is dl1==0 or dl2==0 ? */
383 assert(dl1 != 0);
384 assert(dl2 != 0);
385 ORDER(tmp1, dl1, tmp2, dl2);
386 }
387 else
388 {
389 ORDER(pr1, d, pr2, d);
390 pr1 += d; pr2 += d;
391 r1len -= d; r2len -= d;
392 }
393 }
394
395 /* Order the rest of the record. */
396 ORDER(pr1, r1len, pr2, r2len);
397
398 /* If we reached this point, the two RRs are identical.
399 RFC2181 says that an RRset is not allowed to contain duplicate
400 records. If it happens, it is a protocol error and anything goes. */
401 return 1;
402
403 #undef ORDER
Giovanni Bajoe292e932012-04-22 14:32:02 +0200404}
405
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200406typedef struct PendingRRSIGValidation
407{
408 VerifyAlgCtx *alg;
409 char *signer_name;
410 int keytag;
411} PendingRRSIGValidation;
412
Giovanni Bajo7f0485c2012-04-28 12:59:49 +0200413/* strchrnul - like strchr, but when character is not found, returns a pointer to the terminating \0.
414
415 This is an existing C GNU extension, but it's easier to reimplement it,
416 rather than tweaking with configure. */
417static char *strchrnul(char *str, char ch)
418{
419 while (*str && *str != ch)
420 str++;
421 return str;
422}
423
Giovanni Bajo13e435e2012-04-27 03:19:40 +0200424/* Pass a domain name through a verification hash function.
425
426 We must pass domain names in DNS wire format, but uncompressed.
427 This means that we cannot directly use raw data from the original
428 message since it might be compressed. */
429static void verifyalg_add_data_domain(VerifyAlgCtx *alg, char* name)
430{
431 unsigned char len; char *p;
432
Giovanni Bajo7f0485c2012-04-28 12:59:49 +0200433 do
Giovanni Bajo13e435e2012-04-27 03:19:40 +0200434 {
Giovanni Bajo7f0485c2012-04-28 12:59:49 +0200435 p = strchrnul(name, '.');
436 if ((len = p-name))
437 {
438 alg->vtbl->add_data(alg, &len, 1);
439 alg->vtbl->add_data(alg, name, len);
440 }
Giovanni Bajo13e435e2012-04-27 03:19:40 +0200441 name = p+1;
442 }
Giovanni Bajo7f0485c2012-04-28 12:59:49 +0200443 while (*p);
444
445 alg->vtbl->add_data(alg, "\0", 1);
Giovanni Bajo13e435e2012-04-27 03:19:40 +0200446}
447
Giovanni Bajo0ca895f2012-05-01 18:28:43 +0200448
Giovanni Bajo0852d762012-04-28 03:49:24 +0200449/* Pass a DNS domain name in wire format through a hash function. Returns the
450 total number of bytes passed through the function or 0 in case of errors.
451 Updates the rdata pointer moving it further within the RR.
452
453 If alg is NULL, go in dry run mode (still correctly updates rdata and return
454 the correct total).
455
456 The function canonicalizes the domain name (RFC 4034, §6.2), which basically
457 means conversion to lower case, and uncompression. */
458static int verifyalg_add_data_wire_domain(VerifyAlgCtx *alg, struct dns_header *header, size_t pktlen,
459 unsigned char** rdata)
460{
461 int hops = 0, total = 0;
462 unsigned char label_type;
463 unsigned char *end = (unsigned char *)header + pktlen;
464 unsigned char count; unsigned char *p = *rdata;
465
466 while (1)
467 {
468 if (p >= end)
469 return 0;
470 if (!(count = *p++))
471 break;
472 label_type = count & 0xC0;
473 if (label_type == 0xC0)
474 {
475 if (p >= end)
476 return 0;
477 p = (unsigned char*)header + (count & 0x3F) * 256 + *p;
478 if (hops == 0)
479 *rdata = p;
480 if (++hops == 256)
481 return 0;
482 }
483 else if (label_type == 0x00)
484 {
485 if (p+count-1 >= end)
486 return 0;
487 if (alg)
488 {
489 alg->vtbl->add_data(alg, &count, 1);
490 /* TODO: missing conversion to lower-case and alphabet check */
491 alg->vtbl->add_data(alg, p, count);
492 }
493 total += count+1;
494 p += count;
495 }
496 else
497 return 0; /* unsupported label_type */
498 }
499
500 if (hops == 0)
501 *rdata = p;
502 if (alg)
503 alg->vtbl->add_data(alg, &count, 1);
504 return total+1;
505}
506
507/* Pass a resource record's rdata field through a verification hash function.
508
509 We must pass the record in DNS wire format, but if the record contains domain names,
510 they must be uncompressed. This makes things very tricky, because */
511static int verifyalg_add_rdata(VerifyAlgCtx *alg, int sigtype, struct dns_header *header, size_t pktlen,
512 unsigned char *rdata)
513{
514 unsigned char *p;
515 int res; unsigned short rdlen;
516
517 GETSHORT(rdlen, rdata);
518 p = rdata;
519
520 switch (sigtype)
521 {
522 /* TODO: missing lots of RR types, see RFC4034, §6.2 */
Giovanni Bajod1ca25c2012-04-28 12:23:04 +0200523 case T_NS:
Giovanni Bajo0852d762012-04-28 03:49:24 +0200524 case T_CNAME:
525 if (!(res = verifyalg_add_data_wire_domain(NULL, header, pktlen, &p)))
526 return 0;
527 if (p - rdata > rdlen)
528 return 0;
529 rdlen = htons(res);
530 alg->vtbl->add_data(alg, &rdlen, 2);
531 verifyalg_add_data_wire_domain(alg, header, pktlen, &rdata);
532 break;
533
534 default:
535 alg->vtbl->add_data(alg, rdata-2, rdlen+2);
536 break;
537 }
538 return 1;
539}
540
541
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200542static int begin_rrsig_validation(struct dns_header *header, size_t pktlen,
543 unsigned char *reply, int count, char *owner,
544 int sigclass, int sigrdlen, unsigned char *sig,
545 PendingRRSIGValidation *out)
Giovanni Bajoe292e932012-04-22 14:32:02 +0200546{
547 int i, res;
548 int sigtype, sigalg, siglbl;
549 unsigned char *sigrdata = sig;
550 unsigned long sigttl, date_end, date_start;
551 unsigned char* p = reply;
552 char* signer_name = daemon->namebuff;
Giovanni Bajo13e435e2012-04-27 03:19:40 +0200553 int signer_name_rdlen;
Giovanni Bajoe292e932012-04-22 14:32:02 +0200554 int keytag;
555 void *rrset[16]; /* TODO: max RRset size? */
556 int rrsetidx = 0;
557
558 if (sigrdlen < 18)
559 return 0;
560 GETSHORT(sigtype, sig);
561 sigalg = *sig++;
562 siglbl = *sig++;
563 GETLONG(sigttl, sig);
564 GETLONG(date_end, sig);
565 GETLONG(date_start, sig);
566 GETSHORT(keytag, sig);
567 sigrdlen -= 18;
568
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200569 if (!verifyalg_supported(sigalg))
Giovanni Bajoe292e932012-04-22 14:32:02 +0200570 {
571 printf("RRSIG algorithm not supported: %d\n", sigalg);
572 return 0;
573 }
574
Giovanni Bajod31d0572012-04-24 02:02:55 +0200575 if (!check_date_range(date_start, date_end))
Giovanni Bajoe292e932012-04-22 14:32:02 +0200576 {
577 printf("RRSIG outside date range\n");
578 return 0;
579 }
580
581 /* Iterate within the answer and find the RRsets matching the current RRsig */
582 for (i = 0; i < count; ++i)
583 {
584 int qtype, qclass, rdlen;
585 if (!(res = extract_name(header, pktlen, &p, owner, 0, 10)))
586 return 0;
587 rrset[rrsetidx] = p;
588 GETSHORT(qtype, p);
589 GETSHORT(qclass, p);
590 p += 4; /* skip ttl */
591 GETSHORT(rdlen, p);
592 if (res == 1 && qtype == sigtype && qclass == sigclass)
593 {
594 ++rrsetidx;
Giovanni Bajo382e38f2012-04-24 01:46:47 +0200595 if (rrsetidx == countof(rrset))
596 {
597 /* Internal buffer too small */
598 printf("internal buffer too small for this RRset\n");
599 return 0;
600 }
Giovanni Bajoe292e932012-04-22 14:32:02 +0200601 }
602 p += rdlen;
603 }
604
605 /* Sort RRset records in canonical order. */
Giovanni Bajo0ca895f2012-05-01 18:28:43 +0200606 rrset_canonical_order_ctx.header = header;
607 rrset_canonical_order_ctx.pktlen = pktlen;
Giovanni Bajoe292e932012-04-22 14:32:02 +0200608 qsort(rrset, rrsetidx, sizeof(void*), rrset_canonical_order);
609
Giovanni Bajo50a96b62012-04-28 01:04:56 +0200610 /* Skip through the signer name; we don't extract it right now because
611 we don't want to overwrite the single daemon->namebuff which contains
612 the owner name. We'll get to this later. */
613 if (!(p = skip_name(sig, header, pktlen, 0)))
Giovanni Bajoe292e932012-04-22 14:32:02 +0200614 return 0;
Giovanni Bajo50a96b62012-04-28 01:04:56 +0200615 signer_name_rdlen = p - sig;
616 sig = p; sigrdlen -= signer_name_rdlen;
Giovanni Bajo13e435e2012-04-27 03:19:40 +0200617
Giovanni Bajoe292e932012-04-22 14:32:02 +0200618 /* Now initialize the signature verification algorithm and process the whole
619 RRset */
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200620 VerifyAlgCtx *alg = verifyalg_alloc(sigalg);
621 if (!alg)
622 return 0;
623 if (!alg->vtbl->set_signature(alg, sig, sigrdlen))
Giovanni Bajoe292e932012-04-22 14:32:02 +0200624 return 0;
625
Giovanni Bajo4b0eecb2012-04-27 03:18:52 +0200626 sigtype = htons(sigtype);
627 sigclass = htons(sigclass);
628 sigttl = htonl(sigttl);
629
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200630 alg->vtbl->begin_data(alg);
Giovanni Bajo13e435e2012-04-27 03:19:40 +0200631 alg->vtbl->add_data(alg, sigrdata, 18+signer_name_rdlen);
Giovanni Bajoe292e932012-04-22 14:32:02 +0200632 for (i = 0; i < rrsetidx; ++i)
633 {
Giovanni Bajo13e435e2012-04-27 03:19:40 +0200634 p = (unsigned char*)(rrset[i]);
635
636 verifyalg_add_data_domain(alg, owner);
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200637 alg->vtbl->add_data(alg, &sigtype, 2);
638 alg->vtbl->add_data(alg, &sigclass, 2);
639 alg->vtbl->add_data(alg, &sigttl, 4);
Giovanni Bajoe292e932012-04-22 14:32:02 +0200640
Giovanni Bajoe292e932012-04-22 14:32:02 +0200641 p += 8;
Giovanni Bajo0852d762012-04-28 03:49:24 +0200642 if (!verifyalg_add_rdata(alg, ntohs(sigtype), header, pktlen, p))
643 return 0;
Giovanni Bajoe292e932012-04-22 14:32:02 +0200644 }
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200645 alg->vtbl->end_data(alg);
Giovanni Bajoe292e932012-04-22 14:32:02 +0200646
Giovanni Bajo50a96b62012-04-28 01:04:56 +0200647 /* We don't need the owner name anymore; now extract the signer name */
648 if (!extract_name_no_compression(sigrdata+18, signer_name_rdlen, signer_name))
649 return 0;
650
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200651 out->alg = alg;
652 out->keytag = keytag;
653 out->signer_name = signer_name;
654 return 1;
655}
656
657static int end_rrsig_validation(PendingRRSIGValidation *val, struct crec *crec_dnskey)
658{
Giovanni Bajoa7338642012-04-26 14:37:22 +0200659 /* FIXME: keydata is non-contiguous */
Giovanni Bajo708bcd22012-04-25 20:19:07 +0200660 return val->alg->vtbl->verify(val->alg, crec_dnskey->addr.key.keydata, crec_dnskey->uid);
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200661}
662
663static void dnssec_parserrsig(struct dns_header *header, size_t pktlen,
664 unsigned char *reply, int count, char *owner,
665 int sigclass, int sigrdlen, unsigned char *sig)
666{
667 PendingRRSIGValidation val;
668
669 /* Initiate the RRSIG validation process. The pending state is returned into val. */
670 if (!begin_rrsig_validation(header, pktlen, reply, count, owner, sigclass, sigrdlen, sig, &val))
671 return;
672
673 printf("RRSIG: querying cache for DNSKEY %s (keytag: %d)\n", val.signer_name, val.keytag);
Giovanni Bajo20bccd42012-04-25 20:22:16 +0200674
675 /* Look in the cache for *all* the DNSKEYs with matching signer_name and keytag */
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200676 char onekey = 0;
677 struct crec *crecp = NULL;
Giovanni Bajoa55ce082012-04-28 03:48:09 +0200678 while ((crecp = cache_find_by_name(crecp, val.signer_name, time(0), F_DNSKEY))) /* TODO: time(0) */
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200679 {
680 onekey = 1;
681
Giovanni Bajo20bccd42012-04-25 20:22:16 +0200682 if (crecp->addr.key.keytag == val.keytag
683 && crecp->addr.key.algo == verifyalg_algonum(val.alg))
684 {
685 printf("RRSIG: found DNSKEY %d in cache, attempting validation\n", val.keytag);
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200686
Giovanni Bajo20bccd42012-04-25 20:22:16 +0200687 if (end_rrsig_validation(&val, crecp))
688 printf("Validation OK\n");
689 else
690 printf("Validation FAILED\n");
691 }
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200692 }
693
694 if (!onekey)
695 {
Giovanni Bajoccca70c2012-04-25 20:15:35 +0200696 printf("DNSKEY not found, need to fetch it\n");
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200697 /* TODO: store PendingRRSIGValidation in routing table,
698 fetch key (and make it go through dnssec_parskey), then complete validation. */
699 }
Giovanni Bajoe292e932012-04-22 14:32:02 +0200700}
701
Giovanni Bajo3471f182012-04-25 17:49:16 +0200702/* Compute keytag (checksum to quickly index a key). See RFC4034 */
703static int dnskey_keytag(unsigned char *rdata, int rdlen)
704{
705 unsigned long ac;
706 int i;
707
708 ac = 0;
709 for (i = 0; i < rdlen; ++i)
710 ac += (i & 1) ? rdata[i] : rdata[i] << 8;
711 ac += (ac >> 16) & 0xFFFF;
712 return ac & 0xFFFF;
713}
714
715int dnssec_parsekey(struct dns_header *header, size_t pktlen, char *owner, unsigned long ttl,
716 int rdlen, unsigned char *rdata)
717{
718 int flags, proto, alg;
719 struct keydata *key; struct crec *crecp;
Giovanni Bajo3471f182012-04-25 17:49:16 +0200720 unsigned char *ordata = rdata; int ordlen = rdlen;
721
722 CHECKED_GETSHORT(flags, rdata, rdlen);
723 CHECKED_GETCHAR(proto, rdata, rdlen);
724 CHECKED_GETCHAR(alg, rdata, rdlen);
725
726 if (proto != 3)
727 return 0;
Giovanni Bajo0d829eb2012-04-25 18:17:50 +0200728 /* Skip non-signing keys (as specified in RFC4034 */
729 if (!(flags & 0x100))
730 return 0;
Giovanni Bajo3471f182012-04-25 17:49:16 +0200731
Giovanni Bajoa55ce082012-04-28 03:48:09 +0200732 key = keydata_alloc((char*)rdata, rdlen);
Giovanni Bajo3471f182012-04-25 17:49:16 +0200733
Giovanni Bajo3471f182012-04-25 17:49:16 +0200734 /* TODO: time(0) is correct here? */
735 crecp = cache_insert(owner, NULL, time(0), ttl, F_FORWARD | F_DNSKEY);
736 if (crecp)
737 {
738 /* TODO: improve union not to name "uid" this field */
739 crecp->uid = rdlen;
740 crecp->addr.key.keydata = key;
741 crecp->addr.key.algo = alg;
742 crecp->addr.key.keytag = dnskey_keytag(ordata, ordlen);
743 printf("DNSKEY: storing key for %s (keytag: %d)\n", owner, crecp->addr.key.keytag);
744 }
745 else
746 {
747 keydata_free(key);
748 /* TODO: if insertion really might fail, verify we don't depend on cache
749 insertion success for validation workflow correctness */
750 printf("DNSKEY: cache insertion failure\n");
751 return 0;
752 }
Giovanni Bajo3471f182012-04-25 17:49:16 +0200753 return 1;
754}
755
756int dnssec_parseds(struct dns_header *header, size_t pktlen, char *owner, unsigned long ttl,
757 int rdlen, unsigned char *rdata)
758{
759 return 0;
760}
Giovanni Bajoe292e932012-04-22 14:32:02 +0200761
762int dnssec_validate(struct dns_header *header, size_t pktlen)
763{
764 unsigned char *p, *reply;
765 char *owner = daemon->namebuff;
Giovanni Bajo23c21762012-04-28 12:22:41 +0200766 int i, s, qtype, qclass, rdlen;
Giovanni Bajoe292e932012-04-22 14:32:02 +0200767 unsigned long ttl;
Giovanni Bajo23c21762012-04-28 12:22:41 +0200768 int slen[3] = { ntohs(header->ancount), ntohs(header->nscount), ntohs(header->arcount) };
Giovanni Bajoe292e932012-04-22 14:32:02 +0200769
Giovanni Bajo23c21762012-04-28 12:22:41 +0200770 if (slen[0] + slen[1] + slen[2] == 0)
Giovanni Bajoe292e932012-04-22 14:32:02 +0200771 return 0;
772 if (!(reply = p = skip_questions(header, pktlen)))
773 return 0;
Giovanni Bajod0edff72012-04-25 20:16:22 +0200774
775 /* First, process DNSKEY/DS records and add them to the cache. */
776 cache_start_insert();
Giovanni Bajo23c21762012-04-28 12:22:41 +0200777 for (i = 0; i < slen[0]; i++)
Giovanni Bajoe292e932012-04-22 14:32:02 +0200778 {
779 if (!extract_name(header, pktlen, &p, owner, 1, 10))
780 return 0;
781 GETSHORT(qtype, p);
782 GETSHORT(qclass, p);
783 GETLONG(ttl, p);
784 GETSHORT(rdlen, p);
Giovanni Bajo3471f182012-04-25 17:49:16 +0200785 if (qtype == T_DS)
786 {
787 printf("DS found\n");
788 dnssec_parseds(header, pktlen, owner, ttl, rdlen, p);
789 }
790 else if (qtype == T_DNSKEY)
791 {
792 printf("DNSKEY found\n");
Giovanni Bajo47f99dd2012-04-25 18:03:52 +0200793 dnssec_parsekey(header, pktlen, owner, ttl, rdlen, p);
Giovanni Bajo3471f182012-04-25 17:49:16 +0200794 }
Giovanni Bajo4137b842012-04-25 18:13:41 +0200795 p += rdlen;
796 }
Giovanni Bajod0edff72012-04-25 20:16:22 +0200797 cache_end_insert();
Giovanni Bajo4137b842012-04-25 18:13:41 +0200798
Giovanni Bajod0edff72012-04-25 20:16:22 +0200799 /* After we have cached DNSKEY/DS records, start looking for RRSIGs.
Giovanni Bajo4137b842012-04-25 18:13:41 +0200800 We want to do this in a separate step because we want the cache
801 to be already populated with DNSKEYs before parsing signatures. */
802 p = reply;
Giovanni Bajo23c21762012-04-28 12:22:41 +0200803 for (s = 0; s < 3; ++s)
Giovanni Bajo4137b842012-04-25 18:13:41 +0200804 {
Giovanni Bajo23c21762012-04-28 12:22:41 +0200805 reply = p;
806 for (i = 0; i < slen[s]; i++)
Giovanni Bajoe292e932012-04-22 14:32:02 +0200807 {
Giovanni Bajo23c21762012-04-28 12:22:41 +0200808 if (!extract_name(header, pktlen, &p, owner, 1, 10))
809 return 0;
810 GETSHORT(qtype, p);
811 GETSHORT(qclass, p);
812 GETLONG(ttl, p);
813 GETSHORT(rdlen, p);
814 if (qtype == T_RRSIG)
815 {
816 printf("RRSIG found (owner: %s)\n", owner);
817 /* TODO: missing logic. We should only validate RRSIGs for which we
818 have a valid DNSKEY that is referenced by a DS record upstream.
819 There is a memory vs CPU conflict here; should we validate everything
820 to save memory and thus waste CPU, or better first acquire all information
821 (wasting memory) and then doing the minimum CPU computations required? */
822 dnssec_parserrsig(header, pktlen, reply, slen[s], owner, qclass, rdlen, p);
823 }
824 p += rdlen;
Giovanni Bajo4137b842012-04-25 18:13:41 +0200825 }
Giovanni Bajoe292e932012-04-22 14:32:02 +0200826 }
Giovanni Bajo4137b842012-04-25 18:13:41 +0200827
Giovanni Bajoe292e932012-04-22 14:32:02 +0200828 return 1;
829}