The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
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: fdb3c5584e489bb3263baf4b1fa39de951829d97 $
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 malformed 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] da dictionary entry which it should be
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;
84 fr_dict_attr_t *unknown;
85 fr_dict_attr_t const *child;
86
87#if defined(STATIC_ANALYZER) || !defined(NDEBUG)
88 if (!da->parent) return -1; /* stupid static analyzers */
89#endif
90
91 FR_PROTO_TRACE("Creating Raw attribute %s", da->name);
92 FR_PROTO_HEX_DUMP(data, data_len, "fr_pair_raw_from_network");
93
94 /*
95 * Build an unknown attr of the entire data.
96 */
97 unknown = fr_dict_attr_unknown_raw_afrom_da(ctx, da);
98 if (!unknown) return -1;
99
100 vp = fr_pair_afrom_da(ctx, unknown); /* makes a copy of 'unknown' */
101 child = unknown;
102 fr_dict_attr_unknown_free(&child); /* const issues */
103 if (!vp) return -1;
104
105 /*
106 * Don't bother getting data from the network if there's no data.
107 */
108 if (data_len > 0) {
109 slen = fr_value_box_from_network(vp, &vp->data, vp->da->type, vp->da,
110 &FR_DBUFF_TMP(data, data_len), data_len, true);
111 if (slen < 0) {
113 return slen;
114 }
115 }
116
117 /*
118 * Raw VPs are always tainted.
119 */
120 vp->vp_tainted = true;
122
123 return data_len;
124}
125
126
127/** Decode a list of pairs from the network
128 *
129 * @param[in] ctx context to alloc new attributes in.
130 * @param[out] out Where to write the decoded #fr_pair_t
131 * @param[in] parent dictionary entry, must have parent->flags.array set
132 * @param[in] data to parse.
133 * @param[in] data_len of data to parse.
134 * @param[in] decode_ctx passed to decode_tlv
135 * @param[in] decode_tlv function to decode one attribute / option / tlv
136 * @param[in] verify_tlvs simple function to see if the TLVs are even vaguely well-formed
137 * @param[in] nested whether or not we create nested VPs.
138 * <0 on error - decode error, or OOM
139 * data_len on success
140 *
141 * The decode_tlv function should return an error if the option is
142 * malformed. In that case, the entire list of pairs is thrown away,
143 * and a "raw" attribute is created which contains the entire
144 * data_len.
145 *
146 * If the value is malformed, then the decode_tlv function should call
147 * fr_pair_raw_from_network() on the value, and return a positive value.
148 */
150 fr_dict_attr_t const *parent,
151 uint8_t const *data, size_t const data_len,
152 void *decode_ctx, fr_pair_decode_value_t decode_tlv,
154 bool nested)
155{
156 uint8_t const *p, *end;
157 fr_pair_list_t tlvs, *list;
158 fr_pair_t *vp = NULL;
159 TALLOC_CTX *child_ctx;
160
161 FR_PROTO_HEX_DUMP(data, data_len, "fr_pair_tlvs_from_network");
162
163 switch (parent->type) {
164 case FR_TYPE_TLV:
165 case FR_TYPE_VENDOR:
166 case FR_TYPE_STRUCT:
167 break;
168
169 default:
170 fr_strerror_printf("Internal sanity check failed, attribute \"%s\" has unexpected data type '%s'",
171 parent->name, fr_type_to_str(parent->type));
173 }
174
175 /*
176 * Do a quick sanity check to see if the TLVs are at all OK.
177 */
178 if (verify_tlvs && !verify_tlvs(data, data_len)) return -1;
179
180 p = data;
181 end = data + data_len;
182
183 if (!nested) {
184 fr_pair_list_init(&tlvs);
185 list = &tlvs;
186 child_ctx = ctx;
187 } else {
188 vp = fr_pair_afrom_da(ctx, parent);
189 if (!vp) return PAIR_DECODE_OOM;
190 list = &vp->vp_group;
191 child_ctx = vp;
192 }
193
194 while (p < end) {
195 ssize_t slen;
196
197 slen = decode_tlv(child_ctx, list, parent, p, (end - p), decode_ctx);
198 if (slen <= 0) {
199 FR_PROTO_TRACE(" tlv decode failed at offset %zu - %s", (size_t) (p - data), fr_strerror());
200 fr_pair_list_free(list);
202
203 /*
204 * Don't decode it as raw. We don't know how the TLVs are structured, so the
205 * only da we have is the parent. The output has to in the parent with a child
206 * da. So if we create a raw attribute here, then we have the raw attribute of
207 * da==parent going into the parent, which is wrong.
208 */
210 }
211
212 p += slen;
213 }
214
215 if (!nested) {
216 fr_pair_list_append(out, &tlvs);
217 } else {
219 }
220
221 return data_len;
222}
223
224
225/** Decode a DNS label or a list of DNS labels from the network
226 *
227 * @param[in] ctx context to alloc new attributes in.
228 * @param[out] out Where to write the decoded #fr_pair_t
229 * @param[in] parent dictionary entry, must have parent->flags.array set
230 * @param[in] start of the DNS labels to decode
231 * @param[in] data to parse.
232 * @param[in] data_len of data to parse.
233 * @param[in] lb struct to help with decoding packets.
234 * @param[in] exact whether the labels should entirely fill the buffer.
235 * @return
236 * <0 on error - decode error, or OOM
237 * data_len on success
238 *
239 * DNS labels exist in many protocols, and we also have src/lib/dns.c, so we might
240 * as well put a common function here, too.
241 *
242 * This function assumes that the DNS label or labels take up all of the
243 * input. If they do not, then the decoded DNS labels are freed, and
244 * a raw attribute is returned instead.
245 */
247 fr_dict_attr_t const *parent, uint8_t const *start,
248 uint8_t const *data, size_t const data_len, fr_dns_labels_t *lb, bool exact)
249{
250 ssize_t slen;
251 size_t total, labels_len;
252 fr_pair_t *vp;
253 uint8_t const *next = data;
254 fr_pair_list_t tmp;
255
256 FR_PROTO_HEX_DUMP(data, data_len, "fr_pair_dns_labels_from_network");
257
258 fr_pair_list_init(&tmp);
259
260 /*
261 * This function handles both single-valued and array
262 * types. It's just easier that way.
263 */
264 if (!parent->flags.array) {
265 /*
266 * Decode starting at "NEXT", but allowing decodes from the start of the packet.
267 */
268 slen = fr_dns_label_uncompressed_length(start, data, data_len, &next, lb);
269 if (slen <= 0) {
270 FR_PROTO_TRACE("ERROR - uncompressed length failed");
271 goto raw;
272 }
273
274 labels_len = next - data; /* decode only what we've found */
275 } else {
276 /*
277 * Get the length of the entire set of labels, up
278 * to (and including) the final 0x00.
279 *
280 * If any of the labels point outside of this
281 * area, OR they are otherwise invalid, then that's an error.
282 */
283 slen = fr_dns_labels_network_verify(start, data, data_len, data, lb);
284 if (slen <= 0) {
285 FR_PROTO_TRACE("ERROR - network verify failed");
286 goto raw;
287 }
288
289 labels_len = slen;
290 }
291
292 /*
293 * The labels MUST fill the entire buffer.
294 */
295 if (exact && (labels_len != data_len)) {
296 FR_PROTO_TRACE("ERROR - labels_len %zu != data_len %zu", labels_len, data_len);
297 raw:
298 return fr_pair_raw_from_network(ctx, out, parent, data, data_len);
299 }
300
301 /*
302 * Loop over the input buffer, decoding the labels one by
303 * one.
304 *
305 * @todo - put the labels into a child cursor, and then
306 * merge them only if it succeeds. That doesn't seem to
307 * work for some reason, and I don't have time to debug
308 * it right now. So... let's leave it.
309 */
310 for (total = 0; total < labels_len; total += slen) {
311 vp = fr_pair_afrom_da(ctx, parent);
312 if (!vp) return PAIR_DECODE_OOM;
313
314 /*
315 * Having verified the input above, this next
316 * function should never fail unless there's a
317 * bug in the code.
318 */
319 slen = fr_dns_label_to_value_box(vp, &vp->data, data, labels_len, data + total, true, lb);
320 if (slen <= 0) {
321 FR_PROTO_TRACE("ERROR - failed decoding DNS label at with %zu error %zd", total, slen);
323 fr_pair_list_free(&tmp);
324 goto raw;
325 }
326
327 fr_pair_append(&tmp, vp);
328 }
329
331 return labels_len;
332}
333
334/** Generic decode value.
335 *
336 */
338 uint8_t const *data, size_t const data_len, UNUSED void *decode_ctx)
339{
340 fr_pair_t *vp;
341 ssize_t slen;
342
343 if (!fr_type_is_leaf(parent->type)) {
344 FR_PROTO_TRACE("Cannot use generic decoder for data type %s", fr_type_to_str(parent->type));
345 fr_strerror_printf("Cannot decode data type %s", fr_type_to_str(parent->type));
346 return -1;
347 }
348
349 vp = fr_pair_afrom_da(ctx, parent);
350 if (!vp) return PAIR_DECODE_OOM;
351
352 /*
353 * No protocol-specific data types here.
354 *
355 * If we can't decode this field, then the entire
356 * structure is treated as a raw blob.
357 */
358 slen = fr_value_box_from_network(vp, &vp->data, vp->vp_type, vp->da,
359 &FR_DBUFF_TMP(data, data_len), data_len, true);
360 if (slen <= 0) {
361 FR_PROTO_TRACE("failed decoding child VP %s", vp->da->name);
363 return fr_pair_raw_from_network(ctx, out, parent, data, data_len);
364 }
365
367
368 return slen;
369}
#define UNUSED
Definition build.h:317
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:524
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:148
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
static fr_dict_attr_t * fr_dict_attr_unknown_raw_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Definition dict.h:636
void fr_dict_attr_unknown_free(fr_dict_attr_t const **da)
Free dynamically allocated (unknown attributes)
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:45
#define PAIR_DECODE_FATAL_ERROR
Fatal error - Failed decoding the packet.
Definition pair.h:49
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:149
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:246
ssize_t fr_pair_decode_value(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, uint8_t const *data, size_t const data_len, UNUSED void *decode_ctx)
Generic decode value.
Definition decode.c:337
ssize_t fr_pair_raw_from_network(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *da, uint8_t const *data, size_t data_len)
Create a "raw" pair from malformed network data.
Definition decode.c:79
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
talloc_free(reap)
@ FR_TYPE_TLV
Contains nested attributes.
@ FR_TYPE_STRUCT
like TLV, but without T or L, and fixed-width children
@ FR_TYPE_VENDOR
Attribute that represents a vendor in the attribute tree.
long int ssize_t
unsigned char uint8_t
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:1348
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:289
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.
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.
static fr_slen_t parent
Definition pair.h:857
#define FR_PROTO_HEX_DUMP(_data, _data_len, _fmt,...)
Definition proto.h:42
#define FR_PROTO_TRACE(_fmt,...)
Definition proto.h:41
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:553
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_type_is_leaf(_x)
Definition types.h:394
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:455
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:1913
static fr_slen_t data
Definition value.h:1322
static size_t char ** out
Definition value.h:1023