Coverage Report

Created: 2026-04-12 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/protocols/tacacs/encode.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: ae626531476fc4cb7a740d10db086373a9f01a48 $
19
 *
20
 * @file protocols/tacacs/encode.c
21
 * @brief Low-Level TACACS+ encode 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/dbuff.h>
30
#include <freeradius-devel/util/rand.h>
31
#include <freeradius-devel/util/struct.h>
32
33
#include "tacacs.h"
34
#include "attrs.h"
35
36
int fr_tacacs_code_to_packet(fr_tacacs_packet_t *pkt, uint32_t code)
37
0
{
38
0
  switch (code) {
39
0
  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_START:
40
0
    pkt->hdr.type = FR_TAC_PLUS_AUTHEN;
41
0
    pkt->hdr.seq_no = 1;
42
0
    break;
43
44
0
  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_PASS:
45
0
    pkt->hdr.type = FR_TAC_PLUS_AUTHEN;
46
0
    pkt->authen_reply.status = FR_TAC_PLUS_AUTHEN_STATUS_PASS;
47
0
    break;
48
49
0
  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_FAIL:
50
0
    pkt->hdr.type = FR_TAC_PLUS_AUTHEN;
51
0
    pkt->authen_reply.status = FR_TAC_PLUS_AUTHEN_STATUS_FAIL;
52
0
    break;
53
54
0
  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_GETDATA:
55
0
    pkt->hdr.type = FR_TAC_PLUS_AUTHEN;
56
0
    pkt->authen_reply.status = FR_TAC_PLUS_AUTHEN_STATUS_GETDATA;
57
0
    break;
58
59
0
  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_GETUSER:
60
0
    pkt->hdr.type = FR_TAC_PLUS_AUTHEN;
61
0
    pkt->authen_reply.status = FR_TAC_PLUS_AUTHEN_STATUS_GETUSER;
62
0
    break;
63
64
0
  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_GETPASS:
65
0
    pkt->hdr.type = FR_TAC_PLUS_AUTHEN;
66
0
    pkt->authen_reply.status = FR_TAC_PLUS_AUTHEN_STATUS_GETPASS;
67
0
    break;
68
69
0
  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_RESTART:
70
0
    pkt->hdr.type = FR_TAC_PLUS_AUTHEN;
71
0
    pkt->authen_reply.status = FR_TAC_PLUS_AUTHEN_STATUS_RESTART;
72
0
    break;
73
74
0
  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_ERROR:
75
0
    pkt->hdr.type = FR_TAC_PLUS_AUTHEN;
76
0
    pkt->authen_reply.status = FR_TAC_PLUS_AUTHEN_STATUS_ERROR;
77
0
    break;
78
79
0
  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_CONTINUE:
80
0
    pkt->hdr.type = FR_TAC_PLUS_AUTHEN;
81
0
    pkt->authen_cont.flags = FR_TAC_PLUS_CONTINUE_FLAG_UNSET;
82
0
    break;
83
84
0
  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_CONTINUE_ABORT:
85
0
    pkt->hdr.type = FR_TAC_PLUS_AUTHEN;
86
0
    pkt->authen_cont.flags = FR_TAC_PLUS_CONTINUE_FLAG_ABORT;
87
0
    break;
88
89
0
  case FR_PACKET_TYPE_VALUE_AUTHORIZATION_REQUEST:
90
0
    pkt->hdr.type = FR_TAC_PLUS_AUTHOR;
91
0
    break;
92
93
0
  case FR_PACKET_TYPE_VALUE_AUTHORIZATION_PASS_ADD:
94
0
    pkt->hdr.type = FR_TAC_PLUS_AUTHOR;
95
0
    pkt->author_reply.status = FR_TAC_PLUS_AUTHOR_STATUS_PASS_ADD;
96
0
    break;
97
98
0
  case FR_PACKET_TYPE_VALUE_AUTHORIZATION_PASS_REPLACE:
99
0
    pkt->hdr.type = FR_TAC_PLUS_AUTHOR;
100
0
    pkt->author_reply.status = FR_TAC_PLUS_AUTHOR_STATUS_PASS_REPL;
101
0
    break;
102
103
0
  case FR_PACKET_TYPE_VALUE_AUTHORIZATION_FAIL:
104
0
    pkt->hdr.type = FR_TAC_PLUS_AUTHOR;
105
0
    pkt->author_reply.status = FR_TAC_PLUS_AUTHOR_STATUS_FAIL;
106
0
    break;
107
108
0
  case FR_PACKET_TYPE_VALUE_ACCOUNTING_REQUEST:
109
0
    pkt->hdr.type = FR_TAC_PLUS_ACCT;
110
0
    break;
111
112
0
  case FR_PACKET_TYPE_VALUE_ACCOUNTING_SUCCESS:
113
0
    pkt->hdr.type = FR_TAC_PLUS_ACCT;
114
0
    pkt->acct_reply.status = FR_TAC_PLUS_ACCT_STATUS_SUCCESS;
115
0
    break;
116
117
0
  case FR_PACKET_TYPE_VALUE_ACCOUNTING_ERROR:
118
0
    pkt->hdr.type = FR_TAC_PLUS_ACCT;
119
0
    pkt->acct_reply.status = FR_TAC_PLUS_ACCT_STATUS_ERROR;
120
0
    break;
121
122
0
  default:
123
0
    fr_strerror_const("Invalid TACACS+ packet type");
124
0
    return -1;
125
0
  }
126
127
0
  return 0;
128
0
}
129
130
/**
131
 *  Encode a TACACS+ 'arg_N' fields.
132
 */
133
static uint8_t tacacs_encode_body_arg_cnt(fr_pair_list_t *vps, fr_dict_attr_t const *da)
134
0
{
135
0
  int   arg_cnt = 0;
136
0
  fr_pair_t *vp;
137
138
0
  for (vp = fr_pair_list_head(vps);
139
0
       vp;
140
0
       vp = fr_pair_list_next(vps, vp)) {
141
0
    if (arg_cnt == 255) break;
142
143
0
    if (vp->da->flags.internal) continue;
144
145
0
    if (vp->da == attr_tacacs_packet) continue;
146
147
    /*
148
     *  Argument-List = "foo=bar"
149
     */
150
0
    if (vp->da == da) {
151
0
      if (vp->vp_length > 0xff) continue;
152
0
      arg_cnt++;
153
0
      continue;
154
0
    }
155
156
0
    fr_assert(fr_dict_by_da(vp->da) == dict_tacacs);
157
158
    /*
159
     *  Recurse into children.
160
     */
161
0
    if (vp->vp_type == FR_TYPE_VENDOR) {
162
0
      arg_cnt += tacacs_encode_body_arg_cnt(&vp->vp_group, NULL);
163
0
      continue;
164
0
    }
165
166
    /*
167
     *  RFC 8907 attributes.
168
     */
169
0
    if (vp->da->parent->flags.is_root) {
170
0
      arg_cnt++;
171
0
      continue;
172
0
    }
173
174
0
    if (vp->da->parent->type != FR_TYPE_VENDOR) continue;
175
176
0
    arg_cnt++;
177
0
  }
178
179
0
  return arg_cnt;
180
0
}
181
182
static ssize_t tacacs_encode_body_arg_n(fr_dbuff_t *dbuff, uint8_t arg_cnt, uint8_t *arg_len, fr_pair_list_t *vps, fr_dict_attr_t const *da)
183
0
{
184
0
  fr_pair_t   *vp;
185
0
  uint8_t     i = 0;
186
0
  fr_dbuff_t  work_dbuff = FR_DBUFF(dbuff);
187
188
0
  for (vp = fr_pair_list_head(vps);
189
0
       vp;
190
0
       vp = fr_pair_list_next(vps, vp)) {
191
0
    int len;
192
193
0
    if (i == 255) break;
194
0
    if (i > arg_cnt) break;
195
196
0
    if (vp->da->flags.internal) continue;
197
198
0
    if (vp->da == attr_tacacs_packet) continue;
199
200
    /*
201
     *  Argument-List = "foo=bar"
202
     */
203
0
    if (vp->da == da) {
204
0
      if (vp->vp_length > 0xff) continue;
205
206
      /* Append the <arg_N> field */
207
0
      FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_strvalue, vp->vp_length);
208
209
0
      FR_PROTO_TRACE("arg[%d] --> %s", i, vp->vp_strvalue);
210
0
      len = vp->vp_length;
211
212
0
    } else if (vp->vp_type == FR_TYPE_VENDOR) {
213
0
      ssize_t slen;
214
0
      uint8_t child_argc;
215
216
      /*
217
       *  Nested attribute: just recurse.
218
       */
219
0
      child_argc = fr_pair_list_num_elements(&vp->vp_group);
220
0
      if (child_argc > (arg_cnt - i)) child_argc = arg_cnt - i;
221
222
0
      slen = tacacs_encode_body_arg_n(&work_dbuff, child_argc, &arg_len[i], &vp->vp_group, vp->da);
223
0
      if (slen < 0) return FR_DBUFF_ERROR_OFFSET(slen, fr_dbuff_used(&work_dbuff));
224
225
0
      i += child_argc;
226
0
      continue;
227
228
0
    } else if (!vp->da->parent || (!vp->da->parent->flags.is_root && (vp->da->parent->type != FR_TYPE_VENDOR))) {
229
0
      continue;
230
231
0
    } else {
232
0
      ssize_t slen;
233
0
      fr_sbuff_t sbuff;
234
0
      fr_dbuff_t arg_dbuff = FR_DBUFF_MAX(&work_dbuff, 255);
235
0
      fr_value_box_t box;
236
0
      char buffer[256];
237
238
      /*
239
       *  Print it as "name=value"
240
       */
241
0
      FR_DBUFF_IN_MEMCPY_RETURN(&arg_dbuff, vp->da->name, strlen(vp->da->name));
242
0
      FR_DBUFF_IN_BYTES_RETURN(&arg_dbuff, (uint8_t) '=');
243
244
0
      sbuff = FR_SBUFF_OUT(buffer, sizeof(buffer));
245
246
0
      switch (vp->vp_type) {
247
        /*
248
         *  For now, we always print time deltas and dates as integers.
249
         *
250
         *  Because everyone else's date formats are insane.
251
         */
252
0
      case FR_TYPE_DATE:
253
0
      case FR_TYPE_TIME_DELTA:
254
0
        fr_value_box_init(&box, FR_TYPE_UINT64, vp->data.enumv, vp->vp_tainted);
255
0
        if (fr_value_box_cast(NULL, &box, FR_TYPE_UINT64, NULL, &vp->data) < 0) {
256
0
          buffer[0] = '\0';
257
0
          slen = 0;
258
0
          break;
259
0
        }
260
261
0
        slen = fr_sbuff_in_sprintf(&sbuff, "%lu", box.vb_uint64);
262
0
        if (slen <= 0) return -1;
263
0
        break;
264
265
0
      default:
266
0
        slen = fr_pair_print_value_quoted(&sbuff, vp, T_BARE_WORD);
267
0
        if (slen <= 0) return -1;
268
0
      }
269
270
0
      FR_DBUFF_IN_MEMCPY_RETURN(&arg_dbuff, buffer, (size_t) slen);
271
272
0
      len = fr_dbuff_used(&arg_dbuff);
273
274
0
      FR_PROTO_TRACE("arg[%d] --> %.*s", i, len, fr_dbuff_start(&arg_dbuff));
275
276
0
      fr_dbuff_set(&work_dbuff, &arg_dbuff);
277
0
    }
278
279
0
    fr_assert(len <= UINT8_MAX);
280
281
0
    FR_PROTO_TRACE("len(arg[%d]) = %d", i, len);
282
0
    arg_len[i++] = len;
283
0
  }
284
285
0
  return fr_dbuff_set(dbuff, &work_dbuff);
286
0
}
287
288
/*
289
 *  Encode a TACACS+ field.
290
 */
291
static ssize_t tacacs_encode_field(fr_dbuff_t *dbuff, fr_pair_list_t *vps, fr_dict_attr_t const *da, size_t max_len)
292
0
{
293
0
  fr_pair_t  *vp;
294
0
  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
295
296
0
  vp = fr_pair_find_by_da(vps, NULL, da);
297
0
  if (!vp || !vp->vp_length || (vp->vp_length > max_len)) return 0;
298
299
0
  if (da->type == FR_TYPE_STRING) {
300
0
    FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_strvalue, vp->vp_length);
301
0
  } else {
302
0
    FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_octets, vp->vp_length);
303
0
  }
304
305
0
  FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), da->name);
306
307
0
  return fr_dbuff_set(dbuff, &work_dbuff);
308
0
}
309
310
static ssize_t tacacs_encode_chap(fr_dbuff_t *dbuff, fr_tacacs_packet_t *packet, fr_pair_list_t *vps, fr_dict_attr_t const *da_chap, fr_dict_attr_t const *da_challenge)
311
0
{
312
0
  fr_pair_t *chap, *challenge;
313
0
  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
314
315
0
  chap = fr_pair_find_by_da(vps, NULL, da_chap);
316
0
  if (!chap) {
317
0
    packet->authen_start.data_len = 0;
318
0
    return 0;
319
0
  }
320
321
0
  challenge = fr_pair_find_by_da(vps, NULL, da_challenge);
322
0
  if (!challenge) {
323
0
    fr_strerror_printf("Packet contains %s but no %s", da_chap->name, da_challenge->name);
324
0
    return -1;
325
0
  }
326
327
0
  if (!challenge->vp_length) {
328
0
    fr_strerror_printf("%s is empty", da_challenge->name);
329
0
    return -1;
330
0
  }
331
332
0
  if (!chap->vp_length) {
333
0
    fr_strerror_printf("%s is empty", da_chap->name);
334
0
    return -1;
335
0
  }
336
337
0
  if ((chap->vp_length + challenge->vp_length) > 255) {
338
0
    fr_strerror_printf("%s and %s are longer than 255 octets", da_chap->name, da_challenge->name);
339
0
    return -1;
340
0
  }
341
342
0
  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, chap->vp_octets, 1);
343
0
  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, challenge->vp_octets, challenge->vp_length);
344
0
  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, chap->vp_octets + 1, chap->vp_length - 1);
345
346
0
  packet->authen_start.data_len = chap->vp_length + challenge->vp_length;
347
348
0
  return fr_dbuff_set(dbuff, &work_dbuff);
349
0
}
350
351
/*
352
 *  Magic macros to keep things happy.
353
 *
354
 *  Note that the various fields are optional.  If the caller
355
 *  doesn't specify them, then they don't get encoded.
356
 */
357
0
#define ENCODE_FIELD_UINT8(_field, _da) do { \
358
0
  vp = fr_pair_find_by_da(vps, NULL, _da); \
359
0
  _field = (vp) ? vp->vp_uint8 : 0; \
360
0
} while (0)
361
362
0
#define ENCODE_FIELD_STRING8(_field, _da) _field = tacacs_encode_field(&work_dbuff, vps, _da, 0xff)
363
0
#define ENCODE_FIELD_STRING16(_field, _da) _field = htons(tacacs_encode_field(&work_dbuff, vps, _da, 0xffff))
364
365
/**
366
 *  Encode VPS into a raw TACACS packet.
367
 */
368
ssize_t fr_tacacs_encode(fr_dbuff_t *dbuff, uint8_t const *original_packet, char const *secret, size_t secret_len,
369
       unsigned int code, fr_pair_list_t *vps)
370
0
{
371
0
  fr_pair_t   *vp;
372
0
  fr_tacacs_packet_t  *packet;
373
0
  fr_dcursor_t    cursor;
374
0
  fr_da_stack_t     da_stack;
375
0
  ssize_t     len = 0;
376
0
  size_t      body_len, packet_len;
377
0
  fr_dbuff_t    work_dbuff = FR_DBUFF(dbuff);
378
0
  fr_dbuff_marker_t hdr, body, hdr_io;
379
0
  uint8_t     version_byte = 0;
380
381
0
  fr_tacacs_packet_hdr_t const *original = (fr_tacacs_packet_hdr_t const *) original_packet;
382
383
0
  if (!vps) {
384
0
  error:
385
0
    fr_strerror_const("Cannot encode empty packet");
386
0
    return -1;
387
0
  }
388
389
  /*
390
   *  Verify space for the packet...
391
   */
392
0
  FR_DBUFF_REMAINING_RETURN(&work_dbuff, sizeof(fr_tacacs_packet_t));
393
394
  /*
395
   * 3.4. The TACACS+ Packet Header
396
   *
397
   * 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
398
   * +----------------+----------------+----------------+----------------+
399
   * |major  | minor  |                |                |                |
400
   * |version| version|      type      |     seq_no     |   flags        |
401
   * +----------------+----------------+----------------+----------------+
402
   * |                                                                   |
403
   * |                            session_id                             |
404
   * +----------------+----------------+----------------+----------------+
405
   * |                                                                   |
406
   * |                              length                               |
407
   * +----------------+----------------+----------------+----------------+
408
   */
409
410
  /*
411
   *  Let's keep reference for packet header.
412
   */
413
0
  fr_dbuff_marker(&hdr, &work_dbuff);
414
  /*
415
   *  Add marker letting us read/write header bytes without moving hdr.
416
   */
417
0
  fr_dbuff_marker(&hdr_io, &work_dbuff);
418
419
  /*
420
   *  Handle the fields in-place.
421
   */
422
0
  packet = (fr_tacacs_packet_t *)fr_dbuff_start(&work_dbuff);
423
424
  /*
425
   *  Find the first attribute which is parented by TACACS-Packet.
426
   */
427
0
  for (vp = fr_pair_dcursor_init(&cursor, vps);
428
0
       vp;
429
0
       vp = fr_dcursor_next(&cursor)) {
430
0
    if (vp->da == attr_tacacs_packet) break;
431
0
    if (vp->da->parent == attr_tacacs_packet) break;
432
0
  }
433
434
  /*
435
   *  No "Packet" struct to encode.  We MUST have an original packet to copy the various fields
436
   *  from.
437
   */
438
0
  if (!vp) {
439
0
    if (!original) {
440
0
      fr_strerror_printf("%s: No TACACS+ %s in the attribute list",
441
0
             __FUNCTION__, attr_tacacs_packet->name);
442
0
      return -1;
443
0
    }
444
445
    /*
446
     *  Initialize the buffer avoiding invalid values.
447
     */
448
0
    memset(packet, 0, sizeof(fr_tacacs_packet_t));
449
450
    /*
451
     *  Initialize the reply from the request.
452
     *
453
     *  Make room and fill up the original header. We shouldn't just copy the original packet,
454
     *  because the fields 'seq_no' and 'length' are not the same.
455
     */
456
0
    FR_DBUFF_ADVANCE_RETURN(&work_dbuff, sizeof(fr_tacacs_packet_hdr_t));
457
458
0
  } else if (vp->da == attr_tacacs_packet) {
459
0
    fr_dcursor_t child_cursor;
460
461
0
    fr_proto_da_stack_build(&da_stack, attr_tacacs_packet);
462
0
    FR_PROTO_STACK_PRINT(&da_stack, 0);
463
464
0
    fr_pair_dcursor_init(&child_cursor, &vp->vp_group);
465
466
    /*
467
     *  Call the struct encoder to do the actual work,
468
     *  which fills the struct fields with zero if the member VP is not used.
469
     */
470
0
    len = fr_struct_to_network(&work_dbuff, &da_stack, 0, &child_cursor, NULL, NULL, NULL);
471
0
    if (len != sizeof(fr_tacacs_packet_hdr_t)) {
472
0
      fr_strerror_printf("%s: Failed encoding %s using fr_struct_to_network()",
473
0
             __FUNCTION__, attr_tacacs_packet->name);
474
0
      return -1;
475
0
    }
476
0
    fr_dcursor_next(&cursor);
477
478
0
  } else {
479
0
    fr_proto_da_stack_build(&da_stack, attr_tacacs_packet);
480
0
    FR_PROTO_STACK_PRINT(&da_stack, 0);
481
482
    /*
483
     *  Call the struct encoder to do the actual work,
484
     *  which fills the struct fields with zero if the member VP is not used.
485
     */
486
0
    len = fr_struct_to_network(&work_dbuff, &da_stack, 0, &cursor, NULL, NULL, NULL);
487
0
    if (len != sizeof(fr_tacacs_packet_hdr_t)) {
488
0
      fr_strerror_printf("%s: Failed encoding %s using fr_struct_to_network()",
489
0
             __FUNCTION__, attr_tacacs_packet->name);
490
0
      return -1;
491
0
    }
492
0
  }
493
494
  /*
495
   *  Ensure that we send a sane reply to a request.
496
   */
497
0
  if (original) {
498
0
    packet->hdr.version = original->version;
499
0
    packet->hdr.type = original->type;
500
0
    packet->hdr.flags = original->flags; /* encrypted && single connection */
501
0
    packet->hdr.session_id = original->session_id;
502
503
    /*
504
     *  The client may not set SINGLE_CONNECT flag.  So if the administrator has set it in the reply,
505
     *  we allow setting the flag.  This lets the server tell the client that it supports "single
506
     *  connection" mode.
507
     */
508
0
    vp = fr_pair_find_by_da_nested(vps, NULL, attr_tacacs_flags);
509
0
    if (vp) packet->hdr.flags |= (vp->vp_uint8 & FR_TAC_PLUS_SINGLE_CONNECT_FLAG);
510
0
  }
511
512
  /*
513
   *  Starting here is a 'body' that may require encryption.
514
   */
515
0
  fr_dbuff_marker(&body, &work_dbuff);
516
517
  /*
518
   *  Encode 8 octets of various fields not members of STRUCT
519
   */
520
0
  switch (packet->hdr.type) {
521
0
  case FR_TAC_PLUS_AUTHEN:
522
    /*
523
     * seq_no
524
     *
525
     * This is the sequence number of the current packet for the current session.
526
     * The first packet in a session MUST have the sequence number 1 and each
527
     * subsequent packet will increment the sequence number by one. Thus clients
528
     * only send packets containing odd sequence numbers, and TACACS+ servers only
529
     * send packets containing even sequence numbers.
530
     */
531
0
    if (packet_is_authen_start_request(packet)) { /* Start */
532
      /**
533
       * 4.1. The Authentication START Packet Body
534
       *
535
       * 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
536
       * +----------------+----------------+----------------+----------------+
537
       * |    action      |    priv_lvl    |  authen_type   | authen_service |
538
       * +----------------+----------------+----------------+----------------+
539
       * |    user_len    |    port_len    |  rem_addr_len  |    data_len    |
540
       * +----------------+----------------+----------------+----------------+
541
       * |    user ...
542
       * +----------------+----------------+----------------+----------------+
543
       * |    port ...
544
       * +----------------+----------------+----------------+----------------+
545
       * |    rem_addr ...
546
       * +----------------+----------------+----------------+----------------+
547
       * |    data...
548
       * +----------------+----------------+----------------+----------------+
549
       */
550
551
      /*
552
       *  Make room for such body request.
553
       */
554
0
      FR_DBUFF_ADVANCE_RETURN(&work_dbuff, sizeof(packet->authen_start));
555
556
      /*
557
       *  Encode 4 octets of various flags.
558
       */
559
0
      ENCODE_FIELD_UINT8(packet->authen_start.action, attr_tacacs_action);
560
0
      ENCODE_FIELD_UINT8(packet->authen_start.priv_lvl, attr_tacacs_privilege_level);
561
0
      ENCODE_FIELD_UINT8(packet->authen_start.authen_type, attr_tacacs_authentication_type);
562
0
      ENCODE_FIELD_UINT8(packet->authen_start.authen_service, attr_tacacs_authentication_service);
563
564
      /*
565
       *  Encode 4 mandatory fields.
566
       */
567
0
      ENCODE_FIELD_STRING8(packet->authen_start.user_len, attr_tacacs_user_name);
568
0
      ENCODE_FIELD_STRING8(packet->authen_start.port_len, attr_tacacs_client_port);
569
0
      ENCODE_FIELD_STRING8(packet->authen_start.rem_addr_len, attr_tacacs_remote_address);
570
571
      /*
572
       *  No explicit "Data" attribute, try to automatically determine what to do.
573
       */
574
0
      if (fr_pair_find_by_da_nested(vps, NULL, attr_tacacs_data)) {
575
0
        ENCODE_FIELD_STRING8(packet->authen_start.data_len, attr_tacacs_data);
576
577
0
      } else switch (packet->authen_start.authen_type) {
578
0
        default:
579
0
          break;
580
581
0
        case FR_AUTHENTICATION_TYPE_VALUE_PAP:
582
0
          ENCODE_FIELD_STRING8(packet->authen_start.data_len, attr_tacacs_user_password);
583
0
          break;
584
585
0
        case FR_AUTHENTICATION_TYPE_VALUE_CHAP:
586
0
          if (tacacs_encode_chap(&work_dbuff, packet, vps, attr_tacacs_chap_password, attr_tacacs_chap_challenge) < 0) return -1;
587
0
          break;
588
589
0
        case FR_AUTHENTICATION_TYPE_VALUE_MSCHAP: {
590
0
          int rcode;
591
592
0
          rcode = tacacs_encode_chap(&work_dbuff, packet, vps, attr_tacacs_mschap_response, attr_tacacs_mschap_challenge);
593
0
          if (rcode < 0) return rcode;
594
595
0
          if (rcode > 0) break;
596
597
0
          if (tacacs_encode_chap(&work_dbuff, packet, vps, attr_tacacs_mschap2_response, attr_tacacs_mschap_challenge) < 0) return -1;
598
0
          }
599
0
          break;
600
0
      }
601
602
0
      goto check_request;
603
604
0
    } else if (packet_is_authen_continue(packet)) {
605
      /*
606
       * 4.3. The Authentication CONTINUE Packet Body
607
       *
608
       * This packet is sent from the client to the server following the receipt of
609
       * a REPLY packet.
610
       *
611
       *  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
612
       * +----------------+----------------+----------------+----------------+
613
       * |          user_msg len           |            data_len             |
614
       * +----------------+----------------+----------------+----------------+
615
       * |     flags      |  user_msg ...
616
       * +----------------+----------------+----------------+----------------+
617
       * |    data ...
618
       * +----------------+
619
       */
620
621
      /*
622
       *  Make room for such body request.
623
       */
624
0
      FR_DBUFF_ADVANCE_RETURN(&work_dbuff, sizeof(packet->authen_cont));
625
626
      /*
627
       *  Encode 2 mandatory fields.
628
       */
629
0
      ENCODE_FIELD_STRING16(packet->authen_cont.user_msg_len, attr_tacacs_user_message);
630
0
      ENCODE_FIELD_STRING16(packet->authen_cont.data_len, attr_tacacs_data);
631
632
      /*
633
       *  Look at the abort flag after encoding the fields.
634
       */
635
0
      ENCODE_FIELD_UINT8(packet->authen_cont.flags, attr_tacacs_authentication_continue_flags);
636
637
0
      goto check_request;
638
639
0
    } else if (packet_is_authen_reply(packet)) {
640
      /*
641
       * 4.2. The Authentication REPLY Packet Body
642
       *
643
       * 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
644
       * +----------------+----------------+----------------+----------------+
645
       * |     status     |      flags     |        server_msg_len           |
646
       * +----------------+----------------+----------------+----------------+
647
       * |           data_len              |        server_msg ...
648
       * +----------------+----------------+----------------+----------------+
649
       * |           data ...
650
       * +----------------+----------------+
651
       */
652
653
      /*
654
       *  Make room for such body request.
655
       */
656
0
      FR_DBUFF_ADVANCE_RETURN(&work_dbuff, sizeof(packet->authen_reply));
657
658
0
      ENCODE_FIELD_UINT8(packet->authen_reply.status, attr_tacacs_authentication_status);
659
0
      ENCODE_FIELD_UINT8(packet->authen_reply.flags, attr_tacacs_authentication_flags);
660
661
      /*
662
       *  Encode 2 mandatory fields.
663
       */
664
0
      ENCODE_FIELD_STRING16(packet->authen_reply.server_msg_len, attr_tacacs_server_message);
665
0
      ENCODE_FIELD_STRING16(packet->authen_reply.data_len, attr_tacacs_data);
666
667
0
      goto check_reply;
668
0
    }
669
670
0
    fr_strerror_const("encode: Unknown authentication packet type");
671
0
    return -1;
672
673
0
  case FR_TAC_PLUS_AUTHOR:
674
0
    if (packet_is_author_request(packet)) {
675
      /*
676
       * 5.1. The Authorization REQUEST Packet Body
677
       *
678
       *  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
679
       * +----------------+----------------+----------------+----------------+
680
       * |  authen_method |    priv_lvl    |  authen_type   | authen_service |
681
       * +----------------+----------------+----------------+----------------+
682
       * |    user_len    |    port_len    |  rem_addr_len  |    arg_cnt     |
683
       * +----------------+----------------+----------------+----------------+
684
       * |   arg_1_len    |   arg_2_len    |      ...       |   arg_N_len    |
685
       * +----------------+----------------+----------------+----------------+
686
       * |   user ...
687
       * +----------------+----------------+----------------+----------------+
688
       * |   port ...
689
       * +----------------+----------------+----------------+----------------+
690
       * |   rem_addr ...
691
       * +----------------+----------------+----------------+----------------+
692
       * |   arg_1 ...
693
       * +----------------+----------------+----------------+----------------+
694
       * |   arg_2 ...
695
       * +----------------+----------------+----------------+----------------+
696
       * |   ...
697
       * +----------------+----------------+----------------+----------------+
698
       * |   arg_N ...
699
       * +----------------+----------------+----------------+----------------+
700
       */
701
702
      /*
703
       *  Make room for such body request.
704
       */
705
0
      FR_DBUFF_ADVANCE_RETURN(&work_dbuff, sizeof(packet->author_req));
706
707
      /*
708
       *  Encode 4 octets of various flags.
709
       */
710
0
      ENCODE_FIELD_UINT8(packet->author_req.authen_method, attr_tacacs_authentication_method);
711
0
      ENCODE_FIELD_UINT8(packet->author_req.priv_lvl, attr_tacacs_privilege_level);
712
0
      ENCODE_FIELD_UINT8(packet->author_req.authen_type, attr_tacacs_authentication_type);
713
0
      ENCODE_FIELD_UINT8(packet->author_req.authen_service, attr_tacacs_authentication_service);
714
715
      /*
716
       *  Encode 'arg_N' arguments (horrible format)
717
       */
718
0
      packet->author_req.arg_cnt = tacacs_encode_body_arg_cnt(vps, attr_tacacs_argument_list);
719
0
      if (packet->author_req.arg_cnt) FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, packet->author_req.arg_cnt);
720
721
      /*
722
       *  Encode 3 mandatory fields.
723
       */
724
0
      ENCODE_FIELD_STRING8(packet->author_req.user_len, attr_tacacs_user_name);
725
0
      ENCODE_FIELD_STRING8(packet->author_req.port_len, attr_tacacs_client_port);
726
0
      ENCODE_FIELD_STRING8(packet->author_req.rem_addr_len, attr_tacacs_remote_address);
727
728
      /*
729
       *  Append 'args_body' to the end of buffer
730
       */
731
0
      if (packet->author_req.arg_cnt > 0) {
732
0
        if (tacacs_encode_body_arg_n(&work_dbuff, packet->author_req.arg_cnt, &packet->author_req.arg_len[0], vps, attr_tacacs_argument_list) < 0) goto error;
733
0
      }
734
735
0
      goto check_request;
736
0
    } else if (packet_is_author_reply(packet)) {
737
      /*
738
       * 5.2. The Authorization RESPONSE Packet Body
739
       *
740
       *  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
741
       * +----------------+----------------+----------------+----------------+
742
       * |    status      |     arg_cnt    |         server_msg len          |
743
       * +----------------+----------------+----------------+----------------+
744
       * +            data_len             |    arg_1_len   |    arg_2_len   |
745
       * +----------------+----------------+----------------+----------------+
746
       * |      ...       |   arg_N_len    |         server_msg ...
747
       * +----------------+----------------+----------------+----------------+
748
       * |   data ...
749
       * +----------------+----------------+----------------+----------------+
750
       * |   arg_1 ...
751
       * +----------------+----------------+----------------+----------------+
752
       * |   arg_2 ...
753
       * +----------------+----------------+----------------+----------------+
754
       * |   ...
755
       * +----------------+----------------+----------------+----------------+
756
       * |   arg_N ...
757
       * +----------------+----------------+----------------+----------------+
758
       */
759
760
      /*
761
       *  Make room for such body request.
762
       */
763
0
      FR_DBUFF_ADVANCE_RETURN(&work_dbuff, sizeof(packet->author_reply));
764
765
      /*
766
       *  Encode 1 mandatory field.
767
       */
768
0
      ENCODE_FIELD_UINT8(packet->author_reply.status, attr_tacacs_authorization_status);
769
770
      /*
771
       *  Encode 'arg_N' arguments (horrible format)
772
       *
773
       *  For ERRORs, we don't encode arguments.
774
       *
775
       *  5.2
776
       *
777
       *     A status of TAC_PLUS_AUTHOR_STATUS_ERROR indicates an error occurred
778
       *     on the server.  For the differences between ERROR and FAIL, refer to
779
       *     section Session Completion (Section 3.4) . None of the arg values
780
       *     have any relevance if an ERROR is set, and must be ignored.
781
       *
782
       *     When the status equals TAC_PLUS_AUTHOR_STATUS_FOLLOW, then the
783
       *     arg_cnt MUST be 0.
784
       */
785
0
      if (!((packet->author_reply.status == FR_AUTHORIZATION_STATUS_VALUE_ERROR) ||
786
0
            (packet->author_reply.status == FR_AUTHORIZATION_STATUS_VALUE_FOLLOW))) {
787
0
        packet->author_reply.arg_cnt = tacacs_encode_body_arg_cnt(vps, attr_tacacs_argument_list);
788
0
        if (packet->author_reply.arg_cnt) FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, packet->author_reply.arg_cnt);
789
0
      } else {
790
0
        packet->author_reply.arg_cnt = 0;
791
0
      }
792
793
      /*
794
       *  Encode 2 mandatory fields.
795
       */
796
0
      ENCODE_FIELD_STRING16(packet->author_reply.server_msg_len, attr_tacacs_server_message);
797
0
      ENCODE_FIELD_STRING16(packet->author_reply.data_len, attr_tacacs_data);
798
799
      /*
800
       *  Append 'args_body' to the end of buffer
801
       */
802
0
      if (packet->author_reply.arg_cnt > 0) {
803
0
        if (tacacs_encode_body_arg_n(&work_dbuff, packet->author_reply.arg_cnt, &packet->author_reply.arg_len[0], vps, attr_tacacs_argument_list) < 0) goto error;
804
0
      }
805
806
0
      goto check_reply;
807
808
0
    }
809
810
0
    fr_strerror_const("encode: Unknown authorization packet type");
811
0
    return -1;
812
813
0
  case FR_TAC_PLUS_ACCT:
814
0
    if (packet_is_acct_request(packet)) {
815
      /**
816
       * 6.1. The Account REQUEST Packet Body
817
       *
818
       * 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
819
       * +----------------+----------------+----------------+----------------+
820
       * |      flags     |  authen_method |    priv_lvl    |  authen_type   |
821
       * +----------------+----------------+----------------+----------------+
822
       * | authen_service |    user_len    |    port_len    |  rem_addr_len  |
823
       * +----------------+----------------+----------------+----------------+
824
       * |    arg_cnt     |   arg_1_len    |   arg_2_len    |      ...       |
825
       * +----------------+----------------+----------------+----------------+
826
       * |   arg_N_len    |    user ...
827
       * +----------------+----------------+----------------+----------------+
828
       * |   port ...
829
       * +----------------+----------------+----------------+----------------+
830
       * |   rem_addr ...
831
       * +----------------+----------------+----------------+----------------+
832
       * |   arg_1 ...
833
       * +----------------+----------------+----------------+----------------+
834
       * |   arg_2 ...
835
       * +----------------+----------------+----------------+----------------+
836
       * |   ...
837
       * +----------------+----------------+----------------+----------------+
838
       * |   arg_N ...
839
       * +----------------+----------------+----------------+----------------+
840
       */
841
842
      /*
843
       *  Make room for such body request.
844
       */
845
0
      FR_DBUFF_ADVANCE_RETURN(&work_dbuff, sizeof(packet->acct_req));
846
847
      /*
848
       *  Encode 5 octets of various flags.
849
       */
850
0
      ENCODE_FIELD_UINT8(packet->acct_req.flags, attr_tacacs_accounting_flags);
851
0
      ENCODE_FIELD_UINT8(packet->acct_req.authen_method, attr_tacacs_authentication_method);
852
0
      ENCODE_FIELD_UINT8(packet->acct_req.priv_lvl, attr_tacacs_privilege_level);
853
0
      ENCODE_FIELD_UINT8(packet->acct_req.authen_type, attr_tacacs_authentication_type);
854
0
      ENCODE_FIELD_UINT8(packet->acct_req.authen_service, attr_tacacs_authentication_service);
855
856
      /*
857
       *  Encode 'arg_N' arguments (horrible format)
858
       */
859
0
      packet->acct_req.arg_cnt = tacacs_encode_body_arg_cnt(vps, attr_tacacs_argument_list);
860
0
      if (packet->acct_req.arg_cnt) FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, packet->acct_req.arg_cnt);
861
862
      /*
863
       *  Encode 3 mandatory fields.
864
       */
865
0
      ENCODE_FIELD_STRING8(packet->acct_req.user_len, attr_tacacs_user_name);
866
0
      ENCODE_FIELD_STRING8(packet->acct_req.port_len, attr_tacacs_client_port);
867
0
      ENCODE_FIELD_STRING8(packet->acct_req.rem_addr_len, attr_tacacs_remote_address);
868
869
      /*
870
       *  Append 'args_body' to the end of buffer
871
       */
872
0
      if (packet->acct_req.arg_cnt > 0) {
873
0
        if (tacacs_encode_body_arg_n(&work_dbuff, packet->acct_req.arg_cnt, &packet->acct_req.arg_len[0], vps, attr_tacacs_argument_list) < 0) goto error;
874
0
      }
875
876
0
    check_request:
877
      /*
878
       *  Just to avoid malformed packet.
879
       */
880
0
      fr_dbuff_set(&hdr_io, &hdr);
881
0
      fr_dbuff_out(&version_byte, &hdr_io);
882
0
      if (!version_byte) {
883
0
        version_byte = 0xc1; /* version 12.1 */
884
0
        fr_dbuff_set(&hdr_io, &hdr);
885
0
        FR_DBUFF_IN_RETURN(&hdr_io, version_byte);
886
0
      }
887
      /*
888
       *  If the caller didn't set a session ID, use a random one.
889
       */
890
0
      if (!fr_pair_find_by_da_nested(vps, NULL, attr_tacacs_session_id)) {
891
0
        packet->hdr.session_id = fr_rand();
892
0
      }
893
894
      /*
895
       *  Requests have odd sequence numbers.
896
       */
897
0
      packet->hdr.seq_no |= 0x01;
898
0
      break;
899
900
0
    } else if (packet_is_acct_reply(packet)) {
901
      /**
902
       * 6.2. The Accounting REPLY Packet Body
903
       *
904
       * 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
905
       * +----------------+----------------+----------------+----------------+
906
       * |         server_msg len          |            data_len             |
907
       * +----------------+----------------+----------------+----------------+
908
       * |     status     |         server_msg ...
909
       * +----------------+----------------+----------------+----------------+
910
       * |     data ...
911
       * +----------------+
912
       */
913
914
      /*
915
       *  Make room for such body request.
916
       */
917
0
      FR_DBUFF_ADVANCE_RETURN(&work_dbuff, sizeof(packet->acct_reply));
918
919
      /*
920
       *  Encode 2 mandatory fields.
921
       */
922
0
      ENCODE_FIELD_STRING16(packet->acct_reply.server_msg_len, attr_tacacs_server_message);
923
0
      ENCODE_FIELD_STRING16(packet->acct_reply.data_len, attr_tacacs_data);
924
925
      /*
926
       *  And also, the status field.
927
       */
928
0
      ENCODE_FIELD_UINT8(packet->acct_reply.status, attr_tacacs_accounting_status);
929
930
0
    check_reply:
931
      /*
932
       *  fr_struct_to_network() fills the struct fields with 0
933
       *  if there is no matching VP.  In the interest of making
934
       *  things easier for the user, we don't require them to
935
       *  copy all of the fields from the request to the reply.
936
       *
937
       *  Instead, we copy the fields manually, and ensure that
938
       *  they have the correct values.
939
       */
940
0
      if (original) {
941
0
        fr_dbuff_set(&hdr_io, &hdr);
942
0
        fr_dbuff_out(&version_byte, &hdr_io);
943
0
        if (!version_byte) {
944
0
          packet->hdr.version = original->version;
945
0
        }
946
947
0
        if (!packet->hdr.seq_no) {
948
0
          packet->hdr.seq_no = original->seq_no + 1; /* uint8_t */
949
0
        }
950
951
0
        if (!packet->hdr.session_id) {
952
0
          packet->hdr.session_id = original->session_id;
953
0
        }
954
0
      }
955
956
      /*
957
       *  Replies have even sequence numbers.
958
       */
959
0
      packet->hdr.seq_no &= 0xfe;
960
0
      break;
961
0
    }
962
963
0
    fr_strerror_const("encode: Unknown accounting packet type");
964
0
    return -1;
965
966
0
  default:
967
0
    fr_strerror_printf("encode: unknown packet type %d", packet->hdr.type);
968
0
    return -1;
969
0
  }
970
971
  /*
972
   *  Force the correct header type, and randomly-placed
973
   *  status fields.  But only if there's no code field.
974
   *  Only the unit tests pass a zero code field, as that's
975
   *  normally invalid.  The unit tests ensure that all of
976
   *  the VPs are passed to encode a packet, and they all
977
   *  must be correct
978
   */
979
0
  if (code && (fr_tacacs_code_to_packet(packet, code) < 0)) return -1;
980
981
  /*
982
   *  The packet length we store in the header doesn't
983
   *  include the size of the header.  But we tell the
984
   *  caller about the total length of the packet.
985
   */
986
0
  packet_len = fr_dbuff_used(&work_dbuff);
987
0
  fr_assert(packet_len >= sizeof(fr_tacacs_packet_hdr_t));
988
989
0
  body_len = (packet_len - sizeof(fr_tacacs_packet_hdr_t));
990
0
  fr_assert(packet_len < FR_MAX_PACKET_SIZE);
991
0
  packet->hdr.length = htonl(body_len);
992
993
  /*
994
   *  If the original packet is encrypted, then the reply
995
   *  MUST be encrypted too.
996
   *
997
   *  On the other hand, if the request is unencrypted,
998
   *  we're OK with sending an encrypted reply.  Because,
999
   *  whatever.
1000
   */
1001
0
  if (original &&
1002
0
      ((original->flags & FR_TAC_PLUS_UNENCRYPTED_FLAG) == 0)) {
1003
0
    packet->hdr.flags &= ~FR_TAC_PLUS_UNENCRYPTED_FLAG;
1004
0
  }
1005
1006
0
#ifndef NDEBUG
1007
0
  if (fr_debug_lvl >= L_DBG_LVL_4) {
1008
0
    uint8_t flags = packet->hdr.flags;
1009
1010
0
    packet->hdr.flags |= FR_TAC_PLUS_UNENCRYPTED_FLAG;
1011
0
    fr_tacacs_packet_log_hex(&default_log, packet, packet_len);
1012
0
    packet->hdr.flags = flags;
1013
0
  }
1014
0
#endif
1015
1016
  /*
1017
   *  3.6. Encryption
1018
   *
1019
   *  Packets are encrypted if the unencrypted flag is clear.
1020
   */
1021
0
  if (secret) {
1022
0
    if (!secret_len) {
1023
0
      fr_strerror_const("Packet should be decrypted, but the secret has zero length");
1024
0
      return -1;
1025
0
    }
1026
1027
0
    FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), packet_len, "fr_tacacs_packet_t (unencrypted)");
1028
1029
0
    if (fr_tacacs_body_xor(packet, fr_dbuff_current(&body), body_len, secret, secret_len) != 0) return -1;
1030
1031
0
    packet->hdr.flags &= ~FR_TAC_PLUS_UNENCRYPTED_FLAG;
1032
0
  } else {
1033
    /*
1034
     *  Packets which have no secret cannot be encrypted.
1035
     */
1036
0
    packet->hdr.flags |= FR_TAC_PLUS_UNENCRYPTED_FLAG;
1037
1038
0
  }
1039
1040
0
  FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), packet_len, "fr_tacacs_packet_t (encoded)");
1041
1042
0
  return fr_dbuff_set(dbuff, &work_dbuff);
1043
0
}
1044
1045
/*
1046
 *  Test points for protocol encode
1047
 */
1048
static ssize_t fr_tacacs_encode_proto(UNUSED TALLOC_CTX *ctx, fr_pair_list_t *vps,
1049
              uint8_t *data, size_t data_len, void *proto_ctx)
1050
0
{
1051
0
  fr_tacacs_ctx_t *test_ctx = talloc_get_type_abort(proto_ctx, fr_tacacs_ctx_t);
1052
1053
0
  return fr_tacacs_encode(&FR_DBUFF_TMP(data, data_len), NULL, test_ctx->secret,
1054
0
        test_ctx->secret ? talloc_strlen(test_ctx->secret) : 0, 0, vps);
1055
0
}
1056
1057
static int _encode_test_ctx(fr_tacacs_ctx_t *test_ctx)
1058
5.74k
{
1059
5.74k
  talloc_const_free(test_ctx->secret);
1060
1061
5.74k
  fr_tacacs_global_free();
1062
1063
5.74k
  return 0;
1064
5.74k
}
1065
1066
static int encode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict,
1067
         UNUSED fr_dict_attr_t const *root_da)
1068
5.74k
{
1069
5.74k
  fr_tacacs_ctx_t *test_ctx;
1070
1071
5.74k
  if (fr_tacacs_global_init() < 0) return -1;
1072
1073
5.74k
  test_ctx = talloc_zero(ctx, fr_tacacs_ctx_t);
1074
5.74k
  if (!test_ctx) return -1;
1075
1076
5.74k
  test_ctx->root = fr_dict_root(dict_tacacs);
1077
5.74k
  talloc_set_destructor(test_ctx, _encode_test_ctx);
1078
1079
5.74k
  *out = test_ctx;
1080
1081
5.74k
  return 0;
1082
5.74k
}
1083
1084
/*
1085
 *  Test points
1086
 */
1087
extern fr_test_point_proto_encode_t tacacs_tp_encode_proto;
1088
fr_test_point_proto_encode_t tacacs_tp_encode_proto = {
1089
  .test_ctx = encode_test_ctx,
1090
  .func   = fr_tacacs_encode_proto
1091
};