The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 567634561b4b0574a3debb0b718d84ca6f943e07 $
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  */
26 RCSID("$Id: 567634561b4b0574a3debb0b718d84ca6f943e07 $")
27 
28 #include "dns.h"
29 #include "attrs.h"
30 
32 
33 typedef struct {
36 } dns_option_t;
37 
39 
40 static _Thread_local fr_dns_labels_t fr_dns_labels;
41 static _Thread_local fr_dns_block_t fr_dns_blocks[256];
42 static _Thread_local uint8_t fr_dns_marker[65536];
43 
46  { .out = &dict_dns, .proto = "dns" },
47  { NULL }
48 };
49 
50 //fr_dict_attr_t const *attr_dns_packet_type;
56 
59 // { .out = &attr_dns_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT16, .dict = &dict_dns },
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 },
65  { NULL }
66 };
67 
69  [FR_DNS_QUERY] = "Query",
70  [FR_DNS_INVERSE_QUERY] = "Inverse-Query",
71  [FR_DNS_STATUS] = "Status",
72  [FR_DNS_UPDATE] = "Update",
73  [FR_DNS_STATEFUL_OPERATION] = "Stateful-Operation",
74 };
75 
76 #define DECODE_FAIL(_reason) if (reason) *reason = FR_DNS_DECODE_FAIL_ ## _reason
77 
78 static bool fr_dns_tlv_ok(uint8_t const *p, uint8_t const *end, fr_dns_decode_fail_t *reason)
79 {
80  uint16_t len;
81 
82  while (p < end) {
83  if ((p + 4) > end) {
84  DECODE_FAIL(MISSING_TLV_HEADER);
85  return false;
86  }
87 
88  len = fr_nbo_to_uint16(p + 2);
89  if ((p + 4 + len) > end) {
90  DECODE_FAIL(TLV_OVERFLOWS_RR);
91  return false;
92  }
93 
94  p += 4 + len;
95  }
96 
97  return true;
98 }
99 
100 bool fr_dns_packet_ok(uint8_t const *packet, size_t packet_len, bool query, fr_dns_decode_fail_t *reason)
101 {
102  uint8_t const *p, *end;
103  int qdcount, count, expected;
104 
105  if (packet_len <= DNS_HDR_LEN) {
106  DECODE_FAIL(MIN_LENGTH_PACKET);
107  return false;
108  }
109 
110  if (packet_len > 65535) {
111  DECODE_FAIL(MAX_LENGTH_PACKET);
112  return false;
113  }
114 
115  /*
116  * query=0, response=1
117  */
118  if (((packet[2] & 0x80) == 0) != query) {
119  DECODE_FAIL(UNEXPECTED);
120  return false;
121  }
122 
123  qdcount = fr_nbo_to_uint16(packet + 4);
124 
125  if (query) {
126  /*
127  * There should be at least one query, and no
128  * replies in the query.
129  *
130  * @todo - unless it's an IQUERY, in which case
131  * there should be no questions, and at least one
132  * answer.
133  */
134  if (!qdcount) {
135  DECODE_FAIL(NO_QUESTIONS);
136  return false;
137  }
138  if (fr_nbo_to_uint16(packet + 6) != 0) {
139  DECODE_FAIL(NS_IN_QUESTION);
140  return false;
141  }
142  if (fr_nbo_to_uint16(packet + 8) != 0) {
143  DECODE_FAIL(ANSWERS_IN_QUESTION);
144  return false;
145  }
146  // additional records can exist!
147 
148  } else {
149  /*
150  * Replies _usually_ copy the query. But not
151  * always And replies can have zero or more answers.
152  */
153  }
154 
155  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);
156  count = 0;
157 
158  p = packet + DNS_HDR_LEN;
159  end = packet + packet_len;
160 
161  /*
162  * We track valid label targets in a simple array (up to
163  * 2^14 bits of compressed pointer).
164  *
165  * Note that some labels might appear in the RRDATA
166  * field, and we don't verify those here. However, this
167  * function will verify the most common packets. As a
168  * result, any issues with overflow, etc. are more
169  * difficult to exploit.
170  */
171  memset(fr_dns_marker, 0, packet_len < (1 << 14) ? packet_len : (1 << 14));
172 
173  /*
174  * Check for wildly fake packets, by making rough
175  * estimations. This way we don't actually have to walk
176  * the packet.
177  */
178  if (p + (qdcount * 5) > end) {
179  DECODE_FAIL(TOO_MANY_RRS);
180  return false;
181  }
182  p += (qdcount * 5);
183 
184  if ((p + ((expected - qdcount) * (1 + 8 + 2))) > end) {
185  DECODE_FAIL(TOO_MANY_RRS);
186  return false;
187  }
188 
189  /*
190  * The counts are at least vaguely OK, let's walk over the whole packet.
191  */
192  p = packet + DNS_HDR_LEN;
193 
194  /*
195  * Check that lengths of RRs match.
196  */
197  while (p < end) {
198  uint16_t len = 0;
199  uint8_t const *start = p;
200  bool is_opt = false;
201 
202  /*
203  * Simple DNS label decoder
204  *
205  * @todo - move this to src/lib/util/dns.c,
206  * perhaps as fr_dns_label_verify(), and then
207  * have it also return a pointer to the next
208  * label? fr_dns_label_uncompressed_length()
209  * does similar but slightly different things.
210  */
211  while (p < end) {
212  /*
213  * 0x00 is "end of label"
214  */
215  if (!*p) {
216  p++;
217  break;
218  }
219 
220  /*
221  * 2 octets of 14-bit pointer, which must
222  * be at least somewhat sane.
223  */
224  if (*p >= 0xc0) {
225  ptrdiff_t offset;
226 
227  if ((p + 2) > end) {
228  DECODE_FAIL(POINTER_OVERFLOWS_PACKET);
229  return false;
230  }
231 
232  offset = p[1];
233  offset += ((*p & ~0xc0) << 8);
234 
235  /*
236  * Can't point to the header.
237  */
238  if (offset < 12) {
239  DECODE_FAIL(POINTER_TO_HEADER);
240  return false;
241  }
242 
243  /*
244  * Can't point to the current label.
245  */
246  if (offset >= (start - packet)) {
247  DECODE_FAIL(POINTER_LOOPS);
248  return false;
249  }
250 
251  if (!fr_dns_marker[offset]) {
252  DECODE_FAIL(POINTER_TO_NON_LABEL);
253  return false;
254  }
255 
256  /*
257  * A compressed pointer is the end of the current label.
258  */
259  p += 2;
260  break;
261  }
262 
263  /*
264  * 0b10 and 0b10 are forbidden
265  */
266  if (*p > 63) {
267  DECODE_FAIL(INVALID_POINTER);
268  return false;
269  }
270 
271  /*
272  * It must be a length byte, which doesn't cause overflow.
273  */
274  if ((p + *p + 1) > end) {
275  DECODE_FAIL(LABEL_OVERFLOWS_PACKET);
276  return false;
277  }
278 
279  /*
280  * Total length of labels can't be too high.
281  */
282  len += *p;
283  if (len >= 256) {
284  DECODE_FAIL(LABEL_TOO_LONG);
285  return false;
286  }
287 
288  /*
289  * Remember that this is where we have a
290  * label.
291  */
292  fr_dns_marker[p - packet] = 1;
293 
294  /*
295  * Go to the next label.
296  */
297  p += *p + 1;
298  }
299 
300  if (qdcount) {
301  /*
302  * qtype + qclass
303  */
304  if ((p + 4) > end) {
305  DECODE_FAIL(MISSING_QD_HEADER);
306  return false;
307  }
308 
309  p += 4;
310  qdcount--;
311  goto next;
312  }
313 
314  /*
315  * type (2) + class (2) + TTL (4)
316  *
317  * These are overloaded for the OPT RR
318  * and possibly others, but the basic
319  * idea is the same.
320  */
321  if ((p + 8) > end) {
322  DECODE_FAIL(MISSING_RR_HEADER);
323  return false;
324  }
325  is_opt = (p[0] == 0) && (p[1] == 41);
326  p += 8;
327 
328  /*
329  * rr_len
330  */
331  if ((p + 2) > end) {
332  DECODE_FAIL(MISSING_RR_LEN);
333  return false;
334  }
335 
336  len = fr_nbo_to_uint16(p);
337  if (!is_opt && (len == 0)) {
338  DECODE_FAIL(ZERO_RR_LEN);
339  return false;
340  }
341 
342  p += 2;
343  if ((p + len) > end) {
344  DECODE_FAIL(RR_OVERFLOWS_PACKET);
345  return false;
346  }
347 
348  /*
349  * Verify the TLVs, too.
350  */
351  if (is_opt && !fr_dns_tlv_ok(p, p + len, reason)) {
352  return false;
353  }
354 
355  p += len;
356 
357 next:
358  count++;
359 
360  if (count > expected) {
361  DECODE_FAIL(TOO_MANY_RRS);
362  return false;
363  }
364  }
365 
366  if (count != expected) {
367  DECODE_FAIL(TOO_FEW_RRS);
368  return false;
369  }
370 
371  DECODE_FAIL(NONE);
372  return true;
373 }
374 
375 fr_dns_labels_t *fr_dns_labels_get(uint8_t const *packet, size_t packet_len, bool init_mark)
376 {
378 
379  lb->max = 256;
380  lb->mark = fr_dns_marker;
381  lb->blocks = fr_dns_blocks;
382 
383  lb->start = packet;
384  lb->end = packet + packet_len;
385 
386  lb->num = 1;
387  lb->blocks[0].start = DNS_HDR_LEN;
388  lb->blocks[0].end = DNS_HDR_LEN;
389 
390  if (init_mark) {
391  fr_assert(packet_len <= 65535);
392  memset(lb->mark, 0, packet_len);
393  }
394 
395  return lb;
396 }
397 
398 /** Resolve/cache attributes in the DNS dictionary
399  *
400  * @return
401  * - 0 on success.
402  * - -1 on failure.
403  */
405 {
406  if (instance_count > 0) {
407  instance_count++;
408  return 0;
409  }
410 
411  instance_count++;
412 
413  if (fr_dict_autoload(dns_dict) < 0) {
414  fail:
415  instance_count--;
416  return -1;
417  }
420  goto fail;
421  }
422 
423  return 0;
424 }
425 
427 {
429 
430  if (--instance_count > 0) return;
431 
433 }
434 
436  { L("dns_label"), FLAG_ENCODE_DNS_LABEL },
437  { L("uncompressed"), FLAG_ENCODE_DNS_LABEL_UNCOMPRESSED },
438 };
439 
440 
442  UNUSED char const *name, UNUSED int attr, fr_type_t type, fr_dict_attr_flags_t *flags)
443 {
444  /*
445  * "arrays" of string/octets are encoded as a 16-bit
446  * length, followed by the actual data.
447  */
448  if (flags->array && ((type == FR_TYPE_STRING) || (type == FR_TYPE_OCTETS))) {
449  flags->is_known_width = true;
450  }
451 
452  /*
453  * "extra" signifies that subtype is being used by the
454  * dictionaries itself.
455  */
456  if (flags->extra || !flags->subtype) return true;
457 
458  if (type != FR_TYPE_STRING) {
459  fr_strerror_const("The 'dns_label' flag can only be used with attributes of type 'string'");
460  return false;
461  }
462 
463  flags->is_known_width = true;
464 
465  return true;
466 }
467 
470  .name = "dns",
471  .default_type_size = 2,
472  .default_type_length = 2,
473  .subtype_table = subtype_table,
474  .subtype_table_len = NUM_ELEMENTS(subtype_table),
475  .attr_valid = attr_valid,
476 
477  .init = fr_dns_global_init,
478  .free = fr_dns_global_free,
479 };
static fr_dict_t * dict
Definition: fuzzer.c:46
#define RCSID(id)
Definition: build.h:444
#define L(_str)
Helper for initialising arrays of string literals.
Definition: build.h:207
#define UNUSED
Definition: build.h:313
#define NUM_ELEMENTS(_t)
Definition: build.h:335
@ FLAG_ENCODE_DNS_LABEL
encode as DNS label
Definition: dhcpv4.h:71
#define fr_dict_autofree(_to_free)
Definition: dict.h:674
unsigned int array
Pack multiples into 1 attr.
Definition: dict.h:88
unsigned int extra
really "subtype is used by dict, not by protocol"
Definition: dict.h:109
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:250
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:263
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:3647
#define fr_dict_autoload(_to_load)
Definition: dict.h:671
unsigned int is_known_width
is treated as if it has a known width for structs
Definition: dict.h:90
char const * name
name of this protocol
Definition: dict.h:342
uint8_t subtype
protocol-specific values, OR key fields
Definition: dict.h:118
Specifies an attribute which must be present for the module to function.
Definition: dict.h:249
Values of the encryption flags.
Definition: merged_model.c:139
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:262
Protocol-specific callbacks in libfreeradius-PROTOCOL.
Definition: dict.h:341
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 max
Definition: dns.h:40
int num
number of used labels
Definition: dns.h:39
unsigned short uint16_t
Definition: merged_model.c:31
fr_type_t
Definition: merged_model.c:80
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ FR_TYPE_STRUCT
like TLV, but without T or L, and fixed-width children
Definition: merged_model.c:119
@ FR_TYPE_OCTETS
Raw octets.
Definition: merged_model.c:84
unsigned int uint32_t
Definition: merged_model.c:33
unsigned char uint8_t
Definition: merged_model.c:30
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:137
#define DECODE_FAIL(_reason)
Definition: base.c:76
fr_dict_attr_autoload_t dns_dict_attr[]
Definition: base.c:58
int fr_dns_global_init(void)
Resolve/cache attributes in the DNS dictionary.
Definition: base.c:404
fr_dict_protocol_t libfreeradius_dns_dict_protocol
Definition: base.c:469
static _Thread_local fr_dns_block_t fr_dns_blocks[256]
Definition: base.c:41
fr_dict_attr_t const * attr_dns_ns
Definition: base.c:54
uint16_t code
Definition: base.c:34
static _Thread_local fr_dns_labels_t fr_dns_labels
Definition: base.c:40
static _Thread_local uint8_t fr_dns_marker[65536]
Definition: base.c:42
static uint32_t instance_count
Definition: base.c:31
uint16_t length
Definition: base.c:35
fr_dict_attr_t const * attr_dns_packet
Definition: base.c:51
void fr_dns_global_free(void)
Definition: base.c:426
fr_dict_attr_t const * attr_dns_question
Definition: base.c:52
fr_dict_autoload_t dns_dict[]
Definition: base.c:45
fr_dns_labels_t * fr_dns_labels_get(uint8_t const *packet, size_t packet_len, bool init_mark)
Definition: base.c:375
fr_dict_attr_t const * attr_dns_ar
Definition: base.c:55
fr_dict_attr_t const * attr_dns_rr
Definition: base.c:53
char const * fr_dns_packet_names[FR_DNS_CODE_MAX]
Definition: base.c:68
static bool attr_valid(UNUSED fr_dict_t *dict, UNUSED fr_dict_attr_t const *parent, UNUSED char const *name, UNUSED int attr, fr_type_t type, fr_dict_attr_flags_t *flags)
Definition: base.c:441
static bool fr_dns_tlv_ok(uint8_t const *p, uint8_t const *end, fr_dns_decode_fail_t *reason)
Definition: base.c:78
static fr_table_num_ordered_t const subtype_table[]
Definition: base.c:435
fr_dict_t const * dict_dns
Definition: base.c:38
bool fr_dns_packet_ok(uint8_t const *packet, size_t packet_len, bool query, fr_dns_decode_fail_t *reason)
Definition: base.c:100
@ FR_DNS_STATEFUL_OPERATION
Definition: dns.h:98
@ FR_DNS_QUERY
Definition: dns.h:93
@ FR_DNS_INVERSE_QUERY
Definition: dns.h:94
@ FR_DNS_STATUS
Definition: dns.h:95
@ FR_DNS_UPDATE
Definition: dns.h:97
@ FR_DNS_CODE_MAX
Definition: dns.h:99
#define DNS_HDR_LEN
Definition: dns.h:141
fr_dns_decode_fail_t
Definition: dns.h:111
@ FLAG_ENCODE_DNS_LABEL_UNCOMPRESSED
encode as uncompressed DNS label
Definition: dns.h:79
VQP attributes.
static char const * name
return count
Definition: module.c:175
fr_assert(0)
fr_aka_sim_id_type_t type
An element in an arbitrarily ordered array of name to num mappings.
Definition: table.h:53
static fr_slen_t parent
Definition: pair.h:844
#define fr_strerror_const(_msg)
Definition: strerror.h:223