blob: 47bbbc4a3f1ac8767081ba8b5a8744836ded583f [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;
Giovanni Bajoda23c4f2012-05-02 00:31:19 +0200356 int rrtype;
Giovanni Bajo0ca895f2012-05-01 18:28:43 +0200357 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 GETSHORT(rrtype, pr1);
361 pr1 += 6; pr2 += 8;
Giovanni Bajoe292e932012-04-22 14:32:02 +0200362
Giovanni Bajoda23c4f2012-05-02 00:31:19 +0200363 RDataCForm cf1, cf2;
364 rdata_cform_init(&cf1, rrset_canonical_order_ctx.header, rrset_canonical_order_ctx.pktlen,
365 pr1, rrtype, tmp1);
366 rdata_cform_init(&cf2, rrset_canonical_order_ctx.header, rrset_canonical_order_ctx.pktlen,
367 pr2, rrtype, tmp2);
368 while ((pr1 = rdata_cform_next(&cf1, &r1len)) &&
369 (pr2 = rdata_cform_next(&cf2, &r2len)))
370 {
371 int res = memcmp(pr1, pr2, MIN(r1len,r2len));
372 if (res != 0)
373 return res;
374 if (r1len < r2len)
375 return -1;
376 if (r2len > r1len)
377 return 1;
378 }
Giovanni Bajo0ca895f2012-05-01 18:28:43 +0200379
Giovanni Bajoda23c4f2012-05-02 00:31:19 +0200380 /* If we reached this point, the two RRs are identical (or an error occurred).
Giovanni Bajo0ca895f2012-05-01 18:28:43 +0200381 RFC2181 says that an RRset is not allowed to contain duplicate
382 records. If it happens, it is a protocol error and anything goes. */
383 return 1;
Giovanni Bajoe292e932012-04-22 14:32:02 +0200384}
385
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200386typedef struct PendingRRSIGValidation
387{
388 VerifyAlgCtx *alg;
389 char *signer_name;
390 int keytag;
391} PendingRRSIGValidation;
392
Giovanni Bajo7f0485c2012-04-28 12:59:49 +0200393/* strchrnul - like strchr, but when character is not found, returns a pointer to the terminating \0.
394
395 This is an existing C GNU extension, but it's easier to reimplement it,
396 rather than tweaking with configure. */
397static char *strchrnul(char *str, char ch)
398{
399 while (*str && *str != ch)
400 str++;
401 return str;
402}
403
Giovanni Bajo13e435e2012-04-27 03:19:40 +0200404/* Pass a domain name through a verification hash function.
405
406 We must pass domain names in DNS wire format, but uncompressed.
407 This means that we cannot directly use raw data from the original
408 message since it might be compressed. */
409static void verifyalg_add_data_domain(VerifyAlgCtx *alg, char* name)
410{
411 unsigned char len; char *p;
412
Giovanni Bajo7f0485c2012-04-28 12:59:49 +0200413 do
Giovanni Bajo13e435e2012-04-27 03:19:40 +0200414 {
Giovanni Bajo7f0485c2012-04-28 12:59:49 +0200415 p = strchrnul(name, '.');
416 if ((len = p-name))
417 {
418 alg->vtbl->add_data(alg, &len, 1);
419 alg->vtbl->add_data(alg, name, len);
420 }
Giovanni Bajo13e435e2012-04-27 03:19:40 +0200421 name = p+1;
422 }
Giovanni Bajo7f0485c2012-04-28 12:59:49 +0200423 while (*p);
424
425 alg->vtbl->add_data(alg, "\0", 1);
Giovanni Bajo13e435e2012-04-27 03:19:40 +0200426}
427
Giovanni Bajo0ca895f2012-05-01 18:28:43 +0200428
Giovanni Bajo0852d762012-04-28 03:49:24 +0200429/* Pass a resource record's rdata field through a verification hash function.
430
431 We must pass the record in DNS wire format, but if the record contains domain names,
432 they must be uncompressed. This makes things very tricky, because */
433static int verifyalg_add_rdata(VerifyAlgCtx *alg, int sigtype, struct dns_header *header, size_t pktlen,
434 unsigned char *rdata)
435{
Giovanni Bajof119ed32012-05-02 00:31:55 +0200436 size_t len;
Giovanni Bajo0852d762012-04-28 03:49:24 +0200437 unsigned char *p;
Giovanni Bajof119ed32012-05-02 00:31:55 +0200438 unsigned short total;
439 unsigned char tmpbuf[MAXDNAME]; /* TODO: reuse part of daemon->namebuff */
440 RDataCForm cf1, cf2;
Giovanni Bajo0852d762012-04-28 03:49:24 +0200441
Giovanni Bajof119ed32012-05-02 00:31:55 +0200442 /* Initialize two iterations over the canonical form*/
443 rdata_cform_init(&cf1, header, pktlen, rdata, sigtype, tmpbuf);
444 cf2 = cf1;
Giovanni Bajo0852d762012-04-28 03:49:24 +0200445
Giovanni Bajof119ed32012-05-02 00:31:55 +0200446 /* Iteration 1: go through the canonical record and count the total octects.
447 This number might be different from the non-canonical rdata length
448 because of domain names compression. */
449 total = 0;
450 while ((p = rdata_cform_next(&cf1, &len)))
451 total += len;
452 if (rdata_cform_error(&cf1))
453 return 0;
Giovanni Bajo0852d762012-04-28 03:49:24 +0200454
Giovanni Bajof119ed32012-05-02 00:31:55 +0200455 /* Iteration 2: process the canonical record through the hash function */
456 total = htons(total);
457 alg->vtbl->add_data(alg, &total, 2);
458
459 while ((p = rdata_cform_next(&cf2, &len)))
460 alg->vtbl->add_data(alg, p, len);
461
Giovanni Bajo0852d762012-04-28 03:49:24 +0200462 return 1;
463}
464
465
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200466static int begin_rrsig_validation(struct dns_header *header, size_t pktlen,
467 unsigned char *reply, int count, char *owner,
468 int sigclass, int sigrdlen, unsigned char *sig,
469 PendingRRSIGValidation *out)
Giovanni Bajoe292e932012-04-22 14:32:02 +0200470{
471 int i, res;
472 int sigtype, sigalg, siglbl;
473 unsigned char *sigrdata = sig;
474 unsigned long sigttl, date_end, date_start;
475 unsigned char* p = reply;
476 char* signer_name = daemon->namebuff;
Giovanni Bajo13e435e2012-04-27 03:19:40 +0200477 int signer_name_rdlen;
Giovanni Bajoe292e932012-04-22 14:32:02 +0200478 int keytag;
479 void *rrset[16]; /* TODO: max RRset size? */
480 int rrsetidx = 0;
481
482 if (sigrdlen < 18)
483 return 0;
484 GETSHORT(sigtype, sig);
485 sigalg = *sig++;
486 siglbl = *sig++;
487 GETLONG(sigttl, sig);
488 GETLONG(date_end, sig);
489 GETLONG(date_start, sig);
490 GETSHORT(keytag, sig);
491 sigrdlen -= 18;
492
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200493 if (!verifyalg_supported(sigalg))
Giovanni Bajoe292e932012-04-22 14:32:02 +0200494 {
495 printf("RRSIG algorithm not supported: %d\n", sigalg);
496 return 0;
497 }
498
Giovanni Bajod31d0572012-04-24 02:02:55 +0200499 if (!check_date_range(date_start, date_end))
Giovanni Bajoe292e932012-04-22 14:32:02 +0200500 {
501 printf("RRSIG outside date range\n");
502 return 0;
503 }
504
505 /* Iterate within the answer and find the RRsets matching the current RRsig */
506 for (i = 0; i < count; ++i)
507 {
508 int qtype, qclass, rdlen;
509 if (!(res = extract_name(header, pktlen, &p, owner, 0, 10)))
510 return 0;
511 rrset[rrsetidx] = p;
512 GETSHORT(qtype, p);
513 GETSHORT(qclass, p);
514 p += 4; /* skip ttl */
515 GETSHORT(rdlen, p);
516 if (res == 1 && qtype == sigtype && qclass == sigclass)
517 {
518 ++rrsetidx;
Giovanni Bajo382e38f2012-04-24 01:46:47 +0200519 if (rrsetidx == countof(rrset))
520 {
521 /* Internal buffer too small */
522 printf("internal buffer too small for this RRset\n");
523 return 0;
524 }
Giovanni Bajoe292e932012-04-22 14:32:02 +0200525 }
526 p += rdlen;
527 }
528
529 /* Sort RRset records in canonical order. */
Giovanni Bajo0ca895f2012-05-01 18:28:43 +0200530 rrset_canonical_order_ctx.header = header;
531 rrset_canonical_order_ctx.pktlen = pktlen;
Giovanni Bajoe292e932012-04-22 14:32:02 +0200532 qsort(rrset, rrsetidx, sizeof(void*), rrset_canonical_order);
533
Giovanni Bajo50a96b62012-04-28 01:04:56 +0200534 /* Skip through the signer name; we don't extract it right now because
535 we don't want to overwrite the single daemon->namebuff which contains
536 the owner name. We'll get to this later. */
537 if (!(p = skip_name(sig, header, pktlen, 0)))
Giovanni Bajoe292e932012-04-22 14:32:02 +0200538 return 0;
Giovanni Bajo50a96b62012-04-28 01:04:56 +0200539 signer_name_rdlen = p - sig;
540 sig = p; sigrdlen -= signer_name_rdlen;
Giovanni Bajo13e435e2012-04-27 03:19:40 +0200541
Giovanni Bajoe292e932012-04-22 14:32:02 +0200542 /* Now initialize the signature verification algorithm and process the whole
543 RRset */
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200544 VerifyAlgCtx *alg = verifyalg_alloc(sigalg);
545 if (!alg)
546 return 0;
547 if (!alg->vtbl->set_signature(alg, sig, sigrdlen))
Giovanni Bajoe292e932012-04-22 14:32:02 +0200548 return 0;
549
Giovanni Bajo4b0eecb2012-04-27 03:18:52 +0200550 sigtype = htons(sigtype);
551 sigclass = htons(sigclass);
552 sigttl = htonl(sigttl);
553
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200554 alg->vtbl->begin_data(alg);
Giovanni Bajo13e435e2012-04-27 03:19:40 +0200555 alg->vtbl->add_data(alg, sigrdata, 18+signer_name_rdlen);
Giovanni Bajoe292e932012-04-22 14:32:02 +0200556 for (i = 0; i < rrsetidx; ++i)
557 {
Giovanni Bajo13e435e2012-04-27 03:19:40 +0200558 p = (unsigned char*)(rrset[i]);
559
560 verifyalg_add_data_domain(alg, owner);
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200561 alg->vtbl->add_data(alg, &sigtype, 2);
562 alg->vtbl->add_data(alg, &sigclass, 2);
563 alg->vtbl->add_data(alg, &sigttl, 4);
Giovanni Bajoe292e932012-04-22 14:32:02 +0200564
Giovanni Bajoe292e932012-04-22 14:32:02 +0200565 p += 8;
Giovanni Bajo0852d762012-04-28 03:49:24 +0200566 if (!verifyalg_add_rdata(alg, ntohs(sigtype), header, pktlen, p))
567 return 0;
Giovanni Bajoe292e932012-04-22 14:32:02 +0200568 }
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200569 alg->vtbl->end_data(alg);
Giovanni Bajoe292e932012-04-22 14:32:02 +0200570
Giovanni Bajo50a96b62012-04-28 01:04:56 +0200571 /* We don't need the owner name anymore; now extract the signer name */
572 if (!extract_name_no_compression(sigrdata+18, signer_name_rdlen, signer_name))
573 return 0;
574
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200575 out->alg = alg;
576 out->keytag = keytag;
577 out->signer_name = signer_name;
578 return 1;
579}
580
581static int end_rrsig_validation(PendingRRSIGValidation *val, struct crec *crec_dnskey)
582{
Giovanni Bajoa7338642012-04-26 14:37:22 +0200583 /* FIXME: keydata is non-contiguous */
Giovanni Bajo708bcd22012-04-25 20:19:07 +0200584 return val->alg->vtbl->verify(val->alg, crec_dnskey->addr.key.keydata, crec_dnskey->uid);
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200585}
586
587static void dnssec_parserrsig(struct dns_header *header, size_t pktlen,
588 unsigned char *reply, int count, char *owner,
589 int sigclass, int sigrdlen, unsigned char *sig)
590{
591 PendingRRSIGValidation val;
592
593 /* Initiate the RRSIG validation process. The pending state is returned into val. */
594 if (!begin_rrsig_validation(header, pktlen, reply, count, owner, sigclass, sigrdlen, sig, &val))
595 return;
596
597 printf("RRSIG: querying cache for DNSKEY %s (keytag: %d)\n", val.signer_name, val.keytag);
Giovanni Bajo20bccd42012-04-25 20:22:16 +0200598
599 /* Look in the cache for *all* the DNSKEYs with matching signer_name and keytag */
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200600 char onekey = 0;
601 struct crec *crecp = NULL;
Giovanni Bajoa55ce082012-04-28 03:48:09 +0200602 while ((crecp = cache_find_by_name(crecp, val.signer_name, time(0), F_DNSKEY))) /* TODO: time(0) */
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200603 {
604 onekey = 1;
605
Giovanni Bajo20bccd42012-04-25 20:22:16 +0200606 if (crecp->addr.key.keytag == val.keytag
607 && crecp->addr.key.algo == verifyalg_algonum(val.alg))
608 {
609 printf("RRSIG: found DNSKEY %d in cache, attempting validation\n", val.keytag);
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200610
Giovanni Bajo20bccd42012-04-25 20:22:16 +0200611 if (end_rrsig_validation(&val, crecp))
612 printf("Validation OK\n");
613 else
614 printf("Validation FAILED\n");
615 }
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200616 }
617
618 if (!onekey)
619 {
Giovanni Bajoccca70c2012-04-25 20:15:35 +0200620 printf("DNSKEY not found, need to fetch it\n");
Giovanni Bajoadca3e92012-04-25 17:46:53 +0200621 /* TODO: store PendingRRSIGValidation in routing table,
622 fetch key (and make it go through dnssec_parskey), then complete validation. */
623 }
Giovanni Bajoe292e932012-04-22 14:32:02 +0200624}
625
Giovanni Bajo3471f182012-04-25 17:49:16 +0200626/* Compute keytag (checksum to quickly index a key). See RFC4034 */
627static int dnskey_keytag(unsigned char *rdata, int rdlen)
628{
629 unsigned long ac;
630 int i;
631
632 ac = 0;
633 for (i = 0; i < rdlen; ++i)
634 ac += (i & 1) ? rdata[i] : rdata[i] << 8;
635 ac += (ac >> 16) & 0xFFFF;
636 return ac & 0xFFFF;
637}
638
639int dnssec_parsekey(struct dns_header *header, size_t pktlen, char *owner, unsigned long ttl,
640 int rdlen, unsigned char *rdata)
641{
642 int flags, proto, alg;
643 struct keydata *key; struct crec *crecp;
Giovanni Bajo3471f182012-04-25 17:49:16 +0200644 unsigned char *ordata = rdata; int ordlen = rdlen;
645
646 CHECKED_GETSHORT(flags, rdata, rdlen);
647 CHECKED_GETCHAR(proto, rdata, rdlen);
648 CHECKED_GETCHAR(alg, rdata, rdlen);
649
650 if (proto != 3)
651 return 0;
Giovanni Bajo0d829eb2012-04-25 18:17:50 +0200652 /* Skip non-signing keys (as specified in RFC4034 */
653 if (!(flags & 0x100))
654 return 0;
Giovanni Bajo3471f182012-04-25 17:49:16 +0200655
Giovanni Bajoa55ce082012-04-28 03:48:09 +0200656 key = keydata_alloc((char*)rdata, rdlen);
Giovanni Bajo3471f182012-04-25 17:49:16 +0200657
Giovanni Bajo3471f182012-04-25 17:49:16 +0200658 /* TODO: time(0) is correct here? */
659 crecp = cache_insert(owner, NULL, time(0), ttl, F_FORWARD | F_DNSKEY);
660 if (crecp)
661 {
662 /* TODO: improve union not to name "uid" this field */
663 crecp->uid = rdlen;
664 crecp->addr.key.keydata = key;
665 crecp->addr.key.algo = alg;
666 crecp->addr.key.keytag = dnskey_keytag(ordata, ordlen);
667 printf("DNSKEY: storing key for %s (keytag: %d)\n", owner, crecp->addr.key.keytag);
668 }
669 else
670 {
671 keydata_free(key);
672 /* TODO: if insertion really might fail, verify we don't depend on cache
673 insertion success for validation workflow correctness */
674 printf("DNSKEY: cache insertion failure\n");
675 return 0;
676 }
Giovanni Bajo3471f182012-04-25 17:49:16 +0200677 return 1;
678}
679
680int dnssec_parseds(struct dns_header *header, size_t pktlen, char *owner, unsigned long ttl,
681 int rdlen, unsigned char *rdata)
682{
683 return 0;
684}
Giovanni Bajoe292e932012-04-22 14:32:02 +0200685
686int dnssec_validate(struct dns_header *header, size_t pktlen)
687{
688 unsigned char *p, *reply;
689 char *owner = daemon->namebuff;
Giovanni Bajo23c21762012-04-28 12:22:41 +0200690 int i, s, qtype, qclass, rdlen;
Giovanni Bajoe292e932012-04-22 14:32:02 +0200691 unsigned long ttl;
Giovanni Bajo23c21762012-04-28 12:22:41 +0200692 int slen[3] = { ntohs(header->ancount), ntohs(header->nscount), ntohs(header->arcount) };
Giovanni Bajoe292e932012-04-22 14:32:02 +0200693
Giovanni Bajo23c21762012-04-28 12:22:41 +0200694 if (slen[0] + slen[1] + slen[2] == 0)
Giovanni Bajoe292e932012-04-22 14:32:02 +0200695 return 0;
696 if (!(reply = p = skip_questions(header, pktlen)))
697 return 0;
Giovanni Bajod0edff72012-04-25 20:16:22 +0200698
699 /* First, process DNSKEY/DS records and add them to the cache. */
700 cache_start_insert();
Giovanni Bajo23c21762012-04-28 12:22:41 +0200701 for (i = 0; i < slen[0]; i++)
Giovanni Bajoe292e932012-04-22 14:32:02 +0200702 {
703 if (!extract_name(header, pktlen, &p, owner, 1, 10))
704 return 0;
705 GETSHORT(qtype, p);
706 GETSHORT(qclass, p);
707 GETLONG(ttl, p);
708 GETSHORT(rdlen, p);
Giovanni Bajo3471f182012-04-25 17:49:16 +0200709 if (qtype == T_DS)
710 {
711 printf("DS found\n");
712 dnssec_parseds(header, pktlen, owner, ttl, rdlen, p);
713 }
714 else if (qtype == T_DNSKEY)
715 {
716 printf("DNSKEY found\n");
Giovanni Bajo47f99dd2012-04-25 18:03:52 +0200717 dnssec_parsekey(header, pktlen, owner, ttl, rdlen, p);
Giovanni Bajo3471f182012-04-25 17:49:16 +0200718 }
Giovanni Bajo4137b842012-04-25 18:13:41 +0200719 p += rdlen;
720 }
Giovanni Bajod0edff72012-04-25 20:16:22 +0200721 cache_end_insert();
Giovanni Bajo4137b842012-04-25 18:13:41 +0200722
Giovanni Bajod0edff72012-04-25 20:16:22 +0200723 /* After we have cached DNSKEY/DS records, start looking for RRSIGs.
Giovanni Bajo4137b842012-04-25 18:13:41 +0200724 We want to do this in a separate step because we want the cache
725 to be already populated with DNSKEYs before parsing signatures. */
726 p = reply;
Giovanni Bajo23c21762012-04-28 12:22:41 +0200727 for (s = 0; s < 3; ++s)
Giovanni Bajo4137b842012-04-25 18:13:41 +0200728 {
Giovanni Bajo23c21762012-04-28 12:22:41 +0200729 reply = p;
730 for (i = 0; i < slen[s]; i++)
Giovanni Bajoe292e932012-04-22 14:32:02 +0200731 {
Giovanni Bajo23c21762012-04-28 12:22:41 +0200732 if (!extract_name(header, pktlen, &p, owner, 1, 10))
733 return 0;
734 GETSHORT(qtype, p);
735 GETSHORT(qclass, p);
736 GETLONG(ttl, p);
737 GETSHORT(rdlen, p);
738 if (qtype == T_RRSIG)
739 {
740 printf("RRSIG found (owner: %s)\n", owner);
741 /* TODO: missing logic. We should only validate RRSIGs for which we
742 have a valid DNSKEY that is referenced by a DS record upstream.
743 There is a memory vs CPU conflict here; should we validate everything
744 to save memory and thus waste CPU, or better first acquire all information
745 (wasting memory) and then doing the minimum CPU computations required? */
746 dnssec_parserrsig(header, pktlen, reply, slen[s], owner, qclass, rdlen, p);
747 }
748 p += rdlen;
Giovanni Bajo4137b842012-04-25 18:13:41 +0200749 }
Giovanni Bajoe292e932012-04-22 14:32:02 +0200750 }
Giovanni Bajo4137b842012-04-25 18:13:41 +0200751
Giovanni Bajoe292e932012-04-22 14:32:02 +0200752 return 1;
753}