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: ab6099a5e65e9184df1a4ae17e29d219b525e398 $
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 if (!fr_cond_assert_msg((parent->type == FR_TYPE_TLV || (parent->type == FR_TYPE_VENDOR)),
164 "%s: Internal sanity check failed, attribute \"%s\" is not of type 'tlv'",
165 __FUNCTION__, parent->name)) return PAIR_DECODE_FATAL_ERROR;
166
167 /*
168 * Do a quick sanity check to see if the TLVs are at all OK.
169 */
170 if (verify_tlvs && !verify_tlvs(data, data_len)) return -1;
171
172 p = data;
173 end = data + data_len;
174
175 if (!nested) {
176 fr_pair_list_init(&tlvs);
177 list = &tlvs;
178 child_ctx = ctx;
179 } else {
180 vp = fr_pair_afrom_da(ctx, parent);
181 if (!vp) return PAIR_DECODE_OOM;
182 list = &vp->vp_group;
183 child_ctx = vp;
184 }
185
186 while (p < end) {
187 ssize_t slen;
188
189 slen = decode_tlv(child_ctx, list, parent, p, (end - p), decode_ctx);
190 if (slen <= 0) {
191 FR_PROTO_TRACE(" tlv decode failed at offset %zu - %s", (size_t) (p - data), fr_strerror());
192 fr_pair_list_free(list);
194
195 /*
196 * Don't decode it as raw. We don't know how the TLVs are structured, so the
197 * only da we have is the parent. The output has to in the parent with a child
198 * da. So if we create a raw attribute here, then we have the raw attribute of
199 * da==parent going into the parent, which is wrong.
200 */
202 }
203
204 p += slen;
205 }
206
207 if (!nested) {
208 fr_pair_list_append(out, &tlvs);
209 } else {
211 }
212
213 return data_len;
214}
215
216
217/** Decode a DNS label or a list of DNS labels from the network
218 *
219 * @param[in] ctx context to alloc new attributes in.
220 * @param[out] out Where to write the decoded #fr_pair_t
221 * @param[in] parent dictionary entry, must have parent->flags.array set
222 * @param[in] start of the DNS labels to decode
223 * @param[in] data to parse.
224 * @param[in] data_len of data to parse.
225 * @param[in] lb struct to help with decoding packets.
226 * @param[in] exact whether the labels should entirely fill the buffer.
227 * @return
228 * <0 on error - decode error, or OOM
229 * data_len on success
230 *
231 * DNS labels exist in many protocols, and we also have src/lib/dns.c, so we might
232 * as well put a common function here, too.
233 *
234 * This function assumes that the DNS label or labels take up all of the
235 * input. If they do not, then the decoded DNS labels are freed, and
236 * a raw attribute is returned instead.
237 */
239 fr_dict_attr_t const *parent, uint8_t const *start,
240 uint8_t const *data, size_t const data_len, fr_dns_labels_t *lb, bool exact)
241{
242 ssize_t slen;
243 size_t total, labels_len;
244 fr_pair_t *vp;
245 uint8_t const *next = data;
246 fr_pair_list_t tmp;
247
248 FR_PROTO_HEX_DUMP(data, data_len, "fr_pair_dns_labels_from_network");
249
250 fr_pair_list_init(&tmp);
251
252 /*
253 * This function handles both single-valued and array
254 * types. It's just easier that way.
255 */
256 if (!parent->flags.array) {
257 /*
258 * Decode starting at "NEXT", but allowing decodes from the start of the packet.
259 */
260 slen = fr_dns_label_uncompressed_length(start, data, data_len, &next, lb);
261 if (slen <= 0) {
262 FR_PROTO_TRACE("ERROR - uncompressed length failed");
263 goto raw;
264 }
265
266 labels_len = next - data; /* decode only what we've found */
267 } else {
268 /*
269 * Get the length of the entire set of labels, up
270 * to (and including) the final 0x00.
271 *
272 * If any of the labels point outside of this
273 * area, OR they are otherwise invalid, then that's an error.
274 */
275 slen = fr_dns_labels_network_verify(start, data, data_len, data, lb);
276 if (slen <= 0) {
277 FR_PROTO_TRACE("ERROR - network verify failed");
278 goto raw;
279 }
280
281 labels_len = slen;
282 }
283
284 /*
285 * The labels MUST fill the entire buffer.
286 */
287 if (exact && (labels_len != data_len)) {
288 FR_PROTO_TRACE("ERROR - labels_len %zu != data_len %zu", labels_len, data_len);
289 raw:
290 return fr_pair_raw_from_network(ctx, out, parent, data, data_len);
291 }
292
293 /*
294 * Loop over the input buffer, decoding the labels one by
295 * one.
296 *
297 * @todo - put the labels into a child cursor, and then
298 * merge them only if it succeeds. That doesn't seem to
299 * work for some reason, and I don't have time to debug
300 * it right now. So... let's leave it.
301 */
302 for (total = 0; total < labels_len; total += slen) {
303 vp = fr_pair_afrom_da(ctx, parent);
304 if (!vp) return PAIR_DECODE_OOM;
305
306 /*
307 * Having verified the input above, this next
308 * function should never fail unless there's a
309 * bug in the code.
310 */
311 slen = fr_dns_label_to_value_box(vp, &vp->data, data, labels_len, data + total, true, lb);
312 if (slen <= 0) {
313 FR_PROTO_TRACE("ERROR - failed decoding DNS label at with %zu error %zd", total, slen);
315 fr_pair_list_free(&tmp);
316 goto raw;
317 }
318
319 fr_pair_append(&tmp, vp);
320 }
321
323 return labels_len;
324}
325
326/** Generic decode value.
327 *
328 */
330 uint8_t const *data, size_t const data_len, UNUSED void *decode_ctx)
331{
332 fr_pair_t *vp;
333 ssize_t slen;
334
335 if (!fr_type_is_leaf(parent->type)) {
336 FR_PROTO_TRACE("Cannot use generic decoder for data type %s", fr_type_to_str(parent->type));
337 fr_strerror_printf("Cannot decode data type %s", fr_type_to_str(parent->type));
338 return -1;
339 }
340
341 vp = fr_pair_afrom_da(ctx, parent);
342 if (!vp) return PAIR_DECODE_OOM;
343
344 /*
345 * No protocol-specific data types here.
346 *
347 * If we can't decode this field, then the entire
348 * structure is treated as a raw blob.
349 */
350 slen = fr_value_box_from_network(vp, &vp->data, vp->vp_type, vp->da,
351 &FR_DBUFF_TMP(data, data_len), data_len, true);
352 if (slen <= 0) {
353 FR_PROTO_TRACE("failed decoding child VP %s", vp->da->name);
355 return fr_pair_raw_from_network(ctx, out, parent, data, data_len);
356 }
357
359
360 return slen;
361}
#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:514
#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
fr_dict_attr_t * fr_dict_attr_unknown_raw_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da))
Initialise an octets type attribute from a da.
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:238
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:329
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_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:1345
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:287
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:841
#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:1291
static size_t char ** out
Definition value.h:1023