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: 1d9e0d65a04823c67f7d2c294fa64b15a690dd2c $
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: 1d9e0d65a04823c67f7d2c294fa64b15a690dd2c $")
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 if ((opcode >= FR_DNS_CODE_MAX) || !fr_dns_packet_names[opcode]) {
146 DECODE_FAIL(UNKNOWN_OPCODE);
147 return false;
148 }
149
150 /*
151 * RFC 2136 (DNS update) defines the four "count" fields to have different meanings:
152 *
153 * ZOCOUNT The number of RRs in the Zone Section.
154 * PRCOUNT The number of RRs in the Prerequisite Section.
155 * UPCOUNT The number of RRs in the Update Section.
156 * ADCOUNT The number of RRs in the Additional Data Section.
157 *
158 * @todo - we can likely do more validation checks on input packets.
159 */
160 if (query && (opcode != FR_OPCODE_VALUE_UPDATE)) {
161 /*
162 * There should be at least one query, and no
163 * replies in the query.
164 *
165 * @todo - unless it's an IQUERY, in which case
166 * there should be no questions, and at least one
167 * answer.
168 */
169 if (!qdcount) {
170 DECODE_FAIL(NO_QUESTIONS);
171 return false;
172 }
173
174 if (fr_nbo_to_uint16(packet + 6) != 0) {
175 DECODE_FAIL(ANSWERS_IN_QUESTION);
176 return false;
177 }
178
179 if (fr_nbo_to_uint16(packet + 8) != 0) {
180 DECODE_FAIL(NS_IN_QUESTION);
181 return false;
182 }
183 // additional records can exist!
184
185 } else {
186 /*
187 * Replies _usually_ copy the query. But not
188 * always And replies can have zero or more answers.
189 */
190 }
191
192 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);
193 count = 0;
194
195 p = packet + DNS_HDR_LEN;
196 end = packet + packet_len;
197
198 /*
199 * We track valid label targets in a simple array (up to
200 * 2^14 bits of compressed pointer).
201 *
202 * Note that some labels might appear in the RRDATA
203 * field, and we don't verify those here. However, this
204 * function will verify the most common packets. As a
205 * result, any issues with overflow, etc. are more
206 * difficult to exploit.
207 */
208 memset(fr_dns_marker, 0, packet_len < (1 << 14) ? packet_len : (1 << 14));
209
210 /*
211 * Check for wildly fake packets, by making rough
212 * estimations. This way we don't actually have to walk
213 * the packet.
214 */
215 if (p + (qdcount * 5) > end) {
216 DECODE_FAIL(TOO_MANY_RRS);
217 return false;
218 }
219 p += (qdcount * 5);
220
221 if ((p + ((expected - qdcount) * (1 + 8 + 2))) > end) {
222 DECODE_FAIL(TOO_MANY_RRS);
223 return false;
224 }
225
226 /*
227 * The counts are at least vaguely OK, let's walk over the whole packet.
228 */
229 p = packet + DNS_HDR_LEN;
230
231 /*
232 * Check that lengths of RRs match.
233 */
234 while (p < end) {
235 uint16_t len = 0;
236 uint8_t const *start = p;
237 bool is_opt = false;
238
239 /*
240 * Simple DNS label decoder
241 *
242 * @todo - move this to src/lib/util/dns.c,
243 * perhaps as fr_dns_label_verify(), and then
244 * have it also return a pointer to the next
245 * label? fr_dns_label_uncompressed_length()
246 * does similar but slightly different things.
247 */
248 while (p < end) {
249 /*
250 * 0x00 is "end of label"
251 */
252 if (!*p) {
253 p++;
254 break;
255 }
256
257 /*
258 * 2 octets of 14-bit pointer, which must
259 * be at least somewhat sane.
260 */
261 if (*p >= 0xc0) {
262 ptrdiff_t offset;
263
264 if ((p + 2) > end) {
265 DECODE_FAIL(POINTER_OVERFLOWS_PACKET);
266 return false;
267 }
268
269 offset = p[1];
270 offset += ((*p & ~0xc0) << 8);
271
272 /*
273 * Can't point to the header.
274 */
275 if (offset < 12) {
276 DECODE_FAIL(POINTER_TO_HEADER);
277 return false;
278 }
279
280 /*
281 * Can't point to the current label.
282 */
283 if (offset >= (start - packet)) {
284 DECODE_FAIL(POINTER_LOOPS);
285 return false;
286 }
287
288 if (!fr_dns_marker[offset]) {
289 DECODE_FAIL(POINTER_TO_NON_LABEL);
290 return false;
291 }
292
293 /*
294 * A compressed pointer is the end of the current label.
295 */
296 p += 2;
297 break;
298 }
299
300 /*
301 * 0b01 and 0b10 are forbidden
302 */
303 if (*p > 63) {
304 DECODE_FAIL(INVALID_POINTER);
305 return false;
306 }
307
308 /*
309 * It must be a length byte, which doesn't cause overflow.
310 */
311 if ((p + *p + 1) > end) {
312 DECODE_FAIL(LABEL_OVERFLOWS_PACKET);
313 return false;
314 }
315
316 /*
317 * Total length of labels can't be too high.
318 */
319 len += *p;
320 if (len >= 256) {
321 DECODE_FAIL(LABEL_TOO_LONG);
322 return false;
323 }
324
325 /*
326 * Remember that this is where we have a
327 * label.
328 */
329 fr_dns_marker[p - packet] = 1;
330
331 /*
332 * Go to the next label.
333 */
334 p += *p + 1;
335 }
336
337 if (qdcount) {
338 /*
339 * qtype + qclass
340 */
341 if ((p + 4) > end) {
342 DECODE_FAIL(MISSING_QD_HEADER);
343 return false;
344 }
345
346 p += 4;
347 qdcount--;
348 goto next;
349 }
350
351 /*
352 * type (2) + class (2) + TTL (4)
353 *
354 * These are overloaded for the OPT RR
355 * and possibly others, but the basic
356 * idea is the same.
357 */
358 if ((p + 8) > end) {
359 DECODE_FAIL(MISSING_RR_HEADER);
360 return false;
361 }
362 is_opt = (p[0] == 0) && (p[1] == 41);
363 p += 8;
364
365 /*
366 * rr_len
367 */
368 if ((p + 2) > end) {
369 DECODE_FAIL(MISSING_RR_LEN);
370 return false;
371 }
372
373 /*
374 * @todo - RFC2136 allows RDLENGTH=0 for many cases.
375 */
376 len = fr_nbo_to_uint16(p);
377 if (!is_opt && (len == 0)) {
378 DECODE_FAIL(ZERO_RR_LEN);
379 return false;
380 }
381
382 p += 2;
383 if ((p + len) > end) {
384 DECODE_FAIL(RR_OVERFLOWS_PACKET);
385 return false;
386 }
387
388 /*
389 * Verify the TLVs, too.
390 */
391 if (is_opt && !fr_dns_tlv_ok(p, p + len, reason)) {
392 return false;
393 }
394
395 p += len;
396
397next:
398 count++;
399
400 if (count > expected) {
401 DECODE_FAIL(TOO_MANY_RRS);
402 return false;
403 }
404 }
405
406 if (count != expected) {
407 DECODE_FAIL(TOO_FEW_RRS);
408 return false;
409 }
410
411 /*
412 * @todo - save fr_dns_marker[] data, so that it can be used by fr_dns_labels_get(). This helps
413 * to reduce redundant work.
414 */
415
416 DECODE_FAIL(NONE);
417 return true;
418}
419
420fr_dns_labels_t *fr_dns_labels_get(uint8_t const *packet, size_t packet_len, bool init_mark)
421{
423
424 lb->max = 256;
425 lb->mark = fr_dns_marker;
426 lb->blocks = fr_dns_blocks;
427
428 lb->start = packet;
429 lb->end = packet + packet_len;
430
431 lb->num = 1;
432 lb->blocks[0].start = DNS_HDR_LEN;
433 lb->blocks[0].end = DNS_HDR_LEN;
434
435 if (init_mark) {
436 fr_assert(packet_len <= 65535);
437 memset(lb->mark, 0, packet_len);
438 }
439
440 return lb;
441}
442
443/** Resolve/cache attributes in the DNS dictionary
444 *
445 * @return
446 * - 0 on success.
447 * - -1 on failure.
448 */
450{
451 if (instance_count > 0) {
453 return 0;
454 }
455
457
458 if (fr_dict_autoload(dns_dict) < 0) {
459 fail:
461 return -1;
462 }
465 goto fail;
466 }
467
468 instantiated = true;
469 return 0;
470}
471
473{
474 if (!instantiated) return;
475
477
478 if (--instance_count > 0) return;
479
481 instantiated = false;
482}
483
485{
486 if (da->flags.array) {
487 fr_strerror_const("The 'array' flag cannot be used with DNS");
488 return false;
489 }
490
491 if (da->type == FR_TYPE_ATTR) {
492 fr_strerror_const("The 'attribute' data type cannot be used with DNS");
493 return false;
494 }
495
497 if (da->type != FR_TYPE_STRING) {
498 fr_strerror_const("The 'dns_label' flag can only be used with attributes of type 'string'");
499 return false;
500 }
501 da->flags.is_known_width = true; /* Lie so we don't trip up the main validation checks */
502 }
503
504 switch (da->type) {
505 case FR_TYPE_IP:
506 da->flags.is_known_width = true;
507 break;
508
509 default:
510 break;
511 }
512
513 return true;
514}
515
518 .name = "dns",
519 .default_type_size = 2,
520 .default_type_length = 2,
521 .attr = {
522 .flags = {
523 .table = dns_flags,
524 .table_len = NUM_ELEMENTS(dns_flags),
525 .len = sizeof(fr_dns_attr_flags_t)
526 },
527 .valid = attr_valid
528 },
529
530 .init = fr_dns_global_init,
531 .free = fr_dns_global_free,
532};
#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:4396
#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:449
fr_dict_protocol_t libfreeradius_dns_dict_protocol
Definition base.c:517
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:472
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:420
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:146
@ 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:134
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