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