The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
decode.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: 77e30d51aabb02ff1c5064937cad835370af4378 $
19  *
20  * @file src/lib/util/decode.c
21  * @brief Generic functions for decoding protocols.
22  *
23  * @copyright 2022 Network RADIUS SAS (legal@networkradius.com)
24  */
25 #include <freeradius-devel/io/test_point.h>
26 #include <freeradius-devel/util/proto.h>
27 #include <freeradius-devel/util/decode.h>
28 
29 /** Decode an array of values from the network
30  *
31  * @param[in] ctx context to alloc new attributes in.
32  * @param[out] out Where to write the decoded #fr_pair_t
33  * @param[in] parent dictionary entry, must have parent->flags.array set
34  * @param[in] data to parse.
35  * @param[in] data_len of data to parse.
36  * @param[in] decode_ctx passed to decode_value
37  * @param[in] decode_value function to decode one value.
38  * <0 on error - decode error, or OOM
39  * data_len on success
40  */
42  uint8_t const *data, size_t data_len,
43  void *decode_ctx, fr_pair_decode_value_t decode_value)
44 {
45  uint8_t const *p = data, *end = p + data_len;
46  ssize_t slen;
47 
48  FR_PROTO_HEX_DUMP(data, data_len, "fr_pair_array_from_network");
49 
50  if (!fr_cond_assert_msg(parent->flags.array,
51  "%s: Internal sanity check failed, attribute \"%s\" does not have array bit set",
52  __FUNCTION__, parent->name)) return PAIR_DECODE_FATAL_ERROR;
53 
54  /*
55  * Catch stupidities.
56  */
57  if (data_len == 0) return data_len;
58 
59  while (p < end) {
60  slen = decode_value(ctx, out, parent, p, (end - p), decode_ctx);
61  if (slen <= 0) return slen - (p - data);
62 
63  p += slen;
64  }
65 
66  return data_len;
67 }
68 
69 /** Create a "raw" pair from the network data
70  *
71  * @param[in] ctx context to alloc new attributes in.
72  * @param[out] out Where to write the decoded #fr_pair_t
73  * @param[in] parent dictionary entry
74  * @param[in] data to parse.
75  * @param[in] data_len of data to parse.
76  * <0 on error - decode error, or OOM
77  * data_len on success
78  */
80  uint8_t const *data, size_t data_len)
81 {
82  ssize_t slen;
83  fr_pair_t *vp;
84  fr_dict_attr_t *unknown;
85  fr_dict_attr_t const *child;
86 
87 #if defined(STATIC_ANALYZER) || !defined(NDEBUG)
88  if (!parent->parent) return -1; /* stupid static analyzers */
89 #endif
90 
91  FR_PROTO_HEX_DUMP(data, data_len, "fr_pair_raw_from_network");
92 
93  /*
94  * Build an unknown attr of the entire data.
95  */
96  unknown = fr_dict_unknown_attr_afrom_da(ctx, parent);
97  if (!unknown) return -1;
98 
99  vp = fr_pair_afrom_da(ctx, unknown); /* makes a copy of 'unknown' */
100  child = unknown;
101  fr_dict_unknown_free(&child); /* const issues */
102  if (!vp) return -1;
103 
104  /*
105  * Don't bother getting data from the network if there's no data.
106  */
107  if (data_len > 0) {
108  slen = fr_value_box_from_network(vp, &vp->data, vp->da->type, vp->da,
109  &FR_DBUFF_TMP(data, data_len), data_len, true);
110  if (slen < 0) {
111  talloc_free(vp);
112  return slen;
113  }
114  }
115 
116  /*
117  * Raw VPs are always tainted.
118  */
119  vp->vp_tainted = true;
121 
122  return data_len;
123 }
124 
125 
126 /** Decode a list of pairs from the network
127  *
128  * @param[in] ctx context to alloc new attributes in.
129  * @param[out] out Where to write the decoded #fr_pair_t
130  * @param[in] parent dictionary entry, must have parent->flags.array set
131  * @param[in] data to parse.
132  * @param[in] data_len of data to parse.
133  * @param[in] decode_ctx passed to decode_tlv
134  * @param[in] decode_tlv function to decode one attribute / option / tlv
135  * @param[in] verify_tlvs simple function to see if the TLVs are even vaguely well-formed
136  * @param[in] nested whether or not we create nested VPs.
137  * <0 on error - decode error, or OOM
138  * data_len on success
139  *
140  * The decode_tlv function should return an error if the option is
141  * malformed. In that case, the entire list of pairs is thrown away,
142  * and a "raw" attribute is created which contains the entire
143  * data_len.
144  *
145  * If the value is malformed, then the decode_tlv function should call
146  * fr_pair_raw_from_network() on the value, and return a positive value.
147  */
149  fr_dict_attr_t const *parent,
150  uint8_t const *data, size_t const data_len,
151  void *decode_ctx, fr_pair_decode_value_t decode_tlv,
153  bool nested)
154 {
155  uint8_t const *p, *end;
156  fr_pair_list_t tlvs, *list;
157  fr_pair_t *vp = NULL;
158  TALLOC_CTX *child_ctx;
159 
160  FR_PROTO_HEX_DUMP(data, data_len, "fr_pair_tlvs_from_network");
161 
162  if (!fr_cond_assert_msg((parent->type == FR_TYPE_TLV || (parent->type == FR_TYPE_VENDOR)),
163  "%s: Internal sanity check failed, attribute \"%s\" is not of type 'tlv'",
164  __FUNCTION__, parent->name)) return PAIR_DECODE_FATAL_ERROR;
165 
166  /*
167  * Do a quick sanity check to see if the TLVs are at all OK.
168  */
169  if (verify_tlvs && !verify_tlvs(data, data_len)) return -1;
170 
171  p = data;
172  end = data + data_len;
173 
174  if (!nested) {
175  fr_pair_list_init(&tlvs);
176  list = &tlvs;
177  child_ctx = ctx;
178  } else {
179  vp = fr_pair_afrom_da(ctx, parent);
180  if (!vp) return PAIR_DECODE_OOM;
181  list = &vp->vp_group;
182  child_ctx = vp;
183  }
184 
185  while (p < end) {
186  ssize_t slen;
187 
188  slen = decode_tlv(child_ctx, list, parent, p, (end - p), decode_ctx);
189  if (slen <= 0) {
190  FR_PROTO_TRACE(" tlv decode failed at offset %zu - %s", (size_t) (p - data), fr_strerror());
191  fr_pair_list_free(list);
192  talloc_free(vp);
193 
194  /*
195  * Don't decode it as raw. We don't know how the TLVs are structured, so the
196  * only da we have is the parent. The output has to in the parent with a child
197  * da. So if we create a raw attribute here, then we have the raw attribute of
198  * da==parent going into the parent, which is wrong.
199  */
201  }
202 
203  p += slen;
204  }
205 
206  if (!nested) {
207  fr_pair_list_append(out, &tlvs);
208  } else {
210  }
211 
212  return data_len;
213 }
214 
215 
216 /** Decode a DNS label or a list of DNS labels from the network
217  *
218  * @param[in] ctx context to alloc new attributes in.
219  * @param[out] out Where to write the decoded #fr_pair_t
220  * @param[in] parent dictionary entry, must have parent->flags.array set
221  * @param[in] start of the DNS labels to decode
222  * @param[in] data to parse.
223  * @param[in] data_len of data to parse.
224  * @param[in] lb struct to help with decoding packets.
225  * @param[in] exact whether the labels should entirely fill the buffer.
226  * @return
227  * <0 on error - decode error, or OOM
228  * data_len on success
229  *
230  * DNS labels exist in many protocols, and we also have src/lib/dns.c, so we might
231  * as well put a common function here, too.
232  *
233  * This function assumes that the DNS label or labels take up all of the
234  * input. If they do not, then the decoded DNS labels are freed, and
235  * a raw attribute is returned instead.
236  */
238  fr_dict_attr_t const *parent, uint8_t const *start,
239  uint8_t const *data, size_t const data_len, fr_dns_labels_t *lb, bool exact)
240 {
241  ssize_t slen;
242  size_t total, labels_len;
243  fr_pair_t *vp;
244  uint8_t const *next = data;
245  fr_pair_list_t tmp;
246 
247  FR_PROTO_HEX_DUMP(data, data_len, "fr_pair_dns_labels_from_network");
248 
249  fr_pair_list_init(&tmp);
250 
251  /*
252  * This function handles both single-valued and array
253  * types. It's just easier that way.
254  */
255  if (!parent->flags.array) {
256  /*
257  * Decode starting at "NEXT", but allowing decodes from the start of the packet.
258  */
259  slen = fr_dns_label_uncompressed_length(start, data, data_len, &next, lb);
260  if (slen <= 0) {
261  FR_PROTO_TRACE("ERROR - uncompressed length failed");
262  goto raw;
263  }
264 
265  labels_len = next - data; /* decode only what we've found */
266  } else {
267  /*
268  * Get the length of the entire set of labels, up
269  * to (and including) the final 0x00.
270  *
271  * If any of the labels point outside of this
272  * area, OR they are otherwise invalid, then that's an error.
273  */
274  slen = fr_dns_labels_network_verify(start, data, data_len, data, lb);
275  if (slen <= 0) {
276  FR_PROTO_TRACE("ERROR - network verify failed");
277  goto raw;
278  }
279 
280  labels_len = slen;
281  }
282 
283  /*
284  * The labels MUST fill the entire buffer.
285  */
286  if (exact && (labels_len != data_len)) {
287  FR_PROTO_TRACE("ERROR - labels_len %zu != data_len %zu", labels_len, data_len);
288  raw:
289  return fr_pair_raw_from_network(ctx, out, parent, data, data_len);
290  }
291 
292  /*
293  * Loop over the input buffer, decoding the labels one by
294  * one.
295  *
296  * @todo - put the labels into a child cursor, and then
297  * merge them only if it succeeds. That doesn't seem to
298  * work for some reason, and I don't have time to debug
299  * it right now. So... let's leave it.
300  */
301  for (total = 0; total < labels_len; total += slen) {
302  vp = fr_pair_afrom_da(ctx, parent);
303  if (!vp) return PAIR_DECODE_OOM;
304 
305  /*
306  * Having verified the input above, this next
307  * function should never fail unless there's a
308  * bug in the code.
309  */
310  slen = fr_dns_label_to_value_box(vp, &vp->data, data, labels_len, data + total, true, lb);
311  if (slen <= 0) {
312  FR_PROTO_TRACE("ERROR - failed decoding DNS label at with %zu error %zd", total, slen);
313  talloc_free(vp);
314  fr_pair_list_free(&tmp);
315  goto raw;
316  }
317 
318  fr_pair_append(&tmp, vp);
319  }
320 
321  fr_pair_list_append(out, &tmp);
322  return labels_len;
323 }
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition: dbuff.h:509
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition: debug.h:154
bool(* fr_pair_tlvs_verify_t)(uint8_t const *data, size_t const data_len)
Definition: decode.h:49
ssize_t(* fr_pair_decode_value_t)(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, uint8_t const *data, size_t const data_len, void *decode_ctx)
Decode a value from the network into an output fr_pair_list_t.
Definition: decode.h:45
void fr_dict_unknown_free(fr_dict_attr_t const **da)
Free dynamically allocated (unknown attributes)
Definition: dict_unknown.c:148
fr_dict_attr_t * fr_dict_unknown_attr_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da))
Initialise an octets type attribute from a da.
Definition: dict_unknown.c:378
ssize_t fr_dns_label_uncompressed_length(uint8_t const *packet, uint8_t const *buf, size_t buf_len, uint8_t const **next, fr_dns_labels_t *lb)
Get the uncompressed length of a DNS label in a network buffer.
Definition: dns.c:884
ssize_t fr_dns_labels_network_verify(uint8_t const *packet, uint8_t const *buf, size_t buf_len, uint8_t const *start, fr_dns_labels_t *lb)
Verify that a network buffer contains valid DNS labels.
Definition: dns.c:1137
ssize_t fr_dns_label_to_value_box(TALLOC_CTX *ctx, fr_value_box_t *dst, uint8_t const *src, size_t len, uint8_t const *label, bool tainted, fr_dns_labels_t *lb)
Decode a fr_value_box_t from one DNS label.
Definition: dns.c:1225
#define PAIR_DECODE_OOM
Fatal error - Out of memory.
Definition: pair.h:46
#define PAIR_DECODE_FATAL_ERROR
Fatal error - Failed decoding the packet.
Definition: pair.h:50
ssize_t fr_pair_tlvs_from_network(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, uint8_t const *data, size_t const data_len, void *decode_ctx, fr_pair_decode_value_t decode_tlv, fr_pair_tlvs_verify_t verify_tlvs, bool nested)
Decode a list of pairs from the network.
Definition: decode.c:148
ssize_t fr_pair_dns_labels_from_network(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, uint8_t const *start, uint8_t const *data, size_t const data_len, fr_dns_labels_t *lb, bool exact)
Decode a DNS label or a list of DNS labels from the network.
Definition: decode.c:237
ssize_t fr_pair_array_from_network(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, uint8_t const *data, size_t data_len, void *decode_ctx, fr_pair_decode_value_t decode_value)
Decode an array of values from the network.
Definition: decode.c:41
ssize_t fr_pair_raw_from_network(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, uint8_t const *data, size_t data_len)
Create a "raw" pair from the network data.
Definition: decode.c:79
talloc_free(reap)
@ FR_TYPE_TLV
Contains nested attributes.
Definition: merged_model.c:118
@ FR_TYPE_VENDOR
Attribute that represents a vendor in the attribute tree.
Definition: merged_model.c:122
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition: pair.c:278
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition: pair.c:1340
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition: pair.c:46
static bool verify_tlvs(uint8_t const *data, size_t data_len)
Definition: decode.c:41
#define decode_value
Definition: decode.c:410
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition: pair.h:69
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
Definition: pair_inline.c:113
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
Definition: pair_inline.c:182
static fr_slen_t parent
Definition: pair.h:844
#define FR_PROTO_HEX_DUMP(_data, _data_len, _fmt,...)
Definition: proto.h:41
#define FR_PROTO_TRACE(_fmt,...)
Definition: proto.h:40
char const * fr_strerror(void)
Get the last library error.
Definition: strerror.c:554
ssize_t fr_value_box_from_network(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t type, fr_dict_attr_t const *enumv, fr_dbuff_t *dbuff, size_t len, bool tainted)
Decode a fr_value_box_t from serialized binary data.
Definition: value.c:1709
static fr_slen_t data
Definition: value.h:1259
static size_t char ** out
Definition: value.h:984