]>
git.ipfire.org Git - people/ms/dnsmasq.git/blob - src/dnssec.c
3 #include "dnssec-crypto.h"
6 #define SERIAL_UNDEF -100
11 /* Updated registry that merges various RFCs:
12 https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xml */
13 static const VerifyAlg valgs
[] =
15 {0,0,0,0,0}, /* 0: reserved */
16 {0,0,0,0,0}, /* 1: RSAMD5 */
17 {0,0,0,0,0}, /* 2: DH */
18 {0,0,0,0,0}, /* 3: DSA */
19 {0,0,0,0,0}, /* 4: ECC */
20 VALG_VTABLE(rsasha1
), /* 5: RSASHA1 */
21 {0,0,0,0,0}, /* 6: DSA-NSEC3-SHA1 */
22 {0,0,0,0,0}, /* 7: RSASHA1-NSEC3-SHA1 */
23 VALG_VTABLE(rsasha256
), /* 8: RSASHA256 */
24 {0,0,0,0,0}, /* 9: unassigned */
25 {0,0,0,0,0}, /* 10: RSASHA512 */
26 {0,0,0,0,0}, /* 11: unassigned */
27 {0,0,0,0,0}, /* 12: ECC-GOST */
28 {0,0,0,0,0}, /* 13: ECDSAP256SHA256 */
29 {0,0,0,0,0}, /* 14: ECDSAP384SHA384 */
32 /* Implement RFC1982 wrapped compare for 32-bit numbers */
33 static int serial_compare_32(unsigned long s1
, unsigned long s2
)
38 if ((s1
< s2
&& (s2
- s1
) < (1UL<<31)) ||
39 (s1
> s2
&& (s1
- s2
) > (1UL<<31)))
41 if ((s1
< s2
&& (s2
- s1
) > (1UL<<31)) ||
42 (s1
> s2
&& (s1
- s2
) < (1UL<<31)))
47 /* Extract a DNS name from wire format, without handling compression. This is
48 faster than extract_name() and does not require access to the full dns
50 static int extract_name_no_compression(unsigned char *rr
, int maxlen
, char *buf
)
52 unsigned char *start
=rr
, *end
= rr
+maxlen
;
55 while (rr
< end
&& *rr
!= 0)
58 while (count
-- > 0 && rr
< end
)
61 if (!isascii(*buf
) || iscntrl(*buf
) || *buf
== '.')
63 if (*buf
>= 'A' && *buf
<= 'Z')
76 /* Check whether today/now is between date_start and date_end */
77 static int check_date_range(unsigned long date_start
, unsigned long date_end
)
79 /* TODO: double-check that time(0) is the correct time we are looking for */
80 /* TODO: dnssec requires correct timing; implement SNTP in dnsmasq? */
81 unsigned long curtime
= time(0);
83 /* We must explicitly check against wanted values, because of SERIAL_UNDEF */
84 if (serial_compare_32(curtime
, date_start
) != SERIAL_GT
)
86 if (serial_compare_32(curtime
, date_end
) != SERIAL_LT
)
91 /* Sort RRs within a RRset in canonical order, according to RFC4034, ยง6.3
92 Notice that the RRDATA sections have been already normalized, so a memcpy
94 NOTE: r1/r2 point immediately after the owner name. */
95 static int rrset_canonical_order(const void *r1
, const void *r2
)
97 int r1len
, r2len
, res
;
98 const unsigned char *pr1
=*(unsigned char**)r1
, *pr2
=*(unsigned char**)r2
;
101 GETSHORT(r1len
, pr1
); GETSHORT(r2len
, pr2
);
103 /* Lexicographically compare RDATA (thus, if equal, smaller length wins) */
104 res
= memcmp(pr1
, pr2
, MIN(r1len
, r2len
));
110 /* NOTE: RFC2181 says that an RRset is not allowed to contain duplicate
111 records. If it happens, it is a protocol error and anything goes. */
118 static int validate_rrsig(struct dns_header
*header
, size_t pktlen
,
119 unsigned char *reply
, int count
, char *owner
,
120 int sigclass
, int sigrdlen
, unsigned char *sig
)
123 int sigtype
, sigalg
, siglbl
;
124 unsigned char *sigrdata
= sig
;
125 unsigned long sigttl
, date_end
, date_start
;
126 unsigned char* p
= reply
;
127 char* signer_name
= daemon
->namebuff
;
129 void *rrset
[16]; /* TODO: max RRset size? */
134 GETSHORT(sigtype
, sig
);
137 GETLONG(sigttl
, sig
);
138 GETLONG(date_end
, sig
);
139 GETLONG(date_start
, sig
);
140 GETSHORT(keytag
, sig
);
143 if (sigalg
>= countof(valgs
) || !valgs
[sigalg
].set_signature
)
145 printf("RRSIG algorithm not supported: %d\n", sigalg
);
149 if (!check_date_range(date_start
, date_end
))
151 printf("RRSIG outside date range\n");
155 /* Iterate within the answer and find the RRsets matching the current RRsig */
156 for (i
= 0; i
< count
; ++i
)
158 int qtype
, qclass
, rdlen
;
159 if (!(res
= extract_name(header
, pktlen
, &p
, owner
, 0, 10)))
164 p
+= 4; /* skip ttl */
166 if (res
== 1 && qtype
== sigtype
&& qclass
== sigclass
)
169 if (rrsetidx
== countof(rrset
))
171 /* Internal buffer too small */
172 printf("internal buffer too small for this RRset\n");
179 /* Sort RRset records in canonical order. */
180 qsort(rrset
, rrsetidx
, sizeof(void*), rrset_canonical_order
);
182 /* Extract the signer name (we need to query DNSKEY of this name) */
183 if (!(res
= extract_name_no_compression(sig
, sigrdlen
, signer_name
)))
185 sig
+= res
; sigrdlen
-= res
;
187 /* Now initialize the signature verification algorithm and process the whole
189 const VerifyAlg
*alg
= &valgs
[sigalg
];
190 if (!alg
->set_signature(sig
, sigrdlen
))
194 alg
->add_data(sigrdata
, 18);
195 alg
->add_data(signer_name
, strlen(signer_name
)-1); /* remove trailing dot */
196 for (i
= 0; i
< rrsetidx
; ++i
)
200 alg
->add_data(owner
, strlen(owner
));
201 alg
->add_data(&sigtype
, 2);
202 alg
->add_data(&sigclass
, 2);
203 alg
->add_data(&sigttl
, 4);
205 p
= (unsigned char*)(rrset
[i
]);
208 /* TODO: instead of a direct add_data(), we must call a RRtype-specific
209 function, that extract and canonicalizes domain names within RDATA. */
210 alg
->add_data(p
-2, rdlen
+2);
214 /* TODO: now we need to fetch the DNSKEY of signer_name with the specified
215 keytag, and check whether it validates with the current algorithm. */
219 char *key; int keylen;
220 if (!fetch_dnskey(signer_name, keytag, &key, &keylen))
222 return alg->verify(key, keylen);
228 int dnssec_validate(struct dns_header
*header
, size_t pktlen
)
230 unsigned char *p
, *reply
;
231 char *owner
= daemon
->namebuff
;
232 int i
, qtype
, qclass
, rdlen
;
235 if (header
->ancount
== 0)
237 if (!(reply
= p
= skip_questions(header
, pktlen
)))
239 for (i
= 0; i
< ntohs(header
->ancount
); i
++)
241 if (!extract_name(header
, pktlen
, &p
, owner
, 1, 10))
247 if (qtype
== T_RRSIG
)
249 printf("RRSIG found\n");
250 /* TODO: missing logic. We should only validate RRSIGs for which we
251 have a valid DNSKEY that is referenced by a DS record upstream.
252 There is a memory vs CPU conflict here; should we validate everything
253 to save memory and thus waste CPU, or better first acquire all information
254 (wasting memory) and then doing the minimum CPU computations required? */
255 validate_rrsig(header
, pktlen
, reply
, ntohs(header
->ancount
), owner
, qclass
, rdlen
, p
);