The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
base.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 707701bc4a31d08f16424690f9632efb9444dae5 $
19 *
20 * @file protocols/dns/base.c
21 * @brief Functions to send/receive dns packets.
22 *
23 * @copyright 2008 The FreeRADIUS server project
24 * @copyright 2021 Network RADIUS SAS (legal@networkradius.com)
25 */
26RCSID("$Id: 707701bc4a31d08f16424690f9632efb9444dae5 $")
27
28#include "dns.h"
29#include "attrs.h"
30#include <freeradius-devel/protocol/dns/rfc1034.h>
31
33static bool instantiated = false;
34
39
41
42static _Thread_local fr_dns_labels_t fr_dns_labels;
43static _Thread_local fr_dns_block_t fr_dns_blocks[256];
44static _Thread_local uint8_t fr_dns_marker[65536];
45
48 { .out = &dict_dns, .proto = "dns" },
50};
51
57
60 { .out = &attr_dns_packet, .name = "Header", .type = FR_TYPE_STRUCT, .dict = &dict_dns },
61 { .out = &attr_dns_question, .name = "Question", .type = FR_TYPE_STRUCT, .dict = &dict_dns },
62 { .out = &attr_dns_rr, .name = "Resource-Record", .type = FR_TYPE_STRUCT, .dict = &dict_dns },
63 { .out = &attr_dns_ns, .name = "Name-Server", .type = FR_TYPE_STRUCT, .dict = &dict_dns },
64 { .out = &attr_dns_ar, .name = "Additional-Record", .type = FR_TYPE_STRUCT, .dict = &dict_dns },
66};
67
69 [FR_DNS_QUERY] = "Query",
70 [FR_DNS_INVERSE_QUERY] = "Inverse-Query",
71 [FR_DNS_STATUS] = "Status",
72 [FR_DNS_NOTIFY] = "Notify",
73 [FR_DNS_UPDATE] = "Update",
74 [FR_DNS_STATEFUL_OPERATION] = "Stateful-Operation",
75};
76
78FR_DICT_ATTR_FLAG_FUNC(fr_dns_attr_flags_t, dns_label_uncompressed)
79
81 { L("dns_label"), { .func = dict_flag_dns_label } },
82 { L("dns_label_uncompressed"), { .func = dict_flag_dns_label_uncompressed } }
83};
84
85#define DECODE_FAIL(_reason) if (reason) *reason = FR_DNS_DECODE_FAIL_ ## _reason
86
87static bool fr_dns_tlv_ok(uint8_t const *p, uint8_t const *end, fr_dns_decode_fail_t *reason)
88{
89 uint16_t len;
90
91 while (p < end) {
92 if ((p + 4) > end) {
93 DECODE_FAIL(MISSING_TLV_HEADER);
94 return false;
95 }
96
97 len = fr_nbo_to_uint16(p + 2);
98 if ((p + 4 + len) > end) {
99 DECODE_FAIL(TLV_OVERFLOWS_RR);
100 return false;
101 }
102
103 p += 4 + len;
104 }
105
106 return true;
107}
108
109bool fr_dns_packet_ok(uint8_t const *packet, size_t packet_len, bool query, fr_dns_decode_fail_t *reason)
110{
111 uint8_t const *p, *end;
112 int qdcount, count, expected;
113 uint8_t opcode;
114
115 if (packet_len <= DNS_HDR_LEN) {
116 DECODE_FAIL(MIN_LENGTH_PACKET);
117 return false;
118 }
119
120 if (packet_len > 65535) {
121 DECODE_FAIL(MAX_LENGTH_PACKET);
122 return false;
123 }
124
125 /*
126 * query=0, response=1
127 */
128 if (((packet[2] & 0x80) == 0) != query) {
129 DECODE_FAIL(UNEXPECTED);
130 return false;
131 }
132
133 /*
134 * @todo - the truncation rules mean that the various counts below are wrong, and the caller
135 * should retry over TCP. This is really an indication to us, that we need to fully implement
136 * the truncation checks.
137 */
138 if ((packet[2] & 0x02) != 0) {
139 DECODE_FAIL(TRUNCATED);
140 return false;
141 }
142 qdcount = fr_nbo_to_uint16(packet + 4);
143
144 opcode = (packet[2] >> 3) & 0x0f;
145
146 /*
147 * RFC 2136 (DNS update) defines the four "count" fields to have different meanings:
148 *
149 * ZOCOUNT The number of RRs in the Zone Section.
150 * PRCOUNT The number of RRs in the Prerequisite Section.
151 * UPCOUNT The number of RRs in the Update Section.
152 * ADCOUNT The number of RRs in the Additional Data Section.
153 *
154 * @todo - we can likely do more validation checks on input packets.
155 */
156 if (query && (opcode != FR_OPCODE_VALUE_UPDATE)) {
157 /*
158 * There should be at least one query, and no
159 * replies in the query.
160 *
161 * @todo - unless it's an IQUERY, in which case
162 * there should be no questions, and at least one
163 * answer.
164 */
165 if (!qdcount) {
166 DECODE_FAIL(NO_QUESTIONS);
167 return false;
168 }
169
170 if (fr_nbo_to_uint16(packet + 6) != 0) {
171 DECODE_FAIL(ANSWERS_IN_QUESTION);
172 return false;
173 }
174
175 if (fr_nbo_to_uint16(packet + 8) != 0) {
176 DECODE_FAIL(NS_IN_QUESTION);
177 return false;
178 }
179 // additional records can exist!
180
181 } else {
182 /*
183 * Replies _usually_ copy the query. But not
184 * always And replies can have zero or more answers.
185 */
186 }
187
188 expected = fr_nbo_to_uint16(packet + 4) + fr_nbo_to_uint16(packet + 6) + fr_nbo_to_uint16(packet + 8) + fr_nbo_to_uint16(packet + 10);
189 count = 0;
190
191 p = packet + DNS_HDR_LEN;
192 end = packet + packet_len;
193
194 /*
195 * We track valid label targets in a simple array (up to
196 * 2^14 bits of compressed pointer).
197 *
198 * Note that some labels might appear in the RRDATA
199 * field, and we don't verify those here. However, this
200 * function will verify the most common packets. As a
201 * result, any issues with overflow, etc. are more
202 * difficult to exploit.
203 */
204 memset(fr_dns_marker, 0, packet_len < (1 << 14) ? packet_len : (1 << 14));
205
206 /*
207 * Check for wildly fake packets, by making rough
208 * estimations. This way we don't actually have to walk
209 * the packet.
210 */
211 if (p + (qdcount * 5) > end) {
212 DECODE_FAIL(TOO_MANY_RRS);
213 return false;
214 }
215 p += (qdcount * 5);
216
217 if ((p + ((expected - qdcount) * (1 + 8 + 2))) > end) {
218 DECODE_FAIL(TOO_MANY_RRS);
219 return false;
220 }
221
222 /*
223 * The counts are at least vaguely OK, let's walk over the whole packet.
224 */
225 p = packet + DNS_HDR_LEN;
226
227 /*
228 * Check that lengths of RRs match.
229 */
230 while (p < end) {
231 uint16_t len = 0;
232 uint8_t const *start = p;
233 bool is_opt = false;
234
235 /*
236 * Simple DNS label decoder
237 *
238 * @todo - move this to src/lib/util/dns.c,
239 * perhaps as fr_dns_label_verify(), and then
240 * have it also return a pointer to the next
241 * label? fr_dns_label_uncompressed_length()
242 * does similar but slightly different things.
243 */
244 while (p < end) {
245 /*
246 * 0x00 is "end of label"
247 */
248 if (!*p) {
249 p++;
250 break;
251 }
252
253 /*
254 * 2 octets of 14-bit pointer, which must
255 * be at least somewhat sane.
256 */
257 if (*p >= 0xc0) {
258 ptrdiff_t offset;
259
260 if ((p + 2) > end) {
261 DECODE_FAIL(POINTER_OVERFLOWS_PACKET);
262 return false;
263 }
264
265 offset = p[1];
266 offset += ((*p & ~0xc0) << 8);
267
268 /*
269 * Can't point to the header.
270 */
271 if (offset < 12) {
272 DECODE_FAIL(POINTER_TO_HEADER);
273 return false;
274 }
275
276 /*
277 * Can't point to the current label.
278 */
279 if (offset >= (start - packet)) {
280 DECODE_FAIL(POINTER_LOOPS);
281 return false;
282 }
283
284 if (!fr_dns_marker[offset]) {
285 DECODE_FAIL(POINTER_TO_NON_LABEL);
286 return false;
287 }
288
289 /*
290 * A compressed pointer is the end of the current label.
291 */
292 p += 2;
293 break;
294 }
295
296 /*
297 * 0b01 and 0b10 are forbidden
298 */
299 if (*p > 63) {
300 DECODE_FAIL(INVALID_POINTER);
301 return false;
302 }
303
304 /*
305 * It must be a length byte, which doesn't cause overflow.
306 */
307 if ((p + *p + 1) > end) {
308 DECODE_FAIL(LABEL_OVERFLOWS_PACKET);
309 return false;
310 }
311
312 /*
313 * Total length of labels can't be too high.
314 */
315 len += *p;
316 if (len >= 256) {
317 DECODE_FAIL(LABEL_TOO_LONG);
318 return false;
319 }
320
321 /*
322 * Remember that this is where we have a
323 * label.
324 */
325 fr_dns_marker[p - packet] = 1;
326
327 /*
328 * Go to the next label.
329 */
330 p += *p + 1;
331 }
332
333 if (qdcount) {
334 /*
335 * qtype + qclass
336 */
337 if ((p + 4) > end) {
338 DECODE_FAIL(MISSING_QD_HEADER);
339 return false;
340 }
341
342 p += 4;
343 qdcount--;
344 goto next;
345 }
346
347 /*
348 * type (2) + class (2) + TTL (4)
349 *
350 * These are overloaded for the OPT RR
351 * and possibly others, but the basic
352 * idea is the same.
353 */
354 if ((p + 8) > end) {
355 DECODE_FAIL(MISSING_RR_HEADER);
356 return false;
357 }
358 is_opt = (p[0] == 0) && (p[1] == 41);
359 p += 8;
360
361 /*
362 * rr_len
363 */
364 if ((p + 2) > end) {
365 DECODE_FAIL(MISSING_RR_LEN);
366 return false;
367 }
368
369 /*
370 * @todo - RFC2136 allows RDLENGTH=0 for many cases.
371 */
372 len = fr_nbo_to_uint16(p);
373 if (!is_opt && (len == 0)) {
374 DECODE_FAIL(ZERO_RR_LEN);
375 return false;
376 }
377
378 p += 2;
379 if ((p + len) > end) {
380 DECODE_FAIL(RR_OVERFLOWS_PACKET);
381 return false;
382 }
383
384 /*
385 * Verify the TLVs, too.
386 */
387 if (is_opt && !fr_dns_tlv_ok(p, p + len, reason)) {
388 return false;
389 }
390
391 p += len;
392
393next:
394 count++;
395
396 if (count > expected) {
397 DECODE_FAIL(TOO_MANY_RRS);
398 return false;
399 }
400 }
401
402 if (count != expected) {
403 DECODE_FAIL(TOO_FEW_RRS);
404 return false;
405 }
406
407 /*
408 * @todo - save fr_dns_marker[] data, so that it can be used by fr_dns_labels_get(). This helps
409 * to reduce redundant work.
410 */
411
412 DECODE_FAIL(NONE);
413 return true;
414}
415
416fr_dns_labels_t *fr_dns_labels_get(uint8_t const *packet, size_t packet_len, bool init_mark)
417{
419
420 lb->max = 256;
421 lb->mark = fr_dns_marker;
422 lb->blocks = fr_dns_blocks;
423
424 lb->start = packet;
425 lb->end = packet + packet_len;
426
427 lb->num = 1;
428 lb->blocks[0].start = DNS_HDR_LEN;
429 lb->blocks[0].end = DNS_HDR_LEN;
430
431 if (init_mark) {
432 fr_assert(packet_len <= 65535);
433 memset(lb->mark, 0, packet_len);
434 }
435
436 return lb;
437}
438
439/** Resolve/cache attributes in the DNS dictionary
440 *
441 * @return
442 * - 0 on success.
443 * - -1 on failure.
444 */
446{
447 if (instance_count > 0) {
449 return 0;
450 }
451
453
454 if (fr_dict_autoload(dns_dict) < 0) {
455 fail:
457 return -1;
458 }
461 goto fail;
462 }
463
464 instantiated = true;
465 return 0;
466}
467
469{
470 if (!instantiated) return;
471
473
474 if (--instance_count > 0) return;
475
477 instantiated = false;
478}
479
481{
482 if (da->flags.array) {
483 fr_strerror_const("The 'array' flag cannot be used with DNS");
484 return false;
485 }
486
487 if (da->type == FR_TYPE_ATTR) {
488 fr_strerror_const("The 'attribute' data type cannot be used with DNS");
489 return false;
490 }
491
493 if (da->type != FR_TYPE_STRING) {
494 fr_strerror_const("The 'dns_label' flag can only be used with attributes of type 'string'");
495 return false;
496 }
497 da->flags.is_known_width = true; /* Lie so we don't trip up the main validation checks */
498 }
499
500 switch (da->type) {
501 case FR_TYPE_IP:
502 da->flags.is_known_width = true;
503 break;
504
505 default:
506 break;
507 }
508
509 return true;
510}
511
514 .name = "dns",
515 .default_type_size = 2,
516 .default_type_length = 2,
517 .attr = {
518 .flags = {
519 .table = dns_flags,
520 .table_len = NUM_ELEMENTS(dns_flags),
521 .len = sizeof(fr_dns_attr_flags_t)
522 },
523 .valid = attr_valid
524 },
525
526 .init = fr_dns_global_init,
527 .free = fr_dns_global_free,
528};
#define RCSID(id)
Definition build.h:487
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define NUM_ELEMENTS(_t)
Definition build.h:339
#define fr_dict_autofree(_to_free)
Definition dict.h:917
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:294
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:307
int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load)
Process a dict_attr_autoload element to load/verify a dictionary attribute.
Definition dict_util.c:4402
#define fr_dict_autoload(_to_load)
Definition dict.h:914
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:313
char const * name
name of this protocol
Definition dict.h:458
#define FR_DICT_ATTR_FLAG_FUNC(_struct, _name)
Define a flag setting function, which sets one bit in a fr_dict_attr_flags_t.
Definition dict.h:437
Specifies an attribute which must be present for the module to function.
Definition dict.h:293
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:306
Protocol specific custom flag definitnion.
Definition dict.h:427
Protocol-specific callbacks in libfreeradius-PROTOCOL.
Definition dict.h:457
static uint32_t instance_count
Definition base.c:44
uint16_t start
Definition dns.h:31
uint8_t const * start
start of packet
Definition dns.h:36
uint8_t const * end
end of the packet
Definition dns.h:37
fr_dns_block_t * blocks
maximum number of labels
Definition dns.h:41
uint8_t * mark
markup buffer used for decoding.
Definition dns.h:38
uint16_t end
Definition dns.h:32
int num
number of used labels
Definition dns.h:39
unsigned short uint16_t
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_STRUCT
like TLV, but without T or L, and fixed-width children
unsigned int uint32_t
unsigned char uint8_t
static uint16_t fr_nbo_to_uint16(uint8_t const data[static sizeof(uint16_t)])
Read an unsigned 16bit integer from wire format (big endian)
Definition nbo.h:146
static fr_dict_t const * dict_dns
Definition base.c:38
static bool instantiated
Definition base.c:39
static bool attr_valid(fr_dict_attr_t *da)
Definition base.c:721
#define DECODE_FAIL(_reason)
Definition base.c:85
fr_dict_attr_autoload_t dns_dict_attr[]
Definition base.c:59
int fr_dns_global_init(void)
Resolve/cache attributes in the DNS dictionary.
Definition base.c:445
fr_dict_protocol_t libfreeradius_dns_dict_protocol
Definition base.c:513
static _Thread_local fr_dns_block_t fr_dns_blocks[256]
Definition base.c:43
fr_dict_attr_t const * attr_dns_ns
Definition base.c:55
uint16_t code
Definition base.c:36
static _Thread_local fr_dns_labels_t fr_dns_labels
Definition base.c:42
static _Thread_local uint8_t fr_dns_marker[65536]
Definition base.c:44
uint16_t length
Definition base.c:37
fr_dict_attr_t const * attr_dns_packet
Definition base.c:52
void fr_dns_global_free(void)
Definition base.c:468
fr_dict_attr_t const * attr_dns_question
Definition base.c:53
fr_dict_autoload_t dns_dict[]
Definition base.c:47
fr_dns_labels_t * fr_dns_labels_get(uint8_t const *packet, size_t packet_len, bool init_mark)
Definition base.c:416
fr_dict_attr_t const * attr_dns_ar
Definition base.c:56
fr_dict_attr_t const * attr_dns_rr
Definition base.c:54
char const * fr_dns_packet_names[FR_DNS_CODE_MAX]
Definition base.c:68
static fr_dict_flag_parser_t const dns_flags[]
Definition base.c:80
static bool fr_dns_tlv_ok(uint8_t const *p, uint8_t const *end, fr_dns_decode_fail_t *reason)
Definition base.c:87
bool fr_dns_packet_ok(uint8_t const *packet, size_t packet_len, bool query, fr_dns_decode_fail_t *reason)
Definition base.c:109
static bool fr_dns_flag_dns_label_any(fr_dict_attr_t const *da)
Definition dns.h:145
@ FR_DNS_STATEFUL_OPERATION
Definition dns.h:89
@ FR_DNS_QUERY
Definition dns.h:84
@ FR_DNS_NOTIFY
Definition dns.h:87
@ FR_DNS_INVERSE_QUERY
Definition dns.h:85
@ FR_DNS_STATUS
Definition dns.h:86
@ FR_DNS_UPDATE
Definition dns.h:88
@ FR_DNS_CODE_MAX
Definition dns.h:90
#define DNS_HDR_LEN
Definition dns.h:133
fr_dns_decode_fail_t
Definition dns.h:102
VQP attributes.
#define fr_assert(_expr)
Definition rad_assert.h:38
return count
Definition module.c:155
#define fr_strerror_const(_msg)
Definition strerror.h:223
@ FR_TYPE_ATTR
A contains an attribute reference.
Definition types.h:84
#define FR_TYPE_IP
Definition types.h:309