/src/freeradius-server/src/protocols/dhcpv4/decode.c
Line | Count | Source |
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: b1ea167299a685e0a27776663cbedc1f91b58187 $ |
19 | | * |
20 | | * @file protocols/dhcpv4/decode.c |
21 | | * @brief Functions to decode DHCP options. |
22 | | * |
23 | | * @copyright 2008,2017 The FreeRADIUS server project |
24 | | * @copyright 2008 Alan DeKok (aland@deployingradius.com) |
25 | | * @copyright 2015,2017 Arran Cudbard-Bell (a.cudbardb@freeradius.org) |
26 | | */ |
27 | | #include <freeradius-devel/io/test_point.h> |
28 | | #include <freeradius-devel/util/proto.h> |
29 | | #include <freeradius-devel/util/struct.h> |
30 | | #include <freeradius-devel/util/dns.h> |
31 | | |
32 | | #include "dhcpv4.h" |
33 | | #include "attrs.h" |
34 | | |
35 | | static _Thread_local uint8_t concat_buffer[1500]; /* ethernet max */ |
36 | | |
37 | | static ssize_t decode_option(TALLOC_CTX *ctx, fr_pair_list_t *out, |
38 | | fr_dict_attr_t const *parent, |
39 | | uint8_t const *data, size_t const data_len, void *decode_ctx); |
40 | | |
41 | | static bool verify_tlvs(uint8_t const *data, size_t data_len) |
42 | 7.75k | { |
43 | 7.75k | uint8_t const *p = data; |
44 | 7.75k | uint8_t const *end = data + data_len; |
45 | | |
46 | 18.6k | while (p < end) { |
47 | 13.2k | if ((end - p) < 2) return false; |
48 | | |
49 | 11.9k | if ((p + p[1]) > end) return false; |
50 | | |
51 | 10.8k | p += 2 + p[1]; |
52 | 10.8k | } |
53 | | |
54 | 5.38k | return true; |
55 | 7.75k | } |
56 | | |
57 | | static ssize_t decode_tlv_trampoline(TALLOC_CTX *ctx, fr_pair_list_t *out, |
58 | | fr_dict_attr_t const *parent, |
59 | | uint8_t const *data, size_t const data_len, void *decode_ctx) |
60 | 1.51k | { |
61 | 1.51k | return fr_pair_tlvs_from_network(ctx, out, parent, data, data_len, decode_ctx, decode_option, verify_tlvs, true); |
62 | 1.51k | } |
63 | | |
64 | | static ssize_t decode_value(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, |
65 | | uint8_t const *data, size_t data_len, void *decode_ctx); |
66 | | |
67 | | /** Handle arrays of DNS labels for fr_struct_from_network() |
68 | | * |
69 | | */ |
70 | | static ssize_t decode_value_trampoline(TALLOC_CTX *ctx, fr_pair_list_t *out, |
71 | | fr_dict_attr_t const *parent, |
72 | | uint8_t const *data, size_t const data_len, void *decode_ctx) |
73 | 34.2k | { |
74 | 34.2k | FR_PROTO_TRACE("decode_value_trampoline of %s with %zu bytes", parent->name, data_len); |
75 | | |
76 | | /* |
77 | | * @todo - we might need to limit this to only one DNS label. |
78 | | */ |
79 | 34.2k | if ((parent->type == FR_TYPE_STRING) && fr_dhcpv4_flag_dns_label(parent)) { |
80 | 927 | return fr_pair_dns_labels_from_network(ctx, out, parent, data, data, data_len, NULL, false); |
81 | 927 | } |
82 | | |
83 | 33.3k | return decode_value(ctx, out, parent, data, data_len, decode_ctx); |
84 | 34.2k | } |
85 | | |
86 | | /* |
87 | | * Decode ONE value into a VP |
88 | | */ |
89 | | static ssize_t decode_value(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *da, |
90 | | uint8_t const *data, size_t data_len, void *decode_ctx) |
91 | 94.9k | { |
92 | 94.9k | ssize_t slen; |
93 | 94.9k | fr_pair_t *vp; |
94 | 94.9k | uint8_t const *p = data; |
95 | 94.9k | uint8_t const *end = data + data_len; |
96 | 94.9k | bool exact = !da->flags.array; |
97 | | |
98 | 94.9k | FR_PROTO_TRACE("%s called to parse %zu bytes from %s", __FUNCTION__, data_len, da->name); |
99 | 94.9k | FR_PROTO_HEX_DUMP(data, data_len, NULL); |
100 | | |
101 | | /* |
102 | | * Structs create their own VP wrapper. |
103 | | */ |
104 | 94.9k | if (da->type == FR_TYPE_STRUCT) { |
105 | 23.5k | slen = fr_struct_from_network(ctx, out, da, data, data_len, |
106 | 23.5k | decode_ctx, decode_value_trampoline, decode_tlv_trampoline); |
107 | 23.5k | if (slen < 0) return slen; |
108 | | |
109 | 15.5k | if (!exact) return slen; |
110 | | |
111 | 7.32k | return data_len; |
112 | 15.5k | } |
113 | | |
114 | | /* |
115 | | * These are always raw. |
116 | | */ |
117 | 71.4k | if (da->flags.is_unknown) { |
118 | 0 | return fr_pair_raw_from_network(ctx, out, da, data, data_len); |
119 | 0 | } |
120 | | |
121 | 71.4k | vp = fr_pair_afrom_da(ctx, da); |
122 | 71.4k | if (!vp) return PAIR_DECODE_OOM; |
123 | 71.4k | PAIR_ALLOCED(vp); |
124 | | |
125 | | /* |
126 | | * string / octets / bool can be empty. Other data types are |
127 | | * raw if they're empty. |
128 | | */ |
129 | 71.4k | if (data_len == 0) { |
130 | 8.50k | if (da->type == FR_TYPE_BOOL) { |
131 | 737 | vp->vp_bool = true; |
132 | 737 | goto finish; |
133 | 737 | } |
134 | | |
135 | 7.77k | if ((da->type == FR_TYPE_OCTETS) || (da->type == FR_TYPE_STRING)) { |
136 | 3.29k | goto finish; |
137 | 3.29k | } |
138 | | |
139 | 7.77k | talloc_free(vp); |
140 | 4.47k | return fr_pair_raw_from_network(ctx, out, da, data, 0); |
141 | 7.77k | } |
142 | | |
143 | 62.9k | switch (vp->vp_type) { |
144 | 10.6k | case FR_TYPE_ATTR: |
145 | | /* |
146 | | * Force the length of the data to be one, |
147 | | * otherwise the "from network" call complains. |
148 | | * Because we pass in the enumv as the _parent_ |
149 | | * and not the da. The da is marked as "array", |
150 | | * but the parent is not. |
151 | | */ |
152 | 10.6k | end = p + 1; |
153 | | |
154 | 10.6k | fr_assert(da->parent->flags.is_root); |
155 | | |
156 | 10.6k | slen = fr_value_box_from_network(vp, &vp->data, vp->vp_type, da->parent, |
157 | 10.6k | &FR_DBUFF_TMP(p, end - p), end - p, true); |
158 | 10.6k | if (slen <= 0) goto raw; |
159 | | |
160 | 10.6k | p++; |
161 | 10.6k | break; |
162 | | |
163 | | /* |
164 | | * Doesn't include scope, whereas the generic format can. |
165 | | */ |
166 | 1.60k | case FR_TYPE_IPV6_ADDR: |
167 | 1.60k | slen = fr_value_box_ipaddr_from_network(&vp->data, da->type, da, |
168 | 1.60k | 128, p, (size_t) (end - p), |
169 | 1.60k | exact, true); |
170 | 1.60k | if (slen < 0) goto raw; |
171 | 744 | fr_assert(slen == sizeof(vp->vp_ipv6addr)); |
172 | | |
173 | 744 | p += sizeof(vp->vp_ipv6addr); |
174 | 744 | break; |
175 | | |
176 | 0 | case FR_TYPE_IPV6_PREFIX: |
177 | | /* |
178 | | * Not enough room for the prefix length, that's an issue. |
179 | | * |
180 | | * Note that there's actually no standard for IPv6 prefixes inside of DHCPv4. |
181 | | */ |
182 | 0 | if ((end - p) < 1) goto raw; |
183 | | |
184 | 0 | slen = fr_value_box_ipaddr_from_network(&vp->data, da->type, da, |
185 | 0 | p[0], p + 1, ((size_t) (end - p)) - 1, |
186 | 0 | exact, true); |
187 | 0 | if (slen < 0) goto raw; |
188 | | |
189 | 0 | p += slen + 1; |
190 | 0 | break; |
191 | | |
192 | 0 | case FR_TYPE_STRUCTURAL: |
193 | 0 | fr_strerror_printf("Cannot decode type '%s' as value", fr_type_to_str(vp->vp_type)); |
194 | 0 | talloc_free(vp); |
195 | 0 | return 0; |
196 | | |
197 | 8.01k | case FR_TYPE_IPV4_PREFIX: |
198 | 8.01k | fr_value_box_init(&vp->data, FR_TYPE_IPV4_PREFIX, vp->da, true); |
199 | 8.01k | vp->vp_ip.af = AF_INET; |
200 | | |
201 | | /* |
202 | | * 4 octets of address |
203 | | * 4 octets of mask |
204 | | */ |
205 | 8.01k | if (fr_dhcpv4_flag_prefix_split(da)) { |
206 | 2.55k | uint32_t ipaddr, mask; |
207 | | |
208 | 2.55k | if (data_len < 8) goto raw; |
209 | | |
210 | 1.54k | ipaddr = fr_nbo_to_uint32(p); |
211 | 1.54k | mask = fr_nbo_to_uint32(p + 4); |
212 | 1.54k | p += 8; |
213 | | |
214 | | /* |
215 | | * 0/0 means a prefix of 0, too. |
216 | | */ |
217 | 1.54k | if (!mask) { |
218 | 287 | break; |
219 | 287 | } |
220 | | |
221 | | /* |
222 | | * Try to figure out the prefix value from the mask. |
223 | | */ |
224 | 37.1k | while (mask) { |
225 | 35.8k | vp->vp_ip.prefix++; |
226 | 35.8k | mask <<= 1; |
227 | 35.8k | } |
228 | | |
229 | | /* |
230 | | * Mash the IP based on the calculated mask. We don't really care if the mask |
231 | | * has holes, or if the IP address overlaps with the mask. We just fix it all up |
232 | | * so it's sane. |
233 | | */ |
234 | 1.25k | mask = ~(uint32_t) 0; |
235 | 1.25k | mask <<= (32 - vp->vp_ip.prefix); |
236 | | |
237 | 1.25k | vp->vp_ipv4addr = htonl(ipaddr & mask); |
238 | 1.25k | break; |
239 | 1.54k | } |
240 | | |
241 | 5.45k | if (fr_dhcpv4_flag_prefix_bits(vp->da)) { |
242 | 5.45k | size_t needs; |
243 | | |
244 | 5.45k | if ((data_len == 0) || (*p > 32)) goto raw; |
245 | | |
246 | 4.54k | needs = 1 + ((*p + 0x07) >> 3); |
247 | 4.54k | if (data_len < needs) goto raw; |
248 | | |
249 | | /* |
250 | | * Don't do exact checks here, as the content is variable-sized. |
251 | | */ |
252 | | |
253 | 3.62k | vp->vp_ip.prefix = *p; |
254 | | |
255 | | /* |
256 | | * If the IP address is longer than necessary, then only grab the pieces we need. |
257 | | */ |
258 | 3.62k | if (vp->vp_ip.prefix) { |
259 | 2.15k | uint32_t ipaddr, mask; |
260 | | |
261 | 2.15k | mask = ~(uint32_t) 0; |
262 | 2.15k | mask <<= (32 - vp->vp_ip.prefix); |
263 | | |
264 | 2.15k | if (*p > 24) { |
265 | 353 | ipaddr = fr_nbo_to_uint32(p + 1); |
266 | | |
267 | 1.79k | } else if (*p > 16) { |
268 | 416 | ipaddr = fr_nbo_to_uint24(p + 1); |
269 | 416 | ipaddr <<= 8; |
270 | | |
271 | 1.38k | } else if (*p > 8) { |
272 | 367 | ipaddr = fr_nbo_to_uint16(p + 1); |
273 | 367 | ipaddr <<= 16; |
274 | | |
275 | 1.01k | } else { /* 1..8 */ |
276 | 1.01k | ipaddr = p[1]; |
277 | 1.01k | ipaddr <<= 24; |
278 | 1.01k | } |
279 | | |
280 | 2.15k | vp->vp_ipv4addr = htonl(ipaddr & mask); |
281 | 2.15k | } /* else *p==0, and we leave ipaddr set to zero */ |
282 | | |
283 | 3.62k | p += needs; |
284 | 3.62k | break; |
285 | 4.54k | } |
286 | | |
287 | 0 | FALL_THROUGH; |
288 | |
|
289 | 42.6k | default: |
290 | 42.6k | slen = fr_value_box_from_network(vp, &vp->data, vp->vp_type, da, |
291 | 42.6k | &FR_DBUFF_TMP(p, end - p), end - p, true); |
292 | 42.6k | if (slen < 0) { |
293 | 14.8k | raw: |
294 | 14.8k | FR_PROTO_TRACE("decoding as unknown type"); |
295 | 14.8k | if (fr_pair_raw_afrom_pair(vp, p, (end - p)) < 0) { |
296 | 0 | return -1; |
297 | 0 | } |
298 | 14.8k | p = end; |
299 | 14.8k | break; |
300 | 14.8k | } |
301 | | |
302 | 31.6k | if (exact && (slen != (end - p))) { |
303 | 98 | goto raw; |
304 | 98 | } |
305 | | |
306 | 31.5k | p += (size_t) slen; |
307 | 31.5k | break; |
308 | 62.9k | } |
309 | | |
310 | 66.9k | finish: |
311 | 66.9k | FR_PROTO_TRACE("decoding value complete, adding new pair and returning %zu byte(s)", (size_t) (p - data)); |
312 | 66.9k | fr_pair_append(out, vp); |
313 | | |
314 | 66.9k | return p - data; |
315 | 62.9k | } |
316 | | |
317 | | /** RFC 4243 Vendor Specific Suboptions |
318 | | * |
319 | | * Vendor specific suboptions are in the format. |
320 | | @verbatim |
321 | | 0 1 2 3 |
322 | | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
323 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
324 | | | Enterprise Number 0 | |
325 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
326 | | | Len 0 | / |
327 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
328 | | / Suboption Data 0 / |
329 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
330 | | | Enterprise Number n | |
331 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
332 | | | Len n | / |
333 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
334 | | / Suboption Data n / |
335 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
336 | | @endverbatim |
337 | | * |
338 | | * So although the vendor is identified, the format of the data isn't |
339 | | * specified so we can't actually resolve the suboption to an |
340 | | * attribute. For now, we just convert it to an attribute of |
341 | | * Vendor-Specific-Information with raw octets contents. |
342 | | */ |
343 | | |
344 | | |
345 | | /* |
346 | | * One VSA option may contain multiple vendors, each vendor |
347 | | * may contain one or more sub-options. |
348 | | * |
349 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
350 | | * | option-code | option-len | |
351 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
352 | | * | enterprise-number1 | |
353 | | * | | |
354 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
355 | | * | data-len1 | | |
356 | | * +-+-+-+-+-+-+-+-+ option-data1 | |
357 | | * / / |
358 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ---- |
359 | | * | enterprise-number2 | ^ |
360 | | * | | | |
361 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
362 | | * | data-len2 | | optional |
363 | | * +-+-+-+-+-+-+-+-+ option-data2 | | |
364 | | * / / | |
365 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
366 | | * ~ ... ~ V |
367 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ---- |
368 | | */ |
369 | | static ssize_t decode_vsa(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, |
370 | | uint8_t const *data, size_t const data_len, void *decode_ctx) |
371 | 7.56k | { |
372 | 7.56k | ssize_t len; |
373 | 7.56k | uint8_t option_len; |
374 | 7.56k | uint32_t pen; |
375 | 7.56k | fr_pair_t *vp; |
376 | 7.56k | fr_dict_attr_t const *vendor; |
377 | 7.56k | uint8_t const *end = data + data_len; |
378 | 7.56k | uint8_t const *p = data; |
379 | | |
380 | 7.56k | FR_PROTO_HEX_DUMP(data, data_len, "decode_vsa"); |
381 | | |
382 | 7.56k | if (!fr_cond_assert_msg(parent->type == FR_TYPE_VSA, |
383 | 7.56k | "%s: Internal sanity check failed, attribute \"%s\" is not of type 'vsa'", |
384 | 7.56k | __FUNCTION__, parent->name)) return PAIR_DECODE_FATAL_ERROR; |
385 | | |
386 | 9.54k | next: |
387 | | /* |
388 | | * We need at least 4 (PEN) + 1 (data-len) + 1 (vendor option num) to be able to decode vendor |
389 | | * specific attributes. If we don't have that, then we return an error. The caller will free |
390 | | * the VSA, and create a "raw.VSA" attribute. |
391 | | */ |
392 | 9.54k | if ((size_t)(end - p) < (sizeof(uint32_t) + 1 + 1)) { |
393 | 5.09k | return -1; |
394 | 5.09k | } |
395 | | |
396 | 4.45k | pen = fr_nbo_to_uint32(p); |
397 | | |
398 | | /* |
399 | | * Verify that the parent (which should be a VSA) |
400 | | * contains a fake attribute representing the vendor. |
401 | | * |
402 | | * If it doesn't then this vendor is unknown, but we know |
403 | | * vendor attributes have a standard format, so we can |
404 | | * decode the data anyway. |
405 | | */ |
406 | 4.45k | vendor = fr_dict_attr_child_by_num(parent, pen); |
407 | 4.45k | if (!vendor) { |
408 | 3.66k | fr_dict_attr_t *n; |
409 | | |
410 | 3.66k | n = fr_dict_attr_unknown_vendor_afrom_num(ctx, parent, pen); |
411 | 3.66k | if (!n) return PAIR_DECODE_OOM; |
412 | 3.66k | vendor = n; |
413 | 3.66k | } |
414 | 4.45k | p += sizeof(uint32_t); |
415 | | |
416 | 4.45k | FR_PROTO_TRACE("decode context %s -> %s", parent->name, vendor->name); |
417 | | |
418 | 4.45k | option_len = p[0]; |
419 | 4.45k | if ((p + 1 + option_len) > end) { |
420 | 969 | len = fr_pair_raw_from_network(ctx, out, vendor, p, end - p); |
421 | 969 | if (len < 0) return len; |
422 | | |
423 | 969 | return data_len + 2; /* decoded the whole thing */ |
424 | 969 | } |
425 | 3.48k | p++; |
426 | | |
427 | | /* |
428 | | * Pathological case of no data. |
429 | | */ |
430 | 3.48k | if (option_len == 0) goto next; |
431 | | |
432 | 2.40k | vp = fr_pair_find_by_da(out, NULL, vendor); |
433 | 2.40k | if (!vp) { |
434 | 2.00k | vp = fr_pair_afrom_da(ctx, vendor); |
435 | 2.00k | if (!vp) return PAIR_DECODE_FATAL_ERROR; |
436 | 2.00k | PAIR_ALLOCED(vp); |
437 | | |
438 | 2.00k | fr_pair_append(out, vp); |
439 | 2.00k | } |
440 | | |
441 | 2.40k | len = fr_pair_tlvs_from_network(vp, &vp->vp_group, vendor, p, option_len, decode_ctx, decode_option, verify_tlvs, false); |
442 | 2.40k | if (len < 0) return len; |
443 | | |
444 | 1.86k | p += option_len; |
445 | 1.86k | if (p < end) goto next; |
446 | | |
447 | | /* |
448 | | * Tell the caller we read all of it, even if we didn't. |
449 | | */ |
450 | 952 | return data_len + 2; |
451 | 1.86k | } |
452 | | |
453 | | |
454 | | static ssize_t decode_option(TALLOC_CTX *ctx, fr_pair_list_t *out, |
455 | | fr_dict_attr_t const *parent, |
456 | | uint8_t const *data, size_t const data_len, void *decode_ctx) |
457 | 65.6k | { |
458 | 65.6k | unsigned int option; |
459 | 65.6k | size_t len; |
460 | 65.6k | ssize_t slen; |
461 | 65.6k | fr_dict_attr_t const *da; |
462 | 65.6k | fr_dhcpv4_ctx_t *packet_ctx = decode_ctx; |
463 | | |
464 | | #ifdef STATIC_ANALYZER |
465 | | if (!packet_ctx || !packet_ctx->tmp_ctx) return PAIR_DECODE_FATAL_ERROR; |
466 | | #endif |
467 | | |
468 | 65.6k | fr_assert(parent != NULL); |
469 | | |
470 | | /* |
471 | | * RFC 3046 is very specific about not allowing termination |
472 | | * with a 255 sub-option. But it's required for decoding |
473 | | * option 43, and vendors will probably screw it up |
474 | | * anyway. |
475 | | * |
476 | | * Similarly, option 0 is sometimes treated as |
477 | | * "end of options". |
478 | | * |
479 | | * @todo - this check is likely correct only when at the |
480 | | * dhcpv4 root, OR inside of option 43. It could be |
481 | | * argued that it's wrong for all other TLVs. |
482 | | */ |
483 | 65.6k | if ((data_len == 1) && ((data[0] == 0) || (data[1] == 255))) return data_len; |
484 | | |
485 | | /* |
486 | | * Must have at least an option header. |
487 | | */ |
488 | 65.6k | if (data_len < 2) { |
489 | 0 | fr_strerror_printf("%s: Insufficient data", __FUNCTION__); |
490 | 0 | return -(data_len); |
491 | 0 | } |
492 | | |
493 | 65.6k | option = data[0]; |
494 | 65.6k | len = data[1]; |
495 | 65.6k | if (len > (data_len - 2)) { |
496 | 760 | fr_strerror_printf("%s: Option overflows input. " |
497 | 760 | "Optional length must be less than %zu bytes, got %zu bytes", |
498 | 760 | __FUNCTION__, data_len - 2, len); |
499 | 760 | return PAIR_DECODE_FATAL_ERROR; |
500 | 760 | } |
501 | | |
502 | 64.8k | da = fr_dict_attr_child_by_num(parent, option); |
503 | 64.8k | if (!da) { |
504 | 6.76k | da = fr_dict_attr_unknown_raw_afrom_num(packet_ctx->tmp_ctx, parent, option); |
505 | 6.76k | if (!da) return PAIR_DECODE_OOM; |
506 | | |
507 | 6.76k | slen = fr_pair_raw_from_network(ctx, out, da, data + 2, len); |
508 | | |
509 | 58.0k | } else if ((da->type == FR_TYPE_STRING) && fr_dhcpv4_flag_dns_label(da)) { |
510 | 7.77k | slen = fr_pair_dns_labels_from_network(ctx, out, da, data + 2, data + 2, len, NULL, true); |
511 | | |
512 | 50.3k | } else if (da->flags.array) { |
513 | 8.38k | slen = fr_pair_array_from_network(ctx, out, da, data + 2, len, decode_ctx, decode_value); |
514 | | |
515 | 41.9k | } else if (da->type == FR_TYPE_VSA) { |
516 | 6.90k | bool append = false; |
517 | 6.90k | fr_pair_t *vp; |
518 | | |
519 | 6.90k | vp = fr_pair_find_by_da(out, NULL, da); |
520 | 6.90k | if (!vp) { |
521 | 4.23k | vp = fr_pair_afrom_da(ctx, da); |
522 | 4.23k | if (!vp) return PAIR_DECODE_FATAL_ERROR; |
523 | 4.23k | PAIR_ALLOCED(vp); |
524 | | |
525 | 4.23k | append = true; |
526 | 4.23k | } |
527 | | |
528 | 6.90k | slen = decode_vsa(vp, &vp->vp_group, da, data + 2, len, decode_ctx); |
529 | 6.90k | if (append) { |
530 | 4.23k | if (slen < 0) { |
531 | 3.74k | TALLOC_FREE(vp); |
532 | 3.74k | } else { |
533 | 487 | fr_pair_append(out, vp); |
534 | 487 | } |
535 | 4.23k | } |
536 | | |
537 | 35.0k | } else if (da->type == FR_TYPE_TLV) { |
538 | 3.77k | slen = fr_pair_tlvs_from_network(ctx, out, da, data + 2, len, decode_ctx, decode_option, verify_tlvs, true); |
539 | | |
540 | 31.2k | } else { |
541 | 31.2k | slen = decode_value(ctx, out, da, data + 2, len, decode_ctx); |
542 | 31.2k | } |
543 | | |
544 | 64.8k | if (slen < 0) { |
545 | 14.2k | slen = fr_pair_raw_from_network(ctx, out, da, data + 2, len); |
546 | 14.2k | if (slen < 0) return slen; |
547 | 14.2k | } |
548 | | |
549 | 64.8k | return len + 2; |
550 | 64.8k | } |
551 | | |
552 | | /** Decode DHCP option |
553 | | * |
554 | | * @param[in] ctx context to alloc new attributes in. |
555 | | * @param[out] out Where to write the decoded options. |
556 | | * @param[in] data to parse. |
557 | | * @param[in] data_len of data to parse. |
558 | | * @param[in] decode_ctx Unused. |
559 | | */ |
560 | | ssize_t fr_dhcpv4_decode_option(TALLOC_CTX *ctx, fr_pair_list_t *out, |
561 | | uint8_t const *data, size_t data_len, void *decode_ctx) |
562 | 71.7k | { |
563 | 71.7k | ssize_t slen; |
564 | 71.7k | uint8_t const *p = data, *end = data + data_len; |
565 | 71.7k | uint8_t const *next; |
566 | 71.7k | fr_dhcpv4_ctx_t *packet_ctx = decode_ctx; |
567 | | |
568 | 71.7k | FR_PROTO_TRACE("%s called to parse %zu byte(s)", __FUNCTION__, data_len); |
569 | | |
570 | 71.7k | if (data_len == 0) return 0; |
571 | | |
572 | 71.7k | FR_PROTO_HEX_DUMP(data, data_len, NULL); |
573 | | |
574 | | /* |
575 | | * Padding / End of options |
576 | | */ |
577 | 71.7k | if (p[0] == 0) { /* 0x00 - Padding option */ |
578 | 8.94k | data_len = 1; /* Walk over any consecutive 0x00 */ |
579 | 8.94k | p++; /* for efficiency */ |
580 | 14.3k | while ((p < end) && (p[0] == 0)) { |
581 | 5.37k | p++; |
582 | 5.37k | data_len ++; |
583 | 5.37k | } |
584 | 8.94k | return data_len; |
585 | 8.94k | } |
586 | 62.7k | if (p[0] == 255) return data_len; /* 0xff - End of options signifier */ |
587 | | |
588 | | /* |
589 | | * Everything else should be real options |
590 | | */ |
591 | 61.4k | if ((data_len < 2) || ((size_t) (data[1] + 2) > data_len)) { |
592 | 2.18k | fr_strerror_printf("%s: Insufficient data", __FUNCTION__); |
593 | 2.18k | return -1; |
594 | 2.18k | } |
595 | | |
596 | | /* |
597 | | * Check for multiple options of the same type, and concatenate their values together. |
598 | | * |
599 | | * RFC 2131 Section 4.1 says: |
600 | | * |
601 | | * The client concatenates the values of multiple |
602 | | * instances of the same option into a single parameter |
603 | | * list for configuration. |
604 | | * |
605 | | * which presumably also means the same for the server on reception. |
606 | | * |
607 | | * We therefore peek ahead, and concatenate the values into a temporary buffer. The buffer is |
608 | | * allocated only if necessary, and is re-used for the entire packet. |
609 | | * |
610 | | * If the options are *not* consecutive, then we don't concatenate them. Too bad for you! |
611 | | * |
612 | | * Note that we don't (yet) do this for TLVs. |
613 | | */ |
614 | 59.2k | next = data + 2 + data[1]; |
615 | 59.2k | if ((data[1] > 0) && (next < end) && (next[0] == data[0])) { |
616 | 3.74k | uint8_t *q; |
617 | 3.74k | fr_dict_attr_t const *da; |
618 | | |
619 | 3.74k | q = concat_buffer; |
620 | | |
621 | 12.6k | for (next = data; next < end; next += 2 + next[1]) { |
622 | 12.3k | if (next >= end) return -1; |
623 | 12.3k | if (next[0] != data[0]) break; |
624 | 9.09k | if ((end - next) < 2) return -1; |
625 | 9.03k | if ((next + 2 + next[1]) > end) return -1; |
626 | | |
627 | 8.88k | if ((size_t) (q + next[1] - concat_buffer) > sizeof(concat_buffer)) return -1; |
628 | | |
629 | 8.88k | memcpy(q, next + 2, next[1]); |
630 | 8.88k | q += next[1]; |
631 | 8.88k | } |
632 | | |
633 | 3.52k | if (q == concat_buffer) return 0; |
634 | | |
635 | 3.52k | da = fr_dict_attr_child_by_num(packet_ctx->root, p[0]); |
636 | 3.52k | if (!da) { |
637 | 455 | da = fr_dict_attr_unknown_raw_afrom_num(packet_ctx->tmp_ctx, packet_ctx->root, p[0]); |
638 | 455 | if (!da) return -1; |
639 | | |
640 | 455 | slen = fr_pair_raw_from_network(ctx, out, da, concat_buffer, q - concat_buffer); |
641 | | |
642 | 3.07k | } else if (da->type == FR_TYPE_VSA) { |
643 | 659 | slen = decode_vsa(ctx, out, da, concat_buffer, q - concat_buffer, packet_ctx); |
644 | | |
645 | 2.41k | } else if (da->type == FR_TYPE_TLV) { |
646 | 567 | slen = fr_pair_tlvs_from_network(ctx, out, da, concat_buffer, q - concat_buffer, |
647 | 567 | packet_ctx, decode_option, verify_tlvs, true); |
648 | | |
649 | 1.84k | } else if (da->flags.array) { |
650 | 855 | slen = fr_pair_array_from_network(ctx, out, da, concat_buffer, q - concat_buffer, packet_ctx, decode_value); |
651 | | |
652 | 989 | } else { |
653 | 989 | slen = decode_value(ctx, out, da, concat_buffer, q - concat_buffer, packet_ctx); |
654 | 989 | } |
655 | 3.52k | if (slen < 0) return slen; |
656 | | |
657 | | /* |
658 | | * The actual amount of data we decoded, including the various headers. |
659 | | */ |
660 | 3.39k | FR_PROTO_TRACE("decoding option complete, %zd decoded, returning %zu byte(s)", slen, (size_t) (next - data)); |
661 | 3.39k | return next - data; |
662 | 3.52k | } |
663 | | |
664 | 55.5k | slen = decode_option(ctx, out, packet_ctx->root, data, data[1] + 2, decode_ctx); |
665 | 55.5k | if (slen < 0) return slen; |
666 | | |
667 | 55.5k | FR_PROTO_TRACE("decoding option complete, %zd decoded, returning %u byte(s)", slen, (unsigned int) data[1] + 2); |
668 | 55.5k | return data[1] + 2; |
669 | 55.5k | } |
670 | | |
671 | | ssize_t fr_dhcpv4_decode_foreign(TALLOC_CTX *ctx, fr_pair_list_t *out, |
672 | | uint8_t const *data, size_t data_len) |
673 | 3.95k | { |
674 | 3.95k | ssize_t slen; |
675 | 3.95k | uint8_t const *attr, *end; |
676 | | |
677 | 3.95k | fr_dhcpv4_ctx_t decode_ctx = { |
678 | 3.95k | .root = fr_dict_root(dict_dhcpv4) |
679 | 3.95k | }; |
680 | | |
681 | 3.95k | fr_assert(dict_dhcpv4 != NULL); |
682 | | |
683 | 3.95k | decode_ctx.tmp_ctx = talloc(ctx, uint8_t); |
684 | | |
685 | 3.95k | attr = data; |
686 | 3.95k | end = data + data_len; |
687 | | |
688 | 33.8k | while (attr < end) { |
689 | 32.0k | slen = fr_dhcpv4_decode_option(ctx, out, attr, (end - attr), &decode_ctx); |
690 | 32.0k | if (slen < 0) { |
691 | 2.15k | talloc_free(decode_ctx.tmp_ctx); |
692 | 2.15k | return slen; |
693 | 2.15k | } |
694 | | |
695 | | /* |
696 | | * If slen is larger than the room in the packet, |
697 | | * all kinds of bad things happen. |
698 | | */ |
699 | 29.8k | if (!fr_cond_assert(slen <= (end - attr))) { |
700 | 0 | talloc_free(decode_ctx.tmp_ctx); |
701 | 0 | return -slen - (attr - data); |
702 | 0 | } |
703 | | |
704 | 29.8k | attr += slen; |
705 | 29.8k | talloc_free_children(decode_ctx.tmp_ctx); |
706 | 29.8k | } |
707 | | |
708 | 3.95k | talloc_free(decode_ctx.tmp_ctx); |
709 | 1.80k | return data_len; |
710 | 3.95k | } |
711 | | |
712 | | |
713 | | static int decode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict, |
714 | | fr_dict_attr_t const *root_da) |
715 | 1.18k | { |
716 | 1.18k | fr_dhcpv4_ctx_t *test_ctx; |
717 | | |
718 | 1.18k | test_ctx = talloc_zero(ctx, fr_dhcpv4_ctx_t); |
719 | 1.18k | test_ctx->tmp_ctx = talloc(test_ctx, uint8_t); |
720 | 1.18k | test_ctx->root = root_da ? root_da : fr_dict_root(dict_dhcpv4); |
721 | | |
722 | 1.18k | *out = test_ctx; |
723 | | |
724 | 1.18k | return 0; |
725 | 1.18k | } |
726 | | |
727 | | |
728 | | static ssize_t fr_dhcpv4_decode_proto(TALLOC_CTX *ctx, fr_pair_list_t *out, |
729 | | uint8_t const *data, size_t data_len, UNUSED void *proto_ctx) |
730 | 1.18k | { |
731 | 1.18k | unsigned int code; |
732 | | |
733 | 1.18k | if (!fr_dhcpv4_ok(data, data_len, NULL, NULL)) return -1; |
734 | | |
735 | 1.03k | if (fr_dhcpv4_decode(ctx, out, data, data_len, &code) < 0) return -1; |
736 | | |
737 | 639 | return data_len; |
738 | 1.03k | } |
739 | | |
740 | | static ssize_t decode_option_wrapper(TALLOC_CTX *ctx, fr_pair_list_t *out, NDEBUG_UNUSED fr_dict_attr_t const *parent, |
741 | | uint8_t const *data, size_t data_len, void *decode_ctx) |
742 | 0 | { |
743 | 0 | fr_assert(parent == fr_dict_root(dict_dhcpv4)); |
744 | |
|
745 | 0 | return fr_dhcpv4_decode_option(ctx, out, data, data_len, decode_ctx); |
746 | 0 | } |
747 | | |
748 | | /* |
749 | | * Test points |
750 | | */ |
751 | | extern fr_test_point_pair_decode_t dhcpv4_tp_decode_pair; |
752 | | fr_test_point_pair_decode_t dhcpv4_tp_decode_pair = { |
753 | | .test_ctx = decode_test_ctx, |
754 | | .func = decode_option_wrapper |
755 | | }; |
756 | | |
757 | | extern fr_test_point_proto_decode_t dhcpv4_tp_decode_proto; |
758 | | fr_test_point_proto_decode_t dhcpv4_tp_decode_proto = { |
759 | | .test_ctx = decode_test_ctx, |
760 | | .func = fr_dhcpv4_decode_proto |
761 | | }; |