Coverage Report

Created: 2026-11-12 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/protocols/tacacs/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: 869469061b21171c508d7ab3d674e05bd51bf5cb $
19
 *
20
 * @file protocols/tacacs/decode.c
21
 * @brief Low-Level TACACS+ decoding functions
22
 *
23
 * @copyright 2017 The FreeRADIUS server project
24
 * @copyright 2017 Network RADIUS SAS (legal@networkradius.com)
25
 */
26
27
#include <freeradius-devel/io/test_point.h>
28
#include <freeradius-devel/protocol/tacacs/tacacs.h>
29
#include <freeradius-devel/util/debug.h>
30
#include <freeradius-devel/util/net.h>
31
#include <freeradius-devel/util/struct.h>
32
33
#include "tacacs.h"
34
#include "attrs.h"
35
36
fr_tacacs_packet_code_t fr_tacacs_packet_to_code(fr_tacacs_packet_t const *pkt)
37
4.74k
{
38
4.74k
  switch (pkt->hdr.type) {
39
86
  case FR_TAC_PLUS_AUTHEN:
40
86
    if (pkt->hdr.seq_no == 1) return FR_TACACS_CODE_AUTH_START;
41
42
39
    if ((pkt->hdr.seq_no & 0x01) == 1) {
43
30
      if (pkt->authen_cont.flags == FR_TAC_PLUS_CONTINUE_FLAG_UNSET) return FR_TACACS_CODE_AUTH_CONT;
44
45
26
      if (pkt->authen_cont.flags == FR_TAC_PLUS_CONTINUE_FLAG_ABORT) return FR_TACACS_CODE_AUTH_CONT_ABORT;
46
47
25
      fr_strerror_printf("Invalid value %d for authentication continue flag", pkt->authen_cont.flags);
48
25
      return FR_TACACS_CODE_INVALID;
49
26
    }
50
51
9
    switch (pkt->authen_reply.status) {
52
1
    case FR_TAC_PLUS_AUTHEN_STATUS_PASS:
53
1
      return FR_TACACS_CODE_AUTH_PASS;
54
55
1
    case FR_TAC_PLUS_AUTHEN_STATUS_FAIL:
56
1
      return FR_TACACS_CODE_AUTH_FAIL;
57
58
1
    case FR_TAC_PLUS_AUTHEN_STATUS_GETDATA:
59
1
      return FR_TACACS_CODE_AUTH_GETDATA;
60
61
1
    case FR_TAC_PLUS_AUTHEN_STATUS_GETUSER:
62
1
      return FR_TACACS_CODE_AUTH_GETUSER;
63
64
1
    case FR_TAC_PLUS_AUTHEN_STATUS_GETPASS:
65
1
      return FR_TACACS_CODE_AUTH_GETPASS;
66
67
1
    case FR_TAC_PLUS_AUTHEN_STATUS_RESTART:
68
1
      return FR_TACACS_CODE_AUTH_RESTART;
69
70
1
    case FR_TAC_PLUS_AUTHEN_STATUS_ERROR:
71
1
      return FR_TACACS_CODE_AUTH_ERROR;
72
73
1
    case FR_TAC_PLUS_AUTHEN_STATUS_FOLLOW:
74
1
      return FR_TACACS_CODE_AUTH_FAIL;
75
76
1
    default:
77
1
      break;
78
9
    }
79
80
1
    fr_strerror_printf("Invalid value %d for authentication reply status", pkt->authen_reply.status);
81
1
    return FR_TACACS_CODE_INVALID;
82
83
4.28k
  case FR_TAC_PLUS_AUTHOR:
84
4.28k
    if ((pkt->hdr.seq_no & 0x01) == 1) {
85
556
      return FR_TACACS_CODE_AUTZ_REQUEST;
86
556
    }
87
88
3.73k
    switch (pkt->author_reply.status) {
89
37
    case FR_TAC_PLUS_AUTHOR_STATUS_PASS_ADD:
90
37
      return FR_TACACS_CODE_AUTZ_PASS_ADD;
91
92
27
    case FR_TAC_PLUS_AUTHOR_STATUS_PASS_REPL:
93
27
      return FR_TACACS_CODE_AUTZ_PASS_REPLACE;
94
95
25
    case FR_TAC_PLUS_AUTHOR_STATUS_FAIL:
96
108
    case FR_TAC_PLUS_AUTHOR_STATUS_FOLLOW:
97
108
      return FR_TACACS_CODE_AUTZ_FAIL;
98
99
44
    case FR_TAC_PLUS_AUTHOR_STATUS_ERROR:
100
44
      return FR_TACACS_CODE_AUTZ_ERROR;
101
102
3.51k
    default:
103
3.51k
      break;
104
3.73k
    }
105
106
3.51k
    fr_strerror_printf("Invalid value %d for authorization reply status", pkt->author_reply.status);
107
3.51k
    return FR_TACACS_CODE_INVALID;
108
109
368
  case FR_TAC_PLUS_ACCT:
110
368
    if ((pkt->hdr.seq_no & 0x01) == 1) {
111
360
      return FR_TACACS_CODE_ACCT_REQUEST;
112
360
    }
113
114
8
    switch (pkt->acct_reply.status) {
115
1
    case FR_TAC_PLUS_ACCT_STATUS_SUCCESS:
116
1
      return FR_TACACS_CODE_ACCT_SUCCESS;
117
118
4
    case FR_TAC_PLUS_ACCT_STATUS_ERROR:
119
6
    case FR_TAC_PLUS_ACCT_STATUS_FOLLOW:
120
6
      return FR_TACACS_CODE_ACCT_ERROR;
121
122
1
    default:
123
1
      break;
124
8
    }
125
126
1
    fr_strerror_printf("Invalid value %d for accounting reply status", pkt->acct_reply.status);
127
1
    return FR_TACACS_CODE_INVALID;
128
129
0
  default:
130
0
    fr_strerror_const("Invalid header type");
131
0
    return FR_TACACS_CODE_INVALID;
132
4.74k
  }
133
4.74k
}
134
135
5.10k
#define PACKET_HEADER_CHECK(_msg, _hdr) do { \
136
5.10k
  p = buffer + FR_HEADER_LENGTH; \
137
5.10k
  if (sizeof(_hdr) > (size_t) (end - p)) { \
138
37
    fr_strerror_printf("Header for %s is too small (%zu < %zu)", _msg, (size_t) (end - p), sizeof(_hdr)); \
139
37
    goto fail; \
140
37
  } \
141
5.10k
  body = p + sizeof(_hdr); \
142
5.06k
  data_len = sizeof(_hdr); \
143
5.06k
} while (0)
144
145
/*
146
 *  Check argv[i] after the user_msg / server_msg / argc lengths have been added to data_len
147
 */
148
4.76k
#define ARG_COUNT_CHECK(_msg, _hdr) do { \
149
4.76k
  fr_assert(p == (uint8_t const *) &(_hdr)); \
150
4.76k
  if (data_len > (size_t) (end - p)) { \
151
60
    fr_strerror_printf("Argument count %u overflows the remaining data (%zu) in the %s packet", _hdr.arg_cnt, (size_t) (end - p), _msg); \
152
60
    goto fail; \
153
60
  } \
154
4.76k
  argv = body; \
155
4.70k
  attrs = buffer + FR_HEADER_LENGTH + data_len; \
156
4.70k
  body += _hdr.arg_cnt; \
157
4.70k
  p = attrs; \
158
78.7k
  for (unsigned int i = 0; i < _hdr.arg_cnt; i++) { \
159
74.0k
    if (_hdr.arg_len[i] > (size_t) (end - p)) { \
160
55
      fr_strerror_printf("Argument %u length %u overflows packet", i, _hdr.arg_len[i]); \
161
55
      goto fail; \
162
55
    } \
163
74.0k
    p += _hdr.arg_len[i]; \
164
74.0k
  } \
165
4.70k
} while (0)
166
167
12.7k
#define DECODE_FIELD_UINT8(_da, _field) do { \
168
12.7k
  vp = fr_pair_afrom_da(ctx, _da); \
169
12.7k
  if (!vp) goto fail; \
170
12.7k
  vp->vp_uint8 = _field; \
171
12.7k
  FR_PAIR_APPEND(out, vp); \
172
12.7k
} while (0)
173
174
2.52k
#define DECODE_FIELD_STRING8(_da, _field) do { \
175
2.52k
  if (tacacs_decode_field(ctx, out, _da, &p, \
176
2.52k
      _field, end) < 0) goto fail; \
177
2.52k
} while (0)
178
179
7.55k
#define DECODE_FIELD_STRING16(_da, _field) do { \
180
7.55k
  if (tacacs_decode_field(ctx, out, _da, &p, \
181
7.55k
      ntohs(_field), end) < 0) goto fail; \
182
7.55k
} while (0)
183
184
8
#define BODY(_x) (((uint8_t const *) pkt) + sizeof(pkt->hdr) + sizeof(pkt->_x))
185
186
/** Decode a TACACS+ 'arg_N' fields.
187
 *
188
 */
189
static int tacacs_decode_args(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent,
190
            uint8_t arg_cnt, uint8_t const *argv, uint8_t const *attrs, uint8_t const *end)
191
4.64k
{
192
4.64k
  uint8_t i;
193
4.64k
  bool append = false;
194
4.64k
  uint8_t const *p = attrs;
195
4.64k
  fr_pair_t *vp;
196
4.64k
  fr_pair_t *vendor = NULL;
197
4.64k
  fr_dict_attr_t const *root;
198
199
  /*
200
   *  No one? Just get out!
201
   */
202
4.64k
  if (!arg_cnt) return 0;
203
204
  /*
205
   *  Try to decode as nested attributes.  If we can't, everything is
206
   *
207
   *    Argument-List = "foo=bar"
208
   */
209
4.61k
  if (parent) {
210
0
    vendor = fr_pair_find_by_da(out, NULL, parent);
211
0
    if (!vendor) {
212
0
      vendor = fr_pair_afrom_da(ctx, parent);
213
0
      if (!vendor) return -1;
214
0
      PAIR_ALLOCED(vendor);
215
216
0
      append = true;
217
0
    }
218
0
  }
219
220
4.61k
  root = fr_dict_root(dict_tacacs);
221
222
  /*
223
   *  Then, do the dirty job of creating attributes.
224
   */
225
78.2k
  for (i = 0; i < arg_cnt; i++) {
226
73.6k
    uint8_t const *value, *name_end, *arg_end;
227
73.6k
    fr_dict_attr_t const *da;
228
73.6k
    fr_pair_list_t *dst;
229
73.6k
    uint8_t buffer[256];
230
231
73.6k
    if (argv[i] > (end - p)) return -1;
232
233
73.6k
    if (argv[i] < 2) goto next; /* skip malformed */
234
235
64.6k
    memcpy(buffer, p, argv[i]);
236
64.6k
    buffer[argv[i]] = '\0';
237
238
64.6k
    arg_end = buffer + argv[i];
239
240
435k
    for (value = buffer, name_end = NULL; value < arg_end; value++) {
241
      /*
242
       *  RFC 8907 Section 3.7 says control
243
       *  characters MUST be excluded.
244
       */
245
432k
      if (*value < ' ') goto next;
246
247
416k
      if ((*value == '=') || (*value == '*')) {
248
45.9k
        name_end = value;
249
45.9k
        buffer[value - buffer] = '\0';
250
45.9k
        value++;
251
45.9k
        break;
252
45.9k
      }
253
416k
    }
254
255
    /*
256
     *  Skip fields which aren't in "name=value" or "name*value" format.
257
     */
258
49.2k
    if (!name_end) goto next;
259
260
    /*
261
     *  Prefer to decode from the attribute root, first.
262
     */
263
45.9k
    da = fr_dict_attr_by_name(NULL, root, (char *) buffer);
264
45.9k
    if (da) {
265
21.1k
      vp = fr_pair_afrom_da(ctx, da);
266
21.1k
      if (!vp) goto oom;
267
21.1k
      PAIR_ALLOCED(vp);
268
269
21.1k
      dst = out;
270
21.1k
      goto decode;
271
21.1k
    }
272
273
    /*
274
     *  If the attribute isn't in the main dictionary,
275
     *  maybe it's in the vendor dictionary?
276
     */
277
24.7k
    if (vendor) {
278
0
      da = fr_dict_attr_by_name(NULL, parent, (char *) buffer);
279
0
      if (!da) goto raw;
280
281
0
      vp = fr_pair_afrom_da(vendor, da);
282
0
      if (!vp) goto oom;
283
0
      PAIR_ALLOCED(vp);
284
285
0
      dst = &vendor->vp_group;
286
287
21.1k
    decode:
288
      /*
289
       *      If it's OCTETS or STRING type, then just copy the value verbatim, as the
290
       *      contents are (should be?) binary-safe.  But if it's zero length, then don't need to
291
       *      copy anything.
292
       *
293
       *      Note that we copy things manually here because
294
       *      we don't want the OCTETS type to be parsed as
295
       *      hex.  And, we don't want the string type to be
296
       *      unescaped.
297
       */
298
21.1k
      if (da->type == FR_TYPE_OCTETS) {
299
112
        if ((arg_end > value) &&
300
74
            (fr_pair_value_memdup(vp, value, arg_end - value, true) < 0)) {
301
0
          goto fail;
302
0
        }
303
304
21.0k
      } else if (da->type == FR_TYPE_STRING) {
305
236
        if ((arg_end > value) &&
306
79
            (fr_pair_value_bstrndup(vp, (char const *) value, arg_end - value, true) < 0)) {
307
0
          goto fail;
308
0
        }
309
310
20.8k
      } else if (arg_end == value) {
311
        /*
312
         *  Any other leaf type MUST have non-zero contents.
313
         */
314
106
        talloc_free(vp);
315
106
        goto raw;
316
317
20.7k
      } else {
318
        /*
319
         *      Parse the string, and try to convert it to the
320
         *      underlying data type.  If it can't be
321
         *      converted as a data type, just convert it as
322
         *      Argument-List.
323
         *
324
         *      And if that fails, just ignore it completely.
325
         */
326
20.7k
        if (fr_pair_value_from_str(vp, (char const *) value, arg_end - value, NULL, true) < 0) {
327
18.2k
          talloc_free(vp);
328
18.2k
          goto raw;
329
18.2k
        }
330
331
        /*
332
         *  Else it parsed fine, append it to the output vendor list.
333
         */
334
20.7k
      }
335
336
2.86k
      fr_pair_append(dst, vp);
337
338
24.7k
    } else {
339
43.1k
    raw:
340
43.1k
      vp = fr_pair_afrom_da(ctx, attr_tacacs_argument_list);
341
43.1k
      if (!vp) {
342
0
      oom:
343
0
        fr_strerror_const("Out of Memory");
344
0
      fail:
345
0
        if (append) {
346
0
          talloc_free(vendor);
347
0
        } else {
348
0
          talloc_free(vp);
349
0
        }
350
0
        return -1;
351
0
      }
352
43.1k
      PAIR_ALLOCED(vp);
353
354
43.1k
      value = p;
355
43.1k
      arg_end = p + argv[i];
356
357
43.1k
      if ((arg_end > value) &&
358
43.1k
          (fr_pair_value_bstrndup(vp, (char const *) value, arg_end - value, true) < 0)) {
359
0
        goto fail;
360
0
      }
361
362
43.1k
      fr_pair_append(out, vp);
363
43.1k
    }
364
365
73.6k
  next:
366
73.6k
    p += argv[i];
367
73.6k
  }
368
369
4.61k
  if (append) {
370
0
    if (fr_pair_list_num_elements(&vendor->vp_group) > 0) {
371
0
      fr_pair_append(out, vendor);
372
0
    } else {
373
0
      talloc_free(vendor);
374
0
    }
375
0
  }
376
377
4.61k
  return 0;
378
4.61k
}
379
380
/**
381
 *  Decode a TACACS+ field.
382
 */
383
static int tacacs_decode_field(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *da,
384
        uint8_t const **field_data, uint16_t field_len, uint8_t const *end)
385
10.0k
{
386
10.0k
  uint8_t const *p = *field_data;
387
10.0k
  fr_pair_t *vp;
388
389
10.0k
  if (field_len > (end - p)) {
390
0
    fr_strerror_printf("'%s' length %u overflows the remaining data (%zu) in the packet",
391
0
           da->name, field_len, (size_t) (end - p));
392
0
    return -1;
393
0
  }
394
395
10.0k
  vp = fr_pair_afrom_da(ctx, da);
396
10.0k
  if (!vp) {
397
0
    fr_strerror_const("Out of Memory");
398
0
    return -1;
399
0
  }
400
10.0k
  PAIR_ALLOCED(vp);
401
402
10.0k
  if (field_len) {
403
1.15k
    if (da->type == FR_TYPE_STRING) {
404
1.04k
      fr_pair_value_bstrndup(vp, (char const *)p, field_len, true);
405
1.04k
    } else if (da->type == FR_TYPE_OCTETS) {
406
111
      fr_pair_value_memdup(vp, p, field_len, true);
407
111
    } else {
408
0
      fr_assert(0);
409
0
    }
410
1.15k
    p += field_len;
411
1.15k
    *field_data = p;
412
1.15k
  }
413
414
10.0k
  fr_pair_append(out, vp);
415
416
10.0k
  return 0;
417
10.0k
}
418
419
/**
420
 *  Decode a TACACS+ packet
421
 */
422
ssize_t fr_tacacs_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *vendor,
423
       uint8_t const *buffer, size_t buffer_len,
424
       const uint8_t *original, char const * const secret, size_t secret_len, fr_tacacs_packet_code_t *code)
425
5.19k
{
426
5.19k
  fr_tacacs_packet_t const *pkt;
427
5.19k
  fr_pair_t   *vp;
428
5.19k
  size_t      data_len;
429
5.19k
  uint8_t const     *p, *body, *argv, *attrs, *end;
430
5.19k
  uint8_t     *decrypted = NULL;
431
432
  /*
433
   * 3.4. The TACACS+ Packet Header
434
   *
435
   * 1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8
436
   * +----------------+----------------+----------------+----------------+
437
   * |major  | minor  |                |                |                |
438
   * |version| version|      type      |     seq_no     |   flags        |
439
   * +----------------+----------------+----------------+----------------+
440
   * |                                                                   |
441
   * |                            session_id                             |
442
   * +----------------+----------------+----------------+----------------+
443
   * |                                                                   |
444
   * |                              length                               |
445
   * +----------------+----------------+----------------+----------------+
446
   */
447
5.19k
  pkt = (fr_tacacs_packet_t const *) buffer;
448
449
  /*
450
   *  p miscellaneous pointer for decoding things
451
   *  body  points to just past the (randomly sized) per-packet header,
452
   *    where the various user / server messages are.
453
   *    sometimes this is after "argv".
454
   *  argv  points to the array of argv[i] length entries
455
   *  attrs points to the attributes we need to decode as "foo=bar".
456
   */
457
5.19k
  argv = attrs = NULL;
458
5.19k
  end = buffer + buffer_len;
459
460
  /*
461
   *  Check that we have a full TACACS+ header before
462
   *  decoding anything.
463
   */
464
5.19k
  if (buffer_len < sizeof(pkt->hdr)) {
465
6
    fr_strerror_printf("Packet is too small (%zu < 12) to be TACACS+.", buffer_len);
466
6
    return -1;
467
6
  }
468
469
  /*
470
   *  TACACS major / minor version MUST be 12.0 or 12.1
471
   */
472
5.18k
  if (!(pkt->hdr.ver.major == 12 && (pkt->hdr.ver.minor == 0 || pkt->hdr.ver.minor == 1))) {
473
14
    fr_strerror_printf("Unsupported TACACS+ version %d.%d (%02x)", pkt->hdr.ver.major, pkt->hdr.ver.minor, buffer[0]);
474
14
    return -1;
475
14
  }
476
477
  /*
478
   *  There's no reason to accept 64K TACACS+ packets.
479
   *
480
   *  In any case, the largest possible packet has the
481
   *  header, plus 2 16-bit fields, plus 255 8-bit fields,
482
   *  which is a bit under 2^18.
483
   */
484
5.17k
  if ((buffer[8] != 0) || (buffer[9] != 0)) {
485
18
    fr_strerror_const("Packet is too large.  Our limit is 64K");
486
18
    return -1;
487
18
  }
488
489
  /*
490
   *  As a stream protocol, the TACACS+ packet MUST fit
491
   *  exactly into however many bytes we read.
492
   */
493
5.15k
  if ((buffer + sizeof(pkt->hdr) + ntohl(pkt->hdr.length)) != end) {
494
40
    fr_strerror_const("Packet does not exactly fill buffer");
495
40
    return -1;
496
40
  }
497
498
  /*
499
   *  There are only 3 types of packets which are supported.
500
   */
501
5.11k
  if (!((pkt->hdr.type == FR_TAC_PLUS_AUTHEN) ||
502
4.87k
        (pkt->hdr.type == FR_TAC_PLUS_AUTHOR) ||
503
485
        (pkt->hdr.type == FR_TAC_PLUS_ACCT))) {
504
13
    fr_strerror_printf("Unknown packet type %d", pkt->hdr.type);
505
13
    return -1;
506
13
  }
507
508
  /*
509
   *  Check that the session IDs are correct.
510
   */
511
5.10k
  if (original && (memcmp(original + 4, buffer + 4, 4) != 0)) {
512
0
    fr_strerror_printf("Session ID %08x does not match expected number %08x",
513
0
           fr_nbo_to_uint32(buffer + 4), fr_nbo_to_uint32(original + 4));
514
0
    return -1;
515
0
  }
516
517
5.10k
  if (!secret && packet_is_encrypted(pkt)) {
518
0
    fr_strerror_const("Packet is encrypted, but there is no secret to decrypt it");
519
0
    return -1;
520
0
  }
521
522
  /*
523
   *  Call the struct encoder to do the actual work.
524
   */
525
5.10k
  if (fr_struct_from_network(ctx, out, attr_tacacs_packet, buffer, buffer_len, NULL, NULL, NULL) < 0) {
526
0
    fr_strerror_printf_push("Failed decoding TACACS header");
527
0
    return -1;
528
0
  }
529
530
  /*
531
   *  3.6. Encryption
532
   *
533
   *  If there's a secret, we always decrypt the packets.
534
   */
535
5.10k
  if (secret && packet_is_encrypted(pkt)) {
536
115
    size_t length;
537
538
115
    if (!secret_len) {
539
0
      fr_strerror_const("Packet should be encrypted, but the secret has zero length");
540
0
      return -1;
541
0
    }
542
543
115
    length = ntohl(pkt->hdr.length);
544
545
    /*
546
     *  We need that to decrypt the body content.
547
     *
548
     *  @todo - use thread-local storage to avoid allocations?
549
     */
550
115
    decrypted = talloc_memdup(ctx, buffer, buffer_len);
551
115
    if (!decrypted) {
552
0
      fr_strerror_const("Out of Memory");
553
0
      return -1;
554
0
    }
555
556
115
    pkt = (fr_tacacs_packet_t const *) decrypted;
557
115
    end = decrypted + buffer_len;
558
559
115
    if (fr_tacacs_body_xor(pkt, decrypted + sizeof(pkt->hdr), length, secret, secret_len) < 0) {
560
3.90k
    fail:
561
3.90k
      talloc_free(decrypted);
562
3.90k
      return -1;
563
0
    }
564
565
115
    decrypted[3] |= FR_TAC_PLUS_UNENCRYPTED_FLAG;
566
567
115
    FR_PROTO_HEX_DUMP(decrypted, buffer_len, "fr_tacacs_packet_t (unencrypted)");
568
569
115
    buffer = decrypted;
570
115
  }
571
572
5.10k
#ifndef NDEBUG
573
5.10k
  if (fr_debug_lvl >= L_DBG_LVL_4) fr_tacacs_packet_log_hex(&default_log, pkt, (end - buffer));
574
5.10k
#endif
575
576
5.10k
  switch (pkt->hdr.type) {
577
240
  case FR_TAC_PLUS_AUTHEN:
578
240
    if (packet_is_authen_start_request(pkt)) {
579
83
      uint8_t want;
580
83
      bool raw;
581
83
      fr_dict_attr_t const *da, *challenge;
582
583
      /**
584
       * 4.1. The Authentication START Packet Body
585
       *
586
       *  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8
587
       * +----------------+----------------+----------------+----------------+
588
       * |    action      |    priv_lvl    |  authen_type   | authen_service |
589
       * +----------------+----------------+----------------+----------------+
590
       * |    user_len    |    port_len    |  rem_addr_len  |    data_len    |
591
       * +----------------+----------------+----------------+----------------+
592
       * |    user ...
593
       * +----------------+----------------+----------------+----------------+
594
       * |    port ...
595
       * +----------------+----------------+----------------+----------------+
596
       * |    rem_addr ...
597
       * +----------------+----------------+----------------+----------------+
598
       * |    data...
599
       * +----------------+----------------+----------------+----------------+
600
       */
601
83
      PACKET_HEADER_CHECK("Authentication-Start", pkt->authen_start);
602
603
78
      data_len += p[4] + p[5] + p[6] + p[7];
604
78
      if (data_len > (size_t) (end - p)) {
605
135
      overflow:
606
135
        if ((buffer[3] & FR_TAC_PLUS_UNENCRYPTED_FLAG) == 0) {
607
0
        bad_secret:
608
0
          fr_strerror_const("Invalid packet after decryption - is the secret key incorrect?");
609
0
          goto fail;
610
0
        }
611
612
135
        fr_strerror_const("Data overflows the packet");
613
135
        goto fail;
614
135
      }
615
62
      if (data_len < (size_t) (end - p)) {
616
70
      underflow:
617
70
        if ((buffer[3] & FR_TAC_PLUS_UNENCRYPTED_FLAG) == 0) goto bad_secret;
618
619
70
        fr_strerror_const("Data underflows the packet");
620
70
        goto fail;
621
70
      }
622
623
48
      DECODE_FIELD_UINT8(attr_tacacs_packet_body_type, FR_PACKET_BODY_TYPE_START);
624
625
      /*
626
       *  Decode 4 octets of various flags.
627
       */
628
48
      DECODE_FIELD_UINT8(attr_tacacs_action, pkt->authen_start.action);
629
48
      DECODE_FIELD_UINT8(attr_tacacs_privilege_level, pkt->authen_start.priv_lvl);
630
48
      DECODE_FIELD_UINT8(attr_tacacs_authentication_type, pkt->authen_start.authen_type);
631
48
      DECODE_FIELD_UINT8(attr_tacacs_authentication_service, pkt->authen_start.authen_service);
632
633
      /*
634
       *  Decode 3 fields, based on their "length"
635
       *  user and rem_addr are optional - indicated by zero length
636
       */
637
48
      p = body;
638
48
      if (pkt->authen_start.user_len > 0) DECODE_FIELD_STRING8(attr_tacacs_user_name,
639
48
                     pkt->authen_start.user_len);
640
48
      DECODE_FIELD_STRING8(attr_tacacs_client_port, pkt->authen_start.port_len);
641
48
      if (pkt->authen_start.rem_addr_len > 0) DECODE_FIELD_STRING8(attr_tacacs_remote_address,
642
48
                         pkt->authen_start.rem_addr_len);
643
644
      /*
645
       *  Check the length on the various
646
       *  authentication types.
647
       */
648
48
      raw = false;
649
48
      challenge = NULL;
650
651
48
      switch (pkt->authen_start.authen_type) {
652
14
      default:
653
14
        raw = true;
654
14
        want = pkt->authen_start.data_len;
655
14
        da = attr_tacacs_data;
656
14
        break;
657
658
9
      case FR_AUTHENTICATION_TYPE_VALUE_PAP:
659
9
        want = pkt->authen_start.data_len;
660
9
        da = attr_tacacs_user_password;
661
9
        break;
662
663
15
      case FR_AUTHENTICATION_TYPE_VALUE_CHAP:
664
15
        want = 1 + 16; /* id + HOPEFULLY 8 octets of challenge + 16 hash */
665
15
        da = attr_tacacs_chap_password;
666
15
        challenge = attr_tacacs_chap_challenge;
667
15
        break;
668
669
5
      case FR_AUTHENTICATION_TYPE_VALUE_MSCHAP:
670
5
        want = 1 + 49; /* id + HOPEFULLY 8 octets of challenge + 49 MS-CHAP stuff */
671
5
        da = attr_tacacs_mschap_response;
672
5
        challenge = attr_tacacs_mschap_challenge;
673
5
        break;
674
675
5
      case FR_AUTHENTICATION_TYPE_VALUE_MSCHAPV2:
676
5
        want = 1 + 49; /* id + HOPEFULLY 16 octets of challenge + 49 MS-CHAP stuff */
677
5
        da = attr_tacacs_mschap2_response;
678
5
        challenge = attr_tacacs_mschap_challenge;
679
5
        break;
680
48
      }
681
682
      /*
683
       *  If we have enough data, decode it as
684
       *  the claimed authentication type.
685
       *
686
       *  Otherwise, decode the entire field as an unknown
687
       *  attribute.
688
       */
689
48
      if (raw || (pkt->authen_start.data_len < want)) {
690
25
        fr_dict_attr_t *da_unknown;
691
692
25
        da_unknown = fr_dict_attr_unknown_raw_afrom_num(ctx, fr_dict_root(dict_tacacs),
693
25
                    attr_tacacs_data->attr);
694
25
        if (!da_unknown) goto fail;
695
696
25
        want = pkt->authen_start.data_len;
697
698
25
        DECODE_FIELD_STRING8(da_unknown, want);
699
25
        talloc_free(da_unknown);
700
701
25
      } else if (!challenge) {
702
9
        DECODE_FIELD_STRING8(da, want);
703
704
14
      } else if (pkt->authen_start.data_len == want)  {
705
1
        fr_strerror_printf("%s has zero length", challenge->name);
706
1
        goto fail;
707
708
13
      } else { /* 1 of ID + ??? of challenge + (want-1) of data */
709
13
        uint8_t challenge_len = pkt->authen_start.data_len - want;
710
13
        uint8_t hash[50];
711
712
        /*
713
         *  Rework things to make sense.
714
         *  RFC 8079 says that MS-CHAP responses should follow RFC 2433 and 2759
715
         *  which have "Flags" at the end.
716
         *  RADIUS attributes expect "Flags" after the ID as per RFC 2548.
717
         *  Re-arrange to make things consistent.
718
         */
719
13
        hash[0] = p[0];
720
13
        switch (pkt->authen_start.authen_type) {
721
1
        case FR_AUTHENTICATION_TYPE_VALUE_MSCHAP:
722
4
        case FR_AUTHENTICATION_TYPE_VALUE_MSCHAPV2:
723
4
          hash[1] = p[want - 1];
724
4
          memcpy(hash + 2, p + 1 + challenge_len, want - 2);
725
4
          break;
726
727
9
        default:
728
9
          memcpy(hash + 1, p + 1 + challenge_len, want - 1);
729
9
          break;
730
13
        }
731
732
13
        vp = fr_pair_afrom_da(ctx, da);
733
13
        if (!vp) goto fail;
734
735
13
        FR_PAIR_APPEND(out, vp);
736
737
        /*
738
         *  ID + hash
739
         */
740
13
        if (fr_pair_value_memdup(vp, hash, want, true) < 0) goto fail;
741
742
        /*
743
         *  And then the challenge.
744
         */
745
13
        vp = fr_pair_afrom_da(ctx, challenge);
746
13
        if (!vp) goto fail;
747
748
13
        FR_PAIR_APPEND(out, vp);
749
750
13
        if (fr_pair_value_memdup(vp, p + 1, challenge_len, true) < 0) goto fail;
751
752
13
        p += pkt->authen_start.data_len;
753
13
      }
754
755
157
    } else if (packet_is_authen_continue(pkt)) {
756
      /*
757
       * 4.3. The Authentication CONTINUE Packet Body
758
       *
759
       * This packet is sent from the client to the server following the receipt of
760
       * a REPLY packet.
761
       *
762
       *  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8
763
       * +----------------+----------------+----------------+----------------+
764
       * |          user_msg len           |            data_len             |
765
       * +----------------+----------------+----------------+----------------+
766
       * |     flags      |  user_msg ...
767
       * +----------------+----------------+----------------+----------------+
768
       * |    data ...
769
       * +----------------+
770
       */
771
772
      /*
773
       *  Version 1 is ONLY used for PAP / CHAP
774
       *  / MS-CHAP start and reply packets.
775
       */
776
89
      if (pkt->hdr.ver.minor != 0) {
777
4
      invalid_version:
778
4
        fr_strerror_const("Invalid TACACS+ version");
779
4
        goto fail;
780
2
      }
781
782
87
      PACKET_HEADER_CHECK("Authentication-Continue", pkt->authen_cont);
783
81
      data_len += fr_nbo_to_uint16(p) + fr_nbo_to_uint16(p + 2);
784
81
      if (data_len > (size_t) (end - p)) goto overflow;
785
49
      if (data_len < (size_t) (end - p)) goto underflow;
786
787
30
      DECODE_FIELD_UINT8(attr_tacacs_packet_body_type, FR_PACKET_BODY_TYPE_CONTINUE);
788
789
      /*
790
       *  Decode 2 fields, based on their "length"
791
       */
792
30
      p = body;
793
30
      DECODE_FIELD_STRING16(attr_tacacs_user_message, pkt->authen_cont.user_msg_len);
794
30
      DECODE_FIELD_STRING16(attr_tacacs_data, pkt->authen_cont.data_len);
795
796
      /*
797
       *  And finally the flags.
798
       */
799
30
      DECODE_FIELD_UINT8(attr_tacacs_authentication_continue_flags, pkt->authen_cont.flags);
800
801
68
    } else if (packet_is_authen_reply(pkt)) {
802
      /*
803
       * 4.2. The Authentication REPLY Packet Body
804
       *
805
       * 1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8
806
       * +----------------+----------------+----------------+----------------+
807
       * |     status     |      flags     |        server_msg_len           |
808
       * +----------------+----------------+----------------+----------------+
809
       * |           data_len              |        server_msg ...
810
       * +----------------+----------------+----------------+----------------+
811
       * |           data ...
812
       * +----------------+----------------+
813
       */
814
815
      /*
816
       *  We don't care about versions for replies.
817
       *  We just echo whatever was sent in the request.
818
       */
819
68
      PACKET_HEADER_CHECK("Authentication-Reply", pkt->authen_reply);
820
60
      data_len += fr_nbo_to_uint16(p + 2) + fr_nbo_to_uint16(p + 4);
821
60
      if (data_len > (size_t) (end - p)) goto overflow;
822
28
      if (data_len < (size_t) (end - p)) goto underflow;
823
824
9
      DECODE_FIELD_UINT8(attr_tacacs_packet_body_type, FR_PACKET_BODY_TYPE_REPLY);
825
826
9
      DECODE_FIELD_UINT8(attr_tacacs_authentication_status, pkt->authen_reply.status);
827
9
      DECODE_FIELD_UINT8(attr_tacacs_authentication_flags, pkt->authen_reply.flags);
828
829
      /*
830
       *  Decode 2 fields, based on their "length"
831
       */
832
9
      p = body;
833
9
      DECODE_FIELD_STRING16(attr_tacacs_server_message, pkt->authen_reply.server_msg_len);
834
9
      DECODE_FIELD_STRING16(attr_tacacs_data, pkt->authen_reply.data_len);
835
836
9
    } else {
837
0
      fr_strerror_const("Unknown authentication packet");
838
0
      goto fail;
839
0
    }
840
86
    break;
841
842
4.39k
  case FR_TAC_PLUS_AUTHOR:
843
4.39k
    if (packet_is_author_request(pkt)) {
844
      /*
845
       * 5.1. The Authorization REQUEST Packet Body
846
       *
847
       *  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8
848
       * +----------------+----------------+----------------+----------------+
849
       * |  authen_method |    priv_lvl    |  authen_type   | authen_service |
850
       * +----------------+----------------+----------------+----------------+
851
       * |    user_len    |    port_len    |  rem_addr_len  |    arg_cnt     |
852
       * +----------------+----------------+----------------+----------------+
853
       * |   arg_1_len    |   arg_2_len    |      ...       |   arg_N_len    |
854
       * +----------------+----------------+----------------+----------------+
855
       * |   user ...
856
       * +----------------+----------------+----------------+----------------+
857
       * |   port ...
858
       * +----------------+----------------+----------------+----------------+
859
       * |   rem_addr ...
860
       * +----------------+----------------+----------------+----------------+
861
       * |   arg_1 ...
862
       * +----------------+----------------+----------------+----------------+
863
       * |   arg_2 ...
864
       * +----------------+----------------+----------------+----------------+
865
       * |   ...
866
       * +----------------+----------------+----------------+----------------+
867
       * |   arg_N ...
868
       * +----------------+----------------+----------------+----------------+
869
       */
870
871
605
      if (pkt->hdr.ver.minor != 0) goto invalid_version;
872
873
604
      PACKET_HEADER_CHECK("Authorization-Request", pkt->author_req);
874
599
      data_len += p[4] + p[5] + p[6] + p[7];
875
876
599
      ARG_COUNT_CHECK("Authorization-Request", pkt->author_req);
877
878
556
      DECODE_FIELD_UINT8(attr_tacacs_packet_body_type, FR_PACKET_BODY_TYPE_REQUEST);
879
880
      /*
881
       *  Decode 4 octets of various flags.
882
       */
883
556
      DECODE_FIELD_UINT8(attr_tacacs_authentication_method, pkt->author_req.authen_method);
884
556
      DECODE_FIELD_UINT8(attr_tacacs_privilege_level, pkt->author_req.priv_lvl);
885
556
      DECODE_FIELD_UINT8(attr_tacacs_authentication_type, pkt->author_req.authen_type);
886
556
      DECODE_FIELD_UINT8(attr_tacacs_authentication_service, pkt->author_req.authen_service);
887
888
      /*
889
       *  Decode 3 fields, based on their "length"
890
       *  rem_addr is optional - indicated by zero length
891
       */
892
556
      p = body;
893
556
      DECODE_FIELD_STRING8(attr_tacacs_user_name, pkt->author_req.user_len);
894
556
      DECODE_FIELD_STRING8(attr_tacacs_client_port, pkt->author_req.port_len);
895
556
      if (pkt->author_req.rem_addr_len > 0) DECODE_FIELD_STRING8(attr_tacacs_remote_address,
896
556
                       pkt->author_req.rem_addr_len);
897
898
      /*
899
       *  Decode 'arg_N' arguments (horrible format)
900
       */
901
556
      if (tacacs_decode_args(ctx, out, vendor,
902
556
                 pkt->author_req.arg_cnt, argv, attrs, end) < 0) goto fail;
903
904
3.78k
    } else if (packet_is_author_reply(pkt)) {
905
      /*
906
       * 5.2. The Authorization RESPONSE Packet Body
907
       *
908
       *  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8
909
       * +----------------+----------------+----------------+----------------+
910
       * |    status      |     arg_cnt    |         server_msg len          |
911
       * +----------------+----------------+----------------+----------------+
912
       * +            data_len             |    arg_1_len   |    arg_2_len   |
913
       * +----------------+----------------+----------------+----------------+
914
       * |      ...       |   arg_N_len    |         server_msg ...
915
       * +----------------+----------------+----------------+----------------+
916
       * |   data ...
917
       * +----------------+----------------+----------------+----------------+
918
       * |   arg_1 ...
919
       * +----------------+----------------+----------------+----------------+
920
       * |   arg_2 ...
921
       * +----------------+----------------+----------------+----------------+
922
       * |   ...
923
       * +----------------+----------------+----------------+----------------+
924
       * |   arg_N ...
925
       * +----------------+----------------+----------------+----------------+
926
       */
927
928
      /*
929
       *  We don't care about versions for replies.
930
       *  We just echo whatever was sent in the request.
931
       */
932
933
3.78k
      PACKET_HEADER_CHECK("Authorization-Reply", pkt->author_reply);
934
3.78k
      data_len += p[1] + fr_nbo_to_uint16(p + 2) + fr_nbo_to_uint16(p + 4);
935
936
3.78k
      ARG_COUNT_CHECK("Authorization-Reply", pkt->author_reply);
937
3.73k
      DECODE_FIELD_UINT8(attr_tacacs_packet_body_type, FR_PACKET_BODY_TYPE_RESPONSE);
938
939
      /*
940
       *  Decode 1 octets
941
       */
942
3.73k
      DECODE_FIELD_UINT8(attr_tacacs_authorization_status, pkt->author_reply.status);
943
944
      /*
945
       *  Decode 2 fields, based on their "length"
946
       */
947
3.73k
      p = body;
948
3.73k
      DECODE_FIELD_STRING16(attr_tacacs_server_message, pkt->author_reply.server_msg_len);
949
3.73k
      DECODE_FIELD_STRING16(attr_tacacs_data, pkt->author_reply.data_len);
950
951
      /*
952
       *  Decode 'arg_N' arguments (horrible format)
953
       */
954
3.73k
      if (tacacs_decode_args(ctx, out, vendor,
955
3.73k
                 pkt->author_reply.arg_cnt, argv, attrs, end) < 0) goto fail;
956
957
3.73k
    } else {
958
0
      fr_strerror_const("Unknown authorization packet");
959
0
      goto fail;
960
0
    }
961
4.28k
    break;
962
963
4.28k
  case FR_TAC_PLUS_ACCT:
964
472
    if (packet_is_acct_request(pkt)) {
965
      /**
966
       * 6.1. The Account REQUEST Packet Body
967
       *
968
       1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8
969
       * +----------------+----------------+----------------+----------------+
970
       * |      flags     |  authen_method |    priv_lvl    |  authen_type   |
971
       * +----------------+----------------+----------------+----------------+
972
       * | authen_service |    user_len    |    port_len    |  rem_addr_len  |
973
       * +----------------+----------------+----------------+----------------+
974
       * |    arg_cnt     |   arg_1_len    |   arg_2_len    |      ...       |
975
       * +----------------+----------------+----------------+----------------+
976
       * |   arg_N_len    |    user ...
977
       * +----------------+----------------+----------------+----------------+
978
       * |   port ...
979
       * +----------------+----------------+----------------+----------------+
980
       * |   rem_addr ...
981
       * +----------------+----------------+----------------+----------------+
982
       * |   arg_1 ...
983
       * +----------------+----------------+----------------+----------------+
984
       * |   arg_2 ...
985
       * +----------------+----------------+----------------+----------------+
986
       * |   ...
987
       * +----------------+----------------+----------------+----------------+
988
       * |   arg_N ...
989
       * +----------------+----------------+----------------+----------------+
990
       */
991
992
407
      if (pkt->hdr.ver.minor != 0) goto invalid_version;
993
994
406
      PACKET_HEADER_CHECK("Accounting-Request", pkt->acct_req);
995
401
      data_len += p[5] + p[6] + p[7] + p[8];
996
401
      if (data_len > (size_t) (end - p)) goto overflow;
997
      /* can't check for underflow, as we have argv[argc] */
998
999
381
      ARG_COUNT_CHECK("Accounting-Request", pkt->acct_req);
1000
1001
360
      DECODE_FIELD_UINT8(attr_tacacs_packet_body_type, FR_PACKET_BODY_TYPE_REQUEST);
1002
1003
      /*
1004
       *  Decode 5 octets of various flags.
1005
       */
1006
360
      DECODE_FIELD_UINT8(attr_tacacs_accounting_flags, pkt->acct_req.flags);
1007
360
      DECODE_FIELD_UINT8(attr_tacacs_authentication_method, pkt->acct_req.authen_method);
1008
360
      DECODE_FIELD_UINT8(attr_tacacs_privilege_level, pkt->acct_req.priv_lvl);
1009
360
      DECODE_FIELD_UINT8(attr_tacacs_authentication_type, pkt->acct_req.authen_type);
1010
360
      DECODE_FIELD_UINT8(attr_tacacs_authentication_service, pkt->acct_req.authen_service);
1011
1012
      /*
1013
       *  Decode 3 fields, based on their "length"
1014
       */
1015
360
      p = body;
1016
360
      DECODE_FIELD_STRING8(attr_tacacs_user_name, pkt->acct_req.user_len);
1017
360
      DECODE_FIELD_STRING8(attr_tacacs_client_port, pkt->acct_req.port_len);
1018
360
      DECODE_FIELD_STRING8(attr_tacacs_remote_address, pkt->acct_req.rem_addr_len);
1019
1020
      /*
1021
       *  Decode 'arg_N' arguments (horrible format)
1022
       */
1023
360
      if (tacacs_decode_args(ctx, out, vendor,
1024
360
                 pkt->acct_req.arg_cnt, argv, attrs, end) < 0) goto fail;
1025
1026
360
    } else if (packet_is_acct_reply(pkt)) {
1027
      /**
1028
       * 6.2. The Accounting REPLY Packet Body
1029
       *
1030
       * 1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8
1031
       * +----------------+----------------+----------------+----------------+
1032
       * |         server_msg len          |            data_len             |
1033
       * +----------------+----------------+----------------+----------------+
1034
       * |     status     |         server_msg ...
1035
       * +----------------+----------------+----------------+----------------+
1036
       * |     data ...
1037
       * +----------------+
1038
       */
1039
1040
      /*
1041
       *  We don't care about versions for replies.
1042
       *  We just echo whatever was sent in the request.
1043
       */
1044
1045
65
      PACKET_HEADER_CHECK("Accounting-Reply", pkt->acct_reply);
1046
61
      data_len += fr_nbo_to_uint16(p) + fr_nbo_to_uint16(p + 2);
1047
61
      if (data_len > (size_t) (end - p)) goto overflow;
1048
26
      if (data_len < (size_t) (end - p)) goto underflow;
1049
1050
8
      p = BODY(acct_reply);
1051
8
      DECODE_FIELD_UINT8(attr_tacacs_packet_body_type, FR_PACKET_BODY_TYPE_REPLY);
1052
1053
      /*
1054
       *  Decode 2 fields, based on their "length"
1055
       */
1056
8
      DECODE_FIELD_STRING16(attr_tacacs_server_message, pkt->acct_reply.server_msg_len);
1057
8
      DECODE_FIELD_STRING16(attr_tacacs_data, pkt->acct_reply.data_len);
1058
1059
      /* Decode 1 octet */
1060
8
      DECODE_FIELD_UINT8(attr_tacacs_accounting_status, pkt->acct_reply.status);
1061
8
    } else {
1062
0
      fr_strerror_const("Unknown accounting packet");
1063
0
      goto fail;
1064
0
    }
1065
368
    break;
1066
368
  default:
1067
0
    fr_strerror_printf("decode: Unsupported packet type %d", pkt->hdr.type);
1068
0
    goto fail;
1069
5.10k
  }
1070
1071
  /*
1072
   *  fr_tacacs_packet_to_code() reads the status or flags field
1073
   *  of the body.  The switch above has checked that the body is
1074
   *  long enough to hold that field.
1075
   */
1076
4.74k
  if (code) {
1077
4.74k
    *code = fr_tacacs_packet_to_code((fr_tacacs_packet_t const *) buffer);
1078
4.74k
    if (*code == FR_TACACS_CODE_INVALID) goto fail;
1079
4.74k
  }
1080
1081
4.74k
  talloc_free(decrypted);
1082
1.19k
  return buffer_len;
1083
4.74k
}
1084
1085
/*
1086
 *  Test points for protocol decode
1087
 */
1088
static ssize_t fr_tacacs_decode_proto(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len, void *proto_ctx)
1089
5.19k
{
1090
5.19k
  fr_tacacs_ctx_t *test_ctx = talloc_get_type_abort(proto_ctx, fr_tacacs_ctx_t);
1091
5.19k
  fr_dict_attr_t const *dv;
1092
5.19k
  fr_tacacs_packet_code_t code;
1093
5.19k
  fr_pair_t   *vp;
1094
5.19k
  ssize_t     slen;
1095
1096
5.19k
  dv = fr_dict_attr_by_name(NULL, fr_dict_root(dict_tacacs), "Test");
1097
5.19k
  fr_assert(!dv || (dv->type == FR_TYPE_VENDOR));
1098
1099
5.19k
  slen = fr_tacacs_decode(ctx, out, dv, data, data_len, NULL, test_ctx->secret,
1100
5.19k
        test_ctx->secret ? talloc_strlen(test_ctx->secret) : 0, &code);
1101
5.19k
  if (slen < 0) return slen;
1102
1103
1.19k
  MEM(vp = fr_pair_afrom_da(ctx, attr_tacacs_packet_type));
1104
1.19k
  vp->vp_uint32 = code;
1105
1.19k
  fr_pair_append(out, vp);
1106
1107
1.19k
  return slen;
1108
5.19k
}
1109
1110
static int _encode_test_ctx(fr_tacacs_ctx_t *test_ctx)
1111
5.19k
{
1112
5.19k
  talloc_const_free(test_ctx->secret);
1113
1114
5.19k
  fr_tacacs_global_free();
1115
1116
5.19k
  return 0;
1117
5.19k
}
1118
1119
static int decode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict,
1120
         UNUSED fr_dict_attr_t const *root_da)
1121
5.59k
{
1122
5.59k
  fr_tacacs_ctx_t *test_ctx;
1123
1124
5.59k
  if (fr_tacacs_global_init() < 0) return -1;
1125
1126
5.59k
  test_ctx = talloc_zero(ctx, fr_tacacs_ctx_t);
1127
5.59k
  if (!test_ctx) return -1;
1128
1129
5.59k
  test_ctx->secret = talloc_strdup(test_ctx, "testing123");
1130
5.59k
  test_ctx->root = fr_dict_root(dict_tacacs);
1131
5.59k
  talloc_set_destructor(test_ctx, _encode_test_ctx);
1132
1133
5.59k
  *out = test_ctx;
1134
1135
5.59k
  return 0;
1136
5.59k
}
1137
1138
extern fr_test_point_proto_decode_t tacacs_tp_decode_proto;
1139
fr_test_point_proto_decode_t tacacs_tp_decode_proto = {
1140
  .test_ctx = decode_test_ctx,
1141
  .func   = fr_tacacs_decode_proto
1142
};