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