/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: 968f784d79c62029261742542bda8ca8b50e264b $ |
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 | 4.45k | { |
43 | 4.45k | uint8_t const *p = data; |
44 | 4.45k | uint8_t const *end = data + data_len; |
45 | | |
46 | 8.83k | while (p < end) { |
47 | 6.42k | if ((end - p) < 2) return false; |
48 | | |
49 | 5.18k | if ((p + 2 + p[1]) > end) return false; |
50 | | |
51 | 4.38k | p += 2 + p[1]; |
52 | 4.38k | } |
53 | | |
54 | 2.41k | return true; |
55 | 4.45k | } |
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 | 691 | { |
61 | 691 | return fr_pair_tlvs_from_network(ctx, out, parent, data, data_len, decode_ctx, decode_option, verify_tlvs, true); |
62 | 691 | } |
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 | 16.3k | { |
74 | 16.3k | 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 | 16.3k | if ((parent->type == FR_TYPE_STRING) && fr_dhcpv4_flag_dns_label(parent)) { |
80 | 522 | return fr_pair_dns_labels_from_network(ctx, out, parent, data, data, data_len, NULL, false); |
81 | 522 | } |
82 | | |
83 | 15.8k | return decode_value(ctx, out, parent, data, data_len, decode_ctx); |
84 | 16.3k | } |
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 | 44.8k | { |
92 | 44.8k | ssize_t slen; |
93 | 44.8k | fr_pair_t *vp; |
94 | 44.8k | uint8_t const *p = data; |
95 | 44.8k | uint8_t const *end = data + data_len; |
96 | 44.8k | bool exact = !da->flags.array; |
97 | | |
98 | 44.8k | FR_PROTO_TRACE("%s called to parse %zu bytes from %s", __FUNCTION__, data_len, da->name); |
99 | 44.8k | FR_PROTO_HEX_DUMP(data, data_len, NULL); |
100 | | |
101 | | /* |
102 | | * Structs create their own VP wrapper. |
103 | | */ |
104 | 44.8k | if (da->type == FR_TYPE_STRUCT) { |
105 | 10.8k | slen = fr_struct_from_network(ctx, out, da, data, data_len, |
106 | 10.8k | decode_ctx, decode_value_trampoline, decode_tlv_trampoline); |
107 | 10.8k | if (slen < 0) return slen; |
108 | | |
109 | 6.70k | if (!exact) return slen; |
110 | | |
111 | 2.73k | return data_len; |
112 | 6.70k | } |
113 | | |
114 | | /* |
115 | | * These are always raw. |
116 | | */ |
117 | 33.9k | if (da->flags.is_unknown) { |
118 | 0 | return fr_pair_raw_from_network(ctx, out, da, data, data_len); |
119 | 0 | } |
120 | | |
121 | 33.9k | vp = fr_pair_afrom_da(ctx, da); |
122 | 33.9k | if (!vp) return PAIR_DECODE_OOM; |
123 | 33.9k | PAIR_ALLOCED(vp); |
124 | | |
125 | | /* |
126 | | * string / octets / bool can be empty. Other data types are |
127 | | * raw if they're empty. |
128 | | */ |
129 | 33.9k | if (data_len == 0) { |
130 | 3.51k | if (da->type == FR_TYPE_BOOL) { |
131 | 242 | vp->vp_bool = true; |
132 | 242 | goto finish; |
133 | 242 | } |
134 | | |
135 | 3.27k | if ((da->type == FR_TYPE_OCTETS) || (da->type == FR_TYPE_STRING)) { |
136 | 1.87k | goto finish; |
137 | 1.87k | } |
138 | | |
139 | 3.27k | talloc_free(vp); |
140 | 1.40k | return fr_pair_raw_from_network(ctx, out, da, data, 0); |
141 | 3.27k | } |
142 | | |
143 | 30.4k | switch (vp->vp_type) { |
144 | 7.28k | 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 | 7.28k | end = p + 1; |
153 | | |
154 | 7.28k | fr_assert(da->parent->flags.is_root); |
155 | | |
156 | 7.28k | slen = fr_value_box_from_network(vp, &vp->data, vp->vp_type, da->parent, |
157 | 7.28k | &FR_DBUFF_TMP(p, end - p), end - p, true); |
158 | 7.28k | if (slen <= 0) goto raw; |
159 | | |
160 | 7.28k | p++; |
161 | 7.28k | break; |
162 | | |
163 | | /* |
164 | | * Doesn't include scope, whereas the generic format can. |
165 | | */ |
166 | 789 | case FR_TYPE_IPV6_ADDR: |
167 | 789 | slen = fr_value_box_ipaddr_from_network(&vp->data, da->type, da, |
168 | 789 | 128, p, (size_t) (end - p), |
169 | 789 | exact, true); |
170 | 789 | if (slen < 0) goto raw; |
171 | 365 | fr_assert(slen == sizeof(vp->vp_ipv6addr)); |
172 | | |
173 | 365 | p += sizeof(vp->vp_ipv6addr); |
174 | 365 | 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 | 3.76k | case FR_TYPE_IPV4_PREFIX: |
198 | 3.76k | fr_value_box_init(&vp->data, FR_TYPE_IPV4_PREFIX, vp->da, true); |
199 | 3.76k | vp->vp_ip.af = AF_INET; |
200 | | |
201 | | /* |
202 | | * 4 octets of address |
203 | | * 4 octets of mask |
204 | | */ |
205 | 3.76k | if (fr_dhcpv4_flag_prefix_split(da)) { |
206 | 863 | uint32_t ipaddr, mask; |
207 | | |
208 | 863 | if (data_len < 8) goto raw; |
209 | | |
210 | 384 | ipaddr = fr_nbo_to_uint32(p); |
211 | 384 | mask = fr_nbo_to_uint32(p + 4); |
212 | 384 | p += 8; |
213 | | |
214 | | /* |
215 | | * 0/0 means a prefix of 0, too. |
216 | | */ |
217 | 384 | if (!mask) { |
218 | 130 | break; |
219 | 130 | } |
220 | | |
221 | | /* |
222 | | * Try to figure out the prefix value from the mask. |
223 | | */ |
224 | 7.40k | while (mask) { |
225 | 7.15k | vp->vp_ip.prefix++; |
226 | 7.15k | mask <<= 1; |
227 | 7.15k | } |
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 | 254 | mask = ~(uint32_t) 0; |
235 | 254 | mask <<= (32 - vp->vp_ip.prefix); |
236 | | |
237 | 254 | vp->vp_ipv4addr = htonl(ipaddr & mask); |
238 | 254 | break; |
239 | 384 | } |
240 | | |
241 | 2.90k | if (fr_dhcpv4_flag_prefix_bits(vp->da)) { |
242 | 2.90k | size_t needs; |
243 | | |
244 | 2.90k | if ((data_len == 0) || (*p > 32)) goto raw; |
245 | | |
246 | 2.28k | needs = 1 + ((*p + 0x07) >> 3); |
247 | 2.28k | if (data_len < needs) goto raw; |
248 | | |
249 | | /* |
250 | | * Don't do exact checks here, as the content is variable-sized. |
251 | | */ |
252 | | |
253 | 1.82k | 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 | 1.82k | if (vp->vp_ip.prefix) { |
259 | 1.28k | uint32_t ipaddr, mask; |
260 | | |
261 | 1.28k | mask = ~(uint32_t) 0; |
262 | 1.28k | mask <<= (32 - vp->vp_ip.prefix); |
263 | | |
264 | 1.28k | if (*p > 24) { |
265 | 224 | ipaddr = fr_nbo_to_uint32(p + 1); |
266 | | |
267 | 1.05k | } else if (*p > 16) { |
268 | 229 | ipaddr = fr_nbo_to_uint24(p + 1); |
269 | 229 | ipaddr <<= 8; |
270 | | |
271 | 828 | } else if (*p > 8) { |
272 | 251 | ipaddr = fr_nbo_to_uint16(p + 1); |
273 | 251 | ipaddr <<= 16; |
274 | | |
275 | 577 | } else { /* 1..8 */ |
276 | 577 | ipaddr = p[1]; |
277 | 577 | ipaddr <<= 24; |
278 | 577 | } |
279 | | |
280 | 1.28k | vp->vp_ipv4addr = htonl(ipaddr & mask); |
281 | 1.28k | } /* else *p==0, and we leave ipaddr set to zero */ |
282 | | |
283 | 1.82k | p += needs; |
284 | 1.82k | break; |
285 | 2.28k | } |
286 | | |
287 | 0 | FALL_THROUGH; |
288 | |
|
289 | 18.6k | default: |
290 | 18.6k | slen = fr_value_box_from_network(vp, &vp->data, vp->vp_type, da, |
291 | 18.6k | &FR_DBUFF_TMP(p, end - p), end - p, true); |
292 | 18.6k | if (slen < 0) { |
293 | 6.72k | raw: |
294 | 6.72k | FR_PROTO_TRACE("decoding as unknown type"); |
295 | 6.72k | if (fr_pair_raw_afrom_pair(vp, p, (end - p)) < 0) { |
296 | 0 | return -1; |
297 | 0 | } |
298 | 6.72k | p = end; |
299 | 6.72k | break; |
300 | 6.72k | } |
301 | | |
302 | 13.9k | if (exact && (slen != (end - p))) { |
303 | 74 | goto raw; |
304 | 74 | } |
305 | | |
306 | 13.8k | p += (size_t) slen; |
307 | 13.8k | break; |
308 | 30.4k | } |
309 | | |
310 | 32.5k | finish: |
311 | 32.5k | FR_PROTO_TRACE("decoding value complete, adding new pair and returning %zu byte(s)", (size_t) (p - data)); |
312 | 32.5k | fr_pair_append(out, vp); |
313 | | |
314 | 32.5k | return p - data; |
315 | 30.4k | } |
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 | 4.46k | { |
372 | 4.46k | size_t len; |
373 | 4.46k | ssize_t slen; |
374 | 4.46k | uint8_t option_len; |
375 | 4.46k | uint32_t pen; |
376 | 4.46k | fr_pair_t *vp; |
377 | 4.46k | fr_dict_attr_t const *vendor; |
378 | 4.46k | uint8_t const *end = data + data_len; |
379 | 4.46k | uint8_t const *p = data; |
380 | 4.46k | fr_pair_list_t list; |
381 | | |
382 | 4.46k | FR_PROTO_HEX_DUMP(data, data_len, "decode_vsa"); |
383 | | |
384 | 4.46k | if (!fr_cond_assert_msg(parent->type == FR_TYPE_VSA, |
385 | 4.46k | "%s: Internal sanity check failed, attribute \"%s\" is not of type 'vsa'", |
386 | 4.46k | __FUNCTION__, parent->name)) return PAIR_DECODE_FATAL_ERROR; |
387 | | |
388 | 4.46k | fr_pair_list_init(&list); |
389 | | |
390 | 5.15k | next: |
391 | | /* |
392 | | * RFC 4243 Section 3 says that "the minimum length is 4 bytes." |
393 | | */ |
394 | 5.15k | len = (size_t) (end - p); |
395 | 5.15k | if (len < (sizeof(uint32_t))) { |
396 | 3.06k | fail: |
397 | 3.06k | fr_pair_list_free(&list); |
398 | 3.06k | return -1; |
399 | 2.83k | } |
400 | | |
401 | | /* |
402 | | * RFC 4243 is silent about this, but we assime that anything with no vendor data means that we |
403 | | * ignore it. i.e. we don't create an empty vendor attribute. |
404 | | */ |
405 | 2.32k | if (len == (sizeof(uint32_t) + 1)) { |
406 | | /* |
407 | | * There's no more data, so this length field must be zero. |
408 | | */ |
409 | 3 | if (p[4] != 0) goto fail; |
410 | 0 | goto done; |
411 | 3 | } |
412 | | |
413 | 2.32k | pen = fr_nbo_to_uint32(p); |
414 | | |
415 | | /* |
416 | | * Verify that the parent (which should be a VSA) |
417 | | * contains a fake attribute representing the vendor. |
418 | | * |
419 | | * If it doesn't then this vendor is unknown, but we know |
420 | | * vendor attributes have a standard format, so we can |
421 | | * decode the data anyway. |
422 | | */ |
423 | 2.32k | vendor = fr_dict_attr_child_by_num(parent, pen); |
424 | 2.32k | if (!vendor) { |
425 | 1.85k | fr_dict_attr_t *n; |
426 | | |
427 | 1.85k | n = fr_dict_attr_unknown_vendor_afrom_num(ctx, parent, pen); |
428 | 1.85k | if (!n) { |
429 | 0 | oom: |
430 | 0 | fr_pair_list_free(&list); |
431 | 0 | return PAIR_DECODE_OOM; |
432 | 0 | } |
433 | 1.85k | vendor = n; |
434 | 1.85k | } |
435 | 2.32k | p += sizeof(uint32_t); |
436 | | |
437 | 2.32k | FR_PROTO_TRACE("decode context %s -> %s", parent->name, vendor->name); |
438 | | |
439 | 2.32k | option_len = p[0]; |
440 | 2.32k | if ((p + 1 + option_len) > end) { |
441 | 654 | slen = fr_pair_raw_from_network(ctx, out, vendor, p, end - p); |
442 | 654 | if (slen < 0) goto fail; |
443 | | |
444 | 654 | goto done; |
445 | 654 | } |
446 | 1.66k | p++; |
447 | | |
448 | | /* |
449 | | * Pathological case of no data. |
450 | | */ |
451 | 1.66k | if (option_len == 0) goto next; |
452 | | |
453 | 1.24k | vp = fr_pair_find_by_da(out, NULL, vendor); |
454 | 1.24k | if (!vp) { |
455 | 976 | vp = fr_pair_afrom_da(ctx, vendor); |
456 | 976 | if (!vp) goto oom; |
457 | 976 | PAIR_ALLOCED(vp); |
458 | | |
459 | 976 | fr_pair_append(out, vp); |
460 | 976 | } |
461 | | |
462 | 1.24k | slen = fr_pair_tlvs_from_network(vp, &vp->vp_group, vendor, p, option_len, decode_ctx, decode_option, verify_tlvs, false); |
463 | 1.24k | if (slen < 0) goto fail; |
464 | | |
465 | 1.01k | p += option_len; |
466 | 1.01k | if (p < end) goto next; |
467 | | |
468 | | /* |
469 | | * Tell the caller we read all of it, even if we didn't. |
470 | | */ |
471 | 1.40k | done: |
472 | 1.40k | fr_pair_list_append(out, &list); |
473 | 1.40k | return data_len; |
474 | 1.01k | } |
475 | | |
476 | | |
477 | | static ssize_t decode_option(TALLOC_CTX *ctx, fr_pair_list_t *out, |
478 | | fr_dict_attr_t const *parent, |
479 | | uint8_t const *data, size_t const data_len, void *decode_ctx) |
480 | 28.9k | { |
481 | 28.9k | unsigned int option; |
482 | 28.9k | size_t len; |
483 | 28.9k | ssize_t slen; |
484 | 28.9k | fr_dict_attr_t const *da; |
485 | 28.9k | fr_dhcpv4_ctx_t *packet_ctx = decode_ctx; |
486 | | |
487 | | #ifdef STATIC_ANALYZER |
488 | | if (!packet_ctx || !packet_ctx->tmp_ctx) return PAIR_DECODE_FATAL_ERROR; |
489 | | #endif |
490 | | |
491 | 28.9k | fr_assert(parent != NULL); |
492 | | |
493 | | /* |
494 | | * RFC 3046 is very specific about not allowing termination |
495 | | * with a 255 sub-option. But it's required for decoding |
496 | | * option 43, and vendors will probably screw it up |
497 | | * anyway. |
498 | | * |
499 | | * Similarly, option 0 is sometimes treated as |
500 | | * "end of options". |
501 | | * |
502 | | * @todo - this check is likely correct only when at the |
503 | | * dhcpv4 root, OR inside of option 43. It could be |
504 | | * argued that it's wrong for all other TLVs. |
505 | | */ |
506 | 28.9k | if ((data_len == 1) && ((data[0] == 0) || (data[0] == 255))) return data_len; |
507 | | |
508 | | /* |
509 | | * Must have at least an option header. |
510 | | */ |
511 | 28.9k | if (data_len < 2) { |
512 | 0 | fr_strerror_printf("%s: Insufficient data", __FUNCTION__); |
513 | 0 | return -(data_len); |
514 | 0 | } |
515 | | |
516 | 28.9k | option = data[0]; |
517 | 28.9k | len = data[1]; |
518 | 28.9k | if (len > (data_len - 2)) { |
519 | 0 | fr_strerror_printf("%s: Option overflows input. " |
520 | 0 | "Optional length must be less than %zu bytes, got %zu bytes", |
521 | 0 | __FUNCTION__, data_len - 2, len); |
522 | 0 | return PAIR_DECODE_FATAL_ERROR; |
523 | 0 | } |
524 | | |
525 | 28.9k | da = fr_dict_attr_child_by_num(parent, option); |
526 | 28.9k | if (!da) { |
527 | 2.86k | da = fr_dict_attr_unknown_raw_afrom_num(packet_ctx->tmp_ctx, parent, option); |
528 | 2.86k | if (!da) return PAIR_DECODE_OOM; |
529 | | |
530 | 2.86k | slen = fr_pair_raw_from_network(ctx, out, da, data + 2, len); |
531 | | |
532 | 26.1k | } else if ((da->type == FR_TYPE_STRING) && fr_dhcpv4_flag_dns_label(da)) { |
533 | 2.88k | slen = fr_pair_dns_labels_from_network(ctx, out, da, data + 2, data + 2, len, NULL, true); |
534 | | |
535 | 23.2k | } else if (da->flags.array) { |
536 | 4.27k | slen = fr_pair_array_from_network(ctx, out, da, data + 2, len, decode_ctx, decode_value); |
537 | | |
538 | 18.9k | } else if (da->type == FR_TYPE_VSA) { |
539 | 3.92k | bool append = false; |
540 | 3.92k | fr_pair_t *vp; |
541 | | |
542 | 3.92k | vp = fr_pair_find_by_da(out, NULL, da); |
543 | 3.92k | if (!vp) { |
544 | 2.12k | vp = fr_pair_afrom_da(ctx, da); |
545 | 2.12k | if (!vp) return PAIR_DECODE_FATAL_ERROR; |
546 | 2.12k | PAIR_ALLOCED(vp); |
547 | | |
548 | 2.12k | append = true; |
549 | 2.12k | } |
550 | | |
551 | 3.92k | slen = decode_vsa(vp, &vp->vp_group, da, data + 2, len, decode_ctx); |
552 | 3.92k | if (append) { |
553 | 2.12k | if (slen < 0) { |
554 | 1.80k | TALLOC_FREE(vp); |
555 | 1.80k | } else { |
556 | 323 | fr_pair_append(out, vp); |
557 | 323 | } |
558 | 2.12k | } |
559 | | |
560 | 15.0k | } else if (da->type == FR_TYPE_TLV) { |
561 | 2.38k | slen = fr_pair_tlvs_from_network(ctx, out, da, data + 2, len, decode_ctx, decode_option, verify_tlvs, true); |
562 | | |
563 | 12.6k | } else { |
564 | 12.6k | slen = decode_value(ctx, out, da, data + 2, len, decode_ctx); |
565 | 12.6k | } |
566 | | |
567 | 28.9k | if (slen < 0) { |
568 | 7.72k | slen = fr_pair_raw_from_network(ctx, out, da, data + 2, len); |
569 | 7.72k | if (slen < 0) return slen; |
570 | 7.72k | } |
571 | | |
572 | 28.9k | return len + 2; |
573 | 28.9k | } |
574 | | |
575 | | /** Decode DHCP option |
576 | | * |
577 | | * @param[in] ctx context to alloc new attributes in. |
578 | | * @param[out] out Where to write the decoded options. |
579 | | * @param[in] data to parse. |
580 | | * @param[in] data_len of data to parse. |
581 | | * @param[in] decode_ctx Unused. |
582 | | */ |
583 | | ssize_t fr_dhcpv4_decode_option(TALLOC_CTX *ctx, fr_pair_list_t *out, |
584 | | uint8_t const *data, size_t data_len, void *decode_ctx) |
585 | 33.3k | { |
586 | 33.3k | ssize_t slen; |
587 | 33.3k | uint8_t const *p = data, *end = data + data_len; |
588 | 33.3k | uint8_t const *next; |
589 | 33.3k | fr_dhcpv4_ctx_t *packet_ctx = decode_ctx; |
590 | | |
591 | 33.3k | FR_PROTO_TRACE("%s called to parse %zu byte(s)", __FUNCTION__, data_len); |
592 | | |
593 | 33.3k | if (data_len == 0) return 0; |
594 | | |
595 | 33.3k | FR_PROTO_HEX_DUMP(data, data_len, NULL); |
596 | | |
597 | | /* |
598 | | * Padding / End of options |
599 | | */ |
600 | 33.3k | if (p[0] == 0) { /* 0x00 - Padding option */ |
601 | 3.01k | data_len = 1; /* Walk over any consecutive 0x00 */ |
602 | 3.01k | p++; /* for efficiency */ |
603 | 4.83k | while ((p < end) && (p[0] == 0)) { |
604 | 1.82k | p++; |
605 | 1.82k | data_len ++; |
606 | 1.82k | } |
607 | 3.01k | return data_len; |
608 | 3.01k | } |
609 | 30.3k | if (p[0] == 255) return data_len; /* 0xff - End of options signifier */ |
610 | | |
611 | | /* |
612 | | * Everything else should be real options |
613 | | */ |
614 | 30.0k | if ((data_len < 2) || ((size_t) (data[1] + 2) > data_len)) { |
615 | 1.62k | fr_strerror_printf("%s: Insufficient data", __FUNCTION__); |
616 | 1.62k | return -1; |
617 | 1.62k | } |
618 | | |
619 | | /* |
620 | | * Check for multiple options of the same type, and concatenate their values together. |
621 | | * |
622 | | * RFC 2131 Section 4.1 says: |
623 | | * |
624 | | * The client concatenates the values of multiple |
625 | | * instances of the same option into a single parameter |
626 | | * list for configuration. |
627 | | * |
628 | | * which presumably also means the same for the server on reception. |
629 | | * |
630 | | * We therefore peek ahead, and concatenate the values into a temporary buffer. The buffer is |
631 | | * allocated only if necessary, and is re-used for the entire packet. |
632 | | * |
633 | | * If the options are *not* consecutive, then we don't concatenate them. Too bad for you! |
634 | | * |
635 | | * Note that we don't (yet) do this for TLVs. |
636 | | */ |
637 | 28.4k | next = data + 2 + data[1]; |
638 | 28.4k | if ((data[1] > 0) && (next < end) && (next[0] == data[0])) { |
639 | 2.47k | uint8_t *q; |
640 | 2.47k | fr_dict_attr_t const *da; |
641 | | |
642 | 2.47k | q = concat_buffer; |
643 | | |
644 | 7.89k | for (next = data; next < end; next += 2 + next[1]) { |
645 | 7.80k | if (next[0] != data[0]) break; |
646 | 5.57k | if ((end - next) < 2) return -1; |
647 | 5.53k | if ((next + 2 + next[1]) > end) return -1; |
648 | | |
649 | 5.41k | if ((size_t) (q + next[1] - concat_buffer) > sizeof(concat_buffer)) return -1; |
650 | | |
651 | 5.41k | memcpy(q, next + 2, next[1]); |
652 | 5.41k | q += next[1]; |
653 | 5.41k | } |
654 | | |
655 | 2.31k | da = fr_dict_attr_child_by_num(packet_ctx->root, p[0]); |
656 | 2.31k | if (!da) { |
657 | 315 | da = fr_dict_attr_unknown_raw_afrom_num(packet_ctx->tmp_ctx, packet_ctx->root, p[0]); |
658 | 315 | if (!da) return -1; |
659 | | |
660 | 315 | slen = fr_pair_raw_from_network(ctx, out, da, concat_buffer, q - concat_buffer); |
661 | | |
662 | 2.00k | } else if (da->type == FR_TYPE_VSA) { |
663 | 541 | slen = decode_vsa(ctx, out, da, concat_buffer, q - concat_buffer, packet_ctx); |
664 | | |
665 | 1.46k | } else if (da->type == FR_TYPE_TLV) { |
666 | 436 | slen = fr_pair_tlvs_from_network(ctx, out, da, concat_buffer, q - concat_buffer, |
667 | 436 | packet_ctx, decode_option, verify_tlvs, true); |
668 | | |
669 | 1.02k | } else if (da->flags.array) { |
670 | 550 | slen = fr_pair_array_from_network(ctx, out, da, concat_buffer, q - concat_buffer, packet_ctx, decode_value); |
671 | 550 | } else if ((da->type == FR_TYPE_STRING) && fr_dhcpv4_flag_dns_label(da)) { |
672 | 0 | slen = fr_pair_dns_labels_from_network(ctx, out, da, concat_buffer, concat_buffer, |
673 | 0 | q - concat_buffer, NULL, true); |
674 | |
|
675 | 475 | } else { |
676 | 475 | slen = decode_value(ctx, out, da, concat_buffer, q - concat_buffer, packet_ctx); |
677 | 475 | } |
678 | 2.31k | if (slen < 0) return slen; |
679 | | |
680 | | /* |
681 | | * The actual amount of data we decoded, including the various headers. |
682 | | */ |
683 | 2.20k | FR_PROTO_TRACE("decoding option complete, %zd decoded, returning %zu byte(s)", slen, (size_t) (next - data)); |
684 | 2.20k | return next - data; |
685 | 2.31k | } |
686 | | |
687 | 25.9k | slen = decode_option(ctx, out, packet_ctx->root, data, data[1] + 2, decode_ctx); |
688 | 25.9k | if (slen < 0) return slen; |
689 | | |
690 | 25.9k | FR_PROTO_TRACE("decoding option complete, %zd decoded, returning %u byte(s)", slen, (unsigned int) data[1] + 2); |
691 | 25.9k | return data[1] + 2; |
692 | 25.9k | } |
693 | | |
694 | | ssize_t fr_dhcpv4_decode_foreign(TALLOC_CTX *ctx, fr_pair_list_t *out, |
695 | | uint8_t const *data, size_t data_len) |
696 | 2.75k | { |
697 | 2.75k | ssize_t slen; |
698 | 2.75k | uint8_t const *attr, *end; |
699 | | |
700 | 2.75k | fr_dhcpv4_ctx_t decode_ctx = { |
701 | 2.75k | .root = fr_dict_root(dict_dhcpv4) |
702 | 2.75k | }; |
703 | | |
704 | 2.75k | fr_assert(dict_dhcpv4 != NULL); |
705 | | |
706 | 2.75k | decode_ctx.tmp_ctx = talloc(ctx, uint8_t); |
707 | | |
708 | 2.75k | attr = data; |
709 | 2.75k | end = data + data_len; |
710 | | |
711 | 27.8k | while (attr < end) { |
712 | 26.9k | slen = fr_dhcpv4_decode_option(ctx, out, attr, (end - attr), &decode_ctx); |
713 | 26.9k | if (slen < 0) { |
714 | 1.82k | talloc_free(decode_ctx.tmp_ctx); |
715 | 1.82k | return slen; |
716 | 1.82k | } |
717 | | |
718 | | /* |
719 | | * If slen is larger than the room in the packet, |
720 | | * all kinds of bad things happen. |
721 | | */ |
722 | 25.1k | if (!fr_cond_assert(slen <= (end - attr))) { |
723 | 0 | talloc_free(decode_ctx.tmp_ctx); |
724 | 0 | return -slen - (attr - data); |
725 | 0 | } |
726 | | |
727 | 25.1k | attr += slen; |
728 | 25.1k | talloc_free_children(decode_ctx.tmp_ctx); |
729 | 25.1k | } |
730 | | |
731 | 2.75k | talloc_free(decode_ctx.tmp_ctx); |
732 | 927 | return data_len; |
733 | 2.75k | } |
734 | | |
735 | | |
736 | | static int decode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict, |
737 | | fr_dict_attr_t const *root_da) |
738 | 1.23k | { |
739 | 1.23k | fr_dhcpv4_ctx_t *test_ctx; |
740 | | |
741 | 1.23k | test_ctx = talloc_zero(ctx, fr_dhcpv4_ctx_t); |
742 | 1.23k | test_ctx->tmp_ctx = talloc(test_ctx, uint8_t); |
743 | 1.23k | test_ctx->root = root_da ? root_da : fr_dict_root(dict_dhcpv4); |
744 | | |
745 | 1.23k | *out = test_ctx; |
746 | | |
747 | 1.23k | return 0; |
748 | 1.23k | } |
749 | | |
750 | | |
751 | | static ssize_t fr_dhcpv4_decode_proto(TALLOC_CTX *ctx, fr_pair_list_t *out, |
752 | | uint8_t const *data, size_t data_len, UNUSED void *proto_ctx) |
753 | 1.23k | { |
754 | 1.23k | unsigned int code; |
755 | | |
756 | 1.23k | if (!fr_dhcpv4_ok(data, data_len, NULL, NULL)) return -1; |
757 | | |
758 | 200 | if (fr_dhcpv4_decode(ctx, out, data, data_len, &code) < 0) return -1; |
759 | | |
760 | 125 | return data_len; |
761 | 200 | } |
762 | | |
763 | | static ssize_t decode_option_wrapper(TALLOC_CTX *ctx, fr_pair_list_t *out, NDEBUG_UNUSED fr_dict_attr_t const *parent, |
764 | | uint8_t const *data, size_t data_len, void *decode_ctx) |
765 | 0 | { |
766 | 0 | fr_assert(parent == fr_dict_root(dict_dhcpv4)); |
767 | |
|
768 | 0 | return fr_dhcpv4_decode_option(ctx, out, data, data_len, decode_ctx); |
769 | 0 | } |
770 | | |
771 | | /* |
772 | | * Test points |
773 | | */ |
774 | | extern fr_test_point_pair_decode_t dhcpv4_tp_decode_pair; |
775 | | fr_test_point_pair_decode_t dhcpv4_tp_decode_pair = { |
776 | | .test_ctx = decode_test_ctx, |
777 | | .func = decode_option_wrapper |
778 | | }; |
779 | | |
780 | | extern fr_test_point_proto_decode_t dhcpv4_tp_decode_proto; |
781 | | fr_test_point_proto_decode_t dhcpv4_tp_decode_proto = { |
782 | | .test_ctx = decode_test_ctx, |
783 | | .func = fr_dhcpv4_decode_proto |
784 | | }; |