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