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: d44eea04063ae7d40f1c8eac3c74a7dfd5c286f7 $
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: d44eea04063ae7d40f1c8eac3c74a7dfd5c286f7 $")
27
28#include "dns.h"
29#include "attrs.h"
30
32static bool instantiated = false;
33
38
40
41static _Thread_local fr_dns_labels_t fr_dns_labels;
42static _Thread_local fr_dns_block_t fr_dns_blocks[256];
43static _Thread_local uint8_t fr_dns_marker[65536];
44
47 { .out = &dict_dns, .proto = "dns" },
48 { NULL }
49};
50
51//fr_dict_attr_t const *attr_dns_packet_type;
57
60// { .out = &attr_dns_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT16, .dict = &dict_dns },
61 { .out = &attr_dns_packet, .name = "Header", .type = FR_TYPE_STRUCT, .dict = &dict_dns },
62 { .out = &attr_dns_question, .name = "Question", .type = FR_TYPE_STRUCT, .dict = &dict_dns },
63 { .out = &attr_dns_rr, .name = "Resource-Record", .type = FR_TYPE_STRUCT, .dict = &dict_dns },
64 { .out = &attr_dns_ns, .name = "Name-Server", .type = FR_TYPE_STRUCT, .dict = &dict_dns },
65 { .out = &attr_dns_ar, .name = "Additional-Record", .type = FR_TYPE_STRUCT, .dict = &dict_dns },
66 { NULL }
67};
68
70 [FR_DNS_QUERY] = "Query",
71 [FR_DNS_INVERSE_QUERY] = "Inverse-Query",
72 [FR_DNS_STATUS] = "Status",
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
114 if (packet_len <= DNS_HDR_LEN) {
115 DECODE_FAIL(MIN_LENGTH_PACKET);
116 return false;
117 }
118
119 if (packet_len > 65535) {
120 DECODE_FAIL(MAX_LENGTH_PACKET);
121 return false;
122 }
123
124 /*
125 * query=0, response=1
126 */
127 if (((packet[2] & 0x80) == 0) != query) {
128 DECODE_FAIL(UNEXPECTED);
129 return false;
130 }
131
132 qdcount = fr_nbo_to_uint16(packet + 4);
133
134 if (query) {
135 /*
136 * There should be at least one query, and no
137 * replies in the query.
138 *
139 * @todo - unless it's an IQUERY, in which case
140 * there should be no questions, and at least one
141 * answer.
142 */
143 if (!qdcount) {
144 DECODE_FAIL(NO_QUESTIONS);
145 return false;
146 }
147 if (fr_nbo_to_uint16(packet + 6) != 0) {
148 DECODE_FAIL(NS_IN_QUESTION);
149 return false;
150 }
151 if (fr_nbo_to_uint16(packet + 8) != 0) {
152 DECODE_FAIL(ANSWERS_IN_QUESTION);
153 return false;
154 }
155 // additional records can exist!
156
157 } else {
158 /*
159 * Replies _usually_ copy the query. But not
160 * always And replies can have zero or more answers.
161 */
162 }
163
164 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);
165 count = 0;
166
167 p = packet + DNS_HDR_LEN;
168 end = packet + packet_len;
169
170 /*
171 * We track valid label targets in a simple array (up to
172 * 2^14 bits of compressed pointer).
173 *
174 * Note that some labels might appear in the RRDATA
175 * field, and we don't verify those here. However, this
176 * function will verify the most common packets. As a
177 * result, any issues with overflow, etc. are more
178 * difficult to exploit.
179 */
180 memset(fr_dns_marker, 0, packet_len < (1 << 14) ? packet_len : (1 << 14));
181
182 /*
183 * Check for wildly fake packets, by making rough
184 * estimations. This way we don't actually have to walk
185 * the packet.
186 */
187 if (p + (qdcount * 5) > end) {
188 DECODE_FAIL(TOO_MANY_RRS);
189 return false;
190 }
191 p += (qdcount * 5);
192
193 if ((p + ((expected - qdcount) * (1 + 8 + 2))) > end) {
194 DECODE_FAIL(TOO_MANY_RRS);
195 return false;
196 }
197
198 /*
199 * The counts are at least vaguely OK, let's walk over the whole packet.
200 */
201 p = packet + DNS_HDR_LEN;
202
203 /*
204 * Check that lengths of RRs match.
205 */
206 while (p < end) {
207 uint16_t len = 0;
208 uint8_t const *start = p;
209 bool is_opt = false;
210
211 /*
212 * Simple DNS label decoder
213 *
214 * @todo - move this to src/lib/util/dns.c,
215 * perhaps as fr_dns_label_verify(), and then
216 * have it also return a pointer to the next
217 * label? fr_dns_label_uncompressed_length()
218 * does similar but slightly different things.
219 */
220 while (p < end) {
221 /*
222 * 0x00 is "end of label"
223 */
224 if (!*p) {
225 p++;
226 break;
227 }
228
229 /*
230 * 2 octets of 14-bit pointer, which must
231 * be at least somewhat sane.
232 */
233 if (*p >= 0xc0) {
234 ptrdiff_t offset;
235
236 if ((p + 2) > end) {
237 DECODE_FAIL(POINTER_OVERFLOWS_PACKET);
238 return false;
239 }
240
241 offset = p[1];
242 offset += ((*p & ~0xc0) << 8);
243
244 /*
245 * Can't point to the header.
246 */
247 if (offset < 12) {
248 DECODE_FAIL(POINTER_TO_HEADER);
249 return false;
250 }
251
252 /*
253 * Can't point to the current label.
254 */
255 if (offset >= (start - packet)) {
256 DECODE_FAIL(POINTER_LOOPS);
257 return false;
258 }
259
260 if (!fr_dns_marker[offset]) {
261 DECODE_FAIL(POINTER_TO_NON_LABEL);
262 return false;
263 }
264
265 /*
266 * A compressed pointer is the end of the current label.
267 */
268 p += 2;
269 break;
270 }
271
272 /*
273 * 0b10 and 0b10 are forbidden
274 */
275 if (*p > 63) {
276 DECODE_FAIL(INVALID_POINTER);
277 return false;
278 }
279
280 /*
281 * It must be a length byte, which doesn't cause overflow.
282 */
283 if ((p + *p + 1) > end) {
284 DECODE_FAIL(LABEL_OVERFLOWS_PACKET);
285 return false;
286 }
287
288 /*
289 * Total length of labels can't be too high.
290 */
291 len += *p;
292 if (len >= 256) {
293 DECODE_FAIL(LABEL_TOO_LONG);
294 return false;
295 }
296
297 /*
298 * Remember that this is where we have a
299 * label.
300 */
301 fr_dns_marker[p - packet] = 1;
302
303 /*
304 * Go to the next label.
305 */
306 p += *p + 1;
307 }
308
309 if (qdcount) {
310 /*
311 * qtype + qclass
312 */
313 if ((p + 4) > end) {
314 DECODE_FAIL(MISSING_QD_HEADER);
315 return false;
316 }
317
318 p += 4;
319 qdcount--;
320 goto next;
321 }
322
323 /*
324 * type (2) + class (2) + TTL (4)
325 *
326 * These are overloaded for the OPT RR
327 * and possibly others, but the basic
328 * idea is the same.
329 */
330 if ((p + 8) > end) {
331 DECODE_FAIL(MISSING_RR_HEADER);
332 return false;
333 }
334 is_opt = (p[0] == 0) && (p[1] == 41);
335 p += 8;
336
337 /*
338 * rr_len
339 */
340 if ((p + 2) > end) {
341 DECODE_FAIL(MISSING_RR_LEN);
342 return false;
343 }
344
345 len = fr_nbo_to_uint16(p);
346 if (!is_opt && (len == 0)) {
347 DECODE_FAIL(ZERO_RR_LEN);
348 return false;
349 }
350
351 p += 2;
352 if ((p + len) > end) {
353 DECODE_FAIL(RR_OVERFLOWS_PACKET);
354 return false;
355 }
356
357 /*
358 * Verify the TLVs, too.
359 */
360 if (is_opt && !fr_dns_tlv_ok(p, p + len, reason)) {
361 return false;
362 }
363
364 p += len;
365
366next:
367 count++;
368
369 if (count > expected) {
370 DECODE_FAIL(TOO_MANY_RRS);
371 return false;
372 }
373 }
374
375 if (count != expected) {
376 DECODE_FAIL(TOO_FEW_RRS);
377 return false;
378 }
379
380 DECODE_FAIL(NONE);
381 return true;
382}
383
384fr_dns_labels_t *fr_dns_labels_get(uint8_t const *packet, size_t packet_len, bool init_mark)
385{
387
388 lb->max = 256;
389 lb->mark = fr_dns_marker;
390 lb->blocks = fr_dns_blocks;
391
392 lb->start = packet;
393 lb->end = packet + packet_len;
394
395 lb->num = 1;
396 lb->blocks[0].start = DNS_HDR_LEN;
397 lb->blocks[0].end = DNS_HDR_LEN;
398
399 if (init_mark) {
400 fr_assert(packet_len <= 65535);
401 memset(lb->mark, 0, packet_len);
402 }
403
404 return lb;
405}
406
407/** Resolve/cache attributes in the DNS dictionary
408 *
409 * @return
410 * - 0 on success.
411 * - -1 on failure.
412 */
414{
415 if (instance_count > 0) {
417 return 0;
418 }
419
421
422 if (fr_dict_autoload(dns_dict) < 0) {
423 fail:
425 return -1;
426 }
429 goto fail;
430 }
431
432 instantiated = true;
433 return 0;
434}
435
437{
438 if (!instantiated) return;
439
441
442 if (--instance_count > 0) return;
443
445 instantiated = false;
446}
447
449{
450 if (da->flags.array) {
451 fr_strerror_const("The 'array' flag cannot be used with DNS");
452 return false;
453 }
454
455 if (da->type == FR_TYPE_ATTR) {
456 fr_strerror_const("The 'attribute' data type cannot be used with DNS");
457 return false;
458 }
459
461 if (da->type != FR_TYPE_STRING) {
462 fr_strerror_const("The 'dns_label' flag can only be used with attributes of type 'string'");
463 return false;
464 }
465 da->flags.is_known_width = true; /* Lie so we don't trip up the main validation checks */
466 }
467
468 return true;
469}
470
473 .name = "dns",
474 .default_type_size = 2,
475 .default_type_length = 2,
476 .attr = {
477 .flags = {
478 .table = dns_flags,
479 .table_len = NUM_ELEMENTS(dns_flags),
480 .len = sizeof(fr_dns_attr_flags_t)
481 },
482 .valid = attr_valid
483 },
484
485 .init = fr_dns_global_init,
486 .free = fr_dns_global_free,
487};
#define RCSID(id)
Definition build.h:485
#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:892
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:287
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:300
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:4227
#define fr_dict_autoload(_to_load)
Definition dict.h:889
char const * name
name of this protocol
Definition dict.h:448
#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:427
Specifies an attribute which must be present for the module to function.
Definition dict.h:286
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:299
Protocol specific custom flag definitnion.
Definition dict.h:417
Protocol-specific callbacks in libfreeradius-PROTOCOL.
Definition dict.h:447
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:34
#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:413
fr_dict_protocol_t libfreeradius_dns_dict_protocol
Definition base.c:472
static _Thread_local fr_dns_block_t fr_dns_blocks[256]
Definition base.c:42
fr_dict_attr_t const * attr_dns_ns
Definition base.c:55
uint16_t code
Definition base.c:35
static _Thread_local fr_dns_labels_t fr_dns_labels
Definition base.c:41
static _Thread_local uint8_t fr_dns_marker[65536]
Definition base.c:43
uint16_t length
Definition base.c:36
static bool attr_valid(fr_dict_attr_t *da)
Definition base.c:448
fr_dict_attr_t const * attr_dns_packet
Definition base.c:52
void fr_dns_global_free(void)
Definition base.c:436
fr_dict_attr_t const * attr_dns_question
Definition base.c:53
fr_dict_autoload_t dns_dict[]
Definition base.c:46
fr_dns_labels_t * fr_dns_labels_get(uint8_t const *packet, size_t packet_len, bool init_mark)
Definition base.c:384
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:69
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:144
@ FR_DNS_STATEFUL_OPERATION
Definition dns.h:89
@ FR_DNS_QUERY
Definition dns.h:84
@ 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:132
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