Coverage Report

Created: 2026-10-18 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/util/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: c70875a6d29fa55196fca148a04e28bef48c74ce $
19
 *
20
 * @file src/lib/util/decode.c
21
 * @brief Generic functions for decoding protocols.
22
 *
23
 * @copyright 2022 Network RADIUS SAS (legal@networkradius.com)
24
 */
25
#include <freeradius-devel/io/test_point.h>
26
#include <freeradius-devel/util/proto.h>
27
#include <freeradius-devel/util/decode.h>
28
29
/** Decode an array of values from the network
30
 *
31
 * @param[in] ctx context to alloc new attributes in.
32
 * @param[out] out    Where to write the decoded #fr_pair_t
33
 * @param[in] parent    dictionary entry, must have parent->flags.array set
34
 * @param[in] data    to parse.
35
 * @param[in] data_len    of data to parse.
36
 * @param[in] decode_ctx  passed to decode_value
37
 * @param[in] decode_value  function to decode one value.
38
 *  <0 on error - decode error, or OOM
39
 *  data_len on success
40
 */
41
ssize_t fr_pair_array_from_network(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent,
42
           uint8_t const *data, size_t data_len,
43
           void *decode_ctx, fr_pair_decode_value_t decode_value)
44
17.8k
{
45
17.8k
  uint8_t const     *p = data, *end = p + data_len;
46
17.8k
  ssize_t     slen;
47
17.8k
  fr_pair_list_t    list;
48
49
17.8k
  fr_pair_list_init(&list);
50
51
17.8k
  FR_PROTO_HEX_DUMP(data, data_len, "fr_pair_array_from_network");
52
53
17.8k
  if (!fr_cond_assert_msg(parent->flags.array,
54
17.8k
        "%s: Internal sanity check failed, attribute \"%s\" does not have array bit set",
55
17.8k
        __FUNCTION__, parent->name)) return PAIR_DECODE_FATAL_ERROR;
56
57
  /*
58
   *  Catch stupidities.
59
   */
60
17.8k
  if (data_len == 0) return data_len;
61
62
823k
  while (p < end) {
63
811k
    slen = decode_value(ctx, &list, parent, p, (end - p), decode_ctx);
64
811k
    if (slen <= 0) {
65
1.46k
      fr_pair_list_free(&list);
66
1.46k
      return slen - (p - data);
67
1.46k
    }
68
69
809k
    p += slen;
70
809k
  }
71
72
12.3k
  fr_pair_list_append(out, &list);
73
12.3k
  return data_len;
74
13.8k
}
75
76
/** Create a "raw" pair from malformed network data
77
 *
78
 * @param[in] ctx context to alloc new attributes in.
79
 * @param[out] out    Where to write the decoded #fr_pair_t
80
 * @param[in] da    dictionary entry which it should be
81
 * @param[in] data    to parse.
82
 * @param[in] data_len    of data to parse.
83
 *  <0 on error - decode error, or OOM
84
 *  data_len on success
85
 */
86
ssize_t fr_pair_raw_from_network(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *da,
87
         uint8_t const *data, size_t data_len)
88
168k
{
89
168k
  ssize_t slen;
90
168k
  fr_pair_t *vp;
91
168k
  fr_dict_attr_t *unknown;
92
168k
  fr_dict_attr_t const *child;
93
94
168k
#if defined(STATIC_ANALYZER) || !defined(NDEBUG)
95
168k
  if (!da->parent) return -1; /* stupid static analyzers */
96
168k
#endif
97
98
168k
  FR_PROTO_TRACE("Creating Raw attribute %s", da->name);
99
168k
  FR_PROTO_HEX_DUMP(data, data_len, "fr_pair_raw_from_network");
100
101
  /*
102
   *  Build an unknown attr of the entire data.
103
   */
104
168k
  unknown = fr_dict_attr_unknown_raw_afrom_da(ctx, da);
105
168k
  if (!unknown) return -1;
106
107
160k
  vp = fr_pair_afrom_da(ctx, unknown); /* makes a copy of 'unknown' */
108
160k
  child = unknown;
109
160k
  fr_dict_attr_unknown_free(&child); /* const issues */
110
160k
  if (!vp) return -1;
111
112
  /*
113
   *  Don't bother getting data from the network if there's no data.
114
   */
115
160k
  if (data_len > 0) {
116
129k
    slen = fr_value_box_from_network(vp, &vp->data, vp->da->type, vp->da,
117
129k
             &FR_DBUFF_TMP(data, data_len), data_len, true);
118
129k
    if (slen < 0) {
119
21.0k
      talloc_free(vp);
120
21.0k
      return slen;
121
21.0k
    }
122
129k
  }
123
124
  /*
125
   *  Raw VPs are always tainted.
126
   */
127
139k
  vp->vp_tainted = true;
128
139k
  fr_pair_append(out, vp);
129
130
139k
  return data_len;
131
160k
}
132
133
134
/** Decode a list of pairs from the network
135
 *
136
 * @param[in] ctx context to alloc new attributes in.
137
 * @param[out] out    Where to write the decoded #fr_pair_t
138
 * @param[in] parent    dictionary entry, must have parent->flags.array set
139
 * @param[in] data    to parse.
140
 * @param[in] data_len    of data to parse.
141
 * @param[in] decode_ctx  passed to decode_tlv
142
 * @param[in] decode_tlv  function to decode one attribute / option / tlv
143
 * @param[in] verify_tlvs simple function to see if the TLVs are even vaguely well-formed
144
 * @param[in] nested    whether or not we create nested VPs.
145
 *  <0 on error - decode error, or OOM
146
 *  data_len on success
147
 *
148
 *  The decode_tlv function should return an error if the option is
149
 *  malformed.  In that case, the entire list of pairs is thrown away,
150
 *  and a "raw" attribute is created which contains the entire
151
 *  data_len.
152
 *
153
 *  If the value is malformed, then the decode_tlv function should call
154
 *  fr_pair_raw_from_network() on the value, and return a positive value.
155
 */
156
ssize_t fr_pair_tlvs_from_network(TALLOC_CTX *ctx, fr_pair_list_t *out,
157
          fr_dict_attr_t const *parent,
158
          uint8_t const *data, size_t const data_len,
159
          void *decode_ctx, fr_pair_decode_value_t decode_tlv,
160
          fr_pair_tlvs_verify_t verify_tlvs,
161
          bool nested)
162
22.4k
{
163
22.4k
  uint8_t const *p, *end;
164
22.4k
  fr_pair_list_t tlvs, *list;
165
22.4k
  fr_pair_t *vp = NULL;
166
22.4k
  TALLOC_CTX *child_ctx;
167
168
22.4k
  FR_PROTO_HEX_DUMP(data, data_len, "fr_pair_tlvs_from_network");
169
170
22.4k
  switch (parent->type) {
171
15.8k
  case FR_TYPE_TLV:
172
22.4k
  case FR_TYPE_VENDOR:
173
22.4k
  case FR_TYPE_STRUCT:
174
22.4k
    break;
175
176
0
  default:
177
0
    fr_strerror_printf("Internal sanity check failed, attribute \"%s\" has unexpected data type '%s'",
178
0
           parent->name, fr_type_to_str(parent->type));
179
0
    return PAIR_DECODE_FATAL_ERROR;
180
22.4k
  }
181
182
  /*
183
   *  Do a quick sanity check to see if the TLVs are at all OK.
184
   */
185
22.4k
  if (verify_tlvs && !verify_tlvs(data, data_len)) return -1;
186
187
19.3k
  p = data;
188
19.3k
  end = data + data_len;
189
190
19.3k
  if (!nested) {
191
14.4k
    fr_pair_list_init(&tlvs);
192
14.4k
    list = &tlvs;
193
14.4k
    child_ctx = ctx;
194
14.4k
  } else {
195
4.82k
    vp = fr_pair_afrom_da(ctx, parent);
196
4.82k
    if (!vp) return PAIR_DECODE_OOM;
197
4.82k
    list = &vp->vp_group;
198
4.82k
    child_ctx = vp;
199
4.82k
  }
200
201
75.3k
  while (p < end) {
202
63.4k
    ssize_t slen;
203
204
63.4k
    slen = decode_tlv(child_ctx, list, parent, p, (end - p), decode_ctx);
205
63.4k
    if (slen <= 0) {
206
7.42k
      FR_PROTO_TRACE("    tlv decode failed at offset %zu - %s", (size_t) (p - data), fr_strerror());
207
7.42k
      fr_pair_list_free(list);
208
7.42k
      talloc_free(vp);
209
210
      /*
211
       *  Don't decode it as raw.  We don't know how the TLVs are structured, so the
212
       *  only da we have is the parent.  The output has to in the parent with a child
213
       *  da.  So if we create a raw attribute here, then we have the raw attribute of
214
       *  da==parent going into the parent, which is wrong.
215
       */
216
7.42k
      return PAIR_DECODE_FATAL_ERROR;
217
7.42k
    }
218
219
56.0k
    p += slen;
220
56.0k
  }
221
222
11.8k
  if (!nested) {
223
7.60k
    fr_pair_list_append(out, &tlvs);
224
7.60k
  } else {
225
4.28k
    fr_pair_append(out, vp);
226
4.28k
  }
227
228
11.8k
  return data_len;
229
19.3k
}
230
231
232
/** Decode a DNS label or a list of DNS labels from the network
233
 *
234
 * @param[in] ctx context to alloc new attributes in.
235
 * @param[out] out    Where to write the decoded #fr_pair_t
236
 * @param[in] parent    dictionary entry, must have parent->flags.array set
237
 * @param[in] start   of the DNS labels to decode
238
 * @param[in] data    to parse.
239
 * @param[in] data_len    of data to parse.
240
 * @param[in] lb    struct to help with decoding packets.
241
 * @param[in] exact   whether the labels should entirely fill the buffer.
242
 * @return
243
 *  <0 on error - decode error, or OOM
244
 *  data_len on success
245
 *
246
 *  DNS labels exist in many protocols, and we also have src/lib/dns.c, so we might
247
 *  as well put a common function here, too.
248
 *
249
 *  This function assumes that the DNS label or labels take up all of the
250
 *  input.  If they do not, then the decoded DNS labels are freed, and
251
 *  a raw attribute is returned instead.
252
 */
253
ssize_t fr_pair_dns_labels_from_network(TALLOC_CTX *ctx, fr_pair_list_t *out,
254
          fr_dict_attr_t const *parent, uint8_t const *start,
255
          uint8_t const *data, size_t const data_len, fr_dns_labels_t *lb, bool exact)
256
48.7k
{
257
48.7k
  ssize_t slen;
258
48.7k
  size_t total, labels_len;
259
48.7k
  fr_pair_t *vp;
260
48.7k
  uint8_t const *next = data;
261
48.7k
  fr_pair_list_t tmp;
262
263
48.7k
  FR_PROTO_HEX_DUMP(data, data_len, "fr_pair_dns_labels_from_network");
264
265
48.7k
  fr_pair_list_init(&tmp);
266
267
  /*
268
   *  This function handles both single-valued and array
269
   *  types.  It's just easier that way.
270
   */
271
48.7k
  if (!parent->flags.array) {
272
    /*
273
     *  Decode starting at "NEXT", but allowing decodes from the start of the packet.
274
     */
275
38.5k
    slen = fr_dns_label_uncompressed_length(start, data, data_len, &next, lb);
276
38.5k
    if (slen <= 0) {
277
6.65k
      FR_PROTO_TRACE("ERROR - uncompressed length failed");
278
6.65k
      goto raw;
279
6.65k
    }
280
281
31.8k
    labels_len = next - data; /* decode only what we've found */
282
31.8k
  } else {
283
    /*
284
     *  Get the length of the entire set of labels, up
285
     *  to (and including) the final 0x00.
286
     *
287
     *  If any of the labels point outside of this
288
     *  area, OR they are otherwise invalid, then that's an error.
289
     */
290
10.2k
    slen = fr_dns_labels_network_verify(start, data, data_len, data, lb);
291
10.2k
    if (slen <= 0) {
292
5.99k
      FR_PROTO_TRACE("ERROR - network verify failed");
293
5.99k
      goto raw;
294
5.99k
    }
295
296
4.25k
    labels_len = slen;
297
4.25k
  }
298
299
  /*
300
   *  The labels MUST fill the entire buffer.
301
   */
302
36.0k
  if (exact && (labels_len != data_len)) {
303
807
    FR_PROTO_TRACE("ERROR - labels_len %zu != data_len %zu", labels_len, data_len);
304
13.4k
  raw:
305
13.4k
    return fr_pair_raw_from_network(ctx, out, parent, data, data_len);
306
807
  }
307
308
  /*
309
   *  Loop over the input buffer, decoding the labels one by
310
   *  one.
311
   *
312
   *  @todo - put the labels into a child cursor, and then
313
   *  merge them only if it succeeds.  That doesn't seem to
314
   *  work for some reason, and I don't have time to debug
315
   *  it right now.  So... let's leave it.
316
   */
317
72.6k
  for (total = 0; total < labels_len; total += slen) {
318
37.3k
    vp = fr_pair_afrom_da(ctx, parent);
319
37.3k
    if (!vp) return PAIR_DECODE_OOM;
320
321
    /*
322
     *  Having verified the input above, this next
323
     *  function should never fail unless there's a
324
     *  bug in the code.
325
     */
326
37.3k
    slen = fr_dns_label_to_value_box(vp, &vp->data, data, labels_len, data + total, true, lb);
327
37.3k
    if (slen <= 0) {
328
0
      FR_PROTO_TRACE("ERROR - failed decoding DNS label at with %zu error %zd", total, slen);
329
0
      talloc_free(vp);
330
0
      fr_pair_list_free(&tmp);
331
0
      goto raw;
332
0
    }
333
334
37.3k
    fr_pair_append(&tmp, vp);
335
37.3k
  }
336
337
35.2k
  fr_pair_list_append(out, &tmp);
338
35.2k
  return labels_len;
339
35.2k
}
340
341
/** Generic decode value.
342
 *
343
 */
344
ssize_t fr_pair_decode_value(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent,
345
           uint8_t const *data, size_t const data_len, UNUSED void *decode_ctx)
346
24.5k
{
347
24.5k
  fr_pair_t *vp;
348
24.5k
  ssize_t slen;
349
350
24.5k
  if (!fr_type_is_leaf(parent->type)) {
351
0
    FR_PROTO_TRACE("Cannot use generic decoder for data type %s", fr_type_to_str(parent->type));
352
0
    fr_strerror_printf("Cannot decode data type %s", fr_type_to_str(parent->type));
353
0
    return -1;
354
0
  }
355
356
24.5k
  vp = fr_pair_afrom_da(ctx, parent);
357
24.5k
  if (!vp) return PAIR_DECODE_OOM;
358
359
  /*
360
   *  No protocol-specific data types here.
361
   *
362
   *  If we can't decode this field, then the entire
363
   *  structure is treated as a raw blob.
364
   */
365
24.5k
  slen = fr_value_box_from_network(vp, &vp->data, vp->vp_type, vp->da,
366
24.5k
            &FR_DBUFF_TMP(data, data_len), data_len, true);
367
24.5k
  if (slen <= 0) {
368
0
    FR_PROTO_TRACE("failed decoding child VP %s", vp->da->name);
369
0
    talloc_free(vp);
370
0
    return fr_pair_raw_from_network(ctx, out, parent, data, data_len);
371
0
  }
372
373
24.5k
  fr_pair_append(out, vp);
374
375
24.5k
  return slen;
376
24.5k
}