blob: 4cf9503719e315063f5771589966052125b3f985 [file] [log] [blame]
Giovanni Bajoe292e932012-04-22 14:32:02 +02001
2#include "dnsmasq.h"
3#include <assert.h>
4
5#define SERIAL_UNDEF -100
6#define SERIAL_EQ 0
7#define SERIAL_LT -1
8#define SERIAL_GT 1
9
10#define countof(x) (long)(sizeof(x) / sizeof(x[0]))
11#define MIN(a,b) ((a) < (b) ? (a) : (b))
12
13/*
14 * vtable for a signature verification algorithm.
15 *
16 * Each algorithm verifies that a certain signature over a (possibly non-contigous)
17 * array of data has been made with the specified key.
18 *
19 * Sample of usage:
20 *
21 * // First, set the signature we need to check. Notice: data is not copied
22 * // nor consumed, so the pointer must stay valid.
23 * alg->set_signature(sig, 16);
24 *
25 * // Second, push the data in; data is consumed immediately, so the buffer
26 * // can be freed or modified.
27 * alg->begin_data();
28 * alg->add_data(buf1, 123);
29 * alg->add_data(buf2, 45);
30 * alg->add_data(buf3, 678);
31 * alg->end_data();
32 *
33 * // Third, verify if we got the correct key for this signature.
34 * alg->verify(key1, 16);
35 * alg->verify(key2, 16);
36 */
37typedef struct
38{
39 int (*set_signature)(unsigned char *data, unsigned len);
40 void (*begin_data)(void);
41 void (*add_data)(void *data, unsigned len);
42 void (*end_data)(void);
43 int (*verify)(unsigned char *key, unsigned key_len);
44} VerifyAlg;
45
Giovanni Bajo970ce222012-04-22 15:22:07 +020046/* Updated registry that merges various RFCs:
47 https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xml */
48static const VerifyAlg valgs[] =
Giovanni Bajoe292e932012-04-22 14:32:02 +020049{
Giovanni Bajo970ce222012-04-22 15:22:07 +020050 {0,0,0,0,0}, /* 0: reserved */
51 {0,0,0,0,0}, /* 1: RSAMD5 */
52 {0,0,0,0,0}, /* 2: DH */
53 {0,0,0,0,0}, /* 3: DSA */
54 {0,0,0,0,0}, /* 4: ECC */
55 {0,0,0,0,0}, /* 5: RSASHA1 */
56 {0,0,0,0,0}, /* 6: DSA-NSEC3-SHA1 */
57 {0,0,0,0,0}, /* 7: RSASHA1-NSEC3-SHA1 */
58 {0,0,0,0,0}, /* 8: RSASHA256 */
59 {0,0,0,0,0}, /* 9: unassigned */
60 {0,0,0,0,0}, /* 10: RSASHA512 */
61 {0,0,0,0,0}, /* 11: unassigned */
62 {0,0,0,0,0}, /* 12: ECC-GOST */
63 {0,0,0,0,0}, /* 13: ECDSAP256SHA256 */
64 {0,0,0,0,0}, /* 14: ECDSAP384SHA384 */
Giovanni Bajoe292e932012-04-22 14:32:02 +020065};
66
67/* Implement RFC1982 wrapped compare for 32-bit numbers */
68static int serial_compare_32(unsigned long s1, unsigned long s2)
69{
70 if (s1 == s2)
71 return SERIAL_EQ;
72
73 if ((s1 < s2 && (s2 - s1) < (1UL<<31)) ||
74 (s1 > s2 && (s1 - s2) > (1UL<<31)))
75 return SERIAL_LT;
76 if ((s1 < s2 && (s2 - s1) > (1UL<<31)) ||
77 (s1 > s2 && (s1 - s2) < (1UL<<31)))
78 return SERIAL_GT;
79 return SERIAL_UNDEF;
80}
81
82/* Extract a DNS name from wire format, without handling compression. This is
83 faster than extract_name() and does not require access to the full dns
84 packet. */
85static int extract_name_no_compression(unsigned char *rr, int maxlen, char *buf)
86{
87 unsigned char *start=rr, *end = rr+maxlen;
88 int count;
89
90 while (rr < end && *rr != 0)
91 {
92 count = *rr++;
93 while (count-- >= 0 && rr < end)
94 {
95 *buf = *rr++;
96 if (*buf >= 'A' && *buf <= 'Z')
97 *buf += 'a' - 'A';
98 buf++;
99 }
100 *buf++ = '.';
101 }
Giovanni Bajoc7a93f62012-04-22 15:53:52 +0200102 rr++;
Giovanni Bajoe292e932012-04-22 14:32:02 +0200103 *buf = 0;
104 if (rr == end)
105 return 0;
106 return rr-start;
107}
108
109/* Check whether today/now is between date_start and date_end */
110static int check_date_range(unsigned long date_start, unsigned long date_end)
111{
112 /* TODO: double-check that time(0) is the correct time we are looking for */
113 /* TODO: dnssec requires correct timing; implement SNTP in dnsmasq? */
114 unsigned long curtime = time(0);
115
116 /* We must explicitly check against wanted values, because of SERIAL_UNDEF */
117 if (serial_compare_32(curtime, date_start) != SERIAL_GT)
118 return 0;
119 if (serial_compare_32(curtime, date_end) != SERIAL_LT)
120 return 0;
121 return 1;
122}
123
124/* Sort RRs within a RRset in canonical order, according to RFC4034, ยง6.3
125 Notice that the RRDATA sections have been already normalized, so a memcpy
126 is sufficient.
127 NOTE: r1/r2 point immediately after the owner name. */
128static int rrset_canonical_order(const void *r1, const void *r2)
129{
130 int r1len, r2len, res;
131 const unsigned char *pr1=r1, *pr2=r2;
132
133 pr1 += 8; pr2 += 8;
134 GETSHORT(r1len, pr1); GETSHORT(r2len, pr2);
135
136 /* Lexicographically compare RDATA (thus, if equal, smaller length wins) */
137 res = memcmp(pr1, pr2, MIN(r1len, r2len));
138 if (res == 0)
139 {
140 if (r1len < r2len)
141 return -1;
142 else
143 /* NOTE: RFC2181 says that an RRset is not allowed to contain duplicate
144 records. If it happens, it is a protocol error and anything goes. */
145 return 1;
146 }
147
148 return res;
149}
150
151static int validate_rrsig(struct dns_header *header, size_t pktlen,
152 unsigned char *reply, int count, char *owner,
153 int sigclass, int sigrdlen, unsigned char *sig)
154{
155 int i, res;
156 int sigtype, sigalg, siglbl;
157 unsigned char *sigrdata = sig;
158 unsigned long sigttl, date_end, date_start;
159 unsigned char* p = reply;
160 char* signer_name = daemon->namebuff;
161 int keytag;
162 void *rrset[16]; /* TODO: max RRset size? */
163 int rrsetidx = 0;
164
165 if (sigrdlen < 18)
166 return 0;
167 GETSHORT(sigtype, sig);
168 sigalg = *sig++;
169 siglbl = *sig++;
170 GETLONG(sigttl, sig);
171 GETLONG(date_end, sig);
172 GETLONG(date_start, sig);
173 GETSHORT(keytag, sig);
174 sigrdlen -= 18;
175
176 if (sigalg >= countof(valgs) || !valgs[sigalg].set_signature)
177 {
178 printf("RRSIG algorithm not supported: %d\n", sigalg);
179 return 0;
180 }
181
182 if (!check_date_range(ntohl(date_start), ntohl(date_end)))
183 {
184 printf("RRSIG outside date range\n");
185 return 0;
186 }
187
188 /* Iterate within the answer and find the RRsets matching the current RRsig */
189 for (i = 0; i < count; ++i)
190 {
191 int qtype, qclass, rdlen;
192 if (!(res = extract_name(header, pktlen, &p, owner, 0, 10)))
193 return 0;
194 rrset[rrsetidx] = p;
195 GETSHORT(qtype, p);
196 GETSHORT(qclass, p);
197 p += 4; /* skip ttl */
198 GETSHORT(rdlen, p);
199 if (res == 1 && qtype == sigtype && qclass == sigclass)
200 {
201 ++rrsetidx;
202 assert(rrsetidx < countof(rrset));
203 /* TODO: here we should convert to lowercase domain names within
204 RDATA. We can do it in place. */
205 }
206 p += rdlen;
207 }
208
209 /* Sort RRset records in canonical order. */
210 qsort(rrset, rrsetidx, sizeof(void*), rrset_canonical_order);
211
212 /* Extract the signer name (we need to query DNSKEY of this name) */
213 if (!(res = extract_name_no_compression(sig, sigrdlen, signer_name)))
214 return 0;
215 sig += res; sigrdlen -= res;
216
217 /* Now initialize the signature verification algorithm and process the whole
218 RRset */
219 const VerifyAlg *alg = &valgs[sigalg];
220 if (!alg->set_signature(sig, sigrdlen))
221 return 0;
222
223 alg->begin_data();
224 alg->add_data(sigrdata, 18);
225 alg->add_data(signer_name, strlen(signer_name)-1); /* remove trailing dot */
226 for (i = 0; i < rrsetidx; ++i)
227 {
228 int rdlen;
229
230 alg->add_data(owner, strlen(owner));
231 alg->add_data(&sigtype, 2);
232 alg->add_data(&sigclass, 2);
233 alg->add_data(&sigttl, 4);
234
235 p = (unsigned char*)(rrset[i]);
236 p += 8;
237 GETSHORT(rdlen, p);
238 alg->add_data(p-2, rdlen+2);
239 }
240 alg->end_data();
241
242 /* TODO: now we need to fetch the DNSKEY of signer_name with the specified
243 keytag, and check whether it validates with the current algorithm. */
244 /*
245 pseudo-code:
246
247 char *key; int keylen;
248 if (!fetch_dnskey(signer_name, keytag, &key, &keylen))
249 return 0;
250 return alg->verify(key, keylen);
251 */
252 return 0;
253}
254
255
256int dnssec_validate(struct dns_header *header, size_t pktlen)
257{
258 unsigned char *p, *reply;
259 char *owner = daemon->namebuff;
260 int i, qtype, qclass, rdlen;
261 unsigned long ttl;
262
263 if (header->ancount == 0)
264 return 0;
265 if (!(reply = p = skip_questions(header, pktlen)))
266 return 0;
267 for (i = 0; i < ntohs(header->ancount); i++)
268 {
269 if (!extract_name(header, pktlen, &p, owner, 1, 10))
270 return 0;
271 GETSHORT(qtype, p);
272 GETSHORT(qclass, p);
273 GETLONG(ttl, p);
274 GETSHORT(rdlen, p);
275 if (qtype == T_RRSIG)
276 {
277 printf("RRSIG found\n");
278 /* TODO: missing logic. We should only validate RRSIGs for which we
279 have a valid DNSKEY that is referenced by a DS record upstream.
280 There is a memory vs CPU conflict here; should we validate everything
281 to save memory and thus waste CPU, or better first acquire all information
282 (wasting memory) and then doing the minimum CPU computations required? */
283 validate_rrsig(header, pktlen, reply, ntohs(header->ancount), owner, qclass, rdlen, p);
284 }
285 p += rdlen;
286 }
287 return 1;
288}