blob: c85b31882cbf5194be9202b0d5beaa69230ca199 [file] [log] [blame]
Simon Kelleyc8e8f5c2021-01-24 21:59:37 +00001/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
Simon Kelley1d030162015-12-21 14:17:06 +00002
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
Simon Kelley5bb88f02015-12-21 16:23:47 +000019unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign, int *is_last)
Simon Kelley1d030162015-12-21 14:17:06 +000020{
21 /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it.
22 also return length of pseudoheader in *len and pointer to the UDP size in *p
23 Finally, check to see if a packet is signed. If it is we cannot change a single bit before
Simon Kelley5bb88f02015-12-21 16:23:47 +000024 forwarding. We look for TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
Simon Kelley1d030162015-12-21 14:17:06 +000025
26 int i, arcount = ntohs(header->arcount);
27 unsigned char *ansp = (unsigned char *)(header+1);
28 unsigned short rdlen, type, class;
29 unsigned char *ret = NULL;
30
31 if (is_sign)
32 {
33 *is_sign = 0;
34
35 if (OPCODE(header) == QUERY)
36 {
37 for (i = ntohs(header->qdcount); i != 0; i--)
38 {
39 if (!(ansp = skip_name(ansp, header, plen, 4)))
40 return NULL;
41
42 GETSHORT(type, ansp);
43 GETSHORT(class, ansp);
44
45 if (class == C_IN && type == T_TKEY)
46 *is_sign = 1;
47 }
48 }
49 }
50 else
51 {
52 if (!(ansp = skip_questions(header, plen)))
53 return NULL;
54 }
55
56 if (arcount == 0)
57 return NULL;
58
59 if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))
60 return NULL;
61
62 for (i = 0; i < arcount; i++)
63 {
64 unsigned char *save, *start = ansp;
65 if (!(ansp = skip_name(ansp, header, plen, 10)))
66 return NULL;
67
68 GETSHORT(type, ansp);
69 save = ansp;
70 GETSHORT(class, ansp);
71 ansp += 4; /* TTL */
72 GETSHORT(rdlen, ansp);
73 if (!ADD_RDLEN(header, ansp, plen, rdlen))
74 return NULL;
75 if (type == T_OPT)
76 {
77 if (len)
78 *len = ansp - start;
Simon Kelley5bb88f02015-12-21 16:23:47 +000079
Simon Kelley1d030162015-12-21 14:17:06 +000080 if (p)
81 *p = save;
Simon Kelley5bb88f02015-12-21 16:23:47 +000082
83 if (is_last)
84 *is_last = (i == arcount-1);
85
Simon Kelley1d030162015-12-21 14:17:06 +000086 ret = start;
87 }
88 else if (is_sign &&
89 i == arcount - 1 &&
90 class == C_ANY &&
91 type == T_TSIG)
92 *is_sign = 1;
93 }
94
95 return ret;
96}
Simon Kelley1d030162015-12-21 14:17:06 +000097
Simon Kelley87985852016-04-25 15:33:30 +010098
99/* replace == 2 ->delete existing option only. */
Simon Kelley1d030162015-12-21 14:17:06 +0000100size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
Simon Kelleyc7f3bd22016-02-28 21:48:34 +0000101 unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do, int replace)
Simon Kelley1d030162015-12-21 14:17:06 +0000102{
Simon Kelley5bb88f02015-12-21 16:23:47 +0000103 unsigned char *lenp, *datap, *p, *udp_len, *buff = NULL;
104 int rdlen = 0, is_sign, is_last;
105 unsigned short flags = set_do ? 0x8000 : 0, rcode = 0;
Simon Kelley1d030162015-12-21 14:17:06 +0000106
Simon Kelley5bb88f02015-12-21 16:23:47 +0000107 p = find_pseudoheader(header, plen, NULL, &udp_len, &is_sign, &is_last);
108
109 if (is_sign)
110 return plen;
111
112 if (p)
Simon Kelley1d030162015-12-21 14:17:06 +0000113 {
Simon Kelley5bb88f02015-12-21 16:23:47 +0000114 /* Existing header */
Simon Kelley1d030162015-12-21 14:17:06 +0000115 int i;
Simon Kelley5bb88f02015-12-21 16:23:47 +0000116 unsigned short code, len;
117
118 p = udp_len;
119 GETSHORT(udp_sz, p);
120 GETSHORT(rcode, p);
Simon Kelley1d030162015-12-21 14:17:06 +0000121 GETSHORT(flags, p);
Simon Kelley5bb88f02015-12-21 16:23:47 +0000122
Simon Kelley1d030162015-12-21 14:17:06 +0000123 if (set_do)
124 {
Simon Kelleyc7f3bd22016-02-28 21:48:34 +0000125 p -= 2;
Simon Kelley5bb88f02015-12-21 16:23:47 +0000126 flags |= 0x8000;
127 PUTSHORT(flags, p);
Simon Kelley1d030162015-12-21 14:17:06 +0000128 }
129
130 lenp = p;
131 GETSHORT(rdlen, p);
132 if (!CHECK_LEN(header, p, plen, rdlen))
133 return plen; /* bad packet */
134 datap = p;
135
136 /* no option to add */
137 if (optno == 0)
138 return plen;
139
140 /* check if option already there */
Simon Kelleyc7f3bd22016-02-28 21:48:34 +0000141 for (i = 0; i + 4 < rdlen;)
Simon Kelley1d030162015-12-21 14:17:06 +0000142 {
143 GETSHORT(code, p);
144 GETSHORT(len, p);
Simon Kelleyc7f3bd22016-02-28 21:48:34 +0000145
146 /* malformed option, delete the whole OPT RR and start again. */
Simon Kelley897c1132017-09-25 20:11:58 +0100147 if (i + 4 + len > rdlen)
Simon Kelleyc7f3bd22016-02-28 21:48:34 +0000148 {
149 rdlen = 0;
Simon Kelleyaa300f72016-03-01 15:19:13 +0000150 is_last = 0;
Simon Kelleyc7f3bd22016-02-28 21:48:34 +0000151 break;
152 }
153
Simon Kelley1d030162015-12-21 14:17:06 +0000154 if (code == optno)
Simon Kelleyc7f3bd22016-02-28 21:48:34 +0000155 {
Simon Kelley87985852016-04-25 15:33:30 +0100156 if (replace == 0)
Simon Kelleyc7f3bd22016-02-28 21:48:34 +0000157 return plen;
158
159 /* delete option if we're to replace it. */
160 p -= 4;
161 rdlen -= len + 4;
Simon Kelley6a0b00f2017-09-25 20:19:55 +0100162 memmove(p, p+len+4, rdlen - i);
Simon Kelleyc7f3bd22016-02-28 21:48:34 +0000163 PUTSHORT(rdlen, lenp);
164 lenp -= 2;
165 }
166 else
167 {
168 p += len;
169 i += len + 4;
170 }
Simon Kelley1d030162015-12-21 14:17:06 +0000171 }
Simon Kelley5bb88f02015-12-21 16:23:47 +0000172
173 /* If we're going to extend the RR, it has to be the last RR in the packet */
174 if (!is_last)
175 {
176 /* First, take a copy of the options. */
177 if (rdlen != 0 && (buff = whine_malloc(rdlen)))
178 memcpy(buff, datap, rdlen);
179
180 /* now, delete OPT RR */
181 plen = rrfilter(header, plen, 0);
182
183 /* Now, force addition of a new one */
184 p = NULL;
185 }
Simon Kelley1d030162015-12-21 14:17:06 +0000186 }
187
Simon Kelley5bb88f02015-12-21 16:23:47 +0000188 if (!p)
189 {
190 /* We are (re)adding the pseudoheader */
191 if (!(p = skip_questions(header, plen)) ||
192 !(p = skip_section(p,
193 ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
194 header, plen)))
Simon Kelley51eadb62017-09-25 20:16:50 +0100195 {
196 free(buff);
Simon Kelley5bb88f02015-12-21 16:23:47 +0000197 return plen;
Simon Kelley51eadb62017-09-25 20:16:50 +0100198 }
Simon Kelley897c1132017-09-25 20:11:58 +0100199 if (p + 11 > limit)
Simon Kelley51eadb62017-09-25 20:16:50 +0100200 {
201 free(buff);
202 return plen; /* Too big */
203 }
Simon Kelley5bb88f02015-12-21 16:23:47 +0000204 *p++ = 0; /* empty name */
205 PUTSHORT(T_OPT, p);
206 PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */
207 PUTSHORT(rcode, p); /* extended RCODE and version */
208 PUTSHORT(flags, p); /* DO flag */
209 lenp = p;
210 PUTSHORT(rdlen, p); /* RDLEN */
211 datap = p;
212 /* Copy back any options */
213 if (buff)
214 {
Simon Kelley897c1132017-09-25 20:11:58 +0100215 if (p + rdlen > limit)
216 {
217 free(buff);
218 return plen; /* Too big */
219 }
Simon Kelley5bb88f02015-12-21 16:23:47 +0000220 memcpy(p, buff, rdlen);
221 free(buff);
222 p += rdlen;
223 }
Simon Kelleya3303e12017-09-07 20:45:00 +0100224
225 /* Only bump arcount if RR is going to fit */
226 if (((ssize_t)optlen) <= (limit - (p + 4)))
227 header->arcount = htons(ntohs(header->arcount) + 1);
Simon Kelley5bb88f02015-12-21 16:23:47 +0000228 }
229
230 if (((ssize_t)optlen) > (limit - (p + 4)))
231 return plen; /* Too big */
232
233 /* Add new option */
Simon Kelley87985852016-04-25 15:33:30 +0100234 if (optno != 0 && replace != 2)
Simon Kelley1d030162015-12-21 14:17:06 +0000235 {
Simon Kelley897c1132017-09-25 20:11:58 +0100236 if (p + 4 > limit)
237 return plen; /* Too big */
Simon Kelley1d030162015-12-21 14:17:06 +0000238 PUTSHORT(optno, p);
239 PUTSHORT(optlen, p);
Simon Kelley897c1132017-09-25 20:11:58 +0100240 if (p + optlen > limit)
241 return plen; /* Too big */
Simon Kelley1d030162015-12-21 14:17:06 +0000242 memcpy(p, opt, optlen);
243 p += optlen;
Simon Kelley5bb88f02015-12-21 16:23:47 +0000244 PUTSHORT(p - datap, lenp);
Simon Kelley1d030162015-12-21 14:17:06 +0000245 }
Simon Kelley1d030162015-12-21 14:17:06 +0000246 return p - (unsigned char *)header;
Simon Kelley1d030162015-12-21 14:17:06 +0000247}
248
Simon Kelley33702ab2015-12-28 23:17:15 +0000249size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit)
Simon Kelleyefef4972015-12-21 17:30:44 +0000250{
Simon Kelleyc7f3bd22016-02-28 21:48:34 +0000251 return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1, 0);
Simon Kelleyefef4972015-12-21 17:30:44 +0000252}
253
Simon Kelley33702ab2015-12-28 23:17:15 +0000254static unsigned char char64(unsigned char c)
255{
256 return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c & 0x3f];
257}
258
259static void encoder(unsigned char *in, char *out)
260{
261 out[0] = char64(in[0]>>2);
262 out[1] = char64((in[0]<<4) | (in[1]>>4));
263 out[2] = char64((in[1]<<2) | (in[2]>>6));
264 out[3] = char64(in[2]);
265}
266
Simon Kelley25e63f12020-11-25 21:17:52 +0000267static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit,
268 union mysockaddr *l3, time_t now, int *cacheablep)
Simon Kelley33702ab2015-12-28 23:17:15 +0000269{
Simon Kelley87985852016-04-25 15:33:30 +0100270 int maclen, replace = 2; /* can't get mac address, just delete any incoming. */
Simon Kelley33702ab2015-12-28 23:17:15 +0000271 unsigned char mac[DHCP_CHADDR_MAX];
Simon Kelley9e4cf472016-02-17 20:26:32 +0000272 char encode[18]; /* handle 6 byte MACs */
Simon Kelley33702ab2015-12-28 23:17:15 +0000273
274 if ((maclen = find_mac(l3, mac, 1, now)) == 6)
275 {
Simon Kelley87985852016-04-25 15:33:30 +0100276 replace = 1;
Simon Kelley25e63f12020-11-25 21:17:52 +0000277 *cacheablep = 0;
Simon Kelley87985852016-04-25 15:33:30 +0100278
Simon Kelley9e4cf472016-02-17 20:26:32 +0000279 if (option_bool(OPT_MAC_HEX))
280 print_mac(encode, mac, maclen);
281 else
282 {
283 encoder(mac, encode);
284 encoder(mac+3, encode+4);
285 encode[8] = 0;
286 }
Simon Kelley33702ab2015-12-28 23:17:15 +0000287 }
288
Simon Kelley87985852016-04-25 15:33:30 +0100289 return add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace);
Simon Kelley33702ab2015-12-28 23:17:15 +0000290}
291
292
Simon Kelley25e63f12020-11-25 21:17:52 +0000293static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit,
294 union mysockaddr *l3, time_t now, int *cacheablep)
Simon Kelley1d030162015-12-21 14:17:06 +0000295{
Simon Kelley11867dc2015-12-23 16:15:58 +0000296 int maclen;
297 unsigned char mac[DHCP_CHADDR_MAX];
Simon Kelley1d030162015-12-21 14:17:06 +0000298
Simon Kelley33702ab2015-12-28 23:17:15 +0000299 if ((maclen = find_mac(l3, mac, 1, now)) != 0)
Simon Kelley25e63f12020-11-25 21:17:52 +0000300 {
301 *cacheablep = 0;
302 plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, 0);
303 }
304
Simon Kelley11867dc2015-12-23 16:15:58 +0000305 return plen;
Simon Kelley1d030162015-12-21 14:17:06 +0000306}
307
308struct subnet_opt {
309 u16 family;
Simon Kelleyee875042018-10-23 22:10:17 +0100310 u8 source_netmask, scope_netmask;
Simon Kelley1d030162015-12-21 14:17:06 +0000311 u8 addr[IN6ADDRSZ];
Simon Kelley1d030162015-12-21 14:17:06 +0000312};
313
314static void *get_addrp(union mysockaddr *addr, const short family)
315{
Simon Kelley1d030162015-12-21 14:17:06 +0000316 if (family == AF_INET6)
317 return &addr->in6.sin6_addr;
Simon Kelley1d030162015-12-21 14:17:06 +0000318
319 return &addr->in.sin_addr;
320}
321
Simon Kelley25e63f12020-11-25 21:17:52 +0000322static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source, int *cacheablep)
Simon Kelley1d030162015-12-21 14:17:06 +0000323{
324 /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
325
326 int len;
Simon Kelley0fbd9802017-05-21 22:24:43 +0100327 void *addrp = NULL;
Simon Kelley1d030162015-12-21 14:17:06 +0000328 int sa_family = source->sa.sa_family;
Simon Kelley25e63f12020-11-25 21:17:52 +0000329 int cacheable = 0;
330
Simon Kelley22fe2fd2016-02-28 17:07:10 +0000331 opt->source_netmask = 0;
332 opt->scope_netmask = 0;
Simon Kelley25e63f12020-11-25 21:17:52 +0000333
Simon Kelley22fe2fd2016-02-28 17:07:10 +0000334 if (source->sa.sa_family == AF_INET6 && daemon->add_subnet6)
Simon Kelley1d030162015-12-21 14:17:06 +0000335 {
336 opt->source_netmask = daemon->add_subnet6->mask;
337 if (daemon->add_subnet6->addr_used)
338 {
339 sa_family = daemon->add_subnet6->addr.sa.sa_family;
340 addrp = get_addrp(&daemon->add_subnet6->addr, sa_family);
Simon Kelley25e63f12020-11-25 21:17:52 +0000341 cacheable = 1;
Simon Kelley1d030162015-12-21 14:17:06 +0000342 }
343 else
344 addrp = &source->in6.sin6_addr;
345 }
Simon Kelley22fe2fd2016-02-28 17:07:10 +0000346
347 if (source->sa.sa_family == AF_INET && daemon->add_subnet4)
Simon Kelley1d030162015-12-21 14:17:06 +0000348 {
349 opt->source_netmask = daemon->add_subnet4->mask;
350 if (daemon->add_subnet4->addr_used)
351 {
352 sa_family = daemon->add_subnet4->addr.sa.sa_family;
353 addrp = get_addrp(&daemon->add_subnet4->addr, sa_family);
Simon Kelley25e63f12020-11-25 21:17:52 +0000354 cacheable = 1; /* Address is constant */
Simon Kelley1d030162015-12-21 14:17:06 +0000355 }
Simon Kelley22fe2fd2016-02-28 17:07:10 +0000356 else
357 addrp = &source->in.sin_addr;
Simon Kelley1d030162015-12-21 14:17:06 +0000358 }
359
Simon Kelley22fe2fd2016-02-28 17:07:10 +0000360 opt->family = htons(sa_family == AF_INET6 ? 2 : 1);
Simon Kelley22fe2fd2016-02-28 17:07:10 +0000361
Simon Kelley0fbd9802017-05-21 22:24:43 +0100362 if (addrp && opt->source_netmask != 0)
Simon Kelley1d030162015-12-21 14:17:06 +0000363 {
Simon Kelley1d030162015-12-21 14:17:06 +0000364 len = ((opt->source_netmask - 1) >> 3) + 1;
365 memcpy(opt->addr, addrp, len);
366 if (opt->source_netmask & 7)
367 opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7));
368 }
Simon Kelley25e63f12020-11-25 21:17:52 +0000369 else
370 {
371 cacheable = 1; /* No address ever supplied. */
372 len = 0;
373 }
374
375 if (cacheablep)
376 *cacheablep = cacheable;
Simon Kelley22fe2fd2016-02-28 17:07:10 +0000377
Simon Kelley1d030162015-12-21 14:17:06 +0000378 return len + 4;
379}
380
Simon Kelley25e63f12020-11-25 21:17:52 +0000381static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable)
Simon Kelley1d030162015-12-21 14:17:06 +0000382{
383 /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
384
385 int len;
386 struct subnet_opt opt;
387
Simon Kelley25e63f12020-11-25 21:17:52 +0000388 len = calc_subnet_opt(&opt, source, cacheable);
Simon Kelleyc7f3bd22016-02-28 21:48:34 +0000389 return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, 0);
Simon Kelley1d030162015-12-21 14:17:06 +0000390}
391
Simon Kelley1d030162015-12-21 14:17:06 +0000392int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
393{
394 /* Section 9.2, Check that subnet option in reply matches. */
Simon Kelley5aa5f0f2015-12-21 17:20:35 +0000395
396 int len, calc_len;
Simon Kelley1d030162015-12-21 14:17:06 +0000397 struct subnet_opt opt;
398 unsigned char *p;
399 int code, i, rdlen;
400
Simon Kelley25e63f12020-11-25 21:17:52 +0000401 calc_len = calc_subnet_opt(&opt, peer, NULL);
Simon Kelley1d030162015-12-21 14:17:06 +0000402
Simon Kelley25e63f12020-11-25 21:17:52 +0000403 if (!(p = skip_name(pseudoheader, header, plen, 10)))
404 return 1;
405
406 p += 8; /* skip UDP length and RCODE */
407
408 GETSHORT(rdlen, p);
409 if (!CHECK_LEN(header, p, plen, rdlen))
410 return 1; /* bad packet */
411
412 /* check if option there */
Simon Kelley1d030162015-12-21 14:17:06 +0000413 for (i = 0; i + 4 < rdlen; i += len + 4)
414 {
415 GETSHORT(code, p);
416 GETSHORT(len, p);
417 if (code == EDNS0_OPTION_CLIENT_SUBNET)
418 {
419 /* make sure this doesn't mismatch. */
420 opt.scope_netmask = p[3];
421 if (len != calc_len || memcmp(p, &opt, len) != 0)
422 return 0;
423 }
424 p += len;
425 }
426
427 return 1;
428}
Simon Kelley33702ab2015-12-28 23:17:15 +0000429
Simon Kelley25e63f12020-11-25 21:17:52 +0000430/* Set *check_subnet if we add a client subnet option, which needs to checked
431 in the reply. Set *cacheable to zero if we add an option which the answer
432 may depend on. */
Simon Kelley33702ab2015-12-28 23:17:15 +0000433size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
Simon Kelley25e63f12020-11-25 21:17:52 +0000434 union mysockaddr *source, time_t now, int *check_subnet, int *cacheable)
Simon Kelley33702ab2015-12-28 23:17:15 +0000435{
436 *check_subnet = 0;
Simon Kelley25e63f12020-11-25 21:17:52 +0000437 *cacheable = 1;
438
Simon Kelley33702ab2015-12-28 23:17:15 +0000439 if (option_bool(OPT_ADD_MAC))
Simon Kelley25e63f12020-11-25 21:17:52 +0000440 plen = add_mac(header, plen, limit, source, now, cacheable);
Simon Kelley33702ab2015-12-28 23:17:15 +0000441
Simon Kelley9e4cf472016-02-17 20:26:32 +0000442 if (option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX))
Simon Kelley25e63f12020-11-25 21:17:52 +0000443 plen = add_dns_client(header, plen, limit, source, now, cacheable);
444
Simon Kelley1e505122016-01-25 21:29:23 +0000445 if (daemon->dns_client_id)
446 plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID,
Simon Kelleyc7f3bd22016-02-28 21:48:34 +0000447 (unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1);
Simon Kelley33702ab2015-12-28 23:17:15 +0000448
449 if (option_bool(OPT_CLIENT_SUBNET))
450 {
Simon Kelley25e63f12020-11-25 21:17:52 +0000451 plen = add_source_addr(header, plen, limit, source, cacheable);
Simon Kelley33702ab2015-12-28 23:17:15 +0000452 *check_subnet = 1;
453 }
454
455 return plen;
456}