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: d2569212023c29091c34f6c57fc825d94616bb63 $
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;
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 */
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_attr_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) {
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);
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);
314 fr_pair_list_free(&tmp);
315 goto raw;
316 }
317
318 fr_pair_append(&tmp, vp);
319 }
320
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: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:156
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: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.
@ 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:283
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:851
#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:1754
static fr_slen_t data
Definition value.h:1265
static size_t char ** out
Definition value.h:997